diff --git a/pyscript.core/package-lock.json b/pyscript.core/package-lock.json index 455afdf1..c7d2edf8 100644 --- a/pyscript.core/package-lock.json +++ b/pyscript.core/package-lock.json @@ -11,21 +11,17 @@ "dependencies": { "@ungap/with-resolvers": "^0.1.0", "basic-devtools": "^0.1.6", - "polyscript": "^0.6.2", "sticky-module": "^0.1.1", "to-json-callback": "^0.1.1", "type-checked-collections": "^0.1.7" }, "devDependencies": { - "@playwright/test": "^1.40.1", "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-terser": "^0.4.4", "@webreflection/toml-j0.4": "^1.1.3", "@xterm/addon-fit": "^0.9.0-beta.1", "chokidar": "^3.5.3", - "eslint": "^8.54.0", - "rollup": "^4.6.1", "rollup-plugin-postcss": "^4.0.2", "rollup-plugin-string": "^3.0.0", "static-handler": "^0.4.3", @@ -43,6 +39,100 @@ "node": ">=0.10.0" } }, + "node_modules/@codemirror/autocomplete": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.11.0.tgz", + "integrity": "sha512-LCPH3W+hl5vcO7OzEQgX6NpKuKVyiKFLGAy7FXROF6nUpsWUdQEgUb3fe/g7B0E1KZCRFfgzdKASt6Wly2UOBg==", + "dev": true, + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0" + }, + "peerDependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@codemirror/commands": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.3.0.tgz", + "integrity": "sha512-tFfcxRIlOWiQDFhjBSWJ10MxcvbCIsRr6V64SgrcaY0MwNk32cUOcCuNlWo8VjV4qRQCgNgUAnIeo0svkk4R5Q==", + "dev": true, + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.2.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.1.0" + } + }, + "node_modules/@codemirror/lang-python": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/@codemirror/lang-python/-/lang-python-6.1.3.tgz", + "integrity": "sha512-S9w2Jl74hFlD5nqtUMIaXAq9t5WlM0acCkyuQWUUSvZclk1sV+UfnpFiZzuZSG+hfEaOmxKR5UxY/Uxswn7EhQ==", + "dev": true, + "dependencies": { + "@codemirror/autocomplete": "^6.3.2", + "@codemirror/language": "^6.8.0", + "@lezer/python": "^1.1.4" + } + }, + "node_modules/@codemirror/language": { + "version": "6.9.2", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.9.2.tgz", + "integrity": "sha512-QGTQXSpAKDIzaSE96zNK1UfIUhPgkT1CLjh1N5qVzZuxgsEOhz5RqaN8QCIdyOQklGLx3MgHd9YrE3X3+Pl1ow==", + "dev": true, + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.1.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0", + "style-mod": "^4.0.0" + } + }, + "node_modules/@codemirror/lint": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.4.2.tgz", + "integrity": "sha512-wzRkluWb1ptPKdzlsrbwwjYCPLgzU6N88YBAmlZi8WFyuiEduSd05MnJYNogzyc8rPK7pj6m95ptUApc8sHKVA==", + "dev": true, + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/search": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.4.tgz", + "integrity": "sha512-YoTrvjv9e8EbPs58opjZKyJ3ewFrVSUzQ/4WXlULQLSDDr1nGPJ67mMXFNNVYwdFhybzhrzrtqgHmtpJwIF+8g==", + "dev": true, + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/state": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.3.1.tgz", + "integrity": "sha512-88e4HhMtKJyw6fKprGaN/yZfiaoGYOi2nM45YCUC6R/kex9sxFWBDGatS1vk4lMgnWmdIIB9tk8Gj1LmL8YfvA==", + "dev": true + }, + "node_modules/@codemirror/view": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.22.0.tgz", + "integrity": "sha512-6zLj4YIoIpfTGKrDMTbeZRpa8ih4EymMCKmddEDcJWrCdp/N1D46B38YEz4creTb4T177AVS9EyXkLeC/HL2jA==", + "dev": true, + "dependencies": { + "@codemirror/state": "^6.1.4", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -190,6 +280,40 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@lezer/common": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.1.1.tgz", + "integrity": "sha512-aAPB9YbvZHqAW+bIwiuuTDGB4DG0sYNRObGLxud8cW7osw1ZQxfDuTZ8KQiqfZ0QJGcR34CvpTMDXEyo/+Htgg==", + "dev": true + }, + "node_modules/@lezer/highlight": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.0.tgz", + "integrity": "sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA==", + "dev": true, + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lezer/lr": { + "version": "1.3.14", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.3.14.tgz", + "integrity": "sha512-z5mY4LStlA3yL7aHT/rqgG614cfcvklS+8oFRFBYrs4YaWLJyKKM4+nN6KopToX0o9Hj6zmH6M5kinOYuy06ug==", + "dev": true, + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lezer/python": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@lezer/python/-/python-1.1.9.tgz", + "integrity": "sha512-8Ua3p8NdICXR6qWvRCnCx5CI1B0DklZGNtRLwOrIS/OHecHIugRHZyr0NsaaQO2H2Nn34EPlRtltXIirLsry5Q==", + "dev": true, + "dependencies": { + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -375,9 +499,6 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.6.1.tgz", - "integrity": "sha512-0WQ0ouLejaUCRsL93GD4uft3rOmB8qoQMU05Kb8CmMtMBe7XUDLAltxVZI1q6byNqEtU7N1ZX1Vw5lIpgulLQA==", "cpu": [ "arm" ], @@ -388,9 +509,6 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.6.1.tgz", - "integrity": "sha512-1TKm25Rn20vr5aTGGZqo6E4mzPicCUD79k17EgTLAsXc1zysyi4xXKACfUbwyANEPAEIxkzwue6JZ+stYzWUTA==", "cpu": [ "arm64" ], @@ -401,9 +519,6 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.6.1.tgz", - "integrity": "sha512-cEXJQY/ZqMACb+nxzDeX9IPLAg7S94xouJJCNVE5BJM8JUEP4HeTF+ti3cmxWeSJo+5D+o8Tc0UAWUkfENdeyw==", "cpu": [ "arm64" ], @@ -414,9 +529,6 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.6.1.tgz", - "integrity": "sha512-LoSU9Xu56isrkV2jLldcKspJ7sSXmZWkAxg7sW/RfF7GS4F5/v4EiqKSMCFbZtDu2Nc1gxxFdQdKwkKS4rwxNg==", "cpu": [ "x64" ], @@ -427,9 +539,6 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.6.1.tgz", - "integrity": "sha512-EfI3hzYAy5vFNDqpXsNxXcgRDcFHUWSx5nnRSCKwXuQlI5J9dD84g2Usw81n3FLBNsGCegKGwwTVsSKK9cooSQ==", "cpu": [ "arm" ], @@ -440,9 +549,6 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.6.1.tgz", - "integrity": "sha512-9lhc4UZstsegbNLhH0Zu6TqvDfmhGzuCWtcTFXY10VjLLUe4Mr0Ye2L3rrtHaDd/J5+tFMEuo5LTCSCMXWfUKw==", "cpu": [ "arm64" ], @@ -453,9 +559,6 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.6.1.tgz", - "integrity": "sha512-FfoOK1yP5ksX3wwZ4Zk1NgyGHZyuRhf99j64I5oEmirV8EFT7+OhUZEnP+x17lcP/QHJNWGsoJwrz4PJ9fBEXw==", "cpu": [ "arm64" ], @@ -466,9 +569,6 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.6.1.tgz", - "integrity": "sha512-DNGZvZDO5YF7jN5fX8ZqmGLjZEXIJRdJEdTFMhiyXqyXubBa0WVLDWSNlQ5JR2PNgDbEV1VQowhVRUh+74D+RA==", "cpu": [ "x64" ], @@ -479,9 +579,6 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.6.1.tgz", - "integrity": "sha512-RkJVNVRM+piYy87HrKmhbexCHg3A6Z6MU0W9GHnJwBQNBeyhCJG9KDce4SAMdicQnpURggSvtbGo9xAWOfSvIQ==", "cpu": [ "x64" ], @@ -492,9 +589,6 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.6.1.tgz", - "integrity": "sha512-v2FVT6xfnnmTe3W9bJXl6r5KwJglMK/iRlkKiIFfO6ysKs0rDgz7Cwwf3tjldxQUrHL9INT/1r4VA0n9L/F1vQ==", "cpu": [ "arm64" ], @@ -505,9 +599,6 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.6.1.tgz", - "integrity": "sha512-YEeOjxRyEjqcWphH9dyLbzgkF8wZSKAKUkldRY6dgNR5oKs2LZazqGB41cWJ4Iqqcy9/zqYgmzBkRoVz3Q9MLw==", "cpu": [ "ia32" ], @@ -518,9 +609,6 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.6.1.tgz", - "integrity": "sha512-0zfTlFAIhgz8V2G8STq8toAjsYYA6eci1hnXuyOTUFnymrtJwnS6uGKiv3v5UrPZkBlamLvrLV2iiaeqCKzb0A==", "cpu": [ "x64" ], @@ -858,6 +946,21 @@ "plain-tag": "^0.1.3" } }, + "node_modules/codemirror": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz", + "integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==", + "dev": true, + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/commands": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/search": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + } + }, "node_modules/coincident": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/coincident/-/coincident-1.1.0.tgz", @@ -926,6 +1029,12 @@ "source-map": "^0.6.1" } }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", + "dev": true + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -2242,9 +2351,6 @@ } }, "node_modules/polyscript": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/polyscript/-/polyscript-0.6.2.tgz", - "integrity": "sha512-JL3aIodfdXVQy65iPqjPxbSHzSGJdyf5Z9CuESZVJobcOcOvymjFgsZeoktz0e6PHqM4YSggtKQ9bN9HzlgMBg==", "dependencies": { "@ungap/structured-clone": "^1.2.0", "@ungap/with-resolvers": "^0.1.0", @@ -2941,9 +3047,6 @@ } }, "node_modules/rollup": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.6.1.tgz", - "integrity": "sha512-jZHaZotEHQaHLgKr8JnQiDT1rmatjgKlMekyksz+yk9jt/8z9quNjnKNRoaM0wd9DC2QKXjmWWuDYtM3jfF8pQ==", "dev": true, "bin": { "rollup": "dist/bin/rollup" @@ -2953,18 +3056,6 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.6.1", - "@rollup/rollup-android-arm64": "4.6.1", - "@rollup/rollup-darwin-arm64": "4.6.1", - "@rollup/rollup-darwin-x64": "4.6.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.6.1", - "@rollup/rollup-linux-arm64-gnu": "4.6.1", - "@rollup/rollup-linux-arm64-musl": "4.6.1", - "@rollup/rollup-linux-x64-gnu": "4.6.1", - "@rollup/rollup-linux-x64-musl": "4.6.1", - "@rollup/rollup-win32-arm64-msvc": "4.6.1", - "@rollup/rollup-win32-ia32-msvc": "4.6.1", - "@rollup/rollup-win32-x64-msvc": "4.6.1", "fsevents": "~2.3.2" } }, @@ -3210,6 +3301,12 @@ "integrity": "sha512-IezA2qp+vcdlhJaVm5SOdPPTUu0FCEqfNSli2vRuSIBbu5Nq5UvygTk/VzeCqfLz2Atj3dVII5QBKGZRZ0edzw==", "dev": true }, + "node_modules/style-mod": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.0.tgz", + "integrity": "sha512-Ca5ib8HrFn+f+0n4N4ScTIA9iTOQ7MaGS1ylHcoVqW9J7w2w8PzN6g9gKmTYgGEBH8e120+RCmhpje6jC5uGWA==", + "dev": true + }, "node_modules/stylehacks": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", @@ -3405,6 +3502,12 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "dev": true + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/pyscript.core/package.json b/pyscript.core/package.json index c0dfe871..9cc9eac4 100644 --- a/pyscript.core/package.json +++ b/pyscript.core/package.json @@ -25,11 +25,12 @@ "build:plugins": "node rollup/plugins.cjs", "build:stdlib": "node rollup/stdlib.cjs", "build:3rd-party": "node rollup/3rd-party.cjs", + "clean:3rd-party": "rm src/3rd-party/*.js && rm src/3rd-party/*.css", "test:mpy": "static-handler --coi . 2>/dev/null & SH_PID=$!; EXIT_CODE=0; playwright test --fully-parallel test/ || EXIT_CODE=$?; kill $SH_PID 2>/dev/null; exit $EXIT_CODE", "dev": "node dev.cjs", "release": "npm run build && npm run zip", - "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 .", + "size": "echo -e \"\\033[1mdist/*.js file size\\033[0m\"; for js in $(ls dist/*.js); do cat $js | brotli > ._; echo -e \"\\033[2m$js:\\033[0m $(du -h --apparent-size ._ | sed -e 's/[[:space:]]*._//')\"; rm ._; done", + "ts": "rm -rf types && tsc -p .", "zip": "zip -r dist.zip ./dist" }, "keywords": [ @@ -47,15 +48,21 @@ "type-checked-collections": "^0.1.7" }, "devDependencies": { - "@playwright/test": "^1.40.1", + "@codemirror/commands": "^6.3.0", + "@codemirror/lang-python": "^6.1.3", + "@codemirror/language": "^6.9.2", + "@codemirror/state": "^6.3.1", + "@codemirror/view": "^6.22.0", + "@playwright/test": "^1.39.0", "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-terser": "^0.4.4", "@webreflection/toml-j0.4": "^1.1.3", "@xterm/addon-fit": "^0.9.0-beta.1", "chokidar": "^3.5.3", - "eslint": "^8.54.0", - "rollup": "^4.6.1", + "codemirror": "^6.0.1", + "eslint": "^8.53.0", + "rollup": "^4.4.1", "rollup-plugin-postcss": "^4.0.2", "rollup-plugin-string": "^3.0.0", "static-handler": "^0.4.3", diff --git a/pyscript.core/rollup/3rd-party.cjs b/pyscript.core/rollup/3rd-party.cjs index c90a5b42..28c34beb 100644 --- a/pyscript.core/rollup/3rd-party.cjs +++ b/pyscript.core/rollup/3rd-party.cjs @@ -10,6 +10,9 @@ const { devDependencies } = require(join(__dirname, "..", "package.json")); const v = (name) => devDependencies[name].replace(/[^\d.]/g, ""); +const dropSourceMap = (str) => + str.replace(/^\/.+? sourceMappingURL=\/.+$/m, ""); + // Fetch a module via jsdelivr CDN `/+esm` orchestration // then sanitize the resulting outcome to avoid importing // anything via `/npm/...` through Rollup @@ -31,25 +34,41 @@ const resolve = (name) => { ); }; +// create a file rollup can then process and understand +const reBundle = (name) => Promise.resolve(`export * from "${name}";\n`); + // key/value pairs as: // "3rd-party/file-name.js" // string as content or // Promise as resolved content const modules = { + // toml "toml.js": join(node_modules, "@webreflection", "toml-j0.4", "toml.js"), + + // xterm "xterm.js": resolve("xterm"), - "xterm.css": fetch(`${CDN}/xterm@${v("xterm")}/css/xterm.min.css`).then( - (b) => b.text(), - ), "xterm-readline.js": resolve("xterm-readline"), "xterm_addon-fit.js": fetch(`${CDN}/@xterm/addon-fit/+esm`).then((b) => b.text(), ), + "xterm.css": fetch(`${CDN}/xterm@${v("xterm")}/css/xterm.min.css`).then( + (b) => b.text(), + ), + + // codemirror + "codemirror.js": reBundle("codemirror"), + "codemirror_state.js": reBundle("@codemirror/state"), + "codemirror_lang-python.js": reBundle("@codemirror/lang-python"), + "codemirror_language.js": reBundle("@codemirror/language"), + "codemirror_view.js": reBundle("@codemirror/view"), + "codemirror_commands.js": reBundle("@codemirror/commands"), }; for (const [target, source] of Object.entries(modules)) { if (typeof source === "string") copyFileSync(source, join(targets, target)); else { - source.then((text) => writeFileSync(join(targets, target), text)); + source.then((text) => + writeFileSync(join(targets, target), dropSourceMap(text)), + ); } } diff --git a/pyscript.core/src/plugins/py-editor.js b/pyscript.core/src/plugins/py-editor.js new file mode 100644 index 00000000..e4823fc9 --- /dev/null +++ b/pyscript.core/src/plugins/py-editor.js @@ -0,0 +1,228 @@ +// PyScript py-editor plugin +import { Hook, XWorker, dedent } from "polyscript/exports"; +import { TYPES } from "../core.js"; + +const RUN_BUTTON = ``; + +let id = 0; +const getID = (type) => `${type}-editor-${id++}`; + +const envs = new Map(); + +const hooks = { + worker: { + // works on both Pyodide and MicroPython + onReady: ({ runAsync, io }, { sync }) => { + io.stdout = (line) => sync.write(line); + io.stderr = (line) => sync.writeErr(line); + sync.revoke(); + sync.runAsync = runAsync; + }, + }, +}; + +async function execute({ currentTarget }) { + const { env, pySrc, outDiv } = this; + + currentTarget.disabled = true; + outDiv.innerHTML = ""; + + if (!envs.has(env)) { + const srcLink = URL.createObjectURL(new Blob([""])); + const xworker = XWorker.call(new Hook(null, hooks), srcLink, { + type: this.interpreter, + }); + + const { sync } = xworker; + const { promise, resolve } = Promise.withResolvers(); + envs.set(env, promise); + sync.revoke = () => { + URL.revokeObjectURL(srcLink); + resolve(xworker); + }; + } + + // wait for the env then set the target div + // before executing the current code + envs.get(env).then((xworker) => { + xworker.onerror = ({ error }) => { + outDiv.innerHTML += `${ + error.message || error + }`; + console.log(error); + }; + + const enable = () => { + currentTarget.disabled = false; + }; + const { sync } = xworker; + sync.write = (str) => { + outDiv.innerText += str; + }; + sync.writeErr = (str) => { + outDiv.innerHTML += `${str}`; + }; + sync.runAsync(pySrc).then(enable, enable); + }); +} + +const makeRunButton = (listener, type) => { + const runButton = document.createElement("button"); + runButton.className = `absolute ${type}-editor-run-button`; + runButton.innerHTML = RUN_BUTTON; + runButton.setAttribute("aria-label", "Python Script Run Button"); + runButton.addEventListener("click", listener); + return runButton; +}; + +const makeEditorDiv = (listener, type) => { + const editorDiv = document.createElement("div"); + editorDiv.className = `${type}-editor-input`; + editorDiv.setAttribute("aria-label", "Python Script Area"); + + const runButton = makeRunButton(listener, type); + const editorShadowContainer = document.createElement("div"); + + // avoid outer elements intercepting key events (reveal as example) + editorShadowContainer.addEventListener("keydown", (event) => { + event.stopPropagation(); + }); + + editorDiv.append(editorShadowContainer, runButton); + + return editorDiv; +}; + +const makeOutDiv = (type) => { + const outDiv = document.createElement("div"); + outDiv.className = `${type}-editor-output`; + outDiv.id = `${getID(type)}-output`; + return outDiv; +}; + +const makeBoxDiv = (listener, type) => { + const boxDiv = document.createElement("div"); + boxDiv.className = `${type}-editor-box`; + + const editorDiv = makeEditorDiv(listener, type); + const outDiv = makeOutDiv(type); + boxDiv.append(editorDiv, outDiv); + + return [boxDiv, outDiv]; +}; + +const init = async (script, type, interpreter) => { + const [ + { basicSetup, EditorView }, + { Compartment }, + { python }, + { indentUnit }, + { keymap }, + { defaultKeymap }, + ] = await Promise.all([ + // TODO: find a way to actually produce these bundles locally + import(/* webpackIgnore: true */ "../3rd-party/codemirror.js"), + import(/* webpackIgnore: true */ "../3rd-party/codemirror_state.js"), + import( + /* webpackIgnore: true */ "../3rd-party/codemirror_lang-python.js" + ), + import(/* webpackIgnore: true */ "../3rd-party/codemirror_language.js"), + import(/* webpackIgnore: true */ "../3rd-party/codemirror_view.js"), + import(/* webpackIgnore: true */ "../3rd-party/codemirror_commands.js"), + ]); + + const selector = script.getAttribute("target"); + + let target; + if (selector) { + target = + document.getElementById(selector) || + document.querySelector(selector); + if (!target) throw new Error(`Unknown target ${selector}`); + } else { + target = document.createElement(`${type}-editor`); + target.style.display = "block"; + script.after(target); + } + + if (!target.id) target.id = getID(type); + if (!target.hasAttribute("exec-id")) target.setAttribute("exec-id", 0); + if (!target.hasAttribute("root")) target.setAttribute("root", target.id); + + const env = `${interpreter}-${script.getAttribute("env") || getID(type)}`; + const context = { + interpreter, + env, + get pySrc() { + return editor.state.doc.toString(); + }, + get outDiv() { + return outDiv; + }, + }; + + // @see https://github.com/JeffersGlass/mkdocs-pyscript/blob/main/mkdocs_pyscript/js/makeblocks.js + const listener = execute.bind(context); + const [boxDiv, outDiv] = makeBoxDiv(listener, type); + + const inputChild = boxDiv.querySelector(`.${type}-editor-input > div`); + const parent = inputChild.attachShadow({ mode: "open" }); + // avoid inheriting styles from the outer component + parent.innerHTML = ``; + + target.appendChild(boxDiv); + + const doc = dedent(script.textContent).trim(); + + // preserve user indentation, if any + const indentation = /^(\s+)/m.test(doc) ? RegExp.$1 : " "; + + const editor = new EditorView({ + extensions: [ + indentUnit.of(indentation), + new Compartment().of(python()), + keymap.of([ + ...defaultKeymap, + { key: "Ctrl-Enter", run: listener, preventDefault: true }, + { key: "Cmd-Enter", run: listener, preventDefault: true }, + { key: "Shift-Enter", run: listener, preventDefault: true }, + ]), + basicSetup, + ], + parent, + doc, + }); + + editor.focus(); +}; + +// avoid too greedy MutationObserver operations at distance +let timeout = 0; + +// reset interval value then check for new scripts +const resetTimeout = () => { + timeout = 0; + pyEditor(); +}; + +// triggered both ASAP on the living DOM and via MutationObserver later +const pyEditor = async () => { + if (timeout) return; + timeout = setTimeout(resetTimeout, 250); + for (const [type, interpreter] of TYPES) { + const selector = `script[type="${type}-editor"]`; + for (const script of document.querySelectorAll(selector)) { + // avoid any further bootstrap + script.type += "-active"; + await init(script, type, interpreter); + } + } +}; + +new MutationObserver(pyEditor).observe(document, { + childList: true, + subtree: true, +}); + +// try to check the current document ASAP +export default pyEditor(); diff --git a/pyscript.core/test/py-editor.html b/pyscript.core/test/py-editor.html new file mode 100644 index 00000000..75040b02 --- /dev/null +++ b/pyscript.core/test/py-editor.html @@ -0,0 +1,53 @@ + + + + + + PyTerminal + + + + + + + + + + + diff --git a/pyscript.core/types/3rd-party/codemirror.d.ts b/pyscript.core/types/3rd-party/codemirror.d.ts new file mode 100644 index 00000000..2a1a953a --- /dev/null +++ b/pyscript.core/types/3rd-party/codemirror.d.ts @@ -0,0 +1 @@ +export * from "codemirror"; diff --git a/pyscript.core/types/3rd-party/codemirror_commands.d.ts b/pyscript.core/types/3rd-party/codemirror_commands.d.ts new file mode 100644 index 00000000..1862cebc --- /dev/null +++ b/pyscript.core/types/3rd-party/codemirror_commands.d.ts @@ -0,0 +1 @@ +export * from "@codemirror/commands"; diff --git a/pyscript.core/types/3rd-party/codemirror_lang-python.d.ts b/pyscript.core/types/3rd-party/codemirror_lang-python.d.ts new file mode 100644 index 00000000..76b718d3 --- /dev/null +++ b/pyscript.core/types/3rd-party/codemirror_lang-python.d.ts @@ -0,0 +1 @@ +export * from "@codemirror/lang-python"; diff --git a/pyscript.core/types/3rd-party/codemirror_language.d.ts b/pyscript.core/types/3rd-party/codemirror_language.d.ts new file mode 100644 index 00000000..632a9f72 --- /dev/null +++ b/pyscript.core/types/3rd-party/codemirror_language.d.ts @@ -0,0 +1 @@ +export * from "@codemirror/language"; diff --git a/pyscript.core/types/3rd-party/codemirror_state.d.ts b/pyscript.core/types/3rd-party/codemirror_state.d.ts new file mode 100644 index 00000000..9dd5c97d --- /dev/null +++ b/pyscript.core/types/3rd-party/codemirror_state.d.ts @@ -0,0 +1 @@ +export * from "@codemirror/state"; diff --git a/pyscript.core/types/3rd-party/codemirror_view.d.ts b/pyscript.core/types/3rd-party/codemirror_view.d.ts new file mode 100644 index 00000000..3669d051 --- /dev/null +++ b/pyscript.core/types/3rd-party/codemirror_view.d.ts @@ -0,0 +1 @@ +export * from "@codemirror/view"; diff --git a/pyscript.core/types/plugins.d.ts b/pyscript.core/types/plugins.d.ts index 286e91d0..d5063613 100644 --- a/pyscript.core/types/plugins.d.ts +++ b/pyscript.core/types/plugins.d.ts @@ -1,6 +1,7 @@ declare const _default: { "deprecations-manager": () => Promise; error: () => Promise; + "py-editor": () => Promise; "py-terminal": () => Promise; }; export default _default; diff --git a/pyscript.core/types/plugins/py-editor.d.ts b/pyscript.core/types/plugins/py-editor.d.ts new file mode 100644 index 00000000..35a35db4 --- /dev/null +++ b/pyscript.core/types/plugins/py-editor.d.ts @@ -0,0 +1,2 @@ +declare const _default: Promise; +export default _default; diff --git a/pyscript.core/types/toml.d.ts b/pyscript.core/types/toml.d.ts deleted file mode 100644 index 14f8437c..00000000 --- a/pyscript.core/types/toml.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -/*! (c) Jak Wings - MIT */ declare class e extends SyntaxError { - constructor(r: any, { offset: t, line: e, column: n }: { - offset: any; - line: any; - column: any; - }); - offset: any; - line: any; - column: any; -} -declare function n(n: any): any; -export { e as SyntaxError, n as parse }; diff --git a/pyscript.core/types/utils.d.ts b/pyscript.core/types/utils.d.ts deleted file mode 100644 index 266953e3..00000000 --- a/pyscript.core/types/utils.d.ts +++ /dev/null @@ -1 +0,0 @@ -export function htmlDecode(html: any): string;