Compare commits

...

14 Commits

Author SHA1 Message Date
Bart Ledoux
8559ec1cb5 make sure the build version works 2025-11-28 17:14:25 +01:00
Bart Ledoux
aea6d0abec bring back codecov 2025-11-28 14:50:57 +01:00
Bart Ledoux
ff9261ea28 fix build 2025-11-28 11:56:23 +01:00
Bart Ledoux
ecfa9e9d86 remove last warning 2025-11-27 17:34:37 +01:00
Bart Ledoux
ca87a56d83 succesful dynamic remote 2025-11-27 17:20:31 +01:00
Bart Ledoux
fba6105ad8 use element plus item to demo modularity 2025-11-27 15:12:20 +01:00
Bart Ledoux
d3f3a5b702 fix remote 2025-11-27 15:08:38 +01:00
Bart Ledoux
3af8749a00 add plugin-ui remote 2025-11-27 15:01:24 +01:00
Bart Ledoux
9b4ea5b54f make custom UI for Execute button 2025-11-27 11:47:53 +01:00
Bart Ledoux
43535fc29b remove codecov 2025-11-27 10:10:46 +01:00
Bart Ledoux
752bbe9595 Merge branch 'develop' into spike/module-federation 2025-11-27 10:00:03 +01:00
Bart Ledoux
3d29647fab add support code for module federation 2025-11-21 13:15:06 +01:00
Bart Ledoux
c7bd71ea4b Revert "refactor: use rolldown-vite for build speed (#11904)"
This reverts commit b59098e61f.
2025-11-21 13:09:36 +01:00
Bart Ledoux
3fee8a9b5a remove type issue 2025-11-21 13:07:43 +01:00
26 changed files with 8855 additions and 471 deletions

15
plugin-ui/.eslintrc.cjs Normal file
View File

@@ -0,0 +1,15 @@
/* eslint-env node */
require("@rushstack/eslint-patch/modern-module-resolution");
module.exports = {
root: true,
extends: [
"plugin:vue/vue3-essential",
"eslint:recommended",
"@vue/eslint-config-typescript",
"@vue/eslint-config-prettier",
],
parserOptions: {
ecmaVersion: "latest",
},
};

30
plugin-ui/.gitignore vendored Normal file
View File

@@ -0,0 +1,30 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules/
.DS_Store
dist
dist-ssr
coverage
*.local
/cypress/videos/
/cypress/screenshots/
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
.__mf__temp

1
plugin-ui/.nvmrc Normal file
View File

@@ -0,0 +1 @@
22.12

View File

@@ -0,0 +1 @@
{}

46
plugin-ui/README.md Normal file
View File

