Skip to content

Commit b7a0dde

Browse files
authored
Merge pull request #60 from khawkins/bug_fixes
Bug fixes
2 parents ae44e59 + 7054421 commit b7a0dde

15 files changed

+534
-79
lines changed

jest.config.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ module.exports = {
1818
'<rootDir>/test/lib/processor.js',
1919
'!**/test/lib/rules/**/test.js',
2020
'!**/test/lib/rules/artifacts-combined-files/helper.js',
21-
'!**/test/lib/rules/shared.js'
21+
'!**/test/lib/rules/shared.js',
22+
'!**/test/lib/**/mock-*.js'
2223
],
2324
moduleFileExtensions: ['js', 'json'],
2425
testResultsProcessor: 'jest-sonar-reporter',

lib/configs/base.js

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99

1010
module.exports = {
1111
plugins: ['@salesforce/lwc-graph-analyzer'],
12-
processor: '@salesforce/lwc-graph-analyzer/bundleAnalyzer',
1312
parser: '@babel/eslint-parser',
1413
parserOptions: {
1514
ecmaVersion: 'latest',
@@ -20,10 +19,5 @@ module.exports = {
2019
plugins: [['decorators', { decoratorsBeforeExport: false }]]
2120
}
2221
}
23-
},
24-
overrides: [
25-
{
26-
files: ['**/*.html']
27-
}
28-
]
22+
}
2923
};

lib/configs/recommended.js

Lines changed: 72 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -9,59 +9,76 @@
99

