You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
264 lines
6.3 KiB
264 lines
6.3 KiB
/* |
|
MIT License http://www.opensource.org/licenses/mit-license.php |
|
Author Tobias Koppers @sokra |
|
*/ |
|
|
|
"use strict"; |
|
|
|
const util = require("util"); |
|
|
|
/** @type {Map<string, Function>} */ |
|
const deprecationCache = new Map(); |
|
|
|
/** |
|
* @typedef {Object} FakeHookMarker |
|
* @property {true} _fakeHook it's a fake hook |
|
*/ |
|
|
|
/** @template T @typedef {T & FakeHookMarker} FakeHook<T> */ |
|
|
|
/** |
|
* @param {string} message deprecation message |
|
* @param {string} code deprecation code |
|
* @returns {Function} function to trigger deprecation |
|
*/ |
|
const createDeprecation = (message, code) => { |
|
const cached = deprecationCache.get(message); |
|
if (cached !== undefined) return cached; |
|
const fn = util.deprecate( |
|
() => {}, |
|
message, |
|
"DEP_WEBPACK_DEPRECATION_" + code |
|
); |
|
deprecationCache.set(message, fn); |
|
return fn; |
|
}; |
|
|
|
const COPY_METHODS = [ |
|
"concat", |
|
"entry", |
|
"filter", |
|
"find", |
|
"findIndex", |
|
"includes", |
|
"indexOf", |
|
"join", |
|
"lastIndexOf", |
|
"map", |
|
"reduce", |
|
"reduceRight", |
|
"slice", |
|
"some" |
|
]; |
|
|
|
const DISABLED_METHODS = [ |
|
"copyWithin", |
|
"entries", |
|
"fill", |
|
"keys", |
|
"pop", |
|
"reverse", |
|
"shift", |
|
"splice", |
|
"sort", |
|
"unshift" |
|
]; |
|
|
|
/** |
|
* @param {any} set new set |
|
* @param {string} name property name |
|
* @returns {void} |
|
*/ |
|
exports.arrayToSetDeprecation = (set, name) => { |
|
for (const method of COPY_METHODS) { |
|
if (set[method]) continue; |
|
const d = createDeprecation( |
|
`${name} was changed from Array to Set (using Array method '${method}' is deprecated)`, |
|
"ARRAY_TO_SET" |
|
); |
|
/** |
|
* @deprecated |
|
* @this {Set} |
|
* @returns {number} count |
|
*/ |
|
set[method] = function () { |
|
d(); |
|
const array = Array.from(this); |
|
return Array.prototype[method].apply(array, arguments); |
|
}; |
|
} |
|
const dPush = createDeprecation( |
|
`${name} was changed from Array to Set (using Array method 'push' is deprecated)`, |
|
"ARRAY_TO_SET_PUSH" |
|
); |
|
const dLength = createDeprecation( |
|
`${name} was changed from Array to Set (using Array property 'length' is deprecated)`, |
|
"ARRAY_TO_SET_LENGTH" |
|
); |
|
const dIndexer = createDeprecation( |
|
`${name} was changed from Array to Set (indexing Array is deprecated)`, |
|
"ARRAY_TO_SET_INDEXER" |
|
); |
|
/** |
|
* @deprecated |
|
* @this {Set} |
|
* @returns {number} count |
|
*/ |
|
set.push = function () { |
|
dPush(); |
|
for (const item of Array.from(arguments)) { |
|
this.add(item); |
|
} |
|
return this.size; |
|
}; |
|
for (const method of DISABLED_METHODS) { |
|
if (set[method]) continue; |
|
set[method] = () => { |
|
throw new Error( |
|
`${name} was changed from Array to Set (using Array method '${method}' is not possible)` |
|
); |
|
}; |
|
} |
|
const createIndexGetter = index => { |
|
/** |
|
* @this {Set} a Set |
|
* @returns {any} the value at this location |
|
*/ |
|
const fn = function () { |
|
dIndexer(); |
|
let i = 0; |
|
for (const item of this) { |
|
if (i++ === index) return item; |
|
} |
|
return undefined; |
|
}; |
|
return fn; |
|
}; |
|
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; |
|
Object.defineProperty(set, "length", { |
|
get() { |
|
dLength(); |
|
const length = this.size; |
|
for (indexerDefined; indexerDefined < length + 1; indexerDefined++) { |
|
defineIndexGetter(indexerDefined); |
|
} |
|
return length; |
|
}, |
|
set(value) { |
|
throw new Error( |
|
`${name} was changed from Array to Set (writing to Array property 'length' is not possible)` |
|
); |
|
} |
|
}); |
|
set[Symbol.isConcatSpreadable] = true; |
|
}; |
|
|
|
exports.createArrayToSetDeprecationSet = name => { |
|
let initialized = false; |
|
class SetDeprecatedArray extends Set { |
|
constructor(items) { |
|
super(items); |
|
if (!initialized) { |
|
initialized = true; |
|
exports.arrayToSetDeprecation(SetDeprecatedArray.prototype, name); |
|
} |
|
} |
|
} |
|
return SetDeprecatedArray; |
|
}; |
|
|
|
exports.soonFrozenObjectDeprecation = (obj, name, code, note = "") => { |
|
const message = `${name} will be frozen in future, all modifications are deprecated.${ |
|
note && `\n${note}` |
|
}`; |
|
return new Proxy(obj, { |
|
set: util.deprecate( |
|
(target, property, value, receiver) => |
|
Reflect.set(target, property, value, receiver), |
|
message, |
|
code |
|
), |
|
defineProperty: util.deprecate( |
|
(target, property, descriptor) => |
|
Reflect.defineProperty(target, property, descriptor), |
|
message, |
|
code |
|
), |
|
deleteProperty: util.deprecate( |
|
(target, property) => Reflect.deleteProperty(target, property), |
|
message, |
|
code |
|
), |
|
setPrototypeOf: util.deprecate( |
|
(target, proto) => Reflect.setPrototypeOf(target, proto), |
|
message, |
|
code |
|
) |
|
}); |
|
}; |
|
|
|
/** |
|
* @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(v => (value = v), message, code) |
|
: undefined |
|
}); |
|
} |
|
} |
|
return /** @type {T} */ (newObj); |
|
}; |
|
exports.deprecateAllProperties = deprecateAllProperties; |
|
|
|
/** |
|
* @template 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 |
|
*/ |
|
exports.createFakeHook = (fakeHook, message, code) => { |
|
if (message && code) { |
|
fakeHook = deprecateAllProperties(fakeHook, message, code); |
|
} |
|
return Object.freeze( |
|
Object.assign(fakeHook, { _fakeHook: /** @type {true} */ (true) }) |
|
); |
|
};
|
|
|