deprecation.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const util = require("util");
  7. /** @type {Map<string, () => void>} */
  8. const deprecationCache = new Map();
  9. /**
  10. * Defines the fake hook marker type used by this module.
  11. * @typedef {object} FakeHookMarker
  12. * @property {true} _fakeHook it's a fake hook
  13. */
  14. /**
  15. * Defines the shared type used by this module.
  16. * @template T
  17. * @typedef {T & FakeHookMarker} FakeHook<T>
  18. */
  19. /**
  20. * Creates a deprecation.
  21. * @param {string} message deprecation message
  22. * @param {string} code deprecation code
  23. * @returns {() => void} function to trigger deprecation
  24. */
  25. const createDeprecation = (message, code) => {
  26. const cached = deprecationCache.get(message);
  27. if (cached !== undefined) return cached;
  28. const fn = util.deprecate(
  29. () => {},
  30. message,
  31. `DEP_WEBPACK_DEPRECATION_${code}`
  32. );
  33. deprecationCache.set(message, fn);
  34. return fn;
  35. };
  36. /** @typedef {"concat" | "entry" | "filter" | "find" | "findIndex" | "includes" | "indexOf" | "join" | "lastIndexOf" | "map" | "reduce" | "reduceRight" | "slice" | "some"} COPY_METHODS_NAMES */
  37. /** @type {COPY_METHODS_NAMES[]} */
  38. const COPY_METHODS = [
  39. "concat",
  40. "entry",
  41. "filter",
  42. "find",
  43. "findIndex",
  44. "includes",
  45. "indexOf",
  46. "join",
  47. "lastIndexOf",
  48. "map",
  49. "reduce",
  50. "reduceRight",
  51. "slice",
  52. "some"
  53. ];
  54. /** @typedef {"copyWithin" | "entries" | "fill" | "keys" | "pop" | "reverse" | "shift" | "splice" | "sort" | "unshift"} DISABLED_METHODS_NAMES */
  55. /** @type {DISABLED_METHODS_NAMES[]} */
  56. const DISABLED_METHODS = [
  57. "copyWithin",
  58. "entries",
  59. "fill",
  60. "keys",
  61. "pop",
  62. "reverse",
  63. "shift",
  64. "splice",
  65. "sort",
  66. "unshift"
  67. ];
  68. /**
  69. * Defines the set with deprecated array methods type used by this module.
  70. * @template T
  71. * @typedef {Set<T> & { [Symbol.isConcatSpreadable]: boolean } & { push: (...items: T[]) => void, length?: number } & { [P in DISABLED_METHODS_NAMES]: () => void } & { [P in COPY_METHODS_NAMES]: P extends keyof Array<T> ? () => Pick<Array<T>, P> : never }} SetWithDeprecatedArrayMethods
  72. */
  73. /**
  74. * Processes the provided set.
  75. * @template T
  76. * @param {Set<T>} set new set
  77. * @param {string} name property name
  78. * @returns {void}
  79. */
  80. module.exports.arrayToSetDeprecation = (set, name) => {
  81. for (const method of COPY_METHODS) {
  82. if (/** @type {SetWithDeprecatedArrayMethods<T>} */ (set)[method]) continue;
  83. const d = createDeprecation(
  84. `${name} was changed from Array to Set (using Array method '${method}' is deprecated)`,
  85. "ARRAY_TO_SET"
  86. );
  87. /** @type {EXPECTED_ANY} */
  88. (set)[method] =
  89. // eslint-disable-next-line func-names
  90. function () {
  91. d();
  92. // eslint-disable-next-line unicorn/prefer-spread
  93. const array = Array.from(this);
  94. return Array.prototype[
  95. /** @type {keyof COPY_METHODS} */ (method)
  96. ].apply(
  97. array,
  98. // eslint-disable-next-line prefer-rest-params
  99. arguments
  100. );
  101. };
  102. }
  103. const dPush = createDeprecation(
  104. `${name} was changed from Array to Set (using Array method 'push' is deprecated)`,
  105. "ARRAY_TO_SET_PUSH"
  106. );
  107. const dLength = createDeprecation(
  108. `${name} was changed from Array to Set (using Array property 'length' is deprecated)`,
  109. "ARRAY_TO_SET_LENGTH"
  110. );
  111. const dIndexer = createDeprecation(
  112. `${name} was changed from Array to Set (indexing Array is deprecated)`,
  113. "ARRAY_TO_SET_INDEXER"
  114. );
  115. /** @type {SetWithDeprecatedArrayMethods<T>} */
  116. (set).push = function push() {
  117. dPush();
  118. // eslint-disable-next-line prefer-rest-params, unicorn/prefer-spread
  119. for (const item of Array.from(arguments)) {
  120. this.add(item);
  121. }
  122. return this.size;
  123. };
  124. for (const method of DISABLED_METHODS) {
  125. if (/** @type {SetWithDeprecatedArrayMethods<T>} */ (set)[method]) continue;
  126. /** @type {SetWithDeprecatedArrayMethods<T>} */
  127. (set)[method] = () => {
  128. throw new Error(
  129. `${name} was changed from Array to Set (using Array method '${method}' is not possible)`
  130. );
  131. };
  132. }
  133. /**
  134. * Creates an index getter.
  135. * @param {number} index index
  136. * @returns {() => T | undefined} value
  137. */
  138. const createIndexGetter = (index) => {
  139. /**
  140. * Returns the value at this location.
  141. * @this {Set<T>} a Set
  142. * @returns {T | undefined} the value at this location
  143. */
  144. // eslint-disable-next-line func-style
  145. const fn = function () {
  146. dIndexer();
  147. let i = 0;
  148. for (const item of this) {
  149. if (i++ === index) return item;
  150. }
  151. };
  152. return fn;
  153. };
  154. /**
  155. * Define index getter.
  156. * @param {number} index index
  157. */
  158. const defineIndexGetter = (index) => {
  159. Object.defineProperty(set, index, {
  160. get: createIndexGetter(index),
  161. set(value) {
  162. throw new Error(
  163. `${name} was changed from Array to Set (indexing Array with write is not possible)`
  164. );
  165. }
  166. });
  167. };
  168. defineIndexGetter(0);
  169. let indexerDefined = 1;
  170. Object.defineProperty(set, "length", {
  171. get() {
  172. dLength();
  173. const length = this.size;
  174. for (indexerDefined; indexerDefined < length + 1; indexerDefined++) {
  175. defineIndexGetter(indexerDefined);
  176. }
  177. return length;
  178. },
  179. set(value) {
  180. throw new Error(
  181. `${name} was changed from Array to Set (writing to Array property 'length' is not possible)`
  182. );
  183. }
  184. });
  185. /** @type {SetWithDeprecatedArrayMethods<T>} */
  186. (set)[Symbol.isConcatSpreadable] = true;
  187. };
  188. /**
  189. * Returns } SetDeprecatedArray.
  190. * @template T
  191. * @param {string} name name
  192. * @returns {{ new <T = EXPECTED_ANY>(values?: ReadonlyArray<T> | null): SetDeprecatedArray<T> }} SetDeprecatedArray
  193. */
  194. module.exports.createArrayToSetDeprecationSet = (name) => {
  195. let initialized = false;
  196. /**
  197. * Represents SetDeprecatedArray.
  198. * @template T
  199. */
  200. class SetDeprecatedArray extends Set {
  201. /**
  202. * Creates an instance of SetDeprecatedArray.
  203. * @param {ReadonlyArray<T> | null=} items items
  204. */
  205. constructor(items) {
  206. super(items);
  207. if (!initialized) {
  208. initialized = true;
  209. module.exports.arrayToSetDeprecation(
  210. /** @type {SetWithDeprecatedArrayMethods<T>} */
  211. (SetDeprecatedArray.prototype),
  212. name
  213. );
  214. }
  215. }
  216. }
  217. return SetDeprecatedArray;
  218. };
  219. /**
  220. * Returns fake hook which redirects.
  221. * @template {object} T
  222. * @param {T} fakeHook fake hook implementation
  223. * @param {string=} message deprecation message (not deprecated when unset)
  224. * @param {string=} code deprecation code (not deprecated when unset)
  225. * @returns {FakeHook<T>} fake hook which redirects
  226. */
  227. module.exports.createFakeHook = (fakeHook, message, code) => {
  228. if (message && code) {
  229. fakeHook = deprecateAllProperties(fakeHook, message, code);
  230. }
  231. return Object.freeze(
  232. Object.assign(fakeHook, { _fakeHook: /** @type {true} */ (true) })
  233. );
  234. };
  235. /**
  236. * Deprecate all properties.
  237. * @template T
  238. * @param {T} obj object
  239. * @param {string} message deprecation message
  240. * @param {string} code deprecation code
  241. * @returns {T} object with property access deprecated
  242. */
  243. const deprecateAllProperties = (obj, message, code) => {
  244. const newObj = {};
  245. const descriptors = Object.getOwnPropertyDescriptors(obj);
  246. for (const name of Object.keys(descriptors)) {
  247. const descriptor = descriptors[name];
  248. if (typeof descriptor.value === "function") {
  249. Object.defineProperty(newObj, name, {
  250. ...descriptor,
  251. value: util.deprecate(descriptor.value, message, code)
  252. });
  253. } else if (descriptor.get || descriptor.set) {
  254. Object.defineProperty(newObj, name, {
  255. ...descriptor,
  256. get: descriptor.get && util.deprecate(descriptor.get, message, code),
  257. set: descriptor.set && util.deprecate(descriptor.set, message, code)
  258. });
  259. } else {
  260. let value = descriptor.value;
  261. Object.defineProperty(newObj, name, {
  262. configurable: descriptor.configurable,
  263. enumerable: descriptor.enumerable,
  264. get: util.deprecate(() => value, message, code),
  265. set: descriptor.writable
  266. ? util.deprecate(
  267. /**
  268. * Handles the callback logic for this hook.
  269. * @template T
  270. * @param {T} v value
  271. * @returns {T} result
  272. */
  273. (v) => (value = v),
  274. message,
  275. code
  276. )
  277. : undefined
  278. });
  279. }
  280. }
  281. return /** @type {T} */ (newObj);
  282. };
  283. module.exports.deprecateAllProperties = deprecateAllProperties;
  284. /**
  285. * Returns frozen object with deprecation when modifying.
  286. * @template {object} T
  287. * @param {T} obj object
  288. * @param {string} name property name
  289. * @param {string} code deprecation code
  290. * @param {string} note additional note
  291. * @returns {T} frozen object with deprecation when modifying
  292. */
  293. module.exports.soonFrozenObjectDeprecation = (obj, name, code, note = "") => {
  294. const message = `${name} will be frozen in future, all modifications are deprecated.${
  295. note && `\n${note}`
  296. }`;
  297. return /** @type {T} */ (
  298. new Proxy(obj, {
  299. set: util.deprecate(
  300. /**
  301. * Handles the callback logic for this hook.
  302. * @param {object} target target
  303. * @param {string | symbol} property property
  304. * @param {EXPECTED_ANY} value value
  305. * @param {EXPECTED_ANY} receiver receiver
  306. * @returns {boolean} result
  307. */
  308. (target, property, value, receiver) =>
  309. Reflect.set(target, property, value, receiver),
  310. message,
  311. code
  312. ),
  313. defineProperty: util.deprecate(
  314. /**
  315. * Handles the define property callback for this hook.
  316. * @param {object} target target
  317. * @param {string | symbol} property property
  318. * @param {PropertyDescriptor} descriptor descriptor
  319. * @returns {boolean} result
  320. */
  321. (target, property, descriptor) =>
  322. Reflect.defineProperty(target, property, descriptor),
  323. message,
  324. code
  325. ),
  326. deleteProperty: util.deprecate(
  327. /**
  328. * Handles the delete property callback for this hook.
  329. * @param {object} target target
  330. * @param {string | symbol} property property
  331. * @returns {boolean} result
  332. */
  333. (target, property) => Reflect.deleteProperty(target, property),
  334. message,
  335. code
  336. ),
  337. setPrototypeOf: util.deprecate(
  338. /**
  339. * Updates prototype of using the provided target.
  340. * @param {object} target target
  341. * @param {EXPECTED_OBJECT | null} proto proto
  342. * @returns {boolean} result
  343. */
  344. (target, proto) => Reflect.setPrototypeOf(target, proto),
  345. message,
  346. code
  347. )
  348. })
  349. );
  350. };