2026-07-07 19:34:03 +02:00
/*
Copyright 2026 Element Creations Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE in the repository root for full details.
*/
import { ESLintUtils } from "@typescript-eslint/utils" ;
/**
* Node types that introduce a new non-module scope. A getChild() call nested
* inside any of these is considered "not at the top level".
*/
const FUNCTION_OR_CLASS_TYPES = new Set ([
"FunctionDeclaration" ,
"FunctionExpression" ,
"ArrowFunctionExpression" ,
"ClassBody" ,
]);
const rule = ESLintUtils . RuleCreator (
() => "https://github.com/element-hq/element-call" ,
)({
name : "no-top-level-logger-get-child" ,
meta : {
type : "problem" ,
docs : {
description :
2026-07-07 19:52:50 +02:00
"Disallow calling logger.getChild() at the top level of a module." +
"`getChild` has to be called after the rageshake logger `init()`." +
"If it is called at the top level the child logger will never be setup for rageshakes." ,
2026-07-07 19:34:03 +02:00
},
messages : {
noTopLevelGetChild :
"Do not call logger.getChild() at the top level of a module; move it inside a function or class instead that gets called after rageshake logger `init()` is called." ,
},
schema : [],
},
create ( context ) {
// Tracks the local binding names that refer to the logger imported from
// 'matrix-js-sdk/lib/logger', e.g. both `logger` and `rootLogger` in:
// import { logger } from "matrix-js-sdk/lib/logger";
// import { logger as rootLogger } from "matrix-js-sdk/lib/logger";
const loggerNames = new Set ();
return {
ImportDeclaration ( node ) {
if ( node . source . value !== "matrix-js-sdk/lib/logger" ) return ;
for ( const specifier of node . specifiers ) {
if (
specifier . type === "ImportSpecifier" &&
specifier . imported . name === "logger"
) {
loggerNames . add ( specifier . local . name );
}
}
},
CallExpression ( node ) {
// Must be a non-computed member expression call: something.getChild(...)
if (
node . callee . type !== "MemberExpression" ||
node . callee . computed ||
node . callee . property . type !== "Identifier" ||
node . callee . property . name !== "getChild"
)
return ;
// The receiver must be one of the tracked logger names.
const object = node . callee . object ;
if ( object . type !== "Identifier" || ! loggerNames . has ( object . name ))
return ;
// Flag the call only when it is at module top level — i.e. there is no
// enclosing function or class body anywhere in the ancestor chain.
const ancestors = context . sourceCode . getAncestors ( node );
const isTopLevel = ! ancestors . some (( a ) =>
FUNCTION_OR_CLASS_TYPES . has ( a . type ),
);
if ( isTopLevel ) {
context . report ({
messageId : "noTopLevelGetChild" ,
node ,
});
}
},
};
},
});
export default rule ;