mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-22 17:45:02 +02:00
remove to prepare for subtree
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
@@ -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, '&').replace(/</g, '<').replace(/>/g, '>')
|
||||
// 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
@@ -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 ==="
|
||||
Reference in New Issue
Block a user