@@ -0,0 +1,46 @@
# remote
This template should help get you started developing with Vue 3 in Vite.
## Recommended IDE Setup
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
## Type Support for `.vue` Imports in TS
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types.
If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:
1. Disable the built-in TypeScript Extension
1) Run `Extensions: Show Built-in Extensions` from VSCode's command palette
2) Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`
2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.
## Customize configuration
See [Vite Configuration Reference](https://vitejs.dev/config/).
## Project Setup
```sh
npm install
```
### Compile and Hot-Reload for Development
```sh
npm run dev
```
### Type-Check, Compile and Minify for Production
```sh
npm run build
```
### Lint with [ESLint](https://eslint.org/)
```sh
npm run lint
```

1
plugin-ui/env.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="vite/client" />

14
plugin-ui/index.html Normal file
View File

@@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Remote</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

6380
plugin-ui/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

36
plugin-ui/package.json Normal file
View File

@@ -0,0 +1,36 @@
{
"name": "module-federation-vite-vue3-remote",
"version": "0.0.0",
"private": true,
"scripts": {
"dev": "vite --port 4174",
"build": "run-p type-check build-only",
"preview": "vite preview --port 4174",
"build-only": "vite build",
"type-check": "vue-tsc --noEmit",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
},
"dependencies": {
"esbuild-plugin-inline-image": "^0.0.9",
"pinia": "^3.0.4",
"vue": "^3.5.24"
},
"devDependencies": {
"@module-federation/vite": "1.9.1",
"@rushstack/eslint-patch": "^1.15.0",
"@types/node": "^24.10.1",
"@vitejs/plugin-vue": "^6.0.2",
"@vitejs/plugin-vue-jsx": "^5.1.2",
"@vue/eslint-config-prettier": "^10.2.0",
"@vue/eslint-config-typescript": "^14.6.0",
"@vue/tsconfig": "^0.8.1",
"esbuild-plugin-vue-next": "^0.1.4",
"eslint": "^9.39.1",
"eslint-plugin-vue": "^10.5.1",
"npm-run-all": "^4.1.5",
"prettier": "^3.6.2",
"typescript": "~5.9.3",
"vite": "^7.2.4",
"vue-tsc": "^3.1.4"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

29
plugin-ui/src/App.vue Normal file
View File

@@ -0,0 +1,29 @@
<script setup lang="ts">
import Counter from "./components/Counter.vue";
import image from "./remote_assets/logo.svg";
</script>
<template>
<div
style="
background: #1f2124;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.4);
border-radius: 5px;
margin: 20px 20px 20px 20px;
width: 250px;
padding: 20px;
text-align: center;
color: white;
float: left;
"
data-e2e="APP__CARD"
>
<div className="icon">
<img :src="image" alt="" />
</div>
<div style="margin-top: 10px; font-size: 25px">I'm the remote app</div>
<Counter />
</div>
</template>
<style scoped />

View File

@@ -0,0 +1,20 @@
<script lang="ts" setup>
import { ref } from "vue";
const onclickOn = ref(false)
import image from "../remote_assets/logo.svg";
defineProps<{
label?: string
}>()
const emit = defineEmits<{
(e: 'click'): void
}>()
</script>
<template>
<el-button @click="onclickOn = true; emit('click')" type="success">
<slot /> {{ label }} <em>new remote</em>
<img :src="image" alt="Kestra Logo" style="width: 16px; height: 16px; margin-left: 5px;" />
</el-button>
</template>

View File

@@ -0,0 +1,19 @@
<script setup lang="ts">
import { storeToRefs } from "pinia";
import { useStore } from "../stores/counter";
import Button from "./Button.vue";
import image from "../remote_assets/logo.svg";
const store = useStore();
const { count } = storeToRefs(store);
</script>
<template>
<Button
@click="store.increment"
>
<img :src="image" alt="Kestra Logo" style="width: 20px; height: 20px; margin-right: 5px;" />
Remote counter: {{ count }}
</Button>
</template>
<style scoped />

View File

@@ -0,0 +1 @@
export default {};

8
plugin-ui/src/main.ts Normal file
View File

@@ -0,0 +1,8 @@
import { createPinia } from "pinia";
import { createApp } from "vue";
import App from "./App.vue";
const app = createApp(App);
app.use(createPinia());
app.mount("#app");

View File

@@ -0,0 +1,15 @@
<svg
enableBackground="new 0 0 512 512"
height="100px"
width="100px"
id="Layer_1"
version="1.1"
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
data-e2e="CLOUD__SYMBOL"
>
<path
d="M285.928,113.067c62.492,0,113.327,50.827,113.327,113.327c0,0.344-0.041,0.705-0.066,1.049 c-0.049,0.836-0.107,1.672-0.123,2.525l-0.426,17.133l17.159,0.065c41.53,0.115,75.313,34.005,75.313,75.535 c0,41.415-33.718,75.305-75.157,75.518l-3.664,0.016H104.977c-46.244-0.049-83.872-37.693-83.872-83.929 c0-35.825,22.806-67.714,56.737-79.356l9.444-3.229l1.664-9.838c4.115-24.282,24.97-41.907,49.588-41.907 c7.846,0,15.428,1.82,22.536,5.394l15.306,7.689l7.386-15.444C202.531,138.398,242.635,113.067,285.928,113.067 M285.928,96.277 c-51.778,0-96.358,30.315-117.303,74.092c-9.059-4.558-19.256-7.182-30.086-7.182c-33.233,0-60.762,24.184-66.14,55.893 C32.82,232.657,4.316,270.104,4.316,314.307c0,55.597,45.063,100.669,100.644,100.718h311.084v-0.016 c50.777-0.263,91.856-41.481,91.856-92.308c0-50.909-41.177-92.177-92.053-92.324c0.033-1.344,0.197-2.639,0.197-3.984 C416.044,154.532,357.79,96.277,285.928,96.277L285.928,96.277z"
fill="#f6b352"
/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,23 @@
import { defineStore } from "pinia";
const _useStore = defineStore("store", {
state: () => ({ count: 0 }),
getters: {
getCount(): number {
return this.count;
},
},
actions: {
increment() {
this.count++;
},
},
});
declare global {
interface Window {
useStore: typeof _useStore;
}
}
export const useStore = window.useStore || _useStore;

View File

@@ -0,0 +1,16 @@
{
"extends": "@vue/tsconfig/tsconfig.lib.json",
"include": [
"vite.config.*",
"vitest.config.*",
"cypress.config.*",
"playwright.config.*",
"module-federation/esBuildAdapter.ts"
],
"compilerOptions": {
"composite": true,
"types": ["node"],
"moduleResolution": "nodenext",
"module": "nodenext"
}
}

17
plugin-ui/tsconfig.json Normal file
View File

@@ -0,0 +1,17 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
"compilerOptions": {
"skipLibCheck": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"],
"shared": ["../shared/shared.ts"]
}
},
"references": [
{
"path": "./tsconfig.config.json"
}
]
}

40
plugin-ui/vite.config.ts Normal file
View File

@@ -0,0 +1,40 @@
import { federation } from "@module-federation/vite";
import vue from "@vitejs/plugin-vue";
import vueJsx from "@vitejs/plugin-vue-jsx";
import path from "path";
import { defineConfig } from "vite";
export default defineConfig(async ({ mode }) => {
return {
server: {
fs: {
allow: [".", "../shared"],
},
},
base: "http://localhost:4174",
plugins: [
federation({
filename: "remoteEntryRemote.js",
name: "remoteCounterApp",
exposes: {
"./remote-app": "./src/App.vue",
"./remote-button": "./src/components/Button.vue",
},
shared: {
vue: {
singleton: true,
requiredVersion: "^3"
},
}
}),
vue({}),
vueJsx({}),
],
resolve: {
alias: {
"@": path.resolve(__dirname, "src"),
shared: path.resolve(__dirname, "../shared/shared"),
},
},
};
});

5
ui/.gitignore vendored
View File

@@ -3,4 +3,7 @@ test-results
tests/.env
tests/data/
tests/e2e/.env
tests/e2e/data/application-secrets.yml
tests/e2e/data/application-secrets.yml
# module federation build files
.__mf__temp

2538
ui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -25,6 +25,8 @@
"dependencies": {
"@js-joda/core": "^5.6.5",
"@kestra-io/ui-libs": "^0.0.263",
"@module-federation/enhanced": "^0.21.6",
"@module-federation/vite": "^1.9.1",
"@vue-flow/background": "^1.3.2",
"@vue-flow/controls": "^1.1.2",
"@vue-flow/core": "^1.47.0",
@@ -63,6 +65,7 @@
"rapidoc": "^9.3.8",
"semver": "^7.7.3",
"shiki": "^3.15.0",
"tapable": "^2.3.0",
"vue": "^3.5.24",
"vue-axios": "^3.5.2",
"vue-chartjs": "^5.3.3",
@@ -120,7 +123,6 @@
"playwright": "^1.55.0",
"prettier": "^3.6.2",
"rimraf": "^6.1.0",
"rolldown-vite": "^7.2.5",
"rollup-plugin-copy": "^3.5.0",
"sass": "^1.93.3",
"storybook": "^9.1.16",
@@ -129,7 +131,7 @@
"typescript": "^5.9.3",
"typescript-eslint": "^8.46.4",
"uuid": "^13.0.0",
"vite": "npm:rolldown-vite@latest",
"vite": "^7.2.4",
"vitest": "^3.2.4",
"vue-tsc": "^3.1.4"
},
@@ -149,7 +151,9 @@
"@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7"
},
"storybook": "$storybook",
"vite": "npm:rolldown-vite@latest"
"@codecov/vite-plugin":{
"vite": "$vite"
}
},
"lint-staged": {
"**/*.{js,mjs,cjs,ts,vue}": "eslint --fix"

View File

@@ -1,6 +1,9 @@
<template>
<div class="trigger-flow-wrapper">
<el-button v-if="playgroundStore.enabled" id="run-all-button" :icon="icon.Play" class="el-button--playground" :disabled="isDisabled() || !playgroundStore.readyToStart" @click="playgroundStore.runUntilTask()">
<component :is="RemoteButton" v-if="RemoteButton" @click="onClick()">
{{ $t("execute") }}
</component>
<el-button v-else-if="playgroundStore.enabled" id="run-all-button" :icon="icon.Play" class="el-button--playground" :disabled="isDisabled() || !playgroundStore.readyToStart" @click="playgroundStore.runUntilTask()">
{{ $t("playground.run_all_tasks") }}
</el-button>
<el-button v-else id="execute-button" :class="{'onboarding-glow': coreStore.guidedProperties.tourStarted}" :icon="icon.LightningBolt" :type="type" :disabled="isDisabled()" @click="onClick()">
@@ -71,10 +74,10 @@
<script>
import {shallowRef} from "vue";
import FlowRun from "./FlowRun.vue";
import LightningBolt from "vue-material-design-icons/LightningBolt.vue";
import Play from "vue-material-design-icons/Play.vue";
import {shallowRef} from "vue";
import {useMediaQuery} from "@vueuse/core";
import {pageFromRoute} from "../../utils/eventsRouter";
import FlowWarningDialog from "./FlowWarningDialog.vue";
@@ -84,6 +87,7 @@
import {useExecutionsStore} from "../../stores/executions";
import {usePlaygroundStore} from "../../stores/playground";
import {useFlowStore} from "../../stores/flow";
import {registerRemotes, registerShared, loadRemote} from "@module-federation/enhanced/runtime";
export default {
components: {
@@ -121,9 +125,29 @@
icon: {
LightningBolt: shallowRef(LightningBolt),
Play: shallowRef(Play)
}
},
RemoteButton: null
};
},
mounted() {
registerRemotes([
{
type: "module",
name: "remote",
entry: "http://localhost:4174/remoteEntryRemote.js"
},
])
registerShared({
vue: {
singleton: true,
},
});
loadRemote("remote/remote-button").then((module) => {
this.RemoteButton = shallowRef(module.default);
});
},
methods: {
onClick() {
if (this.$tours["guidedTour"]?.isRunning?.value) {

View File

@@ -43,7 +43,7 @@
const table = ref<any>(null);
const hasSelection = ref(false);
const container = ref<HTMLElement>(null);
const container = ref<HTMLElement>();
const toggleRowExpansion = (row: any, expand?: boolean) => {
table.value?.toggleRowExpansion(row, expand);

View File

@@ -1,6 +1,7 @@
import path from "path";
import {defineConfig} from "vite";
import vue from "@vitejs/plugin-vue";
import {federation} from "@module-federation/vite";
import {commit} from "./plugins/commit"
import {codecovVitePlugin} from "@codecov/vite-plugin";
@@ -14,24 +15,6 @@ export default defineConfig({
"https://fonts.googleapis.com",
"https://fonts.gstatic.com"
],
output: {
advancedChunks: {
groups: [
{
test: /src\/components\/dashboard/i,
name: "dashboard",
},
{
test: /src\/components\/flows/i,
name: "flows",
},
{
test: /(shiki\/langs)|(src\/utils\/markdownDeps)/,
name: "markdownDeps",
},
],
}
}
}
},
server: {
@@ -54,6 +37,16 @@ export default defineConfig({
},
},
plugins: [
federation({
name: "host",
shared: {
vue: {
singleton: true,
eager: true,
requiredVersion: "^3"
}
}
}),
vue({
template: {
compilerOptions: {