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",
|
||||
"version": "0.2.10",
|
||||
"version": "0.3.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@pyscript/core",
|
||||
"version": "0.2.10",
|
||||
"version": "0.3.0",
|
||||
"license": "APACHE-2.0",
|
||||
"dependencies": {
|
||||
"@ungap/with-resolvers": "^0.1.0",
|
||||
"basic-devtools": "^0.1.6",
|
||||
"polyscript": "^0.4.20",
|
||||
"polyscript": "^0.5.1",
|
||||
"sticky-module": "^0.1.0",
|
||||
"to-json-callback": "^0.1.1",
|
||||
"type-checked-collections": "^0.1.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -20,7 +21,7 @@
|
||||
"@rollup/plugin-terser": "^0.4.4",
|
||||
"@webreflection/toml-j0.4": "^1.1.3",
|
||||
"chokidar": "^3.5.3",
|
||||
"eslint": "^8.51.0",
|
||||
"eslint": "^8.52.0",
|
||||
"rollup": "^4.1.4",
|
||||
"rollup-plugin-postcss": "^4.0.2",
|
||||
"rollup-plugin-string": "^3.0.0",
|
||||
@@ -85,21 +86,21 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "8.51.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.51.0.tgz",
|
||||
"integrity": "sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg==",
|
||||
"version": "8.52.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.52.0.tgz",
|
||||
"integrity": "sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanwhocodes/config-array": {
|
||||
"version": "0.11.11",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz",
|
||||
"integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==",
|
||||
"version": "0.11.13",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz",
|
||||
"integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@humanwhocodes/object-schema": "^1.2.1",
|
||||
"@humanwhocodes/object-schema": "^2.0.1",
|
||||
"debug": "^4.1.1",
|
||||
"minimatch": "^3.0.5"
|
||||
},
|
||||
@@ -121,9 +122,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@humanwhocodes/object-schema": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
|
||||
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz",
|
||||
"integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
@@ -764,12 +765,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/coincident": {
|
||||
"version": "0.13.10",
|
||||
"resolved": "https://registry.npmjs.org/coincident/-/coincident-0.13.10.tgz",
|
||||
"integrity": "sha512-gzSTDRMMVvo40+zj7sRUFMsc5Fpu865qn4gf/FUAaukBal+c2lxvnTigT7HSZLGPYEj6Sm5vPDTik31Uqei3qg==",
|
||||
"version": "0.14.2",
|
||||
"resolved": "https://registry.npmjs.org/coincident/-/coincident-0.14.2.tgz",
|
||||
"integrity": "sha512-Xc/lh56dl/v5GT1R3bEWxiLzF5ZTiXE5Flcd0+qvrBGhZsvDha8bgqhpocrvJmELuerhDO3+EQKDdCzPBPodJQ==",
|
||||
"dependencies": {
|
||||
"@ungap/structured-clone": "^1.2.0",
|
||||
"@ungap/with-resolvers": "^0.1.0"
|
||||
"@ungap/with-resolvers": "^0.1.0",
|
||||
"gc-hook": "^0.2.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"ws": "^8.14.2"
|
||||
@@ -1126,18 +1128,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "8.51.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.51.0.tgz",
|
||||
"integrity": "sha512-2WuxRZBrlwnXi+/vFSJyjMqrNjtJqiasMzehF0shoLaW7DzS3/9Yvrmq5JiT66+pNjiX4UBnLDiKHcWAr/OInA==",
|
||||
"version": "8.52.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.52.0.tgz",
|
||||
"integrity": "sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.6.1",
|
||||
"@eslint/eslintrc": "^2.1.2",
|
||||
"@eslint/js": "8.51.0",
|
||||
"@humanwhocodes/config-array": "^0.11.11",
|
||||
"@eslint/js": "8.52.0",
|
||||
"@humanwhocodes/config-array": "^0.11.13",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
"@nodelib/fs.walk": "^1.2.8",
|
||||
"@ungap/structured-clone": "^1.2.0",
|
||||
"ajv": "^6.12.4",
|
||||
"chalk": "^4.0.0",
|
||||
"cross-spawn": "^7.0.2",
|
||||
@@ -1385,6 +1388,11 @@
|
||||
"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": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/generic-names/-/generic-names-4.0.0.tgz",
|
||||
@@ -2053,15 +2061,15 @@
|
||||
"integrity": "sha512-yyVAOFKTAElc7KdLt2+UKGExNYwYb/Y/WE9i+1ezCQsJE8gbKSjewfpRqK2nQgZ4d4hhAAGgDCOcIZVilqE5UA=="
|
||||
},
|
||||
"node_modules/polyscript": {
|
||||
"version": "0.4.20",
|
||||
"resolved": "https://registry.npmjs.org/polyscript/-/polyscript-0.4.20.tgz",
|
||||
"integrity": "sha512-MNpZQPcJ0047Gq/SXZTw8H929nMKcc3dqxbegd42remBJ0y2UwrKXiE0NMhlO85cbxLrV+sGD2ZCt8+/2m55EQ==",
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/polyscript/-/polyscript-0.5.1.tgz",
|
||||
"integrity": "sha512-jMFFWCJjYKcan7RV5UBVNg9IfVSss7wB6l5RIdx28pnBu1WSyA7sApLz7NEVbF14g5a5OjwtlvZCphQEA4CfQQ==",
|
||||
"dependencies": {
|
||||
"@ungap/structured-clone": "^1.2.0",
|
||||
"@ungap/with-resolvers": "^0.1.0",
|
||||
"basic-devtools": "^0.1.6",
|
||||
"codedent": "^0.1.2",
|
||||
"coincident": "^0.13.10",
|
||||
"coincident": "^0.14.2",
|
||||
"html-escaper": "^3.0.3",
|
||||
"sticky-module": "^0.1.0"
|
||||
}
|
||||
@@ -3092,6 +3100,11 @@
|
||||
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
|
||||
"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": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@pyscript/core",
|
||||
"version": "0.2.10",
|
||||
"version": "0.3.0",
|
||||
"type": "module",
|
||||
"description": "PyScript",
|
||||
"module": "./index.js",
|
||||
@@ -40,8 +40,9 @@
|
||||
"dependencies": {
|
||||
"@ungap/with-resolvers": "^0.1.0",
|
||||
"basic-devtools": "^0.1.6",
|
||||
"polyscript": "^0.4.20",
|
||||
"polyscript": "^0.5.1",
|
||||
"sticky-module": "^0.1.0",
|
||||
"to-json-callback": "^0.1.1",
|
||||
"type-checked-collections": "^0.1.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -49,7 +50,7 @@
|
||||
"@rollup/plugin-terser": "^0.4.4",
|
||||
"@webreflection/toml-j0.4": "^1.1.3",
|
||||
"chokidar": "^3.5.3",
|
||||
"eslint": "^8.51.0",
|
||||
"eslint": "^8.52.0",
|
||||
"rollup": "^4.1.4",
|
||||
"rollup-plugin-postcss": "^4.0.2",
|
||||
"rollup-plugin-string": "^3.0.0",
|
||||
|
||||
@@ -14,16 +14,18 @@ import {
|
||||
dispatch,
|
||||
queryTarget,
|
||||
unescape,
|
||||
whenDefined,
|
||||
} from "polyscript/exports";
|
||||
|
||||
import "./all-done.js";
|
||||
import TYPES from "./types.js";
|
||||
import configs from "./config.js";
|
||||
import hooks from "./hooks.js";
|
||||
import sync from "./sync.js";
|
||||
import stdlib from "./stdlib.js";
|
||||
import bootstrapNodeAndPlugins from "./plugins-helper.js";
|
||||
import { ErrorCode } from "./exceptions.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
|
||||
let currentElement;
|
||||
@@ -31,25 +33,6 @@ let currentElement;
|
||||
// generic helper to disambiguate between custom element and 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;
|
||||
const registerModule = ({ XWorker: $XWorker, interpreter, io }) => {
|
||||
// automatically use the pyscript stderr (when/if defined)
|
||||
@@ -73,33 +56,31 @@ const registerModule = ({ XWorker: $XWorker, interpreter, io }) => {
|
||||
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
|
||||
const [
|
||||
{
|
||||
PyWorker: exportedPyWorker,
|
||||
hooks: exportedHooks,
|
||||
config: exportedConfig,
|
||||
whenDefined: exportedWhenDefined,
|
||||
},
|
||||
alreadyLive,
|
||||
] = stickyModule("@pyscript/core", { PyWorker, hooks, config: {} });
|
||||
] = stickyModule("@pyscript/core", {
|
||||
PyWorker,
|
||||
hooks,
|
||||
config: {},
|
||||
whenDefined,
|
||||
});
|
||||
|
||||
export {
|
||||
exportedPyWorker as PyWorker,
|
||||
exportedHooks as hooks,
|
||||
exportedConfig as config,
|
||||
exportedWhenDefined as whenDefined,
|
||||
};
|
||||
|
||||
const hooked = new Map();
|
||||
|
||||
for (const [TYPE, interpreter] of TYPES) {
|
||||
// avoid any dance if the module already landed
|
||||
if (alreadyLive) break;
|
||||
@@ -149,107 +130,141 @@ for (const [TYPE, interpreter] of TYPES) {
|
||||
// possible early errors sent by polyscript
|
||||
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, {
|
||||
config,
|
||||
interpreter,
|
||||
hooks,
|
||||
env: `${TYPE}-script`,
|
||||
version: config?.interpreter,
|
||||
onerror(error, element) {
|
||||
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(
|
||||
@@ -305,12 +320,13 @@ for (const [TYPE, interpreter] of TYPES) {
|
||||
* @returns {Worker & {sync: ProxyHandler<object>}}
|
||||
*/
|
||||
function PyWorker(file, options) {
|
||||
const hooks = hooked.get("py");
|
||||
// this propagates pyscript worker hooks without needing a pyscript
|
||||
// bootstrap + it passes arguments and enforces `pyodide`
|
||||
// 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
|
||||
// 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",
|
||||
...options,
|
||||
});
|
||||
|
||||
@@ -1,28 +1,91 @@
|
||||
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 SetString = typedSet({ typeof: "string" });
|
||||
|
||||
export default {
|
||||
/** @type {Set<function>} */
|
||||
onInterpreterReady: 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<function>} */
|
||||
onWorkerReady: new SetFunction(),
|
||||
/** @type {Set<string>} */
|
||||
codeBeforeRunWorker: new SetString(),
|
||||
/** @type {Set<string>} */
|
||||
codeBeforeRunWorkerAsync: new SetString(),
|
||||
/** @type {Set<string>} */
|
||||
codeAfterRunWorker: new SetString(),
|
||||
/** @type {Set<string>} */
|
||||
codeAfterRunWorkerAsync: new SetString(),
|
||||
export const hooks = {
|
||||
main: {
|
||||
/** @type {Set<function>} */
|
||||
onWorker: new SetFunction(),
|
||||
/** @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(),
|
||||
},
|
||||
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
|
||||
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
|
||||
hooks.onInterpreterReady.delete(override);
|
||||
hooks.main.onReady.delete(override);
|
||||
|
||||
// trap generic `stderr` to propagate to it regardless
|
||||
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 -->
|
||||
<script type="module">
|
||||
import { PyWorker } from '../dist/core.js';
|
||||
PyWorker('./worker.py', {config: {fetch: [{files: ['./a.py']}]}});
|
||||
import { PyWorker, whenDefined } from '../dist/core.js';
|
||||
whenDefined('py').then(() => {
|
||||
PyWorker('./worker.py', {config: {fetch: [{files: ['./a.py']}]}});
|
||||
});
|
||||
// the type is overwritten as "pyodide" in PyScript as the module
|
||||
// lives in that env too
|
||||
</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 exportedHooks: 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 {
|
||||
let onInterpreterReady: Set<Function>;
|
||||
let onBeforeRun: Set<Function>;
|
||||
let onBeforeRunAsync: Set<Function>;
|
||||
let onAfterRun: Set<Function>;
|
||||
let onAfterRunAsync: Set<Function>;
|
||||
let onWorkerReady: Set<Function>;
|
||||
let codeBeforeRunWorker: Set<string>;
|
||||
let codeBeforeRunWorkerAsync: Set<string>;
|
||||
let codeAfterRunWorker: Set<string>;
|
||||
let codeAfterRunWorkerAsync: Set<string>;
|
||||
export function main(name: any): any;
|
||||
export function worker(name: any): any;
|
||||
export function codeFor(branch: any): {};
|
||||
export function createFunction(self: any, name: any): any;
|
||||
export namespace hooks {
|
||||
namespace main {
|
||||
let onWorker: Set<Function>;
|
||||
let onReady: Set<Function>;
|
||||
let onBeforeRun: Set<Function>;
|
||||
let onBeforeRunAsync: Set<Function>;
|
||||
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