polyfill!

This commit is contained in:
Miao Wang 2024-04-10 13:50:43 +08:00
parent 279e69e60f
commit bb9370aee7
5 changed files with 549 additions and 2 deletions

View File

@ -0,0 +1,495 @@
// Constant variables
var UNDEFINED; // => undefined
var PROXY_TARGET = "[[ProxyTarget]]";
var PROXY_HANDLER = "[[ProxyHandler]]";
var GET = "[[Get]]";
var SET = "[[Set]]";
var CALL = "[[Call]]";
var CONSTRUCT = "[[Construct]]";
var PROTOTYPE = "__proto__";
var PROXY_FLAG = "__PROXY__";
var REVOKED_FLAG = "REVOKED";
var defineProperty = Object.defineProperty;
var defineProperties = Object.defineProperties;
var getPrototypeOf = Object.getPrototypeOf;
var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
var supportES5 = defineProperties ? isNativeFn(defineProperties) : false;
/**
* Return the prototype of proxy object
* @param {object} obj
* @returns {object}
*/
var getProxyProto = supportES5
? Object[PROTOTYPE]
? getPrototypeOf
: function (obj) {
return typeof obj === "function"
? obj[PROTOTYPE] || {}
: getPrototypeOf(obj);
}
: function (obj) {
return (_isVbObject(obj) && _getVbInternalOf(obj)[PROTOTYPE]) || {};
};
/**
* Check if `value` is a pristine native function
* @param {any} value
* @returns {boolean}
*/
function isNativeFn(value) {
return (
typeof value === "function" && /\[native code\]/.test(value.toString())
);
}
/**
* The Proxy constructor
* @constructor
* @param {object} target
* @param {object} handler
*/
function ProxyPolyfill(target, handler) {
if (this instanceof ProxyPolyfill) {
return createProxy(new Internal(target, handler));
} else {
throwTypeError("Constructor Proxy requires 'new'");
}
}
/**
* Create a revocable Proxy object
* @param {object} target
* @param {object} handler
* @returns {{proxy: object, revoke: function}}
*/
ProxyPolyfill.revocable = function (target, handler) {
if (this instanceof ProxyPolyfill.revocable) {
throwTypeError("Proxy.revocable is not a constructor");
}
var internal = new Internal(target, handler);
var proxy = createProxy(internal);
return {
proxy: proxy,
revoke: function () {
internal[PROXY_TARGET] = UNDEFINED;
internal[PROXY_HANDLER] = UNDEFINED;
if (!supportES5) {
getProxyProto(proxy)[PROXY_FLAG] = REVOKED_FLAG;
}
},
};
};
/**
* Create a Proxy object
* @param {Internal} internal
* @returns {object}
*/
function createProxy(internal) {
var target = internal[PROXY_TARGET];
var proxy;
if (typeof target === "function") {
proxy = proxyFunction(internal);
} else if (target instanceof Array) {
proxy = proxyArray(internal);
} else {
proxy = proxyObject(internal);
}
return proxy;
}
/**
* Internal data
* @constructor
* @param {object} target
* @param {object} handler
*/
function Internal(target, handler) {
if (!isObject(target) || !isObject(handler)) {
throwTypeError(
"Cannot create proxy with a non-object as target or handler",
);
}
if (
(getProxyProto(target) && getProxyProto(target)[PROXY_FLAG]) ||
(getProxyProto(handler) && getProxyProto(handler)[PROXY_FLAG]) ===
REVOKED_FLAG
) {
throwTypeError(
"Cannot create proxy with a revoked proxy as target or handler",
);
}
this[PROXY_TARGET] = target;
this[PROXY_HANDLER] = handler;
}
/**
* The implementation of internal method [[Get]]
* @param {string} property
* @param {object} receiver
* @returns {any}
*/
Internal.prototype[GET] = function (property, receiver) {
var handler = this[PROXY_HANDLER];
validateProxyHanler(handler, "get");
if (handler.get == UNDEFINED) {
return this[PROXY_TARGET][property];
}
if (typeof handler.get === "function") {
return handler.get(this[PROXY_TARGET], property, receiver);
}
throwTypeError("Trap 'get' is not a function: " + handler.get);
};
/**
* The implementation of internal method [[Set]]
* @param {string} property
* @param {any} value
* @param {object} receiver
*/
Internal.prototype[SET] = function (property, value, receiver) {
var handler = this[PROXY_HANDLER];
validateProxyHanler(handler, "set");
if (handler.set == UNDEFINED) {
this[PROXY_TARGET][property] = value;
} else if (typeof handler.set === "function") {
var result = handler.set(this[PROXY_TARGET], property, value, receiver);
if (!result) {
// If the set() method returns false in strict-mode code, a TypeError will be thrown.
// throwTypeError("Trap 'set' returned false for property '" + property + "'");
console.warn("Trap 'set' returned false for property '" + property + "'");
}
return Boolean(result);
} else {
throwTypeError("Trap 'set' is not a function: " + handler.set);
}
};
/**
* The implementation of internal method [[Call]]
* @param {object} thisArg
* @param {any[]} argList
* @returns {any}
*/
Internal.prototype[CALL] = function (thisArg, argList) {
var handler = this[PROXY_HANDLER];
validateProxyHanler(handler, "apply");
if (handler.apply == UNDEFINED) {
return this[PROXY_TARGET].apply(thisArg, argList);
}
if (typeof handler.apply === "function") {
return handler.apply(this[PROXY_TARGET], thisArg, argList);
}
throwTypeError("Trap 'apply' is not a function: " + handler.apply);
};
/**
* The implementation of internal method [[Construct]]
* @param {any[]} argList
* @param {object} newTarget
* @returns {object}
*/
Internal.prototype[CONSTRUCT] = function (argList, newTarget) {
var handler = this[PROXY_HANDLER];
validateProxyHanler(handler, "construct");
var newObj;
if (handler.construct == UNDEFINED) {
newObj = evaluateNew(this[PROXY_TARGET], argList);
} else if (typeof handler.construct === "function") {
newObj = handler.construct(this[PROXY_TARGET], argList, newTarget);
} else {
throwTypeError("Trap 'construct' is not a function: " + handler.construct);
}
if (isObject(newObj)) {
return newObj;
} else {
throwTypeError("Trap 'construct' returned non-object: " + newObj);
}
};
/**
* Validate the proxy hanler
* @param {object} handler
* @param {string} trap
*/
function validateProxyHanler(handler, trap) {
if (!handler) {
throwTypeError(
"Cannot perform '" + trap + "' on a proxy that has been revoked",
);
}
}
/**
* Call constructor with 'new'
* @param {function} F constructor
* @param {any[]} argList
* @returns {object}
*/
function evaluateNew(F, argList) {
var params = [];
for (var i = 0, len = argList.length; i < len; ++i) {
params.push("args[" + i + "]");
}
var executor = new Function(
"Ctor",
"args",
"return new Ctor(" + params.join(", ") + ")",
);
return executor(F, argList);
}
/**
* Throw a type error
* @param {string} message
*/
function throwTypeError(message) {
throw new TypeError(message);
}
/**
* Check if value is the language type of Object
* @param {any} value
* @returns {boolean}
*/
function isObject(value) {
return value
? typeof value === "object" || typeof value === "function"
: false;
}
/**
* Check if key is an own property of object
* @param {object} obj
* @param {string} key
* @returns {boolean}
*/
function hasOwnProperty(obj, key) {
return Object.prototype.hasOwnProperty.call(obj, key);
}
/**
* Hack `Object.getOwnPropertyNames`
*/
var getOwnPropertyNames =
Object.getOwnPropertyNames ||
function (obj) {
var names = [];
for (var key in obj) {
if (hasOwnProperty(obj, key)) {
names.push(key);
}
}
return names;
};
/**
* Set the prototype of function
* @param {function} fn
* @param {object} proto
* @returns {object}
*/
var setFuncProto = isNativeFn(Object.setPrototypeOf)
? Object.setPrototypeOf
: Object[PROTOTYPE]
? function (fn, proto) {
fn[PROTOTYPE] = proto;
return fn;
}
: function (fn, proto) {
return defineProperty(fn, PROTOTYPE, { value: proto });
};
/**
* Hack `Object.create`
*/
var objectCreate = supportES5
? Object.create
: function (_, props) {
var obj = defineProperties({}, props);
if (_isVbObject(obj)) {
var proto = {};
proto[PROXY_FLAG] = UNDEFINED;
_getVbInternalOf(obj)[PROTOTYPE] = proto;
}
return obj;
};
/**
* Hack `Object.assign`
*/
var objectAssign =
Object.assign ||
function (target, source) {
for (var key in source) {
if (hasOwnProperty(source, key)) {
target[key] = source[key];
}
}
return target;
};
/**
* Proxy function
* @param {Internal} internal
* @returns {function}
*/
function proxyFunction(internal) {
var target = internal[PROXY_TARGET];
function P() {
return this instanceof P
? internal[CONSTRUCT](arguments, P)
: internal[CALL](this, arguments);
}
P.prototype = target.prototype; // `prototype` is not configurable
if (supportES5) {
var descMap = observeProto(internal);
var newProto = objectCreate(getPrototypeOf(target), descMap);
setFuncProto(P, newProto);
descMap = observeProperties(target, internal);
for (var key in descMap) {
if (hasOwnProperty(P, key)) delete descMap[key];
}
defineProperties(P, descMap);
} else {
objectAssign(P, target);
}
return P;
}
/**
* Proxy array
* @param {Internal} internal
* @returns {object} array-like object
*/
function proxyArray(internal) {
var target = internal[PROXY_TARGET];
var descMap, newProto;
if (supportES5) {
descMap = observeProto(internal);
// Fix: `concat` does not work correctly on array-like object
descMap.concat.get = function () {
var val = internal[GET]("concat", this);
return val === Array.prototype.concat ? val.bind(target) : val;
};
newProto = objectCreate(getPrototypeOf(target), descMap);
}
descMap = observeProperties(target, internal);
// Observe the change of `length`, and synchronize
// the properties of Proxy object to target array
descMap.length.set = function (value) {
var lenDiff = value - target.length;
internal[SET]("length", value, this);
if (lenDiff) syncArrayElement(lenDiff, this, internal);
};
return objectCreate(newProto, descMap);
}
/**
* Proxy object
* @param {Internal} internal
* @returns {object}
*/
function proxyObject(internal) {
var target = internal[PROXY_TARGET];
var descMap, newProto;
if (supportES5) {
descMap = observeProto(internal);
newProto = objectCreate(getPrototypeOf(target), descMap);
}
descMap = observeProperties(target, internal);
return objectCreate(newProto, descMap);
}
/**
* Observe [[Prototype]]
* @param {Internal} internal
* @returns {object} descriptors
*/
function observeProto(internal) {
var descMap = {};
var proto = internal[PROXY_TARGET];
while ((proto = getPrototypeOf(proto))) {
var props = observeProperties(proto, internal);
objectAssign(descMap, props);
}
descMap[PROXY_FLAG] = {
get: function () {
return internal[PROXY_TARGET] ? UNDEFINED : REVOKED_FLAG;
},
};
return descMap;
}
/**
* Observe properties
* @param {object} obj
* @param {Internal} internal
* @returns {object} descriptors
*/
function observeProperties(obj, internal) {
var names = getOwnPropertyNames(obj);
var descMap = {};
for (var i = names.length - 1; i >= 0; --i) {
descMap[names[i]] = observeProperty(obj, names[i], internal);
}
return descMap;
}
/**
* Observe property
* @param {object} obj
* @param {string} prop
* @param {Internal} internal
* @returns {{get: function, set: function, enumerable: boolean, configurable: boolean}}
*/
function observeProperty(obj, prop, internal) {
var desc = getOwnPropertyDescriptor(obj, prop);
return {
get: function () {
return internal[GET](prop, this);
},
set: function (value) {
internal[SET](prop, value, this);
},
enumerable: desc.enumerable,
configurable: desc.configurable,
};
}
/**
* Sync array element from P to target
* @param {number} lenDiff
* @param {object} P
* @param {Internal} internal
*/
function syncArrayElement(lenDiff, P, internal) {
var target = internal[PROXY_TARGET];
if (lenDiff > 0) {
for (var tLen = target.length, i = tLen - lenDiff; i < tLen; ++i) {
var desc = getOwnPropertyDescriptor(P, i);
if (desc) defineProperty(target, i, desc);
else target[i] = UNDEFINED;
desc = observeProperty(target, i, internal);
defineProperty(P, i, desc);
}
} else {
for (var i = target.length, pLen = i - lenDiff; i < pLen; ++i) {
delete P[i];
}
}
}
export default typeof Proxy === "undefined" ? ProxyPolyfill : Proxy;

