no-deprecated-api.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  1. /**
  2. * @author Toru Nagashima
  3. * See LICENSE file in root directory for full license.
  4. */
  5. "use strict"
  6. const { CALL, CONSTRUCT, READ, ReferenceTracker } = require("eslint-utils")
  7. const enumeratePropertyNames = require("../util/enumerate-property-names")
  8. const modules = {
  9. _linklist: {
  10. [READ]: { since: "5.0.0", replacedBy: null },
  11. },
  12. assert: {
  13. deepEqual: {
  14. [READ]: {
  15. since: "10.0.0",
  16. replacedBy:
  17. "'assert.deepStrictEqual' or 'assert.strict.deepEqual'",
  18. },
  19. },
  20. equal: {
  21. [READ]: {
  22. since: "10.0.0",
  23. replacedBy: "'assert.strictEqual' or 'assert.strict.equal'",
  24. },
  25. },
  26. notDeepEqual: {
  27. [READ]: {
  28. since: "10.0.0",
  29. replacedBy:
  30. "'assert.notDeepStrictEqual' or 'assert.strict.notDeepEqual'",
  31. },
  32. },
  33. notEqual: {
  34. [READ]: {
  35. since: "10.0.0",
  36. replacedBy:
  37. "'assert.notStrictEqual' or 'assert.strict.notEqual'",
  38. },
  39. },
  40. },
  41. //eslint-disable-next-line camelcase
  42. async_hooks: {
  43. currentId: {
  44. [READ]: {
  45. since: "8.2.0",
  46. replacedBy: "'async_hooks.executionAsyncId()'",
  47. },
  48. },
  49. triggerId: {
  50. [READ]: {
  51. since: "8.2.0",
  52. replacedBy: "'async_hooks.triggerAsyncId()'",
  53. },
  54. },
  55. },
  56. buffer: {
  57. Buffer: {
  58. [CONSTRUCT]: {
  59. since: "6.0.0",
  60. replacedBy: "'buffer.Buffer.alloc()' or 'buffer.Buffer.from()'",
  61. },
  62. [CALL]: {
  63. since: "6.0.0",
  64. replacedBy: "'buffer.Buffer.alloc()' or 'buffer.Buffer.from()'",
  65. },
  66. },
  67. SlowBuffer: {
  68. [READ]: {
  69. since: "6.0.0",
  70. replacedBy: "'buffer.Buffer.allocUnsafeSlow()'",
  71. },
  72. },
  73. },
  74. constants: {
  75. [READ]: {
  76. since: "6.3.0",
  77. replacedBy: "'constants' property of each module",
  78. },
  79. },
  80. crypto: {
  81. _toBuf: {
  82. [READ]: { since: "11.0.0", replacedBy: null },
  83. },
  84. Credentials: {
  85. [READ]: { since: "0.12.0", replacedBy: "'tls.SecureContext'" },
  86. },
  87. DEFAULT_ENCODING: {
  88. [READ]: { since: "10.0.0", replacedBy: null },
  89. },
  90. createCipher: {
  91. [READ]: {
  92. since: "10.0.0",
  93. replacedBy: "'crypto.createCipheriv()'",
  94. },
  95. },
  96. createCredentials: {
  97. [READ]: {
  98. since: "0.12.0",
  99. replacedBy: "'tls.createSecureContext()'",
  100. },
  101. },
  102. createDecipher: {
  103. [READ]: {
  104. since: "10.0.0",
  105. replacedBy: "'crypto.createDecipheriv()'",
  106. },
  107. },
  108. fips: {
  109. [READ]: {
  110. since: "10.0.0",
  111. replacedBy: "'crypto.getFips()' and 'crypto.setFips()'",
  112. },
  113. },
  114. prng: {
  115. [READ]: { since: "11.0.0", replacedBy: "'crypto.randomBytes()'" },
  116. },
  117. pseudoRandomBytes: {
  118. [READ]: { since: "11.0.0", replacedBy: "'crypto.randomBytes()'" },
  119. },
  120. rng: {
  121. [READ]: { since: "11.0.0", replacedBy: "'crypto.randomBytes()'" },
  122. },
  123. },
  124. domain: {
  125. [READ]: { since: "4.0.0", replacedBy: null },
  126. },
  127. events: {
  128. EventEmitter: {
  129. listenerCount: {
  130. [READ]: {
  131. since: "4.0.0",
  132. replacedBy: "'events.EventEmitter#listenerCount()'",
  133. },
  134. },
  135. },
  136. listenerCount: {
  137. [READ]: {
  138. since: "4.0.0",
  139. replacedBy: "'events.EventEmitter#listenerCount()'",
  140. },
  141. },
  142. },
  143. freelist: {
  144. [READ]: { since: "4.0.0", replacedBy: null },
  145. },
  146. fs: {
  147. SyncWriteStream: {
  148. [READ]: { since: "4.0.0", replacedBy: null },
  149. },
  150. exists: {
  151. [READ]: {
  152. since: "4.0.0",
  153. replacedBy: "'fs.stat()' or 'fs.access()'",
  154. },
  155. },
  156. lchmod: {
  157. [READ]: { since: "0.4.0", replacedBy: null },
  158. },
  159. lchmodSync: {
  160. [READ]: { since: "0.4.0", replacedBy: null },
  161. },
  162. lchown: {
  163. [READ]: { since: "0.4.0", replacedBy: null },
  164. },
  165. lchownSync: {
  166. [READ]: { since: "0.4.0", replacedBy: null },
  167. },
  168. },
  169. http: {
  170. createClient: {
  171. [READ]: { since: "0.10.0", replacedBy: "'http.request()'" },
  172. },
  173. },
  174. module: {
  175. Module: {
  176. requireRepl: {
  177. [READ]: { since: "6.0.0", replacedBy: "'require(\"repl\")'" },
  178. },
  179. _debug: {
  180. [READ]: { since: "9.0.0", replacedBy: null },
  181. },
  182. },
  183. requireRepl: {
  184. [READ]: { since: "6.0.0", replacedBy: "'require(\"repl\")'" },
  185. },
  186. _debug: {
  187. [READ]: { since: "9.0.0", replacedBy: null },
  188. },
  189. },
  190. os: {
  191. getNetworkInterfaces: {
  192. [READ]: { since: "0.6.0", replacedBy: "'os.networkInterfaces()'" },
  193. },
  194. tmpDir: {
  195. [READ]: { since: "7.0.0", replacedBy: "'os.tmpdir()'" },
  196. },
  197. },
  198. path: {
  199. _makeLong: {
  200. [READ]: { since: "9.0.0", replacedBy: "'path.toNamespacedPath()'" },
  201. },
  202. },
  203. process: {
  204. EventEmitter: {
  205. [READ]: { since: "0.6.0", replacedBy: "'require(\"events\")'" },
  206. },
  207. assert: {
  208. [READ]: { since: "10.0.0", replacedBy: "'require(\"assert\")'" },
  209. },
  210. binding: {
  211. [READ]: { since: "10.9.0", replacedBy: null },
  212. },
  213. env: {
  214. NODE_REPL_HISTORY_FILE: {
  215. [READ]: { since: "4.0.0", replacedBy: "'NODE_REPL_HISTORY'" },
  216. },
  217. },
  218. },
  219. punycode: {
  220. [READ]: {
  221. since: "7.0.0",
  222. replacedBy: "'https://www.npmjs.com/package/punycode'",
  223. },
  224. },
  225. readline: {
  226. codePointAt: {
  227. [READ]: { since: "4.0.0", replacedBy: null },
  228. },
  229. getStringWidth: {
  230. [READ]: { since: "6.0.0", replacedBy: null },
  231. },
  232. isFullWidthCodePoint: {
  233. [READ]: { since: "6.0.0", replacedBy: null },
  234. },
  235. stripVTControlCharacters: {
  236. [READ]: { since: "6.0.0", replacedBy: null },
  237. },
  238. },
  239. // safe-buffer.Buffer function/constructror is just a re-export of buffer.Buffer
  240. // and should be deprecated likewise.
  241. "safe-buffer": {
  242. Buffer: {
  243. [CONSTRUCT]: {
  244. since: "6.0.0",
  245. replacedBy: "'buffer.Buffer.alloc()' or 'buffer.Buffer.from()'",
  246. },
  247. [CALL]: {
  248. since: "6.0.0",
  249. replacedBy: "'buffer.Buffer.alloc()' or 'buffer.Buffer.from()'",
  250. },
  251. },
  252. SlowBuffer: {
  253. [READ]: {
  254. since: "6.0.0",
  255. replacedBy: "'buffer.Buffer.allocUnsafeSlow()'",
  256. },
  257. },
  258. },
  259. sys: {
  260. [READ]: { since: "0.3.0", replacedBy: "'util' module" },
  261. },
  262. timers: {
  263. enroll: {
  264. [READ]: {
  265. since: "10.0.0",
  266. replacedBy: "'setTimeout()' or 'setInterval()'",
  267. },
  268. },
  269. unenroll: {
  270. [READ]: {
  271. since: "10.0.0",
  272. replacedBy: "'clearTimeout()' or 'clearInterval()'",
  273. },
  274. },
  275. },
  276. tls: {
  277. CleartextStream: {
  278. [READ]: { since: "0.10.0", replacedBy: null },
  279. },
  280. CryptoStream: {
  281. [READ]: { since: "0.12.0", replacedBy: "'tls.TLSSocket'" },
  282. },
  283. SecurePair: {
  284. [READ]: { since: "6.0.0", replacedBy: "'tls.TLSSocket'" },
  285. },
  286. convertNPNProtocols: {
  287. [READ]: { since: "10.0.0", replacedBy: null },
  288. },
  289. createSecurePair: {
  290. [READ]: { since: "6.0.0", replacedBy: "'tls.TLSSocket'" },
  291. },
  292. parseCertString: {
  293. [READ]: { since: "8.6.0", replacedBy: "'querystring.parse()'" },
  294. },
  295. },
  296. tty: {
  297. setRawMode: {
  298. [READ]: {
  299. since: "0.10.0",
  300. replacedBy:
  301. "'tty.ReadStream#setRawMode()' (e.g. 'process.stdin.setRawMode()')",
  302. },
  303. },
  304. },
  305. url: {
  306. parse: {
  307. [READ]: { since: "11.0.0", replacedBy: "'url.URL' constructor" },
  308. },
  309. resolve: {
  310. [READ]: { since: "11.0.0", replacedBy: "'url.URL' constructor" },
  311. },
  312. },
  313. util: {
  314. debug: {
  315. [READ]: { since: "0.12.0", replacedBy: "'console.error()'" },
  316. },
  317. error: {
  318. [READ]: { since: "0.12.0", replacedBy: "'console.error()'" },
  319. },
  320. isArray: {
  321. [READ]: { since: "4.0.0", replacedBy: "'Array.isArray()'" },
  322. },
  323. isBoolean: {
  324. [READ]: { since: "4.0.0", replacedBy: null },
  325. },
  326. isBuffer: {
  327. [READ]: { since: "4.0.0", replacedBy: "'Buffer.isBuffer()'" },
  328. },
  329. isDate: {
  330. [READ]: { since: "4.0.0", replacedBy: null },
  331. },
  332. isError: {
  333. [READ]: { since: "4.0.0", replacedBy: null },
  334. },
  335. isFunction: {
  336. [READ]: { since: "4.0.0", replacedBy: null },
  337. },
  338. isNull: {
  339. [READ]: { since: "4.0.0", replacedBy: null },
  340. },
  341. isNullOrUndefined: {
  342. [READ]: { since: "4.0.0", replacedBy: null },
  343. },
  344. isNumber: {
  345. [READ]: { since: "4.0.0", replacedBy: null },
  346. },
  347. isObject: {
  348. [READ]: { since: "4.0.0", replacedBy: null },
  349. },
  350. isPrimitive: {
  351. [READ]: { since: "4.0.0", replacedBy: null },
  352. },
  353. isRegExp: {
  354. [READ]: { since: "4.0.0", replacedBy: null },
  355. },
  356. isString: {
  357. [READ]: { since: "4.0.0", replacedBy: null },
  358. },
  359. isSymbol: {
  360. [READ]: { since: "4.0.0", replacedBy: null },
  361. },
  362. isUndefined: {
  363. [READ]: { since: "4.0.0", replacedBy: null },
  364. },
  365. log: {
  366. [READ]: { since: "6.0.0", replacedBy: "a third party module" },
  367. },
  368. print: {
  369. [READ]: { since: "0.12.0", replacedBy: "'console.log()'" },
  370. },
  371. pump: {
  372. [READ]: { since: "0.10.0", replacedBy: "'stream.Readable#pipe()'" },
  373. },
  374. puts: {
  375. [READ]: { since: "0.12.0", replacedBy: "'console.log()'" },
  376. },
  377. _extend: {
  378. [READ]: { since: "6.0.0", replacedBy: "'Object.assign()'" },
  379. },
  380. },
  381. vm: {
  382. runInDebugContext: {
  383. [READ]: { since: "8.0.0", replacedBy: null },
  384. },
  385. },
  386. }
  387. const globals = {
  388. Buffer: {
  389. [CONSTRUCT]: {
  390. since: "6.0.0",
  391. replacedBy: "'Buffer.alloc()' or 'Buffer.from()'",
  392. },
  393. [CALL]: {
  394. since: "6.0.0",
  395. replacedBy: "'Buffer.alloc()' or 'Buffer.from()'",
  396. },
  397. },
  398. COUNTER_NET_SERVER_CONNECTION: {
  399. [READ]: { since: "11.0.0", replacedBy: null },
  400. },
  401. COUNTER_NET_SERVER_CONNECTION_CLOSE: {
  402. [READ]: { since: "11.0.0", replacedBy: null },
  403. },
  404. COUNTER_HTTP_SERVER_REQUEST: {
  405. [READ]: { since: "11.0.0", replacedBy: null },
  406. },
  407. COUNTER_HTTP_SERVER_RESPONSE: {
  408. [READ]: { since: "11.0.0", replacedBy: null },
  409. },
  410. COUNTER_HTTP_CLIENT_REQUEST: {
  411. [READ]: { since: "11.0.0", replacedBy: null },
  412. },
  413. COUNTER_HTTP_CLIENT_RESPONSE: {
  414. [READ]: { since: "11.0.0", replacedBy: null },
  415. },
  416. GLOBAL: {
  417. [READ]: { since: "6.0.0", replacedBy: "'global'" },
  418. },
  419. Intl: {
  420. v8BreakIterator: {
  421. [READ]: { since: "7.0.0", replacedBy: null },
  422. },
  423. },
  424. require: {
  425. extensions: {
  426. [READ]: {
  427. since: "0.12.0",
  428. replacedBy: "compiling them ahead of time",
  429. },
  430. },
  431. },
  432. root: {
  433. [READ]: { since: "6.0.0", replacedBy: "'global'" },
  434. },
  435. process: modules.process,
  436. }
  437. /**
  438. * Makes a replacement message.
  439. *
  440. * @param {string|null} replacedBy - The text of substitute way.
  441. * @returns {string} Replacement message.
  442. */
  443. function toReplaceMessage(replacedBy) {
  444. return replacedBy ? `. Use ${replacedBy} instead` : ""
  445. }
  446. /**
  447. * Convert a given path to name.
  448. * @param {symbol} type The report type.
  449. * @param {string[]} path The property access path.
  450. * @returns {string} The name.
  451. */
  452. function toName(type, path) {
  453. const baseName = path.join(".")
  454. return type === ReferenceTracker.CALL
  455. ? `${baseName}()`
  456. : type === ReferenceTracker.CONSTRUCT
  457. ? `new ${baseName}()`
  458. : baseName
  459. }
  460. module.exports = {
  461. meta: {
  462. docs: {
  463. description: "disallow deprecated APIs",
  464. category: "Best Practices",
  465. recommended: true,
  466. url:
  467. "https://github.com/mysticatea/eslint-plugin-node/blob/v8.0.1/docs/rules/no-deprecated-api.md",
  468. },
  469. type: "problem",
  470. fixable: null,
  471. schema: [
  472. {
  473. type: "object",
  474. properties: {
  475. ignoreModuleItems: {
  476. type: "array",
  477. items: {
  478. enum: Array.from(enumeratePropertyNames(modules)),
  479. },
  480. additionalItems: false,
  481. uniqueItems: true,
  482. },
  483. ignoreGlobalItems: {
  484. type: "array",
  485. items: {
  486. enum: Array.from(enumeratePropertyNames(globals)),
  487. },
  488. additionalItems: false,
  489. uniqueItems: true,
  490. },
  491. // Deprecated since v4.2.0
  492. ignoreIndirectDependencies: { type: "boolean" },
  493. },
  494. additionalProperties: false,
  495. },
  496. ],
  497. },
  498. create(context) {
  499. const options = context.options[0] || {}
  500. const ignoredModuleItems = new Set(options.ignoreModuleItems || [])
  501. const ignoredGlobalItems = new Set(options.ignoreGlobalItems || [])
  502. /**
  503. * Reports a use of a deprecated API.
  504. *
  505. * @param {ASTNode} node - A node to report.
  506. * @param {string} name - The name of a deprecated API.
  507. * @param {{since: number, replacedBy: string}} info - Information of the API.
  508. * @returns {void}
  509. */
  510. function reportItem(node, name, info) {
  511. context.report({
  512. node,
  513. loc: node.loc,
  514. message:
  515. "{{name}} was deprecated since v{{version}}{{replace}}.",
  516. data: {
  517. name,
  518. version: info.since,
  519. replace: toReplaceMessage(info.replacedBy),
  520. },
  521. })
  522. }
  523. return {
  524. "Program:exit"() {
  525. const tracker = new ReferenceTracker(context.getScope(), {
  526. mode: "legacy",
  527. })
  528. for (const report of tracker.iterateGlobalReferences(globals)) {
  529. const { node, path, type, info } = report
  530. const name = toName(type, path)
  531. if (!ignoredGlobalItems.has(name)) {
  532. reportItem(node, `'${name}'`, info)
  533. }
  534. }
  535. for (const report of [
  536. ...tracker.iterateCjsReferences(modules),
  537. ...tracker.iterateEsmReferences(modules),
  538. ]) {
  539. const { node, path, type, info } = report
  540. const name = toName(type, path)
  541. const suffix = path.length === 1 ? " module" : ""
  542. if (!ignoredModuleItems.has(name)) {
  543. reportItem(node, `'${name}'${suffix}`, info)
  544. }
  545. }
  546. },
  547. }
  548. },
  549. }