@salesforce-ux/slds-linter 1.1.0 → 1.2.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.
@@ -27,7 +27,7 @@ function registerLintCommand(program) {
27
27
  ));
28
28
  }
29
29
  const lintResults = await lint(normalizedOptions);
30
- const { totalErrors } = printLintResults(lintResults, normalizedOptions.editor);
30
+ const { totalErrors } = printLintResults(lintResults, { editor: normalizedOptions.editor });
31
31
  const elapsedTime = ((Date.now() - startTime) / 1e3).toFixed(2);
32
32
  Logger.newLine().success(`Linting completed in ${elapsedTime} seconds.`);
33
33
  process.exit(totalErrors > 0 ? 1 : 0);
@@ -17,4 +17,13 @@ export declare function lint(config: LintConfig): Promise<LintResult[]>;
17
17
  * @throws Error if report generation fails
18
18
  */
19
19
  export declare function report(config: ReportConfig, results?: LintResult[]): Promise<Readable>;
20
+ /**
21
+ * This function supports user to supply array of files to be linted
22
+ *
23
+ * @param files Array of file paths to be linted
24
+ * @param config Linting configuration options
25
+ * @returns Promise resolving to an array of lint results
26
+ * @throws Error if linting fails or if any file is not found
27
+ */
28
+ export declare function lintFiles(files: string[], config: LintConfig): Promise<LintResult[]>;
20
29
  export type { LintResult, LintResultEntry, LintConfig, ReportConfig, ExitCode, WorkerResult, SarifResultEntry } from '../types';
@@ -69,7 +69,31 @@ async function report(config, results) {
69
69
  throw new Error(errorMessage);
70
70
  }
71
71
  }
72
+ async function lintFiles(files, config) {
73
+ try {
74
+ Logger.debug("Starting linting with Node API");
75
+ const normalizedConfig = normalizeCliOptions(config, {
76
+ configEslint: DEFAULT_ESLINT_CONFIG_PATH
77
+ });
78
+ const batches = FileScanner.createBatches(files, FileScanner.DEFAULT_BATCH_SIZE);
79
+ Logger.debug(
80
+ `Found ${files.length} files, split into ${batches.length} batches`
81
+ );
82
+ const results = await LintRunner.runLinting(batches, {
83
+ fix: normalizedConfig.fix,
84
+ configPath: normalizedConfig.configEslint,
85
+ // when linting files, use the directory of the files as the working directory
86
+ cwd: normalizedConfig.directory
87
+ });
88
+ return results;
89
+ } catch (error) {
90
+ const errorMessage = `Linting failed: ${error.message}`;
91
+ Logger.error(errorMessage);
92
+ throw new Error(errorMessage);
93
+ }
94
+ }
72
95
  export {
73
96
  lint,
97
+ lintFiles,
74
98
  report
75
99
  };
