deprecation.js 8.1 KB

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