Skip to content

Commit 182d734

Browse files
functional mutation-not-supported-rule
1 parent 9a12b87 commit 182d734

12 files changed

+171
-22
lines changed

eslint.config.mjs

+7-10
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,10 @@
88
import eslint from '@eslint/js';
99
import tsEslint from 'typescript-eslint';
1010

11-
export default tsEslint.config(
12-
eslint.configs.recommended,
13-
...tsEslint.configs.recommended,
14-
{
15-
rules: {
16-
strict: ['error', 'global'],
17-
'@typescript-eslint/no-extra-non-null-assertion': 'off'
18-
}
19-
},
20-
);
11+
export default tsEslint.config(eslint.configs.recommended, ...tsEslint.configs.recommended, {
12+
rules: {
13+
strict: ['error', 'global'],
14+
'@typescript-eslint/no-extra-non-null-assertion': 'off',
15+
'@typescript-eslint/no-explicit-any': 'off'
16+
}
17+
});

jest.config.ts

+7-6
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,18 @@ const config: Config = {
1111
displayName: 'Unit Tests',
1212
setupFilesAfterEnv: ['jest-extended', 'jest-chain'],
1313
preset: 'ts-jest',
14-
testMatch: [
15-
'<rootDir>/test/plugin.ts',
16-
'<rootDir>/test/rules/**/*.ts',
17-
'!**/test/rules/shared.ts'
18-
],
14+
testMatch: ['<rootDir>/test/rules/**/*.ts'],
1915
moduleFileExtensions: ['ts', 'js', 'json'],
2016
testResultsProcessor: 'jest-sonar-reporter',
2117
testPathIgnorePatterns: ['/node_modules/', '<rootDir>/lib/'],
2218
moduleDirectories: ['node_modules'],
2319
collectCoverage: true,
24-
collectCoverageFrom: ['src/**/*.ts'],
20+
collectCoverageFrom: [
21+
'src/**/*.ts',
22+
'!src/index.ts',
23+
'!src/configs/*.ts',
24+
'!src/graphql/types.ts'
25+
],
2526
coverageDirectory: 'reports/coverage',
2627
reporters: [
2728
'default',

package-lock.json

+11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
"devDependencies": {
6666
"@eslint/js": "^9.2.0",
6767
"@jest/globals": "^29.7.0",
68+
"@types/jest": "^29.5.12",
6869
"@typescript-eslint/eslint-plugin": "^7.8.0",
6970
"@typescript-eslint/parser": "^7.8.0",
7071
"@typescript-eslint/rule-tester": "^7.8.0",

src/configs/base.ts

+18-1
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,22 @@
77
import type { ClassicConfig } from '@typescript-eslint/utils/ts-eslint';
88

99
export = {
10-
plugins: ['@salesforce/lwc-mobile']
10+
plugins: ['@salesforce/lwc-mobile', '@graphql-eslint'],
11+
overrides: [
12+
{
13+
files: ['*.js'],
14+
processor: '@graphql-eslint/graphql'
15+
},
16+
{
17+
files: ['*.graphql'],
18+
parser: '@graphql-eslint/eslint-plugin',
19+
20+
parserOptions: {
21+
skipGraphQLConfig: true
22+
},
23+
rules: {
24+
'@salesforce/lwc-mobile/mutation-not-supported': 'warn'
25+
}
26+
}
27+
]
1128
} satisfies ClassicConfig.Config;

src/index.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,10 @@
55
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
66
*/
77

8-
import type { Linter } from '@typescript-eslint/utils/ts-eslint';
9-
108
import base from './configs/base.js';
119
import recommended from './configs/recommended.js';
1210
import enforceFooBar from './rules/dummy/enforce-foo-bar.js';
11+
import { rule as mutionNotSupported } from './rules/graphql/mutation-not-supported.js';
1312
import { name, version } from '../package.json';
1413
export = {
1514
configs: {
@@ -21,6 +20,7 @@ export = {
2120
version
2221
},
2322
rules: {
24-
'enforce-foo-bar': enforceFooBar
23+
'enforce-foo-bar': enforceFooBar,
24+
'mutation-not-supported': mutionNotSupported
2525
}
26-
} satisfies Linter.Plugin;
26+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { GraphQLESLintRule, GraphQLESLintRuleContext } from '@graphql-eslint/eslint-plugin';
2+
3+
import { getLocation } from '../utils';
4+
export const MESSAGE_ID = 'mutation-not-supported';
5+
6+
export const rule: GraphQLESLintRule = {
7+
meta: {
8+
type: 'problem',
9+
hasSuggestions: false,
10+
docs: {
11+
description: 'mutation is not supported offline',
12+
category: 'Operations',
13+
recommended: true,
14+
examples: [
15+
{
16+
title: 'Incorrect',
17+
code: /* GraphQL */ `
18+
mutation AccountExample {
19+
uiapi {
20+
AccountCreate(input: { Account: { Name: "Trailblazer Express" } }) {
21+
Record {
22+
Id
23+
Name {
24+
value
25+
}
26+
}
27+
}
28+
}
29+
}
30+
`
31+
}
32+
]
33+
},
34+
messages: {
35+
[MESSAGE_ID]: 'Mutation is not supported offline'
36+
},
37+
schema: []
38+
},
39+
40+
create(context: GraphQLESLintRuleContext) {
41+
return {
42+
OperationDefinition(node) {
43+
if (node.operation === 'mutation') {
44+
context.report({
45+
messageId: MESSAGE_ID,
46+
loc: getLocation(node.loc.start, node.operation)
47+
});
48+
}
49+
}
50+
};
51+
}
52+
};
File renamed without changes.

src/rules/utils.ts

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { Position } from 'estree';
2+
import { AST } from 'eslint';
3+
4+
export function getLocation(start: Position, fieldName = ''): AST.SourceLocation {
5+
const { line, column } = start;
6+
return {
7+
start: {
8+
line,
9+
column
10+
},
11+
end: {
12+
line,
13+
column: column + fieldName.length
14+
}
15+
};
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { RuleTester } from '@typescript-eslint/rule-tester';
2+
import { rule, MESSAGE_ID } from '../../../src/rules/graphql/mutation-not-supported';
3+
4+
const ruleTester = new RuleTester({
5+
parser: '@graphql-eslint/eslint-plugin',
6+
parserOptions: {
7+
graphQLConfig: {}
8+
}
9+
});
10+
11+
ruleTester.run('@salesforce/lwc-mobile/mutation-not-supported', rule as any, {
12+
valid: [],
13+
invalid: [
14+
{
15+
code: /* GraphQL */ `
16+
mutation AccountExample {
17+
uiapi {
18+
AccountCreate(input: { Account: { Name: "Trailblazer Express" } }) {
19+
Record {
20+
Id
21+
Name {
22+
value
23+
}
24+
}
25+
}
26+
}
27+
}
28+
`,
29+
errors: [{ messageId: MESSAGE_ID }]
30+
}
31+
]
32+
});

test/rules/utils.spec.ts

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { describe } from 'node:test';
2+
import { getLocation } from '../../src/rules/utils';
3+
import { Position } from 'estree';
4+
5+
import { expect } from '@jest/globals';
6+
7+
describe('utils', () => {
8+
it('getLocation() should return location with field name length added', () => {
9+
const startLocation: Position = {
10+
line: 1,
11+
column: 1
12+
};
13+
14+
const result = getLocation(startLocation, 'Field1');
15+
expect(result.end).toMatchObject({
16+
line: 1,
17+
column: 7
18+
});
19+
});
20+
});

tsconfig.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
"target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
55
"module": "Node16" /* Specify what module code is generated. */,
66
"moduleResolution": "Node16" /* Specify how TypeScript looks up a file from a given module specifier. */,
7-
"types": [] /* Specify type package names to be included without being referenced in a source file. */,
7+
"types": [
8+
"jest"
9+
] /* Specify type package names to be included without being referenced in a source file. */,
810
"rootDir": "./src",
911
"outDir": "dist" /* Specify an output folder for all emitted files. */,
1012
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,

0 commit comments

Comments
 (0)