webpack/lib/util/deprecation.js

351 lines
9.1 KiB
JavaScript
Raw Normal View History

2018-09-06 22:57:32 +08:00
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const util = require("util");
2025-03-14 00:34:04 +08:00
/** @type {Map<string, () => void>} */
2018-09-12 19:07:36 +08:00
const deprecationCache = new Map();
/**
2024-06-11 21:09:50 +08:00
* @typedef {object} FakeHookMarker
* @property {true} _fakeHook it's a fake hook
*/
2024-08-07 23:22:25 +08:00
/**
* @template T
* @typedef {T & FakeHookMarker} FakeHook<T>
*/
2018-09-12 19:07:36 +08:00
/**
* @param {string} message deprecation message
2019-11-14 22:01:25 +08:00
* @param {string} code deprecation code
2025-03-14 00:34:04 +08:00
* @returns {() => void} function to trigger deprecation
2018-09-12 19:07:36 +08:00
*/
2019-11-14 22:01:25 +08:00
const createDeprecation = (message, code) => {
2018-09-12 19:07:36 +08:00
const cached = deprecationCache.get(message);
if (cached !== undefined) return cached;
2019-11-14 22:01:25 +08:00
const fn = util.deprecate(
() => {},
message,
2024-07-31 10:39:30 +08:00
`DEP_WEBPACK_DEPRECATION_${code}`
2019-11-14 22:01:25 +08:00
);
2018-09-12 19:07:36 +08:00
deprecationCache.set(message, fn);
return fn;
};
2025-03-27 08:07:25 +08:00
/** @typedef {"concat" | "entry" | "filter" | "find" | "findIndex" | "includes" | "indexOf" | "join" | "lastIndexOf" | "map" | "reduce" | "reduceRight" | "slice" | "some"} COPY_METHODS_NAMES */
/** @type {COPY_METHODS_NAMES[]} */
2018-09-06 22:57:32 +08:00
const COPY_METHODS = [
"concat",
"entry",
"filter",
"find",
"findIndex",
"includes",
"indexOf",
"join",
"lastIndexOf",
"map",
"reduce",
"reduceRight",
"slice",
"some"
];
2025-03-27 08:07:25 +08:00
/** @typedef {"copyWithin" | "entries" | "fill" | "keys" | "pop" | "reverse" | "shift" | "splice" | "sort" | "unshift"} DISABLED_METHODS_NAMES */
/** @type {DISABLED_METHODS_NAMES[]} */
2018-09-06 22:57:32 +08:00
const DISABLED_METHODS = [
"copyWithin",
"entries",
"fill",
"keys",
"pop",
"reverse",
"shift",
"splice",
"sort",
"unshift"
];
/**
2025-03-27 08:07:25 +08:00
* @template T
2025-09-09 23:41:52 +08:00
* @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
2025-03-27 08:07:25 +08:00
*/
/**
* @template T
2025-09-09 23:41:52 +08:00
* @param {Set<T>} set new set
2018-09-06 22:57:32 +08:00
* @param {string} name property name
* @returns {void}
*/
2024-07-31 04:54:55 +08:00
module.exports.arrayToSetDeprecation = (set, name) => {
2018-09-06 22:57:32 +08:00
for (const method of COPY_METHODS) {
2025-09-09 23:41:52 +08:00
if (/** @type {SetWithDeprecatedArrayMethods<T>} */ (set)[method]) continue;
2018-09-12 19:07:36 +08:00
const d = createDeprecation(
2019-11-14 22:01:25 +08:00
`${name} was changed from Array to Set (using Array method '${method}' is deprecated)`,
"ARRAY_TO_SET"
2018-09-06 22:57:32 +08:00
);
2025-09-09 23:41:52 +08:00
/** @type {EXPECTED_ANY} */
(set)[method] =
// eslint-disable-next-line func-names
function () {
d();
// eslint-disable-next-line unicorn/prefer-spread
const array = Array.from(this);
return Array.prototype[
/** @type {keyof COPY_METHODS} */ (method)
].apply(
array,
// eslint-disable-next-line prefer-rest-params
arguments
);
};
2018-09-12 19:07:36 +08:00
}
const dPush = createDeprecation(
2019-11-14 22:01:25 +08:00
`${name} was changed from Array to Set (using Array method 'push' is deprecated)`,
"ARRAY_TO_SET_PUSH"
2018-09-06 22:57:32 +08:00
);
const dLength = createDeprecation(
2019-11-14 22:01:25 +08:00
`${name} was changed from Array to Set (using Array property 'length' is deprecated)`,
"ARRAY_TO_SET_LENGTH"
);
const dIndexer = createDeprecation(
2019-11-14 22:01:25 +08:00
`${name} was changed from Array to Set (indexing Array is deprecated)`,
"ARRAY_TO_SET_INDEXER"
);
2025-09-09 23:41:52 +08:00
/** @type {SetWithDeprecatedArrayMethods<T>} */
(set).push = function push() {
2018-09-12 19:07:36 +08:00
dPush();
2025-07-03 17:06:45 +08:00
// eslint-disable-next-line prefer-rest-params, unicorn/prefer-spread
2018-09-12 19:07:36 +08:00
for (const item of Array.from(arguments)) {
this.add(item);
}
return this.size;
};
2018-09-06 22:57:32 +08:00
for (const method of DISABLED_METHODS) {
2025-09-09 23:41:52 +08:00
if (/** @type {SetWithDeprecatedArrayMethods<T>} */ (set)[method]) continue;
2025-03-27 08:07:25 +08:00
2025-09-09 23:41:52 +08:00
/** @type {SetWithDeprecatedArrayMethods<T>} */
(set)[method] = () => {
2018-09-06 22:57:32 +08:00
throw new Error(
2018-09-12 19:07:36 +08:00
`${name} was changed from Array to Set (using Array method '${method}' is not possible)`
2018-09-06 22:57:32 +08:00
);
};
}
2023-06-12 22:21:21 +08:00
/**
* @param {number} index index
2025-03-27 08:07:25 +08:00
* @returns {() => T | undefined} value
2023-06-12 22:21:21 +08:00
*/
const createIndexGetter = (index) => {
/**
2025-03-27 08:07:25 +08:00
* @this {Set<T>} a Set
* @returns {T | undefined} the value at this location
*/
2024-07-31 11:50:02 +08:00
// eslint-disable-next-line func-style
2020-03-29 06:10:15 +08:00
const fn = function () {
dIndexer();
let i = 0;
for (const item of this) {
if (i++ === index) return item;
}
};
return fn;
};
2023-06-12 22:21:21 +08:00
/**
* @param {number} index index
*/
const defineIndexGetter = (index) => {
Object.defineProperty(set, index, {
get: createIndexGetter(index),
set(value) {
throw new Error(
`${name} was changed from Array to Set (indexing Array with write is not possible)`
);
}
});
};
defineIndexGetter(0);
let indexerDefined = 1;
2018-09-06 22:57:32 +08:00
Object.defineProperty(set, "length", {
get() {
dLength();
const length = this.size;
for (indexerDefined; indexerDefined < length + 1; indexerDefined++) {
defineIndexGetter(indexerDefined);
}
return length;
2018-09-06 22:57:32 +08:00
},
set(value) {
throw new Error(
2018-09-12 19:07:36 +08:00
`${name} was changed from Array to Set (writing to Array property 'length' is not possible)`
2018-09-06 22:57:32 +08:00
);
}
});
2025-09-09 23:41:52 +08:00
/** @type {SetWithDeprecatedArrayMethods<T>} */
(set)[Symbol.isConcatSpreadable] = true;
2018-09-06 22:57:32 +08:00
};
2024-10-24 05:41:15 +08:00
/**
* @template T
* @param {string} name name
* @returns {{ new <T = any>(values?: readonly T[] | null): SetDeprecatedArray<T> }} SetDeprecatedArray
*/
module.exports.createArrayToSetDeprecationSet = (name) => {
let initialized = false;
2024-10-24 05:41:15 +08:00
/**
* @template T
*/
class SetDeprecatedArray extends Set {
2024-10-24 05:41:15 +08:00
/**
* @param {readonly T[] | null=} items items
*/
constructor(items) {
super(items);
if (!initialized) {
initialized = true;
2024-07-31 04:54:55 +08:00
module.exports.arrayToSetDeprecation(
2025-09-09 23:41:52 +08:00
/** @type {SetWithDeprecatedArrayMethods<T>} */
(SetDeprecatedArray.prototype),
2024-07-31 04:54:55 +08:00
name
);
}
}
}
return SetDeprecatedArray;
};
2025-07-03 17:06:45 +08:00
/**
* @template {object} T
* @param {T} fakeHook fake hook implementation
* @param {string=} message deprecation message (not deprecated when unset)
* @param {string=} code deprecation code (not deprecated when unset)
* @returns {FakeHook<T>} fake hook which redirects
*/
module.exports.createFakeHook = (fakeHook, message, code) => {
if (message && code) {
fakeHook = deprecateAllProperties(fakeHook, message, code);
}
return Object.freeze(
Object.assign(fakeHook, { _fakeHook: /** @type {true} */ (true) })
);
};
/**
* @template T
* @param {T} obj object
* @param {string} message deprecation message
* @param {string} code deprecation code
* @returns {T} object with property access deprecated
*/
const deprecateAllProperties = (obj, message, code) => {
const newObj = {};
const descriptors = Object.getOwnPropertyDescriptors(obj);
for (const name of Object.keys(descriptors)) {
const descriptor = descriptors[name];
if (typeof descriptor.value === "function") {
Object.defineProperty(newObj, name, {
...descriptor,
value: util.deprecate(descriptor.value, message, code)
});
} else if (descriptor.get || descriptor.set) {
Object.defineProperty(newObj, name, {
...descriptor,
get: descriptor.get && util.deprecate(descriptor.get, message, code),
set: descriptor.set && util.deprecate(descriptor.set, message, code)
});
} else {
let value = descriptor.value;
Object.defineProperty(newObj, name, {
configurable: descriptor.configurable,
enumerable: descriptor.enumerable,
get: util.deprecate(() => value, message, code),
set: descriptor.writable
? util.deprecate(
/**
* @template T
* @param {T} v value
* @returns {T} result
*/
(v) => (value = v),
2025-07-03 17:06:45 +08:00
message,
code
)
: undefined
});
}
}
return /** @type {T} */ (newObj);
};
module.exports.deprecateAllProperties = deprecateAllProperties;
2023-05-01 04:51:59 +08:00
/**
2024-10-24 05:41:15 +08:00
* @template {object} T
* @param {T} obj object
2023-05-01 04:51:59 +08:00
* @param {string} name property name
* @param {string} code deprecation code
* @param {string} note additional note
* @returns {T} frozen object with deprecation when modifying
2023-05-01 04:51:59 +08:00
*/
2024-07-31 04:54:55 +08:00
module.exports.soonFrozenObjectDeprecation = (obj, name, code, note = "") => {
const message = `${name} will be frozen in future, all modifications are deprecated.${
note && `\n${note}`
}`;
return /** @type {T} */ (
new Proxy(obj, {
2024-10-24 05:41:15 +08:00
set: util.deprecate(
/**
* @param {object} target target
2024-10-24 05:41:15 +08:00
* @param {string | symbol} property property
2025-03-07 21:12:22 +08:00
* @param {EXPECTED_ANY} value value
* @param {EXPECTED_ANY} receiver receiver
2024-10-24 05:41:15 +08:00
* @returns {boolean} result
*/
(target, property, value, receiver) =>
2025-03-27 08:07:25 +08:00
Reflect.set(target, property, value, receiver),
2024-10-24 05:41:15 +08:00
message,
code
),
defineProperty: util.deprecate(
/**
* @param {object} target target
2024-10-24 05:41:15 +08:00
* @param {string | symbol} property property
* @param {PropertyDescriptor} descriptor descriptor
* @returns {boolean} result
*/
(target, property, descriptor) =>
2025-03-27 08:07:25 +08:00
Reflect.defineProperty(target, property, descriptor),
2024-10-24 05:41:15 +08:00
message,
code
),
deleteProperty: util.deprecate(
/**
* @param {object} target target
2024-10-24 05:41:15 +08:00
* @param {string | symbol} property property
* @returns {boolean} result
*/
2025-03-27 08:07:25 +08:00
(target, property) => Reflect.deleteProperty(target, property),
2024-10-24 05:41:15 +08:00
message,
code
),
setPrototypeOf: util.deprecate(
/**
* @param {object} target target
2025-03-27 08:07:25 +08:00
* @param {EXPECTED_OBJECT | null} proto proto
2024-10-24 05:41:15 +08:00
* @returns {boolean} result
*/
2025-03-27 08:07:25 +08:00
(target, proto) => Reflect.setPrototypeOf(target, proto),
2024-10-24 05:41:15 +08:00
message,
code
)
})
);
};