Improve offline dist content (#1836)

This commit is contained in:
Andrea Giammarchi
2023-11-03 10:00:52 +01:00
committed by GitHub
parent e81830a2ea
commit c8ec29a3d8
18 changed files with 385 additions and 98 deletions

2
.gitignore vendored
View File

@@ -147,3 +147,5 @@ pyscript.core/dist
pyscript.core/dist.zip
pyscript.core/src/plugins.js
pyscript.core/src/stdlib/pyscript.js
pyscript.core/src/3rd-party/*
!pyscript.core/src/3rd-party/READMEmd

View File

@@ -42,7 +42,7 @@ repos:
rev: "v3.0.0-alpha.6"
hooks:
- id: prettier
exclude: pyscript\.core/test|pyscript\.core/dist|pyscript\.core/types|pyscript.core/src/stdlib/pyscript.js|pyscript\.sw/|pyscript.core/src/toml\.js
exclude: pyscript\.core/test|pyscript\.core/dist|pyscript\.core/types|pyscript.core/src/stdlib/pyscript.js|pyscript\.sw/|pyscript.core/src/3rd-party
args: [--tab-width, "4"]
- repo: https://github.com/pycqa/isort

View File

@@ -19,7 +19,7 @@ module.exports = {
ecmaVersion: "latest",
sourceType: "module",
},
ignorePatterns: ["toml.js"],
ignorePatterns: ["3rd-party"],
rules: {
"no-implicit-globals": ["error"],
},

View File

@@ -18,6 +18,7 @@
},
"devDependencies": {
"@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",
@@ -27,7 +28,9 @@
"rollup-plugin-postcss": "^4.0.2",
"rollup-plugin-string": "^3.0.0",
"static-handler": "^0.4.3",
"typescript": "^5.2.2"
"typescript": "^5.2.2",
"xterm": "^5.3.0",
"xterm-readline": "^1.1.1"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
@@ -236,6 +239,71 @@
"node": ">=16"
}
},
"node_modules/@rollup/plugin-commonjs": {
"version": "25.0.7",
"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.7.tgz",
"integrity": "sha512-nEvcR+LRjEjsaSsc4x3XZfCCvZIaSMenZu/OiwOKGN2UhQpAYI7ru7czFvyWbErlpoGjnSX3D5Ch5FcMA3kRWQ==",
"dev": true,
"dependencies": {
"@rollup/pluginutils": "^5.0.1",
"commondir": "^1.0.1",
"estree-walker": "^2.0.2",
"glob": "^8.0.3",
"is-reference": "1.2.1",
"magic-string": "^0.30.3"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^2.68.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/@rollup/plugin-commonjs/node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dev": true,
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/@rollup/plugin-commonjs/node_modules/glob": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
"integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
"dev": true,
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^5.0.1",
"once": "^1.3.0"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@rollup/plugin-commonjs/node_modules/minimatch": {
"version": "5.1.6",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
"dev": true,
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@rollup/plugin-node-resolve": {
"version": "15.2.3",
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz",
@@ -826,6 +894,12 @@
"node": ">= 10"
}
},
"node_modules/commondir": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
"integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
"dev": true
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -1113,6 +1187,12 @@
"integrity": "sha512-gpXfJslSi4hYDkA0mTLEpYKRv9siAgSUgZ+UWyk+J5Cttpd1ThCVwdclzIwQSclz3hYn049+M2fgrP1WpvF8xg==",
"dev": true
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true
},
"node_modules/entities": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
@@ -1643,6 +1723,15 @@
"node": ">=0.10.0"
}
},
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
@@ -1679,6 +1768,15 @@
"node": ">=8"
}
},
"node_modules/is-reference": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
"integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==",
"dev": true,
"dependencies": {
"@types/estree": "*"
}
},
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -1794,6 +1892,18 @@
"integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==",
"dev": true
},
"node_modules/magic-string": {
"version": "0.30.5",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz",
"integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==",
"dev": true,
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.15"
},
"engines": {
"node": ">=12"
}
},
"node_modules/mdn-data": {
"version": "2.0.14",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz",
@@ -3039,6 +3149,20 @@
"integrity": "sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A==",
"dev": true
},
"node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
@@ -3306,6 +3430,24 @@
}
}
},
"node_modules/xterm": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/xterm/-/xterm-5.3.0.tgz",
"integrity": "sha512-8QqjlekLUFTrU6x7xck1MsPzPA571K5zNqWm0M0oroYEWVOptZ0+ubQSkQ3uxIEhcIHRujJy6emDWX4A7qyFzg==",
"dev": true
},
"node_modules/xterm-readline": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/xterm-readline/-/xterm-readline-1.1.1.tgz",
"integrity": "sha512-f87S2/jKwRZoZTxE2vkPgBCipDl6k6tTkMTb9pmwC4R6XkfR491fWBuToZd/nZasp6seD2u0jdABinUDWsK6dw==",
"dev": true,
"dependencies": {
"string-width": "^4.0.0"
},
"peerDependencies": {
"xterm": "^5.0.0"
}
},
"node_modules/yaml": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",

View File

@@ -20,11 +20,11 @@
},
"scripts": {
"server": "npx static-handler --coi .",
"build": "npm run build:toml && npm run build:stdlib && npm run build:plugins && npm run build:core && eslint src/ && npm run ts && npm run test:mpy",
"build:core": "rm -rf dist && rollup --config rollup/core.config.js",
"build": "npm run build:3rd-party && npm run build:stdlib && npm run build:plugins && npm run build:core && eslint src/ && npm run ts && npm run test:mpy",
"build:core": "rm -rf dist && rollup --config rollup/core.config.js && cp src/3rd-party/*.css dist/",
"build:plugins": "node rollup/plugins.cjs",
"build:stdlib": "node rollup/stdlib.cjs",
"build:toml": "node rollup/toml.cjs",
"build:3rd-party": "node rollup/3rd-party.cjs",
"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",
@@ -48,6 +48,7 @@
},
"devDependencies": {
"@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",
@@ -57,7 +58,9 @@
"rollup-plugin-postcss": "^4.0.2",
"rollup-plugin-string": "^3.0.0",
"static-handler": "^0.4.3",
"typescript": "^5.2.2"
"typescript": "^5.2.2",
"xterm": "^5.3.0",
"xterm-readline": "^1.1.1"
},
"repository": {
"type": "git",

View File

@@ -0,0 +1,52 @@
const { copyFileSync, writeFileSync } = require("node:fs");
const { join } = require("node:path");
const CDN = "https://cdn.jsdelivr.net/npm";
const targets = join(__dirname, "..", "src", "3rd-party");
const node_modules = join(__dirname, "..", "node_modules");
const { devDependencies } = require(join(__dirname, "..", "package.json"));
const v = (name) => devDependencies[name].replace(/[^\d.]/g, "");
// Fetch a module via jsdelivr CDN `/+esm` orchestration
// then sanitize the resulting outcome to avoid importing
// anything via `/npm/...` through Rollup
const resolve = (name) => {
const cdn = `${CDN}/${name}@${v(name)}/+esm`;
console.debug("fetching", cdn);
return fetch(cdn)
.then((b) => b.text())
.then((text) =>
text.replace(
/("|')\/npm\/(.+)?\+esm\1/g,
// normalize `/npm/module@version/+esm` as
// just `module` so that rollup can do the rest
(_, quote, module) => {
const i = module.lastIndexOf("@");
return `${quote}${module.slice(0, i)}${quote}`;
},
),
);
};
// key/value pairs as:
// "3rd-party/file-name.js"
// string as content or
// Promise<string> as resolved content
const modules = {
"toml.js": join(node_modules, "@webreflection", "toml-j0.4", "toml.js"),
"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"),
};
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));
}
}

View File

@@ -2,6 +2,7 @@
// the default exported as npm entry.
import { nodeResolve } from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import terser from "@rollup/plugin-terser";
import postcss from "rollup-plugin-postcss";
@@ -11,7 +12,9 @@ export default [
{
input: "./src/core.js",
plugins: plugins.concat(
process.env.NO_MIN ? [nodeResolve()] : [nodeResolve(), terser()],
process.env.NO_MIN
? [nodeResolve(), commonjs()]
: [nodeResolve(), commonjs(), terser()],
),
output: {
esModule: true,

View File

@@ -1,14 +0,0 @@
const { copyFileSync } = require("node:fs");
const { join } = require("node:path");
copyFileSync(
join(
__dirname,
"..",
"node_modules",
"@webreflection",
"toml-j0.4",
"toml.js",
),
join(__dirname, "..", "src", "toml.js"),
);

7
pyscript.core/src/3rd-party/README.md vendored Normal file
View File

@@ -0,0 +1,7 @@
# PyScript 3rd Party
This folder contains artifacts created via [3rd-party.cjs](../../rollup/3rd-party.cjs).
As we would like to offer a way to run PyScript offline, and we already offer a `dist` folder with all the necessary scripts, we have created a foreign dependencies resolver that allow to lazy-load CDN dependencies out of the box.
Please **note** these dependencies are **not interpreters**, because interpreters have their own mechanism, folders structure, WASM files, and whatnot, to work locally, but at least XTerm or the TOML parser, among other lazy dependencies, should be available within the dist folder.

View File

@@ -89,7 +89,7 @@ for (const [TYPE] of TYPES) {
} else if (toml || type === "toml") {
try {
const { parse } = await import(
/* webpackIgnore: true */ "./toml.js"
/* webpackIgnore: true */ "./3rd-party/toml.js"
);
parsed = parse(text);
} catch (e) {

View File

@@ -1,9 +1,6 @@
// PyScript py-terminal plugin
import { TYPES, hooks } from "../core.js";
const CDN = "https://cdn.jsdelivr.net/npm/xterm";
const XTERM = "5.3.0";
const XTERM_READLINE = "1.1.1";
const SELECTOR = [...TYPES.keys()]
.map((type) => `script[type="${type}"][terminal],${type}-script[terminal]`)
.join(",");
@@ -26,22 +23,18 @@ const pyTerminal = async () => {
if (element.matches('script[type="mpy"],mpy-script'))
throw new Error("Unsupported terminal");
// import styles once and lazily (only on valid terminal)
if (!document.querySelector(`link[href^="${CDN}"]`)) {
document.head.append(
Object.assign(document.createElement("link"), {
rel: "stylesheet",
href: `${CDN}@${XTERM}/css/xterm.min.css`,
}),
);
}
// import styles lazily
document.head.append(
Object.assign(document.createElement("link"), {
rel: "stylesheet",
href: new URL("./xterm.css", import.meta.url),
}),
);
// lazy load these only when a valid terminal is found
const [{ Terminal }, { Readline }] = await Promise.all([
import(/* webpackIgnore: true */ `${CDN}@${XTERM}/+esm`),
import(
/* webpackIgnore: true */ `${CDN}-readline@${XTERM_READLINE}/+esm`
),
import(/* webpackIgnore: true */ "../3rd-party/xterm.js"),
import(/* webpackIgnore: true */ "../3rd-party/xterm-readline.js"),
]);
const readline = new Readline();

File diff suppressed because one or more lines are too long

View File

@@ -1,37 +0,0 @@
<!doctype html>
<html>
<head>
<title>PyScript Next - Terminal</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@latest/css/xterm.min.css">
<script type="module">
import { Readline } from "https://cdn.jsdelivr.net/npm/xterm-readline@latest/+esm";
const rl = new Readline;
rl.setCheckHandler(text => !text.trimEnd().endsWith("&&"));
import { Terminal } from "https://cdn.jsdelivr.net/npm/xterm@latest/+esm";
const term = new Terminal({
theme: {
background: "#191A19",
foreground: "#F5F2E7",
},
cursorBlink: true,
cursorStyle: "block",
});
term.loadAddon(rl);
term.open(terminal);
term.focus();
import { PyWorker } from "../dist/core.js";
const { sync } = new PyWorker("terminal.py");
Object.assign(sync, {
readline: prompt => rl.read(prompt),
write: line => term.write(line),
});
</script>
</head>
<body>
<div id="terminal"></div>
</body>
</html>

View File

@@ -1,14 +0,0 @@
###### magic monkey patching ######
import builtins
import sys
from pyodide.code import eval_code
from pyscript import sync
sys.stdout = sync
builtins.input = sync.readline
####### main code ######
import code
code.interact()

12
pyscript.core/types/3rd-party/toml.d.ts vendored Normal file
View File

@@ -0,0 +1,12 @@
/*! (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 };

View File

@@ -0,0 +1,138 @@
declare var b: any;
declare var I: boolean;
declare namespace r {
export let __esModule: boolean;
export { Readline };
}
declare class Readline {
highlighter: {
highlight(t: any, e: any): any;
highlightPrompt(t: any): any;
highlightChar(t: any, e: any): boolean;
};
history: {
entries: any[];
cursor: number;
maxEntries: any;
saveToLocalStorage(): void;
restoreFromLocalStorage(): void;
append(t: any): void;
resetCursor(): void;
next(): any;
prev(): any;
};
disposables: any[];
watermark: number;
highWatermark: number;
lowWatermark: number;
highWater: boolean;
state: {
line: {
buf: string;
pos: number;
buffer(): string;
pos_buffer(): string;
length(): number;
char_length(): number;
update(t: any, e: any): void;
insert(t: any): boolean;
moveBack(t: any): boolean;
moveForward(t: any): boolean;
moveHome(): boolean;
moveEnd(): boolean;
startOfLine(): number;
endOfLine(): number;
moveLineUp(t: any): boolean;
moveLineDown(t: any): boolean;
set_pos(t: any): void;
prevPos(t: any): number;
nextPos(t: any): number;
backspace(t: any): boolean;
delete(t: any): boolean;
deleteEndOfLine(): boolean;
};
highlighting: boolean;
prompt: any;
tty: any;
highlighter: any;
history: any;
promptSize: any;
layout: p;
buffer(): string;
shouldHighlight(): boolean;
clearScreen(): void;
editInsert(t: any): void;
update(t: any): void;
editBackspace(t: any): void;
editDelete(t: any): void;
editDeleteEndOfLine(): void;
refresh(): void;
moveCursorBack(t: any): void;
moveCursorForward(t: any): void;
moveCursorUp(t: any): void;
moveCursorDown(t: any): void;
moveCursorHome(): void;
moveCursorEnd(): void;
moveCursorToEnd(): void;
previousHistory(): void;
nextHistory(): void;
moveCursor(): void;
};
checkHandler: () => boolean;
ctrlCHandler: () => void;
pauseHandler: (t: any) => void;
activate(t: any): void;
term: any;
dispose(): void;
appendHistory(t: any): void;
setHighlighter(t: any): void;
setCheckHandler(t: any): void;
setCtrlCHandler(t: any): void;
setPauseHandler(t: any): void;
writeReady(): boolean;
write(t: any): void;
print(t: any): void;
println(t: any): void;
output(): this;
tty(): {
tabWidth: any;
col: any;
row: any;
out: any;
write(t: any): any;
print(t: any): any;
println(t: any): any;
clearScreen(): void;
calculatePosition(t: any, e: any): any;
computeLayout(t: any, e: any): {
promptSize: any;
cursor: any;
end: any;
};
refreshLine(t: any, e: any, s: any, i: any, r: any): void;
clearOldRows(t: any): void;
moveCursor(t: any, e: any): void;
};
read(t: any): Promise<any>;
activeRead: {
prompt: any;
resolve: (value: any) => void;
reject: (reason?: any) => void;
};
handleKeyEvent(t: any): boolean;
readData(t: any): void;
readPaste(t: any): void;
readKey(t: any): void;
}
declare class p {
constructor(t: any);
promptSize: any;
cursor: c;
end: c;
}
declare class c {
constructor(t: any, e: any);
row: any;
col: any;
}
export { b as Readline, I as __esModule, r as default };

View File

@@ -0,0 +1,4 @@
declare var i: any;
declare var s: any;
declare var t: {};
export { i as Terminal, s as __esModule, t as default };

View File

@@ -1,14 +1,13 @@
declare const _default: {
pyscript: {
declare namespace _default {
let pyscript: {
"__init__.py": string;
"display.py": string;
"event_handling.py": string;
"magic_js.py": string;
"util.py": string;
};
"pyscript.py": string;
pyweb: {
let pyweb: {
"pydom.py": string;
};
};
}
export default _default;