diff --git a/pyscript.core/package-lock.json b/pyscript.core/package-lock.json index 95eaa1fd..4533ba03 100644 --- a/pyscript.core/package-lock.json +++ b/pyscript.core/package-lock.json @@ -1,27 +1,27 @@ { "name": "@pyscript/core", - "version": "0.4.11", + "version": "0.4.12", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@pyscript/core", - "version": "0.4.11", + "version": "0.4.12", "license": "APACHE-2.0", "dependencies": { "@ungap/with-resolvers": "^0.1.0", "basic-devtools": "^0.1.6", - "polyscript": "^0.11.3", + "polyscript": "^0.12.1", "sticky-module": "^0.1.1", "to-json-callback": "^0.1.1", "type-checked-collections": "^0.1.7" }, "devDependencies": { "@codemirror/commands": "^6.3.3", - "@codemirror/lang-python": "^6.1.4", + "@codemirror/lang-python": "^6.1.5", "@codemirror/language": "^6.10.1", "@codemirror/state": "^6.4.1", - "@codemirror/view": "^6.26.0", + "@codemirror/view": "^6.26.1", "@playwright/test": "^1.42.1", "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-node-resolve": "^15.2.3", @@ -31,7 +31,7 @@ "chokidar": "^3.6.0", "codemirror": "^6.0.1", "eslint": "^8.57.0", - "rollup": "^4.13.1", + "rollup": "^4.13.2", "rollup-plugin-postcss": "^4.0.2", "rollup-plugin-string": "^3.0.0", "static-handler": "^0.4.3", @@ -80,9 +80,9 @@ } }, "node_modules/@codemirror/lang-python": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@codemirror/lang-python/-/lang-python-6.1.4.tgz", - "integrity": "sha512-b6d1TDqrkCjFNvMO01SWldFiDoZ39yl3tDMC1Y5f8glA2eZpynPxJhwYVTlGFr0stizcJgrp6ojLEGH2myoZAw==", + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/@codemirror/lang-python/-/lang-python-6.1.5.tgz", + "integrity": "sha512-hCm+8X6wrnXJCGf+QhmFu1AXkdTVG7dHy0Ly6SI1N3SRPptaMvwX6oNQonOXOMPvmcjiB0xq342KAxX3BYpijw==", "dev": true, "dependencies": { "@codemirror/autocomplete": "^6.3.2", @@ -135,9 +135,9 @@ "dev": true }, "node_modules/@codemirror/view": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.26.0.tgz", - "integrity": "sha512-nSSmzONpqsNzshPOxiKhK203R6BvABepugAe34QfQDbNDslyjkqBuKgrK5ZBvqNXpfxz5iLrlGTmEfhbQyH46A==", + "version": "6.26.1", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.26.1.tgz", + "integrity": "sha512-wLw0t3R9AwOSQThdZ5Onw8QQtem5asE7+bPlnzc57eubPqiuJKIzwjMZ+C42vQett+iva+J8VgFV4RYWDBh5FA==", "dev": true, "dependencies": { "@codemirror/state": "^6.4.0", @@ -472,9 +472,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.13.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.1.tgz", - "integrity": "sha512-4C4UERETjXpC4WpBXDbkgNVgHyWfG3B/NKY46e7w5H134UDOFqUJKpsLm0UYmuupW+aJmRgeScrDNfvZ5WV80A==", + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.2.tgz", + "integrity": "sha512-3XFIDKWMFZrMnao1mJhnOT1h2g0169Os848NhhmGweEcfJ4rCi+3yMCOLG4zA61rbJdkcrM/DjVZm9Hg5p5w7g==", "cpu": [ "arm" ], @@ -485,9 +485,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.13.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.1.tgz", - "integrity": "sha512-TrTaFJ9pXgfXEiJKQ3yQRelpQFqgRzVR9it8DbeRzG0RX7mKUy0bqhCFsgevwXLJepQKTnLl95TnPGf9T9AMOA==", + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.2.tgz", + "integrity": "sha512-GdxxXbAuM7Y/YQM9/TwwP+L0omeE/lJAR1J+olu36c3LqqZEBdsIWeQ91KBe6nxwOnb06Xh7JS2U5ooWU5/LgQ==", "cpu": [ "arm64" ], @@ -498,9 +498,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.13.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.1.tgz", - "integrity": "sha512-fz7jN6ahTI3cKzDO2otQuybts5cyu0feymg0bjvYCBrZQ8tSgE8pc0sSNEuGvifrQJWiwx9F05BowihmLxeQKw==", + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.2.tgz", + "integrity": "sha512-mCMlpzlBgOTdaFs83I4XRr8wNPveJiJX1RLfv4hggyIVhfB5mJfN4P8Z6yKh+oE4Luz+qq1P3kVdWrCKcMYrrA==", "cpu": [ "arm64" ], @@ -511,9 +511,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.13.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.1.tgz", - "integrity": "sha512-WTvdz7SLMlJpektdrnWRUN9C0N2qNHwNbWpNo0a3Tod3gb9leX+yrYdCeB7VV36OtoyiPAivl7/xZ3G1z5h20g==", + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.2.tgz", + "integrity": "sha512-yUoEvnH0FBef/NbB1u6d3HNGyruAKnN74LrPAfDQL3O32e3k3OSfLrPgSJmgb3PJrBZWfPyt6m4ZhAFa2nZp2A==", "cpu": [ "x64" ], @@ -524,9 +524,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.13.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.1.tgz", - "integrity": "sha512-dBHQl+7wZzBYcIF6o4k2XkAfwP2ks1mYW2q/Gzv9n39uDcDiAGDqEyml08OdY0BIct0yLSPkDTqn4i6czpBLLw==", + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.2.tgz", + "integrity": "sha512-GYbLs5ErswU/Xs7aGXqzc3RrdEjKdmoCrgzhJWyFL0r5fL3qd1NPcDKDowDnmcoSiGJeU68/Vy+OMUluRxPiLQ==", "cpu": [ "arm" ], @@ -537,9 +537,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.13.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.1.tgz", - "integrity": "sha512-bur4JOxvYxfrAmocRJIW0SADs3QdEYK6TQ7dTNz6Z4/lySeu3Z1H/+tl0a4qDYv0bCdBpUYM0sYa/X+9ZqgfSQ==", + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.2.tgz", + "integrity": "sha512-L1+D8/wqGnKQIlh4Zre9i4R4b4noxzH5DDciyahX4oOz62CphY7WDWqJoQ66zNR4oScLNOqQJfNSIAe/6TPUmQ==", "cpu": [ "arm64" ], @@ -550,9 +550,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.13.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.1.tgz", - "integrity": "sha512-ssp77SjcDIUSoUyj7DU7/5iwM4ZEluY+N8umtCT9nBRs3u045t0KkW02LTyHouHDomnMXaXSZcCSr2bdMK63kA==", + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.2.tgz", + "integrity": "sha512-tK5eoKFkXdz6vjfkSTCupUzCo40xueTOiOO6PeEIadlNBkadH1wNOH8ILCPIl8by/Gmb5AGAeQOFeLev7iZDOA==", "cpu": [ "arm64" ], @@ -562,10 +562,23 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.13.2.tgz", + "integrity": "sha512-zvXvAUGGEYi6tYhcDmb9wlOckVbuD+7z3mzInCSTACJ4DQrdSLPNUeDIcAQW39M3q6PDquqLWu7pnO39uSMRzQ==", + "cpu": [ + "ppc64le" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.13.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.1.tgz", - "integrity": "sha512-Jv1DkIvwEPAb+v25/Unrnnq9BO3F5cbFPT821n3S5litkz+O5NuXuNhqtPx5KtcwOTtaqkTsO+IVzJOsxd11aQ==", + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.2.tgz", + "integrity": "sha512-C3GSKvMtdudHCN5HdmAMSRYR2kkhgdOfye4w0xzyii7lebVr4riCgmM6lRiSCnJn2w1Xz7ZZzHKuLrjx5620kw==", "cpu": [ "riscv64" ], @@ -576,9 +589,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.13.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.13.1.tgz", - "integrity": "sha512-U564BrhEfaNChdATQaEODtquCC7Ez+8Hxz1h5MAdMYj0AqD0GA9rHCpElajb/sQcaFL6NXmHc5O+7FXpWMa73Q==", + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.13.2.tgz", + "integrity": "sha512-l4U0KDFwzD36j7HdfJ5/TveEQ1fUTjFFQP5qIt9gBqBgu1G8/kCaq5Ok05kd5TG9F8Lltf3MoYsUMw3rNlJ0Yg==", "cpu": [ "s390x" ], @@ -589,9 +602,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.13.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.1.tgz", - "integrity": "sha512-zGRDulLTeDemR8DFYyFIQ8kMP02xpUsX4IBikc7lwL9PrwR3gWmX2NopqiGlI2ZVWMl15qZeUjumTwpv18N7sQ==", + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.2.tgz", + "integrity": "sha512-xXMLUAMzrtsvh3cZ448vbXqlUa7ZL8z0MwHp63K2IIID2+DeP5iWIT6g1SN7hg1VxPzqx0xZdiDM9l4n9LRU1A==", "cpu": [ "x64" ], @@ -602,9 +615,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.13.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.1.tgz", - "integrity": "sha512-VTk/MveyPdMFkYJJPCkYBw07KcTkGU2hLEyqYMsU4NjiOfzoaDTW9PWGRsNwiOA3qI0k/JQPjkl/4FCK1smskQ==", + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.2.tgz", + "integrity": "sha512-M/JYAWickafUijWPai4ehrjzVPKRCyDb1SLuO+ZyPfoXgeCEAlgPkNXewFZx0zcnoIe3ay4UjXIMdXQXOZXWqA==", "cpu": [ "x64" ], @@ -615,9 +628,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.13.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.1.tgz", - "integrity": "sha512-L+hX8Dtibb02r/OYCsp4sQQIi3ldZkFI0EUkMTDwRfFykXBPptoz/tuuGqEd3bThBSLRWPR6wsixDSgOx/U3Zw==", + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.2.tgz", + "integrity": "sha512-2YWwoVg9KRkIKaXSh0mz3NmfurpmYoBBTAXA9qt7VXk0Xy12PoOP40EFuau+ajgALbbhi4uTj3tSG3tVseCjuA==", "cpu": [ "arm64" ], @@ -628,9 +641,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.13.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.1.tgz", - "integrity": "sha512-+dI2jVPfM5A8zme8riEoNC7UKk0Lzc7jCj/U89cQIrOjrZTCWZl/+IXUeRT2rEZ5j25lnSA9G9H1Ob9azaF/KQ==", + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.2.tgz", + "integrity": "sha512-2FSsE9aQ6OWD20E498NYKEQLneShWes0NGMPQwxWOdws35qQXH+FplabOSP5zEe1pVjurSDOGEVCE2agFwSEsw==", "cpu": [ "ia32" ], @@ -641,9 +654,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.13.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.1.tgz", - "integrity": "sha512-YY1Exxo2viZ/O2dMHuwQvimJ0SqvL+OAWQLLY6rvXavgQKjhQUzn7nc1Dd29gjB5Fqi00nrBWctJBOyfVMIVxw==", + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.2.tgz", + "integrity": "sha512-7h7J2nokcdPePdKykd8wtc8QqqkqxIrUz7MHj6aNr8waBRU//NLDVnNjQnqQO6fqtjrtCdftpbTuOKAyrAQETQ==", "cpu": [ "x64" ], @@ -2427,9 +2440,9 @@ } }, "node_modules/polyscript": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/polyscript/-/polyscript-0.11.3.tgz", - "integrity": "sha512-0wzunsH9b/h+EoB0tZKMRUg0YoFHraRuBuPK+cJsmUmNr1bVM1LvzFk21grDstXjo6Ah78dEC7SjaA0sSBsJeQ==", + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/polyscript/-/polyscript-0.12.1.tgz", + "integrity": "sha512-fQtpelwephyl34UMHxPwKNpGkUDbAIEMa09EFLthBpvThRSmrNQrus/WuouGKjqedK5dv+qCwP5RU1Lbe1bQLA==", "dependencies": { "@ungap/structured-clone": "^1.2.0", "@ungap/with-resolvers": "^0.1.0", @@ -3149,9 +3162,9 @@ } }, "node_modules/rollup": { - "version": "4.13.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.1.tgz", - "integrity": "sha512-hFi+fU132IvJ2ZuihN56dwgpltpmLZHZWsx27rMCTZ2sYwrqlgL5sECGy1eeV2lAihD8EzChBVVhsXci0wD4Tg==", + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.2.tgz", + "integrity": "sha512-MIlLgsdMprDBXC+4hsPgzWUasLO9CE4zOkj/u6j+Z6j5A4zRY+CtiXAdJyPtgCsc42g658Aeh1DlrdVEJhsL2g==", "dev": true, "dependencies": { "@types/estree": "1.0.5" @@ -3164,20 +3177,21 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.13.1", - "@rollup/rollup-android-arm64": "4.13.1", - "@rollup/rollup-darwin-arm64": "4.13.1", - "@rollup/rollup-darwin-x64": "4.13.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.13.1", - "@rollup/rollup-linux-arm64-gnu": "4.13.1", - "@rollup/rollup-linux-arm64-musl": "4.13.1", - "@rollup/rollup-linux-riscv64-gnu": "4.13.1", - "@rollup/rollup-linux-s390x-gnu": "4.13.1", - "@rollup/rollup-linux-x64-gnu": "4.13.1", - "@rollup/rollup-linux-x64-musl": "4.13.1", - "@rollup/rollup-win32-arm64-msvc": "4.13.1", - "@rollup/rollup-win32-ia32-msvc": "4.13.1", - "@rollup/rollup-win32-x64-msvc": "4.13.1", + "@rollup/rollup-android-arm-eabi": "4.13.2", + "@rollup/rollup-android-arm64": "4.13.2", + "@rollup/rollup-darwin-arm64": "4.13.2", + "@rollup/rollup-darwin-x64": "4.13.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.13.2", + "@rollup/rollup-linux-arm64-gnu": "4.13.2", + "@rollup/rollup-linux-arm64-musl": "4.13.2", + "@rollup/rollup-linux-powerpc64le-gnu": "4.13.2", + "@rollup/rollup-linux-riscv64-gnu": "4.13.2", + "@rollup/rollup-linux-s390x-gnu": "4.13.2", + "@rollup/rollup-linux-x64-gnu": "4.13.2", + "@rollup/rollup-linux-x64-musl": "4.13.2", + "@rollup/rollup-win32-arm64-msvc": "4.13.2", + "@rollup/rollup-win32-ia32-msvc": "4.13.2", + "@rollup/rollup-win32-x64-msvc": "4.13.2", "fsevents": "~2.3.2" } }, diff --git a/pyscript.core/package.json b/pyscript.core/package.json index f7b04a6d..0b1e7fc8 100644 --- a/pyscript.core/package.json +++ b/pyscript.core/package.json @@ -1,6 +1,6 @@ { "name": "@pyscript/core", - "version": "0.4.11", + "version": "0.4.12", "type": "module", "description": "PyScript", "module": "./index.js", @@ -42,17 +42,17 @@ "dependencies": { "@ungap/with-resolvers": "^0.1.0", "basic-devtools": "^0.1.6", - "polyscript": "^0.11.3", + "polyscript": "^0.12.1", "sticky-module": "^0.1.1", "to-json-callback": "^0.1.1", "type-checked-collections": "^0.1.7" }, "devDependencies": { "@codemirror/commands": "^6.3.3", - "@codemirror/lang-python": "^6.1.4", + "@codemirror/lang-python": "^6.1.5", "@codemirror/language": "^6.10.1", "@codemirror/state": "^6.4.1", - "@codemirror/view": "^6.26.0", + "@codemirror/view": "^6.26.1", "@playwright/test": "^1.42.1", "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-node-resolve": "^15.2.3", @@ -62,7 +62,7 @@ "chokidar": "^3.6.0", "codemirror": "^6.0.1", "eslint": "^8.57.0", - "rollup": "^4.13.1", + "rollup": "^4.13.2", "rollup-plugin-postcss": "^4.0.2", "rollup-plugin-string": "^3.0.0", "static-handler": "^0.4.3", diff --git a/pyscript.core/src/plugins/py-terminal.js b/pyscript.core/src/plugins/py-terminal.js index bd66156b..83301d0d 100644 --- a/pyscript.core/src/plugins/py-terminal.js +++ b/pyscript.core/src/plugins/py-terminal.js @@ -1,11 +1,10 @@ // PyScript py-terminal plugin import { TYPES, hooks } from "../core.js"; import { notify } from "./error.js"; -import { defineProperty } from "polyscript/exports"; +import { customObserver, defineProperty } from "polyscript/exports"; -const SELECTOR = [...TYPES.keys()] - .map((type) => `script[type="${type}"][terminal],${type}-script[terminal]`) - .join(","); +// will contain all valid selectors +const SELECTORS = []; // show the error on main and // stops the module from keep executing @@ -14,8 +13,6 @@ const notifyAndThrow = (message) => { throw new Error(message); }; -const notParsedYet = (script) => !bootstrapped.has(script); - const onceOnMain = ({ attributes: { worker } }) => !worker; const bootstrapped = new WeakSet(); @@ -25,11 +22,68 @@ let addStyle = true; // this callback will be serialized as string and it never needs // to be invoked multiple times. Each xworker here is bootstrapped // only once thanks to the `sync.is_pyterminal()` check. -const workerReady = ({ interpreter, io, run }, { sync }) => { +const workerReady = ({ interpreter, io, run, type }, { sync }) => { if (!sync.is_pyterminal()) return; // in workers it's always safe to grab the polyscript currentScript - run("from polyscript.currentScript import terminal as __terminal__"); + // the ugly `_` dance is due MicroPython not able to import via: + // `from polyscript.currentScript import terminal as __terminal__` + run( + "from polyscript import currentScript as _; __terminal__ = _.terminal; del _", + ); + + // This part is shared among both Pyodide and MicroPython + io.stderr = (error) => { + sync.pyterminal_write(`${error.message || error}\n`); + }; + + const isMicroPython = type === "mpy"; + + // MicroPython has no code or code.interact() + // This part patches it in a way that simulate + // the code.interact() module in Pyodide. + if (isMicroPython) { + const encoder = new TextEncoder(); + const processData = () => { + if (data.length) { + for ( + let i = 0, b = encoder.encode(`${data}\r`); + i < b.length; + i++ + ) { + const code = interpreter.replProcessChar(b[i]); + if (code) { + throw new Error( + `replProcessChar failed with code ${code}`, + ); + } + } + } + data = ">>> "; + data = io.stdin(); + processData(); + }; + interpreter.setStderr = Object; // as no-op + interpreter.setStdout = ({ write }) => { + io.stdout = (str) => { + // avoid duplicated outcome due i/o + readline + const ignore = str.startsWith(`>>> ${data}`); + return ignore ? 0 : write(`${str}\n`); + }; + }; + interpreter.setStdin = ({ stdin }) => { + io.stdin = stdin; + }; + // tiny shim of the code module with only interact + // to bootstrap a REPL like environment + interpreter.registerJsModule("code", { + interact() { + interpreter.replInit(); + data = ""; + processData(); + }, + }); + } // This part is inevitably duplicated as external scope // can't be reached by workers out of the box. @@ -39,7 +93,7 @@ const workerReady = ({ interpreter, io, run }, { sync }) => { const generic = { isatty: true, write(buffer) { - data = decoder.decode(buffer); + data = isMicroPython ? buffer : decoder.decode(buffer); sync.pyterminal_write(data); return buffer.length; }, @@ -50,38 +104,9 @@ const workerReady = ({ interpreter, io, run }, { sync }) => { isatty: true, stdin: () => sync.pyterminal_read(data), }); - - io.stderr = (error) => { - sync.pyterminal_write(`${error.message || error}\n`); - }; }; -const pyTerminal = async () => { - const terminals = document.querySelectorAll(SELECTOR); - - const unknown = [].filter.call(terminals, notParsedYet); - - // no results will look further for runtime nodes - if (!unknown.length) return; - // early flag elements as known to avoid concurrent - // MutationObserver invokes of this async handler - else unknown.forEach(bootstrapped.add, bootstrapped); - - // we currently support only one terminal as in "classic" - if ([].filter.call(terminals, onceOnMain).length > 1) - notifyAndThrow("You can use at most 1 main terminal"); - - // import styles lazily - if (addStyle) { - addStyle = false; - document.head.append( - Object.assign(document.createElement("link"), { - rel: "stylesheet", - href: new URL("./xterm.css", import.meta.url), - }), - ); - } - +const pyTerminal = async (element) => { // lazy load these only when a valid terminal is found const [{ Terminal }, { Readline }, { FitAddon }] = await Promise.all([ import(/* webpackIgnore: true */ "../3rd-party/xterm.js"), @@ -89,118 +114,140 @@ const pyTerminal = async () => { import(/* webpackIgnore: true */ "../3rd-party/xterm_addon-fit.js"), ]); - for (const element of unknown) { - // hopefully to be removed in the near future! - if (element.matches('script[type="mpy"],mpy-script')) - notifyAndThrow("Unsupported terminal."); + const readline = new Readline(); - const readline = new Readline(); - - // common main thread initialization for both worker - // or main case, bootstrapping the terminal on its target - const init = (options) => { - let target = element; - const selector = element.getAttribute("target"); - if (selector) { - target = - document.getElementById(selector) || - document.querySelector(selector); - if (!target) throw new Error(`Unknown target ${selector}`); - } else { - target = document.createElement("py-terminal"); - target.style.display = "block"; - element.after(target); - } - const terminal = new Terminal({ - theme: { - background: "#191A19", - foreground: "#F5F2E7", - }, - ...options, - }); - const fitAddon = new FitAddon(); - terminal.loadAddon(fitAddon); - terminal.loadAddon(readline); - terminal.open(target); - fitAddon.fit(); - terminal.focus(); - defineProperty(element, "terminal", { value: terminal }); - return terminal; - }; - - // branch logic for the worker - if (element.hasAttribute("worker")) { - // add a hook on the main thread to setup all sync helpers - // also bootstrapping the XTerm target on main *BUT* ... - hooks.main.onWorker.add(function worker(_, xworker) { - // ... as multiple workers will add multiple callbacks - // be sure no xworker is ever initialized twice! - if (bootstrapped.has(xworker)) return; - bootstrapped.add(xworker); - - // still cleanup this callback for future scripts/workers - hooks.main.onWorker.delete(worker); - - init({ - disableStdin: false, - cursorBlink: true, - cursorStyle: "block", - }); - - xworker.sync.is_pyterminal = () => true; - xworker.sync.pyterminal_read = readline.read.bind(readline); - xworker.sync.pyterminal_write = readline.write.bind(readline); - }); - - // setup remote thread JS/Python code for whenever the - // worker is ready to become a terminal - hooks.worker.onReady.add(workerReady); + // common main thread initialization for both worker + // or main case, bootstrapping the terminal on its target + const init = (options) => { + let target = element; + const selector = element.getAttribute("target"); + if (selector) { + target = + document.getElementById(selector) || + document.querySelector(selector); + if (!target) throw new Error(`Unknown target ${selector}`); } else { - // in the main case, just bootstrap XTerm without - // allowing any input as that's not possible / awkward - hooks.main.onReady.add(function main({ interpreter, io, run }) { - console.warn("py-terminal is read only on main thread"); - hooks.main.onReady.delete(main); - - // on main, it's easy to trash and clean the current terminal - globalThis.__py_terminal__ = init({ - disableStdin: true, - cursorBlink: false, - cursorStyle: "underline", - }); - run("from js import __py_terminal__ as __terminal__"); - delete globalThis.__py_terminal__; - - // This part is inevitably duplicated as external scope - // can't be reached by workers out of the box. - // The detail is that here we use readline here, not sync. - const decoder = new TextDecoder(); - let data = ""; - const generic = { - isatty: true, - write(buffer) { - data = decoder.decode(buffer); - readline.write(data); - return buffer.length; - }, - }; - interpreter.setStdout(generic); - interpreter.setStderr(generic); - interpreter.setStdin({ - isatty: true, - stdin: () => readline.read(data), - }); - - io.stderr = (error) => { - readline.write(`${error.message || error}\n`); - }; - }); + target = document.createElement("py-terminal"); + target.style.display = "block"; + element.after(target); } + const terminal = new Terminal({ + theme: { + background: "#191A19", + foreground: "#F5F2E7", + }, + ...options, + }); + const fitAddon = new FitAddon(); + terminal.loadAddon(fitAddon); + terminal.loadAddon(readline); + terminal.open(target); + fitAddon.fit(); + terminal.focus(); + defineProperty(element, "terminal", { value: terminal }); + return terminal; + }; + + // branch logic for the worker + if (element.hasAttribute("worker")) { + // add a hook on the main thread to setup all sync helpers + // also bootstrapping the XTerm target on main *BUT* ... + hooks.main.onWorker.add(function worker(_, xworker) { + // ... as multiple workers will add multiple callbacks + // be sure no xworker is ever initialized twice! + if (bootstrapped.has(xworker)) return; + bootstrapped.add(xworker); + + // still cleanup this callback for future scripts/workers + hooks.main.onWorker.delete(worker); + + init({ + disableStdin: false, + cursorBlink: true, + cursorStyle: "block", + }); + + xworker.sync.is_pyterminal = () => true; + xworker.sync.pyterminal_read = readline.read.bind(readline); + xworker.sync.pyterminal_write = readline.write.bind(readline); + }); + + // setup remote thread JS/Python code for whenever the + // worker is ready to become a terminal + hooks.worker.onReady.add(workerReady); + } else { + // in the main case, just bootstrap XTerm without + // allowing any input as that's not possible / awkward + hooks.main.onReady.add(function main({ interpreter, io, run, type }) { + console.warn("py-terminal is read only on main thread"); + hooks.main.onReady.delete(main); + + // on main, it's easy to trash and clean the current terminal + globalThis.__py_terminal__ = init({ + disableStdin: true, + cursorBlink: false, + cursorStyle: "underline", + }); + run("from js import __py_terminal__ as __terminal__"); + delete globalThis.__py_terminal__; + + io.stderr = (error) => { + readline.write(`${error.message || error}\n`); + }; + + const isMicroPython = type === "mpy"; + + if (isMicroPython) { + interpreter.setStderr = Object; // as no-op + interpreter.setStdin = Object; // as no-op + interpreter.setStdout = ({ write }) => { + io.stdout = (str) => write(`${str}\n`); + }; + } + + // This part is inevitably duplicated as external scope + // can't be reached by workers out of the box. + // The detail is that here we use readline here, not sync. + const decoder = new TextDecoder(); + let data = ""; + const generic = { + isatty: true, + write(buffer) { + data = isMicroPython ? buffer : decoder.decode(buffer); + readline.write(data); + return buffer.length; + }, + }; + interpreter.setStdout(generic); + interpreter.setStderr(generic); + interpreter.setStdin({ + isatty: true, + stdin: () => readline.read(data), + }); + }); } }; -const mo = new MutationObserver(pyTerminal); -mo.observe(document, { childList: true, subtree: true }); +for (const key of TYPES.keys()) { + const selector = `script[type="${key}"][terminal],${key}-script[terminal]`; + SELECTORS.push(selector); + customObserver.set(selector, async (element) => { + // we currently support only one terminal on main as in "classic" + const terminals = document.querySelectorAll(SELECTORS.join(",")); + if ([].filter.call(terminals, onceOnMain).length > 1) + notifyAndThrow("You can use at most 1 main terminal"); -// try to check the current document ASAP -export default pyTerminal(); + // import styles lazily + if (addStyle) { + addStyle = false; + document.head.append( + Object.assign(document.createElement("link"), { + rel: "stylesheet", + href: new URL("./xterm.css", import.meta.url), + }), + ); + } + + await pyTerminal(element); + }); +} diff --git a/pyscript.core/test/py-terminal.html b/pyscript.core/test/py-terminal.html index ebf7f04d..d917be11 100644 --- a/pyscript.core/test/py-terminal.html +++ b/pyscript.core/test/py-terminal.html @@ -13,7 +13,7 @@ def greetings(event): print('hello world') - + diff --git a/pyscript.core/types/plugins/py-terminal.d.ts b/pyscript.core/types/plugins/py-terminal.d.ts index 35a35db4..cb0ff5c3 100644 --- a/pyscript.core/types/plugins/py-terminal.d.ts +++ b/pyscript.core/types/plugins/py-terminal.d.ts @@ -1,2 +1 @@ -declare const _default: Promise; -export default _default; +export {};