package/build/index.js CHANGED
@@ -19,7 +19,7 @@ process.on("uncaughtException", (error) => {
19
19
  var program = new Command();
20
20
  program.name("npx @salesforce-ux/slds-linter@latest").showHelpAfterError();
21
21
  function registerVersion() {
22
- program.description("SLDS Linter CLI tool for linting styles and components").version("1.1.0");
22
+ program.description("SLDS Linter CLI tool for linting styles and components").version("1.2.0");
23
23
  }
24
24
  registerLintCommand(program);
25
25
  registerReportCommand(program);
@@ -3,7 +3,7 @@ import { resolvePath } from "../utils/nodeVersionUtil.js";
3
3
  import ruleMessages from "@salesforce-ux/eslint-plugin-slds/rule-messages";
4
4
  var DEFAULT_ESLINT_CONFIG_PATH = resolvePath("@salesforce-ux/eslint-plugin-slds/config", import.meta);
5
5
  var ESLINT_VERSION = "9.36.0";
6
- var LINTER_CLI_VERSION = "1.1.0";
6
+ var LINTER_CLI_VERSION = "1.2.0";
7
7
  var getRuleDescription = (ruleId) => {
8
8
  const ruleIdWithoutNameSpace = `${ruleId}`.replace(/\@salesforce-ux\//, "").replace(/^slds\//, "");
9
9
  return ruleMessages[ruleIdWithoutNameSpace]?.description || "--";
@@ -1,6 +1,6 @@
1
1
  import { ScanOptions, ScanResult } from "../types";
2
2
  export declare class FileScanner {
3
- private static DEFAULT_BATCH_SIZE;
3
+ static DEFAULT_BATCH_SIZE: number;
4
4
  /**
5
5
  * Scans directory for files matching the given patterns
6
6
  * @param directory Base directory to scan
@@ -15,5 +15,5 @@ export declare class FileScanner {
15
15
  /**
16
16
  * Splits array of files into batches
17
17
  */
18
- private static createBatches;
18
+ static createBatches(files: string[], batchSize: number): string[][];
19
19
  }
@@ -18,7 +18,8 @@ var LintRunner = class {
18
18
  const configPath = await ConfigLoader.processConfig(options.configPath);
19
19
  const workerConfig = {
20
20
  configPath,
21
- fix: options.fix
21
+ fix: options.fix,
22
+ cwd: options.cwd
22
23
  };
23
24
  const results = await BatchProcessor.processBatches(
24
25
  fileBatches,
@@ -6,7 +6,7 @@ import { SarifBuilder, SarifRunBuilder, SarifResultBuilder, SarifRuleBuilder } f
6
6
  import { createWriteStream } from "fs";
7
7
  import { JsonStreamStringify } from "json-stream-stringify";
8
8
  import { getRuleDescription } from "./config.resolver.js";
9
- import { parseText, replaceNamespaceinRules, transformedResults } from "../utils/lintResultsUtil.js";
9
+ import { replaceNamespaceinRules, toCSVRow, toSarifResult } from "../utils/lintResultsUtil.js";
10
10
  import { processArtifacts } from "./artifact-processor.js";
11
11
  var ReportGenerator = class {
12
12
  /**
@@ -97,7 +97,7 @@ var ReportGenerator = class {
97
97
  */
98
98
  static addResultsToSarif(runBuilder, lintResult) {
99
99
  lintResult.messages.forEach((message) => {
100
- const resultBuilder = new SarifResultBuilder().initSimple(transformedResults(lintResult, message, "error"));
100
+ const resultBuilder = new SarifResultBuilder().initSimple(toSarifResult(lintResult, message));
101
101
  runBuilder.addResult(resultBuilder);
102
102
  });
103
103
  }
@@ -123,7 +123,6 @@ var CsvReportGenerator = class {
123
123
  * Convert lint results to CSV-compatible data format
124
124
  */
125
125
  static convertResultsToCsvData(results) {
126
- const cwd = process.cwd();
127
126
  const csvConfig = mkConfig({
128
127
  fieldSeparator: ",",
129
128
  quoteStrings: true,
@@ -132,24 +131,13 @@ var CsvReportGenerator = class {
132
131
  useBom: true,
133
132
  useKeysAsHeaders: true
134
133
  });
135
- const transformedResults2 = [];
134
+ const transformedResults = [];
136
135
  results.forEach((result) => {
137
136
  result.messages.forEach((message) => {
138
- transformedResults2.push({
139
- "File Path": path.relative(cwd, result.filePath),
140
- "Message": parseText(message.message),
141
- "Severity": "error",
142
- "Rule ID": replaceNamespaceinRules(message.ruleId || "N/A"),
143
- "Start Line": message.line,
144
- "Start Column": message.column,
145
- "End Line": message.endLine || message.line,
146
- // Default to start line if missing
147
- "End Column": message.endColumn || message.column
148
- // Default to start column if missing
149
- });
137
+ transformedResults.push(toCSVRow(result, message));
150
138
  });
151
139
  });
152
- return generateCsv(csvConfig)(transformedResults2);
140
+ return generateCsv(csvConfig)(transformedResults);
153
141
  }
154
142
  };
155
143
  export {
@@ -27,18 +27,17 @@ export interface LintConfig extends BaseConfig {
27
27
  export interface ReportConfig extends BaseConfig {
28
28
  format?: 'sarif' | 'csv';
29
29
  }
30
- export interface LintRunnerOptions {
31
- fix?: boolean;
32
- configPath?: string;
33
- maxWorkers?: number;
34
- timeoutMs?: number;
35
- }
36
30
  export type LintResultEntry = Linter.LintMessage;
37
31
  export type LintResult = ESLint.LintResult;
38
32
  export type ExitCode = 0 | 1 | 2;
39
33
  export interface WorkerConfig {
40
34
  configPath?: string;
41
35
  fix?: boolean;
36
+ cwd?: string;
37
+ }
38
+ export interface LintRunnerOptions extends WorkerConfig {
39
+ maxWorkers?: number;
40
+ timeoutMs?: number;
42
41
  }
43
42
  export interface WorkerResult {
44
43
  filePath: string;
@@ -74,3 +73,17 @@ export interface ScanResult {
74
73
  filesCount: number;
75
74
  batches: string[][];
76
75
  }
76
+ export interface PrintOptions {
77
+ editor?: string;
78
+ cwd?: string;
79
+ }
80
+ export interface CsvRowEntry {
81
+ "File Path": string;
82
+ "Message": string;
83
+ "Severity": string;
84
+ "Rule ID": string;
85
+ "Start Line": number;
86
+ "Start Column": number;
87
+ "End Line": number;
88
+ "End Column": number;
89
+ }
@@ -1,4 +1,4 @@
1
- import { LintResult, LintResultEntry, SarifResultEntry, LintResultSummary } from '../types';
1
+ import { LintResult, LintResultEntry, SarifResultEntry, LintResultSummary, PrintOptions, CsvRowEntry } from '../types';
2
2
  /**
3
3
  *
4
4
  * @param id - Rule id
@@ -17,5 +17,6 @@ export declare function parseText(text: string): string;
17
17
  * @param results - Array of lint results.
18
18
  * @param editor - The chosen editor for clickable links (e.g., "vscode", "atom", "sublime"). If not provided, will auto-detect.
19
19
  */
20
- export declare function printLintResults(results: LintResult[], editor?: string): LintResultSummary;
21
- export declare function transformedResults(lintResult: LintResult, entry: LintResultEntry, level: 'error' | 'warning'): SarifResultEntry;
20
+ export declare function printLintResults(results: LintResult[], options?: PrintOptions): LintResultSummary;
21
+ export declare function toSarifResult(lintResult: LintResult, entry: LintResultEntry): SarifResultEntry;
22
+ export declare function toCSVRow(lintResult: LintResult, entry: LintResultEntry): CsvRowEntry;
@@ -4,7 +4,7 @@ import { createClickableLineCol } from "./editorLinkUtil.js";
4
4
  import { Logger } from "../utils/logger.js";
5
5
  import { Colors } from "./colors.js";
6
6
  function replaceNamespaceinRules(id) {
7
- return id.includes("@salesforce-ux/") ? id.replace("@salesforce-ux/", "") : id;
7
+ return id?.replace("@salesforce-ux/", "") ?? "N/A";
8
8
  }
9
9
  function parseText(text) {
10
10
  let safeText = text;
@@ -44,7 +44,7 @@ function printFixableViolationsSummary(fixableErrors, fixableWarnings) {
44
44
  }
45
45
  console.log(` ${fixableBreakdown.join(" and ")} potentially fixable with the \`--fix\` option.`);
46
46
  }
47
- function printLintResults(results, editor) {
47
+ function printLintResults(results, options) {
48
48
  let totalErrors = 0;
49
49
  let totalWarnings = 0;
50
50
  let fixableErrors = 0;
@@ -52,7 +52,7 @@ function printLintResults(results, editor) {
52
52
  results.forEach((result) => {
53
53
  if (!result.messages || result.messages.length === 0) return;
54
54
  const absolutePath = result.filePath || "";
55
- const relativeFile = path.relative(process.cwd(), absolutePath) || "Unknown file";
55
+ const relativeFile = path.relative(options?.cwd || process.cwd(), absolutePath) || "Unknown file";
56
56
  console.log(`
57
57
  ${Colors.info.underline(relativeFile)}
58
58
  `);
@@ -68,7 +68,7 @@ ${Colors.info.underline(relativeFile)}
68
68
  if (msg.fix) fixableWarnings++;
69
69
  }
70
70
  const lineCol = msg.line && msg.column ? `${msg.line}:${msg.column}` : "-";
71
- const clickableLineCol = msg.line && msg.column ? createClickableLineCol(lineCol, absolutePath, msg.line, msg.column, editor) : lineCol;
71
+ const clickableLineCol = msg.line && msg.column ? createClickableLineCol(lineCol, absolutePath, msg.line, msg.column, options?.editor) : lineCol;
72
72
  const severityText = isError ? Colors.error("error") : Colors.warning("warning");
73
73
  const message = parseText(msg.message);
74
74
  const ruleId = msg.ruleId ? Colors.lowEmphasis(replaceNamespaceinRules(msg.ruleId)) : "";
@@ -90,21 +90,37 @@ ${Colors.info.underline(relativeFile)}
90
90
  }
91
91
  return { totalErrors, totalWarnings, fixableErrors, fixableWarnings };
92
92
  }
93
- function transformedResults(lintResult, entry, level) {
93
+ function getLevel(severity) {
94
+ return severity === 2 ? "error" : "warning";
95
+ }
96
+ function toSarifResult(lintResult, entry) {
94
97
  return {
95
98
  ruleId: replaceNamespaceinRules(entry.ruleId),
96
- level,
99
+ level: getLevel(entry.severity),
97
100
  messageText: parseText(entry.message),
98
101
  fileUri: path.relative(process.cwd(), lintResult.filePath),
99
102
  startLine: entry.line,
100
103
  startColumn: entry.column,
101
- endLine: entry.line,
102
- endColumn: entry.endColumn
104
+ endLine: entry.endLine || entry.line,
105
+ endColumn: entry.endColumn || entry.column
106
+ };
107
+ }
108
+ function toCSVRow(lintResult, entry) {
109
+ return {
110
+ "File Path": path.relative(process.cwd(), lintResult.filePath),
111
+ "Message": parseText(entry.message),
112
+ "Severity": getLevel(entry.severity),
113
+ "Rule ID": replaceNamespaceinRules(entry.ruleId),
114
+ "Start Line": entry.line,
115
+ "Start Column": entry.column,
116
+ "End Line": entry.endLine || entry.line,
117
+ "End Column": entry.endColumn || entry.column
103
118
  };
104
119
  }
105
120
  export {
106
121
  parseText,
107
122
  printLintResults,
108
123
  replaceNamespaceinRules,
109
- transformedResults
124
+ toCSVRow,
125
+ toSarifResult
110
126
  };
@@ -6,10 +6,14 @@ var ESLintWorker = class extends BaseWorker {
6
6
  eslint;
7
7
  constructor() {
8
8
  super();
9
- this.eslint = new ESLint({
9
+ const linterOptions = {
10
10
  overrideConfigFile: this.task.config.configPath,
11
11
  fix: this.task.config.fix
12
- });
12
+ };
13
+ if ("cwd" in this.task.config) {
14
+ linterOptions.cwd = this.task.config.cwd;
15
+ }
16
+ this.eslint = new ESLint(linterOptions);
13
17
  }
14
18
  async processFile(filePath) {
15
19
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce-ux/slds-linter",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "SLDS Linter CLI tool for linting styles and components",
5
5
  "keywords": [
6
6
  "lightning design system linter",
@@ -29,7 +29,7 @@
29
29
  ],
30
30
  "bin": "build/index.js",
31
31
  "dependencies": {
32
- "@salesforce-ux/eslint-plugin-slds": "1.1.0",
32
+ "@salesforce-ux/eslint-plugin-slds": "1.2.0",
33
33
  "@typescript-eslint/eslint-plugin": "^8.36.0",
34
34
  "@typescript-eslint/parser": "^8.36.0",
35
35
  "chalk": "^4.1.2",