chore(vue): guard browser side effects
This commit is contained in:
parent
4e942f77d7
commit
8476ab2c99
|
|
@ -388,6 +388,66 @@ const noGeneratedTestIdLiterals = {
|
|||
}
|
||||
}
|
||||
|
||||
const noBrowserSideEffectsInVue = {
|
||||
meta: {
|
||||
docs: {
|
||||
description:
|
||||
'Disallow direct browser side effects in Vue components — use VueUse, refs, or app services'
|
||||
}
|
||||
},
|
||||
create(context) {
|
||||
const file = normalizedFilename(context)
|
||||
if (!file.endsWith('.vue')) return {}
|
||||
|
||||
function propertyName(property) {
|
||||
if (property?.type === 'Identifier') return property.name
|
||||
if (property?.type === 'Literal' && typeof property.value === 'string') return property.value
|
||||
return null
|
||||
}
|
||||
|
||||
function objectName(object) {
|
||||
return object?.type === 'Identifier' ? object.name : null
|
||||
}
|
||||
|
||||
return {
|
||||
CallExpression(node) {
|
||||
const callee = node.callee
|
||||
if (callee?.type !== 'MemberExpression') return
|
||||
const object = objectName(callee.object)
|
||||
const property = propertyName(callee.property)
|
||||
|
||||
if (
|
||||
(object === 'window' || object === 'document') &&
|
||||
(property === 'addEventListener' || property === 'removeEventListener')
|
||||
) {
|
||||
context.report({
|
||||
node,
|
||||
message: 'Use VueUse useEventListener() instead of direct browser event listeners in Vue components.'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (object === 'document' && property === 'createElement') {
|
||||
context.report({
|
||||
node,
|
||||
message:
|
||||
'Do not create DOM elements directly in Vue components. Use template refs, components, or an app service.'
|
||||
})
|
||||
}
|
||||
},
|
||||
MemberExpression(node) {
|
||||
const object = objectName(node.object)
|
||||
if (object !== 'localStorage' && object !== 'sessionStorage') return
|
||||
context.report({
|
||||
node,
|
||||
message:
|
||||
'Do not access localStorage/sessionStorage directly in Vue components. Use VueUse storage helpers or an app service.'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const noDocumentQuerySelectorInVue = {
|
||||
meta: {
|
||||
docs: {
|
||||
|
|
@ -1479,6 +1539,7 @@ const plugin = {
|
|||
'no-invalid-test-id-attributes': noInvalidTestIdAttributes,
|
||||
'no-raw-test-id-selectors-in-tests': noRawTestIdSelectorsInTests,
|
||||
'no-generated-test-id-literals': noGeneratedTestIdLiterals,
|
||||
'no-browser-side-effects-in-vue': noBrowserSideEffectsInVue,
|
||||
'no-document-query-selector-in-vue': noDocumentQuerySelectorInVue,
|
||||
'no-direct-selection-tool-state-mutation': noDirectSelectionToolStateMutation,
|
||||
'no-math-random': noMathRandom,
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@
|
|||
"open-pencil/no-invalid-test-id-attributes": "error",
|
||||
"open-pencil/no-raw-test-id-selectors-in-tests": "error",
|
||||
"open-pencil/no-generated-test-id-literals": "error",
|
||||
"open-pencil/no-browser-side-effects-in-vue": "error",
|
||||
"open-pencil/no-document-query-selector-in-vue": "error",
|
||||
"open-pencil/no-direct-selection-tool-state-mutation": "error",
|
||||
"open-pencil/no-math-random": "error",
|
||||
|
|
|
|||
Loading…
Reference in a new issue