@ovh-ux/manager-static-analysis-kit 0.10.0 → 0.11.1

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.
@@ -1,143 +1,106 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
1
  import process from 'node:process';
4
2
 
5
3
  import { codeDuplicationConfig } from '../../dist/configs/code-duplication-config.js';
6
4
  import { typesCoverageConfig } from '../../dist/configs/types-coverage-config.js';
7
- import { isReactApp } from './apps-utils.js';
8
- import { logError, logInfo, logWarn } from './log-utils.js';
5
+ import { logError, logInfo } from './log-utils.js';
6
+ import { discoverModules, getModule } from './module-utils.js';
9
7
 
10
8
  /**
11
- * Extract an app folder name from an OVH Manager package name.
9
+ * Parse CLI arguments to resolve target modules (apps, packages, or libraries).
12
10
  *
13
- * Example:
14
- * "@ovh-ux/manager-zimbra-app" → "zimbra"
11
+ * This function looks for one of the supported flags in `process.argv`:
12
+ * - `--apps` or `--app`
13
+ * - `--packages` or `--package`
14
+ * - `--libraries` or `--library`
15
15
  *
16
- * @param {string} packageName - Full npm package name.
17
- * @returns {string|null} The extracted app folder name, or null if the format is invalid.
18
- */
19
- function extractAppFolderFromPackageName(packageName) {
20
- const match = packageName.match(/^@ovh-ux\/manager-(.+)-app$/);
21
- if (!match) {
22
- logError(`❌ Invalid package name format: ${packageName}`);
23
- return null;
24
- }
25
- return match[1];
26
- }
27
-
28
- /**
29
- * Parse CLI arguments for `--apps` or `--app` flags.
16
+ * Each flag must be followed by a comma-separated list of module identifiers.
17
+ * Each identifier is resolved using {@link getModule}, which returns a module descriptor.
30
18
  *
31
- * Accepts a comma-separated list of app folder names. Example:
32
- * `--apps zimbra,web`
19
+ * For every valid pattern:
20
+ * - The corresponding module descriptor (containing `fullPath`, `shortPath`, and `packageName`)
21
+ * is added to the result list.
22
+ * - If a module cannot be resolved, an error is logged and it is skipped.
33
23
  *
34
- * @param {string} appsDir - Absolute path to the apps root directory.
35
- * @returns {{appFolders: string[], packageNames: string[]} | null}
36
- * Object containing valid app folders and an empty `packageNames` array,
37
- * or null if no `--apps`/`--app` flag is provided.
38
- */
39
- function parseAppsArg(appsDir) {
40
- const appsArg = process.argv.find((arg) => arg === '--apps' || arg === '--app');
41
- if (!appsArg) return null;
42
-
43
- const index = process.argv.indexOf(appsArg);
44
- const rawValue = process.argv[index + 1] || '';
45
- const appFolders = rawValue
46
- .split(',')
47
- .map((n) => n.trim())
48
- .filter(Boolean);
49
-
50
- const validFolders = appFolders.filter((folder) => {
51
- if (!fs.existsSync(path.join(appsDir, folder))) {
52
- logError(`❌ App not found: ${folder} → skipping`);
53
- return false;
54
- }
55
- return true;
56
- });
57
-
58
- logInfo(`Running in apps mode for: ${validFolders.join(', ')}`);
59
- return { appFolders: validFolders, packageNames: [] };
60
- }
61
-
62
- /**
63
- * Parse CLI arguments for `--packages` or `--package` flags.
24
+ * @typedef {Object} ModuleDescriptor
25
+ * @property {string} fullPath - Absolute path to the module directory.
26
+ * @property {string} shortPath - Folder name (short identifier) of the module.
27
+ * @property {string} packageName - Full npm package name (e.g. "@ovh-ux/manager-core-shell").
64
28
  *
65
- * Accepts a comma-separated list of package names. Example:
66
- * `--packages @ovh-ux/manager-zimbra-app,@ovh-ux/manager-web-app`
29
+ * @returns {ModuleDescriptor[] | null}
30
+ * Array of resolved module descriptors, or `null` if no supported CLI flags were provided.
67
31
  *
68
- * Each package name is mapped to its corresponding app folder.
32
+ * @example
33
+ * // Example 1: Apps mode
34
+ * // CLI: --apps zimbra,web
35
+ * parseArgs();
36
+ * // → [
37
+ * // { fullPath: "/.../manager/apps/zimbra", shortPath: "zimbra", packageName: "@ovh-ux/manager-zimbra-app" },
38
+ * // { fullPath: "/.../manager/apps/web", shortPath: "web", packageName: "@ovh-ux/manager-web-app" }
39
+ * // ]
69
40
  *
70
- * @param {string} appsDir - Absolute path to the apps root directory.
71
- * @returns {{appFolders: string[], packageNames: string[]} | null}
72
- * Object mapping package names to discovered app folders,
73
- * or null if no `--packages`/`--package` flag is provided.
41
+ * @example
42
+ * // Example 2: Libraries mode
43
+ * // CLI: --libraries core-shell,react-components
44
+ * parseArgs();
45
+ * // → [
46
+ * // { fullPath: "/.../packages/manager/core/core-shell", shortPath: "core-shell", packageName: "@ovh-ux/manager-core-shell" },
47
+ * // { fullPath: "/.../packages/components/react-components", shortPath: "react-components", packageName: "@ovh-ux/manager-react-components" }
48
+ * // ]
74
49
  */
