@salesforce-ux/slds-linter 0.1.3 → 0.1.4-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.
package/README.md CHANGED
@@ -48,16 +48,14 @@ Usage: npx @salesforce-ux/slds-linter@latest [options] [command]
48
48
  SLDS Linter CLI tool for linting styles and components
49
49
 
50
50
  Options:
51
- -V, --version output the version number
52
- -h, --help display help for command
51
+ -V, --version output the version number
52
+ -h, --help display help for command
53
53
 
54
54
  Commands:
55
- lint:styles [options] Run stylelint on all style files
56
- lint:components [options] Run eslint on all markup files
57
- lint [options] Run both style and component linting
58
- report [options] Generate SARIF report from linting results
59
- emit [options] Emits the configuration files used by slds-linter cli
60
- help [command] display help for command
55
+ lint [options] [directory] Run both style and component linting
56
+ report [options] [directory] Generate SARIF report from linting results
57
+ emit [options] Emits the configuration files used by slds-linter cli
58
+ help [command] display help for command
61
59
  ```
62
60
 
63
61
  ## Run SLDS Linter
@@ -70,14 +68,12 @@ In your project root directory, follow these steps.
70
68
  2. Run `npx @salesforce-ux/slds-linter lint`.
71
69
  The linting output displayed in the console includes the row and column numbers on the left. Navigate to specific lines in your source code by clicking on the displayed numbers (Command + Click on Mac).
72
70
 
73
- 3. (Optional) To see lint errors only on CSS files, run `npx @salesforce-ux/slds-linter lint:styles` .
74
- 4. (Optional) To see lint errors only on component markup files (componentName.html or componentName.cmp files), run `npx @salesforce-ux/slds-linter lint:components` .
75
- 5. (Optional) To run SLDS Linter on a specific folder, add the `-d` option to specify the directory to be linted: `npx @salesforce-ux/slds-linter -d [directory name]` . This option accepts directories or folders using glob patterns, enabling flexible and efficient matching of multiple paths. For example, run `npx @salesforce-ux/slds-linter lint -d "com/CORE/**"` gives the lint output for all the files under `com/CORE`.
76
- 6. To produce a SARIF report in your project root directory and specify an output directory, run `npx @salesforce-ux/slds-linter report -o [output directory]`. The output file is named as `slds-linter-report.sarif`.
77
- 7. Open the generated `.sarif` report file.
78
- 8. Make a note of how many components SLDS Linter has identified that you must update.
79
- 9. (Optional) To automatically fix validation errors in bulk, run the `lint` command with the `fix` option, `npx @salesforce-ux/slds-linter lint --fix`.
80
- 7. (Optional) To emit the configuration files used by `slds-linter`, run `npx @salesforce-ux/slds-linter emit` in your component source directory. Note that this command defaults to current working directory. These configuration files are discovered by your VS Code ESLint and Stylelint extensions to display squiggly lines in CSS and HTML files when opened in your code editor.
71
+ 3. (Optional) To run SLDS Linter on a specific folder, specify the directory to be linted: `npx @salesforce-ux/slds-linter [directory name]` . This option accepts directories or folders using [glob](https://github.com/sindresorhus/globby?tab=readme-ov-file#globbing-patterns) patterns, enabling flexible and efficient matching of multiple paths. For example, run `npx @salesforce-ux/slds-linter lint "com/CORE/**"` gives the lint output for all the files under `com/CORE`.
72
+ 4. To produce a SARIF report in your project root directory and specify an output directory, run `npx @salesforce-ux/slds-linter report -o [output directory]`. The output file is named as `slds-linter-report.sarif`.
73
+ 5. Open the generated `.sarif` report file.
74
+ 6. Make a note of how many components SLDS Linter has identified that you must update.
75
+ 7. (Optional) To automatically fix validation errors in bulk, run the `lint` command with the `fix` option, `npx @salesforce-ux/slds-linter lint --fix`.
76
+ 8. (Optional) To emit the configuration files used by `slds-linter`, run `npx @salesforce-ux/slds-linter emit` in your component source directory. Note that this command defaults to current working directory. These configuration files are discovered by your VS Code ESLint and Stylelint extensions to display squiggly lines in CSS and HTML files when opened in your code editor.
81
77
 
82
78
 
83
79
  ### Troubleshoot SARIF Viewer Navigation
@@ -94,8 +90,6 @@ If the SARIF viewer doesn’t automatically go to the line of code when you clic
94
90
  Use these commands to run SLDS Linter rules. Review the output violations and fix any issues to uplift your code to SLDS best practices.
95
91
 
96
92
  - `npx @salesforce-ux/slds-linter lint`. Runs ESlint and Stylelint rules on HTML, CSS, and CMP files.
97
- - `npx @salesforce-ux/slds-linter lint:styles`. Runs the Stylelint rules on your CSS files.
98
- - `npx @salesforce-ux/slds-linter lint:components`. Runs the ESlint rules on your HTML/CMP files.
99
93
  - `npx @salesforce-ux/slds-linter report`. Generates a SARIF report for static analysis.
100
94
  - `npx @salesforce-ux/slds-linter emit`. Emits the configuration files used by `slds-linter`. Defaults to current directory.
101
95
 
@@ -103,13 +97,12 @@ These options are available on SLDS Linter commands.
103
97
 
104
98
  | **Option** | **Description** | **Availability** |
105
99
  | ------------------------ | ---------------------------------------------------------------------------- | ------------------------------------------ |
106
- | `-d, --directory <path>` | Target directory to scan (defaults to current directory). Accepts glob patterns. | `lint`, `lint:styles`, `lint:components`, `report` |
100
+ | `-d, --directory <path>` | Target directory to scan (defaults to current directory). Supports glob patterns. | `lint`, `report` |
107
101
  | `-o, --output <path>` | Output directory for reports (defaults to current directory) | `report` |
108
- | `--fix` | Automatically fix problems | `lint`, `lint:styles`, `lint:components` |
109
- | `--config <path>` | Path to eslint/stylelint config file' | `lint:styles`, `lint:components` |
110
- | `--config-style <path>` | Path to stylelint config file' | `lint` |
111
- | `--config-eslint <path>` | Path to eslint config file' | `lint` |
112
- | `--editor <editor>` | Editor to open files with (e.g., vscode, atom, sublime). Defaults to vscode | `lint`, `lint:styles`, `lint:components` |
102
+ | `--fix` | Automatically fix problems | `lint` |
103
+ | `--config-stylelint <path>` | Path to stylelint config file | `lint`, `report`|
104
+ | `--config-eslint <path>` | Path to eslint config file | `lint`, `report`|
105
+ | `--editor <editor>` | Editor to open files with (e.g., vscode, atom, sublime). Defaults to vscode | `lint` |
113
106
 
114
107
  To view help for these options, add `--help` to each command. For example, run `npx @salesforce-ux/slds-linter lint --help` to see which options you can use with `lint`.
115
108
 
@@ -124,6 +117,54 @@ To enhance your linting and error analysis experience, we recommend that you ins
124
117
  ## Best Practices
125
118
 
126
119
  - Run `npx @salesforce-ux/slds-linter lint` to see the lint output on Terminal.
127
- - To run SLDS Linter on a specific folder, add option `-d` For example, `npx @salesforce-ux/slds-linter lint -d`
120
+ - To run SLDS Linter on a specific folder, input as argument. For example, `npx @salesforce-ux/slds-linter lint <directory>`
128
121
 
129
- For any questions or issues, open an issue in this repository.
122
+ ### Example - single folder
123
+
124
+ Recursively linting all style and markup files in the `aura` directory:
125
+
126
+ ```shell
127
+ npx @salesforce-ux/slds-linter lint aura
128
+ ```
129
+
130
+ ### Example - multiple folders
131
+
132
+ Recursively linting all style and markup files in the `aura` and `lwc` directory:
133
+
134
+ ```shell
135
+ npx @salesforce-ux/slds-linter lint "**/{aura,lwc}/**"
136
+ ```
137
+
138
+ ### Example - lint style files
139
+
140
+ Recursively linting all `.css` files in the `aura` directory:
141
+
142
+ ```shell
143
+ npx @salesforce-ux/slds-linter lint "aura/**/*.css"
144
+ ```
145
+
146
+ ### Example - multiple style file extensions
147
+
148
+ Linting all `.css`, `.scss`, and `.sass` files:
149
+
150
+ ```shell
151
+ npx @salesforce-ux/slds-linter lint "**/*.{css,scss,sass}"
152
+ ```
153
+
154
+ ### Example - lint markeup files
155
+
156
+ Recursively linting all `.html` files in the `aura` directory:
157
+
158
+ ```shell
159
+ npx @salesforce-ux/slds-linter lint "aura/**/*.html"
160
+ ```
161
+
162
+ ### Example - multiple markup file extensions
163
+
164
+ Linting all `.html` and `.cmp` files:
165
+
166
+ ```shell
167
+ npx @salesforce-ux/slds-linter lint "**/*.{html,cmp}"
168
+ ```
169
+
170
+ For any questions or issues, open an issue in this repository.
@@ -11,19 +11,19 @@ import { copyFile } from "fs/promises";
11
11
  function registerEmitCommand(program) {
12
12
  program.command("emit").description("Emits the configuration files used by slds-linter cli").option(
13
13
  "-d, --directory <path>",
14
- "Target directory to emit (defaults to current directory)"
14
+ "Target directory to emit (defaults to current directory). Support glob patterns"
15
15
  ).action(async (options) => {
16
16
  try {
17
17
  Logger.info(chalk.blue("Emitting configuration files..."));
18
18
  const normalizedOptions = normalizeCliOptions(options, {
19
- configStyle: DEFAULT_STYLELINT_CONFIG_PATH,
19
+ configStylelint: DEFAULT_STYLELINT_CONFIG_PATH,
20
20
  configEslint: DEFAULT_ESLINT_CONFIG_PATH
21
21
  });
22
22
  const destStyleConfigPath = path.join(
23
23
  normalizedOptions.directory,
24
- path.basename(normalizedOptions.configStyle)
24
+ path.basename(normalizedOptions.configStylelint)
25
25
  );
26
- await copyFile(normalizedOptions.configStyle, destStyleConfigPath);
26
+ await copyFile(normalizedOptions.configStylelint, destStyleConfigPath);
27
27
  Logger.success(chalk.green(`Stylelint configuration created at:
