@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 +67 -26
- package/build/commands/emit.js +4 -4
- package/build/commands/lint.js +22 -10
- package/build/commands/report.js +21 -7
- package/build/index.js +6 -5
- package/build/services/__tests__/file-scanner.test.js +1 -1
- package/build/services/config.resolver.js +1 -1
- package/build/services/file-patterns.js +2 -14
- package/build/services/file-scanner.d.ts +1 -1
- package/build/services/file-scanner.js +17 -19
- package/build/services/report-generator.js +24 -31
- package/build/types/index.d.ts +1 -2
- package/build/utils/cli-args.d.ts +2 -2
- package/build/utils/cli-args.js +17 -14
- package/build/utils/lintResultsUtil.js +1 -2
- package/build/utils/logger.d.ts +1 -0
- package/build/utils/logger.js +4 -0
- package/package.json +4 -4
- package/build/commands/lint-components.d.ts +0 -2
- package/build/commands/lint-components.js +0 -52
- package/build/commands/lint-styles.d.ts +0 -2
- package/build/commands/lint-styles.js +0 -52
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
|
|
52
|
-
-h, --help
|
|
51
|
+
-V, --version output the version number
|
|
52
|
+
-h, --help display help for command
|
|
53
53
|
|
|
54
54
|
Commands:
|
|
55
|
-
lint
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
|
74
|
-
4.
|
|
75
|
-
5.
|
|
76
|
-
6.
|
|
77
|
-
7.
|
|
78
|
-
8.
|
|
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).
|
|
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
|
|
109
|
-
| `--config <path>`
|
|
110
|
-
| `--config-
|
|
111
|
-
| `--
|
|
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,
|
|
120
|
+
- To run SLDS Linter on a specific folder, input as argument. For example, `npx @salesforce-ux/slds-linter lint <directory>`
|
|
128
121
|
|
|
129
|
-
|
|
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.
|
package/build/commands/emit.js
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
24
|
+
path.basename(normalizedOptions.configStylelint)
|
|
25
25
|
);
|
|
26
|
-
await copyFile(normalizedOptions.
|
|
26
|
+
await copyFile(normalizedOptions.configStylelint, destStyleConfigPath);
|
|
27
27
|
Logger.success(chalk.green(`Stylelint configuration created at:
|
|
28
28
|
${destStyleConfigPath}
|
|
29
29
|
`));
|
package/build/commands/lint.js
CHANGED
|
@@ -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").
|
|
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
|
-
|
|
21
|
+
configStylelint: DEFAULT_STYLELINT_CONFIG_PATH,
|
|
17
22
|
configEslint: DEFAULT_ESLINT_CONFIG_PATH
|
|
18
23
|
});
|
|
19
|
-
|
|
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.
|
|
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("
|
|
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
|
-
|
|
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}`));
|
package/build/commands/report.js
CHANGED
|
@@ -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
|
|
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").
|
|
13
|
-
const spinner = ora("Starting report generation...")
|
|
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
|
-
|
|
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.
|
|
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
|
|
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.
|
|
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);
|
|
@@ -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.
|
|
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
|
-
|
|
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
|
-
|
|
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,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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
package/build/types/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { CliOptions } from "../types";
|
|
2
|
-
export declare function
|
|
3
|
-
export declare function
|
|
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>;
|
package/build/utils/cli-args.js
CHANGED
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
// src/utils/cli-args.ts
|
|
2
2
|
import path from "path";
|
|
3
3
|
import { accessSync } from "fs";
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
|
|
27
|
-
configStyle: "",
|
|
30
|
+
configStylelint: "",
|
|
28
31
|
configEslint: "",
|
|
29
32
|
...defultOptions,
|
|
30
33
|
...options,
|
|
31
|
-
directory:
|
|
32
|
-
output:
|
|
34
|
+
directory: nomalizeDirPath(options.directory),
|
|
35
|
+
output: nomalizeAndValidatePath(options.output)
|
|
33
36
|
};
|
|
34
37
|
}
|
|
35
38
|
export {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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) {
|
package/build/utils/logger.d.ts
CHANGED
package/build/utils/logger.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce-ux/slds-linter",
|
|
3
|
-
"version": "0.1.
|
|
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.
|
|
24
|
-
"@salesforce-ux/stylelint-plugin-slds": "0.1.
|
|
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
|
-
"
|
|
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,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,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
|
-
};
|