1010
module.exports = {
1111
extends: ['./configs/base'],
12-
rules: {
13-
'@salesforce/lwc-graph-analyzer/no-getter-contains-more-than-return-statement': 'warn',
14-
'@salesforce/lwc-graph-analyzer/no-assignment-expression-assigns-value-to-member-variable':
15-
'warn',
16-
'@salesforce/lwc-graph-analyzer/no-wire-config-references-non-local-property-reactive-value':
17-
'warn',
18-
'@salesforce/lwc-graph-analyzer/no-private-wire-config-property': 'warn',
19-
'@salesforce/lwc-graph-analyzer/no-unresolved-parent-class-reference': 'warn',
20-
'@salesforce/lwc-graph-analyzer/no-class-refers-to-parent-class-from-unsupported-namespace':
21-
'warn',
22-
'@salesforce/lwc-graph-analyzer/no-reference-to-unsupported-namespace-reference': 'warn',
23-
'@salesforce/lwc-graph-analyzer/no-wire-config-property-uses-getter-function-returning-inaccessible-import':
24-
'warn',
25-
'@salesforce/lwc-graph-analyzer/no-wire-config-property-uses-getter-function-returning-non-literal':
26-
'warn',
27-
'@salesforce/lwc-graph-analyzer/no-wire-config-property-circular-wire-dependency': 'warn',
28-
'@salesforce/lwc-graph-analyzer/no-wire-configuration-property-using-output-of-non-primeable-wire':
29-
'warn',
30-
'@salesforce/lwc-graph-analyzer/no-missing-resource-cannot-prime-wire-adapter': 'warn',
31-
'@salesforce/lwc-graph-analyzer/no-wire-config-property-uses-imported-artifact-from-unsupported-namespace':
32-
'warn',
33-
'@salesforce/lwc-graph-analyzer/no-wire-adapter-of-resource-cannot-be-primed': 'warn',
34-
'@salesforce/lwc-graph-analyzer/no-unsupported-member-variable-in-member-expression':
35-
'warn',
36-
'@salesforce/lwc-graph-analyzer/no-multiple-template-files': 'warn',
37-
'@salesforce/lwc-graph-analyzer/no-assignment-expression-for-external-components': 'warn',
38-
'@salesforce/lwc-graph-analyzer/no-tagged-template-expression-contains-unsupported-namespace':
39-
'warn',
40-
'@salesforce/lwc-graph-analyzer/no-expression-contains-module-level-variable-ref': 'warn',
41-
'@salesforce/lwc-graph-analyzer/no-call-expression-references-unsupported-namespace':
42-
'warn',
43-
'@salesforce/lwc-graph-analyzer/no-eval-usage': 'warn',
44-
'@salesforce/lwc-graph-analyzer/no-reference-to-class-functions': 'warn',
45-
'@salesforce/lwc-graph-analyzer/no-reference-to-module-functions': 'warn',
46-
'@salesforce/lwc-graph-analyzer/no-functions-declared-within-getter-method': 'warn',
47-
'@salesforce/lwc-graph-analyzer/no-member-expression-reference-to-non-existent-member-variable':
48-
'warn',
49-
'@salesforce/lwc-graph-analyzer/no-member-expression-reference-to-unsupported-namespace-reference':
50-
'warn',
51-
'@salesforce/lwc-graph-analyzer/no-member-expression-contains-non-portable-identifier':
52-
'warn',
53-
'@salesforce/lwc-graph-analyzer/no-member-expression-reference-to-super-class': 'warn',
54-
'@salesforce/lwc-graph-analyzer/no-member-expression-reference-to-unsupported-global':
55-
'warn',
56-
'@salesforce/lwc-graph-analyzer/no-composition-on-unanalyzable-getter-property': 'warn',
57-
'@salesforce/lwc-graph-analyzer/no-composition-on-unanalyzable-property-from-unresolvable-wire':
58-
'warn',
59-
'@salesforce/lwc-graph-analyzer/no-composition-on-unanalyzable-property-missing': 'warn',
60-
'@salesforce/lwc-graph-analyzer/no-composition-on-unanalyzable-property-non-public': 'warn',
61-
'@salesforce/lwc-graph-analyzer/no-render-function-contains-more-than-return-statement':
62-
'warn',
63-
'@salesforce/lwc-graph-analyzer/no-render-function-return-statement-not-returning-imported-template':
64-
'warn',
65-
'@salesforce/lwc-graph-analyzer/no-render-function-return-statement': 'warn'
66-
}
12+
overrides: [
13+
{
14+
files: ['*.html', '**/*.html', '*.js', '**/*.js'],
15+
processor: '@salesforce/lwc-graph-analyzer/bundleAnalyzer',
16+
rules: {
17+
'@salesforce/lwc-graph-analyzer/no-getter-contains-more-than-return-statement':
18+
'warn',
19+
'@salesforce/lwc-graph-analyzer/no-assignment-expression-assigns-value-to-member-variable':
20+
'warn',
21+
'@salesforce/lwc-graph-analyzer/no-wire-config-references-non-local-property-reactive-value':
22+
'warn',
23+
'@salesforce/lwc-graph-analyzer/no-private-wire-config-property': 'warn',
24+
'@salesforce/lwc-graph-analyzer/no-unresolved-parent-class-reference': 'warn',
25+
'@salesforce/lwc-graph-analyzer/no-class-refers-to-parent-class-from-unsupported-namespace':
26+
'warn',
27+
'@salesforce/lwc-graph-analyzer/no-reference-to-unsupported-namespace-reference':
28+
'warn',
29+
'@salesforce/lwc-graph-analyzer/no-wire-config-property-uses-getter-function-returning-inaccessible-import':
30+
'warn',
31+
'@salesforce/lwc-graph-analyzer/no-wire-config-property-uses-getter-function-returning-non-literal':
32+
'warn',
33+
'@salesforce/lwc-graph-analyzer/no-wire-config-property-circular-wire-dependency':
34+
'warn',
35+
'@salesforce/lwc-graph-analyzer/no-wire-configuration-property-using-output-of-non-primeable-wire':
36+
'warn',
37+
'@salesforce/lwc-graph-analyzer/no-missing-resource-cannot-prime-wire-adapter':
38+
'warn',
39+
'@salesforce/lwc-graph-analyzer/no-wire-config-property-uses-imported-artifact-from-unsupported-namespace':
40+
'warn',
41+
'@salesforce/lwc-graph-analyzer/no-wire-adapter-of-resource-cannot-be-primed':
42+
'warn',
43+
'@salesforce/lwc-graph-analyzer/no-unsupported-member-variable-in-member-expression':
44+
'warn',
45+
'@salesforce/lwc-graph-analyzer/no-multiple-template-files': 'warn',
46+
'@salesforce/lwc-graph-analyzer/no-assignment-expression-for-external-components':
47+
'warn',
48+
'@salesforce/lwc-graph-analyzer/no-tagged-template-expression-contains-unsupported-namespace':
49+
'warn',
50+
'@salesforce/lwc-graph-analyzer/no-expression-contains-module-level-variable-ref':
51+
'warn',
52+
'@salesforce/lwc-graph-analyzer/no-call-expression-references-unsupported-namespace':
53+
'warn',
54+
'@salesforce/lwc-graph-analyzer/no-eval-usage': 'warn',
55+
'@salesforce/lwc-graph-analyzer/no-reference-to-class-functions': 'warn',
56+
'@salesforce/lwc-graph-analyzer/no-reference-to-module-functions': 'warn',
57+
'@salesforce/lwc-graph-analyzer/no-functions-declared-within-getter-method': 'warn',
58+
'@salesforce/lwc-graph-analyzer/no-member-expression-reference-to-non-existent-member-variable':
59+
'warn',
60+
'@salesforce/lwc-graph-analyzer/no-member-expression-reference-to-unsupported-namespace-reference':
61+
'warn',
62+
'@salesforce/lwc-graph-analyzer/no-member-expression-contains-non-portable-identifier':
63+
'warn',
64+
'@salesforce/lwc-graph-analyzer/no-member-expression-reference-to-super-class':
65+
'warn',
66+
'@salesforce/lwc-graph-analyzer/no-member-expression-reference-to-unsupported-global':
67+
'warn',
68+
'@salesforce/lwc-graph-analyzer/no-composition-on-unanalyzable-getter-property':
69+
'warn',
70+
'@salesforce/lwc-graph-analyzer/no-composition-on-unanalyzable-property-from-unresolvable-wire':
71+
'warn',
72+
'@salesforce/lwc-graph-analyzer/no-composition-on-unanalyzable-property-missing':
73+
'warn',
74+
'@salesforce/lwc-graph-analyzer/no-composition-on-unanalyzable-property-non-public':
75+
'warn',
76+
'@salesforce/lwc-graph-analyzer/no-render-function-contains-more-than-return-statement':
77+
'warn',
78+
'@salesforce/lwc-graph-analyzer/no-render-function-return-statement-not-returning-imported-template':
79+
'warn',
80+
'@salesforce/lwc-graph-analyzer/no-render-function-return-statement': 'warn'
81+
}
82+
}
83+
]
6784
};

