@hubspot/eslint-config-ui-extensions 0.12.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +168 -24
- package/dist/__tests__/index.spec.d.ts +1 -0
- package/dist/__tests__/index.spec.js +303 -0
- package/dist/__tests__/rules/no-browser-dialogs.spec.d.ts +1 -0
- package/dist/__tests__/rules/no-browser-dialogs.spec.js +263 -0
- package/dist/__tests__/rules/no-browser-storage.spec.d.ts +1 -0
- package/dist/__tests__/rules/no-browser-storage.spec.js +428 -0
- package/dist/__tests__/rules/no-console.spec.d.ts +1 -0
- package/dist/__tests__/rules/no-console.spec.js +262 -0
- package/dist/__tests__/rules/no-dom-access.spec.d.ts +1 -0
- package/dist/__tests__/rules/no-dom-access.spec.js +288 -0
- package/dist/__tests__/rules/no-experimental-imports.spec.d.ts +1 -0
- package/dist/__tests__/rules/no-experimental-imports.spec.js +67 -0
- package/dist/__tests__/rules/no-html-elements.spec.d.ts +1 -0
- package/dist/__tests__/rules/no-html-elements.spec.js +142 -0
- package/dist/__tests__/rules/no-invalid-extension-point-imports.spec.d.ts +1 -0
- package/dist/__tests__/rules/no-invalid-extension-point-imports.spec.js +269 -0
- package/dist/__tests__/rules/no-invalid-image-src.spec.d.ts +1 -0
- package/dist/__tests__/rules/no-invalid-image-src.spec.js +227 -0
- package/dist/__tests__/rules/no-native-http.spec.d.ts +1 -0
- package/dist/__tests__/rules/no-native-http.spec.js +596 -0
- package/dist/__tests__/rules/no-parent-imports.spec.d.ts +1 -0
- package/dist/__tests__/rules/no-parent-imports.spec.js +337 -0
- package/dist/__tests__/rules/no-restricted-globals.spec.d.ts +1 -0
- package/dist/__tests__/rules/no-restricted-globals.spec.js +434 -0
- package/dist/__tests__/utils/ast-utils.spec.d.ts +1 -0
- package/dist/__tests__/utils/ast-utils.spec.js +297 -0
- package/dist/__tests__/utils/extension-point-utils.spec.d.ts +1 -0
- package/dist/__tests__/utils/extension-point-utils.spec.js +125 -0
- package/dist/__tests__/utils/import-utils.spec.d.ts +1 -0
- package/dist/__tests__/utils/import-utils.spec.js +176 -0
- package/dist/__tests__/utils/jsx-utils.spec.d.ts +1 -0
- package/dist/__tests__/utils/jsx-utils.spec.js +120 -0
- package/dist/__tests__/utils/scope-utils.spec.d.ts +1 -0
- package/dist/__tests__/utils/scope-utils.spec.js +80 -0
- package/dist/constants.d.ts +3 -0
- package/dist/constants.js +124 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +75 -0
- package/dist/rules/no-browser-dialogs.d.ts +2 -0
- package/dist/rules/no-browser-dialogs.js +62 -0
- package/dist/rules/no-browser-storage.d.ts +2 -0
- package/dist/rules/no-browser-storage.js +78 -0
- package/dist/rules/no-console.d.ts +2 -0
- package/dist/rules/no-console.js +78 -0
- package/dist/rules/no-dom-access.d.ts +2 -0
- package/dist/rules/no-dom-access.js +63 -0
- package/dist/rules/no-experimental-imports.d.ts +2 -0
- package/dist/rules/no-experimental-imports.js +29 -0
- package/dist/rules/no-html-elements.d.ts +2 -0
- package/dist/rules/no-html-elements.js +39 -0
- package/dist/rules/no-invalid-extension-point-imports.d.ts +2 -0
- package/dist/rules/no-invalid-extension-point-imports.js +54 -0
- package/dist/rules/no-invalid-image-src.d.ts +2 -0
- package/dist/rules/no-invalid-image-src.js +120 -0
- package/dist/rules/no-native-http.d.ts +2 -0
- package/dist/rules/no-native-http.js +70 -0
- package/dist/rules/no-parent-imports.d.ts +2 -0
- package/dist/rules/no-parent-imports.js +46 -0
- package/dist/rules/no-restricted-globals.d.ts +2 -0
- package/dist/rules/no-restricted-globals.js +132 -0
- package/dist/runtime.d.ts +2 -0
- package/dist/runtime.js +13 -0
- package/dist/utils/ast-utils.d.ts +42 -0
- package/dist/utils/ast-utils.js +79 -0
- package/dist/utils/extension-point-utils.d.ts +57 -0
- package/dist/utils/extension-point-utils.js +108 -0
- package/dist/utils/import-utils.d.ts +34 -0
- package/dist/utils/import-utils.js +52 -0
- package/dist/utils/jsx-utils.d.ts +30 -0
- package/dist/utils/jsx-utils.js +40 -0
- package/dist/utils/scope-utils.d.ts +30 -0
- package/dist/utils/scope-utils.js +66 -0
- package/package.json +43 -24
- package/CHANGELOG.md +0 -51
- package/eslintrc.js +0 -12
- package/lib/index.js +0 -57
- package/lib/runtime.js +0 -17
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { RuleTester } from 'eslint';
|
|
2
|
+
import tseslint from 'typescript-eslint';
|
|
3
|
+
import { noConsole } from "../../rules/no-console.js";
|
|
4
|
+
import { languageOptions } from "../../runtime.js";
|
|
5
|
+
const ruleTester = new RuleTester({
|
|
6
|
+
languageOptions: {
|
|
7
|
+
...languageOptions,
|
|
8
|
+
parserOptions: {
|
|
9
|
+
ecmaVersion: 'latest',
|
|
10
|
+
sourceType: 'module',
|
|
11
|
+
ecmaFeatures: {
|
|
12
|
+
jsx: true,
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
// Separate tester for TypeScript code
|
|
18
|
+
const tsRuleTester = new RuleTester({
|
|
19
|
+
languageOptions: {
|
|
20
|
+
...languageOptions,
|
|
21
|
+
parser: tseslint.parser,
|
|
22
|
+
parserOptions: {
|
|
23
|
+
ecmaVersion: 'latest',
|
|
24
|
+
sourceType: 'module',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
describe('no-console', () => {
|
|
29
|
+
// Main test suite with JavaScript code
|
|
30
|
+
ruleTester.run('no-console', noConsole, {
|
|
31
|
+
valid: [
|
|
32
|
+
{
|
|
33
|
+
name: 'allows console methods on other objects',
|
|
34
|
+
code: `
|
|
35
|
+
const myConsole = { log: () => {}, error: () => {} };
|
|
36
|
+
myConsole.log('msg');
|
|
37
|
+
myConsole.error('error');
|
|
38
|
+
`,
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'allows locally shadowed console object',
|
|
42
|
+
code: `
|
|
43
|
+
const console = { log: () => {}, error: () => {} };
|
|
44
|
+
console.log('test');
|
|
45
|
+
console.error('error');
|
|
46
|
+
`,
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: 'allows SDK logger methods (recommended alternative)',
|
|
50
|
+
code: `
|
|
51
|
+
logger.debug('debug message');
|
|
52
|
+
logger.info('info message');
|
|
53
|
+
logger.warn('warning message');
|
|
54
|
+
logger.error('error message');
|
|
55
|
+
`,
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
invalid: [
|
|
59
|
+
{
|
|
60
|
+
name: 'reports errors for global console.log',
|
|
61
|
+
code: `console.log('debug message');`,
|
|
62
|
+
errors: [{ messageId: 'noConsole' }],
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'reports errors for global console.info',
|
|
66
|
+
code: `console.info('info message');`,
|
|
67
|
+
errors: [{ messageId: 'noConsole' }],
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: 'reports errors for global console.warn',
|
|
71
|
+
code: `console.warn('warning message');`,
|
|
72
|
+
errors: [{ messageId: 'noConsole' }],
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: 'reports errors for global console.debug',
|
|
76
|
+
code: `console.debug('debug message');`,
|
|
77
|
+
errors: [{ messageId: 'noConsole' }],
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: 'reports errors for global console.error',
|
|
81
|
+
code: `console.error('error message');`,
|
|
82
|
+
errors: [{ messageId: 'noConsole' }],
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: 'reports errors for all console methods in one file',
|
|
86
|
+
code: `
|
|
87
|
+
console.log('log');
|
|
88
|
+
console.info('info');
|
|
89
|
+
console.warn('warn');
|
|
90
|
+
console.debug('debug');
|
|
91
|
+
console.error('error');
|
|
92
|
+
`,
|
|
93
|
+
errors: [
|
|
94
|
+
{ messageId: 'noConsole' },
|
|
95
|
+
{ messageId: 'noConsole' },
|
|
96
|
+
{ messageId: 'noConsole' },
|
|
97
|
+
{ messageId: 'noConsole' },
|
|
98
|
+
{ messageId: 'noConsole' },
|
|
99
|
+
],
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: 'reports errors for window.console calls',
|
|
103
|
+
code: `
|
|
104
|
+
window.console.log('log');
|
|
105
|
+
window.console.info('info');
|
|
106
|
+
window.console.warn('warn');
|
|
107
|
+
window.console.debug('debug');
|
|
108
|
+
window.console.error('error');
|
|
109
|
+
`,
|
|
110
|
+
errors: [
|
|
111
|
+
{ messageId: 'noConsole' },
|
|
112
|
+
{ messageId: 'noConsole' },
|
|
113
|
+
{ messageId: 'noConsole' },
|
|
114
|
+
{ messageId: 'noConsole' },
|
|
115
|
+
{ messageId: 'noConsole' },
|
|
116
|
+
],
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
name: 'reports errors for globalThis.console calls',
|
|
120
|
+
code: `
|
|
121
|
+
globalThis.console.log('log');
|
|
122
|
+
globalThis.console.info('info');
|
|
123
|
+
globalThis.console.warn('warn');
|
|
124
|
+
globalThis.console.debug('debug');
|
|
125
|
+
globalThis.console.error('error');
|
|
126
|
+
`,
|
|
127
|
+
errors: [
|
|
128
|
+
{ messageId: 'noConsole' },
|
|
129
|
+
{ messageId: 'noConsole' },
|
|
130
|
+
{ messageId: 'noConsole' },
|
|
131
|
+
{ messageId: 'noConsole' },
|
|
132
|
+
{ messageId: 'noConsole' },
|
|
133
|
+
],
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: 'reports errors for self.console calls',
|
|
137
|
+
code: `
|
|
138
|
+
self.console.log('log');
|
|
139
|
+
self.console.info('info');
|
|
140
|
+
self.console.warn('warn');
|
|
141
|
+
self.console.debug('debug');
|
|
142
|
+
self.console.error('error');
|
|
143
|
+
`,
|
|
144
|
+
errors: [
|
|
145
|
+
{ messageId: 'noConsole' },
|
|
146
|
+
{ messageId: 'noConsole' },
|
|
147
|
+
{ messageId: 'noConsole' },
|
|
148
|
+
{ messageId: 'noConsole' },
|
|
149
|
+
{ messageId: 'noConsole' },
|
|
150
|
+
],
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
name: 'reports errors for bracket notation console calls',
|
|
154
|
+
code: `
|
|
155
|
+
console['log']('log');
|
|
156
|
+
console['info']('info');
|
|
157
|
+
window.console['warn']('warn');
|
|
158
|
+
globalThis.console['debug']('debug');
|
|
159
|
+
self.console['error']('error');
|
|
160
|
+
`,
|
|
161
|
+
errors: [
|
|
162
|
+
{ messageId: 'noConsole' },
|
|
163
|
+
{ messageId: 'noConsole' },
|
|
164
|
+
{ messageId: 'noConsole' },
|
|
165
|
+
{ messageId: 'noConsole' },
|
|
166
|
+
{ messageId: 'noConsole' },
|
|
167
|
+
],
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
name: 'reports error in React component with JSX',
|
|
171
|
+
code: `
|
|
172
|
+
import { Text } from '@hubspot/ui-extensions';
|
|
173
|
+
function Component() {
|
|
174
|
+
console.log('Debug info');
|
|
175
|
+
console.error('Error occurred');
|
|
176
|
+
return <Text>Hello</Text>;
|
|
177
|
+
}
|
|
178
|
+
`,
|
|
179
|
+
errors: [{ messageId: 'noConsole' }, { messageId: 'noConsole' }],
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
name: 'reports error in async, arrow, and class method contexts',
|
|
183
|
+
code: `
|
|
184
|
+
async function test1() { console.log('async'); }
|
|
185
|
+
const test2 = () => console.warn('arrow');
|
|
186
|
+
class Test { method() { console.error('class'); } }
|
|
187
|
+
`,
|
|
188
|
+
errors: [
|
|
189
|
+
{ messageId: 'noConsole' },
|
|
190
|
+
{ messageId: 'noConsole' },
|
|
191
|
+
{ messageId: 'noConsole' },
|
|
192
|
+
],
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
name: 'reports error in try/catch and nested scopes',
|
|
196
|
+
code: `
|
|
197
|
+
try { console.error('error'); } catch (e) { console.warn('caught'); }
|
|
198
|
+
function outer() { function inner() { console.debug('nested'); } }
|
|
199
|
+
`,
|
|
200
|
+
errors: [
|
|
201
|
+
{ messageId: 'noConsole' },
|
|
202
|
+
{ messageId: 'noConsole' },
|
|
203
|
+
{ messageId: 'noConsole' },
|
|
204
|
+
],
|
|
205
|
+
},
|
|
206
|
+
],
|
|
207
|
+
});
|
|
208
|
+
// TypeScript-specific tests (requires TypeScript parser)
|
|
209
|
+
tsRuleTester.run('no-console (TypeScript)', noConsole, {
|
|
210
|
+
valid: [
|
|
211
|
+
{
|
|
212
|
+
name: 'allows console types in type alias',
|
|
213
|
+
code: `
|
|
214
|
+
type LogType = typeof console.log;
|
|
215
|
+
type InfoType = typeof console.info;
|
|
216
|
+
type WarnType = typeof console.warn;
|
|
217
|
+
type DebugType = typeof console.debug;
|
|
218
|
+
type ErrorType = typeof console.error;
|
|
219
|
+
`,
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
name: 'allows console types in interface property',
|
|
223
|
+
code: `
|
|
224
|
+
interface Logger {
|
|
225
|
+
log: typeof console.log;
|
|
226
|
+
info: typeof console.info;
|
|
227
|
+
warn: typeof console.warn;
|
|
228
|
+
debug: typeof console.debug;
|
|
229
|
+
error: typeof console.error;
|
|
230
|
+
}
|
|
231
|
+
`,
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
name: 'allows console types in function return type',
|
|
235
|
+
code: `
|
|
236
|
+
function getLogFn(): typeof console.log {
|
|
237
|
+
return logger.debug;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function getErrorFn(): typeof console.error {
|
|
241
|
+
return logger.error;
|
|
242
|
+
}
|
|
243
|
+
`,
|
|
244
|
+
},
|
|
245
|
+
],
|
|
246
|
+
invalid: [
|
|
247
|
+
{
|
|
248
|
+
name: 'reports error for console calls even after type declarations',
|
|
249
|
+
code: `
|
|
250
|
+
type LogType = typeof console.log;
|
|
251
|
+
type ErrorType = typeof console.error;
|
|
252
|
+
|
|
253
|
+
function getData() {
|
|
254
|
+
console.log('Logging');
|
|
255
|
+
console.error('Error');
|
|
256
|
+
}
|
|
257
|
+
`,
|
|
258
|
+
errors: [{ messageId: 'noConsole' }, { messageId: 'noConsole' }],
|
|
259
|
+
},
|
|
260
|
+
],
|
|
261
|
+
});
|
|
262
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import { RuleTester } from 'eslint';
|
|
2
|
+
import tseslint from 'typescript-eslint';
|
|
3
|
+
import { noDomAccess } from "../../rules/no-dom-access.js";
|
|
4
|
+
import { languageOptions } from "../../runtime.js";
|
|
5
|
+
const ruleTester = new RuleTester({
|
|
6
|
+
languageOptions: {
|
|
7
|
+
...languageOptions,
|
|
8
|
+
parserOptions: {
|
|
9
|
+
ecmaVersion: 'latest',
|
|
10
|
+
sourceType: 'module',
|
|
11
|
+
ecmaFeatures: {
|
|
12
|
+
jsx: true,
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
// Separate tester for TypeScript code
|
|
18
|
+
const tsRuleTester = new RuleTester({
|
|
19
|
+
languageOptions: {
|
|
20
|
+
...languageOptions,
|
|
21
|
+
parser: tseslint.parser,
|
|
22
|
+
parserOptions: {
|
|
23
|
+
ecmaVersion: 'latest',
|
|
24
|
+
sourceType: 'module',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
describe('no-dom-access', () => {
|
|
29
|
+
// Main test suite with JavaScript code
|
|
30
|
+
ruleTester.run('no-dom-access', noDomAccess, {
|
|
31
|
+
valid: [
|
|
32
|
+
{
|
|
33
|
+
name: 'allows document as a local variable',
|
|
34
|
+
code: `
|
|
35
|
+
const document = { body: null };
|
|
36
|
+
document.querySelector('.test');
|
|
37
|
+
`,
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: 'allows document as a function parameter',
|
|
41
|
+
code: `
|
|
42
|
+
function parse(document) {
|
|
43
|
+
return document.body;
|
|
44
|
+
}
|
|
45
|
+
`,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: 'allows document as a destructured property',
|
|
49
|
+
code: `
|
|
50
|
+
const { document } = customParser;
|
|
51
|
+
document.parse();
|
|
52
|
+
`,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'allows document property on other objects',
|
|
56
|
+
code: `
|
|
57
|
+
const parser = { document: {} };
|
|
58
|
+
parser.document.parse();
|
|
59
|
+
`,
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: 'allows string "document" in code',
|
|
63
|
+
code: `
|
|
64
|
+
const selector = 'document.body';
|
|
65
|
+
// Check document for changes
|
|
66
|
+
`,
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: 'allows UI Extensions components',
|
|
70
|
+
code: `
|
|
71
|
+
import { Text, Button } from '@hubspot/ui-extensions';
|
|
72
|
+
function Component() {
|
|
73
|
+
return <Text>Hello</Text>;
|
|
74
|
+
}
|
|
75
|
+
`,
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
invalid: [
|
|
79
|
+
{
|
|
80
|
+
name: 'reports error for direct document identifier usage',
|
|
81
|
+
code: `
|
|
82
|
+
const element = document.querySelector('.test');
|
|
83
|
+
`,
|
|
84
|
+
errors: [
|
|
85
|
+
{
|
|
86
|
+
messageId: 'noDomAccess',
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: 'reports error for document method calls',
|
|
92
|
+
code: `
|
|
93
|
+
document.getElementById('test');
|
|
94
|
+
document.querySelector('.class');
|
|
95
|
+
document.querySelectorAll('div');
|
|
96
|
+
document.createElement('div');
|
|
97
|
+
`,
|
|
98
|
+
errors: [
|
|
99
|
+
{ messageId: 'noDomAccess' },
|
|
100
|
+
{ messageId: 'noDomAccess' },
|
|
101
|
+
{ messageId: 'noDomAccess' },
|
|
102
|
+
{ messageId: 'noDomAccess' },
|
|
103
|
+
],
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: 'reports error for document property access',
|
|
107
|
+
code: `
|
|
108
|
+
const body = document.body;
|
|
109
|
+
const head = document.head;
|
|
110
|
+
const cookie = document.cookie;
|
|
111
|
+
`,
|
|
112
|
+
errors: [
|
|
113
|
+
{ messageId: 'noDomAccess' },
|
|
114
|
+
{ messageId: 'noDomAccess' },
|
|
115
|
+
{ messageId: 'noDomAccess' },
|
|
116
|
+
],
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
name: 'reports error for document reference assignment',
|
|
120
|
+
code: `
|
|
121
|
+
const doc = document;
|
|
122
|
+
doc.querySelector('.test');
|
|
123
|
+
`,
|
|
124
|
+
errors: [
|
|
125
|
+
{
|
|
126
|
+
messageId: 'noDomAccess',
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
name: 'reports error for window.document access',
|
|
132
|
+
code: `
|
|
133
|
+
window.document.querySelector('.test');
|
|
134
|
+
window.document.body;
|
|
135
|
+
window['document'].getElementById('test');
|
|
136
|
+
`,
|
|
137
|
+
errors: [
|
|
138
|
+
{ messageId: 'noDomAccess' },
|
|
139
|
+
{ messageId: 'noDomAccess' },
|
|
140
|
+
{ messageId: 'noDomAccess' },
|
|
141
|
+
],
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
name: 'reports error for globalThis.document access',
|
|
145
|
+
code: `
|
|
146
|
+
globalThis.document.querySelector('.test');
|
|
147
|
+
globalThis['document'].body;
|
|
148
|
+
`,
|
|
149
|
+
errors: [{ messageId: 'noDomAccess' }, { messageId: 'noDomAccess' }],
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
name: 'reports error for self.document access',
|
|
153
|
+
code: `
|
|
154
|
+
self.document.querySelector('.test');
|
|
155
|
+
self['document'].getElementById('test');
|
|
156
|
+
`,
|
|
157
|
+
errors: [{ messageId: 'noDomAccess' }, { messageId: 'noDomAccess' }],
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
name: 'reports error in React component with JSX',
|
|
161
|
+
code: `
|
|
162
|
+
import { Text } from '@hubspot/ui-extensions';
|
|
163
|
+
function Component() {
|
|
164
|
+
const element = document.querySelector('.test');
|
|
165
|
+
return <Text>Hello</Text>;
|
|
166
|
+
}
|
|
167
|
+
`,
|
|
168
|
+
errors: [{ messageId: 'noDomAccess' }],
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
name: 'reports error in async, arrow, and class method contexts',
|
|
172
|
+
code: `
|
|
173
|
+
async function test1() { document.querySelector('.test'); }
|
|
174
|
+
const test2 = () => document.body;
|
|
175
|
+
class Test { method() { return document.head; } }
|
|
176
|
+
`,
|
|
177
|
+
errors: [
|
|
178
|
+
{ messageId: 'noDomAccess' },
|
|
179
|
+
{ messageId: 'noDomAccess' },
|
|
180
|
+
{ messageId: 'noDomAccess' },
|
|
181
|
+
],
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
name: 'reports error in try/catch and nested scopes',
|
|
185
|
+
code: `
|
|
186
|
+
try {
|
|
187
|
+
document.querySelector('.test');
|
|
188
|
+
} catch (e) {
|
|
189
|
+
console.log(e);
|
|
190
|
+
}
|
|
191
|
+
function outer() {
|
|
192
|
+
function inner() {
|
|
193
|
+
return document.body;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
`,
|
|
197
|
+
errors: [{ messageId: 'noDomAccess' }, { messageId: 'noDomAccess' }],
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
name: 'reports error for chained property access',
|
|
201
|
+
code: `
|
|
202
|
+
const title = document.body.firstChild;
|
|
203
|
+
`,
|
|
204
|
+
errors: [
|
|
205
|
+
{
|
|
206
|
+
messageId: 'noDomAccess',
|
|
207
|
+
},
|
|
208
|
+
],
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
name: 'reports error for document in conditional',
|
|
212
|
+
code: `
|
|
213
|
+
if (document.body) {
|
|
214
|
+
console.log('body exists');
|
|
215
|
+
}
|
|
216
|
+
`,
|
|
217
|
+
errors: [
|
|
218
|
+
{
|
|
219
|
+
messageId: 'noDomAccess',
|
|
220
|
+
},
|
|
221
|
+
],
|
|
222
|
+
},
|
|
223
|
+
],
|
|
224
|
+
});
|
|
225
|
+
// TypeScript-specific tests (requires TypeScript parser)
|
|
226
|
+
tsRuleTester.run('no-dom-access (TypeScript)', noDomAccess, {
|
|
227
|
+
valid: [
|
|
228
|
+
{
|
|
229
|
+
name: 'allows document type in type alias',
|
|
230
|
+
code: `
|
|
231
|
+
type DocumentType = typeof document;
|
|
232
|
+
`,
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
name: 'allows document type in interface property',
|
|
236
|
+
code: `
|
|
237
|
+
interface Parser {
|
|
238
|
+
doc: typeof document;
|
|
239
|
+
}
|
|
240
|
+
`,
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
name: 'allows document type in function return type',
|
|
244
|
+
code: `
|
|
245
|
+
function getDoc(): typeof document {
|
|
246
|
+
return {} as any;
|
|
247
|
+
}
|
|
248
|
+
`,
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
name: 'allows Document type references',
|
|
252
|
+
code: `
|
|
253
|
+
interface CustomElement extends Document {
|
|
254
|
+
customMethod(): void;
|
|
255
|
+
}
|
|
256
|
+
`,
|
|
257
|
+
},
|
|
258
|
+
],
|
|
259
|
+
invalid: [
|
|
260
|
+
{
|
|
261
|
+
name: 'reports error for document usage even after type declarations',
|
|
262
|
+
code: `
|
|
263
|
+
type DocumentType = typeof document;
|
|
264
|
+
|
|
265
|
+
function getData() {
|
|
266
|
+
const element = document.querySelector('.test');
|
|
267
|
+
}
|
|
268
|
+
`,
|
|
269
|
+
errors: [
|
|
270
|
+
{
|
|
271
|
+
messageId: 'noDomAccess',
|
|
272
|
+
},
|
|
273
|
+
],
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
name: 'reports error for document in TypeScript with explicit types',
|
|
277
|
+
code: `
|
|
278
|
+
const element: HTMLElement | null = document.querySelector('.test');
|
|
279
|
+
`,
|
|
280
|
+
errors: [
|
|
281
|
+
{
|
|
282
|
+
messageId: 'noDomAccess',
|
|
283
|
+
},
|
|
284
|
+
],
|
|
285
|
+
},
|
|
286
|
+
],
|
|
287
|
+
});
|
|
288
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { RuleTester } from 'eslint';
|
|
2
|
+
import { noExperimentalImports } from "../../rules/no-experimental-imports.js";
|
|
3
|
+
import { languageOptions } from "../../runtime.js";
|
|
4
|
+
const ruleTesterOptions = {
|
|
5
|
+
languageOptions: {
|
|
6
|
+
...languageOptions,
|
|
7
|
+
parserOptions: {
|
|
8
|
+
ecmaFeatures: {
|
|
9
|
+
jsx: true,
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
const experimentalImport = `
|
|
15
|
+
import { Grid } from '@hubspot/ui-extensions/experimental';
|
|
16
|
+
`;
|
|
17
|
+
const experimentalSubpathImport = `
|
|
18
|
+
import { getDistanceFromTshirtSize } from '@hubspot/ui-extensions/experimental/Layout/constants';
|
|
19
|
+
`;
|
|
20
|
+
const stableImport = `
|
|
21
|
+
import { Text, Button } from '@hubspot/ui-extensions';
|
|
22
|
+
`;
|
|
23
|
+
const otherPackageImport = `
|
|
24
|
+
import { useState } from 'react';
|
|
25
|
+
`;
|
|
26
|
+
describe('no-experimental-imports', () => {
|
|
27
|
+
const ruleTester = new RuleTester(ruleTesterOptions);
|
|
28
|
+
ruleTester.run('no-experimental-imports', noExperimentalImports, {
|
|
29
|
+
valid: [
|
|
30
|
+
{
|
|
31
|
+
name: 'stable @hubspot/ui-extensions import',
|
|
32
|
+
code: stableImport,
|
|
33
|
+
filename: '/path/to/project/src/app/cards/Component.tsx',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: 'other package import',
|
|
37
|
+
code: otherPackageImport,
|
|
38
|
+
filename: '/path/to/project/src/app/cards/Component.tsx',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'crm extension point import',
|
|
42
|
+
code: `import { useCrmProperties } from '@hubspot/ui-extensions/crm/hooks';`,
|
|
43
|
+
filename: '/path/to/project/src/app/cards/Component.tsx',
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
invalid: [
|
|
47
|
+
{
|
|
48
|
+
name: 'import from experimental path',
|
|
49
|
+
code: experimentalImport,
|
|
50
|
+
filename: '/path/to/project/src/app/cards/Component.tsx',
|
|
51
|
+
errors: [{ messageId: 'noExperimentalImports' }],
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: 'import from experimental subpath',
|
|
55
|
+
code: experimentalSubpathImport,
|
|
56
|
+
filename: '/path/to/project/src/app/extensions/Component.tsx',
|
|
57
|
+
errors: [{ messageId: 'noExperimentalImports' }],
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: 'default import from experimental',
|
|
61
|
+
code: `import ExperimentalComponent from '@hubspot/ui-extensions/experimental';`,
|
|
62
|
+
filename: '/path/to/project/src/app/settings/Component.tsx',
|
|
63
|
+
errors: [{ messageId: 'noExperimentalImports' }],
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
});
|
|
67
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|