@@ -107,6 +107,37 @@ const GLOBAL_RESTRICTED_REQUIRES = [
107107 } ,
108108]
109109
110+ const SRC_RESTRICTED_SYNTAX = [
111+ {
112+ // Inline `.evaluate(<fn>)` callbacks (Playwright/Puppeteer) are serialized with
113+ // `toString()` and run in chromium — coverage counters inside would ReferenceError.
114+ selector :
115+ "CallExpression[callee.property.name='evaluate']" +
116+ ":matches([arguments.0.type='ArrowFunctionExpression'], [arguments.0.type='FunctionExpression'])" ,
117+ message :
118+ 'Move the inline `.evaluate(...)` callback into a `*-browser-scripts.js` file ' +
119+ '(NYC-excluded in nyc.config.js) and import it here.' ,
120+ } ,
121+ {
122+ // Static-analysis bundlers (esbuild, webpack, rollup) only see literals as require
123+ // arguments; once any transform (e.g. NYC) wraps them, this shape breaks bundling.
124+ selector : "CallExpression[callee.name='require'][arguments.0.type='ConditionalExpression']" ,
125+ message : 'Use `cond ? require(\'a\') : require(\'b\')` instead of `require(cond ? \'a\' : \'b\')`.' ,
126+ } ,
127+ ]
128+
129+ // Matches only probe positions; a genuine count (`writeMapPrefix(Object.keys(x).length)`) must stay allowed.
130+ const OBJECT_KEYS_LENGTH_PROBE = {
131+ selector :
132+ ':matches(BinaryExpression[right.value=0], BinaryExpression[left.value=0], UnaryExpression[operator="!"],' +
133+ ' IfStatement, ConditionalExpression, LogicalExpression, WhileStatement, DoWhileStatement)' +
134+ " > MemberExpression[property.name='length']" +
135+ " > CallExpression[callee.object.name='Object'][callee.property.name='keys']" ,
136+ message : 'Do not probe emptiness with `Object.keys(obj).length`; the keys array is allocated on every call. ' +
137+ 'Track presence with a boolean at the assignment site, probe a known key (`obj.field !== undefined`), or ' +
138+ 'return `undefined` when there is nothing to report instead of an empty object.' ,
139+ }
140+
110141export default [
111142 {
112143 name : 'dd-trace/global-ignore' ,
@@ -643,21 +674,7 @@ export default [
643674 'eslint-rules/eslint-prefer-set-service-name' : 'error' ,
644675 'eslint-rules/eslint-timer-unref' : 'error' ,
645676
646- 'no-restricted-syntax' : [ 'error' , {
647- // Inline `.evaluate(<fn>)` callbacks (Playwright/Puppeteer) are serialized with
648- // `toString()` and run in chromium — coverage counters inside would ReferenceError.
649- selector :
650- "CallExpression[callee.property.name='evaluate']" +
651- ":matches([arguments.0.type='ArrowFunctionExpression'], [arguments.0.type='FunctionExpression'])" ,
652- message :
653- 'Move the inline `.evaluate(...)` callback into a `*-browser-scripts.js` file ' +
654- '(NYC-excluded in nyc.config.js) and import it here.' ,
655- } , {
656- // Static-analysis bundlers (esbuild, webpack, rollup) only see literals as require
657- // arguments; once any transform (e.g. NYC) wraps them, this shape breaks bundling.
658- selector : "CallExpression[callee.name='require'][arguments.0.type='ConditionalExpression']" ,
659- message : 'Use `cond ? require(\'a\') : require(\'b\')` instead of `require(cond ? \'a\' : \'b\')`.' ,
660- } ] ,
677+ 'no-restricted-syntax' : [ 'error' , ...SRC_RESTRICTED_SYNTAX ] ,
661678
662679 'n/no-restricted-require' : [ 'error' , [
663680 ...GLOBAL_RESTRICTED_REQUIRES ,
@@ -793,6 +810,16 @@ export default [
793810 'unicorn/prefer-optional-catch-binding' : 'error' ,
794811 } ,
795812 } ,
813+ {
814+ name : 'dd-trace/packages/src' ,
815+ files : [
816+ 'packages/*/src/**/*.js' ,
817+ 'packages/*/src/**/*.mjs' ,
818+ ] ,
819+ rules : {
820+ 'no-restricted-syntax' : [ 'error' , ...SRC_RESTRICTED_SYNTAX , OBJECT_KEYS_LENGTH_PROBE ] ,
821+ } ,
822+ } ,
796823 {
797824 name : 'dd-trace/config-sync' ,
798825 files : [
0 commit comments