diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ff73a8e2cb..e94f6b7ed8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -159,17 +159,7 @@ jobs: - name: Build Bundle working-directory: ./frontend run: | - corepack enable; - corepack install; - yarn install - yarn run build:app:assets - yarn run build:app - yarn run build:app:libs - - - name: Build WASM - working-directory: "./render-wasm" - run: | - ./build release + ./scripts/build 0.0.0 - name: Store Bundle Cache uses: actions/cache@v4 @@ -177,6 +167,7 @@ jobs: key: "integration-bundle-${{ github.sha }}" path: frontend/resources/public + test-integration-1: name: "Integration Tests 1/4" runs-on: ubuntu-24.04 diff --git a/frontend/deps.edn b/frontend/deps.edn index 3d4ff15c09..b9200eb272 100644 --- a/frontend/deps.edn +++ b/frontend/deps.edn @@ -20,8 +20,8 @@ :git/url "https://github.com/funcool/beicon.git"} funcool/rumext - {:git/tag "v2.24" - :git/sha "17a0c94" + {:git/tag "v2.25" + :git/sha "27e5a1a" :git/url "https://github.com/funcool/rumext.git"} instaparse/instaparse {:mvn/version "1.5.0"} @@ -42,7 +42,7 @@ :dev {:extra-paths ["dev"] :extra-deps - {thheller/shadow-cljs {:mvn/version "3.2.0"} + {thheller/shadow-cljs {:mvn/version "3.2.2"} com.bhauman/rebel-readline {:mvn/version "RELEASE"} org.clojure/tools.namespace {:mvn/version "RELEASE"} criterium/criterium {:mvn/version "RELEASE"} diff --git a/frontend/package.json b/frontend/package.json index 9ac5975668..e50b10d403 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -45,9 +45,9 @@ "translations": "node ./scripts/translations.js", "watch:app:assets": "node ./scripts/watch.js", "watch:app:libs": "node ./scripts/build-libs.js --watch", - "watch:app:main": "clojure -M:dev:shadow-cljs watch main storybook", + "watch:app:main": "clojure -M:dev:shadow-cljs watch main worker storybook", "clear:shadow-cache": "rm -rf .shadow-cljs", - "watch:app": "yarn run clear:shadow-cache && yarn run build:app:worker && concurrently \"yarn run watch:app:main\" \"yarn run watch:app:libs\"", + "watch:app": "yarn run clear:shadow-cache && concurrently \"yarn run watch:app:main\" \"yarn run watch:app:libs\"", "watch": "yarn run watch:app:assets", "watch:storybook": "yarn run build:storybook:assets && concurrently \"storybook dev -p 6006 --no-open\" \"yarn run watch:storybook:assets\"", "watch:storybook:assets": "node ./scripts/watch-storybook.js" diff --git a/frontend/playwright/ui/pages/BasePage.js b/frontend/playwright/ui/pages/BasePage.js index 628415b97b..e0c170396e 100644 --- a/frontend/playwright/ui/pages/BasePage.js +++ b/frontend/playwright/ui/pages/BasePage.js @@ -73,7 +73,7 @@ export class BasePage { } static async mockConfigFlags(page, flags) { - const url = "**/js/config.js?ts=*"; + const url = "**/js/config.js"; return await page.route(url, (route) => route.fulfill({ status: 200, diff --git a/frontend/resources/templates/index.mustache b/frontend/resources/templates/index.mustache index deeba32f81..66a4a05539 100644 --- a/frontend/resources/templates/index.mustache +++ b/frontend/resources/templates/index.mustache @@ -25,14 +25,14 @@ {{# manifest}} - - + + {{/manifest}} - @@ -44,9 +44,11 @@ {{# manifest}} - - - + + {{/manifest}} diff --git a/frontend/resources/templates/rasterizer.mustache b/frontend/resources/templates/rasterizer.mustache index 2bba47a86d..b967370b10 100644 --- a/frontend/resources/templates/rasterizer.mustache +++ b/frontend/resources/templates/rasterizer.mustache @@ -19,9 +19,11 @@ {{# manifest}} - - - + + {{/manifest}} diff --git a/frontend/resources/templates/render.mustache b/frontend/resources/templates/render.mustache index c36e4f9a71..e37ce8c3f3 100644 --- a/frontend/resources/templates/render.mustache +++ b/frontend/resources/templates/render.mustache @@ -18,9 +18,11 @@
{{# manifest}} - - - + + {{/manifest}} diff --git a/frontend/scripts/_helpers.js b/frontend/scripts/_helpers.js index 3183d060c3..72f9eee631 100644 --- a/frontend/scripts/_helpers.js +++ b/frontend/scripts/_helpers.js @@ -193,26 +193,30 @@ async function readShadowManifest() { const index = { ts: ts, - config: "js/config.js?ts=" + ts, - polyfills: "js/polyfills.js?ts=" + ts, - worker_main: "js/worker/main.js?ts=" + ts, + config: "./js/config.js", + polyfills: "./js/polyfills.js", + worker_main: "./js/worker/main.js", + libs: "./js/libs.js", }; for (let item of content) { - index[item.name] = "js/" + item["output-name"]; + index[item.name] = "./js/" + item["output-name"] + ""; } return index; } catch (cause) { - return { + const index = { ts: ts, - config: "js/config.js?ts=" + ts, - polyfills: "js/polyfills.js?ts=" + ts, - main: "js/main.js?ts=" + ts, - shared: "js/shared.js?ts=" + ts, - worker_main: "js/worker/main.js?ts=" + ts, - rasterizer: "js/rasterizer.js?ts=" + ts, + config: "./js/config.js", + polyfills: "./js/polyfills.js", + main: "./js/main.js", + shared: "./js/shared.js", + worker_main: "./js/worker/main.js", + rasterizer: "./js/rasterizer.js", + libs: "./js/libs.js", }; + + return index; } } diff --git a/frontend/scripts/build b/frontend/scripts/build index 71ff300451..e7b96de4e9 100755 --- a/frontend/scripts/build +++ b/frontend/scripts/build @@ -26,7 +26,10 @@ yarn install || exit 1; rm -rf resources/public; rm -rf target/dist; -yarn run build:app:main --config-merge "{:release-version \"${CURRENT_HASH}-${TS}\"}" $EXTRA_PARAMS || exit 1 +mkdir -p resources/public; +mkdir -p target/dist; + +yarn run build:app:main $EXTRA_PARAMS || exit 1 if [ "$INCLUDE_WASM" = "yes" ]; then yarn run build:wasm || exit 1; @@ -35,19 +38,18 @@ fi yarn run build:app:libs || exit 1; yarn run build:app:assets || exit 1; -mkdir -p target/dist; -rsync -avr resources/public/ target/dist/ - -sed -i -re "s/\%version\%/$CURRENT_VERSION/g" ./target/dist/index.html; -sed -i -re "s/\%version\%/$CURRENT_VERSION/g" ./target/dist/render.html; -sed -i -re "s/\%version\%/$CURRENT_VERSION/g" ./target/dist/rasterizer.html; -sed -i -re "s/\%buildDate\%/$BUILD_DATE/g" ./target/dist/index.html; -sed -i -re "s/\%buildDate\%/$BUILD_DATE/g" ./target/dist/rasterizer.html; +sed -i -re "s/\%version\%/$CURRENT_VERSION/g" ./resources/public/index.html; +sed -i -re "s/\%version\%/$CURRENT_VERSION/g" ./resources/public/render.html; +sed -i -re "s/\%version\%/$CURRENT_VERSION/g" ./resources/public/rasterizer.html; +sed -i -re "s/\%buildDate\%/$BUILD_DATE/g" ./resources/public/index.html; +sed -i -re "s/\%buildDate\%/$BUILD_DATE/g" ./resources/public/rasterizer.html; if [ "$INCLUDE_WASM" = "yes" ]; then - sed -i "s/version=develop/version=$CURRENT_VERSION/g" ./target/dist/js/render_wasm.js; + sed -i "s/version=develop/version=$CURRENT_VERSION/g" ./resources/public/js/render_wasm.js; fi +rsync -avr resources/public/ target/dist/; + if [ "$INCLUDE_STORYBOOK" = "yes" ]; then # build storybook yarn run build:storybook || exit 1; diff --git a/frontend/scripts/build-libs.js b/frontend/scripts/build-libs.js index b1d308866b..d4a9923d92 100644 --- a/frontend/scripts/build-libs.js +++ b/frontend/scripts/build-libs.js @@ -31,9 +31,9 @@ const rebuildNotify = { const config = { entryPoints: ["target/index.js"], bundle: true, - format: "iife", + format: "esm", banner: { - js: '"use strict"; var global = globalThis;', + js: '"use strict";\nvar global = globalThis;', }, outfile: "resources/public/js/libs.js", plugins: [fixReactVirtualized, rebuildNotify], diff --git a/frontend/shadow-cljs.edn b/frontend/shadow-cljs.edn index dab57241a8..1f8add3995 100644 --- a/frontend/shadow-cljs.edn +++ b/frontend/shadow-cljs.edn @@ -6,13 +6,12 @@ :builds {:main - {:target :browser + {:target :esm :output-dir "resources/public/js/" :asset-path "/js" :devtools {:watch-dir "resources/public" :reload-strategy :full} :build-options {:manifest-name "manifest.json"} - :module-loader true :modules {:shared {:entries []} @@ -20,7 +19,7 @@ :main {:entries [app.main app.plugins.api] :depends-on #{:shared} - :init-fn app.main/init} + :exports {init app.main/init}} :util-highlight {:entries [app.util.code-highlight] @@ -50,12 +49,12 @@ :render {:entries [app.render] :depends-on #{:shared} - :init-fn app.render/init} + :exports {init app.render/init}} :rasterizer {:entries [app.rasterizer] :depends-on #{:shared} - :init-fn app.rasterizer/init}} + :exports {init app.rasterizer/init}}} :js-options {:entry-keys ["module" "browser" "main"] @@ -75,8 +74,6 @@ :compiler-options {:fn-invoke-direct true :optimizations #shadow/env ["PENPOT_BUILD_OPTIMIZATIONS" :as :keyword :default :advanced] - :output-wrapper true - :rename-prefix-namespace "PENPOT" :source-map true :elide-asserts true :anon-fn-naming-policy :off diff --git a/frontend/src/app/main/ui.cljs b/frontend/src/app/main/ui.cljs index 07283230b5..44e7a24549 100644 --- a/frontend/src/app/main/ui.cljs +++ b/frontend/src/app/main/ui.cljs @@ -31,27 +31,28 @@ [app.main.ui.static :as static] [app.util.dom :as dom] [app.util.i18n :refer [tr]] + [app.util.modules :as mod] [app.util.theme :as theme] [beicon.v2.core :as rx] [rumext.v2 :as mf])) (def auth-page - (mf/lazy-component app.main.ui.auth/auth)) + (mf/lazy #(mod/load 'app.main.ui.auth/auth-page*))) -(def verify-token-page - (mf/lazy-component app.main.ui.auth.verify-token/verify-token)) +(def verify-token-page* + (mf/lazy #(mod/load 'app.main.ui.auth.verify-token/verify-token-page*))) (def viewer-page* - (mf/lazy-component app.main.ui.viewer/viewer*)) + (mf/lazy #(mod/load 'app.main.ui.viewer/viewer-page*))) (def dashboard-page* - (mf/lazy-component app.main.ui.dashboard/dashboard*)) + (mf/lazy #(mod/load 'app.main.ui.dashboard/dashboard-page*))) (def settings-page* - (mf/lazy-component app.main.ui.settings/settings*)) + (mf/lazy #(mod/load 'app.main.ui.settings/settings-page*))) (def workspace-page* - (mf/lazy-component app.main.ui.workspace/workspace*)) + (mf/lazy #(mod/load 'app.main.ui.workspace/workspace-page*))) (mf/defc workspace-legacy-redirect* {::mf/props :obj @@ -189,7 +190,7 @@ [:? [:& auth-page {:route route}]] :auth-verify-token - [:? [:& verify-token-page {:route route}]] + [:? [:& verify-token-page* {:route route}]] (:settings-profile :settings-password diff --git a/frontend/src/app/main/ui/auth.cljs b/frontend/src/app/main/ui/auth.cljs index c3542fe84a..bdd42963b5 100644 --- a/frontend/src/app/main/ui/auth.cljs +++ b/frontend/src/app/main/ui/auth.cljs @@ -19,8 +19,7 @@ [app.util.i18n :as i18n :refer [tr]] [rumext.v2 :as mf])) -(mf/defc auth - {::mf/props :obj} +(mf/defc auth* [{:keys [route]}] (let [section (dm/get-in route [:data :name]) is-register (or @@ -69,3 +68,9 @@ (when (= section :auth-register) [:& terms-register])]])) + + +(mf/defc auth-page* + {::mf/lazy-load true} + [props] + [:> auth* props]) diff --git a/frontend/src/app/main/ui/auth/verify_token.cljs b/frontend/src/app/main/ui/auth/verify_token.cljs index ec80e4e9a5..334303ade4 100644 --- a/frontend/src/app/main/ui/auth/verify_token.cljs +++ b/frontend/src/app/main/ui/auth/verify_token.cljs @@ -61,9 +61,9 @@ (rt/nav :auth-login) (ntf/warn (tr "errors.unexpected-token")))) -(mf/defc verify-token - [{:keys [route] :as props}] - (let [token (get-in route [:query-params :token]) +(mf/defc verify-token* + [{:keys [route]}] + (let [token (get-in route [:query-params :token]) bad-token (mf/use-state false)] (mf/with-effect [] @@ -99,3 +99,8 @@ [:> static/invalid-token {}] [:> loader* {:title (tr "labels.loading") :overlay true}]))) + +(mf/defc verify-token-page* + {::mf/lazy-load true} + [props] + [:> verify-token* props]) diff --git a/frontend/src/app/main/ui/components/code_block.cljs b/frontend/src/app/main/ui/components/code_block.cljs index b766772344..b15f4eb33d 100644 --- a/frontend/src/app/main/ui/components/code_block.cljs +++ b/frontend/src/app/main/ui/components/code_block.cljs @@ -8,24 +8,28 @@ (:require-macros [app.main.style :as stl]) (:require [app.common.data.macros :as dm] + [app.util.modules :as modules] [cuerdas.core :as str] [promesa.core :as p] - [rumext.v2 :as mf] - [shadow.lazy :as lazy])) + [rumext.v2 :as mf])) (def highlight-fn - (lazy/loadable app.util.code-highlight/highlight!)) + (delay (modules/load-fn 'app.util.code-highlight/highlight!))) (mf/defc code-block {::mf/wrap-props false} [{:keys [code type]}] (let [block-ref (mf/use-ref) - code (str/trim code)] + code (str/trim code)] (mf/with-effect [code type] (when-let [node (mf/ref-val block-ref)] - (p/let [highlight-fn (lazy/load highlight-fn)] - (highlight-fn node)))) + (->> @highlight-fn + (p/fmap (fn [f] (f))) + (p/fnly (fn [f cause] + (if cause + (js/console.error cause) + (f node))))))) [:pre {:class (dm/str type " " (stl/css :code-display)) :ref block-ref} code])) diff --git a/frontend/src/app/main/ui/dashboard.cljs b/frontend/src/app/main/ui/dashboard.cljs index 419bb89aa0..23277606cd 100644 --- a/frontend/src/app/main/ui/dashboard.cljs +++ b/frontend/src/app/main/ui/dashboard.cljs @@ -247,7 +247,6 @@ (swap! storage/session dissoc :template)))))) (mf/defc dashboard* - {::mf/props :obj} [{:keys [profile project-id team-id search-term plugin-url template section]}] (let [team (mf/deref refs/team) projects (mf/deref refs/projects) @@ -313,3 +312,8 @@ :section section :search-term search-term :team team}]]])) + +(mf/defc dashboard-page* + {::mf/lazy-load true} + [props] + [:> dashboard* props]) diff --git a/frontend/src/app/main/ui/settings.cljs b/frontend/src/app/main/ui/settings.cljs index 5935620068..bc40ae20fa 100644 --- a/frontend/src/app/main/ui/settings.cljs +++ b/frontend/src/app/main/ui/settings.cljs @@ -78,3 +78,9 @@ :settings-notifications [:& notifications-page* {:profile profile}])]]]])) + +(mf/defc settings-page* + {::mf/lazy-load true} + [props] + [:> settings* props]) + diff --git a/frontend/src/app/main/ui/viewer.cljs b/frontend/src/app/main/ui/viewer.cljs index e50e36a042..f5715c3bd6 100644 --- a/frontend/src/app/main/ui/viewer.cljs +++ b/frontend/src/app/main/ui/viewer.cljs @@ -624,7 +624,6 @@ ;; --- Component: Viewer (mf/defc viewer* - {::mf/props :obj} [{:keys [file-id share-id page-id] :as props}] (mf/with-effect [file-id page-id share-id] (let [params {:file-id file-id @@ -643,3 +642,8 @@ [:> loader* {:title (tr "labels.loading") :overlay true}])) + +(mf/defc viewer-page* + {::mf/lazy-load true} + [props] + [:> viewer* props]) diff --git a/frontend/src/app/main/ui/workspace.cljs b/frontend/src/app/main/ui/workspace.cljs index 4125e47b99..97c89d1392 100644 --- a/frontend/src/app/main/ui/workspace.cljs +++ b/frontend/src/app/main/ui/workspace.cljs @@ -165,7 +165,7 @@ (dsh/lookup-page state file-id page-id)))) st/state)) -(mf/defc workspace-page* +(mf/defc workspace-inner* {::mf/private true} [{:keys [page-id file-id file layout wglobal]}] (let [page-ref (mf/with-memo [file-id page-id] @@ -252,10 +252,16 @@ :touch-action "none"}} [:> context-menu*] (if (and file-loaded? page-id) - [:> workspace-page* + [:> workspace-inner* {:page-id page-id :file-id file-id :file file :wglobal wglobal :layout layout}] [:> workspace-loader*])]]]]]])) + +(mf/defc workspace-page* + {::mf/lazy-load true} + [props] + [:> workspace* props]) + diff --git a/frontend/src/app/render_wasm/api.cljs b/frontend/src/app/render_wasm/api.cljs index be85dc1fd7..eb5d522475 100644 --- a/frontend/src/app/render_wasm/api.cljs +++ b/frontend/src/app/render_wasm/api.cljs @@ -40,6 +40,7 @@ [app.util.debug :as dbg] [app.util.functions :as fns] [app.util.globals :as ug] + [app.util.modules :as mod] [app.util.text.content :as tc] [beicon.v2.core :as rx] [promesa.core :as p] @@ -1361,7 +1362,7 @@ (delay (if (exists? js/dynamicImport) (let [uri (cf/resolve-static-asset "js/render_wasm.js")] - (->> (js/dynamicImport (str uri)) + (->> (mod/import uri) (p/mcat init-wasm-module) (p/fmap (fn [default] diff --git a/frontend/src/app/util/code_highlight.cljs b/frontend/src/app/util/code_highlight.cljs index c435dc3e7e..710cdd1e12 100644 --- a/frontend/src/app/util/code_highlight.cljs +++ b/frontend/src/app/util/code_highlight.cljs @@ -10,6 +10,7 @@ [app.util.dom :as dom])) (defn highlight! + {:lazy-loadable true} [node] (dom/set-data! node "highlighted" nil) (hljs/highlightElement node)) diff --git a/frontend/src/app/util/globals.js b/frontend/src/app/util/globals.js index ecdff5fbb0..1236f6d158 100644 --- a/frontend/src/app/util/globals.js +++ b/frontend/src/app/util/globals.js @@ -20,7 +20,7 @@ goog.provide("app.util.globals"); goog.scope(function () { var self = app.util.globals; - self.global = goog.global; + self.global = globalThis; function createMockedEventEmitter(k) { /* Allow mocked objects to be event emitters, so other modules diff --git a/frontend/src/app/util/i18n.cljs b/frontend/src/app/util/i18n.cljs index 0bd5f39f7d..5560586d31 100644 --- a/frontend/src/app/util/i18n.cljs +++ b/frontend/src/app/util/i18n.cljs @@ -92,8 +92,9 @@ is executed in the critical part (application bootstrap) and used in many parts of the application." [data] - (reset! locale (or (get storage/global ::locale) (autodetect))) - (set! translations data)) + (set! translations data) + (reset! locale (or (get storage/global ::locale) (autodetect)))) + (defn set-locale! [lname] diff --git a/frontend/src/app/util/modules.clj b/frontend/src/app/util/modules.clj new file mode 100644 index 0000000000..95ecfdcb5c --- /dev/null +++ b/frontend/src/app/util/modules.clj @@ -0,0 +1,17 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC + +(ns app.util.modules + (:refer-clojure :exclude [load resolve])) + +(defmacro load + [thing] + `(-> (shadow.esm/load-by-name ~thing) + (.then (fn [f#] (cljs.core/js-obj "default" (f#)))))) + +(defmacro load-fn + [thing] + `(shadow.esm/load-by-name ~thing)) diff --git a/frontend/src/app/util/modules.cljs b/frontend/src/app/util/modules.cljs new file mode 100644 index 0000000000..28e0576c5c --- /dev/null +++ b/frontend/src/app/util/modules.cljs @@ -0,0 +1,16 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC + +(ns app.util.modules + (:refer-clojure :exclude [import]) + (:require-macros [app.util.modules]) + (:require + [shadow.esm :refer [dynamic-import]])) + +(defn import + "Dynamic esm module import import" + [path] + (dynamic-import (str path))) diff --git a/frontend/src/app/util/worker.cljs b/frontend/src/app/util/worker.cljs index cf3260ad30..fde1796eef 100644 --- a/frontend/src/app/util/worker.cljs +++ b/frontend/src/app/util/worker.cljs @@ -101,8 +101,8 @@ (rx/push! bus message)))) handle-error - (fn [error] - (on-error worker (.-data error)))] + (fn [event] + (on-error worker (.-data event)))] (.addEventListener instance "message" handle-message) (.addEventListener instance "error" handle-error) diff --git a/frontend/src/app/worker/thumbnails.cljs b/frontend/src/app/worker/thumbnails.cljs index 517d7895a4..69991a2c37 100644 --- a/frontend/src/app/worker/thumbnails.cljs +++ b/frontend/src/app/worker/thumbnails.cljs @@ -20,12 +20,12 @@ [app.render-wasm.api :as wasm.api] [app.render-wasm.wasm :as wasm] [app.util.http :as http] + [app.util.modules :as mod] [app.worker.impl :as impl] [beicon.v2.core :as rx] [okulary.core :as l] [promesa.core :as p] - [rumext.v2 :as mf] - [shadow.esm :refer (dynamic-import)])) + [rumext.v2 :as mf])) (log/set-level! :trace) @@ -101,7 +101,7 @@ (def init-wasm (delay (let [uri (cf/resolve-static-asset "js/render_wasm.js")] - (-> (dynamic-import (str uri)) + (-> (mod/import (str uri)) (p/then #(wasm.api/init-wasm-module %)) (p/then #(set! wasm/internal-module %))))))