diff --git a/assets/bitbake-2.8.0.zip b/assets/bitbake-2.8.0.zip new file mode 100644 index 0000000..3724afc Binary files /dev/null and b/assets/bitbake-2.8.0.zip differ diff --git a/assets/yocto-3.1.33.zip b/assets/yocto-3.1.33.zip deleted file mode 100644 index 920cd05..0000000 Binary files a/assets/yocto-3.1.33.zip and /dev/null differ diff --git a/babel.config.js b/babel.config.js index 6ee5d6c..da0e45d 100644 --- a/babel.config.js +++ b/babel.config.js @@ -5,11 +5,14 @@ module.exports = { [ '@babel/preset-env', { - useBuiltIns: 'usage', // or 'entry' corejs: 3, // or 2, but 3 is recommended - targets: "> 0.25%, not dead", // Example: browsers with >0.25% market share and not end-of-life + targets: "fully supports es6-module-dynamic-import", // Example: browsers with >0.25% market share and not end-of-life }, ], ["@babel/preset-react", {"runtime": "automatic"}], - "@babel/preset-typescript"], + "@babel/preset-typescript" + ], + plugins: [ + "@babel/plugin-syntax-dynamic-import" + ] }; diff --git a/package-lock.json b/package-lock.json index 86b6e63..62bb435 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ }, "devDependencies": { "@babel/core": "^7.24.4", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/preset-env": "^7.24.4", "@babel/preset-react": "^7.24.1", "@babel/preset-typescript": "^7.24.1", @@ -40,6 +41,7 @@ "eslint-plugin-you-dont-need-lodash-underscore": "^6.14.0", "file-loader": "^6.2.0", "html-webpack-plugin": "^5.6.0", + "isomorphic-fetch": "^3.0.0", "mini-css-extract-plugin": "^2.8.1", "postcss": "^8.4.38", "postcss-loader": "^7.3.4", @@ -6864,6 +6866,16 @@ "node": ">=0.10.0" } }, + "node_modules/isomorphic-fetch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz", + "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==", + "dev": true, + "dependencies": { + "node-fetch": "^2.6.1", + "whatwg-fetch": "^3.4.1" + } + }, "node_modules/iterator.prototype": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", @@ -7423,6 +7435,26 @@ "tslib": "^2.0.3" } }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", @@ -9724,6 +9756,12 @@ "node": ">=6" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, "node_modules/ts-api-utils": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", @@ -10297,6 +10335,12 @@ "minimalistic-assert": "^1.0.0" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, "node_modules/webpack": { "version": "5.91.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.91.0.tgz", @@ -10758,6 +10802,22 @@ "node": ">=0.8.0" } }, + "node_modules/whatwg-fetch": { + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 5670038..0e6ebce 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,6 @@ { "browserslist": [ - "> 1%", - "last 2 versions" + "fully supports es6-module-dynamic-import" ], "engines": { "node": "18.x" @@ -13,6 +12,7 @@ }, "devDependencies": { "@babel/core": "^7.24.4", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/preset-env": "^7.24.4", "@babel/preset-react": "^7.24.1", "@babel/preset-typescript": "^7.24.1", @@ -33,6 +33,7 @@ "eslint-plugin-you-dont-need-lodash-underscore": "^6.14.0", "file-loader": "^6.2.0", "html-webpack-plugin": "^5.6.0", + "isomorphic-fetch": "^3.0.0", "mini-css-extract-plugin": "^2.8.1", "postcss": "^8.4.38", "postcss-loader": "^7.3.4", diff --git a/src/components/App.tsx b/src/components/App.tsx index 0f68700..68b7e9d 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -1,8 +1,10 @@ -import React from "react"; +import React, {useEffect, useState} from "react"; import { MantineProvider, createTheme, MantineColorsTuple } from '@mantine/core'; import FetchWithProgress from "./FetchWithProgress"; +import PyodideLoader from "./PyodideLoader"; +import pyodide from "pyodide"; const myColor: MantineColorsTuple = [ '#e4f8ff', @@ -24,9 +26,162 @@ const theme = createTheme({ }); export const App: React.FC = () => { + const [data, setData] = useState(null); + const [pyodideModule, setPyodideModule] = useState(null); + + useEffect(() => { + const go = async () => { + if (data && pyodideModule) { + console.warn("LOADING SQLITE"); + await pyodideModule.loadPackage("sqlite3"); + console.warn("LOADED!"); + + pyodideModule.unpackArchive(data, "zip", { + extractDir: "bb" + }); + pyodideModule.runPython(` +import os.path +import sys +from importlib.abc import Loader, MetaPathFinder +from importlib.util import spec_from_file_location, spec_from_loader + +import _frozen_importlib +import _frozen_importlib_external + + +class MyMetaFinder(MetaPathFinder): + def find_spec(self, fullname, path=None, target=None): + print(f"importing {fullname}") + + if path is None or path == "": + path = sys.path + if "." in fullname: + *parents, name = fullname.split(".") + else: + name = fullname + for entry in path: + if os.path.isdir(os.path.join(entry, name)): + # this module has child modules + filename = os.path.join(entry, name, "__init__.py") + submodule_locations = [os.path.join(entry, name)] + else: + filename = os.path.join(entry, name + ".py") + submodule_locations = None + if not os.path.exists(filename): + continue + + print(">> " + filename) + + return spec_from_file_location(fullname, filename, loader=MyLoader(filename), + submodule_search_locations=submodule_locations) + + # we don't know how to import this + return None + + +class MyLoader(Loader): + def __init__(self, filename): + self.filename = filename + + def create_module(self, spec): + # Use default module creation semantics + return None + + def exec_module(self, module): + print("FILE NAME: " + self.filename) + if self.filename.endswith("fcntl/__init__.py"): + data = """ +from unittest.mock import Mock +fcntl = Mock() + """ + else: + with open(self.filename) as f: + data = f.read() + + try: + exec(data, vars(module)) + except Exception as e: + print(e) + raise e + + +class BuiltinImporterShim(_frozen_importlib.BuiltinImporter): + @classmethod + def find_spec(cls, fullname, path=None, target=None): + ret = super().find_spec(fullname, path, target) + + if ret: + print(f"shim handling: {ret}") + return ret + + +class FrozenImporterShim: + _original_importer = None + + @classmethod + def set_original_importer(cls, imp): + cls._original_importer = imp + + @classmethod + def find_spec(cls, fullname, path=None, target=None): + if fullname == "fcntl": + return spec_from_loader("fcntl", MyLoader("fcntl/__init__.py")) + ret = cls._original_importer.find_spec(fullname, path, target) + + if ret: + print(f"frozen shim handling: {ret}") + return ret + +print(sys.meta_path) +js_finder = sys.meta_path.pop() +path_finder = sys.meta_path.pop() +frozen_finder = sys.meta_path.pop() +builtin = sys.meta_path.pop() + +#sys.meta_path.append(MyMetaFinder()) +sys.meta_path.append(path_finder) + +i = FrozenImporterShim() +i.set_original_importer(frozen_finder) +sys.meta_path.append(i) + +sys.meta_path.append(BuiltinImporterShim()) + +print(sys.meta_path) + `) + let file = pyodideModule.FS.readdir("./bb"); + console.log(file); + + pyodideModule.runPython(` + import sys + sys.path.insert(0, "./bb/bitbake-2.8.0/lib/") + from bb.data_smart import DataSmart + `) + + let DataSmart = pyodideModule.globals.get('DataSmart'); + const d = DataSmart(); + + d.setVar("A", "B"); + d.setVar("A:test", "C"); + d.setVar("OVERRIDES", "test"); + d.setVarFlag("A", "p", "OK"); + + console.log(d.getVar("A")); + + DataSmart.destroy(); + + } else { + console.warn(`data = ${!!data}, p = ${!!pyodideModule}`); + } + } + + go(); + }, [data, pyodideModule]); + return ( - + + ); }; \ No newline at end of file diff --git a/src/components/FetchWithProgress.tsx b/src/components/FetchWithProgress.tsx index 23a81f1..03b9906 100644 --- a/src/components/FetchWithProgress.tsx +++ b/src/components/FetchWithProgress.tsx @@ -1,19 +1,21 @@ import React, { useState } from 'react'; + interface FetchProgressProps { url: string; + data: ArrayBuffer | null; + setData: (value: ArrayBuffer) => void, } -const FetchWithProgress: React.FC = ({ url }) => { +const FetchWithProgress: React.FC = (props: FetchProgressProps) => { const [progress, setProgress] = useState(0); - const [data, setData] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const fetchData = () => { setLoading(true); setError(null); - fetch(url) + fetch(props.url) .then(response => { if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); @@ -50,22 +52,23 @@ const FetchWithProgress: React.FC = ({ url }) => { return new Response(stream, { headers: { "Content-Type": "application/octet-stream" } }); }) .then(response => response.arrayBuffer()) - .then(buffer => { - setData(buffer); + .then(async buffer => { + props.setData(buffer); setLoading(false); }) .catch(err => { setError(err.message); + console.error(err); setLoading(false); }); }; return (
-

Download Progress

+

Download Progress

{loading &&

Progress: {progress}%

} {error &&

Error: {error}

} - {data &&

Download complete!

} + {props.data &&

Download complete!

} diff --git a/src/components/PyodideLoader.tsx b/src/components/PyodideLoader.tsx new file mode 100644 index 0000000..0b80c4b --- /dev/null +++ b/src/components/PyodideLoader.tsx @@ -0,0 +1,28 @@ +import React, { useEffect, useState } from 'react'; + +import pyodide from "pyodide"; + +interface Props { + pyodide: pyodide | null; + setPyodide: (value: pyodide) => void; +} + +const PyodideLoader: React.FC = (props: Props) => { + useEffect(() => { + async function loadPyodide() { + const { loadPyodide: loadPyodideModule } = await import("https://cdn.jsdelivr.net/pyodide/v0.25.1/full/pyodide.mjs"); + const pyodideInstance: pyodide = await loadPyodideModule(); + props.setPyodide(pyodideInstance); + } + + loadPyodide(); + }, []); + + return ( +
+ {props.pyodide ? 'Pyodide Loaded!' : 'Loading Pyodide...'} +
+ ); +}; + +export default PyodideLoader; diff --git a/webpack.config.ts b/webpack.config.ts index 5215d80..529ddae 100644 --- a/webpack.config.ts +++ b/webpack.config.ts @@ -38,6 +38,7 @@ module.exports = (env: any, argv: any) => { entry: { 'main': './src/index', }, + target: ["web"], optimization: { splitChunks: { @@ -50,6 +51,11 @@ module.exports = (env: any, argv: any) => { filename: "[name]-[contenthash].js", chunkFilename: "[name]-[contenthash].js", publicPath: PUBLIC_PATH, + environment: { + dynamicImport: true, + asyncFunction: true, + }, + globalObject: 'globalThis', }, plugins: [ @@ -63,7 +69,7 @@ module.exports = (env: any, argv: any) => { patterns: [ { from: path.resolve(__dirname, 'assets/'), to: 'assets' } ] - }) + }), ], module: { @@ -138,5 +144,10 @@ module.exports = (env: any, argv: any) => { watchOptions: { aggregateTimeout: 2000, }, + + externals: { + pyodide: 'pyodide' + }, + } }