@diplodoc/lint 1.12.0 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -85,6 +85,7 @@ Initializes linting in a package:
85
85
  Updates configuration files to the latest versions:
86
86
 
87
87
  - Updates `.eslintrc.js`, `.prettierrc.js`, `.stylelintrc.js`
88
+ - Updates/copies GitHub Actions workflows (including `sonarcloud.yml`, `coverage.yml`) and `sonar-project.properties` (with `{{PACKAGE_NAME}}` substitution)
88
89
  - Updates ignore files with new patterns
89
90
  - **Does not** re-initialize Husky
90
91
  - **Does not** modify existing scripts in `package.json`
@@ -224,6 +225,20 @@ build({
224
225
  - **Import**: Use `@diplodoc/lint/esbuild` — it re-exports the full esbuild API (ESM and CJS).
225
226
  - **Why**: Aligns esbuild version and avoids duplicate native bindings; add `@diplodoc/lint` as a devDependency and use this export instead of installing `esbuild` directly in the package.
226
227
 
228
+ ### SonarCloud
229
+
230
+ Scaffolding provides optional SonarCloud integration for code quality and coverage:
231
+
232
+ - **`sonar-project.properties`** — copied to the package root; the placeholder `{{PACKAGE_NAME}}` is replaced with the package name **without scope** (e.g. `@diplodoc/foo` → `foo`) so each repo has a unique SonarCloud project key.
233
+ - **`.github/workflows/sonarcloud.yml`** — runs SonarCloud analysis on push/PR to `master`/`main`. The scan runs only when the package has a `test:coverage` script and coverage was generated; otherwise the job skips the scan.
234
+ - **`.github/workflows/coverage.yml`** — optional workflow that runs `test:coverage` when the script exists (see above); does not block merging.
235
+
236
+ To enable SonarCloud for a repository:
237
+
238
+ 1. Add the repository in [SonarCloud](https://sonarcloud.io) (organization `diplodoc-platform`).
239
+ 2. Add the **SONAR_TOKEN** secret in the GitHub repo settings.
240
+ 3. Optionally add a `test:coverage` script (e.g. `vitest run --coverage`) so the SonarCloud workflow can upload coverage.
241
+
227
242
  ## Metapackage vs Standalone Usage
228
243
 
229
244
  The package works in two modes:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diplodoc/lint",
3
- "version": "1.12.0",
3
+ "version": "1.13.0",
4
4
  "description": "Diplodoc platform internal utility set for linting",
5
5
  "bin": {
6
6
  "lint": "./bin/lint.js",
@@ -0,0 +1,63 @@
1
+ # ⚠️ IMPORTANT: This workflow is a template from diplodoc/lint/scaffolding
2
+ # Do not edit this file in other packages - it is automatically copied from here.
3
+ # To modify this workflow, edit it in diplodoc/devops/lint/scaffolding/.github/workflows/
4
+ # and then run the scaffolding update process.
5
+ #
6
+ # This workflow is optional: it does not block merging even if it fails (continue-on-error).
7
+
8
+ name: Coverage
9
+
10
+ on:
11
+ push:
12
+ branches:
13
+ - master
14
+ - main
15
+ pull_request:
16
+ branches:
17
+ - master
18
+ - main
19
+ workflow_dispatch:
20
+
21
+ jobs:
22
+ coverage:
23
+ name: Test coverage
24
+ runs-on: ubuntu-latest
25
+ continue-on-error: true
26
+ steps:
27
+ - name: Checkout repository
28
+ uses: actions/checkout@v4
29
+ with:
30
+ fetch-depth: 0
31
+
32
+ - name: Setup Node.js
33
+ uses: actions/setup-node@v4
34
+ with:
35
+ node-version: 22
36
+ cache: 'npm'
37
+
38
+ - name: Install latest npm (>= 11.5.1)
39
+ run: |
40
+ [ "$(printf '%s\n' "11.5.1" "$(npm -v)" | sort -V | head -n1)" = "11.5.1" ] || npm install -g npm@latest
41
+ shell: bash
42
+
43
+ - name: Install dependencies
44
+ run: npm ci
45
+
46
+ - name: Run tests with coverage
47
+ id: coverage-run
48
+ run: |
49
+ if node -e "const s=require('./package.json').scripts; process.exit(s && s['test:coverage'] ? 0 : 1)"; then
50
+ npm run test:coverage
51
+ echo "ran=true" >> "$GITHUB_OUTPUT"
52
+ else
53
+ echo "test:coverage script not found, skipping."
54
+ echo "ran=false" >> "$GITHUB_OUTPUT"
55
+ fi
56
+
57
+ - name: Upload coverage artifact
58
+ if: success() && steps.coverage-run.outputs.ran == 'true'
59
+ uses: actions/upload-artifact@v4
60
+ with:
61
+ name: coverage
62
+ path: coverage/
63
+ retention-days: 7
@@ -0,0 +1,45 @@
1
+ name: SonarCloud Analysis
2
+
3
+ on:
4
+ push:
5
+ branches: [master, main]
6
+ pull_request:
7
+ branches: [master, main]
8
+ types: [opened, synchronize, reopened]
9
+
10
+ jobs:
11
+ sonarcloud:
12
+ name: SonarCloud Analysis
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - name: Checkout code
16
+ uses: actions/checkout@v4
17
+ with:
18
+ fetch-depth: 0 # Shallow clones should be disabled for better analysis
19
+
20
+ - name: Setup Node.js
21
+ uses: actions/setup-node@v4
22
+ with:
23
+ node-version: 22.x
24
+ cache: 'npm'
25
+
26
+ - name: Install dependencies
27
+ run: npm ci
28
+
29
+ - name: Run tests with coverage
30
+ id: coverage-run
31
+ run: |
32
+ if node -e "const s=require('./package.json').scripts; process.exit(s && s['test:coverage'] ? 0 : 1)"; then
33
+ npm run test:coverage
34
+ echo "ran=true" >> "$GITHUB_OUTPUT"
35
+ else
36
+ echo "test:coverage script not found, skipping."
37
+ echo "ran=false" >> "$GITHUB_OUTPUT"
38
+ fi
39
+
40
+ - name: SonarCloud Scan
41
+ if: success() && steps.coverage-run.outputs.ran == 'true'
42
+ uses: SonarSource/sonarqube-scan-action@v7.0.0
43
+ env:
44
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
45
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
@@ -0,0 +1,27 @@
1
+ # SonarCloud project configuration
2
+ # This file will be generated/updated by SonarCloud when you connect your repository
3
+
4
+ # Project identification
5
+ # Note: sonar.organization can also be set via environment variable SONAR_ORGANIZATION
6
+ # or via GitHub Actions workflow. If not set here, it must be provided in the workflow.
7
+ sonar.organization=diplodoc-platform
8
+ sonar.projectKey=diplodoc-platform_{{package}}
9
+ sonar.projectName={{package}}
10
+
11
+ # Source code location
12
+ sonar.sources=src
13
+ sonar.exclusions=**/*.spec.ts,**/*.test.ts,**/__tests__/**
14
+
15
+ # Test code location
16
+ sonar.tests=src
17
+ sonar.test.inclusions=**/*.spec.ts,**/*.test.ts,**/__tests__/**
18
+
19
+ # Coverage reports
20
+ sonar.javascript.lcov.reportPaths=coverage/lcov.info
21
+ sonar.typescript.lcov.reportPaths=coverage/lcov.info
22
+
23
+ # TypeScript configuration
24
+ sonar.typescript.tsconfigPath=tsconfig.json
25
+
26
+ # Encoding
27
+ sonar.sourceEncoding=UTF-8
@@ -1,5 +1,5 @@
1
1
  const {join, relative, dirname} = require('node:path');
2
- const {readdirSync, copyFileSync, mkdirSync, existsSync, realpathSync} = require('node:fs');
2
+ const {readdirSync, readFileSync, writeFileSync, copyFileSync, mkdirSync, existsSync, realpathSync} = require('node:fs');
3
3
 
4
4
  // Determine package root directory
5
5
  // Try multiple strategies to find the package root
@@ -32,6 +32,41 @@ if (!existsSync(srcDir)) {
32
32
 
33
33
  const targetDir = process.cwd();
34
34
 
35
+ /** @type {{ PACKAGE_NAME: string }} Variables for scaffolding template substitution */
36
+ let scaffoldVars = {PACKAGE_NAME: 'package'};
37
+ const packageJsonPath = join(targetDir, 'package.json');
38
+ if (existsSync(packageJsonPath)) {
39
+ try {
40
+ const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
41
+ const name = pkg.name && typeof pkg.name === 'string' ? pkg.name : '';
42
+ scaffoldVars.PACKAGE_NAME = name.replace(/^@[^/]+\//, '') || 'package';
43
+ } catch {
44
+ // leave default
45
+ }
46
+ }
47
+
48
+ function applyTemplate(content) {
49
+ if (typeof content !== 'string' || !content.includes('{{')) {
50
+ return content;
51
+ }
52
+ return content.replace(/\{\{PACKAGE_NAME\}\}/g, scaffoldVars.PACKAGE_NAME);
53
+ }
54
+
55
+ function copyFileWithSubstitution(srcPath, targetPath) {
56
+ let content;
57
+ try {
58
+ content = readFileSync(srcPath, 'utf8');
59
+ } catch {
60
+ copyFileSync(srcPath, targetPath);
61
+ return;
62
+ }
63
+ if (content.includes('{{PACKAGE_NAME}}')) {
64
+ writeFileSync(targetPath, applyTemplate(content), 'utf8');
65
+ } else {
66
+ writeFileSync(targetPath, content, 'utf8');
67
+ }
68
+ }
69
+
35
70
  // Verify scaffolding directory exists
36
71
  if (!existsSync(srcDir)) {
37
72
  console.error(`[@diplodoc/lint] Error: scaffolding directory not found at ${srcDir}`);
@@ -74,9 +109,9 @@ function copyScaffoldingFiles(excludePatterns = []) {
74
109
  if (!existsSync(targetParent)) {
75
110
  mkdirSync(targetParent, {recursive: true});
76
111
  }
77
- // Force overwrite existing files
112
+ // Force overwrite existing files (with optional {{PACKAGE_NAME}} substitution)
78
113
  try {
79
- copyFileSync(srcPath, targetPath);
114
+ copyFileWithSubstitution(srcPath, targetPath);
80
115
  } catch (error) {
81
116
  console.error(`[@diplodoc/lint] Error copying ${srcPath} to ${targetPath}:`, error.message);
82
117
  throw error;
@@ -105,7 +140,7 @@ function copyWorkflows() {
105
140
  if (entry.isFile()) {
106
141
  const srcPath = join(workflowsSrc, entry.name);
107
142
  const targetPath = join(workflowsTarget, entry.name);
108
- copyFileSync(srcPath, targetPath);
143
+ copyFileWithSubstitution(srcPath, targetPath);
109
144
  }
110
145
  }
111
146
  }