8000 Wait for plugins before defining the custom type (#1788) · pyscript/pyscript@d5b6935 · GitHub
[go: up one dir, main page]

Skip to content

Commit d5b6935

Browse files
Wait for plugins before defining the custom type (#1788)
1 parent b4503ef commit d5b6935

File tree

2 files changed

+148
-148
lines changed

2 files changed

+148
-148
lines changed

pyscript.core/src/config.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ const syntaxError = (type, url, { message }) => {
5050
const configs = new Map();
5151

5252
for (const [TYPE] of TYPES) {
53-
/** @type {Promise<any> | undefined} A Promise wrapping any plugins which should be loaded. */
53+
/** @type {Promise<[...any]>} A Promise wrapping any plugins which should be loaded. */
5454
let plugins;
5555

5656
/** @type {any} The PyScript configuration parsed from the JSON or TOML object*. May be any of the return types of JSON.parse() or toml-j0.4's parse() ( {number | string | boolean | null | object | Array} ) */
@@ -119,7 +119,7 @@ for (const [TYPE] of TYPES) {
119119
}
120120

121121
// assign plugins as Promise.all only if needed
122-
if (toBeAwaited.length) plugins = Promise.all(toBeAwaited);
122+
plugins = Promise.all(toBeAwaited);
123123

124124
configs.set(TYPE, { config: parsed, plugins, error });
125125
}

pyscript.core/src/core.js

Lines changed: 146 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -128,162 +128,162 @@ for (const [TYPE, interpreter] of TYPES) {
128128
// define the module as both `<script type="py">` and `<py-script>`
129129
// but only if the config didn't throw an error
130130
if (!error) {
131-
// possible early errors sent by polyscript
132-
const errors = new Map();
133-
134-
define(TYPE, {
135-
config,
136-
interpreter,
137-
env: `${TYPE}-script`,
138-
version: config?.interpreter,
139-
onerror(error, element) {
140-
errors.set(element, error);
141-
},
142-
...workerHooks,
143-
onWorkerReady(_, xworker) {
144-
assign(xworker.sync, sync);
145-
for (const callback of hooks.onWorkerReady)
146-
callback(_, xworker);
147-
},
148-
onBeforeRun(wrap, element) {
149-
currentElement = element;
150-
bootstrapNodeAndPlugins(wrap, element, before, "onBeforeRun");
151-
},
152-
onBeforeRunAsync(wrap, element) {
153-
currentElement = element;
154-
bootstrapNodeAndPlugins(
155-
wra B421 p,
156-
element,
157-
before,
158-
"onBeforeRunAsync",
159-
);
160-
},
161-
onAfterRun(wrap, element) {
162-
bootstrapNodeAndPlugins(wrap, element, after, "onAfterRun");
163-
},
164-
onAfterRunAsync(wrap, element) {
165-
bootstrapNodeAndPlugins(
166-
wrap,
167-
element,
168-
after,
169-
"onAfterRunAsync",
170-
);
171-
},
172-
async onInterpreterReady(wrap, element) {
173-
if (shouldRegister) {
174-
shouldRegister = false;
175-
registerModule(wrap);
176-
}
177-
178-
// ensure plugins are bootstrapped already
179-
if (plugins) await plugins;
180-
181-
// allows plugins to do whatever they want with the element
182-
// before regular stuff happens in here
183-
for (const callback of hooks.onInterpreterReady)
184-
callback(wrap, element);
185-
186-
// now that all possible plugins are configured,
187-
// bail out if polyscript encountered an error
188-
if (errors.has(element)) {
189-
let { message } = errors.get(element);
190-
errors.delete(element);
191-
const clone = message === INVALID_CONTENT;
192-
message = `(${ErrorCode.CONFLICTING_CODE}) ${message} for `;
193-
message += element.cloneNode(clone).outerHTML;
194-
wrap.io.stderr(message);
195-
return;
196-
}
197-
198-
if (isScript(element)) {
199-
const {
200-
attributes: { async: isAsync, target },
201-
} = element;
202-
const hasTarget = !!target?.value;
203-
const show = hasTarget
204-
? queryTarget(element, target.value)
205-
: document.createElement("script-py");
131+
// ensure plugins are bootstrapped already before custom type definition
132+
// NOTE: we cannot top-level await in here as plugins import other utilities
133+
// from core.js itself so that custom definition should not be blocking.
134+
plugins.then(() => {
135+
// possible early errors sent by polyscript
136+
const errors = new Map();
137+
138+
define(TYPE, {
139+
config,
140+
interpreter,
141+
env: `${TYPE}-script`,
142+
version: config?.interpreter,
143+
onerror(error, element) {
144+
errors.set(element, error);
145+
},
146+
...workerHooks,
147+
onWorkerReady(_, xworker) {
148+
assign(xworker.sync, sync);
149+
for (const callback of hooks.onWorkerReady)
150+
callback(_, xworker);
151+
},
152+
onBeforeRun(wrap, element) {
153+
currentElement = element;
154+
bootstrapNodeAndPlugins(
155+
wrap,
156+
element,
157+
before,
158+
"onBeforeRun",
159+
);
160+
},
161+
onBeforeRunAsync(wrap, element) {
162+
currentElement = element;
163+
bootstrapNodeAndPlugins(
164+
wrap,
165+
element,
166+
before,
167+
"onBeforeRunAsync",
168+
);
169+
},
170+
onAfterRun(wrap, element) {
171+
bootstrapNodeAndPlugins(wrap, element, after, "onAfterRun");
172+
},
173+
onAfterRunAsync(wrap, element) {
174+
bootstrapNodeAndPlugins(
175+
wrap,
176+
element,
177+
after,
178+
"onAfterRunAsync",
179+
);
180+
},
181+
async onInterpreterReady(wrap, element) {
182+
if (shouldRegister) {
183+
shouldRegister = false;
184+
registerModule(wrap);
185+
}
206186

207-
if (!hasTarget) {
208-
const { head, body } = document;
209-
if (head.contains(element)) body.append(show);
210-
else element.after(show);
187+
// allows plugins to do whatever they want with the element
188+
// before regular stuff happens in here
189+
for (const callback of hooks.onInterpreterReady)
190+
callback(wrap, element);
191+
192+
// now that all possible plugins are configured,
193+
// bail out if polyscript encountered an error
194+
if (errors.has(element)) {
195+
let { message } = errors.get(element);
196+
errors.delete(element);
197+
const clone = message === INVALID_CONTENT;
198+
message = `(${ErrorCode.CONFLICTING_CODE}) ${message} for `;
199+
message += element.cloneNode(clone).outerHTML;
200+
wrap.io.stderr(message);
201+
return;
211202
}
212-
if (!show.id) show.id = getID();
213203

214-
// allows the code to retrieve the target element via
215-
// document.currentScript.target if needed
216-
defineProperty(element, "target", { value: show });
204+
if (isScript(element)) {
205+
const {
206+
attributes: { async: isAsync, target },
207+
} = element;
208+
const hasTarget = !!target?.value;
209+
const show = hasTarget
210+
? queryTarget(element, target.value)
211+
: document.createElement("script-py");
212+
213+
if (!hasTarget) {
214+
const { head, body } = document;
215+
if (head.contains(element)) body.append(show);
216+
else element.after(show);
217+
}
218+
if (!show.id) show.id = getID();
219+
220+
// allows the code to retrieve the target element via
221+
// document.currentScript.target if needed
222+
defineProperty(element, "target", { value: show });
223+
224+
// notify before the code runs
225+
dispatch(element, TYPE, "ready");
226+
dispatchDone(
227+
element,
228+
isAsync,
229+
wrap[`run${isAsync ? "Async" : ""}`](
230+
await fetchSource(element, wrap.io, true),
231+
),
232+
);
233+
} else {
234+
// resolve PyScriptElement to allow connectedCallback
235+
element._wrap.resolve(wrap);
236+
}
237+
console.debug("[pyscript/main] PyScript Ready");
238+
},
239+
});
217240

218-
// notify before the code runs
219-
dispatch(element, TYPE, "ready");
220-
dispatchDone(
221-
element,
222-
isAsync,
223-
wrap[`run${isAsync ? "Async" : ""}`](
224-
await fetchSource(element, wrap.io, true),
225-
),
226-
);
227-
} else {
228-
// resolve PyScriptElement to allow connectedCallback
229-
element._wrap.resolve(wrap);
230-
}
231-
console.debug("[pyscript/main] PyScript Ready");
232-
},
241+
customElements.define(
242+
`${TYPE}-script`,
243+
class extends HTMLElement {
244+
constructor() {
245+
assign(super(), {
246+
_wrap: Promise.withResolvers(),
247+
srcCode: "",
248+
executed: false,
249+
});
250+
}
251+
get id() {
252+
return super.id || (super.id = getID());
253+
}
254+
set id(value) {
255+
super.id = value;
256+
}
257+
async connectedCallback() {
258+
if (!this.executed) {
259+
this.executed = true;
260+
const isAsync = this.hasAttribute("async");
261+
const { io, run, runAsync } = await this._wrap
262+
.promise;
263+
this.srcCode = await fetchSource(
264+
this,
265+
io,
266+
!this.childElementCount,
267+
);
268+
this.replaceChildren();
269+
this.style.display = "block";
270+
dispatch(this, TYPE, "ready");
271+
dispatchDone(
272+
this,
273+
isAsync,
274+
(isAsync ? runAsync : run)(this.srcCode),
275+
);
276+
}
277+
}
278+
},
279+
);
233280
});
234281
}
235282

236-
class PyScriptElement extends HTMLElement {
237-
constructor() {
238-
assign(super(), {
239-
_wrap: Promise.withResolvers(),
240-
srcCode: "",
241-
executed: false,
242-
});
243-
}
244-
get _pyodide() {
245-
// TODO: deprecate this hidden attribute already
246-
// currently used by integration tests
247-
return this._wrap;
248-
}
249-
get id() {
250-
return super.id || (super.id = getID());
251-
}
252-
set id(value) {
253-
super.id = value;
254-
}
255-
async connectedCallback() {
256-
if (!this.executed) {
257-
this.executed = true;
258-
const isAsync = this.hasAttribute("async");
259-
const { io, run, runAsync } = await this._wrap.promise;
260-
this.srcCode = await fetchSource(
261-
this,
262-
io,
263-
!this.childElementCount,
264-
);
265-
this.replaceChildren();
266-
this.style.display = "block";
267-
dispatch(this, TYPE, "ready");
268-
dispatchDone(
269-
this,
270-
isAsync,
271-
(isAsync ? runAsync : run)(this.srcCode),
272-
);
273-
}
274-
}
275-
}
276-
277-
// define py-script only if the config didn't throw an error
278-
if (!error) customElements.define(`${TYPE}-script`, PyScriptElement);
279-
280283
// export the used config without allowing leaks through it
281284
exportedConfig[TYPE] = structuredClone(config);
282285
}
283286

284-
// TBD: I think manual worker cases are interesting in pyodide only
285-
// so for the time being we should be fine with this export.
286-
287287
/**
288288
* A `Worker` facade able to bootstrap on the worker thread only a PyScript module.
289289
* @param {string} file the python file to run ina worker.
@@ -297,8 +297,8 @@ export function PyWorker(file, options) {
297297
// and as `pyodide` is the only default interpreter that can deal with
298298
// all the features we need to deliver pyscript out there.
299299
const xworker = XWorker.call(new Hook(null, workerHooks), file, {
300-
...options,
301300
type: "pyodide",
301+
...options,
302302
});
303303
assign(xworker.sync, sync);
304304
return xworker;

0 commit comments

Comments
 (0)
0