75
- function parsePackagesArg(appsDir) {
50
+ export function parseArgs() {
51
+ const libsArg = process.argv.find((arg) => arg === '--libraries' || arg === '--library');
76
52
  const packagesArg = process.argv.find((arg) => arg === '--packages' || arg === '--package');
77
- if (!packagesArg) return null;
53
+ const appsArg = process.argv.find((arg) => arg === '--apps' || arg === '--app');
54
+
55
+ const moduleModeArgs = libsArg || packagesArg || appsArg;
78
56
 
79
- const index = process.argv.indexOf(packagesArg);
57
+ if (!moduleModeArgs) return discoverModules(false);
58
+
59
+ const index = process.argv.indexOf(moduleModeArgs);
80
60
  const rawValue = process.argv[index + 1] || '';
81
- const packageNames = rawValue
61
+
62
+ const modulePatterns = rawValue
82
63
  .split(',')
83
64
  .map((n) => n.trim())
84
65
  .filter(Boolean);
85
66
 
86
- const mappedFolders = [];
87
- for (const pkgName of packageNames) {
88
- const folder = extractAppFolderFromPackageName(pkgName);
89
- if (folder && fs.existsSync(path.join(appsDir, folder))) {
90
- mappedFolders.push(folder);
67
+ /** @type {ModuleDescriptor[]} */
68
+ const modules = [];
69
+
70
+ for (const modulePattern of modulePatterns) {
71
+ const discoveredModule = getModule(modulePattern, !!libsArg);
72
+ if (discoveredModule) {
73
+ modules.push(discoveredModule);
91
74
  } else {
92
- logError(`❌ Package ${pkgName} → app folder ${folder} not found, skipping`);
75
+ logError(
76
+ `❌ Module ${modulePattern} → folder ${discoveredModule?.shortPath || 'unknown'} not found, skipping`,
77
+ );
93
78
  }
94
79
  }
95
80
 
96
81
  logInfo(
97
- `Running in packages mode for: ${packageNames.join(', ')} → apps: ${mappedFolders.join(', ')}`,
82
+ `patterns ${modulePatterns.join(', ')} → folders: ${modules
83
+ .map((item) => item.fullPath)
84
+ .join(', ')}`,
98
85
  );
99
- return { appFolders: mappedFolders, packageNames };
100
- }
101
86
 
102
- /**
103
- * Discover all React apps in the apps directory when no CLI flags are provided.
104
- *
105
- * Only apps that contain a `package.json` file and are identified as React apps
106
- * (via `isReactApp`) will be included.
107
- *
108
- * @param {string} appsDir - Absolute path to the apps root directory.
109
- * @returns {{appFolders: string[], packageNames: string[]}}
110
- * Object containing discovered app folders and an empty `packageNames` array.
111
- */
112
- function discoverApps(appsDir) {
113
- const discoveredAppFolders = fs.readdirSync(appsDir).filter((dir) => {
114
- const pkgPath = path.join(appsDir, dir, 'package.json');
115
- return fs.existsSync(pkgPath) && isReactApp(pkgPath);
116
- });
117
-
118
- if (discoveredAppFolders.length === 0) {
119
- logWarn('⚠️ No React apps found to analyze.');
120
- return { appFolders: [], packageNames: [] };
121
- }
122
-
123
- logInfo(`Running in auto-discovery mode (${discoveredAppFolders.length} apps).`);
124
- return { appFolders: discoveredAppFolders, packageNames: [] };
87
+ return modules;
125
88
  }
126
89
 
127
90
  /**
128
- * Parse CLI arguments to resolve which apps or packages should be analyzed.
91
+ * Alias for {@link parseArgs}, used for semantic clarity in CLI entrypoints.
92
+ *
93
+ * This function simply delegates to {@link parseArgs} and returns the same result.
129
94
  *
130
- * Priority order:
131
- * 1. Explicit `--apps` flag
132
- * 2. Explicit `--packages` flag
133
- * 3. Auto-discovery fallback
95
+ * @returns {ModuleDescriptor[] | null}
96
+ * Array of resolved module descriptors, or `null` if no valid flags were provided.
134
97
  *
135
- * @param {string} appsDir - Absolute path to the apps root directory.
136
- * @returns {{appFolders: string[], packageNames: string[]}}
137
- * Object containing app folder names and package names for analysis.
98
+ * @example
99
+ * parseCliTargets();
100
+ * // Equivalent to parseArgs(), typically used in CLI commands.
138
101
  */
139
- export function parseCliTargets(appsDir) {
140
- return parseAppsArg(appsDir) || parsePackagesArg(appsDir) || discoverApps(appsDir);
102
+ export function parseCliTargets() {
103
+ return parseArgs();
141
104
  }
142
105
 
143
106
  /**
@@ -0,0 +1,121 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+
4
+ import { appsDir, librariesBasePaths, staticDiscoveredLibraries } from '../cli-path-config.js';
5
+ import { readJsonSafe } from './file-utils.js';
6
+ import { logWarn } from './log-utils.js';
7
+
8
+ /**
9
+ * Determine whether a given package directory represents a React-based module.
10
+ *
11
+ * This function inspects the `package.json` file located at `pkgPath`
12
+ * and checks whether any of its dependencies, devDependencies, or peerDependencies
13
+ * include `react`. If the file does not exist, cannot be parsed, or the
14
+ * dependency is missing, it returns `false`.
15
+ *
16
+ * @param {string} pkgPath - Absolute path to a `package.json` file.
17
+ * @returns {boolean} `true` if the module depends on React, otherwise `false`.
18
+ *
19
+ * @example
20
+ * isReactModule('/path/to/package.json');
21
+ * // → true if the package declares React as a dependency
22
+ */
23
+ export function isReactModule(pkgPath) {
24
+ try {
25
+ if (!fs.existsSync(pkgPath)) return false;
26
+
27
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
28
+ if (!pkg || typeof pkg !== 'object') return false;
29
+
30
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies, ...pkg.peerDependencies };
31
+ return Boolean(deps.react);
32
+ } catch (err) {
33
+ logWarn(`Failed to read or parse ${pkgPath}: ${err.message}`);
34
+ return false;
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Discover all valid application or library modules by scanning predefined directories.
40
+ *
41
+ * Depending on the `isLibrary` flag, this function inspects:
42
+ * - Libraries under: `packages/manager/core`, `packages/manager/modules`, and `packages/components`
43
+ * - Applications under: `manager/apps`
44
+ *
45
+ * For each folder containing a `package.json` that represents a React module,
46
+ * a module descriptor object is created with metadata.
47
+ *
48
+ * @param {boolean} [isLibrary=false] - Whether to scan for libraries (`true`) or apps (`false`).
49
+ * @returns {Array<{ fullPath: string, shortPath: string, packageName: string }>}
50
+ * Array of discovered module descriptors.
51
+ *
52
+ * @example
53
+ * // Discover apps
54
+ * discoverModules();
55
+ * // → [{ fullPath: '/.../manager/apps/zimbra', shortPath: 'zimbra', packageName: '@ovh-ux/manager-zimbra-app' }]
56
+ *
57
+ * @example
58
+ * // Discover libraries
59
+ * discoverModules(true);
60
+ * // → [{ fullPath: '/.../packages/manager/core/core-shell', shortPath: 'core-shell', packageName: '@ovh-ux/manager-core-shell' }]
61
+ */
62
+ export function discoverModules(isLibrary = false) {
63
+ const discovered = [];
64
+ const modulesPaths = isLibrary ? librariesBasePaths : [appsDir];
65
+
66
+ for (const moduleBasePath of modulesPaths) {
67
+ if (!fs.existsSync(moduleBasePath)) continue;
68
+
69
+ const items = fs.readdirSync(moduleBasePath, { withFileTypes: true });
70
+ for (const item of items) {
71
+ if (item.isDirectory()) {
72
+ const appFolder = path.join(moduleBasePath, item.name);
73
+ const pkgPath = path.join(appFolder, 'package.json');
74
+ if (fs.existsSync(pkgPath) && isReactModule(pkgPath)) {
75
+ const pkgDetails = readJsonSafe(pkgPath);
76
+ discovered.push({
77
+ fullPath: appFolder,
78
+ shortPath: item.name,
79
+ packageName: pkgDetails.name,
80
+ });
81
+ }
82
+ }
83
+ }
84
+ }
85
+
86
+ return isLibrary ? [...discovered, ...staticDiscoveredLibraries] : discovered;
87
+ }
88
+
89
+ /**
90
+ * Retrieve a single module (app or library) matching a given pattern.
91
+ *
92
+ * This function searches through discovered modules (via {@link discoverModules})
93
+ * and returns the first one matching any of the following:
94
+ * - The module's absolute path (`fullPath`)
95
+ * - The folder name (`shortPath`)
96
+ * - The npm package name (`packageName`)
97
+ *
98
+ * @param {string} modulePattern - Pattern to match against module identifiers.
99
+ * @param {boolean} [isLibrary=false] - Whether to search within libraries (`true`) or apps (`false`).
100
+ * @returns {{ fullPath: string, shortPath: string, packageName: string }}
101
+ * The matched module descriptor, or `undefined` if no match is found.
102
+ *
103
+ * @example
104
+ * // Match by folder name
105
+ * getModule('zimbra');
106
+ * // → { fullPath: '/.../manager/apps/zimbra', shortPath: 'zimbra', packageName: '@ovh-ux/manager-zimbra-app' }
107
+ *
108
+ * @example
109
+ * // Match by package name
110
+ * getModule('@ovh-ux/manager-core-shell', true);
111
+ * // → { fullPath: '/.../packages/manager/core/core-shell', shortPath: 'core-shell', packageName: '@ovh-ux/manager-core-shell' }
112
+ */
113
+ export function getModule(modulePattern, isLibrary = false) {
114
+ const discoveredLibraries = discoverModules(isLibrary) || [];
115
+ return discoveredLibraries.find(
116
+ (library) =>
117
+ library.fullPath === modulePattern ||
118
+ library.shortPath === modulePattern ||
119
+ library.packageName === modulePattern,
120
+ );
121
+ }
@@ -3,12 +3,23 @@ import fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import process from 'node:process';
5
5
 
6
- import { resolveAppInfo } from './apps-utils.js';
7
- import { generateCombinedReports } from './file-utils.js';
6
+ import { generateCombinedReports, readJsonSafe } from './file-utils.js';
8
7
  import { logError, logInfo } from './log-utils.js';
8
+ import { isReactModule } from './module-utils.js';
9
9
 
10
10
  /**
11
- * Ensure the given binary exists.
11
+ * Ensure that a required binary exists before running an analysis command.
12
+ *
13
+ * This function verifies that a binary file exists at the given path.
14
+ * If not found, it logs an error message and terminates the process
15
+ * with exit code `1`.
16
+ *
17
+ * @param {string} binPath - Absolute path to the binary executable.
18
+ * @param {string} label - Human-readable label for the binary (used in error logs).
19
+ *
20
+ * @example
21
+ * ensureBinExists('/usr/local/bin/jscpd', 'JSCPD');
22
+ * // → Exits process if binary is missing
12
23
  */
13
24
  export function ensureBinExists(binPath, label) {
14
25
  if (!fs.existsSync(binPath)) {
@@ -18,7 +29,20 @@ export function ensureBinExists(binPath, label) {
18
29
  }
19
30
 
20
31
  /**
21
- * Run a child process with consistent defaults.
32
+ * Execute a command synchronously using the system shell.
33
+ *
34
+ * This function spawns a child process that runs a binary or command
35
+ * with the provided arguments, inheriting the parent process's stdio streams.
36
+ * It returns `true` if the command completed successfully (exit code `0`).
37
+ *
38
+ * @param {string} bin - Executable name or absolute path to run.
39
+ * @param {string[]} args - Command-line arguments for the executable.
40
+ * @param {string} cwd - Working directory where the command will be executed.
41
+ * @returns {boolean} `true` if the process exits with status `0`, otherwise `false`.
42
+ *
43
+ * @example
44
+ * runCommand('eslint', ['--fix', '.'], process.cwd());
45
+ * // → Runs ESLint and returns true if successful
22
46
  */
23
47
  export function runCommand(bin, args, cwd) {
24
48
  const result = spawnSync(bin, args, {
@@ -30,14 +54,90 @@ export function runCommand(bin, args, cwd) {
30
54
  }
31
55
 
32
56
  /**
33
- * Generic per-app analysis runner with safety.
57
+ * Execute analysis for a single module.
58
+ *
59
+ * This internal helper encapsulates:
60
+ * - Module validation (`fullPath`, `package.json`, React module check)
61
+ * - Error handling for analysis execution
62
+ * - Delegation to a provided analysis runner
63
+ *
64
+ * @param {Object} options - Analysis parameters.
65
+ * @param {Object} options.module - Module descriptor with metadata.
66
+ * @param {Function} options.analysisRunner - Function `(fullPath, shortPath, context) => boolean`
67
+ * that performs the analysis for this module.
68
+ * @returns {boolean} `true` if analysis succeeded for the module, otherwise `false`.
69
+ *
70
+ * @example
71
+ * runModuleAnalysis({
72
+ * module: { fullPath: '/apps/zimbra', shortPath: 'zimbra', packageName: '@ovh-ux/manager-zimbra-app' },
73
+ * analysisRunner: runCoverageAnalysis,
74
+ * });
34
75
  */
35
- export function runAppsAnalysis({
36
- appsDir,
37
- appFolders,
38
- requireReact = false,
76
+ function runModuleAnalysis({ module, analysisRunner }) {
77
+ const { fullPath, shortPath, packageName } = module || {};
78
+ const pkgPath = path.join(fullPath, 'package.json');
79
+ let isAnalysisComplete = false;
80
+
81
+ if (!fs.existsSync(fullPath) || !fs.existsSync(pkgPath) || !isReactModule(pkgPath)) {
82
+ logError(`❌ Module folder not found: ${module} → skipping`);
83
+ return isAnalysisComplete;
84
+ }
85
+
86
+ try {
87
+ isAnalysisComplete = analysisRunner(fullPath, shortPath, {
88
+ pkgPath,
89
+ moduleDir: fullPath,
90
+ moduleFolder: shortPath,
91
+ moduleShortName: packageName,
92
+ pkg: readJsonSafe(pkgPath),
93
+ });
94
+ } catch (err) {
95
+ logError(`❌ Fatal error while analyzing ${shortPath}: ${err.stack || err.message}`);
96
+ }
97
+
98
+ return isAnalysisComplete;
99
+ }
100
+
101
+ /**
102
+ * Run a complete analysis across multiple modules and generate combined reports.
103
+ *
104
+ * This function orchestrates:
105
+ * 1. Iterating over provided modules.
106
+ * 2. Running analysis for each module (via `runModuleAnalysis`).
107
+ * 3. Aggregating successful results.
108
+ * 4. Generating combined JSON and HTML reports.
109
+ *
110
+ * If no modules are successfully analyzed, the process terminates with exit code `1`.
111
+ *
112
+ * @param {Object} options - Analysis configuration.
113
+ * @param {Object[]} options.modules - Array of module descriptors (`{ fullPath, shortPath, packageName }`).
114
+ * @param {string} options.binaryLabel - Label for the analysis type (e.g. "Type Coverage").
115
+ * @param {Function} options.analysisRunner - Function executed per module to run analysis logic.
116
+ * @param {string} options.reportsRootDirName - Directory under the output root to store reports.
117
+ * @param {string} options.combinedJson - Filename for the combined JSON report.
118
+ * @param {string} options.combinedHtml - Filename for the combined HTML report.
119
+ * @param {Function} options.collectFn - Function used to aggregate individual module data.
120
+ * @param {Function} options.generateHtmlFn - Function used to generate the combined HTML output.
121
+ * @param {string} options.outputRootDir - Base path where reports should be written.
122
+ * @returns {string[]} Array of successfully analyzed module short names.
123
+ *
124
+ * @example
125
+ * runModulesAnalysis({
126
+ * modules,
127
+ * binaryLabel: 'Type Coverage',
128
+ * analysisRunner: runTypeCoverage,
129
+ * reportsRootDirName: 'types-coverage-reports',
130
+ * combinedJson: 'types-coverage-combined-report.json',
131
+ * combinedHtml: 'types-coverage-combined-report.html',
132
+ * collectFn: collectTypeCoverage,
133
+ * generateHtmlFn: generateTypeCoverageHtml,
134
+ * outputRootDir: './reports',
135
+ * });
136
+ */
137
+ export function runModulesAnalysis({
138
+ modules,
39
139
  binaryLabel,
40
- appRunner, // (appDir, appShortName, appInfo) => boolean
140
+ analysisRunner,
41
141
  reportsRootDirName,
42
142
  combinedJson,
43
143
  combinedHtml,
@@ -45,21 +145,16 @@ export function runAppsAnalysis({
45
145
  generateHtmlFn,
46
146
  outputRootDir,
47
147
  }) {
148
+ /** @type {string[]} */
48
149
  const analyzed = [];
49
150
 
50
- for (const appFolder of appFolders) {
51
- const appInfo = resolveAppInfo(appsDir, appFolder, { requireReact });
52
- if (!appInfo) continue;
151
+ for (const module of modules) {
152
+ const isAnalysisComplete = runModuleAnalysis({
153
+ module,
154
+ analysisRunner,
155
+ });
53
156
 
54
- let ok = false;
55
- try {
56
- ok = appRunner(appInfo.appDir, appInfo.appShortName, appInfo);
57
- } catch (err) {
58
- logError(
59
- `❌ Fatal error while analyzing ${appInfo.appShortName}: ${err.stack || err.message}`,
60
- );
61
- }
62
- if (ok) analyzed.push(appInfo.appShortName);
157
+ if (isAnalysisComplete) analyzed.push(module.shortPath);
63
158
  }
64
159
 
65
160
  if (analyzed.length > 0) {
@@ -1,76 +1,6 @@
1
1
  import { spawnSync } from 'node:child_process';
2
- import fs from 'node:fs';
3
- import path from 'node:path';
4
2
 
5
- import { logError, logInfo, logWarn } from './log-utils.js';
6
-
7
- /**
8
- * Convert app folder names into Turbo filters by resolving their package.json names.
9
- *
10
- * Each app folder must contain a valid `package.json`. If not found, the app is skipped.
11
- *
12
- * @param {string} appsDir - Absolute path to the apps root directory.
13
- * @param {string[]} appFolders - List of app folder names inside the apps directory.
14
- * @returns {string[]} Array of package names to be used as Turbo filters.
15
- *
16
- * @example
17
- * // appsDir = "/workspace/manager/apps"
18
- * // appFolders = ["zimbra", "web"]
19
- * // Returns: ["@ovh-ux/manager-zimbra-app", "@ovh-ux/manager-web-app"]
20
- */
21
- function resolveTurboFiltersFromAppFolders(appsDir, appFolders) {
22
- return appFolders
23
- .map((appFolder) => {
24
- const pkgPath = path.join(appsDir, appFolder, 'package.json');
25
- if (!fs.existsSync(pkgPath)) {
26
- logError(`❌ No package.json for app folder ${appFolder}, skipping`);
27
- return null;
28
- }
29
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
30
- return pkg.name;
31
- })
32
- .filter(Boolean);
33
- }
34
-
35
- /**
36
- * Resolve Turbo filters (package names) based on input mode.
37
- *
38
- * - If only `appFolders` are provided, resolves their corresponding package names.
39
- * - If `packageNames` are provided, they are used directly as filters.
40
- *
41
- * @param {Object} options
42
- * @param {string} options.appsDir - Absolute path to the apps root directory.
43
- * @param {string[]} options.appFolders - List of app folders to analyze.
44
- * @param {string[]} options.packageNames - List of package names (overrides appFolders).
45
- * @returns {string[]} List of resolved Turbo filters (package names).
46
- *
47
- * @example
48
- * resolveTurboFilters({
49
- * appsDir: "/workspace/manager/apps",
50
- * appFolders: ["zimbra"],
51
- * packageNames: []
52
- * });
53
- * // → ["@ovh-ux/manager-zimbra-app"]
54
- *
55
- * @example
56
- * resolveTurboFilters({
57
- * appsDir: "/workspace/manager/apps",
58
- * appFolders: [],
59
- * packageNames: ["@ovh-ux/manager-web-app"]
60
- * });
61
- * // → ["@ovh-ux/manager-web-app"]
62
- */
63
- export function resolveTurboFilters({ appsDir, appFolders, packageNames }) {
64
- let filters = [];
65
-
66
- if (appFolders.length > 0 && packageNames.length === 0) {
67
- filters = resolveTurboFiltersFromAppFolders(appsDir, appFolders);
68
- } else if (packageNames.length > 0) {
69
- filters = packageNames;
70
- }
71
-
72
- return filters;
73
- }
3
+ import { logInfo, logWarn } from './log-utils.js';
74
4
 
75
5
  /**
76
6
  * Run a Turbo build for the provided filters.
@@ -1 +1 @@
1
- {"version":3,"file":"perfs-bundle-analysis-helper.d.ts","sourceRoot":"","sources":["../../../../src/adapters/perf-budgets/helpers/perfs-bundle-analysis-helper.ts"],"names":[],"mappings":"AAMA,OAAO,EAAoB,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC3E,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAKtE,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAO9C;AAmCD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,WAAW,EAAE,GAAG,YAAY,CAgBlE;AASD,wBAAgB,kBAAkB,CAAC,eAAe,EAAE,MAAM,GAAG,kBAAkB,CAmC9E;AASD,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,kBAAkB,GAAG,MAAM,CAwJ3E"}
1
+ {"version":3,"file":"perfs-bundle-analysis-helper.d.ts","sourceRoot":"","sources":["../../../../src/adapters/perf-budgets/helpers/perfs-bundle-analysis-helper.ts"],"names":[],"mappings":"AAMA,OAAO,EAAoB,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC3E,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAKtE,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAO9C;AAyCD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,WAAW,EAAE,GAAG,YAAY,CAgBlE;AASD,wBAAgB,kBAAkB,CAAC,eAAe,EAAE,MAAM,GAAG,kBAAkB,CAmC9E;AASD,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,kBAAkB,GAAG,MAAM,CAwJ3E"}
@@ -15,14 +15,18 @@ const buildAssetsInfos = (data) => data.map((entry) => {
15
15
  const filename = entry?.filename ?? 'unknown';
16
16
  const sizeKb = typeof entry?.parsedSize === 'number' ? entry.parsedSize / 1024 : 0;
17
17
  let type = 'other';
18
- if (/\.js$/i.test(filename))
18
+ if (/\.(js|mjs|cjs|ts|tsx)$/i.test(filename)) {
19
19
  type = 'js';
20
- else if (/\.css$/i.test(filename))
20
+ }
21
+ else if (/\.css$/i.test(filename)) {
21
22
  type = 'css';
22
- else if (/\.html$/i.test(filename))
23
+ }
24
+ else if (/\.html?$/i.test(filename)) {
23
25
  type = 'html';
24
- else if (/\.(png|jpe?g|gif|svg|webp)$/i.test(filename))
26
+ }
27
+ else if (/\.(png|jpe?g|gif|svg|webp)$/i.test(filename)) {
25
28
  type = 'img';
29
+ }
26
30
  return { filename, type, sizeKb };
27
31
  });
28
32
  export function parseAnalyzerJson(raw) {
@@ -1 +1 @@
1
- {"version":3,"file":"shared-exclusion-patterns.d.ts","sourceRoot":"","sources":["../../src/configs/shared-exclusion-patterns.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,uBAAuB,UAuCnC,CAAC;AAyBF,eAAO,MAAM,4BAA4B,UAuBxC,CAAC"}
1
+ {"version":3,"file":"shared-exclusion-patterns.d.ts","sourceRoot":"","sources":["../../src/configs/shared-exclusion-patterns.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,uBAAuB,UAuCnC,CAAC;AAyBF,eAAO,MAAM,4BAA4B,UAwBxC,CAAC"}
@@ -53,6 +53,7 @@ export const sharedTypesExclusionPatterns = [
53
53
  '**/tailwind*.*',
54
54
  '**/turbo.json',
55
55
  '**/tsconfig.json',
56
+ 'vite-env.d.ts',
56
57
  '**/*.svg',
57
58
  '**/**.md',
58
59
  '**/Messages_*_*.json',