lib/index.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ declare class LwcBundle {
8787

8888
/**
8989
* ESLint processor that analyzes LWC bundles. This will set up the LWC bundle to be processed
90-
* by Komaci.
90+
* by Komaci. The processor is configured to only process .js and .html files through the
91+
* plugin's configuration.
9192
*/
9293
export class BundleAnalyzer implements Linter.Processor {
9394
/** Gets the current LWC bundle being processed */

lib/processor.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,13 @@
88
'use strict';
99

1010
const { extname } = require('path');
11-
const { setLwcBundleCacheEntry, removeLwcBundleCacheEntry } = require('./util/helper');
11+
const {
12+
setLwcBundleCacheEntry,
13+
removeLwcBundleCacheEntry,
14+
extractBundleKey
15+
} = require('./util/helper');
1216
const LwcBundle = require('./lwc-bundle');
17+
const bundleStateManager = require('./util/bundle-state-manager');
1318

1419
const SUPPORTS_AUTOFIX = true;
1520

@@ -83,6 +88,13 @@ class BundleAnalyzer {
8388
* @returns {{ text: string, filename: string }[]} An array of files and their content, to be processed.
8489
*/
8590
preprocess = (text, filename) => {
91+
// Check if this is a file we generated ourselves with `preprocess()` return values.
92+
const bundleKey = extractBundleKey(filename);
93+
if (bundleStateManager.hasBundleWithKey(bundleKey)) {
94+
// Skip processing for files we generated
95+
return [{ text, filename }];
96+
}
97+
8698
const fileExtension = extname(filename);
8799

88100
// If this.#lwcBundle has already been set, use that data. Otherwise, create

lib/util/bundle-state-manager.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,16 @@ class BundleStateManager {
6565
clear() {
6666
this.#bundleMap.clear();
6767
}
68+
69+
/**
70+
* Checks if a bundle exists with the given key
71+
*
72+
* @param {string} key - The unique key to check for
73+
* @returns {boolean} True if a bundle exists with the given key, false otherwise
74+
*/
75+
hasBundleWithKey(key) {
76+
return this.#bundleMap.has(key);
77+
}
6878
}
6979

7080
// Export a singleton instance

lib/util/helper.js

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,25 @@
77

88
'use strict';
99

10-
const { basename } = require('path');
11-
const staticAnalyzer = require('@komaci/static-analyzer');
10+
const { basename, extname } = require('path');
1211
const bundleStateManager = require('./bundle-state-manager');
12+
const StaticAnalyzerProvider = require('./static-analyzer-provider');
1313
// eslint-disable-next-line no-unused-vars
1414
const LwcBundle = require('../lwc-bundle');
1515

1616
const lwcNamespace = 'c';
1717

18+
// Create a default provider instance
19+
let staticAnalyzerProvider = new StaticAnalyzerProvider();
20+
21+
/**
22+
* Sets the static analyzer provider to use
23+
* @param {import('./static-analyzer-interface')} provider - The provider to use
24+
*/
25+
function setStaticAnalyzerProvider(provider) {
26+
staticAnalyzerProvider = provider;
27+
}
28+
1829
function rangeToLoc(range) {
1930
const {
2031
start: { line, character: column },
@@ -63,7 +74,7 @@ function removeLwcBundleCacheEntry(bundle) {
6374
}
6475

6576
function analyzeLWC(context) {
66-
const eslintReports = getKomaciReport(context.id, context.filename);
77+
const eslintReports = getKomaciReport(context.id, context.filename, context.sourceCode.text);
6778

6879
for (const report of eslintReports) {
6980
context.report(report);
@@ -90,16 +101,28 @@ function extractBundleKey(eslintFilename) {
90101
return filename.replace(/^(\d+_)?/, '');
91102
}
92103

93-
function getKomaciReport(ruleName, filename) {
104+
/**
105+
* Gets the Komaci diagnostic reports for a given rule and file.
106+
* Filters the reports to only include those that:
107+
* 1. Match the specified rule
108+
* 2. Target the primary file in the bundle
109+
*
110+
* @param {string} ruleName - The full ESLint rule name (e.g. '@salesforce/lwc-graph-analyzer/rule-name')
111+
* @param {string} filename - The filename being processed by ESLint
112+
* @param {string} sourceCode - The source code of the file being processed by ESLint
113+
* @returns {Array<Object>} An array of ESLint report objects, each containing a message and location information
114+
*/
115+
function getKomaciReport(ruleName, filename, sourceCode) {
94116
const bundleKey = extractBundleKey(filename);
95-
const lwcBundle = bundleStateManager.getBundleByKey(bundleKey);
117+
let lwcBundle = bundleStateManager.getBundleByKey(bundleKey);
96118
if (!lwcBundle) {
97-
console.warn('getKomaciReport(): LWC bundle not configured. Nothing to do.');
98-
return [];
119+
// We didn't stash a bundle for this file. Our processor may not have been invoked, due
120+
// to a clash with another processor. Just create a bundle for this file.
121+
lwcBundle = LwcBundle.lwcBundleFromFile(sourceCode, filename, extname(filename));
99122
}
100123

101124
const lwcBundleFiles = lwcBundle.filesRecord();
102-
let eslintReports = staticAnalyzer.generatePrimingDiagnosticsModule({
125+
let eslintReports = staticAnalyzerProvider.generatePrimingDiagnosticsModule({
103126
type: 'bundle',
104127
namespace: lwcNamespace,
105128
name: lwcBundle.componentBaseName,
@@ -115,7 +138,7 @@ function getKomaciReport(ruleName, filename) {
115138

116139
// Diagnostic messages is the catalog of all Komaci errors. Find the one that matches the rule name
117140
// so that the Komaci reports can be filtered.
118-
const diagnosticMessage = staticAnalyzer.diagnosticMessages[ruleKey];
141+
const diagnosticMessage = staticAnalyzerProvider.diagnosticMessages[ruleKey];
119142

120143
if (!diagnosticMessage) {
121144
// No matching diagnostic message was found. Return an empty array to indicate that
@@ -133,7 +156,11 @@ function getKomaciReport(ruleName, filename) {
133156
eslintReports = eslintReports.filter((reportDiagnostic) => {
134157
const reportDiagnosticValue = extractDiagnosticCodeValue(reportDiagnostic);
135158
const diagnosticValue = extractDiagnosticCodeValue(diagnosticMessage);
136-
return reportDiagnosticValue === diagnosticValue;
159+
// Only include diagnostics that match both the rule and target the primary file
160+
return (
161+
reportDiagnosticValue === diagnosticValue &&
162+
reportDiagnostic.code.target.path === lwcBundle.primaryFile.filename
163+
);
137164
});
138165

139166
return eslintReports.map(diagnosticToReport);
@@ -142,5 +169,8 @@ function getKomaciReport(ruleName, filename) {
142169
module.exports = {
143170
setLwcBundleCacheEntry,
144171
removeLwcBundleCacheEntry,
145-
analyzeLWC
172+
analyzeLWC,
173+
extractBundleKey,
174+
setStaticAnalyzerProvider,
175+
getKomaciReport
146176
};

lib/util/static-analyzer-interface.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright (c) 2025, salesforce.com, inc.
3+
* All rights reserved.
4+
* SPDX-License-Identifier: MIT
5+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
6+
*/
7+
8+
'use strict';
9+
10+
/**
11+
* Interface for static analysis functionality
12+
*/
13+
class StaticAnalyzerInterface {
14+
constructor() {
15+
if (this.constructor === StaticAnalyzerInterface) {
16+
throw new TypeError(
17+
'StaticAnalyzerInterface is an abstract class and cannot be instantiated directly'
18+
);
19+
}
20+
}
21+
22+
/**
23+
* Generates priming diagnostics for a module
24+
* @param {Object} options - The options for generating diagnostics
25+
* @param {string} options.type - The type of module
26+
* @param {string} options.namespace - The namespace of the module
27+
* @param {string} options.name - The name of the module
28+
* @param {Object<string, string>} options.files - The files to analyze
29+
* @returns {Array<Object>} The generated diagnostics
30+
*/
31+
// eslint-disable-next-line no-unused-vars
32+
generatePrimingDiagnosticsModule(options) {
33+
throw new Error('Method not implemented');
34+
}
35+
36+
/**
37+
* Gets the diagnostic messages catalog
38+
* @returns {Object} The diagnostic messages catalog
39+
*/
40+
get diagnosticMessages() {
41+
throw new Error('Method not implemented');
42+
}
43+
}
44+
45+
module.exports = StaticAnalyzerInterface;

lib/util/static-analyzer-provider.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright (c) 2025, salesforce.com, inc.
3+
* All rights reserved.
4+
* SPDX-License-Identifier: MIT
5+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
6+
*/
7+
8+
'use strict';
9+
10+
const StaticAnalyzerInterface = require('./static-analyzer-interface');
11+
const staticAnalyzer = require('@komaci/static-analyzer');
12+
13+
/**
14+
* Concrete implementation of StaticAnalyzerInterface that uses the actual static analyzer
15+
*/
16+
class StaticAnalyzerProvider extends StaticAnalyzerInterface {
17+
generatePrimingDiagnosticsModule(options) {
18+
return staticAnalyzer.generatePrimingDiagnosticsModule(options);
19+
}
20+
21+
get diagnosticMessages() {
22+
return staticAnalyzer.diagnosticMessages;
23+
}
24+
}
25+
26+
module.exports = StaticAnalyzerProvider;

0 commit comments

Comments
 (0)