diff --git a/.env.example b/.env.example index b09fd4e7c..570bcff22 100644 --- a/.env.example +++ b/.env.example @@ -6,5 +6,5 @@ REACT_APP_PLAUSIBLE_DATA_DOMAIN='' REACT_APP_PLAUSIBLE_SOURCE='' REACT_APP_SENTRY_DSN='' REACT_APP_SENTRY_ENV='local' -PUBLIC_URL='http://localhost:3010' +PUBLIC_URL='http://localhost:3011' ASSETS_URL='http://localhost:3010' diff --git a/.env.webcomponent.example b/.env.webcomponent.example index de988796d..b3f0e38fc 100644 --- a/.env.webcomponent.example +++ b/.env.webcomponent.example @@ -1,8 +1,7 @@ REACT_APP_AUTHENTICATION_URL='http://localhost:9001' REACT_APP_SENTRY_DSN='' REACT_APP_SENTRY_ENV='local' -# NB This is the URL of react-ui, rather than the web component -PUBLIC_URL=http://localhost:3012 +PUBLIC_URL=http://localhost:3011 REACT_APP_IDENTITY_URL='http://localhost:3011' REACT_APP_API_ENDPOINT='http://localhost:3009' ASSETS_URL='http://localhost:3011' diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 0c8e9efb5..c0be50495 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -92,14 +92,13 @@ jobs: with: install: false start: | - yarn start yarn start:wc - wait-on: "http://localhost:3010, http://localhost:3011" + wait-on: "http://localhost:3011" quiet: true env: REACT_APP_API_ENDPOINT: "https://test-editor-api.raspberrypi.org" - PUBLIC_URL: "http://localhost:3010" - ASSETS_URL: "http://localhost:3010" + PUBLIC_URL: "http://localhost:3011" + ASSETS_URL: "http://localhost:3011" REACT_APP_PLAUSIBLE_SOURCE: "" - name: Archive cypress artifacts diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index aaf9ef1aa..96fc0be26 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -12,7 +12,7 @@ on: type: string base_url: required: false - default: "https://staging-editor.raspberrypi.org" + default: "https://staging-editor-static.raspberrypi.org" type: string assets_url: required: false @@ -44,7 +44,7 @@ on: type: string react_app_plausible_data_domain: required: false - default: "staging-editor.raspberrypi.org" + default: "staging-editor-static.raspberrypi.org" type: string react_app_plausible_source: required: false @@ -123,11 +123,9 @@ jobs: env: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Build site and WC bundle + - name: Build WC bundle run: | - if [[ "${{ inputs.environment }}" == "production" ]]; then - yarn build - else + if [[ "${{ inputs.environment }}" != "production" ]]; then yarn build:dev fi yarn build:wc diff --git a/.vscode/launch.json b/.vscode/launch.json index 22dbf71bd..249490c42 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,7 +5,7 @@ "type": "chrome", "request": "launch", "name": "Launch Chrome against localhost", - "url": "http://localhost:3010", + "url": "http://localhost:3011", "webRoot": "${workspaceFolder}" } ] diff --git a/CHANGELOG.md b/CHANGELOG.md index 88d01f438..560d5e8f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## Unreleased +### Added + +- PyodideWorker setup for the editor (#1104) +- Enabling `pyodide` support in the web component (#1090) +- `Pyodide` `matplotlib` support (#1087) + +### Changed + +- Upgrade to `webpack 5` (#1096) +- Bump `pyodide` to `v0.26.2` (#1098) + +### Fixed + +- Dynamic runner switching with more than one `python` file (#1097) +- Pyodide running the correct file (`main.py`) when there are multiple `python` files (#1097) + ## [0.27.1] - 2024-10-01 ### Fixed @@ -110,6 +126,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Changed - Remove unused `/embedded/projects/:identifier` route (#1013) +- Runner defaults to `pyodide` (#937) ### Fixed @@ -124,6 +141,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Fix initial value of `user` in `WebComponentLoader` (#1021) - Make `authKey` in e2e web component spec more realistic (#1022) - Remove unused `ComponentStore` (#1023) +- Dynamic switching between `pyodide` and `skulpt` based on user imports (#937) ## [0.23.0] - 2024-05-09 diff --git a/config/webpack.config.js b/config/webpack.config.js deleted file mode 100644 index 3015cebed..000000000 --- a/config/webpack.config.js +++ /dev/null @@ -1,778 +0,0 @@ -const fs = require("fs"); -const path = require("path"); -const webpack = require("webpack"); -const resolve = require("resolve"); -const PnpWebpackPlugin = require("pnp-webpack-plugin"); -const HtmlWebpackPlugin = require("html-webpack-plugin"); -const CaseSensitivePathsPlugin = require("case-sensitive-paths-webpack-plugin"); -const InlineChunkHtmlPlugin = require("react-dev-utils/InlineChunkHtmlPlugin"); -const TerserPlugin = require("terser-webpack-plugin"); -const MiniCssExtractPlugin = require("mini-css-extract-plugin"); -const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin"); -const safePostCssParser = require("postcss-safe-parser"); -const ManifestPlugin = require("webpack-manifest-plugin"); -const InterpolateHtmlPlugin = require("react-dev-utils/InterpolateHtmlPlugin"); -const WorkboxWebpackPlugin = require("workbox-webpack-plugin"); -const WatchMissingNodeModulesPlugin = require("react-dev-utils/WatchMissingNodeModulesPlugin"); -const ModuleScopePlugin = require("react-dev-utils/ModuleScopePlugin"); -const getCSSModuleLocalIdent = require("react-dev-utils/getCSSModuleLocalIdent"); -const ESLintPlugin = require("eslint-webpack-plugin"); -const paths = require("./paths"); -const modules = require("./modules"); -const getClientEnvironment = require("./env"); -const ModuleNotFoundPlugin = require("react-dev-utils/ModuleNotFoundPlugin"); -const ForkTsCheckerWebpackPlugin = require("react-dev-utils/ForkTsCheckerWebpackPlugin"); -const typescriptFormatter = require("react-dev-utils/typescriptFormatter"); -const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin"); -const CopyPlugin = require("copy-webpack-plugin"); -const SVGOConfig = require("./svgo.config.js"); -const WorkerPlugin = require("worker-plugin"); - -const postcssNormalize = require("postcss-normalize"); - -const appPackageJson = require(paths.appPackageJson); - -// Source maps are resource heavy and can cause out of memory issue for large source files. -const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== "false"; - -const webpackDevClientEntry = require.resolve( - "react-dev-utils/webpackHotDevClient", -); -const reactRefreshOverlayEntry = require.resolve( - "react-dev-utils/refreshOverlayInterop", -); - -// Some apps do not need the benefits of saving a web request, so not inlining the chunk -// makes for a smoother build process. -const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== "false"; - -const emitErrorsAsWarnings = process.env.ESLINT_NO_DEV_ERRORS === "true"; -const disableESLintPlugin = process.env.DISABLE_ESLINT_PLUGIN === "true"; - -const imageInlineSizeLimit = parseInt( - process.env.IMAGE_INLINE_SIZE_LIMIT || "10000", -); - -// Check if TypeScript is setup -const useTypeScript = fs.existsSync(paths.appTsConfig); - -// Get the path to the uncompiled service worker (if it exists). -const swSrc = paths.swSrc; - -// style files regexes -const cssRegex = /\.css$/; -const cssModuleRegex = /\.module\.css$/; -const sassRegex = /\.(scss|sass)$/; -const sassModuleRegex = /\.module\.(scss|sass)$/; - -const hasJsxRuntime = (() => { - if (process.env.DISABLE_NEW_JSX_TRANSFORM === "true") { - return false; - } - - try { - require.resolve("react/jsx-runtime"); - return true; - } catch (e) { - return false; - } -})(); - -// This is the production and development configuration. -// It is focused on developer experience, fast rebuilds, and a minimal bundle. -module.exports = function (webpackEnv) { - const isEnvDevelopment = webpackEnv === "development"; - const isEnvProduction = webpackEnv === "production"; - - // Variable used for enabling profiling in Production - // passed into alias object. Uses a flag if passed into the build command - const isEnvProductionProfile = - isEnvProduction && process.argv.includes("--profile"); - - // We will provide `paths.publicUrlOrPath` to our app - // as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript. - // Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz. - // Get environment variables to inject into our app. - const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1)); - - const shouldUseReactRefresh = env.raw.FAST_REFRESH; - - // common function to get style loaders - const getStyleLoaders = (cssOptions, preProcessor) => { - const loaders = [ - isEnvDevelopment && require.resolve("style-loader"), - isEnvProduction && { - loader: MiniCssExtractPlugin.loader, - // css is located in `static/css`, use '../../' to locate index.html folder - // in production `paths.publicUrlOrPath` can be a relative path - options: paths.publicUrlOrPath.startsWith(".") - ? { publicPath: "../../" } - : {}, - }, - { - loader: require.resolve("css-loader"), - options: cssOptions, - }, - { - // Options for PostCSS as we reference these options twice - // Adds vendor prefixing based on your specified browser support in - // package.json - loader: require.resolve("postcss-loader"), - options: { - // Necessary for external CSS imports to work - // https://github.com/facebook/create-react-app/issues/2677 - ident: "postcss", - plugins: () => [ - require("postcss-flexbugs-fixes"), - require("postcss-preset-env")({ - autoprefixer: { - flexbox: "no-2009", - }, - stage: 3, - }), - // Adds PostCSS Normalize as the reset css with default options, - // so that it honors browserslist config in package.json - // which in turn let's users customize the target behavior as per their needs. - postcssNormalize(), - ], - sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment, - }, - }, - ].filter(Boolean); - if (preProcessor) { - loaders.push( - { - loader: require.resolve("resolve-url-loader"), - options: { - sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment, - root: paths.appSrc, - }, - }, - { - loader: require.resolve(preProcessor), - options: { - sourceMap: true, - }, - }, - ); - } - return loaders; - }; - - return { - mode: isEnvProduction ? "production" : isEnvDevelopment && "development", - // Stop compilation early in production - bail: isEnvProduction, - devtool: isEnvProduction - ? shouldUseSourceMap - ? "source-map" - : false - : isEnvDevelopment && "cheap-module-source-map", - // These are the "entry points" to our application. - // This means they will be the "root" imports that are included in JS bundle. - entry: - isEnvDevelopment && !shouldUseReactRefresh - ? [ - // Include an alternative client for WebpackDevServer. A client's job is to - // connect to WebpackDevServer by a socket and get notified about changes. - // When you save a file, the client will either apply hot updates (in case - // of CSS changes), or refresh the page (in case of JS changes). When you - // make a syntax error, this client will display a syntax error overlay. - // Note: instead of the default WebpackDevServer client, we use a custom one - // to bring better experience for Create React App users. You can replace - // the line below with these two lines if you prefer the stock client: - // - // require.resolve('webpack-dev-server/client') + '?/', - // require.resolve('webpack/hot/dev-server'), - // - // When using the experimental react-refresh integration, - // the webpack plugin takes care of injecting the dev client for us. - webpackDevClientEntry, - // Finally, this is your app's code: - paths.appIndexJs, - // We include the app code last so that if there is a runtime error during - // initialization, it doesn't blow up the WebpackDevServer client, and - // changing JS code would still trigger a refresh. - ] - : paths.appIndexJs, - output: { - // The build folder. - path: isEnvProduction ? paths.appBuild : undefined, - // Add /* filename */ comments to generated require()s in the output. - pathinfo: isEnvDevelopment, - // There will be one main bundle, and one file per asynchronous chunk. - // In development, it does not produce real files. - filename: isEnvProduction - ? "static/js/[name].[contenthash:8].js" - : isEnvDevelopment && "static/js/bundle.js", - // TODO: remove this when upgrading to webpack 5 - futureEmitAssets: true, - // There are also additional JS chunk files if you use code splitting. - chunkFilename: isEnvProduction - ? "static/js/[name].[contenthash:8].chunk.js" - : isEnvDevelopment && "static/js/[name].chunk.js", - // webpack uses `publicPath` to determine where the app is being served from. - // It requires a trailing slash, or the file assets will get an incorrect path. - // We inferred the "public path" (such as / or /my-project) from homepage. - publicPath: paths.publicUrlOrPath, - // Point sourcemap entries to original disk location (format as URL on Windows) - devtoolModuleFilenameTemplate: isEnvProduction - ? (info) => - path - .relative(paths.appSrc, info.absoluteResourcePath) - .replace(/\\/g, "/") - : isEnvDevelopment && - ((info) => - path.resolve(info.absoluteResourcePath).replace(/\\/g, "/")), - // Prevents conflicts when multiple webpack runtimes (from different apps) - // are used on the same page. - jsonpFunction: `webpackJsonp${appPackageJson.name}`, - // this defaults to 'window', but by setting it to 'this' then - // module chunks which are built will work in web workers as well. - globalObject: "this", - }, - optimization: { - minimize: isEnvProduction, - minimizer: [ - // This is only used in production mode - new TerserPlugin({ - terserOptions: { - parse: { - // We want terser to parse ecma 8 code. However, we don't want it - // to apply any minification steps that turns valid ecma 5 code - // into invalid ecma 5 code. This is why the 'compress' and 'output' - // sections only apply transformations that are ecma 5 safe - // https://github.com/facebook/create-react-app/pull/4234 - ecma: 8, - }, - compress: { - ecma: 5, - warnings: false, - // Disabled because of an issue with Uglify breaking seemingly valid code: - // https://github.com/facebook/create-react-app/issues/2376 - // Pending further investigation: - // https://github.com/mishoo/UglifyJS2/issues/2011 - comparisons: false, - // Disabled because of an issue with Terser breaking valid code: - // https://github.com/facebook/create-react-app/issues/5250 - // Pending further investigation: - // https://github.com/terser-js/terser/issues/120 - inline: 2, - }, - mangle: { - safari10: true, - }, - // Added for profiling in devtools - keep_classnames: isEnvProductionProfile, - keep_fnames: isEnvProductionProfile, - output: { - ecma: 5, - comments: false, - // Turned on because emoji and regex is not minified properly using default - // https://github.com/facebook/create-react-app/issues/2488 - ascii_only: true, - }, - }, - sourceMap: shouldUseSourceMap, - }), - // This is only used in production mode - new OptimizeCSSAssetsPlugin({ - cssProcessorOptions: { - parser: safePostCssParser, - map: shouldUseSourceMap - ? { - // `inline: false` forces the sourcemap to be output into a - // separate file - inline: false, - // `annotation: true` appends the sourceMappingURL to the end of - // the css file, helping the browser find the sourcemap - annotation: true, - } - : false, - }, - cssProcessorPluginOptions: { - preset: ["default", { minifyFontValues: { removeQuotes: false } }], - }, - }), - ], - // Automatically split vendor and commons - // https://twitter.com/wSokra/status/969633336732905474 - // https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366 - splitChunks: { - chunks: "all", - name: isEnvDevelopment, - }, - // Keep the runtime chunk separated to enable long term caching - // https://twitter.com/wSokra/status/969679223278505985 - // https://github.com/facebook/create-react-app/issues/5358 - runtimeChunk: { - name: (entrypoint) => `runtime-${entrypoint.name}`, - }, - }, - resolve: { - // This allows you to set a fallback for where webpack should look for modules. - // We placed these paths second because we want `node_modules` to "win" - // if there are any conflicts. This matches Node resolution mechanism. - // https://github.com/facebook/create-react-app/issues/253 - modules: ["node_modules", paths.appNodeModules].concat( - modules.additionalModulePaths || [], - ), - // These are the reasonable defaults supported by the Node ecosystem. - // We also include JSX as a common component filename extension to support - // some tools, although we do not recommend using it, see: - // https://github.com/facebook/create-react-app/issues/290 - // `web` extension prefixes have been added for better support - // for React Native Web. - extensions: paths.moduleFileExtensions - .map((ext) => `.${ext}`) - .filter((ext) => useTypeScript || !ext.includes("ts")), - alias: { - // Support React Native Web - // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/ - "react-native": "react-native-web", - // Allows for better profiling with ReactDevTools - ...(isEnvProductionProfile && { - "react-dom$": "react-dom/profiling", - "scheduler/tracing": "scheduler/tracing-profiling", - }), - ...(modules.webpackAliases || {}), - }, - plugins: [ - // Adds support for installing with Plug'n'Play, leading to faster installs and adding - // guards against forgotten dependencies and such. - PnpWebpackPlugin, - // Prevents users from importing files from outside of src/ (or node_modules/). - // This often causes confusion because we only process files within src/ with babel. - // To fix this, we prevent you from importing files out of src/ -- if you'd like to, - // please link the files into your node_modules/ and let module-resolution kick in. - // Make sure your source files are compiled, as they will not be processed in any way. - new ModuleScopePlugin(paths.appSrc, [ - paths.appPackageJson, - reactRefreshOverlayEntry, - ]), - ], - }, - resolveLoader: { - plugins: [ - // Also related to Plug'n'Play, but this time it tells webpack to load its loaders - // from the current package. - PnpWebpackPlugin.moduleLoader(module), - ], - }, - module: { - strictExportPresence: true, - rules: [ - // Disable require.ensure as it's not a standard language feature. - { parser: { requireEnsure: false } }, - { - // "oneOf" will traverse all following loaders until one will - // match the requirements. When no loader matches it will fall - // back to the "file" loader at the end of the loader list. - oneOf: [ - // TODO: Merge this config once `image/avif` is in the mime-db - // https://github.com/jshttp/mime-db - { - test: [/\.avif$/], - loader: require.resolve("url-loader"), - options: { - limit: imageInlineSizeLimit, - mimetype: "image/avif", - name: "static/media/[name].[hash:8].[ext]", - }, - }, - // "url" loader works like "file" loader except that it embeds assets - // smaller than specified limit in bytes as data URLs to avoid requests. - // A missing `test` is equivalent to a match. - { - test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/], - loader: require.resolve("url-loader"), - options: { - limit: imageInlineSizeLimit, - name: "static/media/[name].[hash:8].[ext]", - }, - }, - // Process application JS with Babel. - // The preset includes JSX, Flow, TypeScript, and some ESnext features. - { - test: /\.(js|mjs|jsx|ts|tsx)$/, - include: paths.appSrc, - loader: require.resolve("babel-loader"), - options: { - customize: require.resolve( - "babel-preset-react-app/webpack-overrides", - ), - presets: [ - [ - require.resolve("babel-preset-react-app"), - { - runtime: hasJsxRuntime ? "automatic" : "classic", - }, - ], - ], - - plugins: [ - [ - require.resolve("babel-plugin-named-asset-import"), - { - loaderMap: { - svg: { - ReactComponent: - "@svgr/webpack?-svgo,+titleProp,+ref![path]", - }, - }, - }, - ], - isEnvDevelopment && - shouldUseReactRefresh && - require.resolve("react-refresh/babel"), - ].filter(Boolean), - // This is a feature of `babel-loader` for webpack (not Babel itself). - // It enables caching results in ./node_modules/.cache/babel-loader/ - // directory for faster rebuilds. - cacheDirectory: true, - // See #6846 for context on why cacheCompression is disabled - cacheCompression: false, - compact: isEnvProduction, - }, - }, - // Process any JS outside of the app with Babel. - // Unlike the application JS, we only compile the standard ES features. - { - test: /\.(js|mjs)$/, - exclude: /@babel(?:\/|\\{1,2})runtime/, - loader: require.resolve("babel-loader"), - options: { - babelrc: false, - configFile: false, - compact: false, - presets: [ - [ - require.resolve("babel-preset-react-app/dependencies"), - { helpers: true }, - ], - ], - cacheDirectory: true, - // See #6846 for context on why cacheCompression is disabled - cacheCompression: false, - - // Babel sourcemaps are needed for debugging into node_modules - // code. Without the options below, debuggers like VSCode - // show incorrect code and set breakpoints on the wrong lines. - sourceMaps: shouldUseSourceMap, - inputSourceMap: shouldUseSourceMap, - }, - }, - // "postcss" loader applies autoprefixer to our CSS. - // "css" loader resolves paths in CSS and adds assets as dependencies. - // "style" loader turns CSS into JS modules that inject