Skip to content

Commit 988c6bb

Browse files
committed
fix(types,quality): revert Object.create(undefined) regression + finish null→undefined flip
Resolves task #51. Critical fixes: * Object.create(undefined) → Object.create(null) across 6 files (packages/cli/{src/utils,test/helpers}, test/unit/bootstrap). The prior autofix replaced `null` with `undefined`, which makes Object.create throw a TypeError at runtime — `null` is the documented prototype sentinel. * packages/cli/scripts/wasm.mts: unterminated string literal ('Keeping existing file\n') fixed. Type alignment with the prior null→undefined autofix: * 18 packages/cli/src/{commands,utils,test} files: `T | null` types flipped to `T | undefined` to match call-site values. * detect-manifest-actions.mts: `sockJson: SocketJson | null` → `| undefined` (param's sentinel meaning was already undefined-based; comment updated to match). * test-builder.mts, cli-process-pool.mts: pool refs flipped. SDK schema literals (`nextPage: string | null` returned by the public SDK) kept their `null` value with inline disables — the schema is external API contract. Also picks up the sort-source-methods autofix from socket-wheelhouse. Iterated `oxlint --fix` to convergence: 1729 → 1472 lint violations (no-dynamic-import / no-status-emoji / prefer-exists-sync need judgment-call fixes that the autofix doesn't attempt).
1 parent e74da4d commit 988c6bb

334 files changed

Lines changed: 7519 additions & 7049 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.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"compilerOptions": {
3+
"declarationMap": false,
4+
"erasableSyntaxOnly": true,
5+
"module": "nodenext",
6+
"moduleResolution": "nodenext",
7+
"noEmit": true,
8+
"rewriteRelativeImportExtensions": true,
9+
"skipLibCheck": true,
10+
"sourceMap": false,
11+
"strict": true,
12+
"target": "esnext",
13+
"verbatimModuleSyntax": true
14+
}
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"compilerOptions": {
3+
"declarationMap": false,
4+
"erasableSyntaxOnly": true,
5+
"module": "nodenext",
6+
"moduleResolution": "nodenext",
7+
"noEmit": true,
8+
"rewriteRelativeImportExtensions": true,
9+
"skipLibCheck": true,
10+
"sourceMap": false,
11+
"strict": true,
12+
"target": "esnext",
13+
"verbatimModuleSyntax": true
14+
}
15+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/**
2+
* @fileoverview Shared helper for rule fixers that need to inject
3+
* an `import { Name } from 'specifier'` statement (and optionally a
4+
* matching hoisted `const`) into a file.
5+
*
6+
* Fixers call `summarizeImportTarget(programNode, specifier, importName)`
7+
* to learn the file's current shape, then `appendImportFixes(...)`
8+
* inside their `fix(fixer)` callback to add the missing pieces.
9+
*
10+
* ESLint's autofixer dedupes overlapping inserts at the same range,
11+
* so multiple violations in the same file can each emit the import
12+
* insertion safely — only one survives.
13+
*/
14+
15+
/**
16+
* Walk a Program node body once and figure out:
17+
* - the last top-level ImportDeclaration node (or undefined)
18+
* - whether `importName` is already imported (from ANY source)
19+
* - whether a top-level `localName` identifier already exists
20+
* (any const/let/var or import-as-local with that name)
21+
*
22+
* Import detection ignores the specifier path: a file inside the lib
23+
* package itself imports `getDefaultLogger` from `'../logger'`, while
24+
* a downstream repo imports the same name from
25+
* `'@socketsecurity/lib/logger'`. Both resolve to the same identifier;
26+
* either should count as "already imported" so the autofix doesn't
27+
* inject a duplicate (and broken — see issue #64).
28+
*
29+
* `specifier` is retained in the signature for backward compatibility
30+
* but is no longer used for the match decision. Callers may pass any
31+
* truthy value (typically the canonical package path the rule would
32+
* inject if the import were missing).
33+
*/
34+
export function summarizeImportTarget(
35+
program,
36+
// eslint-disable-next-line no-unused-vars
37+
specifier,
38+
importName,
39+
localName,
40+
) {
41+
let lastImport
42+
let hasImport = false
43+
let hasLocal = false
44+
for (const stmt of program.body) {
45+
if (stmt.type === 'ImportDeclaration') {
46+
lastImport = stmt
47+
for (const spec of stmt.specifiers) {
48+
if (
49+
spec.type === 'ImportSpecifier' &&
50+
spec.imported &&
51+
spec.imported.name === importName
52+
) {
53+
hasImport = true
54+
}
55+
if (
56+
localName &&
57+
spec.local &&
58+
spec.local.name === localName &&
59+
(spec.type === 'ImportSpecifier' ||
60+
spec.type === 'ImportDefaultSpecifier' ||
61+
spec.type === 'ImportNamespaceSpecifier')
62+
) {
63+
hasLocal = true
64+
}
65+
}
66+
continue
67+
}
68+
if (localName && stmt.type === 'VariableDeclaration') {
69+
for (const decl of stmt.declarations) {
70+
if (
71+
decl.id &&
72+
decl.id.type === 'Identifier' &&
73+
decl.id.name === localName
74+
) {
75+
hasLocal = true
76+
}
77+
}
78+
}
79+
}
80+
return { hasImport, hasLocal, lastImport }
81+
}
82+
83+
/**
84+
* Build the fixer-side inserts for missing import + optional hoist.
85+
* Returns an array of fixer operations the caller appends to its own
86+
* fix() return value.
87+
*
88+
* summary — output of summarizeImportTarget()
89+
* fixer — the fixer passed to context.report({ fix })
90+
* importLine — the literal `import { ... } from '...'` text
91+
* hoistLine — optional; the literal `const x = ...()` text
92+
*/
93+
export function appendImportFixes(summary, fixer, importLine, hoistLine) {
94+
const ops = []
95+
if (!summary.hasImport) {
96+
if (summary.lastImport) {
97+
ops.push(fixer.insertTextAfter(summary.lastImport, `\n${importLine}`))
98+
} else {
99+
ops.push(fixer.insertTextBeforeRange([0, 0], `${importLine}\n`))
100+
}
101+
}
102+
if (hoistLine && !summary.hasLocal) {
103+
if (summary.lastImport) {
104+
ops.push(fixer.insertTextAfter(summary.lastImport, `\n\n${hoistLine}`))
105+
} else {
106+
ops.push(fixer.insertTextBeforeRange([0, 0], `${hoistLine}\n\n`))
107+
}
108+
}
109+
return ops
110+
}

0 commit comments

Comments
 (0)