@salesforce-ux/slds-linter 1.0.7 → 1.0.9-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 +7 -14
- package/build/commands/report.js +2 -2
- package/build/executor/index.js +9 -31
- package/build/index.js +1 -1
- package/build/services/batch-processor.js +6 -0
- package/build/services/config.resolver.js +3 -157
- package/build/services/file-patterns.d.ts +1 -1
- package/build/services/file-scanner.d.ts +2 -10
- package/build/services/file-scanner.js +7 -5
- package/build/services/lint-runner.d.ts +1 -1
- package/build/services/lint-runner.js +4 -14
- package/build/services/progress-handler.d.ts +25 -0
- package/build/services/progress-handler.js +56 -0
- package/build/services/report-generator.d.ts +2 -2
- package/build/services/report-generator.js +23 -51
- package/build/types/index.d.ts +24 -28
- package/build/utils/colors.d.ts +16 -0
- package/build/utils/colors.js +24 -0
- package/build/utils/editorLinkUtil.js +2 -2
- package/build/utils/lintResultsUtil.d.ts +2 -2
- package/build/utils/lintResultsUtil.js +67 -27
- package/build/utils/logger.js +7 -7
- package/build/workers/eslint.worker.d.ts +7 -1
- package/build/workers/eslint.worker.js +14 -21
- package/package.json +3 -2
package/build/commands/emit.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/commands/emit.ts
|
|
2
|
-
import chalk from "chalk";
|
|
3
2
|
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(
|
|
50
|
+
Logger.info("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(Colors.success(`ESLint configuration created at:
|
|
58
58
|
${destESLintConfigPath}
|
|
59
59
|
`));
|
|
60
|
-
Logger.info(
|
|
60
|
+
Logger.info("Rules are dynamically loaded based on extends configuration.");
|
|
61
61
|
} catch (error) {
|
|
62
62
|
Logger.error(
|
|
63
|
-
|
|
63
|
+
Colors.error(`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";
|
|
4
3
|
import { printLintResults } from "../utils/lintResultsUtil.js";
|
|
5
4
|
import { normalizeCliOptions, normalizeDirectoryPath } from "../utils/config-utils.js";
|
|
6
5
|
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,32 +14,25 @@ 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(
|
|
17
|
+
Logger.info("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(Colors.warning(
|
|
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
|
-
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
|
-
);
|
|
30
|
+
const { totalErrors } = printLintResults(lintResults, normalizedOptions.editor);
|
|
37
31
|
const elapsedTime = ((Date.now() - startTime) / 1e3).toFixed(2);
|
|
38
|
-
Logger.
|
|
39
|
-
|
|
40
|
-
process.exit(errorCount > 0 ? 1 : 0);
|
|
32
|
+
Logger.newLine().success(`Linting completed in ${elapsedTime} seconds.`);
|
|
33
|
+
process.exit(totalErrors > 0 ? 1 : 0);
|
|
41
34
|
} catch (error) {
|
|
42
|
-
Logger.error(
|
|
35
|
+
Logger.error(Colors.error(`Failed to complete linting: ${error.message}`));
|
|
43
36
|
process.exit(1);
|
|
44
37
|
}
|
|
45
38
|
});
|
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";
|
|
6
5
|
import fs from "fs";
|
|
7
6
|
import { normalizeAndValidatePath, normalizeCliOptions, normalizeDirectoryPath } from "../utils/config-utils.js";
|
|
8
7
|
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(Colors.warning(
|
|
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,20 +13,25 @@ async function lint(config) {
|
|
|
13
13
|
const normalizedConfig = normalizeCliOptions(config, {
|
|
14
14
|
configEslint: DEFAULT_ESLINT_CONFIG_PATH
|
|
15
15
|
});
|
|
16
|
-
const styleFiles = await FileScanner.scanFiles(normalizedConfig.directory, {
|
|
16
|
+
const { filesCount: styleFilesCount, batches: styleFiles } = await FileScanner.scanFiles(normalizedConfig.directory, {
|
|
17
17
|
patterns: StyleFilePatterns,
|
|
18
18
|
batchSize: 100
|
|
19
19
|
});
|
|
20
|
-
const componentFiles = await FileScanner.scanFiles(normalizedConfig.directory, {
|
|
20
|
+
const { filesCount: componentFilesCount, batches: 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
|
+
}
|
|
24
30
|
const { fix, configEslint } = normalizedConfig;
|
|
25
|
-
|
|
31
|
+
return await LintRunner.runLinting([...styleFiles, ...componentFiles], {
|
|
26
32
|
fix,
|
|
27
33
|
configPath: configEslint
|
|
28
34
|
});
|
|
29
|
-
return standardizeLintMessages(lintResults);
|
|
30
35
|
} catch (error) {
|
|
31
36
|
const errorMessage = `Linting failed: ${error.message}`;
|
|
32
37
|
Logger.error(errorMessage);
|
|
@@ -64,33 +69,6 @@ async function report(config, results) {
|
|
|
64
69
|
throw new Error(errorMessage);
|
|
65
70
|
}
|
|
66
71
|
}
|
|
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
|
-
}
|
|
94
72
|
export {
|
|
95
73
|
lint,
|
|
96
74
|
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.9-internal");
|
|
23
23
|
}
|
|
24
24
|
registerLintCommand(program);
|
|
25
25
|
registerReportCommand(program);
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { Worker } from "worker_threads";
|
|
3
3
|
import os from "os";
|
|
4
4
|
import { Logger } from "../utils/logger.js";
|
|
5
|
+
import { ProgressHandler } from "./progress-handler.js";
|
|
5
6
|
var AVAILABLE_CPUS = os.cpus().length - 1;
|
|
6
7
|
var BatchProcessor = class {
|
|
7
8
|
static DEFAULT_MAX_WORKERS = Math.max(1, Math.min(4, AVAILABLE_CPUS));
|
|
@@ -22,6 +23,7 @@ var BatchProcessor = class {
|
|
|
22
23
|
const results = [];
|
|
23
24
|
const activeWorkers = /* @__PURE__ */ new Set();
|
|
24
25
|
let currentBatchIndex = 0;
|
|
26
|
+
const progress = new ProgressHandler({ total: batches.length });
|
|
25
27
|
try {
|
|
26
28
|
while (currentBatchIndex < batches.length || activeWorkers.size > 0) {
|
|
27
29
|
while (activeWorkers.size < maxWorkers && currentBatchIndex < batches.length) {
|
|
@@ -36,6 +38,7 @@ var BatchProcessor = class {
|
|
|
36
38
|
worker.on("message", (result) => {
|
|
37
39
|
results.push(result);
|
|
38
40
|
activeWorkers.delete(worker);
|
|
41
|
+
progress.increment();
|
|
39
42
|
Logger.debug(`Completed batch ${batchIndex} of ${batches.length}`);
|
|
40
43
|
}).on("error", (error) => {
|
|
41
44
|
results.push({
|
|
@@ -44,6 +47,7 @@ var BatchProcessor = class {
|
|
|
44
47
|
results: []
|
|
45
48
|
});
|
|
46
49
|
activeWorkers.delete(worker);
|
|
50
|
+
progress.increment();
|
|
47
51
|
Logger.error(`Worker error in batch ${batchIndex}: ${error.message}`);
|
|
48
52
|
}).on("exit", (code) => {
|
|
49
53
|
if (code !== 0) {
|
|
@@ -54,8 +58,10 @@ var BatchProcessor = class {
|
|
|
54
58
|
}
|
|
55
59
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
56
60
|
}
|
|
61
|
+
progress.stop();
|
|
57
62
|
return results;
|
|
58
63
|
} catch (error) {
|
|
64
|
+
progress.stop();
|
|
59
65
|
Logger.error(`Batch processing failed: ${error.message}`);
|
|
60
66
|
throw error;
|
|
61
67
|
} finally {
|
|
@@ -1,166 +1,12 @@
|
|
|
1
1
|
// src/services/config.resolver.ts
|
|
2
2
|
import { resolvePath } from "../utils/nodeVersionUtil.js";
|
|
3
|
-
|
|
4
|
-
// yaml-file:/Users/ritesh.kumar2/Documents/projects/stylelint-sds/packages/eslint-plugin-slds/src/config/rule-messages.yml
|
|
5
|
-
var rule_messages_default = {
|
|
6
|
-
"no-slds-class-overrides": {
|
|
7
|
-
"description": "Create new custom CSS classes instead of overriding SLDS selectors",
|
|
8
|
-
"url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#no-slds-class-overrides",
|
|
9
|
-
"type": "problem",
|
|
10
|
-
"messages": {
|
|
11
|
-
"sldsClassOverride": "Overriding .{{className}} isn't supported. To differentiate SLDS and custom classes, create a CSS class in your namespace. Examples: myapp-input, myapp-button."
|
|
12
|
-
}
|
|
13
|
-
},
|
|
14
|
-
"no-deprecated-slds-classes": {
|
|
15
|
-
"description": "Please replace the deprecated classes with a modern equivalent",
|
|
16
|
-
"url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#no-deprecated-slds-classes",
|
|
17
|
-
"type": "problem",
|
|
18
|
-
"messages": {
|
|
19
|
-
"deprecatedClass": "The class {{className}} is deprecated and not available in SLDS2. Please update to a supported class."
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
|
-
"no-deprecated-tokens-slds1": {
|
|
23
|
-
"description": "Update outdated design tokens to SLDS 2 styling hooks with similar values. For more information, see Styling Hooks on lightningdesignsystem.com.",
|
|
24
|
-
"url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#no-deprecated-tokens-slds1",
|
|
25
|
-
"type": "problem",
|
|
26
|
-
"messages": {
|
|
27
|
-
"deprecatedToken": "Consider removing {{oldValue}} or replacing it with {{newValue}}. Set the fallback to {{oldValue}}. For more info, see Styling Hooks on lightningdesignsystem.com.",
|
|
28
|
-
"noReplacement": "Update outdated design tokens to SLDS 2 styling hooks with similar values. For more information, see Styling Hooks on lightningdesignsystem.com."
|
|
29
|
-
}
|
|
30
|
-
},
|
|
31
|
-
"enforce-sds-to-slds-hooks": {
|
|
32
|
-
"description": "Convert your existing --sds styling hooks to --slds styling hooks. See lightningdesignsystem.com for more info.",
|
|
33
|
-
"url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#enforce-sds-to-slds-hooks",
|
|
34
|
-
"type": "problem",
|
|
35
|
-
"messages": {
|
|
36
|
-
"replaceSdsWithSlds": "Replace {{oldValue}} with {{suggestedMatch}} styling hook."
|
|
37
|
-
}
|
|
38
|
-
},
|
|
39
|
-
"enforce-bem-usage": {
|
|
40
|
-
"description": "Replace BEM double-dash syntax in class names with single underscore syntax",
|
|
41
|
-
"url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#enforce-bem-usage",
|
|
42
|
-
"type": "problem",
|
|
43
|
-
"messages": {
|
|
44
|
-
"bemDoubleDash": "{{actual}} has been retired. Update it to the new name {{newValue}}.",
|
|
45
|
-
"fixBemNaming": "Update to correct BEM naming convention"
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
"modal-close-button-issue": {
|
|
49
|
-
"description": "Update component attributes or CSS classes for the modal close button to comply with the modal component blueprint",
|
|
50
|
-
"url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#modal-close-button-issue",
|
|
51
|
-
"type": "problem",
|
|
52
|
-
"messages": {
|
|
53
|
-
"modalCloseButtonIssue": "Update component attributes or CSS classes for the modal close button to comply with the modal component blueprint.",
|
|
54
|
-
"removeClass": "Remove the slds-button_icon-inverse class from the modal close button in components that use the SLDS modal blueprint.",
|
|
55
|
-
"changeVariant": "Change the variant attribute value from bare-inverse to bare in <lightning-button-icon> or <lightning-icon>.",
|
|
56
|
-
"removeVariant": "Remove the variant attribute from the <lightning-icon> component inside the <button> element.",
|
|
57
|
-
"ensureButtonClasses": "Add or move slds-button and slds-button_icon to the class attribute of the <button> element or <lightning-button-icon> component.",
|
|
58
|
-
"ensureSizeAttribute": "To size icons properly, set the size attribute \u200Cto large in the <lightning-icon> and <lightning-button-icon> components."
|
|
59
|
-
}
|
|
60
|
-
},
|
|
61
|
-
"no-deprecated-classes-slds2": {
|
|
62
|
-
"description": "Replace classes that aren't available with SLDS 2 classes",
|
|
63
|
-
"url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#no-deprecated-classes-slds2",
|
|
64
|
-
"type": "problem",
|
|
65
|
-
"messages": {
|
|
66
|
-
"deprecatedClass": "The class {{className}} isn't available in SLDS 2. Update it to a class supported in SLDS 2. See lightningdesignsystem.com for more information.",
|
|
67
|
-
"updateToModernClass": "Replace deprecated class with modern equivalent",
|
|
68
|
-
"checkDocumentation": "See lightningdesignsystem.com for SLDS 2 class alternatives"
|
|
69
|
-
}
|
|
70
|
-
},
|
|
71
|
-
"lwc-token-to-slds-hook": {
|
|
72
|
-
"description": "Replace the deprecated --lwc tokens with the latest --slds tokens. See lightningdesignsystem.com for more info.",
|
|
73
|
-
"url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#lwc-token-to-slds-hook",
|
|
74
|
-
"type": "problem",
|
|
75
|
-
"messages": {
|
|
76
|
-
"errorWithReplacement": "The '{{oldValue}}' design token is deprecated. Replace it with '{{newValue}}'. For more info, see Global Styling Hooks on lightningdesignsystem.com.",
|
|
77
|
-
"errorWithStyleHooks": "The '{{oldValue}}' design token is deprecated. Replace it with the SLDS 2 '{{newValue}}' styling hook and set the fallback to '{{oldValue}}'. For more info, see Global Styling Hooks on lightningdesignsystem.com.",
|
|
78
|
-
"errorWithNoRecommendation": "The '{{oldValue}}' design token is deprecated. For more info, see the New Global Styling Hook Guidance on lightningdesignsystem.com."
|
|
79
|
-
}
|
|
80
|
-
},
|
|
81
|
-
"no-sldshook-fallback-for-lwctoken": {
|
|
82
|
-
"description": "Avoid using --slds styling hooks as fallback values for --lwc tokens.",
|
|
83
|
-
"url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#no-sldshook-fallback-for-lwctoken",
|
|
84
|
-
"type": "problem",
|
|
85
|
-
"messages": {
|
|
86
|
-
"unsupportedFallback": "Remove the {{sldsToken}} styling hook that is used as a fallback value for {{lwcToken}}."
|
|
87
|
-
}
|
|
88
|
-
},
|
|
89
|
-
"no-unsupported-hooks-slds2": {
|
|
90
|
-
"description": "Identifies styling hooks that aren't present in SLDS 2. They must be replaced with styling hooks that have a similar effect, or they must be removed.",
|
|
91
|
-
"url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#no-unsupported-hooks-slds2",
|
|
92
|
-
"type": "problem",
|
|
93
|
-
"messages": {
|
|
94
|
-
"deprecated": "The {{token}} styling hook isn't present in SLDS 2 and there's no equivalent replacement. Remove it or replace it with a styling hook with a similar effect."
|
|
95
|
-
}
|
|
96
|
-
},
|
|
97
|
-
"no-slds-var-without-fallback": {
|
|
98
|
-
"description": "Add fallback values to SLDS styling hooks. The fallback values are used in Salesforce environments where styling hooks are unavailable.",
|
|
99
|
-
"url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#no-slds-var-without-fallback",
|
|
100
|
-
"type": "problem",
|
|
101
|
-
"messages": {
|
|
102
|
-
"varWithoutFallback": "Your code uses the {{cssVar}} styling hook without a fallback value. Styling hooks are unavailable in some Salesforce environments. To render your component correctly in all environments, add this fallback value: var({{cssVar}}, {{recommendation}}). To make this fallback value brand-aware, use a branded design token instead of a static value. See Design Tokens on v1.lightningdesignsystem.com."
|
|
103
|
-
}
|
|
104
|
-
},
|
|
105
|
-
"no-slds-namespace-for-custom-hooks": {
|
|
106
|
-
"description": "To differentiate custom styling hooks from SLDS styling hooks, create custom styling hooks in your namespace.",
|
|
107
|
-
"url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#no-slds-namespace-for-custom-hooks",
|
|
108
|
-
"type": "problem",
|
|
109
|
-
"messages": {
|
|
110
|
-
"customHookNamespace": "Using the --slds namespace for {{token}} isn't supported. Create the custom styling hook in your namespace. Example: --myapp-{{tokenWithoutNamespace}}"
|
|
111
|
-
}
|
|
112
|
-
},
|
|
113
|
-
"no-slds-private-var": {
|
|
114
|
-
"description": "Some SLDS styling hooks are private and reserved only for internal Salesforce use. Private SLDS styling hooks have prefixes --_slds- and --slds-s-. For more information, look up private CSS in lightningdesignsystem.com.",
|
|
115
|
-
"url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#no-slds-private-var",
|
|
116
|
-
"type": "problem",
|
|
117
|
-
"messages": {
|
|
118
|
-
"privateVar": "This styling hook is reserved for internal Salesforce use. Remove the --_slds- or \u2013slds-s private variable within selector {{prop}}. For more information, look up private CSS in lightningdesignsystem.com."
|
|
119
|
-
}
|
|
120
|
-
},
|
|
121
|
-
"enforce-component-hook-naming-convention": {
|
|
122
|
-
"description": "Replace component styling hooks that use a deprecated naming convention.",
|
|
123
|
-
"url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#enforce-component-hook-naming-convention",
|
|
124
|
-
"type": "problem",
|
|
125
|
-
"messages": {
|
|
126
|
-
"replace": "Replace the deprecated {{oldValue}} component styling hook with {{suggestedMatch}}."
|
|
127
|
-
}
|
|
128
|
-
},
|
|
129
|
-
"no-hardcoded-values-slds1": {
|
|
130
|
-
"description": "Replace static values with SLDS 1 design tokens. For more information, look up design tokens on lightningdesignsystem.com.",
|
|
131
|
-
"url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#no-hardcoded-value",
|
|
132
|
-
"type": "suggestion",
|
|
133
|
-
"messages": {
|
|
134
|
-
"hardcodedValue": "Replace the {{oldValue}} static value with an SLDS 1 styling hook: {{newValue}}.",
|
|
135
|
-
"noReplacement": "There's no replacement styling hook for the {{oldValue}} static value. Remove the static value."
|
|
136
|
-
}
|
|
137
|
-
},
|
|
138
|
-
"no-hardcoded-values-slds2": {
|
|
139
|
-
"description": "Replace static values with SLDS 2 styling hooks. For more information, look up design tokens on lightningdesignsystem.com.",
|
|
140
|
-
"url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#no-hardcoded-values-slds2",
|
|
141
|
-
"type": "suggestion",
|
|
142
|
-
"messages": {
|
|
143
|
-
"hardcodedValue": "Consider replacing the {{oldValue}} static value with an SLDS 2 styling hook that has a similar value: {{newValue}}.",
|
|
144
|
-
"noReplacement": "There's no replacement styling hook for the {{oldValue}} static value. Remove the static value."
|
|
145
|
-
}
|
|
146
|
-
},
|
|
147
|
-
"reduce-annotations": {
|
|
148
|
-
"description": "Remove your annotations and update your code.",
|
|
149
|
-
"url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#reduce-annotations",
|
|
150
|
-
"type": "problem",
|
|
151
|
-
"messages": {
|
|
152
|
-
"removeAnnotation": "Remove this annotation and update the code to SLDS best practices. For help, file an issue at https://github.com/salesforce-ux/slds-linter/"
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
// src/services/config.resolver.ts
|
|
3
|
+
import ruleMessages from "@salesforce-ux/eslint-plugin-slds/rule-messages";
|
|
158
4
|
var DEFAULT_ESLINT_CONFIG_PATH = resolvePath("@salesforce-ux/eslint-plugin-slds/config", import.meta);
|
|
159
5
|
var ESLINT_VERSION = "9.36.0";
|
|
160
|
-
var LINTER_CLI_VERSION = "1.0.
|
|
6
|
+
var LINTER_CLI_VERSION = "1.0.9-internal";
|
|
161
7
|
var getRuleDescription = (ruleId) => {
|
|
162
8
|
const ruleIdWithoutNameSpace = `${ruleId}`.replace(/\@salesforce-ux\//, "").replace(/^slds\//, "");
|
|
163
|
-
return
|
|
9
|
+
return ruleMessages[ruleIdWithoutNameSpace]?.description || "--";
|
|
164
10
|
};
|
|
165
11
|
export {
|
|
166
12
|
DEFAULT_ESLINT_CONFIG_PATH,
|
|
@@ -1,12 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
extensions: string[];
|
|
3
|
-
exclude?: string[];
|
|
4
|
-
}
|
|
5
|
-
export interface ScanOptions {
|
|
6
|
-
patterns: FilePattern;
|
|
7
|
-
batchSize?: number;
|
|
8
|
-
gitignore?: boolean;
|
|
9
|
-
}
|
|
1
|
+
import { ScanOptions, ScanResult } from "../types";
|
|
10
2
|
export declare class FileScanner {
|
|
11
3
|
private static DEFAULT_BATCH_SIZE;
|
|
12
4
|
/**
|
|
@@ -15,7 +7,7 @@ export declare class FileScanner {
|
|
|
15
7
|
* @param options Scanning options including patterns and batch size
|
|
16
8
|
* @returns Array of file paths in batches
|
|
17
9
|
*/
|
|
18
|
-
static scanFiles(directory: string, options: ScanOptions): Promise<
|
|
10
|
+
static scanFiles(directory: string, options: ScanOptions): Promise<ScanResult>;
|
|
19
11
|
/**
|
|
20
12
|
* Validates that files exist and are readable
|
|
21
13
|
*/
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
// src/services/file-scanner.ts
|
|
2
|
+
import path from "path";
|
|
2
3
|
import { promises as fs } from "fs";
|
|
3
|
-
import { Logger } from "../utils/logger.js";
|
|
4
4
|
import { globby, isDynamicPattern } from "globby";
|
|
5
|
-
import {
|
|
6
|
-
import path from "path";
|
|
5
|
+
import { Logger } from "../utils/logger.js";
|
|
7
6
|
var FileScanner = class {
|
|
8
7
|
static DEFAULT_BATCH_SIZE = 100;
|
|
9
8
|
/**
|
|
@@ -53,7 +52,7 @@ var FileScanner = class {
|
|
|
53
52
|
absolute: true,
|
|
54
53
|
gitignore: options.gitignore !== false
|
|
55
54
|
}).then((matches) => matches.filter((match) => {
|
|
56
|
-
const fileExt = extname(match).substring(1);
|
|
55
|
+
const fileExt = path.extname(match).substring(1);
|
|
57
56
|
return options.patterns.extensions.includes(fileExt);
|
|
58
57
|
}));
|
|
59
58
|
const validFiles = await this.validateFiles(allFiles);
|
|
@@ -62,7 +61,10 @@ var FileScanner = class {
|
|
|
62
61
|
Logger.debug(
|
|
63
62
|
`Found ${validFiles.length} files, split into ${batches.length} batches`
|
|
64
63
|
);
|
|
65
|
-
return
|
|
64
|
+
return {
|
|
65
|
+
filesCount: validFiles.length,
|
|
66
|
+
batches
|
|
67
|
+
};
|
|
66
68
|
} catch (error) {
|
|
67
69
|
Logger.error(`Failed to scan files: ${error.message}`);
|
|
68
70
|
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[][],
|
|
6
|
+
static runLinting(fileBatches: string[][], 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,
|
|
11
|
+
static async runLinting(fileBatches, options = {}) {
|
|
12
12
|
try {
|
|
13
13
|
const workerScript = path.resolve(
|
|
14
14
|
resolveDirName(import.meta),
|
|
@@ -46,21 +46,11 @@ 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 || !result.lintResult) {
|
|
50
|
+
Logger.warning(`File processing failed: ${result.filePath} - ${result.error}`);
|
|
51
51
|
continue;
|
|
52
52
|
}
|
|
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
|
-
});
|
|
53
|
+
results.push(result.lintResult);
|
|
64
54
|
}
|
|
65
55
|
}
|
|
66
56
|
return results;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface ProgressHandlerOptions {
|
|
2
|
+
total: number;
|
|
3
|
+
format?: string;
|
|
4
|
+
}
|
|
5
|
+
export declare class ProgressHandler {
|
|
6
|
+
private progressBar;
|
|
7
|
+
private completed;
|
|
8
|
+
constructor(options: ProgressHandlerOptions);
|
|
9
|
+
/**
|
|
10
|
+
* Increment progress by one unit
|
|
11
|
+
*/
|
|
12
|
+
increment(): void;
|
|
13
|
+
/**
|
|
14
|
+
* Update progress to a specific value
|
|
15
|
+
*/
|
|
16
|
+
update(value: number): void;
|
|
17
|
+
/**
|
|
18
|
+
* Stop and cleanup the progress bar
|
|
19
|
+
*/
|
|
20
|
+
stop(): void;
|
|
21
|
+
/**
|
|
22
|
+
* Get current progress value
|
|
23
|
+
*/
|
|
24
|
+
getCompleted(): number;
|
|
25
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// src/services/progress-handler.ts
|
|
2
|
+
import cliProgress from "cli-progress";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
var ProgressHandler = class {
|
|
5
|
+
progressBar = null;
|
|
6
|
+
completed = 0;
|
|
7
|
+
constructor(options) {
|
|
8
|
+
const format = options.format || `Processing [{bar}] {percentage}% ({value}/{total})`;
|
|
9
|
+
const isCI = String(process.env.CI || "").toLowerCase() === "true" || process.env.CI === "1";
|
|
10
|
+
const isTTY = Boolean(process.stdout.isTTY);
|
|
11
|
+
if (!isTTY || isCI) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
this.progressBar = new cliProgress.SingleBar({
|
|
15
|
+
format,
|
|
16
|
+
barCompleteChar: "\u2588",
|
|
17
|
+
barIncompleteChar: "\u2591",
|
|
18
|
+
hideCursor: true,
|
|
19
|
+
formatBar: (progress, options2) => {
|
|
20
|
+
const completeSize = Math.round(progress * options2.barsize);
|
|
21
|
+
const incompleteSize = options2.barsize - completeSize;
|
|
22
|
+
return chalk.cyan(options2.barCompleteChar.repeat(completeSize)) + chalk.cyanBright(options2.barIncompleteChar.repeat(incompleteSize));
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
this.progressBar.start(options.total, 0);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Increment progress by one unit
|
|
29
|
+
*/
|
|
30
|
+
increment() {
|
|
31
|
+
this.completed++;
|
|
32
|
+
this.progressBar?.update(this.completed);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Update progress to a specific value
|
|
36
|
+
*/
|
|
37
|
+
update(value) {
|
|
38
|
+
this.completed = value;
|
|
39
|
+
this.progressBar?.update(value);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Stop and cleanup the progress bar
|
|
43
|
+
*/
|
|
44
|
+
stop() {
|
|
45
|
+
this.progressBar?.stop();
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Get current progress value
|
|
49
|
+
*/
|
|
50
|
+
getCompleted() {
|
|
51
|
+
return this.completed;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
export {
|
|
55
|
+
ProgressHandler
|
|
56
|
+
};
|
|
@@ -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: LintResult[]): Promise<string>;
|
|
35
35
|
/**
|
|
36
36
|
* Generate CSV string from lint results
|
|
37
37
|
*/
|
|
38
|
-
static generateCsvString(results:
|
|
38
|
+
static generateCsvString(results: LintResult[]): string;
|
|
39
39
|
/**
|
|
40
40
|
* Convert lint results to CSV-compatible data format
|
|
41
41
|
*/
|
|
@@ -75,46 +75,29 @@ var ReportGenerator = class {
|
|
|
75
75
|
*/
|
|
76
76
|
static extractRules(results) {
|
|
77
77
|
const rules = /* @__PURE__ */ new Map();
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (!rules.has(
|
|
81
|
-
rules.set(
|
|
82
|
-
id: replaceNamespaceinRules(
|
|
78
|
+
results.forEach((result) => {
|
|
79
|
+
result.messages.forEach((message) => {
|
|
80
|
+
if (!rules.has(message.ruleId)) {
|
|
81
|
+
rules.set(message.ruleId, {
|
|
82
|
+
id: replaceNamespaceinRules(message.ruleId),
|
|
83
83
|
shortDescription: {
|
|
84
|
-
text: getRuleDescription(replaceNamespaceinRules(
|
|
84
|
+
text: getRuleDescription(replaceNamespaceinRules(message.ruleId))
|
|
85
85
|
},
|
|
86
86
|
properties: {
|
|
87
87
|
category: "Style"
|
|
88
88
|
}
|
|
89
89
|
});
|
|
90
90
|
}
|
|
91
|
-
}
|
|
92
|
-
|
|
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
|
-
}
|
|
91
|
+
});
|
|
92
|
+
});
|
|
106
93
|
return Array.from(rules.values());
|
|
107
94
|
}
|
|
108
95
|
/**
|
|
109
96
|
* Add lint results to SARIF report
|
|
110
97
|
*/
|
|
111
98
|
static addResultsToSarif(runBuilder, lintResult) {
|
|
112
|
-
lintResult.
|
|
113
|
-
const resultBuilder = new SarifResultBuilder().initSimple(transformedResults(lintResult,
|
|
114
|
-
runBuilder.addResult(resultBuilder);
|
|
115
|
-
});
|
|
116
|
-
lintResult.warnings.forEach((warning) => {
|
|
117
|
-
const resultBuilder = new SarifResultBuilder().initSimple(transformedResults(lintResult, warning, "warning"));
|
|
99
|
+
lintResult.messages.forEach((message) => {
|
|
100
|
+
const resultBuilder = new SarifResultBuilder().initSimple(transformedResults(lintResult, message, "error"));
|
|
118
101
|
runBuilder.addResult(resultBuilder);
|
|
119
102
|
});
|
|
120
103
|
}
|
|
@@ -149,34 +132,23 @@ var CsvReportGenerator = class {
|
|
|
149
132
|
useBom: true,
|
|
150
133
|
useKeysAsHeaders: true
|
|
151
134
|
});
|
|
152
|
-
const transformedResults2 =
|
|
153
|
-
|
|
154
|
-
|
|
135
|
+
const transformedResults2 = [];
|
|
136
|
+
results.forEach((result) => {
|
|
137
|
+
result.messages.forEach((message) => {
|
|
138
|
+
transformedResults2.push({
|
|
155
139
|
"File Path": path.relative(cwd, result.filePath),
|
|
156
|
-
"Message": parseText(
|
|
140
|
+
"Message": parseText(message.message),
|
|
157
141
|
"Severity": "error",
|
|
158
|
-
"Rule ID": replaceNamespaceinRules(
|
|
159
|
-
"Start Line":
|
|
160
|
-
"Start Column":
|
|
161
|
-
"End Line":
|
|
142
|
+
"Rule ID": replaceNamespaceinRules(message.ruleId || "N/A"),
|
|
143
|
+
"Start Line": message.line,
|
|
144
|
+
"Start Column": message.column,
|
|
145
|
+
"End Line": message.endLine || message.line,
|
|
162
146
|
// Default to start line if missing
|
|
163
|
-
"End Column":
|
|
147
|
+
"End Column": message.endColumn || message.column
|
|
164
148
|
// Default to start column if missing
|
|
165
|
-
})
|
|
166
|
-
|
|
167
|
-
|
|
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
|
-
);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
});
|
|
180
152
|
return generateCsv(csvConfig)(transformedResults2);
|
|
181
153
|
}
|
|
182
154
|
};
|
package/build/types/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ESLint, Linter } from 'eslint';
|
|
1
2
|
export interface BaseConfig {
|
|
2
3
|
directory?: string;
|
|
3
4
|
files?: string[];
|
|
@@ -32,41 +33,17 @@ export interface LintRunnerOptions {
|
|
|
32
33
|
maxWorkers?: number;
|
|
33
34
|
timeoutMs?: number;
|
|
34
35
|
}
|
|
35
|
-
export
|
|
36
|
-
|
|
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
|
-
}
|
|
36
|
+
export type LintResultEntry = Linter.LintMessage;
|
|
37
|
+
export type LintResult = ESLint.LintResult;
|
|
48
38
|
export type ExitCode = 0 | 1 | 2;
|
|
49
39
|
export interface WorkerConfig {
|
|
50
40
|
configPath?: string;
|
|
51
41
|
fix?: boolean;
|
|
52
42
|
}
|
|
53
43
|
export interface WorkerResult {
|
|
54
|
-
|
|
44
|
+
filePath: string;
|
|
45
|
+
lintResult?: LintResult;
|
|
55
46
|
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
|
-
}>;
|
|
70
47
|
}
|
|
71
48
|
export interface SarifResultEntry {
|
|
72
49
|
level: any;
|
|
@@ -78,3 +55,22 @@ export interface SarifResultEntry {
|
|
|
78
55
|
endLine?: number;
|
|
79
56
|
endColumn?: number;
|
|
80
57
|
}
|
|
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
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
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
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
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
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/utils/editorLinkUtil.ts
|
|
2
|
-
import
|
|
2
|
+
import { Colors } from "./colors.js";
|
|
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${Colors.info(lineCol)}\x1B]8;;\x07`;
|
|
47
47
|
}
|
|
48
48
|
export {
|
|
49
49
|
createClickableLineCol,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LintResult, LintResultEntry, SarifResultEntry } from '../types';
|
|
1
|
+
import { LintResult, LintResultEntry, SarifResultEntry, LintResultSummary } 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): LintResultSummary;
|
|
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";
|
|
3
2
|
import path from "path";
|
|
4
3
|
import { createClickableLineCol } from "./editorLinkUtil.js";
|
|
5
4
|
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
|
}
|
|
@@ -16,39 +16,79 @@ function parseText(text) {
|
|
|
16
16
|
}
|
|
17
17
|
return safeText.endsWith(".") ? safeText : `${safeText}.`;
|
|
18
18
|
}
|
|
19
|
+
function printTotalViolationsSummary(totalErrors, totalWarnings) {
|
|
20
|
+
const totalProblems = totalErrors + totalWarnings;
|
|
21
|
+
let totalProblemsText = `${totalProblems} SLDS Violation${totalProblems !== 1 ? "s" : ""}`;
|
|
22
|
+
if (totalErrors > 0) {
|
|
23
|
+
totalProblemsText = Colors.error(`\u2716 ${totalProblemsText}`);
|
|
24
|
+
} else {
|
|
25
|
+
totalProblemsText = Colors.warning(`\u26A0 ${totalProblemsText}`);
|
|
26
|
+
}
|
|
27
|
+
const totalBreakdown = [
|
|
28
|
+
Colors.error(`${totalErrors} error${totalErrors !== 1 ? "s" : ""}`),
|
|
29
|
+
Colors.warning(`${totalWarnings} warning${totalWarnings !== 1 ? "s" : ""}`)
|
|
30
|
+
];
|
|
31
|
+
console.log(`${totalProblemsText} (${totalBreakdown.join(", ")})`);
|
|
32
|
+
}
|
|
33
|
+
function printFixableViolationsSummary(fixableErrors, fixableWarnings) {
|
|
34
|
+
const fixableTotal = fixableErrors + fixableWarnings;
|
|
35
|
+
if (!fixableTotal) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const fixableBreakdown = [];
|
|
39
|
+
if (fixableErrors > 0) {
|
|
40
|
+
fixableBreakdown.push(Colors.error(`${fixableErrors} error${fixableErrors !== 1 ? "s" : ""}`));
|
|
41
|
+
}
|
|
42
|
+
if (fixableWarnings > 0) {
|
|
43
|
+
fixableBreakdown.push(Colors.warning(`${fixableWarnings} warning${fixableWarnings !== 1 ? "s" : ""}`));
|
|
44
|
+
}
|
|
45
|
+
console.log(` ${fixableBreakdown.join(" and ")} potentially fixable with the \`--fix\` option.`);
|
|
46
|
+
}
|
|
19
47
|
function printLintResults(results, editor) {
|
|
48
|
+
let totalErrors = 0;
|
|
49
|
+
let totalWarnings = 0;
|
|
50
|
+
let fixableErrors = 0;
|
|
51
|
+
let fixableWarnings = 0;
|
|
20
52
|
results.forEach((result) => {
|
|
21
|
-
|
|
22
|
-
const hasWarnings = result.warnings && result.warnings.length > 0;
|
|
23
|
-
if (!hasErrors && !hasWarnings) return;
|
|
53
|
+
if (!result.messages || result.messages.length === 0) return;
|
|
24
54
|
const absolutePath = result.filePath || "";
|
|
25
55
|
const relativeFile = path.relative(process.cwd(), absolutePath) || "Unknown file";
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
56
|
+
console.log(`
|
|
57
|
+
${Colors.info.underline(relativeFile)}
|
|
58
|
+
`);
|
|
59
|
+
const tableData = [];
|
|
60
|
+
result.messages.forEach((msg) => {
|
|
61
|
+
const isError = msg.severity === 2;
|
|
62
|
+
const isWarning = msg.severity === 1;
|
|
63
|
+
if (isError) {
|
|
64
|
+
totalErrors++;
|
|
65
|
+
if (msg.fix) fixableErrors++;
|
|
66
|
+
} else if (isWarning) {
|
|
67
|
+
totalWarnings++;
|
|
68
|
+
if (msg.fix) fixableWarnings++;
|
|
69
|
+
}
|
|
70
|
+
const lineCol = msg.line && msg.column ? `${msg.line}:${msg.column}` : "-";
|
|
71
|
+
const clickableLineCol = msg.line && msg.column ? createClickableLineCol(lineCol, absolutePath, msg.line, msg.column, editor) : lineCol;
|
|
72
|
+
const severityText = isError ? Colors.error("error") : Colors.warning("warning");
|
|
73
|
+
const message = parseText(msg.message);
|
|
74
|
+
const ruleId = msg.ruleId ? Colors.lowEmphasis(replaceNamespaceinRules(msg.ruleId)) : "";
|
|
75
|
+
tableData.push([clickableLineCol, severityText, message, ruleId]);
|
|
76
|
+
});
|
|
77
|
+
if (tableData.length > 0) {
|
|
78
|
+
tableData.forEach(([lineCol, severity, message, ruleId], index) => {
|
|
79
|
+
if (index > 0) console.log();
|
|
80
|
+
console.log(` ${lineCol} ${severity} ${message} ${ruleId}`);
|
|
49
81
|
});
|
|
50
82
|
}
|
|
51
83
|
});
|
|
84
|
+
Logger.newLine();
|
|
85
|
+
if (totalErrors > 0 || totalWarnings > 0) {
|
|
86
|
+
printTotalViolationsSummary(totalErrors, totalWarnings);
|
|
87
|
+
printFixableViolationsSummary(fixableErrors, fixableWarnings);
|
|
88
|
+
} else {
|
|
89
|
+
Logger.success("No SLDS Violations found.");
|
|
90
|
+
}
|
|
91
|
+
return { totalErrors, totalWarnings, fixableErrors, fixableWarnings };
|
|
52
92
|
}
|
|
53
93
|
function transformedResults(lintResult, entry, level) {
|
|
54
94
|
return {
|
package/build/utils/logger.js
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
// src/utils/logger.ts
|
|
2
|
-
import
|
|
2
|
+
import { Colors } from "./colors.js";
|
|
3
3
|
var Logger = class {
|
|
4
4
|
static newLine() {
|
|
5
|
-
console.log("
|
|
5
|
+
console.log("");
|
|
6
6
|
return this;
|
|
7
7
|
}
|
|
8
8
|
static info(message) {
|
|
9
|
-
console.log(
|
|
9
|
+
console.log(Colors.info(`\u2139 ${message}`));
|
|
10
10
|
}
|
|
11
11
|
static success(message) {
|
|
12
|
-
console.log(
|
|
12
|
+
console.log(Colors.success("\u2713"), message);
|
|
13
13
|
}
|
|
14
14
|
static warning(message) {
|
|
15
|
-
console.warn(
|
|
15
|
+
console.warn(Colors.warning("\u26A0"), message);
|
|
16
16
|
}
|
|
17
17
|
static error(message) {
|
|
18
|
-
console.error(
|
|
18
|
+
console.error(Colors.error("\u2716"), message);
|
|
19
19
|
}
|
|
20
20
|
static debug(message) {
|
|
21
21
|
if (process.env.DEBUG) {
|
|
22
|
-
console.debug(
|
|
22
|
+
console.debug(Colors.lowEmphasis("\u{1F50D}"), message);
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
};
|
|
@@ -1 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import { BaseWorker } from './base.worker';
|
|
2
|
+
import { WorkerConfig, WorkerResult } from '../types';
|
|
3
|
+
export declare class ESLintWorker extends BaseWorker<WorkerConfig, WorkerResult> {
|
|
4
|
+
private eslint;
|
|
5
|
+
constructor();
|
|
6
|
+
protected processFile(filePath: string): Promise<WorkerResult>;
|
|
7
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// src/workers/eslint.worker.ts
|
|
2
2
|
import { ESLint } from "eslint";
|
|
3
|
+
import { isMainThread } from "worker_threads";
|
|
3
4
|
import { BaseWorker } from "./base.worker.js";
|
|
4
5
|
var ESLintWorker = class extends BaseWorker {
|
|
5
6
|
eslint;
|
|
@@ -18,32 +19,24 @@ var ESLintWorker = class extends BaseWorker {
|
|
|
18
19
|
await ESLint.outputFixes(results);
|
|
19
20
|
}
|
|
20
21
|
return {
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
}))
|
|
22
|
+
filePath,
|
|
23
|
+
lintResult: fileResult
|
|
36
24
|
};
|
|
37
25
|
} catch (error) {
|
|
38
26
|
return {
|
|
39
|
-
|
|
27
|
+
filePath,
|
|
40
28
|
error: error.message
|
|
41
29
|
};
|
|
42
30
|
}
|
|
43
31
|
}
|
|
44
32
|
};
|
|
45
|
-
|
|
46
|
-
worker
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
33
|
+
if (!isMainThread) {
|
|
34
|
+
const worker = new ESLintWorker();
|
|
35
|
+
worker.process().catch((error) => {
|
|
36
|
+
console.error("Worker failed:", error);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
export {
|
|
41
|
+
ESLintWorker
|
|
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.9-internal",
|
|
4
4
|
"description": "SLDS Linter CLI tool for linting styles and components",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"lightning design system linter",
|
|
@@ -29,10 +29,11 @@
|
|
|
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.9-internal",
|
|
33
33
|
"@typescript-eslint/eslint-plugin": "^8.36.0",
|
|
34
34
|
"@typescript-eslint/parser": "^8.36.0",
|
|
35
35
|
"chalk": "^4.1.2",
|
|
36
|
+
"cli-progress": "^3.12.0",
|
|
36
37
|
"commander": "^13.1.0",
|
|
37
38
|
"eslint": "^9.0.0",
|
|
38
39
|
"export-to-csv": "^1.4.0",
|