mirror of
https://github.com/pyscript/pyscript.git
synced 2025-12-19 18:27:29 -05:00
Breaking: new Polyscript Hooks mechanism (#1811)
* Breaking: new Polyscript Hooks mechanism * Added proper smoke test
This commit is contained in:
committed by
GitHub
parent
28d37cdead
commit
e67eb06d8b
67
pyscript.core/package-lock.json
generated
67
pyscript.core/package-lock.json
generated
@@ -1,18 +1,19 @@
|
|||||||
{
|
{
|
||||||
"name": "@pyscript/core",
|
"name": "@pyscript/core",
|
||||||
"version": "0.2.10",
|
"version": "0.3.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@pyscript/core",
|
"name": "@pyscript/core",
|
||||||
"version": "0.2.10",
|
"version": "0.3.0",
|
||||||
"license": "APACHE-2.0",
|
"license": "APACHE-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ungap/with-resolvers": "^0.1.0",
|
"@ungap/with-resolvers": "^0.1.0",
|
||||||
"basic-devtools": "^0.1.6",
|
"basic-devtools": "^0.1.6",
|
||||||
"polyscript": "^0.4.20",
|
"polyscript": "^0.5.1",
|
||||||
"sticky-module": "^0.1.0",
|
"sticky-module": "^0.1.0",
|
||||||
|
"to-json-callback": "^0.1.1",
|
||||||
"type-checked-collections": "^0.1.7"
|
"type-checked-collections": "^0.1.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -20,7 +21,7 @@
|
|||||||
"@rollup/plugin-terser": "^0.4.4",
|
"@rollup/plugin-terser": "^0.4.4",
|
||||||
"@webreflection/toml-j0.4": "^1.1.3",
|
"@webreflection/toml-j0.4": "^1.1.3",
|
||||||
"chokidar": "^3.5.3",
|
"chokidar": "^3.5.3",
|
||||||
"eslint": "^8.51.0",
|
"eslint": "^8.52.0",
|
||||||
"rollup": "^4.1.4",
|
"rollup": "^4.1.4",
|
||||||
"rollup-plugin-postcss": "^4.0.2",
|
"rollup-plugin-postcss": "^4.0.2",
|
||||||
"rollup-plugin-string": "^3.0.0",
|
"rollup-plugin-string": "^3.0.0",
|
||||||
@@ -85,21 +86,21 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/js": {
|
"node_modules/@eslint/js": {
|
||||||
"version": "8.51.0",
|
"version": "8.52.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.51.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.52.0.tgz",
|
||||||
"integrity": "sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg==",
|
"integrity": "sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@humanwhocodes/config-array": {
|
"node_modules/@humanwhocodes/config-array": {
|
||||||
"version": "0.11.11",
|
"version": "0.11.13",
|
||||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz",
|
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz",
|
||||||
"integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==",
|
"integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@humanwhocodes/object-schema": "^1.2.1",
|
"@humanwhocodes/object-schema": "^2.0.1",
|
||||||
"debug": "^4.1.1",
|
"debug": "^4.1.1",
|
||||||
"minimatch": "^3.0.5"
|
"minimatch": "^3.0.5"
|
||||||
},
|
},
|
||||||
@@ -121,9 +122,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@humanwhocodes/object-schema": {
|
"node_modules/@humanwhocodes/object-schema": {
|
||||||
"version": "1.2.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz",
|
||||||
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
|
"integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@jridgewell/gen-mapping": {
|
"node_modules/@jridgewell/gen-mapping": {
|
||||||
@@ -764,12 +765,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/coincident": {
|
"node_modules/coincident": {
|
||||||
"version": "0.13.10",
|
"version": "0.14.2",
|
||||||
"resolved": "https://registry.npmjs.org/coincident/-/coincident-0.13.10.tgz",
|
"resolved": "https://registry.npmjs.org/coincident/-/coincident-0.14.2.tgz",
|
||||||
"integrity": "sha512-gzSTDRMMVvo40+zj7sRUFMsc5Fpu865qn4gf/FUAaukBal+c2lxvnTigT7HSZLGPYEj6Sm5vPDTik31Uqei3qg==",
|
"integrity": "sha512-Xc/lh56dl/v5GT1R3bEWxiLzF5ZTiXE5Flcd0+qvrBGhZsvDha8bgqhpocrvJmELuerhDO3+EQKDdCzPBPodJQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ungap/structured-clone": "^1.2.0",
|
"@ungap/structured-clone": "^1.2.0",
|
||||||
"@ungap/with-resolvers": "^0.1.0"
|
"@ungap/with-resolvers": "^0.1.0",
|
||||||
|
"gc-hook": "^0.2.1"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"ws": "^8.14.2"
|
"ws": "^8.14.2"
|
||||||
@@ -1126,18 +1128,19 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint": {
|
"node_modules/eslint": {
|
||||||
"version": "8.51.0",
|
"version": "8.52.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.51.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.52.0.tgz",
|
||||||
"integrity": "sha512-2WuxRZBrlwnXi+/vFSJyjMqrNjtJqiasMzehF0shoLaW7DzS3/9Yvrmq5JiT66+pNjiX4UBnLDiKHcWAr/OInA==",
|
"integrity": "sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.2.0",
|
"@eslint-community/eslint-utils": "^4.2.0",
|
||||||
"@eslint-community/regexpp": "^4.6.1",
|
"@eslint-community/regexpp": "^4.6.1",
|
||||||
"@eslint/eslintrc": "^2.1.2",
|
"@eslint/eslintrc": "^2.1.2",
|
||||||
"@eslint/js": "8.51.0",
|
"@eslint/js": "8.52.0",
|
||||||
"@humanwhocodes/config-array": "^0.11.11",
|
"@humanwhocodes/config-array": "^0.11.13",
|
||||||
"@humanwhocodes/module-importer": "^1.0.1",
|
"@humanwhocodes/module-importer": "^1.0.1",
|
||||||
"@nodelib/fs.walk": "^1.2.8",
|
"@nodelib/fs.walk": "^1.2.8",
|
||||||
|
"@ungap/structured-clone": "^1.2.0",
|
||||||
"ajv": "^6.12.4",
|
"ajv": "^6.12.4",
|
||||||
"chalk": "^4.0.0",
|
"chalk": "^4.0.0",
|
||||||
"cross-spawn": "^7.0.2",
|
"cross-spawn": "^7.0.2",
|
||||||
@@ -1385,6 +1388,11 @@
|
|||||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/gc-hook": {
|
||||||
|
"version": "0.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/gc-hook/-/gc-hook-0.2.3.tgz",
|
||||||
|
"integrity": "sha512-XfA+XiE7QzJzonfvNjTXmcyGtprD6m85KEH9fngmxghVR7PJ4f3ZKsLI22QNpWJiDO5EIR6iMzz9wSxLBqw6+A=="
|
||||||
|
},
|
||||||
"node_modules/generic-names": {
|
"node_modules/generic-names": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/generic-names/-/generic-names-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/generic-names/-/generic-names-4.0.0.tgz",
|
||||||
@@ -2053,15 +2061,15 @@
|
|||||||
"integrity": "sha512-yyVAOFKTAElc7KdLt2+UKGExNYwYb/Y/WE9i+1ezCQsJE8gbKSjewfpRqK2nQgZ4d4hhAAGgDCOcIZVilqE5UA=="
|
"integrity": "sha512-yyVAOFKTAElc7KdLt2+UKGExNYwYb/Y/WE9i+1ezCQsJE8gbKSjewfpRqK2nQgZ4d4hhAAGgDCOcIZVilqE5UA=="
|
||||||
},
|
},
|
||||||
"node_modules/polyscript": {
|
"node_modules/polyscript": {
|
||||||
"version": "0.4.20",
|
"version": "0.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/polyscript/-/polyscript-0.4.20.tgz",
|
"resolved": "https://registry.npmjs.org/polyscript/-/polyscript-0.5.1.tgz",
|
||||||
"integrity": "sha512-MNpZQPcJ0047Gq/SXZTw8H929nMKcc3dqxbegd42remBJ0y2UwrKXiE0NMhlO85cbxLrV+sGD2ZCt8+/2m55EQ==",
|
"integrity": "sha512-jMFFWCJjYKcan7RV5UBVNg9IfVSss7wB6l5RIdx28pnBu1WSyA7sApLz7NEVbF14g5a5OjwtlvZCphQEA4CfQQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ungap/structured-clone": "^1.2.0",
|
"@ungap/structured-clone": "^1.2.0",
|
||||||
"@ungap/with-resolvers": "^0.1.0",
|
"@ungap/with-resolvers": "^0.1.0",
|
||||||
"basic-devtools": "^0.1.6",
|
"basic-devtools": "^0.1.6",
|
||||||
"codedent": "^0.1.2",
|
"codedent": "^0.1.2",
|
||||||
"coincident": "^0.13.10",
|
"coincident": "^0.14.2",
|
||||||
"html-escaper": "^3.0.3",
|
"html-escaper": "^3.0.3",
|
||||||
"sticky-module": "^0.1.0"
|
"sticky-module": "^0.1.0"
|
||||||
}
|
}
|
||||||
@@ -3092,6 +3100,11 @@
|
|||||||
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
|
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/to-json-callback": {
|
||||||
|
"version": "0.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/to-json-callback/-/to-json-callback-0.1.1.tgz",
|
||||||
|
"integrity": "sha512-BzOeinTT3NjE+FJ2iCvWB8HvyuyBzoH3WlSnJ+AYVC4tlePyZWSYdkQIFOARWiq0t35/XhmI0uQsFiUsRksRqg=="
|
||||||
|
},
|
||||||
"node_modules/to-regex-range": {
|
"node_modules/to-regex-range": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@pyscript/core",
|
"name": "@pyscript/core",
|
||||||
"version": "0.2.10",
|
"version": "0.3.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "PyScript",
|
"description": "PyScript",
|
||||||
"module": "./index.js",
|
"module": "./index.js",
|
||||||
@@ -40,8 +40,9 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ungap/with-resolvers": "^0.1.0",
|
"@ungap/with-resolvers": "^0.1.0",
|
||||||
"basic-devtools": "^0.1.6",
|
"basic-devtools": "^0.1.6",
|
||||||
"polyscript": "^0.4.20",
|
"polyscript": "^0.5.1",
|
||||||
"sticky-module": "^0.1.0",
|
"sticky-module": "^0.1.0",
|
||||||
|
"to-json-callback": "^0.1.1",
|
||||||
"type-checked-collections": "^0.1.7"
|
"type-checked-collections": "^0.1.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -49,7 +50,7 @@
|
|||||||
"@rollup/plugin-terser": "^0.4.4",
|
"@rollup/plugin-terser": "^0.4.4",
|
||||||
"@webreflection/toml-j0.4": "^1.1.3",
|
"@webreflection/toml-j0.4": "^1.1.3",
|
||||||
"chokidar": "^3.5.3",
|
"chokidar": "^3.5.3",
|
||||||
"eslint": "^8.51.0",
|
"eslint": "^8.52.0",
|
||||||
"rollup": "^4.1.4",
|
"rollup": "^4.1.4",
|
||||||
"rollup-plugin-postcss": "^4.0.2",
|
"rollup-plugin-postcss": "^4.0.2",
|
||||||
"rollup-plugin-string": "^3.0.0",
|
"rollup-plugin-string": "^3.0.0",
|
||||||
|
|||||||
@@ -14,16 +14,18 @@ import {
|
|||||||
dispatch,
|
dispatch,
|
||||||
queryTarget,
|
queryTarget,
|
||||||
unescape,
|
unescape,
|
||||||
|
whenDefined,
|
||||||
} from "polyscript/exports";
|
} from "polyscript/exports";
|
||||||
|
|
||||||
import "./all-done.js";
|
import "./all-done.js";
|
||||||
import TYPES from "./types.js";
|
import TYPES from "./types.js";
|
||||||
import configs from "./config.js";
|
import configs from "./config.js";
|
||||||
import hooks from "./hooks.js";
|
|
||||||
import sync from "./sync.js";
|
import sync from "./sync.js";
|
||||||
import stdlib from "./stdlib.js";
|
import stdlib from "./stdlib.js";
|
||||||
|
import bootstrapNodeAndPlugins from "./plugins-helper.js";
|
||||||
import { ErrorCode } from "./exceptions.js";
|
import { ErrorCode } from "./exceptions.js";
|
||||||
import { robustFetch as fetch, getText } from "./fetch.js";
|
import { robustFetch as fetch, getText } from "./fetch.js";
|
||||||
|
import { hooks, main, worker, codeFor, createFunction } from "./hooks.js";
|
||||||
|
|
||||||
// allows lazy element features on code evaluation
|
// allows lazy element features on code evaluation
|
||||||
let currentElement;
|
let currentElement;
|
||||||
@@ -31,25 +33,6 @@ let currentElement;
|
|||||||
// generic helper to disambiguate between custom element and script
|
// generic helper to disambiguate between custom element and script
|
||||||
const isScript = ({ tagName }) => tagName === "SCRIPT";
|
const isScript = ({ tagName }) => tagName === "SCRIPT";
|
||||||
|
|
||||||
// helper for all script[type="py"] out there
|
|
||||||
const before = (script) => {
|
|
||||||
defineProperty(document, "currentScript", {
|
|
||||||
configurable: true,
|
|
||||||
get: () => script,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const after = () => {
|
|
||||||
delete document.currentScript;
|
|
||||||
};
|
|
||||||
|
|
||||||
// common life-cycle handlers for any node
|
|
||||||
const bootstrapNodeAndPlugins = (wrap, element, callback, hook) => {
|
|
||||||
// make it possible to reach the current target node via Python
|
|
||||||
callback(element);
|
|
||||||
for (const fn of hooks[hook]) fn(wrap, element);
|
|
||||||
};
|
|
||||||
|
|
||||||
let shouldRegister = true;
|
let shouldRegister = true;
|
||||||
const registerModule = ({ XWorker: $XWorker, interpreter, io }) => {
|
const registerModule = ({ XWorker: $XWorker, interpreter, io }) => {
|
||||||
// automatically use the pyscript stderr (when/if defined)
|
// automatically use the pyscript stderr (when/if defined)
|
||||||
@@ -73,33 +56,31 @@ const registerModule = ({ XWorker: $XWorker, interpreter, io }) => {
|
|||||||
interpreter.runPython(stdlib, { globals: interpreter.runPython("{}") });
|
interpreter.runPython(stdlib, { globals: interpreter.runPython("{}") });
|
||||||
};
|
};
|
||||||
|
|
||||||
const workerHooks = {
|
|
||||||
codeBeforeRunWorker: () =>
|
|
||||||
[stdlib, ...hooks.codeBeforeRunWorker].map(dedent).join("\n"),
|
|
||||||
codeBeforeRunWorkerAsync: () =>
|
|
||||||
[stdlib, ...hooks.codeBeforeRunWorkerAsync].map(dedent).join("\n"),
|
|
||||||
codeAfterRunWorker: () =>
|
|
||||||
[...hooks.codeAfterRunWorker].map(dedent).join("\n"),
|
|
||||||
codeAfterRunWorkerAsync: () =>
|
|
||||||
[...hooks.codeAfterRunWorkerAsync].map(dedent).join("\n"),
|
|
||||||
};
|
|
||||||
|
|
||||||
// avoid multiple initialization of the same library
|
// avoid multiple initialization of the same library
|
||||||
const [
|
const [
|
||||||
{
|
{
|
||||||
PyWorker: exportedPyWorker,
|
PyWorker: exportedPyWorker,
|
||||||
hooks: exportedHooks,
|
hooks: exportedHooks,
|
||||||
config: exportedConfig,
|
config: exportedConfig,
|
||||||
|
whenDefined: exportedWhenDefined,
|
||||||
},
|
},
|
||||||
alreadyLive,
|
alreadyLive,
|
||||||
] = stickyModule("@pyscript/core", { PyWorker, hooks, config: {} });
|
] = stickyModule("@pyscript/core", {
|
||||||
|
PyWorker,
|
||||||
|
hooks,
|
||||||
|
config: {},
|
||||||
|
whenDefined,
|
||||||
|
});
|
||||||
|
|
||||||
export {
|
export {
|
||||||
exportedPyWorker as PyWorker,
|
exportedPyWorker as PyWorker,
|
||||||
exportedHooks as hooks,
|
exportedHooks as hooks,
|
||||||
exportedConfig as config,
|
exportedConfig as config,
|
||||||
|
exportedWhenDefined as whenDefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const hooked = new Map();
|
||||||
|
|
||||||
for (const [TYPE, interpreter] of TYPES) {
|
for (const [TYPE, interpreter] of TYPES) {
|
||||||
// avoid any dance if the module already landed
|
// avoid any dance if the module already landed
|
||||||
if (alreadyLive) break;
|
if (alreadyLive) break;
|
||||||
@@ -149,107 +130,141 @@ for (const [TYPE, interpreter] of TYPES) {
|
|||||||
// possible early errors sent by polyscript
|
// possible early errors sent by polyscript
|
||||||
const errors = new Map();
|
const errors = new Map();
|
||||||
|
|
||||||
|
// specific main and worker hooks
|
||||||
|
const hooks = {
|
||||||
|
main: {
|
||||||
|
...codeFor(main),
|
||||||
|
async onReady(wrap, element) {
|
||||||
|
if (shouldRegister) {
|
||||||
|
shouldRegister = false;
|
||||||
|
registerModule(wrap);
|
||||||
|
}
|
||||||
|
|
||||||
|
// allows plugins to do whatever they want with the element
|
||||||
|
// before regular stuff happens in here
|
||||||
|
for (const callback of main("onReady"))
|
||||||
|
await callback(wrap, element);
|
||||||
|
|
||||||
|
// now that all possible plugins are configured,
|
||||||
|
// bail out if polyscript encountered an error
|
||||||
|
if (errors.has(element)) {
|
||||||
|
let { message } = errors.get(element);
|
||||||
|
errors.delete(element);
|
||||||
|
const clone = message === INVALID_CONTENT;
|
||||||
|
message = `(${ErrorCode.CONFLICTING_CODE}) ${message} for `;
|
||||||
|
message += element.cloneNode(clone).outerHTML;
|
||||||
|
wrap.io.stderr(message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isScript(element)) {
|
||||||
|
const {
|
||||||
|
attributes: { async: isAsync, target },
|
||||||
|
} = element;
|
||||||
|
const hasTarget = !!target?.value;
|
||||||
|
const show = hasTarget
|
||||||
|
? queryTarget(element, target.value)
|
||||||
|
: document.createElement("script-py");
|
||||||
|
|
||||||
|
if (!hasTarget) {
|
||||||
|
const { head, body } = document;
|
||||||
|
if (head.contains(element)) body.append(show);
|
||||||
|
else element.after(show);
|
||||||
|
}
|
||||||
|
if (!show.id) show.id = getID();
|
||||||
|
|
||||||
|
// allows the code to retrieve the target element via
|
||||||
|
// document.currentScript.target if needed
|
||||||
|
defineProperty(element, "target", { value: show });
|
||||||
|
|
||||||
|
// notify before the code runs
|
||||||
|
dispatch(element, TYPE, "ready");
|
||||||
|
dispatchDone(
|
||||||
|
element,
|
||||||
|
isAsync,
|
||||||
|
wrap[`run${isAsync ? "Async" : ""}`](
|
||||||
|
await fetchSource(element, wrap.io, true),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// resolve PyScriptElement to allow connectedCallback
|
||||||
|
element._wrap.resolve(wrap);
|
||||||
|
}
|
||||||
|
console.debug("[pyscript/main] PyScript Ready");
|
||||||
|
},
|
||||||
|
onWorker(_, xworker) {
|
||||||
|
assign(xworker.sync, sync);
|
||||||
|
for (const callback of main("onWorker"))
|
||||||
|
callback(_, xworker);
|
||||||
|
},
|
||||||
|
onBeforeRun(wrap, element) {
|
||||||
|
currentElement = element;
|
||||||
|
bootstrapNodeAndPlugins(
|
||||||
|
main,
|
||||||
|
wrap,
|
||||||
|
element,
|
||||||
|
"onBeforeRun",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onBeforeRunAsync(wrap, element) {
|
||||||
|
currentElement = element;
|
||||||
|
return bootstrapNodeAndPlugins(
|
||||||
|
main,
|
||||||
|
wrap,
|
||||||
|
element,
|
||||||
|
"onBeforeRunAsync",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onAfterRun(wrap, element) {
|
||||||
|
bootstrapNodeAndPlugins(
|
||||||
|
main,
|
||||||
|
wrap,
|
||||||
|
element,
|
||||||
|
"onAfterRun",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onAfterRunAsync(wrap, element) {
|
||||||
|
return bootstrapNodeAndPlugins(
|
||||||
|
main,
|
||||||
|
wrap,
|
||||||
|
element,
|
||||||
|
"onAfterRunAsync",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
worker: {
|
||||||
|
...codeFor(worker),
|
||||||
|
// these are lazy getters that returns a composition
|
||||||
|
// of the current hooks or undefined, if no hook is present
|
||||||
|
get onReady() {
|
||||||
|
return createFunction(this, "onReady", true);
|
||||||
|
},
|
||||||
|
get onBeforeRun() {
|
||||||
|
return createFunction(this, "onBeforeRun", false);
|
||||||
|
},
|
||||||
|
get onBeforeRunAsync() {
|
||||||
|
return createFunction(this, "onBeforeRunAsync", true);
|
||||||
|
},
|
||||||
|
get onAfterRun() {
|
||||||
|
return createFunction(this, "onAfterRun", false);
|
||||||
|
},
|
||||||
|
get onAfterRunAsync() {
|
||||||
|
return createFunction(this, "onAfterRunAsync", true);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
hooked.set(TYPE, hooks);
|
||||||
|
|
||||||
define(TYPE, {
|
define(TYPE, {
|
||||||
config,
|
config,
|
||||||
interpreter,
|
interpreter,
|
||||||
|
hooks,
|
||||||
env: `${TYPE}-script`,
|
env: `${TYPE}-script`,
|
||||||
version: config?.interpreter,
|
version: config?.interpreter,
|
||||||
onerror(error, element) {
|
onerror(error, element) {
|
||||||
errors.set(element, error);
|
errors.set(element, error);
|
||||||
},
|
},
|
||||||
...workerHooks,
|
|
||||||
onWorkerReady(_, xworker) {
|
|
||||||
assign(xworker.sync, sync);
|
|
||||||
for (const callback of hooks.onWorkerReady)
|
|
||||||
callback(_, xworker);
|
|
||||||
},
|
|
||||||
onBeforeRun(wrap, element) {
|
|
||||||
currentElement = element;
|
|
||||||
bootstrapNodeAndPlugins(
|
|
||||||
wrap,
|
|
||||||
element,
|
|
||||||
before,
|
|
||||||
"onBeforeRun",
|
|
||||||
);
|
|
||||||
},
|
|
||||||
onBeforeRunAsync(wrap, element) {
|
|
||||||
currentElement = element;
|
|
||||||
bootstrapNodeAndPlugins(
|
|
||||||
wrap,
|
|
||||||
element,
|
|
||||||
before,
|
|
||||||
"onBeforeRunAsync",
|
|
||||||
);
|
|
||||||
},
|
|
||||||
onAfterRun(wrap, element) {
|
|
||||||
bootstrapNodeAndPlugins(wrap, element, after, "onAfterRun");
|
|
||||||
},
|
|
||||||
onAfterRunAsync(wrap, element) {
|
|
||||||
bootstrapNodeAndPlugins(
|
|
||||||
wrap,
|
|
||||||
element,
|
|
||||||
after,
|
|
||||||
"onAfterRunAsync",
|
|
||||||
);
|
|
||||||
},
|
|
||||||
async onInterpreterReady(wrap, element) {
|
|
||||||
if (shouldRegister) {
|
|
||||||
shouldRegister = false;
|
|
||||||
registerModule(wrap);
|
|
||||||
}
|
|
||||||
|
|
||||||
// allows plugins to do whatever they want with the element
|
|
||||||
// before regular stuff happens in here
|
|
||||||
for (const callback of hooks.onInterpreterReady)
|
|
||||||
callback(wrap, element);
|
|
||||||
|
|
||||||
// now that all possible plugins are configured,
|
|
||||||
// bail out if polyscript encountered an error
|
|
||||||
if (errors.has(element)) {
|
|
||||||
let { message } = errors.get(element);
|
|
||||||
errors.delete(element);
|
|
||||||
const clone = message === INVALID_CONTENT;
|
|
||||||
message = `(${ErrorCode.CONFLICTING_CODE}) ${message} for `;
|
|
||||||
message += element.cloneNode(clone).outerHTML;
|
|
||||||
wrap.io.stderr(message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isScript(element)) {
|
|
||||||
const {
|
|
||||||
attributes: { async: isAsync, target },
|
|
||||||
} = element;
|
|
||||||
const hasTarget = !!target?.value;
|
|
||||||
const show = hasTarget
|
|
||||||
? queryTarget(element, target.value)
|
|
||||||
: document.createElement("script-py");
|
|
||||||
|
|
||||||
if (!hasTarget) {
|
|
||||||
const { head, body } = document;
|
|
||||||
if (head.contains(element)) body.append(show);
|
|
||||||
else element.after(show);
|
|
||||||
}
|
|
||||||
if (!show.id) show.id = getID();
|
|
||||||
|
|
||||||
// allows the code to retrieve the target element via
|
|
||||||
// document.currentScript.target if needed
|
|
||||||
defineProperty(element, "target", { value: show });
|
|
||||||
|
|
||||||
// notify before the code runs
|
|
||||||
dispatch(element, TYPE, "ready");
|
|
||||||
dispatchDone(
|
|
||||||
element,
|
|
||||||
isAsync,
|
|
||||||
wrap[`run${isAsync ? "Async" : ""}`](
|
|
||||||
await fetchSource(element, wrap.io, true),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// resolve PyScriptElement to allow connectedCallback
|
|
||||||
element._wrap.resolve(wrap);
|
|
||||||
}
|
|
||||||
console.debug("[pyscript/main] PyScript Ready");
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
customElements.define(
|
customElements.define(
|
||||||
@@ -305,12 +320,13 @@ for (const [TYPE, interpreter] of TYPES) {
|
|||||||
* @returns {Worker & {sync: ProxyHandler<object>}}
|
* @returns {Worker & {sync: ProxyHandler<object>}}
|
||||||
*/
|
*/
|
||||||
function PyWorker(file, options) {
|
function PyWorker(file, options) {
|
||||||
|
const hooks = hooked.get("py");
|
||||||
// this propagates pyscript worker hooks without needing a pyscript
|
// this propagates pyscript worker hooks without needing a pyscript
|
||||||
// bootstrap + it passes arguments and enforces `pyodide`
|
// bootstrap + it passes arguments and enforces `pyodide`
|
||||||
// as the interpreter to use in the worker, as all hooks assume that
|
// as the interpreter to use in the worker, as all hooks assume that
|
||||||
// and as `pyodide` is the only default interpreter that can deal with
|
// and as `pyodide` is the only default interpreter that can deal with
|
||||||
// all the features we need to deliver pyscript out there.
|
// all the features we need to deliver pyscript out there.
|
||||||
const xworker = XWorker.call(new Hook(null, workerHooks), file, {
|
const xworker = XWorker.call(new Hook(null, hooks), file, {
|
||||||
type: "pyodide",
|
type: "pyodide",
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,28 +1,91 @@
|
|||||||
import { typedSet } from "type-checked-collections";
|
import { typedSet } from "type-checked-collections";
|
||||||
|
import { dedent } from "polyscript/exports";
|
||||||
|
import toJSONCallback from "to-json-callback";
|
||||||
|
|
||||||
|
import stdlib from "./stdlib.js";
|
||||||
|
|
||||||
|
export const main = (name) => hooks.main[name];
|
||||||
|
export const worker = (name) => hooks.worker[name];
|
||||||
|
|
||||||
|
const code = (hooks, branch, key, lib) => {
|
||||||
|
hooks[key] = () => {
|
||||||
|
const arr = lib ? [lib] : [];
|
||||||
|
arr.push(...branch(key));
|
||||||
|
return arr.map(dedent).join("\n");
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const codeFor = (branch) => {
|
||||||
|
const hooks = {};
|
||||||
|
code(hooks, branch, `codeBeforeRun`, stdlib);
|
||||||
|
code(hooks, branch, `codeBeforeRunAsync`, stdlib);
|
||||||
|
code(hooks, branch, `codeAfterRun`);
|
||||||
|
code(hooks, branch, `codeAfterRunAsync`);
|
||||||
|
return hooks;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createFunction = (self, name) => {
|
||||||
|
const cbs = [...worker(name)];
|
||||||
|
if (cbs.length) {
|
||||||
|
const cb = toJSONCallback(
|
||||||
|
self[`_${name}`] ||
|
||||||
|
(name.endsWith("Async")
|
||||||
|
? async (wrap, xworker, ...cbs) => {
|
||||||
|
for (const cb of cbs) await cb(wrap, xworker);
|
||||||
|
}
|
||||||
|
: (wrap, xworker, ...cbs) => {
|
||||||
|
for (const cb of cbs) cb(wrap, xworker);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
const a = cbs.map(toJSONCallback).join(", ");
|
||||||
|
return Function(`return(w,x)=>(${cb})(w,x,...[${a}])`)();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const SetFunction = typedSet({ typeof: "function" });
|
const SetFunction = typedSet({ typeof: "function" });
|
||||||
const SetString = typedSet({ typeof: "string" });
|
const SetString = typedSet({ typeof: "string" });
|
||||||
|
|
||||||
export default {
|
export const hooks = {
|
||||||
/** @type {Set<function>} */
|
main: {
|
||||||
onInterpreterReady: new SetFunction(),
|
/** @type {Set<function>} */
|
||||||
/** @type {Set<function>} */
|
onWorker: new SetFunction(),
|
||||||
onBeforeRun: new SetFunction(),
|
/** @type {Set<function>} */
|
||||||
/** @type {Set<function>} */
|
onReady: new SetFunction(),
|
||||||
onBeforeRunAsync: new SetFunction(),
|
/** @type {Set<function>} */
|
||||||
/** @type {Set<function>} */
|
onBeforeRun: new SetFunction(),
|
||||||
onAfterRun: new SetFunction(),
|
/** @type {Set<function>} */
|
||||||
/** @type {Set<function>} */
|
onBeforeRunAsync: new SetFunction(),
|
||||||
onAfterRunAsync: new SetFunction(),
|
/** @type {Set<function>} */
|
||||||
|
onAfterRun: new SetFunction(),
|
||||||
/** @type {Set<function>} */
|
/** @type {Set<function>} */
|
||||||
onWorkerReady: new SetFunction(),
|
onAfterRunAsync: new SetFunction(),
|
||||||
/** @type {Set<string>} */
|
/** @type {Set<string>} */
|
||||||
codeBeforeRunWorker: new SetString(),
|
codeBeforeRun: new SetString(),
|
||||||
/** @type {Set<string>} */
|
/** @type {Set<string>} */
|
||||||
codeBeforeRunWorkerAsync: new SetString(),
|
codeBeforeRunAsync: new SetString(),
|
||||||
/** @type {Set<string>} */
|
/** @type {Set<string>} */
|
||||||
codeAfterRunWorker: new SetString(),
|
codeAfterRun: new SetString(),
|
||||||
/** @type {Set<string>} */
|
/** @type {Set<string>} */
|
||||||
codeAfterRunWorkerAsync: new SetString(),
|
codeAfterRunAsync: new SetString(),
|
||||||
|
},
|
||||||
|
worker: {
|
||||||
|
/** @type {Set<function>} */
|
||||||
|
onReady: new SetFunction(),
|
||||||
|
/** @type {Set<function>} */
|
||||||
|
onBeforeRun: new SetFunction(),
|
||||||
|
/** @type {Set<function>} */
|
||||||
|
onBeforeRunAsync: new SetFunction(),
|
||||||
|
/** @type {Set<function>} */
|
||||||
|
onAfterRun: new SetFunction(),
|
||||||
|
/** @type {Set<function>} */
|
||||||
|
onAfterRunAsync: new SetFunction(),
|
||||||
|
/** @type {Set<string>} */
|
||||||
|
codeBeforeRun: new SetString(),
|
||||||
|
/** @type {Set<string>} */
|
||||||
|
codeBeforeRunAsync: new SetString(),
|
||||||
|
/** @type {Set<string>} */
|
||||||
|
codeAfterRun: new SetString(),
|
||||||
|
/** @type {Set<string>} */
|
||||||
|
codeAfterRunAsync: new SetString(),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
26
pyscript.core/src/plugins-helper.js
Normal file
26
pyscript.core/src/plugins-helper.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { defineProperty } from "polyscript/exports";
|
||||||
|
|
||||||
|
// helper for all script[type="py"] out there
|
||||||
|
const before = (script) => {
|
||||||
|
defineProperty(document, "currentScript", {
|
||||||
|
configurable: true,
|
||||||
|
get: () => script,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const after = () => {
|
||||||
|
delete document.currentScript;
|
||||||
|
};
|
||||||
|
|
||||||
|
// common life-cycle handlers for any node
|
||||||
|
export default async (main, wrap, element, hook) => {
|
||||||
|
const isAsync = hook.endsWith("Async");
|
||||||
|
const isBefore = hook.startsWith("onBefore");
|
||||||
|
// make it possible to reach the current target node via Python
|
||||||
|
// or clean up for other scripts executing around this one
|
||||||
|
(isBefore ? before : after)(element);
|
||||||
|
for (const fn of main(hook)) {
|
||||||
|
if (isAsync) await fn(wrap, element);
|
||||||
|
else fn(wrap, element);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
// PyScript Error Plugin
|
// PyScript Error Plugin
|
||||||
import { hooks } from "../core.js";
|
import { hooks } from "../core.js";
|
||||||
|
|
||||||
hooks.onInterpreterReady.add(function override(pyScript) {
|
hooks.main.onReady.add(function override(pyScript) {
|
||||||
// be sure this override happens only once
|
// be sure this override happens only once
|
||||||
hooks.onInterpreterReady.delete(override);
|
hooks.main.onReady.delete(override);
|
||||||
|
|
||||||
// trap generic `stderr` to propagate to it regardless
|
// trap generic `stderr` to propagate to it regardless
|
||||||
const { stderr } = pyScript.io;
|
const { stderr } = pyScript.io;
|
||||||
|
|||||||
50
pyscript.core/test/hooks.html
Normal file
50
pyscript.core/test/hooks.html
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>PyScript Next Plugin Bug?</title>
|
||||||
|
<link rel="stylesheet" href="../dist/core.css">
|
||||||
|
<script type="module">
|
||||||
|
import { hooks } from "../dist/core.js";
|
||||||
|
|
||||||
|
// Worker
|
||||||
|
hooks.worker.onReady.add((wrap, xworker) => {
|
||||||
|
console.log("worker", "onReady");
|
||||||
|
console.log("worker", "wrap", wrap);
|
||||||
|
console.log("worker", "xworker", xworker);
|
||||||
|
});
|
||||||
|
hooks.worker.onBeforeRun.add(() => {
|
||||||
|
console.log("worker", "onBeforeRun");
|
||||||
|
});
|
||||||
|
hooks.worker.codeBeforeRun.add('print("worker", "codeBeforeRun")');
|
||||||
|
hooks.worker.codeAfterRun.add('print("worker", "codeAfterRun")');
|
||||||
|
hooks.worker.onAfterRun.add(() => {
|
||||||
|
console.log("worker", "onAfterRun");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Main
|
||||||
|
hooks.main.onReady.add((wrap, element) => {
|
||||||
|
console.log("main", "onReady");
|
||||||
|
console.log("main", "wrap", wrap);
|
||||||
|
console.log("main", "element", element);
|
||||||
|
});
|
||||||
|
hooks.main.onBeforeRun.add(() => {
|
||||||
|
console.log("main", "onBeforeRun");
|
||||||
|
});
|
||||||
|
hooks.main.codeBeforeRun.add('print("main", "codeBeforeRun")');
|
||||||
|
hooks.main.codeAfterRun.add('print("main", "codeAfterRun")');
|
||||||
|
hooks.main.onAfterRun.add(() => {
|
||||||
|
console.log("main", "onAfterRun");
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script type="py" worker>
|
||||||
|
print("actual code in worker")
|
||||||
|
</script>
|
||||||
|
<script type="py">
|
||||||
|
print("actual code in main")
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -8,8 +8,10 @@
|
|||||||
|
|
||||||
<!-- the PyWorker approach -->
|
<!-- the PyWorker approach -->
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import { PyWorker } from '../dist/core.js';
|
import { PyWorker, whenDefined } from '../dist/core.js';
|
||||||
PyWorker('./worker.py', {config: {fetch: [{files: ['./a.py']}]}});
|
whenDefined('py').then(() => {
|
||||||
|
PyWorker('./worker.py', {config: {fetch: [{files: ['./a.py']}]}});
|
||||||
|
});
|
||||||
// the type is overwritten as "pyodide" in PyScript as the module
|
// the type is overwritten as "pyodide" in PyScript as the module
|
||||||
// lives in that env too
|
// lives in that env too
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
3
pyscript.core/types/core.d.ts
vendored
3
pyscript.core/types/core.d.ts
vendored
@@ -1,4 +1,5 @@
|
|||||||
declare const exportedPyWorker: any;
|
declare const exportedPyWorker: any;
|
||||||
declare const exportedHooks: any;
|
declare const exportedHooks: any;
|
||||||
declare const exportedConfig: any;
|
declare const exportedConfig: any;
|
||||||
export { exportedPyWorker as PyWorker, exportedHooks as hooks, exportedConfig as config };
|
declare const exportedWhenDefined: any;
|
||||||
|
export { exportedPyWorker as PyWorker, exportedHooks as hooks, exportedConfig as config, exportedWhenDefined as whenDefined };
|
||||||
|
|||||||
49
pyscript.core/types/hooks.d.ts
vendored
49
pyscript.core/types/hooks.d.ts
vendored
@@ -1,13 +1,38 @@
|
|||||||
declare namespace _default {
|
export function main(name: any): any;
|
||||||
let onInterpreterReady: Set<Function>;
|
export function worker(name: any): any;
|
||||||
let onBeforeRun: Set<Function>;
|
export function codeFor(branch: any): {};
|
||||||
let onBeforeRunAsync: Set<Function>;
|
export function createFunction(self: any, name: any): any;
|
||||||
let onAfterRun: Set<Function>;
|
export namespace hooks {
|
||||||
let onAfterRunAsync: Set<Function>;
|
namespace main {
|
||||||
let onWorkerReady: Set<Function>;
|
let onWorker: Set<Function>;
|
||||||
let codeBeforeRunWorker: Set<string>;
|
let onReady: Set<Function>;
|
||||||
let codeBeforeRunWorkerAsync: Set<string>;
|
let onBeforeRun: Set<Function>;
|
||||||
let codeAfterRunWorker: Set<string>;
|
let onBeforeRunAsync: Set<Function>;
|
||||||
let codeAfterRunWorkerAsync: Set<string>;
|
let onAfterRun: Set<Function>;
|
||||||
|
let onAfterRunAsync: Set<Function>;
|
||||||
|
let codeBeforeRun: Set<string>;
|
||||||
|
let codeBeforeRunAsync: Set<string>;
|
||||||
|
let codeAfterRun: Set<string>;
|
||||||
|
let codeAfterRunAsync: Set<string>;
|
||||||
|
}
|
||||||
|
namespace worker {
|
||||||
|
let onReady_1: Set<Function>;
|
||||||
|
export { onReady_1 as onReady };
|
||||||
|
let onBeforeRun_1: Set<Function>;
|
||||||
|
export { onBeforeRun_1 as onBeforeRun };
|
||||||
|
let onBeforeRunAsync_1: Set<Function>;
|
||||||
|
export { onBeforeRunAsync_1 as onBeforeRunAsync };
|
||||||
|
let onAfterRun_1: Set<Function>;
|
||||||
|
export { onAfterRun_1 as onAfterRun };
|
||||||
|
let onAfterRunAsync_1: Set<Function>;
|
||||||
|
export { onAfterRunAsync_1 as onAfterRunAsync };
|
||||||
|
let codeBeforeRun_1: Set<string>;
|
||||||
|
export { codeBeforeRun_1 as codeBeforeRun };
|
||||||
|
let codeBeforeRunAsync_1: Set<string>;
|
||||||
|
export { codeBeforeRunAsync_1 as codeBeforeRunAsync };
|
||||||
|
let codeAfterRun_1: Set<string>;
|
||||||
|
export { codeAfterRun_1 as codeAfterRun };
|
||||||
|
let codeAfterRunAsync_1: Set<string>;
|
||||||
|
export { codeAfterRunAsync_1 as codeAfterRunAsync };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
export default _default;
|
|
||||||
|
|||||||
2
pyscript.core/types/plugins-helper.d.ts
vendored
Normal file
2
pyscript.core/types/plugins-helper.d.ts
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
declare function _default(main: any, wrap: any, element: any, hook: any): Promise<void>;
|
||||||
|
export default _default;
|
||||||
Reference in New Issue
Block a user