remove to prepare for subtree

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov
2025-11-26 22:56:11 +05:00
parent 1ab6638012
commit 6ea92dd409
1351 changed files with 0 additions and 228527 deletions
-297
View File
@@ -1,297 +0,0 @@
# Coverage Scripts Documentation
This directory contains Node.js scripts for managing code coverage in the huly.server monorepo.
## Scripts Overview
### 1. `merge-coverage.js`
Merges individual package coverage reports into a single LCOV file.
**Usage:**
```bash
node merge-coverage.js
```
**What it does:**
- Scans `packages/`, `pods/`, and `tests/` directories for coverage reports
- Finds all `coverage/lcov.info` files
- Merges them into a single `coverage/lcov.info` at the root
- Resolves file paths to absolute paths
- Handles duplicate TN (test name) headers
**Output:**
- `coverage/lcov.info` - Merged coverage data
---
### 2. `generate-coverage-html.js`
Generates HTML coverage reports from LCOV data.
**Usage:**
```bash
node generate-coverage-html.js [input-lcov-file] [output-directory]
```
**Default usage:**
```bash
node generate-coverage-html.js coverage/lcov.info coverage/html
```
**What it does:**
- Parses LCOV format coverage data
- Uses Istanbul library to generate HTML reports
- Creates interactive HTML pages with line-by-line coverage
- Resolves source file paths
- Post-processes HTML to fix missing source code
**Output:**
- `coverage/html/index.html` - Main coverage report
- `coverage/html/**/*.html` - Per-file coverage reports
**Dependencies:**
- `lcov-parse` - Parses LCOV format
- `istanbul-lib-coverage` - Coverage map management
- `istanbul-lib-report` - Report context
- `istanbul-reports` - HTML report generation
---
### 3. `show-coverage-summary.js`
Displays a summary of coverage statistics by package.
**Usage:**
```bash
node show-coverage-summary.js [lcov-file]
```
**Default usage:**
```bash
node show-coverage-summary.js coverage/lcov.info
```
**What it does:**
- Parses the merged LCOV file
- Aggregates coverage by package
- Displays formatted table with:
- Covered lines
- Total lines
- Coverage percentage
- Shows overall totals
**Example Output:**
```
==============================================
COVERAGE SUMMARY BY PACKAGE
==============================================
Package Covered Total Coverage
----------------------------------------------
datalake 10 10 100.00%
minio 111 165 67.27%
postgres 815 1351 60.33%
...
----------------------------------------------
TOTAL 1156 2220 52.07%
```
---
### 4. `run-tests-with-coverage.js`
Runs tests with coverage for all packages sequentially.
**Usage:**
```bash
node run-tests-with-coverage.js
```
**What it does:**
- Scans all packages in `packages/` directory
- For each package with a test script:
- Runs `npm test -- --coverage --silent`
- Extracts and displays coverage summary
- Reports which packages passed/failed
**Note:** This is an alternative to `rush test` for running tests individually.
---
## NPM Scripts
The `package.json` in this directory provides convenient aliases:
```bash
# Merge coverage reports
npm run coverage:merge
# Generate HTML report
npm run coverage:html
# Show coverage summary
npm run coverage:summary
# Run all package tests with coverage
npm run test:coverage
```
---
## Complete Coverage Workflow
### Using Rush (Recommended)
```bash
# Run all tests with coverage and generate reports
rush coverage
```
This command does:
1. `rush test` - Runs all package tests (coverage enabled by jest.config.js)
2. `node scripts/merge-coverage.js` - Merges all LCOV files
3. `node scripts/generate-coverage-html.js` - Generates HTML report
### Manual Workflow
```bash
# 1. Run tests with coverage (coverage enabled in jest.config.js)
rush test
# 2. Merge coverage reports
node common/scripts/merge-coverage.js
# 3. Generate HTML report
node common/scripts/generate-coverage-html.js coverage/lcov.info coverage/html
# 4. View summary
node common/scripts/show-coverage-summary.js
```
---
## Jest Configuration
All packages have coverage enabled by default in `jest.config.js`:
```javascript
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],
roots: ['./src'],
collectCoverage: true, // ✅ Enabled by default
coverageReporters: ['text-summary', 'html', 'lcov'], // ✅ LCOV format
coverageDirectory: 'coverage' // ✅ Output directory
}
```
---
## Output Files
```
coverage/
├── lcov.info # Merged LCOV coverage data
└── html/ # HTML reports
├── index.html # Main report page
├── base.css # Styling
├── prettify.js # Code highlighting
└── [package]/ # Per-package reports
└── [file].html # Per-file coverage
packages/
└── [package-name]/
└── coverage/
├── lcov.info # Package-specific LCOV
└── html/ # Package-specific HTML
```
---
## Coverage Thresholds
Current overall coverage: **52.07%**
Per-package targets:
-**≥90%**: Excellent coverage
-**70-89%**: Good coverage
- ⚠️ **50-69%**: Moderate coverage (needs improvement)
- 🔴 **<50%**: Low coverage (priority improvement)
---
## Troubleshooting
### No LCOV files found
**Error:** `No lcov files found in packages/pods/tests/*/coverage/lcov.info`
**Solution:** Run tests first with `rush test` to generate coverage files.
### Missing dependencies
**Error:** `Cannot find module 'lcov-parse'`
**Solution:**
```bash
cd common/scripts
npm install
```
### Source files not found in HTML report
The scripts attempt to resolve source file paths using multiple strategies:
1. Absolute path (if exists)
2. Relative to package directory
3. Suffix match in repository
If files still can't be found, check that source files exist and paths in LCOV are correct.
---
## Legacy Scripts
The following bash scripts have been replaced with Node.js versions:
-`show-coverage.sh` → ✅ `run-tests-with-coverage.js`
-`show-coverage-summary.sh` → ✅ `show-coverage-summary.js`
The bash scripts are kept for backward compatibility but the Node.js versions are recommended for better cross-platform support.
---
## CI/CD Integration
The LCOV format is compatible with common coverage tools:
- **Codecov**: `bash <(curl -s https://codecov.io/bash)`
- **Coveralls**: `cat coverage/lcov.info | coveralls`
- **SonarQube**: Configure `sonar.javascript.lcov.reportPaths=coverage/lcov.info`
---
## See Also
- [COVERAGE_REPORT.md](../../COVERAGE_REPORT.md) - Full coverage analysis
- [Jest Documentation](https://jestjs.io/docs/configuration#collectcoverage-boolean)
- [Istanbul Documentation](https://istanbul.js.org/)
@@ -1,153 +0,0 @@
#!/usr/bin/env node
const fs = require('fs')
const path = require('path')
const [, , inFile = 'coverage/lcov.info', outDir = 'coverage/html'] = process.argv
if (!fs.existsSync(inFile)) {
console.error('Input lcov not found:', inFile)
process.exit(1)
}
const lcovParse = require('lcov-parse')
const libCoverage = require('istanbul-lib-coverage')
const reports = require('istanbul-reports')
const libReport = require('istanbul-lib-report')
const data = fs.readFileSync(inFile, 'utf8')
// build repo file index to resolve source files
const root = process.cwd()
const ignoreDirs = new Set(['node_modules', '.git', 'coverage', 'lib', 'dist', 'types', '.rush', 'temp', 'pnpm-store'])
const repoFiles = []
function walk(dir) {
const items = fs.readdirSync(dir, { withFileTypes: true })
for (const it of items) {
if (it.isDirectory()) {
if (ignoreDirs.has(it.name)) continue
if (it.name.startsWith('.')) continue
try {
walk(path.join(dir, it.name))
} catch (e) {}
} else if (it.isFile()) {
repoFiles.push(path.join(dir, it.name))
}
}
}
try {
walk(root)
} catch (e) {}
lcovParse(data, (err, parsed) => {
if (err) {
console.error('lcov-parse error:', err)
process.exit(1)
}
const map = libCoverage.createCoverageMap({})
for (const file of parsed) {
// parsed entries include 'file', 'lines', 'functions', 'branches'
const coverage = {
path: file.file,
statementMap: {},
fnMap: {},
branchMap: {},
s: {},
f: {},
b: {}
}
// The lcov parser gives line coverage data; create synthetic statement entries per line
if (file.lines && file.lines.details) {
let idx = 0
for (const d of file.lines.details) {
idx++
const key = String(idx)
coverage.statementMap[key] = { start: { line: d.line, column: 0 }, end: { line: d.line, column: 0 } }
coverage.s[key] = d.hit
}
}
// functions and branches are ignored for more accurate tools; keep minimal
map.addFileCoverage(coverage)
}
// custom source finder: try absolute, repo-relative, and suffix matches
const sourceFinder = (filePath) => {
try {
if (!global.__seenPaths) global.__seenPaths = []
if (global.__seenPaths.length < 500) global.__seenPaths.push(filePath)
if (global.__seenPaths.length === 500 && !global.__seenLogged) {
console.error('sourceFinder seen paths (sample):\n', global.__seenPaths.join('\n'))
global.__seenLogged = true
}
if (global.__seenPaths.length <= 200) console.error('sourceFinder request:', filePath)
} catch (e) {}
try {
if (path.isAbsolute(filePath) && fs.existsSync(filePath)) return fs.readFileSync(filePath, 'utf8')
const abs1 = path.resolve(root, filePath)
if (fs.existsSync(abs1)) return fs.readFileSync(abs1, 'utf8')
// try suffix match
const found = repoFiles.find((p) => p.endsWith(path.sep + filePath) || p.endsWith(filePath))
if (found) return fs.readFileSync(found, 'utf8')
// debug unresolved
if (!found) {
try {
if (!global.__unresolved) global.__unresolved = new Set()
if (global.__unresolved.size < 200) global.__unresolved.add(filePath)
} catch (e) {}
}
} catch (e) {
// ignore and return null below
}
return null
}
const context = libReport.createContext({ dir: outDir, coverageMap: map, sourceFinder })
const report = reports.create('html', {})
report.execute(context)
if (global.__unresolved && global.__unresolved.size) {
console.error('Unresolved filePath samples:\n', Array.from(global.__unresolved).slice(0, 50).join('\n'))
}
console.log('HTML report generated in', outDir)
// Post-process HTML files: if any report page contains the 'Unable to lookup source' placeholder,
// replace it with the actual source file contents when we can resolve it.
try {
for (const file of parsed) {
const srcAbs = file.file
// normalize key as used by report (from last '/src/' onward) if present
let key
const idx = srcAbs.lastIndexOf(path.sep + 'src' + path.sep)
if (idx !== -1) key = srcAbs.slice(idx + 1)
else key = path.basename(srcAbs)
const htmlPath = path.join(outDir, key + '.html')
if (!fs.existsSync(htmlPath)) continue
let html = fs.readFileSync(htmlPath, 'utf8')
if (!html.includes('Unable to lookup source')) continue
// read source
let src
try {
src = fs.readFileSync(srcAbs, 'utf8')
} catch (e) {
src = null
}
if (!src) {
// try suffix match in repoFiles
const found = repoFiles.find((p) => p.endsWith(path.sep + key) || p.endsWith(key))
if (found) src = fs.readFileSync(found, 'utf8')
}
if (!src) continue
// escape HTML
const esc = src.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
// replace the first prettyprint <pre>...</pre> block that contains 'Unable to lookup source'
html = html.replace(
/<pre class="prettyprint[\s\S]*?>[\s\S]*?Unable to lookup source:[\s\S]*?<\/pre>/,
`<pre class="prettyprint lang-js">${esc}</pre>`
)
fs.writeFileSync(htmlPath, html, 'utf8')
}
} catch (e) {
console.error('post-process html error', e)
}
})
@@ -1,31 +0,0 @@
// 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
@@ -1,218 +0,0 @@
// 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__ = ({
/***/ 16928:
/*!***********************!*\
!*** external "path" ***!
\***********************/
/***/ ((module) => {
module.exports = require("path");
/***/ }),
/***/ 179896:
/*!*********************!*\
!*** external "fs" ***!
\*********************/
/***/ ((module) => {
module.exports = require("fs");
/***/ })
/******/ });
/************************************************************************/
/******/ // 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 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 ***!
\************************************************/
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! path */ 16928);
/* 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 */ 179896);
/* 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} <command> [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
@@ -1,31 +0,0 @@
// 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
@@ -1,778 +0,0 @@
// 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__ = ({
/***/ 16928:
/*!***********************!*\
!*** external "path" ***!
\***********************/
/***/ ((module) => {
module.exports = require("path");
/***/ }),
/***/ 179896:
/*!*********************!*\
!*** external "fs" ***!
\*********************/
/***/ ((module) => {
module.exports = require("fs");
/***/ }),
/***/ 370857:
/*!*********************!*\
!*** external "os" ***!
\*********************/
/***/ ((module) => {
module.exports = require("os");
/***/ }),
/***/ 535317:
/*!********************************!*\
!*** external "child_process" ***!
\********************************/
/***/ ((module) => {
module.exports = require("child_process");
/***/ }),
/***/ 832286:
/*!************************************************!*\
!*** ./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 */ trimNpmrcFileLines: () => (/* binding */ trimNpmrcFileLines)
/* harmony export */ });
/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! fs */ 179896);
/* 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 */ 16928);
/* 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, supportEnvVarFallbackSyntax } = 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 = trimNpmrcFileLines(npmrcFileLines, process.env, supportEnvVarFallbackSyntax);
const combinedNpmrc = resultLines.join('\n');
//save the cache
_combinedNpmrcMap.set(sourceNpmrcPath, combinedNpmrc);
return combinedNpmrc;
}
/**
*
* @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}`
* @returns
*/
function trimNpmrcFileLines(npmrcFileLines, env, supportEnvVarFallbackSyntax) {
var _a;
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
*
* ${nameString} -> nameString
* ${nameString-fallbackString} -> name-fallbackString
* ${nameString:-fallbackString} -> name:-fallbackString
*/
const nameWithFallback = token.substring(2, token.length - 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(/^([^:-]+)(?:\:?-(.+))?$/);
// matched: [originStr, variableName, fallback]
environmentVariableName = (_a = matched === null || matched === void 0 ? void 0 : matched[1]) !== null && _a !== void 0 ? _a : nameWithFallback;
fallback = matched === null || matched === void 0 ? void 0 : matched[2];
}
else {
environmentVariableName = nameWithFallback;
}
// Is the environment variable and fallback value defined.
if (!env[environmentVariableName] && !fallback) {
// 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);
}
}
return resultLines;
}
function _copyAndTrimNpmrcFile(options) {
const { logger, sourceNpmrcPath, targetNpmrcPath } = options;
logger.info(`Transforming ${sourceNpmrcPath}`); // Verbose
logger.info(` --> "${targetNpmrcPath}"`);
const combinedNpmrc = _trimNpmrcFile(options);
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 } = 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,
...options
});
}
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, supportEnvVarFallbackSyntax) {
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, supportEnvVarFallbackSyntax });
const variableKeyRegExp = new RegExp(`^${variableKey}=`, 'm');
return trimmedNpmrcFile.match(variableKeyRegExp) !== null;
}
//# sourceMappingURL=npmrcUtilities.js.map
/***/ })
/******/ });
/************************************************************************/
/******/ // 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 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 ***!
\*******************************************/
__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 */ 535317);
/* 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 */ 179896);
/* 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 */ 370857);
/* 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 */ 16928);
/* 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 */ 832286);
// 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,
supportEnvVarFallbackSyntax: false
});
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,
supportEnvVarFallbackSyntax: false
});
_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 <package>@<version> <command> [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
@@ -1,106 +0,0 @@
#!/usr/bin/env node
const fs = require('fs')
const path = require('path')
const root = process.cwd()
const patterns = ['packages', 'pods', 'tests']
let files = []
for (const p of patterns) {
const dir = path.join(root, p)
if (!fs.existsSync(dir)) continue
const items = fs.readdirSync(dir)
for (const it of items) {
const lcov = path.join(dir, it, 'coverage', 'lcov.info')
if (fs.existsSync(lcov)) files.push(lcov)
}
}
if (files.length === 0) {
console.error('No lcov files found in packages/pods/tests/*/coverage/lcov.info')
process.exit(1)
}
const outDir = path.join(root, 'coverage')
if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true })
const outFile = path.join(outDir, 'lcov.info')
let outData = ''
let seenTN = false
// Build a repo file index to help resolve SF entries that are ambiguous
const ignoreDirs = new Set(['node_modules', '.git', 'coverage', 'lib', 'dist', 'types', '.rush', 'temp', 'pnpm-store'])
const repoFiles = []
function walk(dir) {
const items = fs.readdirSync(dir, { withFileTypes: true })
for (const it of items) {
if (it.isDirectory()) {
if (ignoreDirs.has(it.name)) continue
// skip hidden folders except top-level .config maybe
if (it.name.startsWith('.')) continue
try {
walk(path.join(dir, it.name))
} catch (e) {
// ignore permission errors
}
} else if (it.isFile()) {
repoFiles.push(path.join(dir, it.name))
}
}
}
try {
walk(root)
} catch (e) {
/* ignore */
}
for (const f of files) {
const data = fs.readFileSync(f, 'utf8')
const pkgDir = path.dirname(path.dirname(f))
const lines = data.split(/\r?\n/)
const outLines = []
for (const line of lines) {
if (!line) continue
// skip duplicate TN: headers (test name)
if (line.startsWith('TN:')) {
if (seenTN) continue
seenTN = true
outLines.push(line)
continue
}
if (line.startsWith('SF:')) {
const orig = line.slice(3)
// if path is absolute and exists, keep it; otherwise resolve from package dir
if (path.isAbsolute(orig)) {
outLines.push('SF:' + orig)
continue
}
const abs = path.resolve(pkgDir, orig)
if (fs.existsSync(abs)) {
outLines.push('SF:' + abs)
} else {
// try package/src/orig if orig is not already prefixed with src
const alt = path.resolve(pkgDir, orig)
if (fs.existsSync(alt)) {
outLines.push('SF:' + path.relative(root, alt))
} else {
// try to find any file in repo that ends with the orig path
const found = repoFiles.find((p) => p.endsWith(path.sep + orig) || p.endsWith(orig))
if (found) {
outLines.push('SF:' + found)
} else {
// keep original if we can't resolve
outLines.push('SF:' + orig)
}
}
}
continue
}
outLines.push(line)
}
outData += outLines.join('\n') + '\n'
}
fs.writeFileSync(outFile, outData, 'utf8')
console.log('Merged', files.length, 'lcov files into', outFile)
-155
View File
@@ -1,155 +0,0 @@
{
"name": "@hcengineering/scripts",
"version": "0.7.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@hcengineering/scripts",
"version": "0.7.3",
"devDependencies": {
"@hcengineering/platform-rig": "^0.7.10",
"istanbul-lib-coverage": "^3.2.2",
"istanbul-lib-report": "^3.0.1",
"istanbul-reports": "^3.2.0",
"lcov-parse": "^1.0.0"
}
},
"../temp/node_modules/.pnpm/@hcengineering+platform-rig@0.7.10/node_modules/@hcengineering/platform-rig": {
"version": "0.7.10",
"dev": true,
"dependencies": {
"@typescript-eslint/eslint-plugin": "^6.11.0",
"@typescript-eslint/parser": "^6.11.0",
"esbuild": "^0.25.9",
"esbuild-plugin-copy": "^2.1.1",
"eslint": "^8.54.0",
"eslint-config-standard-with-typescript": "^40.0.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-n": "^15.4.0",
"eslint-plugin-promise": "^6.1.1",
"prettier": "^3.1.0",
"typescript": "^5.9.3"
},
"bin": {
"bump-changes-from-tag": "bin/bump-changes-from-tag.js",
"bump-package-version": "bin/bump-package-version.js",
"compile": "bin/compile.js",
"do-svelte-check": "bin/do-svelte-check.js",
"format": "bin/format.js"
}
},
"node_modules/@hcengineering/platform-rig": {
"resolved": "../temp/node_modules/.pnpm/@hcengineering+platform-rig@0.7.10/node_modules/@hcengineering/platform-rig",
"link": true
},
"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==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/html-escaper": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
"dev": true,
"license": "MIT"
},
"node_modules/istanbul-lib-coverage": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz",
"integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=8"
}
},
"node_modules/istanbul-lib-report": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz",
"integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"istanbul-lib-coverage": "^3.0.0",
"make-dir": "^4.0.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/istanbul-reports": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz",
"integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"html-escaper": "^2.0.0",
"istanbul-lib-report": "^3.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/lcov-parse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-1.0.0.tgz",
"integrity": "sha512-aprLII/vPzuQvYZnDRU78Fns9I2Ag3gi4Ipga/hxnVMCZC8DnR2nI7XBqrPoywGfxqIx/DgarGvDJZAD3YBTgQ==",
"dev": true,
"license": "BSD-3-Clause",
"bin": {
"lcov-parse": "bin/cli.js"
}
},
"node_modules/make-dir": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz",
"integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==",
"dev": true,
"license": "MIT",
"dependencies": {
"semver": "^7.5.3"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/semver": {
"version": "7.7.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
"dev": true,
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
"engines": {
"node": ">=8"
}
}
}
}
@@ -1,27 +0,0 @@
{
"name": "@hcengineering/scripts",
"version": "0.7.14",
"scripts": {
"format": "echo \"No format specified\"",
"coverage:merge": "node merge-coverage.js",
"coverage:html": "node generate-coverage-html.js coverage/lcov.info coverage/html",
"coverage:summary": "node show-coverage-summary.js",
"test:coverage": "node run-tests-with-coverage.js"
},
"devDependencies": {
"@hcengineering/platform-rig": "workspace:^0.7.19",
"istanbul-lib-coverage": "^3.2.2",
"istanbul-lib-report": "^3.0.1",
"istanbul-reports": "^3.2.0",
"lcov-parse": "^1.0.0",
"@typescript-eslint/eslint-plugin": "^6.21.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-n": "^15.4.0",
"eslint": "^8.54.0",
"@typescript-eslint/parser": "^6.21.0",
"eslint-config-standard-with-typescript": "^40.0.0",
"eslint-plugin-svelte": "^2.35.1"
},
"private": true
}
@@ -1,116 +0,0 @@
#!/usr/bin/env node
/**
* Run tests with coverage for all packages
* This is a replacement for show-coverage.sh
*/
const fs = require('fs')
const path = require('path')
const { spawn } = require('child_process')
const root = process.cwd()
const packagesDir = path.join(root, 'packages')
if (!fs.existsSync(packagesDir)) {
console.error('Error: packages directory not found')
process.exit(1)
}
console.log('=== RUNNING TESTS WITH COVERAGE ===')
console.log('')
const packages = fs
.readdirSync(packagesDir, { withFileTypes: true })
.filter((dirent) => dirent.isDirectory())
.map((dirent) => dirent.name)
.sort()
async function runTestsForPackage(pkgName) {
const pkgDir = path.join(packagesDir, pkgName)
const packageJson = path.join(pkgDir, 'package.json')
if (!fs.existsSync(packageJson)) {
return null
}
const pkg = JSON.parse(fs.readFileSync(packageJson, 'utf8'))
if (!pkg.scripts || !pkg.scripts.test) {
return null
}
console.log(`📦 Package: ${pkgName}`)
console.log('---')
return new Promise((resolve) => {
const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm'
const child = spawn(npm, ['test', '--', '--coverage', '--silent'], {
cwd: pkgDir,
stdio: 'pipe',
shell: true
})
let output = ''
let inSummary = false
child.stdout.on('data', (data) => {
const text = data.toString()
output += text
// Extract coverage summary
const lines = text.split('\n')
for (const line of lines) {
if (line.includes('Coverage summary') || line.includes('----------')) {
inSummary = true
}
if (
inSummary &&
(line.includes('Statements') ||
line.includes('Branches') ||
line.includes('Functions') ||
line.includes('Lines'))
) {
console.log(line.trim())
}
if (inSummary && line.trim() === '') {
inSummary = false
}
}
})
child.stderr.on('data', (data) => {
// Ignore stderr for cleaner output
})
child.on('close', (code) => {
console.log('')
resolve({ package: pkgName, code, output })
})
})
}
async function main() {
const results = []
for (const pkg of packages) {
const result = await runTestsForPackage(pkg)
if (result) {
results.push(result)
}
}
console.log('=== END OF COVERAGE REPORT ===')
console.log('')
console.log(`Tested ${results.length} packages`)
const failed = results.filter((r) => r.code !== 0)
if (failed.length > 0) {
console.log(`⚠️ ${failed.length} package(s) had test failures:`)
failed.forEach((r) => console.log(` - ${r.package}`))
}
}
main().catch((err) => {
console.error('Error:', err)
process.exit(1)
})
@@ -1,117 +0,0 @@
#!/usr/bin/env node
/**
* Display coverage summary from lcov.info file
* Usage: node show-coverage-summary.js [lcov-file-path]
*/
const fs = require('fs')
const path = require('path')
const lcovFile = process.argv[2] || 'coverage/lcov.info'
const root = process.cwd()
const lcovPath = path.isAbsolute(lcovFile) ? lcovFile : path.join(root, lcovFile)
if (!fs.existsSync(lcovPath)) {
console.error(`Error: LCOV file not found: ${lcovPath}`)
process.exit(1)
}
console.log('==============================================')
console.log('COVERAGE SUMMARY BY PACKAGE')
console.log('==============================================')
console.log('')
const data = fs.readFileSync(lcovPath, 'utf8')
const lines = data.split(/\r?\n/)
const fileStats = new Map()
const filePkg = new Map()
let currentFile = ''
// Parse lcov.info
for (const line of lines) {
if (line.startsWith('SF:')) {
currentFile = line.substring(3)
fileStats.set(currentFile, { total: 0, covered: 0 })
// Extract package name from path
const parts = currentFile.split(path.sep)
const pkgIndex = parts.indexOf('packages')
if (pkgIndex !== -1 && pkgIndex + 1 < parts.length) {
filePkg.set(currentFile, parts[pkgIndex + 1])
}
} else if (line.startsWith('DA:')) {
const [lineNum, hitCount] = line.substring(3).split(',')
const stats = fileStats.get(currentFile)
if (stats) {
stats.total++
if (parseInt(hitCount) > 0) {
stats.covered++
}
}
}
}
// Aggregate by package
const pkgStats = new Map()
for (const [file, stats] of fileStats) {
const pkg = filePkg.get(file)
if (pkg) {
if (!pkgStats.has(pkg)) {
pkgStats.set(pkg, { total: 0, covered: 0 })
}
const pkgStat = pkgStats.get(pkg)
pkgStat.total += stats.total
pkgStat.covered += stats.covered
}
}
// Sort packages alphabetically
const sortedPackages = Array.from(pkgStats.keys()).sort()
// Display header
console.log(padRight('Package', 25) + padLeft('Covered', 10) + padLeft('Total', 10) + padLeft('Coverage', 10))
console.log('----------------------------------------------')
// Display package stats
let overallCovered = 0
let overallTotal = 0
for (const pkg of sortedPackages) {
const stats = pkgStats.get(pkg)
const pct = (stats.covered / stats.total) * 100
console.log(
padRight(pkg, 25) +
padLeft(stats.covered.toString(), 10) +
padLeft(stats.total.toString(), 10) +
padLeft(pct.toFixed(2) + '%', 10)
)
overallCovered += stats.covered
overallTotal += stats.total
}
// Display total
console.log('----------------------------------------------')
const overallPct = (overallCovered / overallTotal) * 100
console.log(
padRight('TOTAL', 25) +
padLeft(overallCovered.toString(), 10) +
padLeft(overallTotal.toString(), 10) +
padLeft(overallPct.toFixed(2) + '%', 10)
)
console.log('')
console.log('==============================================')
console.log('')
console.log('HTML report available at: coverage/html/index.html')
console.log(`Merged LCOV file available at: ${lcovFile}`)
// Helper functions
function padRight(str, width) {
return str + ' '.repeat(Math.max(0, width - str.length))
}
function padLeft(str, width) {
return ' '.repeat(Math.max(0, width - str.length)) + str
}
@@ -1,76 +0,0 @@
#!/bin/bash
# Script to display coverage summary from lcov.info file
LCOV_FILE="${1:-coverage/lcov.info}"
if [ ! -f "$LCOV_FILE" ]; then
echo "Error: LCOV file not found: $LCOV_FILE"
exit 1
fi
echo "=============================================="
echo "COVERAGE SUMMARY BY PACKAGE"
echo "=============================================="
echo ""
# Parse lcov.info and aggregate by package
awk '
BEGIN {
current_file = "";
}
/^SF:/ {
current_file = $0;
sub(/^SF:/, "", current_file);
# Extract package name from path
split(current_file, parts, "/");
pkg = "";
for (i=1; i<=length(parts); i++) {
if (parts[i] == "packages" && i+1 <= length(parts)) {
pkg = parts[i+1];
break;
}
}
total_lines[current_file] = 0;
covered_lines[current_file] = 0;
file_pkg[current_file] = pkg;
}
/^DA:/ {
split($0, parts, ",");
total_lines[current_file]++;
if (parts[2] > 0) covered_lines[current_file]++;
}
END {
# Aggregate by package
for (file in total_lines) {
pkg = file_pkg[file];
if (pkg != "") {
pkg_total[pkg] += total_lines[file];
pkg_covered[pkg] += covered_lines[file];
}
}
printf "%-25s %10s %10s %10s\n", "Package", "Covered", "Total", "Coverage";
print "----------------------------------------------";
overall_covered = 0;
overall_total = 0;
# Display packages
for (pkg in pkg_total) {
pct = (pkg_covered[pkg] / pkg_total[pkg]) * 100;
printf "%-25s %10d %10d %9.2f%%\n", pkg, pkg_covered[pkg], pkg_total[pkg], pct;
overall_covered += pkg_covered[pkg];
overall_total += pkg_total[pkg];
}
print "----------------------------------------------";
overall_pct = (overall_covered / overall_total) * 100;
printf "%-25s %10d %10d %9.2f%%\n", "TOTAL", overall_covered, overall_total, overall_pct;
print "";
}' "$LCOV_FILE"
echo "=============================================="
echo ""
echo "HTML report available at: coverage/html/index.html"
echo "Merged LCOV file available at: $LCOV_FILE"
@@ -1,27 +0,0 @@
#!/bin/bash
echo "=== FINAL COVERAGE REPORT ==="
echo ""
# Iterate through each package directory
for pkg in packages/*/; do
pkgname=$(basename "$pkg")
echo "📦 Package: $pkgname"
echo "---"
# Change to package directory
cd "$pkg" || continue
# Run tests with coverage and extract summary
npm test -- --coverage --silent 2>&1 | \
grep -A 4 "Coverage summary" | \
grep -E "Statements|Branches|Functions|Lines"
# Return to root directory
cd ../.. || exit
echo ""
done
echo "=== END OF COVERAGE REPORT ==="