@salesforce-ux/slds-linter 0.0.12-alpha → 0.0.12-alpha.6

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/README.md +54 -32
  2. package/build/commands/lint-components.d.ts +2 -0
  3. package/build/commands/lint-components.js +52 -0
  4. package/build/commands/lint-styles.d.ts +2 -0
  5. package/build/commands/lint-styles.js +52 -0
  6. package/build/commands/lint.d.ts +2 -0
  7. package/build/commands/lint.js +89 -0
  8. package/build/commands/report.d.ts +2 -0
  9. package/build/commands/report.js +53 -0
  10. package/build/index.d.ts +2 -1
  11. package/build/index.js +25 -0
  12. package/build/services/__tests__/file-scanner.test.js +47 -0
  13. package/build/services/batch-processor.d.ts +29 -0
  14. package/build/services/batch-processor.js +84 -0
  15. package/build/services/config.resolver.d.ts +6 -0
  16. package/build/services/config.resolver.js +23 -0
  17. package/build/services/file-patterns.d.ts +3 -0
  18. package/build/services/file-patterns.js +33 -0
  19. package/build/services/file-scanner.d.ts +26 -0
  20. package/build/services/file-scanner.js +69 -0
  21. package/build/services/lint-runner.d.ts +17 -0
  22. package/build/services/lint-runner.js +68 -0
  23. package/build/services/report-generator.d.ts +20 -0
  24. package/build/services/report-generator.js +119 -0
  25. package/build/types/index.d.ts +51 -0
  26. package/build/types/index.js +0 -0
  27. package/build/utils/cli-args.d.ts +3 -0
  28. package/build/utils/cli-args.js +31 -0
  29. package/build/utils/editorLinkUtil.d.ts +21 -0
  30. package/build/utils/editorLinkUtil.js +21 -0
  31. package/build/utils/lintResultsUtil.d.ts +7 -0
  32. package/build/utils/lintResultsUtil.js +43 -0
  33. package/build/utils/logger.d.ts +7 -0
  34. package/build/utils/logger.js +24 -0
  35. package/build/workers/base.worker.d.ts +15 -0
  36. package/build/workers/base.worker.js +44 -0
  37. package/build/workers/eslint.worker.js +50 -0
  38. package/build/workers/stylelint.worker.d.ts +1 -0
  39. package/build/workers/stylelint.worker.js +40 -0
  40. package/package.json +40 -32
  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
package/README.md CHANGED
@@ -2,62 +2,84 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- SLDS Linter provides custom linting rules specifically built for Salesforce Lightning Design System 2 (SLDS 2 beta). SLDS Linter is designed to uplift your Cascading Style Sheet (CSS), Lightning Web Component (LWC), and Aura component files to SLDS 2 and to conform to SLDS best practices. SLDS Linter rules allow you to maintain consistent styling and identify common issues when working with Lightning components.
5
+ SLDS Linter provides custom linting rules specifically built for Salesforce Lightning Design System 2 (SLDS 2 beta). SLDS Linter is designed to uplift your Cascading Style Sheet (CSS), Lightning Web Component (LWC), and Aura component files to SLDS 2 and to conform to SLDS best practices. SLDS Linter rules allow you to maintain consistent styling and identify common issues when working with Lightning components.
6
6
 
7
7
  ## Features
8
8
 
9
- * Component Linting:
9
+ - Component Linting:
10
10
  The utility supports linting for two types of Salesforce Lightning components:
11
11
 
12
- * LWC and Aura components.
12
+ - LWC and Aura components.
13
13
  LWC Components (.html): Linting is applied to Lightning Web Components.
14
- * Aura Components (.cmp): Linting is applied to Aura Components.
14
+ - Aura Components (.cmp): Linting is applied to Aura Components.
15
15
 
16
- - Stylelint for CSS Files:
16
+ * Stylelint for CSS Files:
17
17
  Stylelint rules are applied to .css files associated with the components. This ensures consistent styling practices are followed throughout the project.
