diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 6f6eafe2..5f2dcae7 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -16,6 +16,7 @@ repos:
- id: check-json
exclude: tsconfig\.json
- id: check-toml
+ exclude: bad\.toml
- id: check-xml
- id: check-yaml
- id: detect-private-key
diff --git a/pyscript.core/package-lock.json b/pyscript.core/package-lock.json
index fe2777b1..74698014 100644
--- a/pyscript.core/package-lock.json
+++ b/pyscript.core/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@pyscript/core",
- "version": "0.1.18",
+ "version": "0.1.19",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@pyscript/core",
- "version": "0.1.18",
+ "version": "0.1.19",
"license": "APACHE-2.0",
"dependencies": {
"@ungap/with-resolvers": "^0.1.0",
@@ -597,9 +597,9 @@
}
},
"node_modules/electron-to-chromium": {
- "version": "1.4.519",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.519.tgz",
- "integrity": "sha512-kqs9oGYL4UFVkLKhqCTgBCYZv+wZ374yABDMqlDda9HvlkQxvSr7kgf4hfWVjMieDbX+1MwPHFBsOGCMIBaFKg==",
+ "version": "1.4.520",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.520.tgz",
+ "integrity": "sha512-Frfus2VpYADsrh1lB3v/ft/WVFlVzOIm+Q0p7U7VqHI6qr7NWHYKe+Wif3W50n7JAFoBsWVsoU0+qDks6WQ60g==",
"dev": true
},
"node_modules/entities": {
diff --git a/pyscript.core/package.json b/pyscript.core/package.json
index 91a378aa..14d68636 100644
--- a/pyscript.core/package.json
+++ b/pyscript.core/package.json
@@ -1,6 +1,6 @@
{
"name": "@pyscript/core",
- "version": "0.1.18",
+ "version": "0.1.19",
"type": "module",
"description": "PyScript",
"module": "./index.js",
@@ -21,6 +21,7 @@
"scripts": {
"server": "npx static-handler --cors --coep --coop --corp .",
"build": "node rollup/stdlib.cjs && node rollup/plugins.cjs && rm -rf dist && rollup --config rollup/core.config.js && npm run ts",
+ "size": "echo -e \"\\033[1mdist/*.js file size\\033[0m\"; for js in $(ls dist/*.js); do echo -e \"\\033[2m$js:\\033[0m $(cat $js | brotli | wc -c) bytes\"; done",
"ts": "tsc -p ."
},
"keywords": [
diff --git a/pyscript.core/src/config.js b/pyscript.core/src/config.js
index 23a52c63..79625ee9 100644
--- a/pyscript.core/src/config.js
+++ b/pyscript.core/src/config.js
@@ -7,38 +7,96 @@ import { $ } from "basic-devtools";
import allPlugins from "./plugins.js";
import { robustFetch as fetch, getText } from "./fetch.js";
+import { ErrorCode } from "./exceptions.js";
-// TODO: this is not strictly polyscript related but handy ... not sure
-// we should factor this utility out a part but this works anyway.
-import { parse } from "../node_modules/polyscript/esm/toml.js";
+const badURL = (url, expected = "") => {
+ let message = `(${ErrorCode.BAD_CONFIG}): Invalid URL: ${url}`;
+ if (expected) message += `\nexpected ${expected} content`;
+ throw new Error(message);
+};
+
+/**
+ * Given a string, returns its trimmed content as text,
+ * fetching it from a file if the content is a URL.
+ * @param {string} config either JSON, TOML, or a file to fetch
+ * @returns {{json: boolean, toml: boolean, text: string}}
+ */
+const configDetails = async (config) => {
+ let text = config?.trim();
+ // we only support an object as root config
+ let url = "",
+ toml = false,
+ json = /^{/.test(text) && /}$/.test(text);
+ // handle files by extension (relaxing urls parts after)
+ if (!json && /\.(\w+)(?:\?\S*)?$/.test(text)) {
+ const ext = RegExp.$1;
+ if (ext === "json" && type !== "toml") json = true;
+ else if (ext === "toml" && type !== "json") toml = true;
+ else badURL(text, type);
+ url = text;
+ text = (await fetch(url).then(getText)).trim();
+ }
+ return { json, toml: toml || (!json && !!text), text, url };
+};
+
+const syntaxError = (type, url, { message }) => {
+ let str = `(${ErrorCode.BAD_CONFIG}): Invalid ${type}`;
+ if (url) str += ` @ ${url}`;
+ return new SyntaxError(`${str}\n${message}`);
+};
// find the shared config for all py-script elements
-let config, plugins, parsed;
+let config, plugins, parsed, error, type;
let pyConfig = $("py-config");
if (pyConfig) config = pyConfig.getAttribute("src") || pyConfig.textContent;
else {
pyConfig = $('script[type="py"][config]');
if (pyConfig) config = pyConfig.getAttribute("config");
}
+if (pyConfig) type = pyConfig.getAttribute("type");
-// load its content if remote
-if (/^https?:\/\//.test(config)) config = await fetch(config).then(getText);
-
-// parse config only if not empty
-if (config?.trim()) {
- try {
- parsed = JSON.parse(config);
- } catch (_) {
- parsed = await parse(config);
+// catch possible fetch errors
+try {
+ const { json, toml, text, url } = await configDetails(config);
+ config = text;
+ if (json || type === "json") {
+ try {
+ parsed = JSON.parse(text);
+ } catch (e) {
+ error = syntaxError("JSON", url, e);
+ }
+ } else if (toml || type === "toml") {
+ try {
+ const { parse } = await import(
+ /* webpackIgnore: true */
+ "https://cdn.jsdelivr.net/npm/@webreflection/toml-j0.4/toml.js"
+ );
+ parsed = parse(text);
+ } catch (e) {
+ error = syntaxError("TOML", url, e);
+ }
}
+} catch (e) {
+ error = e;
}
// parse all plugins and optionally ignore only
// those flagged as "undesired" via `!` prefix
const toBeAwaited = [];
for (const [key, value] of Object.entries(allPlugins)) {
- if (!parsed?.plugins?.includes(`!${key}`)) toBeAwaited.push(value());
+ if (error) {
+ if (key === "error") {
+ // show on page the config is broken, meaning that
+ // it was not possible to disable error plugin neither
+ // as that part wasn't correctly parsed anyway
+ value().then(({ notify }) => notify(error.message));
+ }
+ } else if (!parsed?.plugins?.includes(`!${key}`)) {
+ toBeAwaited.push(value());
+ }
}
+
+// assign plugins as Promise.all only if needed
if (toBeAwaited.length) plugins = Promise.all(toBeAwaited);
-export { config, plugins };
+export { config, plugins, error };
diff --git a/pyscript.core/src/core.js b/pyscript.core/src/core.js
index 56c81c14..d1403394 100644
--- a/pyscript.core/src/core.js
+++ b/pyscript.core/src/core.js
@@ -11,7 +11,7 @@ import { Hook } from "../node_modules/polyscript/esm/worker/hooks.js";
import sync from "./sync.js";
import stdlib from "./stdlib.js";
-import { config, plugins } from "./config.js";
+import { config, plugins, error } from "./config.js";
import { robustFetch as fetch, getText } from "./fetch.js";
const { assign, defineProperty, entries } = Object;
@@ -128,74 +128,81 @@ const workerHooks = {
};
// define the module as both `
+
+
+