@salesforce-ux/slds-linter 0.0.12-alpha → 0.0.12-alpha.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.
Files changed (43) hide show
  1. package/build/commands/lint-components.d.ts +2 -0
  2. package/build/commands/lint-components.js +50 -0
  3. package/build/commands/lint-styles.d.ts +2 -0
  4. package/build/commands/lint-styles.js +50 -0
  5. package/build/commands/lint.d.ts +2 -0
  6. package/build/commands/lint.js +86 -0
  7. package/build/commands/report.d.ts +2 -0
  8. package/build/commands/report.js +50 -0
  9. package/build/index.d.ts +2 -1
  10. package/build/index.js +24 -0
  11. package/build/services/__tests__/file-scanner.test.js +44 -0
  12. package/build/services/batch-processor.d.ts +29 -0
  13. package/build/services/batch-processor.js +84 -0
  14. package/build/services/config.resolver.d.ts +6 -0
  15. package/build/services/config.resolver.js +23 -0
  16. package/build/services/file-patterns.d.ts +3 -0
  17. package/build/services/file-patterns.js +33 -0
  18. package/build/services/file-scanner.d.ts +26 -0
  19. package/build/services/file-scanner.js +69 -0
  20. package/build/services/lint-runner.d.ts +17 -0
  21. package/build/services/lint-runner.js +68 -0
  22. package/build/services/report-generator.d.ts +20 -0
  23. package/build/services/report-generator.js +119 -0
  24. package/build/types/index.d.ts +47 -0
  25. package/build/types/index.js +0 -0
  26. package/build/utils/cli-args.d.ts +3 -0
  27. package/build/utils/cli-args.js +30 -0
  28. package/build/utils/editorLinkUtil.d.ts +21 -0
  29. package/build/utils/editorLinkUtil.js +21 -0
  30. package/build/utils/lintResultsUtil.d.ts +7 -0
  31. package/build/utils/lintResultsUtil.js +43 -0
  32. package/build/utils/logger.d.ts +7 -0
  33. package/build/utils/logger.js +24 -0
  34. package/build/workers/base.worker.d.ts +15 -0
  35. package/build/workers/base.worker.js +44 -0
  36. package/build/workers/eslint.worker.js +48 -0
  37. package/build/workers/stylelint.worker.d.ts +1 -0
  38. package/build/workers/stylelint.worker.js +39 -0
  39. package/package.json +38 -33
  40. package/README.md +0 -63
  41. package/bin/slds-linter.js +0 -119
  42. package/build/setup.js +0 -42
  43. /package/build/{setup.d.ts → workers/eslint.worker.d.ts} +0 -0
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerLintComponentsCommand(program: Command): void;
@@ -0,0 +1,50 @@
1
+ // src/commands/lint-components.ts
2
+ import chalk from "chalk";
3
+ import { printLintResults } from "../utils/lintResultsUtil.js";
4
+ import { normalizeCliOptions } from "../utils/cli-args.js";
5
+ import { Logger } from "../utils/logger.js";
6
+ import { FileScanner } from "../services/file-scanner.js";
7
+ import { ComponentFilePatterns } from "../services/file-patterns.js";
8
+ import { LintRunner } from "../services/lint-runner.js";
9
+ import { DEFAULT_ESLINT_CONFIG_PATH } from "../services/config.resolver.js";
10
+ function registerLintComponentsCommand(program) {
11
+ program.command("lint:components").description("Run eslint on all markup files").option("-d, --directory <path>", "Target directory to scan (defaults to current directory)").option("--fix", "Automatically fix problems").option("--config <path>", "Path to eslint config file", DEFAULT_ESLINT_CONFIG_PATH).option("--editor <editor>", "Editor to open files with (vscode, atom, sublime). Defaults to vscode", "vscode").action(async (options) => {
12
+ const startTime = Date.now();
13
+ try {
14
+ Logger.info(chalk.blue("Starting linting of component files..."));
15
+ const normalizedOptions = normalizeCliOptions(options);
16
+ Logger.info(chalk.blue("Scanning for files..."));
17
+ const fileBatches = await FileScanner.scanFiles(normalizedOptions.directory, {
18
+ patterns: ComponentFilePatterns,
19
+ batchSize: 100
20
+ });
21
+ const totalFiles = fileBatches.reduce((sum, batch) => sum + batch.length, 0);
22
+ Logger.info(chalk.blue(`Scanned ${totalFiles} file(s).`));
23
+ Logger.info(chalk.blue("Running linting..."));
24
+ const results = await LintRunner.runLinting(fileBatches, "component", {
25
+ fix: options.fix,
26
+ configPath: options.config
27
+ });
28
+ printLintResults(results, normalizedOptions.editor);
29
+ const errorCount = results.reduce((sum, r) => sum + r.errors.length, 0);
30
+ const warningCount = results.reduce((sum, r) => sum + r.warnings.length, 0);
31
+ Logger.info(
32
+ chalk.blue(
33
+ `
34
+ Summary: Processed ${totalFiles} file(s) with ${chalk.red(
35
+ errorCount.toString()
36
+ )} error(s) and ${chalk.yellow(warningCount.toString())} warning(s).`
37
+ )
38
+ );
39
+ const elapsedTime = ((Date.now() - startTime) / 1e3).toFixed(2);
40
+ Logger.success(chalk.green(`Component files linting completed in ${elapsedTime} seconds.`));
41
+ process.exit(errorCount > 0 ? 1 : 0);
42
+ } catch (error) {
43
+ Logger.error(chalk.red(`Error during linting: ${error.message}`));
44
+ process.exit(1);
45
+ }
46
+ });
47
+ }
48
+ export {
49
+ registerLintComponentsCommand
50
+ };
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerLintStylesCommand(program: Command): void;
@@ -0,0 +1,50 @@
1
+ // src/commands/lint-styles.ts
2
+ import chalk from "chalk";
3
+ import { printLintResults } from "../utils/lintResultsUtil.js";
4
+ import { normalizeCliOptions } from "../utils/cli-args.js";
5
+ import { Logger } from "../utils/logger.js";
6
+ import { FileScanner } from "../services/file-scanner.js";
7
+ import { StyleFilePatterns } from "../services/file-patterns.js";
8
+ import { LintRunner } from "../services/lint-runner.js";
9
+ import { DEFAULT_STYLELINT_CONFIG_PATH } from "../services/config.resolver.js";
10
+ function registerLintStylesCommand(program) {
11
+ program.command("lint:styles").description("Run stylelint on all style files").option("-d, --directory <path>", "Target directory to scan (defaults to current directory)").option("--fix", "Automatically fix problems").option("--config <path>", "Path to stylelint config file", DEFAULT_STYLELINT_CONFIG_PATH).option("--editor <editor>", "Editor to open files with (vscode, atom, sublime). Defaults to vscode", "vscode").action(async (options) => {
12
+ const startTime = Date.now();
13
+ try {
14
+ Logger.info(chalk.blue("Starting linting of style files..."));
15
+ const normalizedOptions = normalizeCliOptions(options);
16
+ Logger.info(chalk.blue("Scanning for style files..."));
17
+ const fileBatches = await FileScanner.scanFiles(normalizedOptions.directory, {
18
+ patterns: StyleFilePatterns,
19
+ batchSize: 100
20
+ });
21
+ const totalFiles = fileBatches.reduce((sum, batch) => sum + batch.length, 0);
22
+ Logger.info(chalk.blue(`Scanned ${totalFiles} file(s).`));
23
+ Logger.info(chalk.blue("Running stylelint..."));
24
+ const results = await LintRunner.runLinting(fileBatches, "style", {
25
+ fix: options.fix,
26
+ configPath: options.config
27
+ });
28
+ printLintResults(results, normalizedOptions.editor);
29
+ const errorCount = results.reduce((sum, r) => sum + r.errors.length, 0);
30
+ const warningCount = results.reduce((sum, r) => sum + r.warnings.length, 0);
31
+ Logger.info(
32
+ chalk.blue(
33
+ `
34
+ Summary: Processed ${totalFiles} file(s) with ${chalk.red(
35
+ errorCount.toString()
36
+ )} error(s) and ${chalk.yellow(warningCount.toString())} warning(s).`
37
+ )
38
+ );
39
+ const elapsedTime = ((Date.now() - startTime) / 1e3).toFixed(2);
40
+ Logger.success(chalk.green(`Style files linting completed in ${elapsedTime} seconds.`));
41
+ process.exit(errorCount > 0 ? 1 : 0);
42
+ } catch (error) {
43
+ Logger.error(chalk.red(`Error during linting: ${error.message}`));
44
+ process.exit(1);
45
+ }
46
+ });
47
+ }
48
+ export {
49
+ registerLintStylesCommand
50
+ };
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerLintCommand(program: Command): void;
@@ -0,0 +1,86 @@
1
+ // src/commands/lint.ts
2
+ import chalk from "chalk";
3
+ import path from "path";
4
+ import { createClickableLineCol } from "../utils/editorLinkUtil.js";
5
+ import { printLintResults } from "../utils/lintResultsUtil.js";
6
+ import { normalizeCliOptions } from "../utils/cli-args.js";
7
+ import { Logger } from "../utils/logger.js";
8
+ import { FileScanner } from "../services/file-scanner.js";
9
+ import { StyleFilePatterns, ComponentFilePatterns } from "../services/file-patterns.js";
10
+ import { LintRunner } from "../services/lint-runner.js";
11
+ import { DEFAULT_ESLINT_CONFIG_PATH, DEFAULT_STYLELINT_CONFIG_PATH } from "../services/config.resolver.js";
12
+ function registerLintCommand(program) {
13
+ program.command("lint").description("Run both style and component linting").option("-d, --directory <path>", "Target directory to scan (defaults to current directory)").option("--fix", "Automatically fix problems").option("--config-style <path>", "Path to stylelint config file", DEFAULT_STYLELINT_CONFIG_PATH).option("--config-eslint <path>", "Path to eslint config file", DEFAULT_ESLINT_CONFIG_PATH).option("--editor <editor>", "Editor to open files with (e.g., vscode, atom, sublime). Defaults to vscode", "vscode").action(async (options) => {
14
+ const startTime = Date.now();
15
+ try {
16
+ Logger.info(chalk.blue("Starting full linting process..."));
17
+ const normalizedOptions = normalizeCliOptions(options);
18
+ Logger.info(chalk.blue("\nScanning style files..."));
19
+ const styleFileBatches = await FileScanner.scanFiles(normalizedOptions.directory, {
20
+ patterns: StyleFilePatterns,
21
+ batchSize: 100
22
+ });
23
+ const totalStyleFiles = styleFileBatches.reduce((sum, batch) => sum + batch.length, 0);
24
+ Logger.info(chalk.blue(`Found ${totalStyleFiles} style file(s). Running stylelint...
25
+ `));
26
+ const styleResults = await LintRunner.runLinting(styleFileBatches, "style", {
27
+ fix: options.fix,
28
+ configPath: options.configStyle
29
+ });
30
+ styleResults.forEach((result) => {
31
+ const hasErrors = result.errors?.length > 0;
32
+ const hasWarnings = result.warnings?.length > 0;
33
+ if (!hasErrors && !hasWarnings) return;
34
+ const absolutePath = result.filePath || "";
35
+ const relativeFile = path.relative(process.cwd(), absolutePath) || "Unknown file";
36
+ Logger.info(`
37
+ ${chalk.bold(relativeFile)}`);
38
+ result.errors?.forEach((err) => {
39
+ const lineCol = `${err.line}:${err.column}`;
40
+ const clickable = createClickableLineCol(lineCol, absolutePath, err.line, err.column, normalizedOptions.editor);
41
+ const ruleId = err.ruleId ? chalk.dim(err.ruleId) : "";
42
+ Logger.error(` ${clickable} ${err.message} ${ruleId}`);
43
+ });
44
+ result.warnings?.forEach((warn) => {
45
+ const lineCol = `${warn.line}:${warn.column}`;
46
+ const clickable = createClickableLineCol(lineCol, absolutePath, warn.line, warn.column, normalizedOptions.editor);
47
+ const ruleId = warn.ruleId ? chalk.dim(warn.ruleId) : "";
48
+ Logger.warning(` ${clickable} ${warn.message} ${ruleId}`);
49
+ });
50
+ });
51
+ const styleErrorCount = styleResults.reduce((sum, r) => sum + r.errors.length, 0);
52
+ const styleWarningCount = styleResults.reduce((sum, r) => sum + r.warnings.length, 0);
53
+ Logger.info(chalk.blue("\nScanning component files..."));
54
+ const componentFileBatches = await FileScanner.scanFiles(normalizedOptions.directory, {
55
+ patterns: ComponentFilePatterns,
56
+ batchSize: 100
57
+ });
58
+ const totalComponentFiles = componentFileBatches.reduce((sum, batch) => sum + batch.length, 0);
59
+ Logger.info(chalk.blue(`Found ${totalComponentFiles} component file(s). Running eslint...
60
+ `));
61
+ const componentResults = await LintRunner.runLinting(componentFileBatches, "component", {
62
+ fix: options.fix,
63
+ configPath: options.configEslint
64
+ });
65
+ printLintResults(componentResults, normalizedOptions.editor);
66
+ const componentErrorCount = componentResults.reduce((sum, r) => sum + r.errors.length, 0);
67
+ const componentWarningCount = componentResults.reduce((sum, r) => sum + r.warnings.length, 0);
68
+ const totalErrors = styleErrorCount + componentErrorCount;
69
+ const totalWarnings = styleWarningCount + componentWarningCount;
70
+ Logger.info(
71
+ `
72
+ ${chalk.red(`${totalErrors} error${totalErrors !== 1 ? "s" : ""}`)} ${chalk.yellow(`${totalWarnings} warning${totalWarnings !== 1 ? "s" : ""}`)}`
73
+ );
74
+ const elapsedTime = ((Date.now() - startTime) / 1e3).toFixed(2);
75
+ Logger.success(chalk.green(`
76
+ Full linting completed in ${elapsedTime} seconds.`));
77
+ process.exit(totalErrors > 0 ? 1 : 0);
78
+ } catch (error) {
79
+ Logger.error(chalk.red(`Failed to complete linting: ${error.message}`));
80
+ process.exit(1);
81
+ }
82
+ });
83
+ }
84
+ export {
85
+ registerLintCommand
86
+ };
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerReportCommand(program: Command): void;
@@ -0,0 +1,50 @@
1
+ // src/commands/report.ts
2
+ import path from "path";
3
+ import ora from "ora";
4
+ import { normalizeCliOptions } from "../utils/cli-args.js";
5
+ import { Logger } from "../utils/logger.js";
6
+ import { FileScanner } from "../services/file-scanner.js";
7
+ import { StyleFilePatterns, ComponentFilePatterns } from "../services/file-patterns.js";
8
+ import { LintRunner } from "../services/lint-runner.js";
9
+ import { ReportGenerator } from "../services/report-generator.js";
10
+ import { DEFAULT_ESLINT_CONFIG_PATH, DEFAULT_STYLELINT_CONFIG_PATH, LINTER_CLI_VERSION } from "../services/config.resolver.js";
11
+ function registerReportCommand(program) {
12
+ program.command("report").description("Generate SARIF report from linting results").option("-d, --directory <path>", "Target directory to scan (defaults to current directory)").option("-o, --output <path>", "Output directory for reports (defaults to current directory)").option("--config-style <path>", "Path to stylelint config file", DEFAULT_STYLELINT_CONFIG_PATH).option("--config-eslint <path>", "Path to eslint config file", DEFAULT_ESLINT_CONFIG_PATH).action(async (options) => {
13
+ const spinner = ora("Starting report generation...").start();
14
+ try {
15
+ const normalizedOptions = normalizeCliOptions(options);
16
+ spinner.text = "Running styles linting...";
17
+ const styleFileBatches = await FileScanner.scanFiles(normalizedOptions.directory, {
18
+ patterns: StyleFilePatterns,
19
+ batchSize: 100
20
+ });
21
+ const styleResults = await LintRunner.runLinting(styleFileBatches, "style", {
22
+ configPath: options.configStyle
23
+ });
24
+ spinner.text = "Running components linting...";
25
+ const componentFileBatches = await FileScanner.scanFiles(normalizedOptions.directory, {
26
+ patterns: ComponentFilePatterns,
27
+ batchSize: 100
28
+ });
29
+ const componentResults = await LintRunner.runLinting(componentFileBatches, "component", {
30
+ configPath: options.configEslint
31
+ });
32
+ spinner.text = "Generating combined report...";
33
+ const combinedReportPath = path.join(normalizedOptions.output, "slds-linter-report.sarif");
34
+ await ReportGenerator.generateSarifReport([...styleResults, ...componentResults], {
35
+ outputPath: combinedReportPath,
36
+ toolName: "slds-linter",
37
+ toolVersion: LINTER_CLI_VERSION
38
+ });
39
+ spinner.succeed("Report generation completed");
40
+ process.exit(0);
41
+ } catch (error) {
42
+ spinner?.fail("Report generation failed");
43
+ Logger.error(`Failed to generate report: ${error.message}`);
44
+ process.exit(1);
45
+ }
46
+ });
47
+ }
48
+ export {
49
+ registerReportCommand
50
+ };
package/build/index.d.ts CHANGED
@@ -1 +1,2 @@
1
- export declare function exampleFunction(filePath: string): string;
1
+ #!/usr/bin/env node
2
+ export {};
package/build/index.js ADDED
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { Command } from "commander";
5
+ import { registerLintStylesCommand } from "./commands/lint-styles.js";
6
+ import { registerLintComponentsCommand } from "./commands/lint-components.js";
7
+ import { registerLintCommand } from "./commands/lint.js";
8
+ import { registerReportCommand } from "./commands/report.js";
9
+ import { Logger } from "./utils/logger.js";
10
+ process.on("unhandledRejection", (error) => {
11
+ Logger.error(`Unhandled rejection: ${error}`);
12
+ process.exit(1);
13
+ });
14
+ process.on("uncaughtException", (error) => {
15
+ Logger.error(`Uncaught exception: ${error}`);
16
+ process.exit(1);
17
+ });
18
+ var program = new Command();
19
+ program.name("linting-cli").description("A CLI tool for linting styles and components").version("1.0.0");
20
+ registerLintStylesCommand(program);
21
+ registerLintComponentsCommand(program);
22
+ registerLintCommand(program);
23
+ registerReportCommand(program);
24
+ program.parse(process.argv);
@@ -0,0 +1,44 @@
1
+ // src/services/__tests__/file-scanner.test.ts
2
+ import path from "path";
3
+ import { FileScanner } from "../file-scanner.js";
4
+ import { StyleFilePatterns } from "../file-patterns.js";
5
+ import { mkdir, writeFile, rm } from "fs/promises";
6
+ describe("FileScanner", () => {
7
+ const testDir = path.join(import.meta.dirname, "fixtures");
8
+ beforeAll(async () => {
9
+ await mkdir(testDir, { recursive: true });
10
+ await writeFile(
11
+ path.join(testDir, "test.css"),
12
+ "body { color: red; }"
13
+ );
14
+ await writeFile(
15
+ path.join(testDir, "test.scss"),
16
+ "$color: red;"
17
+ );
18
+ });
19
+ afterAll(async () => {
20
+ await rm(testDir, { recursive: true });
21
+ });
22
+ it("should scan and batch files correctly", async () => {
23
+ const options = {
24
+ patterns: StyleFilePatterns,
25
+ batchSize: 1
26
+ };
27
+ const batches = await FileScanner.scanFiles(testDir, options);
28
+ expect(batches).toHaveLength(2);
29
+ expect(batches[0]).toHaveLength(1);
30
+ expect(batches[1]).toHaveLength(1);
31
+ expect(batches[0][0]).toMatch(/test\.(css|scss)$/);
32
+ expect(batches[1][0]).toMatch(/test\.(css|scss)$/);
33
+ });
34
+ it("should handle invalid files gracefully", async () => {
35
+ const options = {
36
+ patterns: {
37
+ include: ["**/*.nonexistent"],
38
+ exclude: []
39
+ }
40
+ };
41
+ const batches = await FileScanner.scanFiles(testDir, options);
42
+ expect(batches).toHaveLength(0);
43
+ });
44
+ });
@@ -0,0 +1,29 @@
1
+ export interface BatchProcessorOptions {
2
+ maxWorkers?: number;
3
+ timeoutMs?: number;
4
+ }
5
+ export interface BatchTask<T> {
6
+ files: string[];
7
+ config: T;
8
+ }
9
+ export interface BatchResult {
10
+ success: boolean;
11
+ error?: string;
12
+ results: any[];
13
+ }
14
+ export declare class BatchProcessor {
15
+ private static DEFAULT_MAX_WORKERS;
16
+ private static DEFAULT_TIMEOUT_MS;
17
+ /**
18
+ * Process batches of files in parallel using worker threads
19
+ * @param batches Array of file batches to process
20
+ * @param workerScript Path to the worker script
21
+ * @param taskConfig Configuration to pass to each worker
22
+ * @param options Processing options
23
+ */
24
+ static processBatches<T>(batches: string[][], workerScript: string, taskConfig: T, options?: BatchProcessorOptions): Promise<BatchResult[]>;
25
+ /**
26
+ * Creates a new worker with timeout handling
27
+ */
28
+ private static createWorker;
29
+ }
@@ -0,0 +1,84 @@
1
+ // src/services/batch-processor.ts
2
+ import { Worker } from "worker_threads";
3
+ import os from "os";
4
+ import { Logger } from "../utils/logger.js";
5
+ var AVAILABLE_CPUS = os.cpus().length - 1;
6
+ var BatchProcessor = class {
7
+ static DEFAULT_MAX_WORKERS = Math.max(1, Math.min(4, AVAILABLE_CPUS));
8
+ static DEFAULT_TIMEOUT_MS = 3e5;
9
+ // 5 minutes
10
+ /**
11
+ * Process batches of files in parallel using worker threads
12
+ * @param batches Array of file batches to process
13
+ * @param workerScript Path to the worker script
14
+ * @param taskConfig Configuration to pass to each worker
15
+ * @param options Processing options
16
+ */
17
+ static async processBatches(batches, workerScript, taskConfig, options = {}) {
18
+ const maxWorkers = options.maxWorkers || this.DEFAULT_MAX_WORKERS;
19
+ const timeoutMs = options.timeoutMs || this.DEFAULT_TIMEOUT_MS;
20
+ Logger.debug(`Starting batch processing with ${maxWorkers} workers`);
21
+ Logger.debug(`Processing ${batches.length} batches`);
22
+ const results = [];
23
+ const activeWorkers = /* @__PURE__ */ new Set();
24
+ let currentBatchIndex = 0;
25
+ try {
26
+ while (currentBatchIndex < batches.length || activeWorkers.size > 0) {
27
+ while (activeWorkers.size < maxWorkers && currentBatchIndex < batches.length) {
28
+ const batchIndex = currentBatchIndex++;
29
+ const batch = batches[batchIndex];
30
+ const worker = this.createWorker(
31
+ workerScript,
32
+ { files: batch, config: taskConfig },
33
+ timeoutMs
34
+ );
35
+ activeWorkers.add(worker);
36
+ worker.on("message", (result) => {
37
+ results.push(result);
38
+ activeWorkers.delete(worker);
39
+ Logger.debug(`Completed batch ${batchIndex} of ${batches.length}`);
40
+ }).on("error", (error) => {
41
+ results.push({
42
+ success: false,
43
+ error: error.message,
44
+ results: []
45
+ });
46
+ activeWorkers.delete(worker);
47
+ Logger.error(`Worker error in batch ${batchIndex}: ${error.message}`);
48
+ }).on("exit", (code) => {
49
+ if (code !== 0) {
50
+ Logger.warning(`Worker exited with code ${code}`);
51
+ }
52
+ activeWorkers.delete(worker);
53
+ });
54
+ }
55
+ await new Promise((resolve) => setTimeout(resolve, 100));
56
+ }
57
+ return results;
58
+ } catch (error) {
59
+ Logger.error(`Batch processing failed: ${error.message}`);
60
+ throw error;
61
+ } finally {
62
+ for (const worker of activeWorkers) {
63
+ worker.terminate();
64
+ }
65
+ }
66
+ }
67
+ /**
68
+ * Creates a new worker with timeout handling
69
+ */
70
+ static createWorker(scriptPath, task, timeoutMs) {
71
+ const worker = new Worker(scriptPath, {
72
+ workerData: task
73
+ });
74
+ const timeoutId = setTimeout(() => {
75
+ Logger.warning(`Worker timeout after ${timeoutMs}ms`);
76
+ worker.terminate();
77
+ }, timeoutMs);
78
+ worker.once("exit", () => clearTimeout(timeoutId));
79
+ return worker;
80
+ }
81
+ };
82
+ export {
83
+ BatchProcessor
84
+ };
@@ -0,0 +1,6 @@
1
+ export declare const DEFAULT_ESLINT_CONFIG_PATH: string;
2
+ export declare const DEFAULT_STYLELINT_CONFIG_PATH: string;
3
+ export declare const STYLELINT_VERSION: string;
4
+ export declare const ESLINT_VERSION: string;
5
+ export declare const LINTER_CLI_VERSION: string;
6
+ export declare const getRuleDescription: (ruleId: string) => string;
@@ -0,0 +1,23 @@
1
+ // src/services/config.resolver.ts
2
+ import { fileURLToPath } from "url";
3
+ import stylelintPackage from "stylelint/package.json" with { type: "json" };
4
+ import eslintPackage from "eslint/package.json" with { type: "json" };
5
+ import cliPackage from "../../package.json" with { type: "json" };
6
+ import { ruleMetadata } from "@salesforce-ux/stylelint-plugin-slds";
7
+ var DEFAULT_ESLINT_CONFIG_PATH = fileURLToPath(import.meta.resolve("@salesforce-ux/eslint-plugin-slds/.eslintrc.yml"));
8
+ var DEFAULT_STYLELINT_CONFIG_PATH = fileURLToPath(import.meta.resolve("@salesforce-ux/stylelint-plugin-slds/.stylelintrc.yml"));
9
+ var STYLELINT_VERSION = stylelintPackage.version;
10
+ var ESLINT_VERSION = eslintPackage.version;
11
+ var LINTER_CLI_VERSION = cliPackage.version;
12
+ var getRuleDescription = (ruleId) => {
13
+ const ruleIdWithoutNameSpace = `${ruleId}`.replace(/\@salesforce-ux\//, "");
14
+ return ruleMetadata(ruleIdWithoutNameSpace)?.ruleDesc || "--";
15
+ };
16
+ export {
17
+ DEFAULT_ESLINT_CONFIG_PATH,
18
+ DEFAULT_STYLELINT_CONFIG_PATH,
19
+ ESLINT_VERSION,
20
+ LINTER_CLI_VERSION,
21
+ STYLELINT_VERSION,
22
+ getRuleDescription
23
+ };
@@ -0,0 +1,3 @@
1
+ import { FilePattern } from './file-scanner';
2
+ export declare const StyleFilePatterns: FilePattern;
3
+ export declare const ComponentFilePatterns: FilePattern;
@@ -0,0 +1,33 @@
1
+ // src/services/file-patterns.ts
2
+ var StyleFilePatterns = {
3
+ include: [
4
+ "**/*.css",
5
+ "**/*.scss",
6
+ "**/*.less",
7
+ "**/*.sass"
8
+ ],
9
+ exclude: [
10
+ "**/node_modules/**",
11
+ "**/dist/**",
12
+ "**/build/**"
13
+ ]
14
+ };
15
+ var ComponentFilePatterns = {
16
+ include: [
17
+ "**/*.html",
18
+ "**/*.cmp",
19
+ "**/*.component",
20
+ "**/*.app",
21
+ "**/*.page",
22
+ "**/*.interface"
23
+ ],
24
+ exclude: [
25
+ "**/node_modules/**",
26
+ "**/dist/**",
27
+ "**/build/**"
28
+ ]
29
+ };
30
+ export {
31
+ ComponentFilePatterns,
32
+ StyleFilePatterns
33
+ };
@@ -0,0 +1,26 @@
1
+ export interface FilePattern {
2
+ include: string[];
3
+ exclude?: string[];
4
+ }
5
+ export interface ScanOptions {
6
+ patterns: FilePattern;
7
+ batchSize?: number;
8
+ }
9
+ export declare class FileScanner {
10
+ private static DEFAULT_BATCH_SIZE;
11
+ /**
12
+ * Scans directory for files matching the given patterns
13
+ * @param directory Base directory to scan
14
+ * @param options Scanning options including patterns and batch size
15
+ * @returns Array of file paths in batches
16
+ */
17
+ static scanFiles(directory: string, options: ScanOptions): Promise<string[][]>;
18
+ /**
19
+ * Validates that files exist and are readable
20
+ */
21
+ private static validateFiles;
22
+ /**
23
+ * Splits array of files into batches
24
+ */
25
+ private static createBatches;
26
+ }
@@ -0,0 +1,69 @@
1
+ // src/services/file-scanner.ts
2
+ import { promises as fs } from "fs";
3
+ import { glob } from "glob";
4
+ import { Logger } from "../utils/logger.js";
5
+ var FileScanner = class {
6
+ static DEFAULT_BATCH_SIZE = 100;
7
+ /**
8
+ * Scans directory for files matching the given patterns
9
+ * @param directory Base directory to scan
10
+ * @param options Scanning options including patterns and batch size
11
+ * @returns Array of file paths in batches
12
+ */
13
+ static async scanFiles(directory, options) {
14
+ try {
15
+ Logger.debug(`Scanning directory: ${directory}`);
16
+ Logger.debug(`Include patterns: ${options.patterns.include.join(", ")}`);
17
+ const allFiles = [];
18
+ for (const pattern of options.patterns.include) {
19
+ const files = await glob(pattern, {
20
+ cwd: directory,
21
+ ignore: options.patterns.exclude,
22
+ withFileTypes: true,
23
+ dot: true
24
+ // Include .dot files
25
+ }).then((matches) => matches.filter((match) => match.isFile()).map((match) => {
26
+ return match.fullpath();
27
+ }));
28
+ allFiles.push(...files);
29
+ }
30
+ const uniqueFiles = [...new Set(allFiles)];
31
+ const validFiles = await this.validateFiles(uniqueFiles);
32
+ const batchSize = options.batchSize || this.DEFAULT_BATCH_SIZE;
33
+ const batches = this.createBatches(validFiles, batchSize);
34
+ Logger.debug(`Found ${validFiles.length} files, split into ${batches.length} batches`);
35
+ return batches;
36
+ } catch (error) {
37
+ Logger.error(`Failed to scan files: ${error.message}`);
38
+ throw error;
39
+ }
40
+ }
41
+ /**
42
+ * Validates that files exist and are readable
43
+ */
44
+ static async validateFiles(files) {
45
+ const validFiles = [];
46
+ for (const file of files) {
47
+ try {
48
+ await fs.access(file, fs.constants.R_OK);
49
+ validFiles.push(file);
50
+ } catch (error) {
51
+ Logger.warning(`Skipping inaccessible file: ${file}`);
52
+ }
53
+ }
54
+ return validFiles;
55
+ }
56
+ /**
57
+ * Splits array of files into batches
58
+ */
59
+ static createBatches(files, batchSize) {
60
+ const batches = [];
61
+ for (let i = 0; i < files.length; i += batchSize) {
62
+ batches.push(files.slice(i, i + batchSize));
63
+ }
64
+ return batches;
65
+ }
66
+ };
67
+ export {
68
+ FileScanner
69
+ };
@@ -0,0 +1,17 @@
1
+ import { LintResult } from '../types';
2
+ export interface LintOptions {
3
+ fix?: boolean;
4
+ configPath?: string;
5
+ maxWorkers?: number;
6
+ timeoutMs?: number;
7
+ }
8
+ export declare class LintRunner {
9
+ /**
10
+ * Run linting on batches of files
11
+ */
12
+ static runLinting(fileBatches: string[][], workerType: 'style' | 'component', options?: LintOptions): Promise<LintResult[]>;
13
+ /**
14
+ * Process and normalize worker results
15
+ */
16
+ private static processResults;
17
+ }