18
18
 
19
19
  Follow the below instructions to integrate SLDS Linter into your project.
20
20
 
21
21
  ---
22
22
 
23
- ### Set Up SLDS Linter in Your Component Repository
23
+ ## Set Up SLDS Linter in Your Component Repository
24
24
 
25
- To install the SLDS Linter Utility in your project, you can use npm:
25
+ ### Pre-requisites
26
26
 
27
- ```
28
- npm install @salesforce-ux/slds-linter --save-dev
29
- ```
27
+ SLDS Linter CLI tool works best with the [Active LTS](https://nodejs.org/en/about/previous-releases) version of Node.js.
30
28
 
31
- ### Command-Line Interface (CLI)
29
+ #### **Minimum Required Node.js Version**
30
+ - The minimum supported Node.js version is **v20.18.3**.
31
+ - We recommend using the latest **Active LTS** release for the best performance and compatibility.
32
32
 
33
- To see what all options does slds-linter provide please run `npx @salesforce-ux/slds-linter --help` which gives the below output.
34
33
 
35
- ```
36
- Usage: npx @salesforce-ux/slds-linter [command]
34
+ ### Command-Line Interface (CLI)
37
35
 
38
- Commands:
39
- lint Run both ESLint and Stylelint
40
- lint:styles Run only Stylelint
41
- lint:components Run only ESLint
42
- fix Fix auto-fixable issues
43
- report Generate a linting report
36
+ To see what all options does slds-linter provide please run `npx @salesforce-ux/slds-linter@latest --help` which gives the below output.
37
+ For the first time, it will ask to install the package. Please reply with `y` as yes to install the package.
44
38
 
45
- Options:
46
- --version Show version number [boolean]
47
- --help Show help [boolean]
48
- ```
39
+ ```
40
+ Usage: npx @salesforce-ux/slds-linter@latest [options] [command]
49
41
 
50
- - `npx @salesforce-ux/slds-linter lint`- Runs the ESlint and Stylelint rules on your HTML/CSS/CMP files and outputs issues.
51
- - `npx @salesforce-ux/slds-linter lint:styles` - Runs the Stylelint rules on your CSS files and outputs issues.
52
- - `npx @salesforce-ux/slds-linter lint:components` - Runs the ESlint rules on your HTML/CMP files and outputs issues.
53
- - `npx @salesforce-ux/slds-linter fix`: Attempts to automatically fix violations.
54
- - `npx @salesforce-ux/slds-linter report`: Generates a SARIF report for static analysis.
42
+ A CLI tool for linting styles and components
55
43
 
44
+ Options:
45
+ -V, --version output the version number
46
+ -h, --help display help for command
56
47
 
57
- 1. Run `npx @salesforce-ux/slds-linter lint` to see the lint output on terminal. For specific files, you can go ahead with either `npx @salesforce-ux/slds-linter lint:styles` for lint errors within css files or `npx @salesforce-ux/slds-linter lint:components` for lint errors within html/cmp files.
58
- 2. To run SLDS Linter, in Terminal, run `npx @salesforce-ux/slds-linter report` to generate a Sarif report in the project root directory. It will be named as `slds-linter-report.sarif`.
48
+ Commands:
49
+ lint:styles [options] Run stylelint on all style files
50
+ lint:components [options] Run eslint on all markup files
51
+ lint [options] Run both style and component linting
52
+ report [options] Generate SARIF report from linting results
53
+ help [command] display help for command
54
+ ```
55
+
56
+ - `npx @salesforce-ux/slds-linter lint` - Runs the ESlint and Stylelint rules on your HTML/CSS/CMP files and outputs issues.
57
+ - `npx @salesforce-ux/slds-linter lint:styles` - Runs the Stylelint rules on your CSS files and outputs issues.
58
+ - `npx @salesforce-ux/slds-linter lint:components` - Runs the ESlint rules on your HTML/CMP files and outputs issues.
59
+ - `npx @salesforce-ux/slds-linter fix`: Attempts to automatically fix violations.
60
+ - `npx @salesforce-ux/slds-linter report`: Generates a SARIF report for static analysis.
61
+
62
+ #### Options available with each command
63
+
64
+ | **Options** | **Description** | **Availability** |
65
+ | ------------------------ | ---------------------------------------------------------------------------- | ------------------------------------------ |
66
+ | `-d, --directory <path>` | Target directory to scan (defaults to current directory) | lint, lint:styles, lint:components, report |
67
+ | `-o, --output <path>` | Output directory for reports (defaults to current directory) | report |
68
+ | `--fix` | Automatically fix problems | lint, lint:styles, lint:components |
69
+ | `--config <path>` | Path to eslint/stylelint config file', DEFAULT_ESLINT_CONFIG_PATH | lint:styles, lint:components |
70
+ | `--config-style <path>` | PPath to stylelint config file', DEFAULT_STYLELINT_CONFIG_PATH | lint |
71
+ | `--config-eslint <path>` | Path to eslint config file', DEFAULT_ESLINT_CONFIG_PATH | lint |
72
+ | `--editor <editor>` | Editor to open files with (e.g., vscode, atom, sublime). Defaults to vscode | lint,lint:styles, lint:components |
73
+
74
+
75
+ These options can also be visualised by using `--help` with each command. For example: Running `slds-linter lint --help` will give the options which can be used along with `lint`.
76
+
77
+ #### Detailed Steps
78
+
79
+ 1. Run `npx @salesforce-ux/slds-linter lint` to see the lint output on terminal. For specific files, you can go ahead with either `npx @salesforce-ux/slds-linter lint:styles` for lint errors within css files or `npx @salesforce-ux/slds-linter lint:components` for lint errors within html/cmp files. To run the linting only on a specific folder, use the option `-d` to specify the directory to be linted.
80
+ 2. To run SLDS Linter, in Terminal, run `npx @salesforce-ux/slds-linter report` to generate a Sarif report in the project root directory. To run it on a different directory, use `-d` to run the report on that directory. For output, `-o` can be used to specify a different output folder for the genreated sarif. It will be named as `slds-linter-report.sarif`.
59
81
  3. Open the generated Sarif file.
60
82
  4. Make a note of how many components SLDS Linter has identified that you must update.
61
83
  5. Run `npx @salesforce-ux/slds-linter fix` to automatically fix validation errors in bulk.
62
84
 
63
- For any questions or issues, feel free to reach out to the maintainers or open an issue in the repository.
85
+ For any questions or issues, feel free to reach out to the maintainers or open an issue in the repository.
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerLintComponentsCommand(program: Command): void;
@@ -0,0 +1,52 @@
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").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
+ config: DEFAULT_ESLINT_CONFIG_PATH
17
+ });
18
+ Logger.info(chalk.blue("Scanning for files..."));
19
+ const fileBatches = await FileScanner.scanFiles(normalizedOptions.directory, {
20
+ patterns: ComponentFilePatterns,
21
+ batchSize: 100
22
+ });
23
+ const totalFiles = fileBatches.reduce((sum, batch) => sum + batch.length, 0);
24
+ Logger.info(chalk.blue(`Scanned ${totalFiles} file(s).`));
25
+ Logger.info(chalk.blue("Running linting..."));
26
+ const results = await LintRunner.runLinting(fileBatches, "component", {
27
+ fix: options.fix,
28
+ configPath: options.config
29
+ });
30
+ printLintResults(results, normalizedOptions.editor);
31
+ const errorCount = results.reduce((sum, r) => sum + r.errors.length, 0);
32
+ const warningCount = results.reduce((sum, r) => sum + r.warnings.length, 0);
33
+ Logger.info(
34
+ chalk.blue(
35
+ `
36
+ Summary: Processed ${totalFiles} file(s) with ${chalk.red(
37
+ errorCount.toString()
38
+ )} error(s) and ${chalk.yellow(warningCount.toString())} warning(s).`
39
+ )
40
+ );
41
+ const elapsedTime = ((Date.now() - startTime) / 1e3).toFixed(2);
42
+ Logger.success(chalk.green(`Component files linting completed in ${elapsedTime} seconds.`));
43
+ process.exit(errorCount > 0 ? 1 : 0);
44
+ } catch (error) {
45
+ Logger.error(chalk.red(`Error during linting: ${error.message}`));
46
+ process.exit(1);
47
+ }
48
+ });
49
+ }
50
+ export {
51
+ registerLintComponentsCommand
52
+ };
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerLintStylesCommand(program: Command): void;
@@ -0,0 +1,52 @@
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").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
+ configStyle: DEFAULT_STYLELINT_CONFIG_PATH
17
+ });
18
+ Logger.info(chalk.blue("Scanning for style files..."));
19
+ const fileBatches = await FileScanner.scanFiles(normalizedOptions.directory, {
20
+ patterns: StyleFilePatterns,
21
+ batchSize: 100
22
+ });
23
+ const totalFiles = fileBatches.reduce((sum, batch) => sum + batch.length, 0);
24
+ Logger.info(chalk.blue(`Scanned ${totalFiles} file(s).`));
25
+ Logger.info(chalk.blue("Running stylelint..."));
26
+ const results = await LintRunner.runLinting(fileBatches, "style", {
27
+ fix: options.fix,
28
+ configPath: options.config
29
+ });
30
+ printLintResults(results, normalizedOptions.editor);
31
+ const errorCount = results.reduce((sum, r) => sum + r.errors.length, 0);
32
+ const warningCount = results.reduce((sum, r) => sum + r.warnings.length, 0);
33
+ Logger.info(
34
+ chalk.blue(
35
+ `
36
+ Summary: Processed ${totalFiles} file(s) with ${chalk.red(
37
+ errorCount.toString()
38
+ )} error(s) and ${chalk.yellow(warningCount.toString())} warning(s).`
39
+ )
40
+ );
41
+ const elapsedTime = ((Date.now() - startTime) / 1e3).toFixed(2);
42
+ Logger.success(chalk.green(`Style files linting completed in ${elapsedTime} seconds.`));
43
+ process.exit(errorCount > 0 ? 1 : 0);
44
+ } catch (error) {
45
+ Logger.error(chalk.red(`Error during linting: ${error.message}`));
46
+ process.exit(1);
47
+ }
48
+ });
49
+ }
50
+ export {
51
+ registerLintStylesCommand
52
+ };
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerLintCommand(program: Command): void;
@@ -0,0 +1,89 @@
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").option("--config-eslint <path>", "Path to eslint config file").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
+ configStyle: DEFAULT_STYLELINT_CONFIG_PATH,
19
+ configEslint: DEFAULT_ESLINT_CONFIG_PATH
20
+ });
21
+ Logger.info(chalk.blue("\nScanning style files..."));
22
+ const styleFileBatches = await FileScanner.scanFiles(normalizedOptions.directory, {
23
+ patterns: StyleFilePatterns,
24
+ batchSize: 100
25
+ });
26
+ const totalStyleFiles = styleFileBatches.reduce((sum, batch) => sum + batch.length, 0);
27
+ Logger.info(chalk.blue(`Found ${totalStyleFiles} style file(s). Running stylelint...
28
+ `));
29
+ const styleResults = await LintRunner.runLinting(styleFileBatches, "style", {
30
+ fix: options.fix,
31
+ configPath: options.configStyle
32
+ });
33
+ styleResults.forEach((result) => {
34
+ const hasErrors = result.errors?.length > 0;
35
+ const hasWarnings = result.warnings?.length > 0;
36
+ if (!hasErrors && !hasWarnings) return;
37
+ const absolutePath = result.filePath || "";
38
+ const relativeFile = path.relative(process.cwd(), absolutePath) || "Unknown file";
39
+ Logger.info(`
40
+ ${chalk.bold(relativeFile)}`);
41
+ result.errors?.forEach((err) => {
42
+ const lineCol = `${err.line}:${err.column}`;
43
+ const clickable = createClickableLineCol(lineCol, absolutePath, err.line, err.column, normalizedOptions.editor);
44
+ const ruleId = err.ruleId ? chalk.dim(err.ruleId) : "";
45
+ Logger.error(` ${clickable} ${err.message} ${ruleId}`);
46
+ });
47
+ result.warnings?.forEach((warn) => {
48
+ const lineCol = `${warn.line}:${warn.column}`;
49
+ const clickable = createClickableLineCol(lineCol, absolutePath, warn.line, warn.column, normalizedOptions.editor);
50
+ const ruleId = warn.ruleId ? chalk.dim(warn.ruleId) : "";
51
+ Logger.warning(` ${clickable} ${warn.message} ${ruleId}`);
52
+ });
53
+ });
54
+ const styleErrorCount = styleResults.reduce((sum, r) => sum + r.errors.length, 0);
55
+ const styleWarningCount = styleResults.reduce((sum, r) => sum + r.warnings.length, 0);
56
+ Logger.info(chalk.blue("\nScanning component files..."));
57
+ const componentFileBatches = await FileScanner.scanFiles(normalizedOptions.directory, {
58
+ patterns: ComponentFilePatterns,
59
+ batchSize: 100
60
+ });
61
+ const totalComponentFiles = componentFileBatches.reduce((sum, batch) => sum + batch.length, 0);
62
+ Logger.info(chalk.blue(`Found ${totalComponentFiles} component file(s). Running eslint...
63
+ `));
64
+ const componentResults = await LintRunner.runLinting(componentFileBatches, "component", {
65
+ fix: options.fix,
66
+ configPath: options.configEslint
67
+ });
68
+ printLintResults(componentResults, normalizedOptions.editor);
69
+ const componentErrorCount = componentResults.reduce((sum, r) => sum + r.errors.length, 0);
70
+ const componentWarningCount = componentResults.reduce((sum, r) => sum + r.warnings.length, 0);
71
+ const totalErrors = styleErrorCount + componentErrorCount;
72
+ const totalWarnings = styleWarningCount + componentWarningCount;
73
+ Logger.info(
74
+ `
75
+ ${chalk.red(`${totalErrors} error${totalErrors !== 1 ? "s" : ""}`)} ${chalk.yellow(`${totalWarnings} warning${totalWarnings !== 1 ? "s" : ""}`)}`
76
+ );
77
+ const elapsedTime = ((Date.now() - startTime) / 1e3).toFixed(2);
78
+ Logger.success(chalk.green(`
79
+ Full linting completed in ${elapsedTime} seconds.`));
80
+ process.exit(totalErrors > 0 ? 1 : 0);
81
+ } catch (error) {
82
+ Logger.error(chalk.red(`Failed to complete linting: ${error.message}`));
83
+ process.exit(1);
84
+ }
85
+ });
86
+ }
87
+ export {
88
+ registerLintCommand
89
+ };
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerReportCommand(program: Command): void;
@@ -0,0 +1,53 @@
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").option("--config-eslint <path>", "Path to eslint config file").action(async (options) => {
13
+ const spinner = ora("Starting report generation...").start();
14
+ try {
15
+ const normalizedOptions = normalizeCliOptions(options, {
16
+ configStyle: DEFAULT_STYLELINT_CONFIG_PATH,
17
+ configEslint: DEFAULT_ESLINT_CONFIG_PATH
18
+ });
19
+ spinner.text = "Running styles linting...";
20
+ const styleFileBatches = await FileScanner.scanFiles(normalizedOptions.directory, {
21
+ patterns: StyleFilePatterns,
22
+ batchSize: 100
23
+ });
24
+ const styleResults = await LintRunner.runLinting(styleFileBatches, "style", {
25
+ configPath: options.configStyle
26
+ });
27
+ spinner.text = "Running components linting...";
28
+ const componentFileBatches = await FileScanner.scanFiles(normalizedOptions.directory, {
29
+ patterns: ComponentFilePatterns,
30
+ batchSize: 100
31
+ });
32
+ const componentResults = await LintRunner.runLinting(componentFileBatches, "component", {
33
+ configPath: options.configEslint
34
+ });
35
+ spinner.text = "Generating combined report...";
36
+ const combinedReportPath = path.join(normalizedOptions.output, "slds-linter-report.sarif");
37
+ await ReportGenerator.generateSarifReport([...styleResults, ...componentResults], {
38
+ outputPath: combinedReportPath,
39
+ toolName: "slds-linter",
40
+ toolVersion: LINTER_CLI_VERSION
41
+ });
42
+ spinner.succeed("Report generation completed");
43
+ process.exit(0);
44
+ } catch (error) {
45
+ spinner?.fail("Report generation failed");
46
+ Logger.error(`Failed to generate report: ${error.message}`);
47
+ process.exit(1);
48
+ }
49
+ });
50
+ }
51
+ export {
52
+ registerReportCommand
53
+ };
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,25 @@
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
+ import pkg from "../package.json" with { type: "json" };
11
+ process.on("unhandledRejection", (error) => {
12
+ Logger.error(`Unhandled rejection: ${error}`);
13
+ process.exit(1);
14
+ });
15
+ process.on("uncaughtException", (error) => {
16
+ Logger.error(`Uncaught exception: ${error}`);
17
+ process.exit(1);
18
+ });
19
+ var program = new Command();
20
+ program.name("npx @salesforce-ux/slds-linter@latest").description(pkg.description).version(pkg.version).showHelpAfterError();
21
+ registerLintStylesCommand(program);
22
+ registerLintComponentsCommand(program);
23
+ registerLintCommand(program);
24
+ registerReportCommand(program);
25
+ program.parse(process.argv);
@@ -0,0 +1,47 @@
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
+ import { fileURLToPath } from "url";
7
+ var __filename = fileURLToPath(import.meta.url);
8
+ var __dirname = path.dirname(__filename);
9
+ describe("FileScanner", () => {
10
+ const testDir = path.join(__dirname, "fixtures");
11
+ beforeAll(async () => {
12
+ await mkdir(testDir, { recursive: true });
13
+ await writeFile(
14
+ path.join(testDir, "test.css"),
15
+ "body { color: red; }"
16
+ );
17
+ await writeFile(
18
+ path.join(testDir, "test.scss"),
19
+ "$color: red;"
20
+ );
21
+ });
22
+ afterAll(async () => {
23
+ await rm(testDir, { recursive: true });
24
+ });
25
+ it("should scan and batch files correctly", async () => {
26
+ const options = {
27
+ patterns: StyleFilePatterns,
28
+ batchSize: 1
29
+ };
30
+ const batches = await FileScanner.scanFiles(testDir, options);
31
+ expect(batches).toHaveLength(2);
32
+ expect(batches[0]).toHaveLength(1);
33
+ expect(batches[1]).toHaveLength(1);
34
+ expect(batches[0][0]).toMatch(/test\.(css|scss)$/);
35
+ expect(batches[1][0]).toMatch(/test\.(css|scss)$/);
36
+ });
37
+ it("should handle invalid files gracefully", async () => {
38
+ const options = {
39
+ patterns: {
40
+ include: ["**/*.nonexistent"],
41
+ exclude: []
42
+ }
43
+ };
44
+ const batches = await FileScanner.scanFiles(testDir, options);
45
+ expect(batches).toHaveLength(0);
46
+ });
47
+ });
@@ -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
+ };