mirror of
https://github.com/pyscript/pyscript.git
synced 2026-02-14 10:01:09 -05:00
More plugins: splashscreen and importmap (#938)
This PR move codes from main.ts into two new plugins: - splashscreen (formerly known as py-loader) - importmap The old setting config.autoclose_loader is still supported but deprecated; the new setting is config.splashscreen.autoclose. Moreover, it does a small refactoring around UserError: now UserErrors are correctly caught even if they are raised from within afterRuntimeLoad.
This commit is contained in:
56
pyscriptjs/src/plugins/importmap.ts
Normal file
56
pyscriptjs/src/plugins/importmap.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import type { Runtime } from '../runtime';
|
||||
import { showWarning } from '../utils';
|
||||
import { Plugin } from '../plugin';
|
||||
import { getLogger } from '../logger';
|
||||
|
||||
const logger = getLogger('plugins/importmap');
|
||||
|
||||
type ImportType = { [key: string]: unknown };
|
||||
type ImportMapType = {
|
||||
imports: ImportType | null;
|
||||
};
|
||||
|
||||
export class ImportmapPlugin extends Plugin {
|
||||
|
||||
async afterSetup(runtime: Runtime) {
|
||||
// make importmap ES modules available from python using 'import'.
|
||||
//
|
||||
// XXX: this code can probably be improved because errors are silently
|
||||
// ignored.
|
||||
//
|
||||
// Moreover, it's also wrong because it's async and currently we don't
|
||||
// await the module to be fully registered before executing the code
|
||||
// inside py-script. It's also unclear whether we want to wait or not
|
||||
// (or maybe only wait only if we do an actual 'import'?)
|
||||
for (const node of document.querySelectorAll("script[type='importmap']")) {
|
||||
const importmap: ImportMapType = (() => {
|
||||
try {
|
||||
return JSON.parse(node.textContent) as ImportMapType;
|
||||
}
|
||||
catch(error) {
|
||||
showWarning("Failed to parse import map: " + error.message);
|
||||
}
|
||||
})();
|
||||
|
||||
if (importmap?.imports == null) continue;
|
||||
|
||||
for (const [name, url] of Object.entries(importmap.imports)) {
|
||||
if (typeof name != 'string' || typeof url != 'string') continue;
|
||||
|
||||
let exports: object;
|
||||
try {
|
||||
// XXX: pyodide doesn't like Module(), failing with
|
||||
// "can't read 'name' of undefined" at import time
|
||||
exports = { ...(await import(url)) } as object;
|
||||
} catch {
|
||||
logger.warn(`failed to fetch '${url}' for '${name}'`);
|
||||
continue;
|
||||
}
|
||||
|
||||
logger.info("Registering JS module", name);
|
||||
runtime.registerJsModule(name, exports);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { PyScriptApp } from '../main';
|
||||
import type { AppConfig } from '../pyconfig';
|
||||
import type { Runtime } from '../runtime';
|
||||
import { Plugin } from '../plugin';
|
||||
import { UserError } from "../exceptions"
|
||||
import { getLogger } from '../logger';
|
||||
@@ -46,7 +47,7 @@ export class PyTerminalPlugin extends Plugin {
|
||||
}
|
||||
}
|
||||
|
||||
afterSetup() {
|
||||
afterSetup(runtime: Runtime) {
|
||||
// the Python interpreter has been initialized and we are ready to
|
||||
// execute user code:
|
||||
//
|
||||
|
||||
103
pyscriptjs/src/plugins/splashscreen.ts
Normal file
103
pyscriptjs/src/plugins/splashscreen.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
import type { PyScriptApp } from '../main';
|
||||
import type { AppConfig } from '../pyconfig';
|
||||
import type { UserError } from '../exceptions';
|
||||
import type { Runtime } from '../runtime';
|
||||
import { showWarning } from '../utils';
|
||||
import { Plugin } from '../plugin';
|
||||
import { getLogger } from '../logger';
|
||||
|
||||
const logger = getLogger('py-splashscreen');
|
||||
|
||||
const AUTOCLOSE_LOADER_DEPRECATED = `
|
||||
The setting autoclose_loader is deprecated. Please use the
|
||||
following instead:<br>
|
||||
<pre>
|
||||
<py-config>
|
||||
[splashscreen]
|
||||
autoclose = false
|
||||
</py-config>
|
||||
</pre>`;
|
||||
|
||||
export class SplashscreenPlugin extends Plugin {
|
||||
elem: PySplashscreen;
|
||||
autoclose: boolean;
|
||||
|
||||
configure(config: AppConfig) {
|
||||
// the officially supported setting is config.splashscreen.autoclose,
|
||||
// but we still also support the old config.autoclose_loader (with a
|
||||
// deprecation warning)
|
||||
this.autoclose = true;
|
||||
|
||||
if ("autoclose_loader" in config) {
|
||||
this.autoclose = config.autoclose_loader;
|
||||
showWarning(AUTOCLOSE_LOADER_DEPRECATED);
|
||||
}
|
||||
|
||||
if (config.splashscreen) {
|
||||
this.autoclose = config.splashscreen.autoclose ?? true;
|
||||
}
|
||||
}
|
||||
|
||||
beforeLaunch(config: AppConfig) {
|
||||
// add the splashscreen to the DOM
|
||||
logger.info('add py-splashscreen');
|
||||
customElements.define('py-splashscreen', PySplashscreen);
|
||||
this.elem = <PySplashscreen>document.createElement('py-splashscreen');
|
||||
document.body.append(this.elem);
|
||||
|
||||
document.addEventListener("py-status-message", (e: CustomEvent) => {
|
||||
const msg = e.detail;
|
||||
this.elem.log(msg);
|
||||
});
|
||||
}
|
||||
|
||||
afterStartup(runtime: Runtime) {
|
||||
if (this.autoclose) {
|
||||
this.elem.close();
|
||||
}
|
||||
}
|
||||
|
||||
onUserError(error: UserError) {
|
||||
if (this.elem !== undefined) {
|
||||
// Remove the splashscreen so users can see the banner better
|
||||
this.elem.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class PySplashscreen extends HTMLElement {
|
||||
widths: string[];
|
||||
label: string;
|
||||
mount_name: string;
|
||||
details: HTMLElement;
|
||||
operation: HTMLElement;
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.innerHTML = `<div id="pyscript_loading_splash" class="py-overlay">
|
||||
<div class="py-pop-up">
|
||||
<div class="smooth spinner"></div>
|
||||
<div id="pyscript-loading-label" class="label">
|
||||
<div id="pyscript-operation-details">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
this.mount_name = this.id.split('-').join('_');
|
||||
this.operation = document.getElementById('pyscript-operation');
|
||||
this.details = document.getElementById('pyscript-operation-details');
|
||||
}
|
||||
|
||||
log(msg: string) {
|
||||
const newLog = document.createElement('p');
|
||||
newLog.innerText = msg;
|
||||
this.details.appendChild(newLog);
|
||||
}
|
||||
|
||||
close() {
|
||||
logger.info('Closing');
|
||||
this.remove();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user