28
28
  ${destStyleConfigPath}
29
29
  `));
@@ -1,38 +1,50 @@
1
1
  // src/commands/lint.ts
2
+ import { Option } from "commander";
2
3
  import chalk from "chalk";
3
4
  import { printLintResults } from "../utils/lintResultsUtil.js";
4
- import { normalizeCliOptions } from "../utils/cli-args.js";
5
+ import { normalizeCliOptions, nomalizeDirPath } from "../utils/cli-args.js";
5
6
  import { Logger } from "../utils/logger.js";
6
7
  import { FileScanner } from "../services/file-scanner.js";
7
8
  import { StyleFilePatterns, ComponentFilePatterns } from "../services/file-patterns.js";
8
9
  import { LintRunner } from "../services/lint-runner.js";
9
10
  import { DEFAULT_ESLINT_CONFIG_PATH, DEFAULT_STYLELINT_CONFIG_PATH } from "../services/config.resolver.js";
10
11
  function registerLintCommand(program) {
11
- 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) => {
12
+ program.command("lint").aliases(["lint:styles", "lint:components"]).configureHelp({
13
+ commandUsage: () => {
14
+ return `${program.name()} lint [directory] [options]`;
15
+ }
16
+ }).description("Run both style and component linting").argument("[directory]", "Target directory to scan (defaults to current directory). Support glob patterns").addOption(new Option("-d, --directory <path>", "Target directory to scan (defaults to current directory). Support glob patterns").hideHelp()).option("--fix", "Automatically fix problems").option("--config-stylelint <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 (directory, options) => {
12
17
  const startTime = Date.now();
13
18
  try {
14
19
  Logger.info(chalk.blue("Starting lint process..."));
15
20
  const normalizedOptions = normalizeCliOptions(options, {
16
- configStyle: DEFAULT_STYLELINT_CONFIG_PATH,
21
+ configStylelint: DEFAULT_STYLELINT_CONFIG_PATH,
17
22
  configEslint: DEFAULT_ESLINT_CONFIG_PATH
18
23
  });
19
- Logger.info(chalk.blue("\nScanning style files..."));
24
+ if (directory) {
25
+ normalizedOptions.directory = nomalizeDirPath(directory);
26
+ } else if (options.directory) {
27
+ Logger.newLine().warning(chalk.yellow(
28
+ `WARNING: --directory, -d option is deprecated. Supply as argument instead.
29
+ Example: npx @salesforce-ux/slds-linter lint ${options.directory}`
30
+ ));
31
+ }
32
+ Logger.newLine().info(chalk.blue("Scanning style files..."));
20
33
  const styleFileBatches = await FileScanner.scanFiles(normalizedOptions.directory, {
21
34
  patterns: StyleFilePatterns,
22
35
  batchSize: 100
23
36
  });
24
37
  const totalStyleFiles = styleFileBatches.reduce((sum, batch) => sum + batch.length, 0);
25
- Logger.info(chalk.blue(`Found ${totalStyleFiles} style file(s). Running stylelint...
26
- `));
27
- Logger.info(chalk.blue(`Running stylelint${normalizedOptions.fix ? " with autofix" : ""}...`));
38
+ Logger.info(chalk.blue(`Found ${totalStyleFiles} style file(s). Running stylelint...`));
39
+ Logger.newLine().info(chalk.blue(`Running stylelint${normalizedOptions.fix ? " with autofix" : ""}...`));
28
40
  const styleResults = await LintRunner.runLinting(styleFileBatches, "style", {
29
41
  fix: normalizedOptions.fix,
30
- configPath: normalizedOptions.configStyle
42
+ configPath: normalizedOptions.configStylelint
31
43
  });
32
44
  printLintResults(styleResults, normalizedOptions.editor);
33
45
  const styleErrorCount = styleResults.reduce((sum, r) => sum + r.errors.length, 0);
34
46
  const styleWarningCount = styleResults.reduce((sum, r) => sum + r.warnings.length, 0);
35
- Logger.info(chalk.blue("\nScanning component files..."));
47
+ Logger.newLine().info(chalk.blue("Scanning component files..."));
36
48
  const componentFileBatches = await FileScanner.scanFiles(normalizedOptions.directory, {
37
49
  patterns: ComponentFilePatterns,
38
50
  batchSize: 100
@@ -56,7 +68,7 @@ ${chalk.red(`${totalErrors} error${totalErrors !== 1 ? "s" : ""}`)} ${chalk.yel
56
68
  );
57
69
  const elapsedTime = ((Date.now() - startTime) / 1e3).toFixed(2);
58
70
  Logger.success(chalk.green(`
