deprecation.js 8.8 KB

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