Chore: Add eslint no-restricted-globals rule (#104519)

* Chore: Add no-restricted-globals eslint rule

* Fix eslint warnings

* Revert some changes

* Update

* Feedback
This commit is contained in:
Alex Khomenko
2025-05-12 12:38:26 +03:00
committed by GitHub
parent 8f17f607fa
commit a9b6d098e9
41 changed files with 74 additions and 41 deletions
+2
View File
@@ -1,5 +1,6 @@
// @ts-check
const emotionPlugin = require('@emotion/eslint-plugin');
const restrictedGlobals = require('confusing-browser-globals');
const importPlugin = require('eslint-plugin-import');
const jestPlugin = require('eslint-plugin-jest');
const jestDomPlugin = require('eslint-plugin-jest-dom');
@@ -136,6 +137,7 @@ module.exports = [
],
},
],
'no-restricted-globals': ['error'].concat(restrictedGlobals),
// Use typescript's no-redeclare for compatibility with overrides
'no-redeclare': 'off',
+2
View File
@@ -108,6 +108,7 @@
"@types/babel__preset-env": "^7",
"@types/chance": "^1.1.3",
"@types/common-tags": "^1.8.0",
"@types/confusing-browser-globals": "^1",
"@types/d3": "7.4.3",
"@types/d3-force": "^3.0.0",
"@types/d3-scale-chromatic": "3.1.0",
@@ -166,6 +167,7 @@
"chance": "^1.0.10",
"chrome-remote-interface": "0.33.2",
"codeowners": "^5.1.1",
"confusing-browser-globals": "^1.0.11",
"copy-webpack-plugin": "12.0.2",
"core-js": "3.40.0",
"crashme": "0.0.15",
@@ -34,10 +34,10 @@ export function PromQueryCodeEditorAutocompleteInfo(props: Readonly<Props>) {
);
useEffect(() => {
addEventListener(CODE_MODE_SUGGESTIONS_INCOMPLETE_EVENT, handleSuggestionsIncompleteEvent);
window.addEventListener(CODE_MODE_SUGGESTIONS_INCOMPLETE_EVENT, handleSuggestionsIncompleteEvent);
return () => {
removeEventListener(CODE_MODE_SUGGESTIONS_INCOMPLETE_EVENT, handleSuggestionsIncompleteEvent);
window.removeEventListener(CODE_MODE_SUGGESTIONS_INCOMPLETE_EVENT, handleSuggestionsIncompleteEvent);
};
}, [handleSuggestionsIncompleteEvent]);
@@ -159,10 +159,10 @@ export function TableNG(props: TableNGProps) {
setIsContextMenuOpen(false);
}
addEventListener('click', onClick);
window.addEventListener('click', onClick);
return () => {
removeEventListener('click', onClick);
window.removeEventListener('click', onClick);
};
}, [isContextMenuOpen]);
+1 -1
View File
@@ -122,7 +122,7 @@ export class GrafanaApp {
try {
await preInitTasks();
// Let iframe container know grafana has started loading
parent.postMessage('GrafanaAppInit', '*');
window.parent.postMessage('GrafanaAppInit', '*');
const initI18nPromise = initializeI18n(config.bootData.user.language);
initI18nPromise.then(({ language }) => updateConfig({ language }));
+2
View File
@@ -2,6 +2,8 @@ import { monacoLanguageRegistry } from '@grafana/data';
import { CorsWorker as Worker } from 'app/core/utils/CorsWorker';
export function setMonacoEnv() {
// Do not use window.self here, as it will not work in the worker context
// eslint-disable-next-line no-restricted-globals
self.MonacoEnvironment = {
getWorker(_moduleId, label) {
const language = monacoLanguageRegistry.getIfExists(label);
@@ -862,6 +862,7 @@ export class Mousetrap {
* correct key ends up getting bound (the last key in the pattern)
*/
bind = (keys: string | string[], callback: MousetrapCallback, action?: string) => {
let self = this;
keys = keys instanceof Array ? keys : [keys];
this._bindMultiple(keys, callback, action);
return self;
@@ -155,7 +155,7 @@ interface ExportMenuItemProps {
}
const ExportMenuItem = ({ identifier }: ExportMenuItemProps) => {
const returnTo = location.pathname + location.search;
const returnTo = window.location.pathname + window.location.search;
const url = createRelativeUrl(
`/alerting/${encodeURIComponent(ruleId.stringifyIdentifier(identifier))}/modify-export`,
{
@@ -73,7 +73,7 @@ export const ConfirmVersionRestoreModal = ({
const ruleFormUrl = urlUtil.renderUrl(`/alerting/${ruleIdentifier.uid}/edit`, {
isManualRestore: true,
defaults: JSON.stringify(payload),
returnTo: location.pathname + location.search,
returnTo: window.location.pathname + window.location.search,
});
navigate(ruleFormUrl);
@@ -96,7 +96,7 @@ export const GrafanaRules = ({ namespaces, expandAll }: Props) => {
{grafanaRecordingRulesEnabled && (
<LinkButton
href={createRelativeUrl('/alerting/new/grafana-recording', {
returnTo: '/alerting/list' + location.search,
returnTo: '/alerting/list' + window.location.search,
})}
icon="plus"
variant="secondary"
@@ -113,7 +113,11 @@ export function RuleDetailsMatchingInstances(props: Props) {
// createViewLink returns a link containing the app subpath prefix hence cannot be used
// in locationService.push as it will result in a double prefix
const ruleViewPageLink = createViewLink(namespace.rulesSource, props.rule, location.pathname + location.search);
const ruleViewPageLink = createViewLink(
namespace.rulesSource,
props.rule,
window.location.pathname + window.location.search
);
const statsComponents = getComponentsFromStats(instanceTotals);
const resetFilter = () => setAlertState(undefined);
@@ -160,7 +160,7 @@ const redirectToRestoreForm = async (ruleToRecover: RulerGrafanaRuleDTO) => {
const ruleFormUrl = createRelativeUrl(urlPath, {
isManualRestore: 'true',
defaults: JSON.stringify(formValues),
returnTo: location.pathname + location.search,
returnTo: window.location.pathname + window.location.search,
});
locationService.push(ruleFormUrl);
@@ -187,7 +187,7 @@ export function CreateAlertButton() {
}
function ExportNewRuleButton() {
const returnTo = location.pathname + location.search;
const returnTo = window.location.pathname + window.location.search;
const url = createRelativeUrl(`/alerting/export-new-rule`, {
returnTo,
});
+1 -1
View File
@@ -45,7 +45,7 @@ export class ScopedResourceClient<T = object, S = object, K = string> implements
fieldSelector: this.parseListOptionsSelector(params?.fieldSelector),
};
if (params?.name) {
requestParams.fieldSelector = `metadata.name=${name}`;
requestParams.fieldSelector = `metadata.name=${params.name}`;
}
// For now, watch over live only supports provisioning
@@ -13,8 +13,11 @@ function _debounce<T>(f: (...args: T[]) => void, timeout: number) {
};
}
// Do not use window.self here, as it will not work in the worker context
// eslint-disable-next-line no-restricted-globals
self.onmessage = _debounce((e: MessageEvent<{ initial: Dashboard; changed: Dashboard }>) => {
const result = detectDashboardChanges(e.data.initial, e.data.changed);
// eslint-disable-next-line no-restricted-globals
self.postMessage(result);
}, 500);
@@ -56,7 +56,7 @@ export class DashboardControls extends SceneObjectBase<DashboardControlsState> {
const isEnabledViaUrl = (key: string) => values[key] === 'true' || values[key] === '';
// Only allow hiding, never "unhiding" from url
// Becasue this should really only change on first init it's fine to do multiple setState here
// Because this should really only change on first init it's fine to do multiple setState here
if (!hideTimeControls && isEnabledViaUrl('_dash.hideTimePicker')) {
this.setState({ hideTimeControls: true });
@@ -122,7 +122,7 @@ function DashboardControlsRenderer({ model }: SceneComponentProps<DashboardContr
const dashboard = getDashboardSceneFor(model);
const { links, editPanel } = dashboard.useState();
const styles = useStyles2(getStyles);
const showDebugger = location.search.includes('scene-debugger');
const showDebugger = window.location.search.includes('scene-debugger');
if (!model.hasControls()) {
// To still have spacing when no controls are rendered
@@ -558,7 +558,7 @@ const onCreateAlert = async (panel: VizPanel) => {
const formValues = await scenesPanelToRuleFormValues(panel);
const ruleFormUrl = urlUtil.renderUrl('/alerting/new', {
defaults: JSON.stringify(formValues),
returnTo: location.pathname + location.search,
returnTo: window.location.pathname + window.location.search,
});
locationService.push(ruleFormUrl);
} catch (err) {
@@ -83,7 +83,7 @@ export class ShareLinkTab extends SceneObjectBase<ShareLinkTabState> implements
const imageUrl = getDashboardUrl({
uid: dashboard.state.uid,
currentQueryParams: location.search,
currentQueryParams: window.location.search,
updateQuery: { ...urlParamsUpdate, ...queryOptions, panelId: panel?.state.key },
absolute: true,
soloRoute: true,
@@ -183,7 +183,7 @@ export function getPanelMenu(
}
const ruleFormUrl = urlUtil.renderUrl('/alerting/new', {
defaults: JSON.stringify(formValues),
returnTo: location.pathname + location.search,
returnTo: window.location.pathname + window.location.search,
});
locationService.push(ruleFormUrl);
@@ -12,7 +12,7 @@ import { trackAddNewDsClicked } from '../tracking';
export function DataSourceAddButton(): JSX.Element | null {
const canCreateDataSource = contextSrv.hasPermission(AccessControlAction.DataSourcesCreate);
const handleClick = useCallback(() => {
trackAddNewDsClicked({ path: location.pathname });
trackAddNewDsClicked({ path: window.location.pathname });
}, []);
return canCreateDataSource ? (
@@ -26,7 +26,7 @@ export function DataSourceCategories({ categories, onClickDataSourceType }: Prop
const handleClick = useCallback(() => {
reportInteraction('connections_add_datasource_find_more_ds_plugins_clicked', {
targetPath: moreDataSourcesLink,
path: location.pathname,
path: window.location.pathname,
creator_team: 'grafana_plugins_catalog',
schema_version: '1.0.0',
});
@@ -13,7 +13,7 @@ export type Props = {
export function DataSourceLoadError({ dataSourceRights, onDelete }: Props) {
const { readOnly, hasDeleteRights } = dataSourceRights;
const canDelete = !readOnly && hasDeleteRights;
const navigateBack = () => history.back();
const navigateBack = () => window.history.back();
return (
<>
@@ -141,7 +141,7 @@ export function DataSourceTestingStatus({ testingStatus, exploreUrl, dataSource
grafana_version: config.buildInfo.version,
datasource_uid: dataSource.uid,
plugin_name: dataSource.typeName,
path: location.pathname,
path: window.location.pathname,
});
};
const styles = useStyles2(getTestingStatusStyles);
@@ -45,7 +45,7 @@ export function DataSourcesListCard({ dataSource, hasWriteRights, hasExploreRigh
grafana_version: config.buildInfo.version,
datasource_uid: dataSource.uid,
plugin_name: dataSource.typeName,
path: location.pathname,
path: window.location.pathname,
});
}}
>
@@ -65,7 +65,7 @@ export function DataSourcesListCard({ dataSource, hasWriteRights, hasExploreRigh
grafana_version: config.buildInfo.version,
datasource_uid: dataSource.uid,
plugin_name: dataSource.typeName,
path: location.pathname,
path: window.location.pathname,
});
}}
>
@@ -28,7 +28,7 @@ export function EditDataSourceActions({ uid }: Props) {
grafana_version: config.buildInfo.version,
datasource_uid: dataSource.uid,
plugin_name: dataSource.typeName,
path: location.pathname,
path: window.location.pathname,
});
}}
>
@@ -45,7 +45,7 @@ export function EditDataSourceActions({ uid }: Props) {
grafana_version: config.buildInfo.version,
datasource_uid: dataSource.uid,
plugin_name: dataSource.typeName,
path: location.pathname,
path: window.location.pathname,
});
}}
>
@@ -394,7 +394,7 @@ describe('addDataSource', () => {
plugin_version: '1.2.3',
datasource_uid: 'azure23',
grafana_version: '1.0',
path: location.pathname,
path: window.location.pathname,
});
});
});
@@ -257,7 +257,7 @@ export function addDataSource(
plugin_id: plugin.id,
datasource_uid: result.datasource.uid,
plugin_version: result.meta?.info?.version,
path: location.pathname,
path: window.location.pathname,
});
locationService.push(editLink);
@@ -69,7 +69,7 @@ export function UnconnectedNodeGraphContainer(props: Props) {
reportInteraction('grafana_traces_node_graph_panel_clicked', {
datasourceType: datasourceType,
grafana_version: config.buildInfo.version,
isExpanded: !open,
isExpanded: !collapsed,
});
};
@@ -107,13 +107,13 @@ function OpenLinkButton(props: LinkButtonProps) {
const { urlLink, label, urlLinkOnDone, labelOnDone, done } = props;
const urlToGoWhenNotDone = urlLink?.url
? createRelativeUrl(urlLink.url, {
returnTo: location.pathname + location.search,
returnTo: window.location.pathname + window.location.search,
...urlLink.queryParams,
})
: '';
const urlToGoWhenDone = urlLinkOnDone?.url
? createRelativeUrl(urlLinkOnDone.url, {
returnTo: location.pathname + location.search,
returnTo: window.location.pathname + window.location.search,
...urlLinkOnDone.queryParams,
})
: '';
@@ -121,7 +121,7 @@ export function useGetEssentialsConfiguration(): EssentialsConfigurationData {
function onIntegrationClick(integrationId: string, url: RelativeUrl) {
const urlToGoWithIntegration = createRelativeUrl(`${url} + ${integrationId}`, {
returnTo: location.pathname + location.search,
returnTo: window.location.pathname + window.location.search,
});
locationService.push(urlToGoWithIntegration);
}
@@ -30,7 +30,7 @@ export function GetStartedWithApp({ plugin }: Props): React.ReactElement | null
const enable = () => {
reportInteraction('plugins_detail_enable_clicked', {
path: location.pathname,
path: window.location.pathname,
plugin_id: plugin.id,
creator_team: 'grafana_plugins_catalog',
schema_version: '1.0.0',
@@ -44,7 +44,7 @@ export function GetStartedWithApp({ plugin }: Props): React.ReactElement | null
const disable = () => {
reportInteraction('plugins_detail_disable_clicked', {
path: location.pathname,
path: window.location.pathname,
plugin_id: plugin.id,
creator_team: 'grafana_plugins_catalog',
schema_version: '1.0.0',
@@ -14,7 +14,7 @@ const UpdateAllButton = ({ disabled, onUpdateAll, updatablePluginsLength }: Upda
<Trans i18nKey="plugins.catalog.no-updates-available">No updates available</Trans>
) : (
<Trans i18nKey="plugins.catalog.update-all.button" values={{ length: updatablePluginsLength }}>
Update all ({{ length }})
Update all ({'{{length}}'})
</Trans>
)}
</Button>
@@ -33,7 +33,7 @@ export const UpdateAllModal = ({ isOpen, onDismiss, isLoading, plugins }: Props)
const pluginsSet = useMemo(() => new Set(plugins.map((plugin) => plugin.id)), [plugins]);
const installsRemaining = plugins.length;
// Since the plugins comes from the store and changes every time we update a plugin,
// Since the plugins come from the store and changes every time we update a plugin,
// we need to keep track of the initial plugins.
useEffect(() => {
if (initialPluginsRef.current.length === 0) {
@@ -88,7 +88,7 @@ export const UpdateAllModal = ({ isOpen, onDismiss, isLoading, plugins }: Props)
const onConfirm = async () => {
if (!inProgress) {
reportInteraction(PLUGINS_UPDATE_ALL_INTERACTION_EVENT_NAME, {
path: location.pathname,
path: window.location.pathname,
count: selectedPlugins?.size,
creator_team: 'grafana_plugins_catalog',
schema_version: '1.0.0',
@@ -59,7 +59,7 @@ export const VersionInstallButton = ({
const performInstallation = () => {
const trackProps = {
path: location.pathname,
path: window.location.pathname,
plugin_id: pluginId,
version: version.version,
is_latest: latestCompatibleVersion === version.version,
@@ -41,7 +41,8 @@ export async function isPluginFrontendSandboxEligible({ pluginId }: SandboxEligi
}
// To fast-test and debug the sandbox in the browser (dev mode only).
const sandboxDisableQueryParam = location.search.includes('nosandbox') && config.buildInfo.env === 'development';
const sandboxDisableQueryParam =
window.location.search.includes('nosandbox') && config.buildInfo.env === 'development';
if (sandboxDisableQueryParam) {
return false;
}
@@ -14,7 +14,7 @@ export function getGrafanaSearcher(): GrafanaSearcher {
const useBluge = config.featureToggles.panelTitleSearch;
searcher = useBluge ? new BlugeSearcher(sqlSearcher) : sqlSearcher;
if (useBluge && location.search.includes('do-frontend-query')) {
if (useBluge && window.location.search.includes('do-frontend-query')) {
return new FrontendSearcher(searcher);
}
@@ -124,7 +124,7 @@ const getCellContent = (
return columnName === 'avatarUrl' ? <Skeleton circle width={24} height={24} /> : <Skeleton width={100} />;
}
const href = `/org/serviceaccounts/${original.uid}`;
const ariaLabel = `Edit service account's ${name} details`;
const ariaLabel = `Edit service account's ${original.name} details`;
switch (columnName) {
case 'avatarUrl':
return (
@@ -1,6 +1,7 @@
import { layout } from './layeredLayout';
// Separate from main implementation so it does not trip out tests
// eslint-disable-next-line no-restricted-globals
addEventListener('message', async (event) => {
const { nodes, edges, config } = event.data;
const [newNodes, newEdges] = layout(nodes, edges, config);
@@ -1,6 +1,7 @@
import { layout } from './forceLayout';
// Separate from main implementation so it does not trip out tests
// eslint-disable-next-line no-restricted-globals
addEventListener('message', (event) => {
const { nodes, edges, config } = event.data;
layout(nodes, edges, config);
+1 -1
View File
@@ -98,7 +98,7 @@ export const Page = () => {
} else {
url.searchParams.delete('api');
}
history.pushState(null, '', url);
window.history.pushState(null, '', url);
setURL(v);
}}
value={url}
+16
View File
@@ -8758,6 +8758,13 @@ __metadata:
languageName: node
linkType: hard
"@types/confusing-browser-globals@npm:^1":
version: 1.0.3
resolution: "@types/confusing-browser-globals@npm:1.0.3"
checksum: 10/596d9ea69fb2b6e5be9b37560e86593a0992e8554850de1a790aea66407c9453b78bc86909da5fd9f4fede99d7652c2b0dfeb9f89ef5d8eca35fadaca3271707
languageName: node
linkType: hard
"@types/connect-history-api-fallback@npm:^1.5.4":
version: 1.5.4
resolution: "@types/connect-history-api-fallback@npm:1.5.4"
@@ -13103,6 +13110,13 @@ __metadata:
languageName: node
linkType: hard
"confusing-browser-globals@npm:^1.0.11":
version: 1.0.11
resolution: "confusing-browser-globals@npm:1.0.11"
checksum: 10/3afc635abd37e566477f610e7978b15753f0e84025c25d49236f1f14d480117185516bdd40d2a2167e6bed8048641a9854964b9c067e3dcdfa6b5d0ad3c3a5ef
languageName: node
linkType: hard
"connect-history-api-fallback@npm:^2.0.0":
version: 2.0.0
resolution: "connect-history-api-fallback@npm:2.0.0"
@@ -17735,6 +17749,7 @@ __metadata:
"@types/babel__preset-env": "npm:^7"
"@types/chance": "npm:^1.1.3"
"@types/common-tags": "npm:^1.8.0"
"@types/confusing-browser-globals": "npm:^1"
"@types/d3": "npm:7.4.3"
"@types/d3-force": "npm:^3.0.0"
"@types/d3-scale-chromatic": "npm:3.1.0"
@@ -17807,6 +17822,7 @@ __metadata:
combokeys: "npm:^3.0.0"
comlink: "npm:4.4.2"
common-tags: "npm:1.8.2"
confusing-browser-globals: "npm:^1.0.11"
copy-webpack-plugin: "npm:12.0.2"
core-js: "npm:3.40.0"
crashme: "npm:0.0.15"