@salesforce-ux/slds-linter 1.0.6-internal → 1.0.7-internal
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/build/commands/emit.js +5 -5
- package/build/commands/lint.js +14 -7
- package/build/commands/report.js +2 -2
- package/build/executor/index.js +31 -9
- package/build/index.js +1 -1
- package/build/services/config.resolver.js +2 -2
- package/build/services/file-patterns.d.ts +1 -1
- package/build/services/file-scanner.d.ts +10 -2
- package/build/services/file-scanner.js +5 -7
- package/build/services/lint-runner.d.ts +1 -1
- package/build/services/lint-runner.js +14 -4
- package/build/services/report-generator.d.ts +2 -2
- package/build/services/report-generator.js +51 -23
- package/build/types/index.d.ts +28 -24
- package/build/utils/editorLinkUtil.js +2 -2
- package/build/utils/lintResultsUtil.d.ts +2 -2
- package/build/utils/lintResultsUtil.js +27 -46
- package/build/utils/logger.js +7 -7
- package/build/workers/eslint.worker.js +16 -3
- package/package.json +2 -2
- package/build/utils/colors.d.ts +0 -16
- package/build/utils/colors.js +0 -24
package/build/commands/emit.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/commands/emit.ts
|
|
2
|
+
import chalk from "chalk";
|
|
2
3
|
import { Logger } from "../utils/logger.js";
|
|
3
|
-
import { Colors } from "../utils/colors.js";
|
|
4
4
|
import { normalizeCliOptions } from "../utils/config-utils.js";
|
|
5
5
|
import {
|
|
6
6
|
DEFAULT_ESLINT_CONFIG_PATH
|
|
@@ -47,20 +47,20 @@ function registerEmitCommand(program) {
|
|
|
47
47
|
"Target directory to emit (defaults to current directory). Support glob patterns"
|
|
48
48
|
).action(async (options) => {
|
|
49
49
|
try {
|
|
50
|
-
Logger.info("Emitting configuration files...");
|
|
50
|
+
Logger.info(chalk.blue("Emitting configuration files..."));
|
|
51
51
|
const normalizedOptions = normalizeCliOptions(options, {
|
|
52
52
|
configEslint: DEFAULT_ESLINT_CONFIG_PATH
|
|
53
53
|
});
|
|
54
54
|
const enhancedConfig = await generateEnhancedESLintConfig(normalizedOptions.configEslint);
|
|
55
55
|
const destESLintConfigPath = path.join(normalizedOptions.directory, path.basename(normalizedOptions.configEslint));
|
|
56
56
|
await writeFile(destESLintConfigPath, enhancedConfig, "utf8");
|
|
57
|
-
Logger.success(
|
|
57
|
+
Logger.success(chalk.green(`ESLint configuration created at:
|
|
58
58
|
${destESLintConfigPath}
|
|
59
59
|
`));
|
|
60
|
-
Logger.info("Rules are dynamically loaded based on extends configuration.");
|
|
60
|
+
Logger.info(chalk.cyan("Rules are dynamically loaded based on extends configuration."));
|
|
61
61
|
} catch (error) {
|
|
62
62
|
Logger.error(
|
|
63
|
-
|
|
63
|
+
chalk.red(`Failed to emit configuration: ${error.message}`)
|
|
64
64
|
);
|
|
65
65
|
process.exit(1);
|
|
66
66
|
}
|
package/build/commands/lint.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// src/commands/lint.ts
|
|
2
2
|
import { Option } from "commander";
|
|
3
|
+
import chalk from "chalk";
|
|
3
4
|
import { printLintResults } from "../utils/lintResultsUtil.js";
|
|
4
5
|
import { normalizeCliOptions, normalizeDirectoryPath } from "../utils/config-utils.js";
|
|
5
6
|
import { Logger } from "../utils/logger.js";
|
|
6
|
-
import { Colors } from "../utils/colors.js";
|
|
7
7
|
import { DEFAULT_ESLINT_CONFIG_PATH } from "../services/config.resolver.js";
|
|
8
8
|
import { lint } from "../executor/index.js";
|
|
9
9
|
function registerLintCommand(program) {
|
|
@@ -14,25 +14,32 @@ function registerLintCommand(program) {
|
|
|
14
14
|
}).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-eslint <path>", "Path to eslint config file").option("--editor <editor>", "Editor to open files with (e.g., vscode, atom, sublime). Auto-detects if not specified").action(async (directory, options) => {
|
|
15
15
|
const startTime = Date.now();
|
|
16
16
|
try {
|
|
17
|
-
Logger.info("Starting lint process...");
|
|
17
|
+
Logger.info(chalk.blue("Starting lint process..."));
|
|
18
18
|
const normalizedOptions = normalizeCliOptions(options, {
|
|
19
19
|
configEslint: DEFAULT_ESLINT_CONFIG_PATH
|
|
20
20
|
});
|
|
21
21
|
if (directory) {
|
|
22
22
|
normalizedOptions.directory = normalizeDirectoryPath(directory);
|
|
23
23
|
} else if (options.directory) {
|
|
24
|
-
Logger.newLine().warning(
|
|
24
|
+
Logger.newLine().warning(chalk.yellow(
|
|
25
25
|
`WARNING: --directory, -d option is deprecated. Supply as argument instead.
|
|
26
26
|
Example: npx @salesforce-ux/slds-linter lint ${options.directory}`
|
|
27
27
|
));
|
|
28
28
|
}
|
|
29
29
|
const lintResults = await lint(normalizedOptions);
|
|
30
|
-
|
|
30
|
+
printLintResults(lintResults, normalizedOptions.editor);
|
|
31
|
+
const errorCount = lintResults.reduce((sum, r) => sum + r.errors.length, 0);
|
|
32
|
+
const warningCount = lintResults.reduce((sum, r) => sum + r.warnings.length, 0);
|
|
33
|
+
Logger.info(
|
|
34
|
+
`
|
|
35
|
+
${chalk.red(`${errorCount} error${errorCount !== 1 ? "s" : ""}`)} ${chalk.yellow(`${warningCount} warning${warningCount !== 1 ? "s" : ""}`)}`
|
|
36
|
+
);
|
|
31
37
|
const elapsedTime = ((Date.now() - startTime) / 1e3).toFixed(2);
|
|
32
|
-
Logger.
|
|
33
|
-
|
|
38
|
+
Logger.success(chalk.green(`
|
|
39
|
+
Linting completed in ${elapsedTime} seconds.`));
|
|
40
|
+
process.exit(errorCount > 0 ? 1 : 0);
|
|
34
41
|
} catch (error) {
|
|
35
|
-
Logger.error(
|
|
42
|
+
Logger.error(chalk.red(`Failed to complete linting: ${error.message}`));
|
|
36
43
|
process.exit(1);
|
|
37
44
|
}
|
|
38
45
|
});
|
package/build/commands/report.js
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import { Option } from "commander";
|
|
3
3
|
import path from "path";
|
|
4
4
|
import ora from "ora";
|
|
5
|
+
import chalk from "chalk";
|
|
5
6
|
import fs from "fs";
|
|
6
7
|
import { normalizeAndValidatePath, normalizeCliOptions, normalizeDirectoryPath } from "../utils/config-utils.js";
|
|
7
8
|
import { Logger } from "../utils/logger.js";
|
|
8
|
-
import { Colors } from "../utils/colors.js";
|
|
9
9
|
import { DEFAULT_ESLINT_CONFIG_PATH } from "../services/config.resolver.js";
|
|
10
10
|
import { report, lint } from "../executor/index.js";
|
|
11
11
|
function registerReportCommand(program) {
|
|
@@ -19,7 +19,7 @@ function registerReportCommand(program) {
|
|
|
19
19
|
if (directory) {
|
|
20
20
|
normalizedOptions.directory = normalizeDirectoryPath(directory);
|
|
21
21
|
} else if (options.directory) {
|
|
22
|
-
Logger.newLine().warning(
|
|
22
|
+
Logger.newLine().warning(chalk.yellow(
|
|
23
23
|
`WARNING: --directory, -d option is deprecated. Supply as argument instead.
|
|
24
24
|
Example: npx @salesforce-ux/slds-linter report ${options.directory}`
|
|
25
25
|
));
|
package/build/executor/index.js
CHANGED
|
@@ -13,25 +13,20 @@ async function lint(config) {
|
|
|
13
13
|
const normalizedConfig = normalizeCliOptions(config, {
|
|
14
14
|
configEslint: DEFAULT_ESLINT_CONFIG_PATH
|
|
15
15
|
});
|
|
16
|
-
const
|
|
16
|
+
const styleFiles = await FileScanner.scanFiles(normalizedConfig.directory, {
|
|
17
17
|
patterns: StyleFilePatterns,
|
|
18
18
|
batchSize: 100
|
|
19
19
|
});
|
|
20
|
-
const
|
|
20
|
+
const componentFiles = await FileScanner.scanFiles(normalizedConfig.directory, {
|
|
21
21
|
patterns: ComponentFilePatterns,
|
|
22
22
|
batchSize: 100
|
|
23
23
|
});
|
|
24
|
-
if (styleFilesCount > 0) {
|
|
25
|
-
Logger.info(`Total style files: ${styleFilesCount}`);
|
|
26
|
-
}
|
|
27
|
-
if (componentFilesCount > 0) {
|
|
28
|
-
Logger.info(`Total component files: ${componentFilesCount}`);
|
|
29
|
-
}
|
|
30
24
|
const { fix, configEslint } = normalizedConfig;
|
|
31
|
-
|
|
25
|
+
const lintResults = await LintRunner.runLinting([...styleFiles, ...componentFiles], "component", {
|
|
32
26
|
fix,
|
|
33
27
|
configPath: configEslint
|
|
34
28
|
});
|
|
29
|
+
return standardizeLintMessages(lintResults);
|
|
35
30
|
} catch (error) {
|
|
36
31
|
const errorMessage = `Linting failed: ${error.message}`;
|
|
37
32
|
Logger.error(errorMessage);
|
|
@@ -69,6 +64,33 @@ async function report(config, results) {
|
|
|
69
64
|
throw new Error(errorMessage);
|
|
70
65
|
}
|
|
71
66
|
}
|
|
67
|
+
function standardizeLintMessages(results) {
|
|
68
|
+
return results.map((result) => ({
|
|
69
|
+
...result,
|
|
70
|
+
errors: result.errors.map((entry) => {
|
|
71
|
+
let msgObj;
|
|
72
|
+
try {
|
|
73
|
+
msgObj = JSON.parse(entry.message);
|
|
74
|
+
if (typeof msgObj === "object" && "message" in msgObj) {
|
|
75
|
+
return { ...entry, ...msgObj };
|
|
76
|
+
}
|
|
77
|
+
} catch {
|
|
78
|
+
}
|
|
79
|
+
return { ...entry, message: entry.message };
|
|
80
|
+
}),
|
|
81
|
+
warnings: result.warnings.map((entry) => {
|
|
82
|
+
let msgObj;
|
|
83
|
+
try {
|
|
84
|
+
msgObj = JSON.parse(entry.message);
|
|
85
|
+
if (typeof msgObj === "object" && "message" in msgObj) {
|
|
86
|
+
return { ...entry, ...msgObj };
|
|
87
|
+
}
|
|
88
|
+
} catch {
|
|
89
|
+
}
|
|
90
|
+
return { ...entry, message: entry.message };
|
|
91
|
+
})
|
|
92
|
+
}));
|
|
93
|
+
}
|
|
72
94
|
export {
|
|
73
95
|
lint,
|
|
74
96
|
report
|
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.0.
|
|
22
|
+
program.description("SLDS Linter CLI tool for linting styles and components").version("1.0.7-internal");
|
|
23
23
|
}
|
|
24
24
|
registerLintCommand(program);
|
|
25
25
|
registerReportCommand(program);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/services/config.resolver.ts
|
|
2
2
|
import { resolvePath } from "../utils/nodeVersionUtil.js";
|
|
3
3
|
|
|
4
|
-
// yaml-file:/Users/
|
|
4
|
+
// yaml-file:/Users/ritesh.kumar2/Documents/projects/stylelint-sds/packages/eslint-plugin-slds/src/config/rule-messages.yml
|
|
5
5
|
var rule_messages_default = {
|
|
6
6
|
"no-slds-class-overrides": {
|
|
7
7
|
"description": "Create new custom CSS classes instead of overriding SLDS selectors",
|
|
@@ -157,7 +157,7 @@ var rule_messages_default = {
|
|
|
157
157
|
// src/services/config.resolver.ts
|
|
158
158
|
var DEFAULT_ESLINT_CONFIG_PATH = resolvePath("@salesforce-ux/eslint-plugin-slds/config", import.meta);
|
|
159
159
|
var ESLINT_VERSION = "9.36.0";
|
|
160
|
-
var LINTER_CLI_VERSION = "1.0.
|
|
160
|
+
var LINTER_CLI_VERSION = "1.0.7-internal";
|
|
161
161
|
var getRuleDescription = (ruleId) => {
|
|
162
162
|
const ruleIdWithoutNameSpace = `${ruleId}`.replace(/\@salesforce-ux\//, "").replace(/^slds\//, "");
|
|
163
163
|
return rule_messages_default[ruleIdWithoutNameSpace]?.description || "--";
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
export interface FilePattern {
|
|
2
|
+
extensions: string[];
|
|
3
|
+
exclude?: string[];
|
|
4
|
+
}
|
|
5
|
+
export interface ScanOptions {
|
|
6
|
+
patterns: FilePattern;
|
|
7
|
+
batchSize?: number;
|
|
8
|
+
gitignore?: boolean;
|
|
9
|
+
}
|
|
2
10
|
export declare class FileScanner {
|
|
3
11
|
private static DEFAULT_BATCH_SIZE;
|
|
4
12
|
/**
|
|
@@ -7,7 +15,7 @@ export declare class FileScanner {
|
|
|
7
15
|
* @param options Scanning options including patterns and batch size
|
|
8
16
|
* @returns Array of file paths in batches
|
|
9
17
|
*/
|
|
10
|
-
static scanFiles(directory: string, options: ScanOptions): Promise<
|
|
18
|
+
static scanFiles(directory: string, options: ScanOptions): Promise<string[][]>;
|
|
11
19
|
/**
|
|
12
20
|
* Validates that files exist and are readable
|
|
13
21
|
*/
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
// src/services/file-scanner.ts
|
|
2
|
-
import path from "path";
|
|
3
2
|
import { promises as fs } from "fs";
|
|
4
|
-
import { globby, isDynamicPattern } from "globby";
|
|
5
3
|
import { Logger } from "../utils/logger.js";
|
|
4
|
+
import { globby, isDynamicPattern } from "globby";
|
|
5
|
+
import { extname } from "path";
|
|
6
|
+
import path from "path";
|
|
6
7
|
var FileScanner = class {
|
|
7
8
|
static DEFAULT_BATCH_SIZE = 100;
|
|
8
9
|
/**
|
|
@@ -52,7 +53,7 @@ var FileScanner = class {
|
|
|
52
53
|
absolute: true,
|
|
53
54
|
gitignore: options.gitignore !== false
|
|
54
55
|
}).then((matches) => matches.filter((match) => {
|
|
55
|
-
const fileExt =
|
|
56
|
+
const fileExt = extname(match).substring(1);
|
|
56
57
|
return options.patterns.extensions.includes(fileExt);
|
|
57
58
|
}));
|
|
58
59
|
const validFiles = await this.validateFiles(allFiles);
|
|
@@ -61,10 +62,7 @@ var FileScanner = class {
|
|
|
61
62
|
Logger.debug(
|
|
62
63
|
`Found ${validFiles.length} files, split into ${batches.length} batches`
|
|
63
64
|
);
|
|
64
|
-
return
|
|
65
|
-
filesCount: validFiles.length,
|
|
66
|
-
batches
|
|
67
|
-
};
|
|
65
|
+
return batches;
|
|
68
66
|
} catch (error) {
|
|
69
67
|
Logger.error(`Failed to scan files: ${error.message}`);
|
|
70
68
|
throw error;
|
|
@@ -3,7 +3,7 @@ export declare class LintRunner {
|
|
|
3
3
|
/**
|
|
4
4
|
* Run linting on batches of files
|
|
5
5
|
*/
|
|
6
|
-
static runLinting(fileBatches: string[][], options?: LintRunnerOptions): Promise<LintResult[]>;
|
|
6
|
+
static runLinting(fileBatches: string[][], workerType: 'component', options?: LintRunnerOptions): Promise<LintResult[]>;
|
|
7
7
|
/**
|
|
8
8
|
* Process and normalize worker results
|
|
9
9
|
*/
|
|
@@ -8,7 +8,7 @@ var LintRunner = class {
|
|
|
8
8
|
/**
|
|
9
9
|
* Run linting on batches of files
|
|
10
10
|
*/
|
|
11
|
-
static async runLinting(fileBatches, options = {}) {
|
|
11
|
+
static async runLinting(fileBatches, workerType, options = {}) {
|
|
12
12
|
try {
|
|
13
13
|
const workerScript = path.resolve(
|
|
14
14
|
resolveDirName(import.meta),
|
|
@@ -46,11 +46,21 @@ var LintRunner = class {
|
|
|
46
46
|
continue;
|
|
47
47
|
}
|
|
48
48
|
for (const result of batch.results) {
|
|
49
|
-
if (result.error
|
|
50
|
-
Logger.warning(`File processing failed: ${result.
|
|
49
|
+
if (result.error) {
|
|
50
|
+
Logger.warning(`File processing failed: ${result.file} - ${result.error}`);
|
|
51
51
|
continue;
|
|
52
52
|
}
|
|
53
|
-
results.push(
|
|
53
|
+
results.push({
|
|
54
|
+
filePath: result.file,
|
|
55
|
+
errors: result.errors?.map((e) => ({
|
|
56
|
+
...e,
|
|
57
|
+
severity: 2
|
|
58
|
+
})) || [],
|
|
59
|
+
warnings: result.warnings?.map((w) => ({
|
|
60
|
+
...w,
|
|
61
|
+
severity: 1
|
|
62
|
+
})) || []
|
|
63
|
+
});
|
|
54
64
|
}
|
|
55
65
|
}
|
|
56
66
|
return results;
|
|
@@ -31,11 +31,11 @@ export declare class CsvReportGenerator {
|
|
|
31
31
|
/**
|
|
32
32
|
* Generate CSV report and write to file
|
|
33
33
|
*/
|
|
34
|
-
static generate(results:
|
|
34
|
+
static generate(results: any[]): Promise<string>;
|
|
35
35
|
/**
|
|
36
36
|
* Generate CSV string from lint results
|
|
37
37
|
*/
|
|
38
|
-
static generateCsvString(results:
|
|
38
|
+
static generateCsvString(results: any[]): string;
|
|
39
39
|
/**
|
|
40
40
|
* Convert lint results to CSV-compatible data format
|
|
41
41
|
*/
|
|
@@ -75,29 +75,46 @@ var ReportGenerator = class {
|
|
|
75
75
|
*/
|
|
76
76
|
static extractRules(results) {
|
|
77
77
|
const rules = /* @__PURE__ */ new Map();
|
|
78
|
-
|
|
79
|
-
result.
|
|
80
|
-
if (!rules.has(
|
|
81
|
-
rules.set(
|
|
82
|
-
id: replaceNamespaceinRules(
|
|
78
|
+
for (const result of results) {
|
|
79
|
+
for (const error of result.errors) {
|
|
80
|
+
if (!rules.has(error.ruleId)) {
|
|
81
|
+
rules.set(error.ruleId, {
|
|
82
|
+
id: replaceNamespaceinRules(error.ruleId),
|
|
83
83
|
shortDescription: {
|
|
84
|
-
text: getRuleDescription(replaceNamespaceinRules(
|
|
84
|
+
text: getRuleDescription(replaceNamespaceinRules(error.ruleId))
|
|
85
85
|
},
|
|
86
86
|
properties: {
|
|
87
87
|
category: "Style"
|
|
88
88
|
}
|
|
89
89
|
});
|
|
90
90
|
}
|
|
91
|
-
}
|
|
92
|
-
|
|
91
|
+
}
|
|
92
|
+
for (const warning of result.warnings) {
|
|
93
|
+
if (!rules.has(warning.ruleId)) {
|
|
94
|
+
rules.set(warning.ruleId, {
|
|
95
|
+
id: replaceNamespaceinRules(warning.ruleId),
|
|
96
|
+
shortDescription: {
|
|
97
|
+
text: getRuleDescription(replaceNamespaceinRules(warning.ruleId))
|
|
98
|
+
},
|
|
99
|
+
properties: {
|
|
100
|
+
category: "Style"
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
93
106
|
return Array.from(rules.values());
|
|
94
107
|
}
|
|
95
108
|
/**
|
|
96
109
|
* Add lint results to SARIF report
|
|
97
110
|
*/
|
|
98
111
|
static addResultsToSarif(runBuilder, lintResult) {
|
|
99
|
-
lintResult.
|
|
100
|
-
const resultBuilder = new SarifResultBuilder().initSimple(transformedResults(lintResult,
|
|
112
|
+
lintResult.errors.forEach((error) => {
|
|
113
|
+
const resultBuilder = new SarifResultBuilder().initSimple(transformedResults(lintResult, error, "error"));
|
|
114
|
+
runBuilder.addResult(resultBuilder);
|
|
115
|
+
});
|
|
116
|
+
lintResult.warnings.forEach((warning) => {
|
|
117
|
+
const resultBuilder = new SarifResultBuilder().initSimple(transformedResults(lintResult, warning, "warning"));
|
|
101
118
|
runBuilder.addResult(resultBuilder);
|
|
102
119
|
});
|
|
103
120
|
}
|
|
@@ -132,23 +149,34 @@ var CsvReportGenerator = class {
|
|
|
132
149
|
useBom: true,
|
|
133
150
|
useKeysAsHeaders: true
|
|
134
151
|
});
|
|
135
|
-
const transformedResults2 =
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
transformedResults2.push({
|
|
152
|
+
const transformedResults2 = results.flatMap(
|
|
153
|
+
(result) => [
|
|
154
|
+
...result.errors.map((error) => ({
|
|
139
155
|
"File Path": path.relative(cwd, result.filePath),
|
|
140
|
-
"Message": parseText(
|
|
156
|
+
"Message": parseText(error.message),
|
|
141
157
|
"Severity": "error",
|
|
142
|
-
"Rule ID": replaceNamespaceinRules(
|
|
143
|
-
"Start Line":
|
|
144
|
-
"Start Column":
|
|
145
|
-
"End Line":
|
|
158
|
+
"Rule ID": replaceNamespaceinRules(error.ruleId || "N/A"),
|
|
159
|
+
"Start Line": error.line,
|
|
160
|
+
"Start Column": error.column,
|
|
161
|
+
"End Line": error.endLine || error.line,
|
|
146
162
|
// Default to start line if missing
|
|
147
|
-
"End Column":
|
|
163
|
+
"End Column": error.endColumn || error.column
|
|
148
164
|
// Default to start column if missing
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
|
|
165
|
+
})),
|
|
166
|
+
...result.warnings.map((warning) => ({
|
|
167
|
+
"File Path": path.relative(cwd, result.filePath),
|
|
168
|
+
"Message": parseText(warning.message),
|
|
169
|
+
"Severity": "warning",
|
|
170
|
+
"Rule ID": replaceNamespaceinRules(warning.ruleId || "N/A"),
|
|
171
|
+
"Start Line": warning.line,
|
|
172
|
+
"Start Column": warning.column,
|
|
173
|
+
"End Line": warning.endLine || warning.line,
|
|
174
|
+
// Default to start line if missing
|
|
175
|
+
"End Column": warning.endColumn || warning.column
|
|
176
|
+
// Default to start column if missing
|
|
177
|
+
}))
|
|
178
|
+
]
|
|
179
|
+
);
|
|
152
180
|
return generateCsv(csvConfig)(transformedResults2);
|
|
153
181
|
}
|
|
154
182
|
};
|
package/build/types/index.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { ESLint, Linter } from 'eslint';
|
|
2
1
|
export interface BaseConfig {
|
|
3
2
|
directory?: string;
|
|
4
3
|
files?: string[];
|
|
@@ -33,17 +32,41 @@ export interface LintRunnerOptions {
|
|
|
33
32
|
maxWorkers?: number;
|
|
34
33
|
timeoutMs?: number;
|
|
35
34
|
}
|
|
36
|
-
export
|
|
37
|
-
|
|
35
|
+
export interface LintResultEntry {
|
|
36
|
+
line: number;
|
|
37
|
+
column: number;
|
|
38
|
+
endColumn: number;
|
|
39
|
+
message: string;
|
|
40
|
+
ruleId: string;
|
|
41
|
+
severity: number;
|
|
42
|
+
}
|
|
43
|
+
export interface LintResult {
|
|
44
|
+
filePath: string;
|
|
45
|
+
errors: Array<LintResultEntry>;
|
|
46
|
+
warnings: Array<LintResultEntry>;
|
|
47
|
+
}
|
|
38
48
|
export type ExitCode = 0 | 1 | 2;
|
|
39
49
|
export interface WorkerConfig {
|
|
40
50
|
configPath?: string;
|
|
41
51
|
fix?: boolean;
|
|
42
52
|
}
|
|
43
53
|
export interface WorkerResult {
|
|
44
|
-
|
|
45
|
-
lintResult?: LintResult;
|
|
54
|
+
file: string;
|
|
46
55
|
error?: string;
|
|
56
|
+
warnings?: Array<{
|
|
57
|
+
line: number;
|
|
58
|
+
column: number;
|
|
59
|
+
endColumn: number;
|
|
60
|
+
message: string;
|
|
61
|
+
ruleId: string;
|
|
62
|
+
}>;
|
|
63
|
+
errors?: Array<{
|
|
64
|
+
line: number;
|
|
65
|
+
column: number;
|
|
66
|
+
endColumn: number;
|
|
67
|
+
message: string;
|
|
68
|
+
ruleId: string;
|
|
69
|
+
}>;
|
|
47
70
|
}
|
|
48
71
|
export interface SarifResultEntry {
|
|
49
72
|
level: any;
|
|
@@ -55,22 +78,3 @@ export interface SarifResultEntry {
|
|
|
55
78
|
endLine?: number;
|
|
56
79
|
endColumn?: number;
|
|
57
80
|
}
|
|
58
|
-
export interface LintResultSummary {
|
|
59
|
-
totalErrors: number;
|
|
60
|
-
totalWarnings: number;
|
|
61
|
-
fixableErrors: number;
|
|
62
|
-
fixableWarnings: number;
|
|
63
|
-
}
|
|
64
|
-
export interface FilePattern {
|
|
65
|
-
extensions: string[];
|
|
66
|
-
exclude?: string[];
|
|
67
|
-
}
|
|
68
|
-
export interface ScanOptions {
|
|
69
|
-
patterns: FilePattern;
|
|
70
|
-
batchSize?: number;
|
|
71
|
-
gitignore?: boolean;
|
|
72
|
-
}
|
|
73
|
-
export interface ScanResult {
|
|
74
|
-
filesCount: number;
|
|
75
|
-
batches: string[][];
|
|
76
|
-
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/utils/editorLinkUtil.ts
|
|
2
|
-
import
|
|
2
|
+
import chalk from "chalk";
|
|
3
3
|
function detectCurrentEditor() {
|
|
4
4
|
const editor = process.env.EDITOR || process.env.VISUAL;
|
|
5
5
|
if (editor) {
|
|
@@ -43,7 +43,7 @@ function getEditorLink(editor, absolutePath, line, column) {
|
|
|
43
43
|
function createClickableLineCol(lineCol, absolutePath, line, column, editor) {
|
|
44
44
|
const detectedEditor = editor || detectCurrentEditor();
|
|
45
45
|
const link = getEditorLink(detectedEditor, absolutePath, line, column);
|
|
46
|
-
return `\x1B]8;;${link}\x07${
|
|
46
|
+
return `\x1B]8;;${link}\x07${chalk.blueBright(lineCol)}\x1B]8;;\x07`;
|
|
47
47
|
}
|
|
48
48
|
export {
|
|
49
49
|
createClickableLineCol,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LintResult, LintResultEntry, SarifResultEntry
|
|
1
|
+
import { LintResult, LintResultEntry, SarifResultEntry } from '../types';
|
|
2
2
|
/**
|
|
3
3
|
*
|
|
4
4
|
* @param id - Rule id
|
|
@@ -17,5 +17,5 @@ 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):
|
|
20
|
+
export declare function printLintResults(results: LintResult[], editor?: string): void;
|
|
21
21
|
export declare function transformedResults(lintResult: LintResult, entry: LintResultEntry, level: 'error' | 'warning'): SarifResultEntry;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// src/utils/lintResultsUtil.ts
|
|
2
|
+
import chalk from "chalk";
|
|
2
3
|
import path from "path";
|
|
3
4
|
import { createClickableLineCol } from "./editorLinkUtil.js";
|
|
4
5
|
import { Logger } from "../utils/logger.js";
|
|
5
|
-
import { Colors } from "./colors.js";
|
|
6
6
|
function replaceNamespaceinRules(id) {
|
|
7
7
|
return id.includes("@salesforce-ux/") ? id.replace("@salesforce-ux/", "") : id;
|
|
8
8
|
}
|
|
@@ -17,57 +17,38 @@ function parseText(text) {
|
|
|
17
17
|
return safeText.endsWith(".") ? safeText : `${safeText}.`;
|
|
18
18
|
}
|
|
19
19
|
function printLintResults(results, editor) {
|
|
20
|
-
let totalErrors = 0;
|
|
21
|
-
let totalWarnings = 0;
|
|
22
|
-
let fixableErrors = 0;
|
|
23
|
-
let fixableWarnings = 0;
|
|
24
20
|
results.forEach((result) => {
|
|
25
|
-
|
|
21
|
+
const hasErrors = result.errors && result.errors.length > 0;
|
|
22
|
+
const hasWarnings = result.warnings && result.warnings.length > 0;
|
|
23
|
+
if (!hasErrors && !hasWarnings) return;
|
|
26
24
|
const absolutePath = result.filePath || "";
|
|
27
25
|
const relativeFile = path.relative(process.cwd(), absolutePath) || "Unknown file";
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
totalWarnings++;
|
|
40
|
-
if (msg.fix) fixableWarnings++;
|
|
41
|
-
}
|
|
42
|
-
const lineCol = msg.line && msg.column ? `${msg.line}:${msg.column}` : "-";
|
|
43
|
-
const clickableLineCol = msg.line && msg.column ? createClickableLineCol(lineCol, absolutePath, msg.line, msg.column, editor) : lineCol;
|
|
44
|
-
const severityText = isError ? Colors.error("error") : Colors.warning("warning");
|
|
45
|
-
const message = parseText(msg.message);
|
|
46
|
-
const ruleId = msg.ruleId ? Colors.lowEmphasis(replaceNamespaceinRules(msg.ruleId)) : "";
|
|
47
|
-
tableData.push([clickableLineCol, severityText, message, ruleId]);
|
|
48
|
-
});
|
|
49
|
-
if (tableData.length > 0) {
|
|
50
|
-
tableData.forEach(([lineCol, severity, message, ruleId], index) => {
|
|
51
|
-
if (index > 0) console.log();
|
|
52
|
-
console.log(` ${lineCol} ${severity} ${message} ${ruleId}`);
|
|
26
|
+
Logger.newLine().info(`${chalk.bold(relativeFile)}`);
|
|
27
|
+
if (hasErrors) {
|
|
28
|
+
result.errors.forEach((error) => {
|
|
29
|
+
if (error.line && error.column && absolutePath) {
|
|
30
|
+
const lineCol = `${error.line}:${error.column}`;
|
|
31
|
+
const clickable = createClickableLineCol(lineCol, absolutePath, error.line, error.column, editor);
|
|
32
|
+
const ruleId = error.ruleId ? chalk.dim(replaceNamespaceinRules(error.ruleId)) : "";
|
|
33
|
+
Logger.error(` ${clickable} ${parseText(error.message)} ${ruleId}`);
|
|
34
|
+
} else {
|
|
35
|
+
Logger.error(` ${chalk.red("Error:")} ${parseText(error.message)}`);
|
|
36
|
+
}
|
|
53
37
|
});
|
|
54
38
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
39
|
+
if (hasWarnings) {
|
|
40
|
+
result.warnings.forEach((warn) => {
|
|
41
|
+
if (warn.line && warn.column && absolutePath) {
|
|
42
|
+
const lineCol = `${warn.line}:${warn.column}`;
|
|
43
|
+
const clickable = createClickableLineCol(lineCol, absolutePath, warn.line, warn.column, editor);
|
|
44
|
+
const ruleId = warn.ruleId ? chalk.dim(replaceNamespaceinRules(warn.ruleId)) : "";
|
|
45
|
+
Logger.warning(` ${clickable} ${parseText(warn.message)} ${ruleId}`);
|
|
46
|
+
} else {
|
|
47
|
+
Logger.warning(` ${chalk.yellow("Warning:")} ${parseText(warn.message)}`);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
66
50
|
}
|
|
67
|
-
}
|
|
68
|
-
Logger.newLine().success("No SLDS Violations found.");
|
|
69
|
-
}
|
|
70
|
-
return { totalErrors, totalWarnings, fixableErrors, fixableWarnings };
|
|
51
|
+
});
|
|
71
52
|
}
|
|
72
53
|
function transformedResults(lintResult, entry, level) {
|
|
73
54
|
return {
|
package/build/utils/logger.js
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
// src/utils/logger.ts
|
|
2
|
-
import
|
|
2
|
+
import chalk from "chalk";
|
|
3
3
|
var Logger = class {
|
|
4
4
|
static newLine() {
|
|
5
|
-
console.log("");
|
|
5
|
+
console.log("\n");
|
|
6
6
|
return this;
|
|
7
7
|
}
|
|
8
8
|
static info(message) {
|
|
9
|
-
console.log(
|
|
9
|
+
console.log(chalk.blue("\u2139"), message);
|
|
10
10
|
}
|
|
11
11
|
static success(message) {
|
|
12
|
-
console.log(
|
|
12
|
+
console.log(chalk.green("\u2713"), message);
|
|
13
13
|
}
|
|
14
14
|
static warning(message) {
|
|
15
|
-
console.warn(
|
|
15
|
+
console.warn(chalk.yellow("\u26A0"), message);
|
|
16
16
|
}
|
|
17
17
|
static error(message) {
|
|
18
|
-
console.error(
|
|
18
|
+
console.error(chalk.red("\u2716"), message);
|
|
19
19
|
}
|
|
20
20
|
static debug(message) {
|
|
21
21
|
if (process.env.DEBUG) {
|
|
22
|
-
console.debug(
|
|
22
|
+
console.debug(chalk.gray("\u{1F50D}"), message);
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
};
|
|
@@ -18,12 +18,25 @@ var ESLintWorker = class extends BaseWorker {
|
|
|
18
18
|
await ESLint.outputFixes(results);
|
|
19
19
|
}
|
|
20
20
|
return {
|
|
21
|
-
filePath,
|
|
22
|
-
|
|
21
|
+
file: filePath,
|
|
22
|
+
warnings: fileResult.messages.filter((msg) => msg.severity === 1).map((warning) => ({
|
|
23
|
+
line: warning.line,
|
|
24
|
+
column: warning.column,
|
|
25
|
+
endColumn: warning.endColumn,
|
|
26
|
+
message: warning.message,
|
|
27
|
+
ruleId: warning.ruleId || "unknown"
|
|
28
|
+
})),
|
|
29
|
+
errors: fileResult.messages.filter((msg) => msg.severity === 2).map((error) => ({
|
|
30
|
+
line: error.line,
|
|
31
|
+
column: error.column,
|
|
32
|
+
endColumn: error.endColumn,
|
|
33
|
+
message: error.message,
|
|
34
|
+
ruleId: error.ruleId || "unknown"
|
|
35
|
+
}))
|
|
23
36
|
};
|
|
24
37
|
} catch (error) {
|
|
25
38
|
return {
|
|
26
|
-
filePath,
|
|
39
|
+
file: filePath,
|
|
27
40
|
error: error.message
|
|
28
41
|
};
|
|
29
42
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce-ux/slds-linter",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7-internal",
|
|
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.0.
|
|
32
|
+
"@salesforce-ux/eslint-plugin-slds": "1.0.7-internal",
|
|
33
33
|
"@typescript-eslint/eslint-plugin": "^8.36.0",
|
|
34
34
|
"@typescript-eslint/parser": "^8.36.0",
|
|
35
35
|
"chalk": "^4.1.2",
|
package/build/utils/colors.d.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
/**
|
|
3
|
-
* Color constants matching the UX design system syntax colors for dark mode
|
|
4
|
-
* Based on the mock design specifications
|
|
5
|
-
*/
|
|
6
|
-
export declare const Colors: {
|
|
7
|
-
readonly error: chalk.Chalk;
|
|
8
|
-
readonly warning: chalk.Chalk;
|
|
9
|
-
readonly success: chalk.Chalk;
|
|
10
|
-
readonly info: chalk.Chalk;
|
|
11
|
-
readonly hyperlink: chalk.Chalk;
|
|
12
|
-
readonly lowEmphasis: chalk.Chalk;
|
|
13
|
-
readonly standard: chalk.Chalk;
|
|
14
|
-
readonly errorBold: chalk.Chalk;
|
|
15
|
-
readonly warningBold: chalk.Chalk;
|
|
16
|
-
};
|
package/build/utils/colors.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
// src/utils/colors.ts
|
|
2
|
-
import chalk from "chalk";
|
|
3
|
-
var Colors = {
|
|
4
|
-
// Error: light red/salmon
|
|
5
|
-
error: chalk.hex("#FF8080"),
|
|
6
|
-
// Warning: vibrant yellow
|
|
7
|
-
warning: chalk.hex("#FFFF80"),
|
|
8
|
-
// Success: bright light green
|
|
9
|
-
success: chalk.hex("#66BB6A"),
|
|
10
|
-
// Informational: light cyan/aqua blue
|
|
11
|
-
info: chalk.hex("#80FFFF"),
|
|
12
|
-
// Hyperlink: light blue/lavender
|
|
13
|
-
hyperlink: chalk.hex("#8080FF"),
|
|
14
|
-
// Low-emphasis text: light gray
|
|
15
|
-
lowEmphasis: chalk.hex("#A9A9A9"),
|
|
16
|
-
// Standard text: white (default)
|
|
17
|
-
standard: chalk.white,
|
|
18
|
-
// Bold variants for emphasis
|
|
19
|
-
errorBold: chalk.hex("#FF8080").bold,
|
|
20
|
-
warningBold: chalk.hex("#FFFF80").bold
|
|
21
|
-
};
|
|
22
|
-
export {
|
|
23
|
-
Colors
|
|
24
|
-
};
|