Skip to content

Commit 54a6d5a

Browse files
committed
feat: port rule n/no-deprecated-api
1 parent be505e9 commit 54a6d5a

19 files changed

Lines changed: 2932 additions & 0 deletions

internal/config/config.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
importPlugin "github.com/web-infra-dev/rslint/internal/plugins/import"
1111
jestPlugin "github.com/web-infra-dev/rslint/internal/plugins/jest"
1212
jsxA11yPlugin "github.com/web-infra-dev/rslint/internal/plugins/jsx_a11y"
13+
nPlugin "github.com/web-infra-dev/rslint/internal/plugins/n"
1314
promisePlugin "github.com/web-infra-dev/rslint/internal/plugins/promise"
1415
reactPlugin "github.com/web-infra-dev/rslint/internal/plugins/react"
1516
reactHooksPlugin "github.com/web-infra-dev/rslint/internal/plugins/react_hooks"
@@ -152,6 +153,11 @@ var KnownPlugins = []PluginInfo{
152153
DeclNames: []string{"eslint-plugin-jsx-a11y", "jsx-a11y"},
153154
getAllRules: func() []rule.Rule { return jsxA11yPlugin.GetAllRules() },
154155
},
156+
{
157+
RulePrefix: "n",
158+
DeclNames: []string{"eslint-plugin-n", "n"},
159+
getAllRules: func() []rule.Rule { return nPlugin.GetAllRules() },
160+
},
155161
{
156162
RulePrefix: "promise",
157163
DeclNames: []string{"eslint-plugin-promise", "promise"},
@@ -242,6 +248,7 @@ func RegisterAllRules() {
242248
registerAllReactHooksPluginRules()
243249
registerAllJestPluginRules()
244250
registerAllJsxA11yPluginRules()
251+
registerAllNPluginRules()
245252
registerAllPromisePluginRules()
246253
registerAllUnicornPluginRules()
247254
registerAllCoreEslintRules()
@@ -278,6 +285,12 @@ func registerAllPromisePluginRules() {
278285
}
279286
}
280287

288+
func registerAllNPluginRules() {
289+
for _, rule := range nPlugin.GetAllRules() {
290+
GlobalRuleRegistry.Register(rule.Name, rule)
291+
}
292+
}
293+
281294
func registerAllUnicornPluginRules() {
282295
for _, rule := range unicornPlugin.GetAllRules() {
283296
GlobalRuleRegistry.Register(rule.Name, rule)

internal/plugins/n/all.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package n_plugin
2+
3+
import (
4+
"github.com/web-infra-dev/rslint/internal/plugins/n/rules/no_deprecated_api"
5+
"github.com/web-infra-dev/rslint/internal/rule"
6+
)
7+
8+
func GetAllRules() []rule.Rule {
9+
return []rule.Rule{
10+
no_deprecated_api.NoDeprecatedApiRule,
11+
}
12+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package fixtures
2+
3+
import (
4+
"path"
5+
"runtime"
6+
)
7+
8+
func GetRootDir() string {
9+
_, filename, _, _ := runtime.Caller(0)
10+
return path.Dir(filename)
11+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"compilerOptions": {
3+
"target": "esnext",
4+
"module": "commonjs",
5+
"strict": true,
6+
"esModuleInterop": true,
7+
"lib": ["es2015", "es2017", "esnext", "DOM"],
8+
"allowJs": true
9+
}
10+
}

internal/plugins/n/plugin.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package n_plugin
2+
3+
const PLUGIN_NAME = "eslint-plugin-n"
Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
package no_deprecated_api
2+
3+
// deprecatedInfo describes a single deprecated API entry, mirroring upstream's
4+
// `DeprecatedInfo` object ({ since, removed?, replacedBy }).
5+
//
6+
// replacedBy has three upstream shapes that map to two fields here:
7+
// - a plain string -> replacedText (used verbatim, no version filtering)
8+
// - an array -> replacedList (filtered by the configured node version)
9+
// - null -> both empty (no "Use ... instead" suffix)
10+
type deprecatedInfo struct {
11+
since string
12+
removed string // "" unless the API was removed (uses the "removed" messageId)
13+
replacedText string
14+
replacedList []replaceEntry
15+
}
16+
17+
// replaceEntry is one `{ name, supported }` alternative in a replacedBy array.
18+
type replaceEntry struct {
19+
name string
20+
supported string
21+
}
22+
23+
// traceMap is the Go equivalent of upstream's `DeprecatedInfoTraceMap`: a tree
24+
// keyed by property name, where the READ/CALL/CONSTRUCT slots mark a terminal
25+
// deprecated access of that kind.
26+
type traceMap struct {
27+
read *deprecatedInfo
28+
call *deprecatedInfo
29+
construct *deprecatedInfo
30+
children map[string]*traceMap
31+
}
32+
33+
// --- deprecatedInfo constructors (keep the data tables below compact) ---
34+
35+
func depNull(since string) *deprecatedInfo { return &deprecatedInfo{since: since} }
36+
37+
func depRemoved(since, removed string) *deprecatedInfo {
38+
return &deprecatedInfo{since: since, removed: removed}
39+
}
40+
41+
func depStr(since, replaced string) *deprecatedInfo {
42+
return &deprecatedInfo{since: since, replacedText: replaced}
43+
}
44+
45+
func depList(since string, entries ...replaceEntry) *deprecatedInfo {
46+
return &deprecatedInfo{since: since, replacedList: entries}
47+
}
48+
49+
func rep(name, supported string) replaceEntry { return replaceEntry{name: name, supported: supported} }
50+
51+
// rawModules mirrors upstream lib/rules/no-deprecated-api.js `rawModules`
52+
// (the CommonJS / ESM module deprecation table) 1:1.
53+
var rawModules = map[string]*traceMap{
54+
"_linklist": {read: depNull("5.0.0")},
55+
"_stream_wrap": {read: depNull("12.0.0")},
56+
"async_hooks": {children: map[string]*traceMap{
57+
"currentId": {read: depList("8.2.0", rep("'async_hooks.executionAsyncId()'", "8.1.0"))},
58+
"triggerId": {read: depStr("8.2.0", "'async_hooks.triggerAsyncId()'")},
59+
}},
60+
"buffer": {children: map[string]*traceMap{
61+
"Buffer": {
62+
construct: depList("6.0.0", rep("'buffer.Buffer.alloc()'", "5.10.0"), rep("'buffer.Buffer.from()'", "5.10.0")),
63+
call: depList("6.0.0", rep("'buffer.Buffer.alloc()'", "5.10.0"), rep("'buffer.Buffer.from()'", "5.10.0")),
64+
},
65+
"SlowBuffer": {read: depList("6.0.0", rep("'buffer.Buffer.allocUnsafeSlow()'", "5.12.0"))},
66+
}},
67+
"constants": {read: depStr("6.3.0", "'constants' property of each module")},
68+
"crypto": {children: map[string]*traceMap{
69+
"_toBuf": {read: depNull("11.0.0")},
70+
"Credentials": {read: depStr("0.12.0", "'tls.SecureContext'")},
71+
"DEFAULT_ENCODING": {read: depNull("10.0.0")},
72+
"createCipher": {read: depList("10.0.0", rep("'crypto.createCipheriv()'", "0.1.94"))},
73+
"createCredentials": {read: depList("0.12.0", rep("'tls.createSecureContext()'", "0.11.13"))},
74+
"createDecipher": {read: depList("10.0.0", rep("'crypto.createDecipheriv()'", "0.1.94"))},
75+
"fips": {read: depList("10.0.0", rep("'crypto.getFips()' and 'crypto.setFips()'", "10.0.0"))},
76+
"prng": {read: depList("11.0.0", rep("'crypto.randomBytes()'", "0.5.8"))},
77+
"pseudoRandomBytes": {read: depList("11.0.0", rep("'crypto.randomBytes()'", "0.5.8"))},
78+
"rng": {read: depList("11.0.0", rep("'crypto.randomBytes()'", "0.5.8"))},
79+
}},
80+
"domain": {read: depNull("4.0.0")},
81+
"events": {children: map[string]*traceMap{
82+
"EventEmitter": {children: map[string]*traceMap{
83+
"listenerCount": {read: depList("4.0.0", rep("'events.EventEmitter#listenerCount()'", "3.2.0"))},
84+
}},
85+
"listenerCount": {read: depList("4.0.0", rep("'events.EventEmitter#listenerCount()'", "3.2.0"))},
86+
}},
87+
"freelist": {read: depNull("4.0.0")},
88+
"fs": {children: map[string]*traceMap{
89+
"SyncWriteStream": {read: depNull("4.0.0")},
90+
"exists": {read: depList("4.0.0", rep("'fs.stat()'", "0.0.2"), rep("'fs.access()'", "0.11.15"))},
91+
"lchmod": {read: depNull("0.4.0")},
92+
"lchmodSync": {read: depNull("0.4.0")},
93+
}},
94+
"http": {children: map[string]*traceMap{
95+
"createClient": {read: depList("0.10.0", rep("'http.request()'", "0.3.6"))},
96+
}},
97+
"module": {children: map[string]*traceMap{
98+
"Module": {children: map[string]*traceMap{
99+
"createRequireFromPath": {read: depList("12.2.0", rep("'module.createRequire()'", "12.2.0"))},
100+
"requireRepl": {read: depStr("6.0.0", "'require(\"repl\")'")},
101+
"_debug": {read: depNull("9.0.0")},
102+
}},
103+
"createRequireFromPath": {read: depList("12.2.0", rep("'module.createRequire()'", "12.2.0"))},
104+
"requireRepl": {read: depStr("6.0.0", "'require(\"repl\")'")},
105+
"_debug": {read: depNull("9.0.0")},
106+
}},
107+
"net": {children: map[string]*traceMap{
108+
"_setSimultaneousAccepts": {read: depNull("12.0.0")},
109+
}},
110+
"os": {children: map[string]*traceMap{
111+
"getNetworkInterfaces": {read: depList("0.6.0", rep("'os.networkInterfaces()'", "0.6.0"))},
112+
"tmpDir": {read: depList("7.0.0", rep("'os.tmpdir()'", "0.9.9"))},
113+
}},
114+
"path": {children: map[string]*traceMap{
115+
"_makeLong": {read: depList("9.0.0", rep("'path.toNamespacedPath()'", "9.0.0"))},
116+
}},
117+
"process": {children: map[string]*traceMap{
118+
"EventEmitter": {read: depStr("0.6.0", "'require(\"events\")'")},
119+
"assert": {read: depStr("10.0.0", "'require(\"assert\")'")},
120+
"binding": {read: depNull("10.9.0")},
121+
"env": {children: map[string]*traceMap{
122+
"NODE_REPL_HISTORY_FILE": {read: depStr("4.0.0", "'NODE_REPL_HISTORY'")},
123+
}},
124+
"report": {children: map[string]*traceMap{
125+
"triggerReport": {read: depStr("11.12.0", "'process.report.writeReport()'")},
126+
}},
127+
}},
128+
"punycode": {read: depStr("7.0.0", "'https://www.npmjs.com/package/punycode'")},
129+
"readline": {children: map[string]*traceMap{
130+
"codePointAt": {read: depNull("4.0.0")},
131+
"getStringWidth": {read: depNull("6.0.0")},
132+
"isFullWidthCodePoint": {read: depNull("6.0.0")},
133+
"stripVTControlCharacters": {read: depNull("6.0.0")},
134+
}},
135+
"repl": {children: map[string]*traceMap{
136+
"REPLServer": {read: depStr("22.9.0", "new repl.REPLServer()")},
137+
"Recoverable": {read: depStr("22.9.0", "new repl.Recoverable()")},
138+
"REPL_MODE_MAGIC": {read: depNull("8.0.0")},
139+
"builtinModules": {read: depStr("22.16.0", "module.builtinModules")},
140+
}},
141+
// safe-buffer.Buffer function/constructor is just a re-export of buffer.Buffer
142+
// and should be deprecated likewise.
143+
"safe-buffer": {children: map[string]*traceMap{
144+
"Buffer": {
145+
construct: depList("6.0.0", rep("'buffer.Buffer.alloc()'", "5.10.0"), rep("'buffer.Buffer.from()'", "5.10.0")),
146+
call: depList("6.0.0", rep("'buffer.Buffer.alloc()'", "5.10.0"), rep("'buffer.Buffer.from()'", "5.10.0")),
147+
},
148+
"SlowBuffer": {read: depList("6.0.0", rep("'buffer.Buffer.allocUnsafeSlow()'", "5.12.0"))},
149+
}},
150+
"sys": {read: depStr("0.3.0", "'util' module")},
151+
"timers": {children: map[string]*traceMap{
152+
"enroll": {read: depList("10.0.0", rep("'setTimeout()'", "0.0.1"), rep("'setInterval()'", "0.0.1"))},
153+
"unenroll": {read: depList("10.0.0", rep("'clearTimeout()'", "0.0.1"), rep("'clearInterval()'", "0.0.1"))},
154+
}},
155+
"tls": {children: map[string]*traceMap{
156+
"CleartextStream": {read: depNull("0.10.0")},
157+
"CryptoStream": {read: depList("0.12.0", rep("'tls.TLSSocket'", "0.11.4"))},
158+
"SecurePair": {read: depList("6.0.0", rep("'tls.TLSSocket'", "0.11.4"))},
159+
"convertNPNProtocols": {read: depNull("10.0.0")},
160+
"createSecurePair": {read: depList("6.0.0", rep("'tls.TLSSocket'", "0.11.4"))},
161+
"parseCertString": {read: depList("8.6.0", rep("'querystring.parse()'", "0.1.25"))},
162+
}},
163+
"tty": {children: map[string]*traceMap{
164+
"setRawMode": {read: depStr("0.10.0", "'tty.ReadStream#setRawMode()' (e.g. 'process.stdin.setRawMode()')")},
165+
}},
166+
"url": {children: map[string]*traceMap{
167+
"parse": {read: depList("11.0.0", rep("'url.URL' constructor", "6.13.0"))},
168+
"resolve": {read: depList("11.0.0", rep("'url.URL' constructor", "6.13.0"))},
169+
}},
170+
"util": {children: map[string]*traceMap{
171+
"debug": {read: depList("0.12.0", rep("'console.error()'", "0.1.100"))},
172+
"error": {read: depList("0.12.0", rep("'console.error()'", "0.1.100"))},
173+
"isArray": {read: depList("4.0.0", rep("'Array.isArray()'", "0.1.100"))},
174+
"isBoolean": {read: depNull("4.0.0")},
175+
"isBuffer": {read: depList("4.0.0", rep("'Buffer.isBuffer()'", "0.1.101"))},
176+
"isDate": {read: depNull("4.0.0")},
177+
"isError": {read: depNull("4.0.0")},
178+
"isFunction": {read: depNull("4.0.0")},
179+
"isNull": {read: depNull("4.0.0")},
180+
"isNullOrUndefined": {read: depNull("4.0.0")},
181+
"isNumber": {read: depNull("4.0.0")},
182+
"isObject": {read: depNull("4.0.0")},
183+
"isPrimitive": {read: depNull("4.0.0")},
184+
"isRegExp": {read: depNull("4.0.0")},
185+
"isString": {read: depNull("4.0.0")},
186+
"isSymbol": {read: depNull("4.0.0")},
187+
"isUndefined": {read: depNull("4.0.0")},
188+
"log": {read: depStr("6.0.0", "a third party module")},
189+
"print": {read: depList("0.12.0", rep("'console.log()'", "0.1.100"))},
190+
"pump": {read: depList("0.10.0", rep("'stream.Readable#pipe()'", "0.9.4"))},
191+
"puts": {read: depList("0.12.0", rep("'console.log()'", "0.1.100"))},
192+
"_extend": {read: depList("6.0.0", rep("'Object.assign()'", "4.0.0"))},
193+
}},
194+
"vm": {children: map[string]*traceMap{
195+
"runInDebugContext": {read: depNull("8.0.0")},
196+
}},
197+
"zlib": {children: map[string]*traceMap{
198+
"BrotliCompress": {call: depStr("22.9.0", "new zlib.BrotliCompress()")},
199+
"BrotliDecompress": {call: depStr("22.9.0", "new zlib.BrotliDecompress()")},
200+
"Deflate": {call: depStr("22.9.0", "new zlib.Deflate()")},
201+
"DeflateRaw": {call: depStr("22.9.0", "new zlib.DeflateRaw()")},
202+
"Gunzip": {call: depStr("22.9.0", "new zlib.Gunzip()")},
203+
"Gzip": {call: depStr("22.9.0", "new zlib.Gzip()")},
204+
"Inflate": {call: depStr("22.9.0", "new zlib.Inflate()")},
205+
"InflateRaw": {call: depStr("22.9.0", "new zlib.InflateRaw()")},
206+
"Unzip": {call: depStr("22.9.0", "new zlib.Unzip()")},
207+
}},
208+
}
209+
210+
// modules is rawModules extended with `node:`-prefixed aliases for builtins.
211+
var modules = extendTraceMapWithNodePrefix(rawModules)
212+
213+
// globals mirrors upstream's `globals` table (deprecated global variables).
214+
// `process` reuses the module trace map so `process.binding` etc. are detected
215+
// as global member accesses too.
216+
var globals = map[string]*traceMap{
217+
"Buffer": {
218+
construct: depList("6.0.0", rep("'Buffer.alloc()'", "5.10.0"), rep("'Buffer.from()'", "5.10.0")),
219+
call: depList("6.0.0", rep("'Buffer.alloc()'", "5.10.0"), rep("'Buffer.from()'", "5.10.0")),
220+
},
221+
"COUNTER_NET_SERVER_CONNECTION": {read: depNull("11.0.0")},
222+
"COUNTER_NET_SERVER_CONNECTION_CLOSE": {read: depNull("11.0.0")},
223+
"COUNTER_HTTP_SERVER_REQUEST": {read: depNull("11.0.0")},
224+
"COUNTER_HTTP_SERVER_RESPONSE": {read: depNull("11.0.0")},
225+
"COUNTER_HTTP_CLIENT_REQUEST": {read: depNull("11.0.0")},
226+
"COUNTER_HTTP_CLIENT_RESPONSE": {read: depNull("11.0.0")},
227+
"GLOBAL": {read: depList("6.0.0", rep("'global'", "0.1.27"))},
228+
"Intl": {children: map[string]*traceMap{
229+
"v8BreakIterator": {read: depRemoved("7.0.0", "9.0.0")},
230+
}},
231+
"require": {children: map[string]*traceMap{
232+
"extensions": {read: depStr("0.12.0", "compiling them ahead of time")},
233+
}},
234+
"root": {read: depList("6.0.0", rep("'global'", "0.1.27"))},
235+
"process": rawModules["process"],
236+
}
237+
238+
// nodeBuiltinModules is the set of Node.js builtin module names, used as the
239+
// `module.isBuiltin` equivalent for extendTraceMapWithNodePrefix. Names with a
240+
// leading underscore, removed modules (`sys`, `freelist`), and third-party
241+
// re-exports (`safe-buffer`) are intentionally absent — they get no `node:`
242+
// alias, matching upstream's `isBuiltin` filter.
243+
var nodeBuiltinModules = map[string]bool{
244+
"assert": true, "assert/strict": true, "async_hooks": true, "buffer": true,
245+
"child_process": true, "cluster": true, "console": true, "constants": true,
246+
"crypto": true, "dgram": true, "diagnostics_channel": true, "dns": true,
247+
"dns/promises": true, "domain": true, "events": true, "fs": true,
248+
"fs/promises": true, "http": true, "http2": true, "https": true,
249+
"inspector": true, "inspector/promises": true, "module": true, "net": true,
250+
"os": true, "path": true, "path/posix": true, "path/win32": true,
251+
"perf_hooks": true, "process": true, "punycode": true, "querystring": true,
252+
"readline": true, "readline/promises": true, "repl": true, "stream": true,
253+
"stream/consumers": true, "stream/promises": true, "stream/web": true,
254+
"string_decoder": true, "timers": true, "timers/promises": true, "tls": true,
255+
"trace_events": true, "tty": true, "url": true, "util": true,
256+
"util/types": true, "v8": true, "vm": true, "wasi": true,
257+
"worker_threads": true, "zlib": true,
258+
}
259+
260+
// extendTraceMapWithNodePrefix mirrors upstream's util of the same name: for
261+
// every builtin module key it adds a `node:`-prefixed alias pointing at the same
262+
// trace map (so `require('node:buffer')` is treated like `require('buffer')`).
263+
func extendTraceMapWithNodePrefix(src map[string]*traceMap) map[string]*traceMap {
264+
out := make(map[string]*traceMap, len(src)*2)
265+
for name, value := range src {
266+
out[name] = value
267+
if nodeBuiltinModules[name] {
268+
out["node:"+name] = value
269+
}
270+
}
271+
return out
272+
}
273+
274+
// unprefixNodeColon removes a leading `node:` from a module name for reporting,
275+
// mirroring upstream's util of the same name.
276+
func unprefixNodeColon(name string) string {
277+
if len(name) > 5 && name[:5] == "node:" {
278+
return name[5:]
279+
}
280+
return name
281+
}

0 commit comments

Comments
 (0)