Skip to content

Commit 5925132

Browse files
committed
chore(eslint): update eslint-plugin-unicorn to 72 (#9503)
Activate Node.js 18 compatible rules that remove redundant syntax and catch correctness issues. 1. Skip esbuild resolution for built-in modules without package paths. 2. Contain detached Cucumber finalizer rejections without changing the returned test promise. 3. Preserve datastream varint continuation bits at 0x80 boundaries. 4. Avoid buffering Electron traces when the trace channel has no subscribers. 5. Replace regex-based HTTP path splitting with a shared delimiter scan, reducing representative cost from 41.4 ns to 14.7 ns per call.
1 parent 8c33807 commit 5925132

98 files changed

Lines changed: 497 additions & 259 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

ci/test-optimization-validation/generated-files.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ function getMissingDirectories (root, directory) {
132132
function cleanupCreatedDirectories (root) {
133133
const outcome = { removed: 0, retained: 0 }
134134
const resolvedRoot = path.resolve(root)
135-
const directories = [...createdGeneratedDirectories.entries()]
135+
const directories = [...createdGeneratedDirectories]
136136
.filter(([directory]) => isPathInside(resolvedRoot, directory))
137137
.sort(([left], [right]) => right.length - left.length)
138138

ci/test-optimization-validation/generated-test-contract.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -192,9 +192,7 @@ function getGeneratedTestContractError (framework) {
192192
if (!cleanupPaths.has(path.normalize(stepsFile))) {
193193
return 'must include the isolated Cucumber step definitions in cleanupPaths.'
194194
}
195-
}
196-
197-
if (framework.framework === 'playwright') {
195+
} else if (framework.framework === 'playwright') {
198196
const configPath = playwrightAdapter.getGeneratedConfigPath(strategy.testDirectory)
199197
const config = files.find(file => path.normalize(file.path) === path.normalize(configPath))
200198
if (config?.contentLines?.join('\n') !== playwrightAdapter.getGeneratedConfigContent()) {

ci/test-optimization-validation/manifest-scaffold.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -477,8 +477,7 @@ function buildGeneratedTestStrategy ({ framework, projectRoot, representative })
477477
const stepsFile = cucumber.getGeneratedStepsPath(testDirectory)
478478
files.push({ path: stepsFile, contentLines: cucumber.getGeneratedStepsContent().split('\n') })
479479
cleanupPaths.push(stepsFile)
480-
}
481-
if (framework === 'playwright') {
480+
} else if (framework === 'playwright') {
482481
const configFile = playwright.getGeneratedConfigPath(testDirectory)
483482
files.push({ path: configFile, contentLines: playwright.getGeneratedConfigContent().split('\n') })
484483
cleanupPaths.push(configFile)

ci/test-optimization-validation/runner-command.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ function getRunnerArgs (framework, testFile, generated) {
211211
*/
212212
function getRunnerEnv (framework) {
213213
return {
214-
...(framework.validation.environment || {}),
214+
...framework.validation.environment,
215215
...(framework.framework === 'cucumber' ? { CUCUMBER_PUBLISH_QUIET: 'true' } : {}),
216216
}
217217
}

ci/test-optimization-validation/scenarios/basic-reporting.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ async function runBasicReporting ({ framework, out, options }) {
5858
selector,
5959
settingsLoadedFromCache: run.offline.inputs.settings?.status === 'loaded',
6060
}
61-
evidence.foundationalReportingEstablished = evidence.foundationalReportingEstablished && selector.verified
61+
evidence.foundationalReportingEstablished &&= selector.verified
6262

6363
if (run.result.timedOut) {
6464
return inconclusive(

ci/test-optimization-validation/scenarios/test-management.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ function buildQuarantinedResponse (framework, scenario, discoveredIdentities = [
149149
for (const identity of identities) {
150150
for (const suite of getSuiteCandidates(identity, scenario)) {
151151
for (const name of getNameCandidates(identity)) {
152-
suites[suite] = suites[suite] || { tests: {} }
152+
suites[suite] ||= { tests: {} }
153153
suites[suite].tests[name] = {
154154
properties: {
155155
quarantined: true,
@@ -210,7 +210,7 @@ function summarizeManagedTests (testManagementTests) {
210210
}
211211
summary.set(displaySuite, testNames)
212212
}
213-
return [...summary.entries()].slice(0, 5).map(([suite, tests]) => ({
213+
return [...summary].slice(0, 5).map(([suite, tests]) => ({
214214
suite,
215215
tests: [...tests].slice(0, 5),
216216
}))

eslint.config.mjs

Lines changed: 46 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -586,60 +586,57 @@ export default [
586586
...eslintPluginUnicorn.configs.recommended.rules,
587587

588588
// Overriding recommended unicorn rules.
589-
// Rules not listed here are left at the `recommended` default. The v65→v68 bump
590-
// turned ~130 rules on in `recommended`; the entries below are the ones that fire
591-
// on our source and are intentionally kept off (counts are from the v68 run).
592-
'unicorn/catch-error-name': ['off', { name: 'err' }], // Many errors
589+
// Rules not listed here are left at the `recommended` default. The entries below
590+
// document deliberate exceptions. Volume markers stay coarse so they do not drift:
591+
// `few` is under ten sites, `many` is tens, `lots` is hundreds or more.
592+
'unicorn/catch-error-name': ['off', { name: 'err' }], // lots
593593
'unicorn/expiring-todo-comments': 'off',
594-
'unicorn/filename-case': ['off', { case: 'kebabCase' }], // Many errors
595-
'unicorn/name-replacements': 'off', // Many errors | naming churn (split out of prevent-abbreviations)
596-
'unicorn/prevent-abbreviations': 'off', // Many errors
594+
'unicorn/filename-case': ['off', { case: 'kebabCase' }], // lots
595+
'unicorn/name-replacements': 'off', // lots | naming churn (split out of prevent-abbreviations)
596+
'unicorn/prevent-abbreviations': 'off', // Its replacements moved to name-replacements
597597

598598
// These rules require a newer Node.js version than we support
599599
'unicorn/no-array-reverse': 'off', // Node.js 20
600600
'unicorn/no-array-sort': 'off', // Node.js 20
601+
'unicorn/prefer-abort-signal-any': 'off', // Node.js 18.17
601602
'unicorn/prefer-dispose': 'off', // Explicit resource management (newer Node.js)
603+
'unicorn/prefer-group-by': 'off', // Node.js 21
604+
'unicorn/prefer-iterator-helpers': 'off', // Iterator helpers (Node.js 22)
602605
'unicorn/prefer-iterator-to-array': 'off', // Iterator helpers (Node.js 22)
603606
'unicorn/prefer-iterator-to-array-at-end': 'off', // Iterator helpers (Node.js 22)
604-
'unicorn/prefer-promise-with-resolvers': 'off', // 6 errors | Promise.withResolvers (Node.js 22)
607+
'unicorn/prefer-promise-try': 'off', // Promise.try (Node.js 24)
608+
'unicorn/prefer-promise-with-resolvers': 'off', // few | Promise.withResolvers (Node.js 22)
609+
'unicorn/prefer-set-methods': 'off', // Set methods (Node.js 22)
605610
'unicorn/prefer-temporal': 'off', // Temporal is not stable on supported Node.js
606611
'unicorn/prefer-uint8array-base64': 'off', // Uint8Array base64 (Node.js 22)
607612

608613
// These rules could potentially be evaluated again at a much later point
609-
'unicorn/class-reference-in-static-methods': 'off', // 6 errors
610-
'unicorn/consistent-class-member-order': 'off', // 55 errors | ordering churn
611-
'unicorn/consistent-conditional-object-spread': 'off', // 3 errors
612-
'unicorn/consistent-optional-chaining': 'off', // 3 errors
614+
'unicorn/class-reference-in-static-methods': 'off', // few
615+
'unicorn/consistent-class-member-order': 'off', // many | ordering churn
616+
'unicorn/consistent-conditional-object-spread': 'off', // few
613617
'unicorn/explicit-length-check': 'off', // Not a big advantage
614618
'unicorn/explicit-timer-delay': 'off', // Covered by our own timer lint rules
615619
'unicorn/no-array-callback-reference': 'off',
616-
'unicorn/no-computed-property-existence-check': 'off', // 160 errors | needs an audit
617-
'unicorn/no-declarations-before-early-exit': 'off', // 62 errors
618-
'unicorn/no-duplicate-if-branches': 'off', // 1 error | may surface real bugs
619-
'unicorn/no-duplicate-logical-operands': 'off', // 2 errors | may surface real bugs
620-
'unicorn/no-error-property-assignment': 'off', // 6 errors
620+
'unicorn/no-computed-property-existence-check': 'off', // lots | needs an audit
621+
'unicorn/no-declarations-before-early-exit': 'off', // many
622+
'unicorn/no-error-property-assignment': 'off', // few | all preserve upstream error metadata
621623
'unicorn/no-for-loop': 'off', // Activate if this is resolved https://github.com/sindresorhus/eslint-plugin-unicorn/issues/2664
622-
'unicorn/no-incorrect-template-string-interpolation': 'off', // 3 errors | audit for real bugs first
623-
'unicorn/no-invalid-argument-count': 'off', // 98 errors | high false-positive risk, worth a focused pass
624-
'unicorn/no-loop-iterable-mutation': 'off', // 2 errors | may surface real bugs
625-
'unicorn/no-nonstandard-builtin-properties': 'off', // 34 errors | needs an audit
624+
'unicorn/no-nonstandard-builtin-properties': 'off', // many | needs an audit
626625
'unicorn/no-this-assignment': 'off', // This would need some further refactoring and the benefit is small
627-
'unicorn/no-undeclared-class-members': 'off', // 272 errors | requires declaring every field
628-
'unicorn/no-unreadable-array-destructuring': 'off', // 4 errors | not autofixable, needs manual rewrite
629-
'unicorn/no-unreadable-for-of-expression': 'off', // 32 errors
630-
'unicorn/no-unreadable-object-destructuring': 'off', // 57 errors
631-
'unicorn/no-unsafe-string-replacement': 'off', // 6 errors
632-
'unicorn/no-useless-recursion': 'off', // 2 errors
626+
'unicorn/no-undeclared-class-members': 'off', // lots | requires declaring every field
627+
'unicorn/no-unreadable-array-destructuring': 'off', // few | not autofixable, needs manual rewrite
628+
'unicorn/no-unreadable-for-of-expression': 'off', // many
629+
'unicorn/no-unreadable-object-destructuring': 'off', // many
630+
'unicorn/no-unsafe-string-replacement': 'off', // many | replacement callbacks reduce readability
631+
'unicorn/no-useless-recursion': 'off', // few | iterative rewrites add substantial nesting
633632
'unicorn/prefer-code-point': 'off', // Should be activated, but needs a refactor of some code
634-
'unicorn/prefer-early-return': 'off', // 67 errors | tension with our positive-`if` style
635-
'unicorn/prefer-hoisting-branch-code': 'off', // 2 errors | reshapes branch bodies
636-
'unicorn/prefer-minimal-ternary': 'off', // 24 errors
637-
'unicorn/prefer-number-is-safe-integer': 'off', // 17 errors
638-
'unicorn/prefer-object-iterable-methods': 'off', // 56 errors
633+
'unicorn/prefer-early-return': 'off', // many | tension with our positive-`if` style
634+
'unicorn/prefer-number-is-safe-integer': 'off', // many
635+
'unicorn/prefer-object-iterable-methods': 'off', // many
639636
'unicorn/prefer-queue-microtask': 'off', // process.nextTick semantics differ
640-
'unicorn/prefer-smaller-scope': 'off', // 3 errors
641-
'unicorn/prefer-split-limit': 'off', // 23 errors
642-
'unicorn/require-array-sort-compare': 'off', // 8 errors | may surface real default-sort bugs
637+
'unicorn/prefer-simple-condition-first': 'off', // lots | needs a short-circuit behavior audit
638+
'unicorn/prefer-then-catch': 'off', // many | broadens rejection boundaries
639+
'unicorn/require-array-sort-compare': 'off', // many | many intentional lexicographic sorts
643640

644641
// The following rules should not be activated!
645642
'unicorn/consistent-boolean-name': 'off', // Would rename public API and config booleans
@@ -649,6 +646,8 @@ export default [
649646
'unicorn/no-array-splice': 'off', // toSpliced copies the whole array (perf)
650647
'unicorn/no-break-in-nested-loop': 'off', // Conflicts with our performance-oriented loops
651648
'unicorn/no-global-object-property-assignment': 'off', // We use globalThis[Symbol.for('dd-trace')]
649+
'unicorn/no-negated-array-predicate': 'off', // Predicate inversion is harder to read and creates churn
650+
'unicorn/no-negated-comparison': 'off', // Opposite comparisons do not preserve NaN handling
652651
'unicorn/no-nested-ternary': 'off', // Not really an issue in the code and the benefit is small
653652
'unicorn/no-new-array': 'off', // new Array is often used for performance reasons
654653
'unicorn/no-null': 'off', // We do not control external APIs and it is hard to differentiate these
@@ -659,47 +658,32 @@ export default [
659658
'unicorn/operator-assignment': 'off', // Covered by core operator-assignment
660659
'unicorn/prefer-array-last-methods': 'off', // Questionable benefit
661660
'unicorn/prefer-await': 'off', // We avoid async/await in production hot paths
661+
'unicorn/prefer-dom-node-html-methods': 'off', // Browser compatibility and different serialization semantics
662662
'unicorn/prefer-event-target': 'off', // Benefit only outside of Node.js
663663
'unicorn/prefer-global-this': 'off', // Questionable benefit in Node.js alone
664664
'unicorn/prefer-includes-over-repeated-comparisons': 'off', // Bad for performance
665665
'unicorn/prefer-math-trunc': 'off', // Math.trunc is not a 1-to-1 replacement for most of our usage
666+
'unicorn/prefer-minimal-ternary': 'off', // Conflicts with our restricted-syntax rule on require(cond ? a : b)
666667
'unicorn/prefer-module': 'off', // We use CJS
667668
'unicorn/prefer-node-protocol': 'off', // May not be used due to guardrails
668669
'unicorn/prefer-number-coercion': 'off', // Number() is not a 1-to-1 replacement for parseInt/parseFloat
669670
'unicorn/prefer-private-class-fields': 'off', // Many `_underscore` fields cross module boundaries
670-
'unicorn/prefer-reflect-apply': 'off', // Questionable benefit and more than 500 matches
671+
'unicorn/prefer-reflect-apply': 'off', // lots | questionable benefit
671672
'unicorn/prefer-short-arrow-method': 'off', // Method shorthand is intentional; arrow properties change `this`
673+
'unicorn/prefer-split-limit': 'off', // A limit is slower than getSegment; the rest read every segment
672674
'unicorn/prefer-switch': 'off', // Questionable benefit
673675
'unicorn/prefer-top-level-await': 'off', // Only useful when using ESM
674676
'unicorn/prefer-unicode-code-point-escapes': 'off', // Replaces the dropped no-hex-escape; questionable benefit
675677
'unicorn/switch-case-braces': 'off', // Questionable benefit
676678

677-
// Safe to enable in a follow-up: autofixable and aligned with our style. Kept off
678-
// here only to keep this version bump free of source churn (counts from the v68 run).
679-
'unicorn/logical-assignment-operators': 'off', // 42 errors | matches our ??=/||= usage
680-
'unicorn/no-confusing-array-splice': 'off', // 1 error
681-
'unicorn/no-for-each': 'off', // 10 errors | we already prefer for-of in production
682-
'unicorn/no-negated-array-predicate': 'off', // 2 errors
683-
'unicorn/no-negated-comparison': 'off', // 1 error
684-
'unicorn/no-subtraction-comparison': 'off', // 2 errors
685-
'unicorn/no-unnecessary-boolean-comparison': 'off', // 6 errors
686-
'unicorn/no-unnecessary-global-this': 'off', // 4 errors
687-
'unicorn/no-unnecessary-splice': 'off', // 2 errors
688-
'unicorn/no-useless-concat': 'off', // 4 errors
689-
'unicorn/no-useless-continue': 'off', // 1 error
690-
'unicorn/no-useless-delete-check': 'off', // 1 error
691-
'unicorn/no-useless-fallback-in-spread': 'off', // 5 errors
692-
'unicorn/no-useless-override': 'off', // 1 error
693-
'unicorn/no-useless-template-literals': 'off', // 13 errors
694-
'unicorn/prefer-array-from-map': 'off', // 6 errors
695-
'unicorn/prefer-boolean-return': 'off', // 1 error
696-
'unicorn/prefer-continue': 'off', // 52 errors
697-
'unicorn/prefer-direct-iteration': 'off', // 5 errors
698-
'unicorn/prefer-else-if': 'off', // 6 errors
699-
'unicorn/prefer-global-number-constants': 'off', // 3 errors
700-
'unicorn/prefer-logical-operator-over-ternary': 'off', // 3 errors
701-
'unicorn/prefer-ternary': 'off', // 16 errors
702-
'unicorn/prefer-unary-minus': 'off', // 1 error
679+
// These remaining rules need focused rewrites before activation.
680+
'unicorn/no-confusing-array-splice': 'off', // few
681+
'unicorn/no-for-each': 'off', // many | we already prefer for-of in production
682+
'unicorn/no-unnecessary-global-this': 'off', // few | explicit globals are clearer
683+
'unicorn/no-useless-continue': 'off', // few
684+
'unicorn/prefer-array-from-map': 'off', // few | loops avoid callback allocation
685+
'unicorn/prefer-continue': 'off', // many
686+
'unicorn/prefer-ternary': 'off', // many
703687
},
704688
},
705689
{

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@
213213
"eslint-plugin-n": "^18.2.1",
214214
"eslint-plugin-promise": "^7.3.0",
215215
"eslint-plugin-sonarjs": "^4.2.0",
216-
"eslint-plugin-unicorn": "^68.0.0",
216+
"eslint-plugin-unicorn": "^72.0.0",
217217
"express": "^5.1.0",
218218
"glob": "^10.4.5",
219219
"globals": "^17.7.0",

packages/datadog-esbuild/index.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -265,10 +265,12 @@ ${build.initialOptions.banner.js}`
265265
}
266266
}
267267
// The file namespace is used when requiring files from disk in userland
268+
if (extracted.pkg === null) return
269+
268270
let pathToPackageJson
269271
try {
270272
// we can't use require.resolve('pkg/package.json') as ESM modules don't make the file available
271-
pathToPackageJson = require.resolve(`${extracted.pkg}`, { paths: [args.resolveDir] })
273+
pathToPackageJson = require.resolve(extracted.pkg, { paths: [args.resolveDir] })
272274
pathToPackageJson = extractPackageAndModulePath(pathToPackageJson).pkgJson
273275
} catch (err) {
274276
if (err.code === 'MODULE_NOT_FOUND') {
@@ -340,7 +342,7 @@ ${build.initialOptions.banner.js}`
340342

341343
if (data.isESM) {
342344
if (args.path.endsWith(ESM_INTERCEPTED_SUFFIX)) {
343-
args.path = args.path.slice(0, -1 * ESM_INTERCEPTED_SUFFIX.length)
345+
args.path = args.path.slice(0, -ESM_INTERCEPTED_SUFFIX.length)
344346

345347
if (data.internal) {
346348
args.path = args.path.slice(INTERNAL_ESM_INTERCEPTED_PREFIX.length)

packages/datadog-esbuild/src/utils.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ async function processModule ({ path, internal = false, context, excludeDefault
174174
for (const n of exportNames) {
175175
if (n === 'default' && excludeDefault) continue
176176

177-
if (isStarExportLine(n) === true) {
177+
if (isStarExportLine(n)) {
178178
// export * from 'wherever'
179179
const [, modFile] = n.split('* from ')
180180

0 commit comments

Comments
 (0)