View File

@ -0,0 +1,14 @@
import Es6ProxyPolyfill from "./es6-proxy-polyfill.js";
import "promise-polyfill/src/polyfill";
import "whatwg-fetch";
import "events-polyfill";
import "element-polyfill";
import "@webcomponents/template/template.min.js";
const globalObj =
(typeof globalThis !== "undefined" && globalThis) ||
(typeof self !== "undefined" && self) ||
// eslint-disable-next-line no-undef
(typeof global !== "undefined" && global) ||
{};
globalObj.Proxy = typeof Proxy === "undefined" ? Es6ProxyPolyfill : Proxy;

View File

@ -107,6 +107,9 @@ export default defineConfig(({mode})=>({
}),
legacy({
targets: [],
additionalLegacyPolyfills: [
resolve(__dirname, '_src/lib/legacy-polyfill.js'),
],
}),
visualizer({
filename: '_stats.html',

32
package-lock.json generated
View File

@ -8,12 +8,16 @@
"dependencies": {
"@vitejs/plugin-legacy": "^5.3.2",
"@vitejs/plugin-vue": "^5.0.4",
"@webcomponents/template": "^1.5.1",
"bootstrap": "^5.3.3",
"element-polyfill": "^1.1.0",
"events-polyfill": "^2.1.2",
"highlight.js": "^11.9.0",
"lato-webfont": "^2.15.1",
"liquidjs": "^10.10.2",
"markup-js": "^1.5.21",
"mustache": "^4.2.0",
"promise-polyfill": "^8.3.0",
"sass": "^1.74.1",
"sass-cast": "^0.5.6",
"source-code-pro": "^2.38.0",
@ -22,7 +26,8 @@
"unplugin-vue-components": "^0.26.0",
"vite": "^5.2.8",
"vite-plugin-ruby": "^5.0.0",
"webfontloader": "^1.6.28"
"webfontloader": "^1.6.28",
"whatwg-fetch": "^3.6.20"
},
"devDependencies": {
"prettier": "^3.2.5",
@ -2393,6 +2398,11 @@
"integrity": "sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==",
"peer": true
},
"node_modules/@webcomponents/template": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/@webcomponents/template/-/template-1.5.1.tgz",
"integrity": "sha512-3e8bx+bgRhyuRwFrDGu7CalILomo11ixuMzVGvpXSxL8lX+ijCIG6J3kS4O/7nElVuKE7vkuXB1xD+RICDNCCg=="
},
"node_modules/acorn": {
"version": "8.11.3",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
@ -2728,6 +2738,11 @@
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.729.tgz",
"integrity": "sha512-bx7+5Saea/qu14kmPTDHQxkp2UnziG3iajUQu3BxFvCOnpAJdDbMV4rSl+EqFDkkpNNVUFlR1kDfpL59xfy1HA=="
},
"node_modules/element-polyfill": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/element-polyfill/-/element-polyfill-1.1.0.tgz",
"integrity": "sha512-t+hZSDS8by+uMyFwGXo8cn6Jf5VM7AjY+p2ZGg1s/E7vA3VmuVFJjKJO3GIPAgH3HfUm/9codl8yGo3GgcMH2A=="
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
@ -2812,6 +2827,11 @@
"node": ">=0.10.0"
}
},
"node_modules/events-polyfill": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/events-polyfill/-/events-polyfill-2.1.2.tgz",
"integrity": "sha512-vx4kpGzymyD3CEjmg2wTQA6k5e0RhGTkX3ZwfC9m/Ol7+me2tbVuJ0GjSd8eIJxFioubicA0nUL0SIOAyfrgZA=="
},
"node_modules/fast-glob": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
@ -3290,6 +3310,11 @@
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/promise-polyfill": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.3.0.tgz",
"integrity": "sha512-H5oELycFml5yto/atYqmjyigJoAo3+OXwolYiH7OfQuYlAqhxNvTfiNMbV9hsC6Yp83yE5r2KTVmtrG6R9i6Pg=="
},
"node_modules/queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@ -3885,6 +3910,11 @@
"resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.1.tgz",
"integrity": "sha512-poXpCylU7ExuvZK8z+On3kX+S8o/2dQ/SVYueKA0D4WEMXROXgY8Ez50/bQEUmvoSMMrWcrJqCHuhAbsiwg7Dg=="
},
"node_modules/whatwg-fetch": {
"version": "3.6.20",
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz",
"integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg=="
},
"node_modules/wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",

View File

@ -8,12 +8,16 @@
"dependencies": {
"@vitejs/plugin-legacy": "^5.3.2",
"@vitejs/plugin-vue": "^5.0.4",
"@webcomponents/template": "^1.5.1",
"bootstrap": "^5.3.3",
"element-polyfill": "^1.1.0",
"events-polyfill": "^2.1.2",
"highlight.js": "^11.9.0",
"lato-webfont": "^2.15.1",
"liquidjs": "^10.10.2",
"markup-js": "^1.5.21",
"mustache": "^4.2.0",
"promise-polyfill": "^8.3.0",
"sass": "^1.74.1",
"sass-cast": "^0.5.6",
"source-code-pro": "^2.38.0",
@ -22,7 +26,8 @@
"unplugin-vue-components": "^0.26.0",
"vite": "^5.2.8",
"vite-plugin-ruby": "^5.0.0",
"webfontloader": "^1.6.28"
"webfontloader": "^1.6.28",
"whatwg-fetch": "^3.6.20"
},
"devDependencies": {
"prettier": "^3.2.5",