59
- Full linting completed in ${elapsedTime} seconds.`));
71
+ Linting completed in ${elapsedTime} seconds.`));
60
72
  process.exit(totalErrors > 0 ? 1 : 0);
61
73
  } catch (error) {
62
74
  Logger.error(chalk.red(`Failed to complete linting: ${error.message}`));
@@ -1,7 +1,9 @@
1
1
  // src/commands/report.ts
2
+ import { Option } from "commander";
2
3
  import path from "path";
3
4
  import ora from "ora";
4
- import { normalizeCliOptions } from "../utils/cli-args.js";
5
+ import chalk from "chalk";
6
+ import { nomalizeDirPath, normalizeCliOptions } from "../utils/cli-args.js";
5
7
  import { Logger } from "../utils/logger.js";
6
8
  import { FileScanner } from "../services/file-scanner.js";
7
9
  import { StyleFilePatterns, ComponentFilePatterns } from "../services/file-patterns.js";
@@ -9,20 +11,30 @@ import { LintRunner } from "../services/lint-runner.js";
9
11
  import { ReportGenerator } from "../services/report-generator.js";
10
12
  import { DEFAULT_ESLINT_CONFIG_PATH, DEFAULT_STYLELINT_CONFIG_PATH, LINTER_CLI_VERSION } from "../services/config.resolver.js";
11
13
  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
+ program.command("report").description("Generate SARIF report from linting results").argument("[directory]", "Target directory to scan (defaults to current directory). Support glob patterns").addOption(new Option("-d, --directory <path>", "Target directory to scan (defaults to current directory). Support glob patterns").hideHelp()).option("-o, --output <path>", "Output directory for reports (defaults to current directory)").option("--config-stylelint <path>", "Path to stylelint config file").option("--config-eslint <path>", "Path to eslint config file").action(async (directory, options) => {
15
+ const spinner = ora("Starting report generation...");
14
16
  try {
15
17
  const normalizedOptions = normalizeCliOptions(options, {
16
- configStyle: DEFAULT_STYLELINT_CONFIG_PATH,
18
+ configStylelint: DEFAULT_STYLELINT_CONFIG_PATH,
17
19
  configEslint: DEFAULT_ESLINT_CONFIG_PATH
18
20
  });
21
+ if (directory) {
22
+ normalizedOptions.directory = nomalizeDirPath(directory);
23
+ } else if (options.directory) {
24
+ Logger.newLine().warning(chalk.yellow(
25
+ `WARNING: --directory, -d option is deprecated. Supply as argument instead.
26
+ Example: npx @salesforce-ux/slds-linter report ${options.directory}
27
+ `
28
+ ));
29
+ }
30
+ spinner.start();
19
31
  spinner.text = "Running styles linting...";
20
32
  const styleFileBatches = await FileScanner.scanFiles(normalizedOptions.directory, {
21
33
  patterns: StyleFilePatterns,
22
34
  batchSize: 100
23
35
  });
24
36
  const styleResults = await LintRunner.runLinting(styleFileBatches, "style", {
25
- configPath: normalizedOptions.configStyle
37
+ configPath: normalizedOptions.configStylelint
26
38
  });
27
39
  spinner.text = "Running components linting...";
28
40
  const componentFileBatches = await FileScanner.scanFiles(normalizedOptions.directory, {
@@ -32,7 +44,7 @@ function registerReportCommand(program) {
32
44
  const componentResults = await LintRunner.runLinting(componentFileBatches, "component", {
33
45
  configPath: normalizedOptions.configEslint
34
46
  });
35
- spinner.text = "Generating combined report...";
47
+ spinner.text = "Generating SARIF report...";
36
48
  const combinedReportPath = path.join(normalizedOptions.output, "slds-linter-report.sarif");
37
49
  await ReportGenerator.generateSarifReport([...styleResults, ...componentResults], {
38
50
  outputPath: combinedReportPath,
@@ -40,10 +52,12 @@ function registerReportCommand(program) {
40
52
  toolVersion: LINTER_CLI_VERSION
41
53
  });
42
54
  spinner.succeed("Report generation completed");
55
+ Logger.success(`SARIF report generated: ${combinedReportPath}
56
+ `);
43
57
  process.exit(0);
44
58
  } catch (error) {
45
59
  spinner?.fail("Report generation failed");
46
- Logger.error(`Failed to generate report: ${error.message}`);
60
+ Logger.error(`Failed to generate SARIF report: ${error.message}`);
47
61
  process.exit(1);
48
62
  }
49
63
  });
package/build/index.js CHANGED
@@ -2,8 +2,6 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { Command } from "commander";
5
- import { registerLintStylesCommand } from "./commands/lint-styles.js";
6
- import { registerLintComponentsCommand } from "./commands/lint-components.js";
7
5
  import { registerLintCommand } from "./commands/lint.js";
8
6
  import { registerReportCommand } from "./commands/report.js";
9
7
  import { registerEmitCommand } from "./commands/emit.js";
@@ -21,12 +19,15 @@ process.on("uncaughtException", (error) => {
21
19
  var program = new Command();
22
20
  program.name("npx @salesforce-ux/slds-linter@latest").showHelpAfterError();
23
21
  function registerVersion() {
24
- program.description("SLDS Linter CLI tool for linting styles and components").version("0.1.3");
22
+ program.description("SLDS Linter CLI tool for linting styles and components").version("0.1.4-alpha.0");
25
23
  }
26
- registerLintStylesCommand(program);
27
- registerLintComponentsCommand(program);
28
24
  registerLintCommand(program);
29
25
  registerReportCommand(program);
30
26
  registerEmitCommand(program);
31
27
  registerVersion();
28
+ program.configureHelp({
29
+ subcommandTerm: (cmd) => {
30
+ return cmd.name();
31
+ }
32
+ });
32
33
  program.parse(process.argv);
@@ -37,7 +37,7 @@ describe("FileScanner", () => {
37
37
  it("should handle invalid files gracefully", async () => {
38
38
  const options = {
39
39
  patterns: {
40
- include: ["**/*.nonexistent"],
40
+ extensions: ["nonexistent"],
41
41
  exclude: []
42
42
  }
43
43
  };
@@ -5,7 +5,7 @@ var DEFAULT_ESLINT_CONFIG_PATH = resolvePath("@salesforce-ux/eslint-plugin-slds/
5
5
  var DEFAULT_STYLELINT_CONFIG_PATH = resolvePath("@salesforce-ux/stylelint-plugin-slds/.stylelintrc.yml", import.meta);
6
6
  var STYLELINT_VERSION = "16.14.1";
7
7
  var ESLINT_VERSION = "8.57.1";
8
- var LINTER_CLI_VERSION = "0.1.3";
8
+ var LINTER_CLI_VERSION = "0.1.4-alpha.0";
9
9
  var getRuleDescription = (ruleId) => {
10
10
  const ruleIdWithoutNameSpace = `${ruleId}`.replace(/\@salesforce-ux\//, "");
11
11
  return ruleMetadata(ruleIdWithoutNameSpace)?.ruleDesc || "--";
@@ -1,11 +1,6 @@
1
1
  // src/services/file-patterns.ts
2
2
  var StyleFilePatterns = {
3
- include: [
4
- "**/*.css",
5
- "**/*.scss",
6
- "**/*.less",
7
- "**/*.sass"
8
- ],
3
+ extensions: ["css", "scss", "less", "sass"],
9
4
  exclude: [
10
5
  "**/node_modules/**",
11
6
  "**/dist/**",
@@ -13,14 +8,7 @@ var StyleFilePatterns = {
13
8
  ]
14
9
  };
15
10
  var ComponentFilePatterns = {
16
- include: [
17
- "**/*.html",
18
- "**/*.cmp",
19
- "**/*.component",
20
- "**/*.app",
21
- "**/*.page",
22
- "**/*.interface"
23
- ],
11
+ extensions: ["html", "cmp", "component", "app", "page", "interface"],
24
12
  exclude: [
25
13
  "**/node_modules/**",
26
14
  "**/dist/**",
@@ -1,5 +1,5 @@
1
1
  export interface FilePattern {
2
- include: string[];
2
+ extensions: string[];
3
3
  exclude?: string[];
4
4
  }
5
5
  export interface ScanOptions {
@@ -1,7 +1,8 @@
1
1
  // src/services/file-scanner.ts
2
2
  import { promises as fs } from "fs";
3
- import { glob } from "glob";
4
3
  import { Logger } from "../utils/logger.js";
4
+ import { globby } from "globby";
5
+ import { extname } from "path";
5
6
  var FileScanner = class {
6
7
  static DEFAULT_BATCH_SIZE = 100;
7
8
  /**
@@ -13,24 +14,21 @@ var FileScanner = class {
13
14
  static async scanFiles(directory, options) {
14
15
  try {
15
16
  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(`${directory}/${pattern}`, {
20
- cwd: process.cwd(),
21
- ignore: options.patterns.exclude,
22
- withFileTypes: true,
23
- dot: true
24
- // Include .dot files
25
- }).then(
26
- (matches) => matches.filter((match) => match.isFile()).map((match) => {
27
- return match.fullpath();
28
- })
29
- );
30
- allFiles.push(...files);
31
- }
32
- const uniqueFiles = [...new Set(allFiles)];
33
- const validFiles = await this.validateFiles(uniqueFiles);
17
+ const allFiles = await globby(directory, {
18
+ cwd: process.cwd(),
19
+ expandDirectories: true,
20
+ unique: true,
21
+ ignore: options.patterns.exclude,
22
+ onlyFiles: true,
23
+ dot: true,
24
+ // Include.dot files
25
+ absolute: true,
26
+ gitignore: true
27
+ }).then((matches) => matches.filter((match) => {
28
+ const fileExt = extname(match).substring(1);
29
+ return options.patterns.extensions.includes(fileExt);
30
+ }));
31
+ const validFiles = await this.validateFiles(allFiles);
34
32
  const batchSize = options.batchSize || this.DEFAULT_BATCH_SIZE;
35
33
  const batches = this.createBatches(validFiles, batchSize);
36
34
  Logger.debug(
@@ -1,7 +1,6 @@
1
1
  // src/services/report-generator.ts
2
2
  import path from "path";
3
3
  import fs from "fs/promises";
4
- import { Logger } from "../utils/logger.js";
5
4
  import { SarifBuilder, SarifRunBuilder, SarifResultBuilder, SarifRuleBuilder } from "node-sarif-builder";
6
5
  import { createWriteStream } from "fs";
7
6
  import { JsonStreamStringify } from "json-stream-stringify";
@@ -12,38 +11,32 @@ var ReportGenerator = class {
12
11
  * Generate SARIF report from lint results
13
12
  */
14
13
  static async generateSarifReport(results, options) {
15
- try {
16
- const builder = new SarifBuilder();
17
- const runBuilder = new SarifRunBuilder().initSimple({
18
- toolDriverName: options.toolName,
19
- toolDriverVersion: options.toolVersion,
20
- url: "https://github.com/salesforce-ux/slds-linter"
14
+ const builder = new SarifBuilder();
15
+ const runBuilder = new SarifRunBuilder().initSimple({
16
+ toolDriverName: options.toolName,
17
+ toolDriverVersion: options.toolVersion,
18
+ url: "https://github.com/salesforce-ux/slds-linter"
19
+ });
20
+ const rules = this.extractRules(results);
21
+ for (const rule of rules) {
22
+ const ruleBuilder = new SarifRuleBuilder().initSimple({
23
+ ruleId: replaceNamespaceinRules(rule.id),
24
+ shortDescriptionText: rule.shortDescription?.text
21
25
  });
22
- const rules = this.extractRules(results);
23
- for (const rule of rules) {
24
- const ruleBuilder = new SarifRuleBuilder().initSimple({
25
- ruleId: replaceNamespaceinRules(rule.id),
26
- shortDescriptionText: rule.shortDescription?.text
27
- });
28
- runBuilder.addRule(ruleBuilder);
29
- }
30
- for (const result of results) {
31
- this.addResultsToSarif(runBuilder, result);
32
- }
33
- builder.addRun(runBuilder);
34
- const sarifReport = builder.buildSarifOutput();
35
- const outputDir = path.dirname(options.outputPath);
36
- await fs.mkdir(outputDir, { recursive: true });
37
- const writeStream = createWriteStream(options.outputPath);
38
- const jsonStream = new JsonStreamStringify(sarifReport, null, 2);
39
- await new Promise((resolve, reject) => {
40
- jsonStream.pipe(writeStream).on("finish", resolve).on("error", reject);
41
- });
42
- Logger.success(`SARIF report generated: ${options.outputPath}`);
43
- } catch (error) {
44
- Logger.error(`Failed to generate SARIF report: ${error.message}`);
45
- throw error;
26
+ runBuilder.addRule(ruleBuilder);
27
+ }
28
+ for (const result of results) {
29
+ this.addResultsToSarif(runBuilder, result);
46
30
  }
31
+ builder.addRun(runBuilder);
32
+ const sarifReport = builder.buildSarifOutput();
33
+ const outputDir = path.dirname(options.outputPath);
34
+ await fs.mkdir(outputDir, { recursive: true });
35
+ const writeStream = createWriteStream(options.outputPath);
36
+ const jsonStream = new JsonStreamStringify(sarifReport, null, 2);
37
+ await new Promise((resolve, reject) => {
38
+ jsonStream.pipe(writeStream).on("finish", resolve).on("error", reject);
39
+ });
47
40
  }
48
41
  /**
49
42
  * Extract unique rules from results
@@ -2,8 +2,7 @@ export interface CliOptions {
2
2
  directory?: string;
3
3
  output?: string;
4
4
  fix?: boolean;
5
- config?: string;
6
- configStyle?: string;
5
+ configStylelint?: string;
7
6
  configEslint?: string;
8
7
  editor?: string;
9
8
  }
@@ -1,4 +1,4 @@
1
1
  import { CliOptions } from "../types";
2
- export declare function validateAndNormalizeDirPath(inputPath?: string): string;
3
- export declare function validateAndNormalizeOutputPath(inputPath?: string): string;
2
+ export declare function nomalizeAndValidatePath(inputPath?: string): string;
3
+ export declare function nomalizeDirPath(inputPath?: string): string;
4
4
  export declare function normalizeCliOptions(options: CliOptions, defultOptions?: Partial<CliOptions>): Required<CliOptions>;
@@ -1,13 +1,8 @@
1
1
  // src/utils/cli-args.ts
2
2
  import path from "path";
3
3
  import { accessSync } from "fs";
4
- function validateAndNormalizeDirPath(inputPath) {
5
- if (!inputPath) {
6
- return process.cwd();
7
- }
8
- return inputPath;
9
- }
10
- function validateAndNormalizeOutputPath(inputPath) {
4
+ import { isDynamicPattern } from "globby";
5
+ function nomalizeAndValidatePath(inputPath) {
11
6
  if (!inputPath) {
12
7
  return process.cwd();
13
8
  }
@@ -19,21 +14,29 @@ function validateAndNormalizeOutputPath(inputPath) {
19
14
  throw new Error(`Invalid path: ${inputPath}`);
20
15
  }
21
16
  }
17
+ function nomalizeDirPath(inputPath) {
18
+ if (!inputPath) {
19
+ return process.cwd();
20
+ }
21
+ if (isDynamicPattern(inputPath)) {
22
+ return inputPath;
23
+ }
24
+ return nomalizeAndValidatePath(inputPath);
25
+ }
22
26
  function normalizeCliOptions(options, defultOptions = {}) {
23
27
  return {
24
28
  fix: false,
25
29
  editor: "vscode",
26
- config: "",
27
- configStyle: "",
30
+ configStylelint: "",
28
31
  configEslint: "",
29
32
  ...defultOptions,
30
33
  ...options,
31
- directory: validateAndNormalizeDirPath(options.directory),
32
- output: validateAndNormalizeOutputPath(options.output)
34
+ directory: nomalizeDirPath(options.directory),
35
+ output: nomalizeAndValidatePath(options.output)
33
36
  };
34
37
  }
35
38
  export {
36
- normalizeCliOptions,
37
- validateAndNormalizeDirPath,
38
- validateAndNormalizeOutputPath
39
+ nomalizeAndValidatePath,
40
+ nomalizeDirPath,
41
+ normalizeCliOptions
39
42
  };
@@ -13,8 +13,7 @@ function printLintResults(results, editor) {
13
13
  if (!hasErrors && !hasWarnings) return;
14
14
  const absolutePath = result.filePath || "";
15
15
  const relativeFile = path.relative(process.cwd(), absolutePath) || "Unknown file";
16
- Logger.info(`
17
- ${chalk.bold(relativeFile)}`);
16
+ Logger.newLine().info(`${chalk.bold(relativeFile)}`);
18
17
  if (hasErrors) {
19
18
  result.errors.forEach((error) => {
20
19
  if (error.line && error.column && absolutePath) {
@@ -1,4 +1,5 @@
1
1
  export declare class Logger {
2
+ static newLine(): typeof Logger;
2
3
  static info(message: string): void;
3
4
  static success(message: string): void;
4
5
  static warning(message: string): void;
@@ -1,6 +1,10 @@
1
1
  // src/utils/logger.ts
2
2
  import chalk from "chalk";
3
3
  var Logger = class {
4
+ static newLine() {
5
+ console.log("\n");
6
+ return this;
7
+ }
4
8
  static info(message) {
5
9
  console.log(chalk.blue("\u2139"), message);
6
10
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce-ux/slds-linter",
3
- "version": "0.1.3",
3
+ "version": "0.1.4-alpha.0",
4
4
  "description": "SLDS Linter CLI tool for linting styles and components",
5
5
  "keywords": [
6
6
  "lightning design system linter",
@@ -20,14 +20,14 @@
20
20
  "slds-linter": "./build/index.js"
21
21
  },
22
22
  "dependencies": {
23
- "@salesforce-ux/eslint-plugin-slds": "0.1.3",
24
- "@salesforce-ux/stylelint-plugin-slds": "0.1.3",
23
+ "@salesforce-ux/eslint-plugin-slds": "0.1.4-alpha.0",
24
+ "@salesforce-ux/stylelint-plugin-slds": "0.1.4-alpha.0",
25
25
  "@typescript-eslint/eslint-plugin": "^5.0.0",
26
26
  "@typescript-eslint/parser": "^5.0.0",
27
27
  "chalk": "^4.1.2",
28
28
  "commander": "^13.1.0",
29
29
  "eslint": "^8.0.0",
30
- "glob": "^11.0.0",
30
+ "globby": "^14.1.0",
31
31
  "import-meta-resolve": "^4.1.0",
32
32
  "json-stream-stringify": "^3.1.6",
33
33
  "node-sarif-builder": "^3.2.0",
@@ -1,2 +0,0 @@
1
- import { Command } from 'commander';
2
- export declare function registerLintComponentsCommand(program: Command): void;
@@ -1,52 +0,0 @@
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${normalizedOptions.fix ? " with autofix" : ""}...`));
26
- const results = await LintRunner.runLinting(fileBatches, "component", {
27
- fix: normalizedOptions.fix,
28
- configPath: normalizedOptions.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
- };
@@ -1,2 +0,0 @@
1
- import { Command } from 'commander';
2
- export declare function registerLintStylesCommand(program: Command): void;
@@ -1,52 +0,0 @@
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
- config: 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${normalizedOptions.fix ? " with autofix" : ""}...`));
26
- const results = await LintRunner.runLinting(fileBatches, "style", {
27
- fix: normalizedOptions.fix,
28
- configPath: normalizedOptions.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
- };