Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 40 additions & 2 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ module.exports = [
'dist/**',
'build/**',
'coverage/**',
'scripts/**',
'src/renderer/bundle.js',
'src/renderer/bundle.js.map',
'src/renderer/bundle.js.LICENSE.txt',
Expand All @@ -31,6 +30,36 @@ module.exports = [
],
},
js.configs.recommended,
{
files: [
'scripts/**/*.js',
'*.config.js',
'eslint.config.js',
'.eslintrc.js',
'.babelrc.js',
'tests/.eslintrc.js',
],
languageOptions: {
ecmaVersion: 2022,
sourceType: 'commonjs',
globals: {
...globals.node,
},
Comment thread
sourcery-ai[bot] marked this conversation as resolved.
},
rules: {
'no-unused-vars': 'off',
'no-case-declarations': 'off',
'no-useless-escape': 'off',
},
Comment on lines +49 to +53

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Disabling these core ESLint rules is generally discouraged as it can hide potential bugs and reduce code quality.

  • no-unused-vars: Instead of turning this off completely, consider configuring it to ignore variables with a specific pattern (e.g., prefixed with an underscore) for intentionally unused variables. This helps catch accidentally unused variables. For example: 'no-unused-vars': ['error', { 'argsIgnorePattern': '^_', 'varsIgnorePattern': '^_' }].
  • no-case-declarations: This rule prevents bugs from lexical declarations being visible across case clauses. Instead of disabling it, you can wrap the code within each case clause in a block { ... }.
  • no-useless-escape: This rule helps maintain cleaner code by removing unnecessary escape characters. It's usually safe to keep it enabled.

If these rules were disabled to facilitate the initial rollout of linting for these files, it would be beneficial to create a follow-up ticket to address the reported issues and re-enable these rules.

},
{
files: ['scripts/capture-ui-screenshot.js'],
languageOptions: {
globals: {
...globals.browser,
},
},
},
{
files: ['src/**/*.{js,jsx,ts,tsx}', 'tests/**/*.{js,jsx,ts,tsx}'],
languageOptions: {
Expand Down Expand Up @@ -58,7 +87,16 @@ module.exports = [
'import/order': [
'error',
{
groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'],
groups: [
'builtin',
'external',
'internal',
'parent',
'sibling',
'index',
'object',
'type',
],
'newlines-between': 'always',
alphabetize: { order: 'asc', caseInsensitive: true },
},
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@
"predev": "npm run build:ts && node scripts/clean-dev-assets.js",
"dev": "node scripts/index.js dev",
"clear-assets": "rimraf src/renderer/bundle.js src/renderer/bundle.js.map src/renderer/bundle.js.LICENSE.txt src/renderer/output.css",
"lint": "npm run format:check && eslint src tests --cache --max-warnings 0 && npm run lint:md && npm run test:catalog && npm run changelog:validate",
"lint": "npm run format:check && eslint src tests scripts eslint.config.js .eslintrc.js .babelrc.js babel.config.js jest.config.js playwright.config.ts postcss.config.js prettier.config.js tailwind.config.js webpack.config.js --cache --max-warnings 0 && npm run lint:md && npm run test:catalog && npm run changelog:validate",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The list of config files in the lint script is quite long. You can simplify this by using globs, which would make the script shorter and easier to maintain. For example, "*.config.js" covers many of the explicitly listed files.

Suggested change
"lint": "npm run format:check && eslint src tests scripts eslint.config.js .eslintrc.js .babelrc.js babel.config.js jest.config.js playwright.config.ts postcss.config.js prettier.config.js tailwind.config.js webpack.config.js --cache --max-warnings 0 && npm run lint:md && npm run test:catalog && npm run changelog:validate",
"lint": "npm run format:check && eslint src tests scripts \"*.config.js\" \".*rc.js\" \"playwright.config.ts\" --cache --max-warnings 0 && npm run lint:md && npm run test:catalog && npm run changelog:validate",

"lint:md": "npm run lint:md:links && npm run lint:md:style",
"lint:md:links": "node scripts/lint-markdown-links.js",
"lint:md:style": "markdownlint \"**/*.{md,mdx}\" --config .markdownlint.json --ignore node_modules --ignore dist",
"changelog:validate": "node scripts/validate-changelog.js",
"lint:tests": "eslint tests --cache --max-warnings 0",
"format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,md,html,css}\"",
"format:check": "prettier --check --end-of-line auto \"**/*.{json,md,html,css}\"",
"format:check": "prettier --check --end-of-line auto \"**/*.{json,md,html,css}\" && prettier --check --end-of-line auto \"scripts/**/*.js\" \"*.config.js\" \"eslint.config.js\" \".eslintrc.js\" \".babelrc.js\" \"jest.config.js\" \"postcss.config.js\" \"prettier.config.js\" \"tailwind.config.js\" \"webpack.config.js\" \"playwright.config.ts\"",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This script can be simplified. The two prettier commands can be combined into one, and the list of config files can be shortened using globs. This improves readability and maintainability.

Suggested change
"format:check": "prettier --check --end-of-line auto \"**/*.{json,md,html,css}\" && prettier --check --end-of-line auto \"scripts/**/*.js\" \"*.config.js\" \"eslint.config.js\" \".eslintrc.js\" \".babelrc.js\" \"jest.config.js\" \"postcss.config.js\" \"prettier.config.js\" \"tailwind.config.js\" \"webpack.config.js\" \"playwright.config.ts\"",
"format:check": "prettier --check --end-of-line auto \"**/*.{json,md,html,css}\" \"scripts/**/*.js\" \"*.config.js\" \".*rc.js\" \"*.config.ts\"",

"test": "jest --config jest.config.js --passWithNoTests",
"test:watch": "jest --watch --config jest.config.js --passWithNoTests",
"test:stress": "jest --config jest.config.js --runInBand --testMatch=\"**/tests/stress/**/*.{js,jsx,ts,tsx}\" --verbose",
Expand Down Expand Up @@ -77,7 +77,7 @@
"*.{json,md,html,css}": [
"prettier --write"
],
"{src,tests}/**/*.{js,jsx,ts,tsx}": [
"{src,tests,scripts}/**/*.{js,jsx,ts,tsx}": [
"eslint --fix"
]
},
Expand Down
10 changes: 6 additions & 4 deletions scripts/audit-actions-freshness.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,7 @@ function readWorkflowFiles(workflowDirectory) {
const entries = fs
.readdirSync(directoryPath, { withFileTypes: true })
.filter(
(entry) =>
entry.isFile() && (entry.name.endsWith('.yml') || entry.name.endsWith('.yaml'))
(entry) => entry.isFile() && (entry.name.endsWith('.yml') || entry.name.endsWith('.yaml'))
)
.map((entry) => entry.name)
.sort((left, right) => left.localeCompare(right));
Expand Down Expand Up @@ -203,7 +202,9 @@ async function githubRequest({ endpoint, token, method = 'GET', body = null }) {

if (!response.ok) {
const detail = data && typeof data === 'object' && data.message ? data.message : responseText;
const error = new Error(`GitHub API ${method} ${endpoint} failed (${response.status}): ${detail}`);
const error = new Error(
`GitHub API ${method} ${endpoint} failed (${response.status}): ${detail}`
);
error.status = response.status;
throw error;
}
Expand Down Expand Up @@ -406,7 +407,8 @@ async function ensureTrackingPullRequestBranch({
endpoint: `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repository)}/git/ref/heads/${toGitHubRefPath(defaultBranch)}`,
token,
});
const defaultBranchSha = defaultBranchRef && defaultBranchRef.object ? defaultBranchRef.object.sha : '';
const defaultBranchSha =
defaultBranchRef && defaultBranchRef.object ? defaultBranchRef.object.sha : '';
if (!defaultBranchSha) {
throw new Error(`Could not resolve latest commit on ${defaultBranch}.`);
}
Expand Down
41 changes: 31 additions & 10 deletions scripts/capture-ui-screenshot.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@ const DEFAULT_SCREENSHOT_NAME = `ui-${process.platform}-${process.arch}.png`;
const FIXED_MTIME = 1700000000000;

function loadSecretScannerHelpers() {
const compiledSecretScannerPath = path.join(ROOT_DIR, 'build', 'ts', 'utils', 'secret-scanner.js');
const compiledSecretScannerPath = path.join(
ROOT_DIR,
'build',
'ts',
'utils',
'secret-scanner.js'
);

try {
return require(compiledSecretScannerPath);
Expand Down Expand Up @@ -49,7 +55,10 @@ function sanitizeScreenshotName(nameCandidate) {
typeof nameCandidate === 'string' && nameCandidate.trim()
? nameCandidate.trim()
: DEFAULT_SCREENSHOT_NAME;
const baseName = path.basename(rawName).replace(/[^a-zA-Z0-9._-]/g, '-').replace(/^\.+/, '');
const baseName = path
.basename(rawName)
.replace(/[^a-zA-Z0-9._-]/g, '-')
.replace(/^\.+/, '');
const withExtension = baseName.toLowerCase().endsWith('.png') ? baseName : `${baseName}.png`;

if (!withExtension || withExtension === '.png') {
Expand Down Expand Up @@ -361,7 +370,8 @@ async function setupMockElectronApi(page) {
localStorage.setItem('configContent', mockConfig);

const cloneTree = (treeItems) => JSON.parse(JSON.stringify(treeItems));
const delay = (durationMs) => new Promise((resolve) => window.setTimeout(resolve, durationMs));
const delay = (durationMs) =>
new Promise((resolve) => window.setTimeout(resolve, durationMs));

window.electronAPI = {
getDefaultConfig: async () => mockConfig,
Expand All @@ -371,14 +381,19 @@ async function setupMockElectronApi(page) {
typeof configContent === 'string' && configContent.trim()
? configContent
: localStorage.getItem('configContent') || '';
const excludeSensitiveFiles = !/(^|\n)\s*enable_secret_scanning\s*:\s*false\b/i.test(
activeConfig
) && !/(^|\n)\s*exclude_suspicious_files\s*:\s*false\b/i.test(activeConfig);
const configLines = activeConfig
.split('\n')
.map((line) => line.trim().toLowerCase().replaceAll(' ', '').replaceAll('\t', ''));
const hasSecretScanningDisabled = configLines.includes('enable_secret_scanning:false');
const hasSuspiciousFilesDisabled = configLines.includes('exclude_suspicious_files:false');
const excludeSensitiveFiles = !hasSecretScanningDisabled && !hasSuspiciousFilesDisabled;
const tree = excludeSensitiveFiles ? mockFilteredDirectoryTree : mockDirectoryTree;
return cloneTree(tree);
},
analyzeRepository: async (options = {}) => {
const selectedFilePaths = Array.isArray(options?.selectedFiles) ? options.selectedFiles : [];
const selectedFilePaths = Array.isArray(options?.selectedFiles)
? options.selectedFiles
: [];
const filesInfo = selectedFilePaths.map((filePath, index) => {
const normalizedPath = String(filePath);
const relativePath = normalizedPath.startsWith(`${mockRootPath}/`)
Expand All @@ -402,7 +417,9 @@ async function setupMockElectronApi(page) {
const filesInfo = inputFilesInfo.map((file, index) => ({
path: String(file?.path || `src/file-${index + 1}.ts`),
tokens:
Number.isFinite(file?.tokens) && Number(file.tokens) > 0 ? Number(file.tokens) : 120 * (index + 1),
Number.isFinite(file?.tokens) && Number(file.tokens) > 0
? Number(file.tokens)
: 120 * (index + 1),
isBinary: false,
}));
const totalTokens = filesInfo.reduce((sum, file) => sum + file.tokens, 0);
Expand All @@ -422,7 +439,8 @@ async function setupMockElectronApi(page) {
'# Repository Analysis',
'',
...filesInfo.map(
(file) => `## ${file.path}\n\n\`\`\`ts\n// Preview for ${file.path}\n\`\`\`\nTokens: ${file.tokens}\n`
(file) =>
`## ${file.path}\n\n\`\`\`ts\n// Preview for ${file.path}\n\`\`\`\nTokens: ${file.tokens}\n`
),
'--END--',
].join('\n');
Expand Down Expand Up @@ -516,7 +534,10 @@ async function captureAppStateScreenshots(page) {
});

await runStep('Verify secret files are hidden by default', async () => {
await page.waitForFunction((selector) => !document.querySelector(selector), UI_SELECTORS.secretFileEntry);
await page.waitForFunction(
(selector) => !document.querySelector(selector),
UI_SELECTORS.secretFileEntry
);
});

await runStep('Capture source tab screenshot', async () => {
Expand Down
16 changes: 6 additions & 10 deletions scripts/lib/actions-freshness.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,9 @@ function extractUsesValue(line) {
return '';
}

const withoutPrefix = normalized
.slice(USES_LINE_PATTERN.length)
.trimStart();
const withoutPrefix = normalized.slice(USES_LINE_PATTERN.length).trimStart();
const commentStart = withoutPrefix.search(/\s#/);
const rawValue =
commentStart >= 0 ? withoutPrefix.slice(0, commentStart) : withoutPrefix;
const rawValue = commentStart >= 0 ? withoutPrefix.slice(0, commentStart) : withoutPrefix;

return normalizeReferenceValue(rawValue.trim());
}
Expand Down Expand Up @@ -131,10 +128,7 @@ function escapeMarkdownTableCell(value) {
return '';
}

return String(value)
.replace(/\\/g, '\\\\')
.replace(/\r?\n/g, '<br>')
.replace(/\|/g, '\\|');
return String(value).replace(/\\/g, '\\\\').replace(/\r?\n/g, '<br>').replace(/\|/g, '\\|');
}

function buildMarkdownReport(report) {
Expand Down Expand Up @@ -202,7 +196,9 @@ function buildMarkdownReport(report) {
}

if (report.staleCount === 0 && report.resolutionErrors.length === 0) {
lines.push('All pinned GitHub Actions references are current against latest upstream releases.');
lines.push(
'All pinned GitHub Actions references are current against latest upstream releases.'
);
lines.push('');
}

Expand Down
11 changes: 8 additions & 3 deletions scripts/lib/security.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,9 @@ function resolveCommand(command, localCandidates = []) {
}

for (const candidate of localCandidates) {
const absolutePath = path.isAbsolute(candidate) ? candidate : path.join(utils.ROOT_DIR, candidate);
const absolutePath = path.isAbsolute(candidate)
? candidate
: path.join(utils.ROOT_DIR, candidate);
if (fs.existsSync(absolutePath)) {
return absolutePath;
}
Expand Down Expand Up @@ -285,7 +287,9 @@ function resolveTokenFromFile() {
return '';
}

const tokenFilePath = path.isAbsolute(tokenFile) ? tokenFile : path.join(utils.ROOT_DIR, tokenFile);
const tokenFilePath = path.isAbsolute(tokenFile)
? tokenFile
: path.join(utils.ROOT_DIR, tokenFile);
if (!fs.existsSync(tokenFilePath)) {
return '';
}
Expand Down Expand Up @@ -508,7 +512,8 @@ async function runMendScan() {
assertAllowedExecutable(mendPath);

const pkg = readPackageMetadata();
const project = process.env.MEND_PROJECT || process.env.BINARY_NAME || pkg.name || 'ai-code-fusion';
const project =
process.env.MEND_PROJECT || process.env.BINARY_NAME || pkg.name || 'ai-code-fusion';
const version = process.env.MEND_PROJECT_VERSION || process.env.VERSION || pkg.version || '0.0.0';
const commandName = process.platform === 'win32' ? 'mend-scan.exe' : 'mend-scan';
const args = ['scan', '--project', project, '--version', version];
Expand Down
4 changes: 3 additions & 1 deletion scripts/lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,9 @@ function printHelp() {
console.log(' prometheus:verify - Verify pushed stress metrics in Prometheus');
console.log(' perf-test - Run stress tests, push metrics, and verify Prometheus');
console.log(' lint - Run linter');
console.log(' lint:md - Validate markdown links, image paths, and no decorative icons');
console.log(
' lint:md - Validate markdown links, image paths, and no decorative icons'
);
console.log(' format - Format code');
console.log(' validate - Run all code quality checks');
console.log(' qa - Run lint + tests + security checks');
Expand Down
20 changes: 16 additions & 4 deletions scripts/lint-markdown-links.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,11 @@ function normalizeTarget(rawTarget) {
function resolveTargetPath(markdownFilePath, target) {
const [pathWithoutAnchor] = target.split('#');

if (!pathWithoutAnchor || isExternalTarget(pathWithoutAnchor) || pathWithoutAnchor.startsWith('#')) {
if (
!pathWithoutAnchor ||
isExternalTarget(pathWithoutAnchor) ||
pathWithoutAnchor.startsWith('#')
) {
return null;
}

Expand Down Expand Up @@ -131,7 +135,11 @@ function lintMarkdownFile(markdownFilePath) {
const rawTargets = extractTargetsFromLine(line);
for (const rawTarget of rawTargets) {
const normalizedTarget = normalizeTarget(rawTarget);
if (!normalizedTarget || isExternalTarget(normalizedTarget) || normalizedTarget.startsWith('#')) {
if (
!normalizedTarget ||
isExternalTarget(normalizedTarget) ||
normalizedTarget.startsWith('#')
) {
continue;
}

Expand Down Expand Up @@ -177,12 +185,16 @@ function run() {
const relativeFilePath = path.relative(ROOT_DIR, error.filePath);

if (error.kind === 'decorative-icon') {
console.error(`- ${relativeFilePath}:${error.lineNumber} -> decorative icon found: ${error.lineText}`);
console.error(
`- ${relativeFilePath}:${error.lineNumber} -> decorative icon found: ${error.lineText}`
);
continue;
}

const relativeResolvedPath = path.relative(ROOT_DIR, error.resolvedPath);
console.error(`- ${relativeFilePath}:${error.lineNumber} -> ${error.target} (missing: ${relativeResolvedPath})`);
console.error(
`- ${relativeFilePath}:${error.lineNumber} -> ${error.target} (missing: ${relativeResolvedPath})`
);
}
process.exit(1);
}
Expand Down
4 changes: 3 additions & 1 deletion scripts/publish-stress-metrics.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,9 @@ function buildPrometheusPayload(records, options = {}) {
}
}

lines.push(`# HELP ${METRIC_PREFIX}_file_count Number of files exercised by the stress scenario.`);
lines.push(
`# HELP ${METRIC_PREFIX}_file_count Number of files exercised by the stress scenario.`
);
lines.push(`# TYPE ${METRIC_PREFIX}_file_count gauge`);

for (const record of records) {
Expand Down
6 changes: 4 additions & 2 deletions scripts/run-perf-metrics-job.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,12 @@ async function runPerfMetricsJob(options = {}) {
);
}

const jobName = (env.PUSHGATEWAY_JOB || DEFAULT_PUSHGATEWAY_JOB).trim() || DEFAULT_PUSHGATEWAY_JOB;
const jobName =
(env.PUSHGATEWAY_JOB || DEFAULT_PUSHGATEWAY_JOB).trim() || DEFAULT_PUSHGATEWAY_JOB;
const instanceName =
(env.PUSHGATEWAY_INSTANCE || '').trim() || buildDefaultInstanceName(nowFn(), hostName);
const strictMode = (env.PUSHGATEWAY_STRICT || 'true').trim().toLowerCase() === 'false' ? 'false' : 'true';
const strictMode =
(env.PUSHGATEWAY_STRICT || 'true').trim().toLowerCase() === 'false' ? 'false' : 'true';
const timeoutMs = toFiniteNumber(env.PROMETHEUS_VERIFY_TIMEOUT_MS) || 60_000;
const pollIntervalMs = toFiniteNumber(env.PROMETHEUS_VERIFY_POLL_INTERVAL_MS) || 5_000;
const minPublishTimestampSeconds = Math.floor(nowFn() / 1000);
Expand Down
13 changes: 8 additions & 5 deletions scripts/sonar-scan.js
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,8 @@ function runWithNativeScanner(scannerOptions, token) {
const scannerOptionsForArgs = { ...scannerOptions };
delete scannerOptionsForArgs['sonar.token'];
const args = Object.entries(scannerOptionsForArgs).map(([key, value]) => `-D${key}=${value}`);
const useWindowsShell = process.platform === 'win32' && scannerBinary.toLowerCase().endsWith('.bat');
const useWindowsShell =
process.platform === 'win32' && scannerBinary.toLowerCase().endsWith('.bat');
const scannerEnv = { ...process.env };
if (token) {
scannerEnv.SONAR_TOKEN = token;
Expand Down Expand Up @@ -278,7 +279,11 @@ function resolveNativeScannerPath() {
function isNpmScannerWrapperPath(scannerPath) {
const normalizedPath = path.normalize(scannerPath).toLowerCase();
const wrapperSuffix = path
.join('node_modules', '.bin', process.platform === 'win32' ? 'sonar-scanner.cmd' : 'sonar-scanner')
.join(
'node_modules',
'.bin',
process.platform === 'win32' ? 'sonar-scanner.cmd' : 'sonar-scanner'
)
.toLowerCase();
return normalizedPath.endsWith(wrapperSuffix);
}
Expand Down Expand Up @@ -397,9 +402,7 @@ try {
console.log(
'2. Check if the project exists on the server or if you have permission to create it'
);
console.log(
'3. Verify the token has not expired and is valid for the specified project key'
);
console.log('3. Verify the token has not expired and is valid for the specified project key');
process.exit(1);
} else {
console.log('SonarQube scan completed successfully!');
Expand Down
Loading
Loading