mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-14 08:02:58 +00:00
refactor: remove vue as a full-code app framework (#11690)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
a8e2f1f1ca
commit
54eeb451e4
@@ -58,10 +58,10 @@ function conditionsFor(svelte: boolean): string[] {
|
||||
/**
|
||||
* Detects which frontend frameworks are present in package.json
|
||||
*/
|
||||
export function detectFrameworks(appDir: string): { svelte: boolean; vue: boolean } {
|
||||
export function detectFrameworks(appDir: string): { svelte: boolean } {
|
||||
const packageJsonPath = path.join(appDir, "package.json");
|
||||
if (!fs.existsSync(packageJsonPath)) {
|
||||
return { svelte: false, vue: false };
|
||||
return { svelte: false };
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -73,10 +73,9 @@ export function detectFrameworks(appDir: string): { svelte: boolean; vue: boolea
|
||||
|
||||
return {
|
||||
svelte: "svelte" in allDeps,
|
||||
vue: "vue" in allDeps,
|
||||
};
|
||||
} catch {
|
||||
return { svelte: false, vue: false };
|
||||
return { svelte: false };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,17 +255,6 @@ export async function createFrameworkPlugins(appDir: string): Promise<any[]> {
|
||||
plugins.push(createSveltePlugin(appDir));
|
||||
}
|
||||
|
||||
if (frameworks.vue) {
|
||||
log.info(colors.blue("🔧 Vue detected, adding vue plugin..."));
|
||||
throw new Error("Vue plugin not supported yet");
|
||||
// try {
|
||||
// const esbuildPluginVue = await import("esbuild-plugin-vue3");
|
||||
// plugins.push(esbuildPluginVue.default());
|
||||
// } catch (error: any) {
|
||||
// log.warn(colors.yellow(`Failed to load vue plugin: ${error.message}`));
|
||||
// }
|
||||
}
|
||||
|
||||
return plugins;
|
||||
}
|
||||
|
||||
@@ -313,7 +301,7 @@ export async function createBundle(
|
||||
// Use the entryPoint's directory if provided, otherwise fall back to cwd.
|
||||
const appDir = options.entryPoint ? path.dirname(options.entryPoint) : process.cwd();
|
||||
const frameworks = detectFrameworks(appDir);
|
||||
const defaultEntry = (frameworks.svelte || frameworks.vue) ? "index.ts" : "index.tsx";
|
||||
const defaultEntry = frameworks.svelte ? "index.ts" : "index.tsx";
|
||||
|
||||
const entryPoint = options.entryPoint ?? defaultEntry;
|
||||
const outDir = options.outDir ?? "dist";
|
||||
@@ -331,7 +319,7 @@ export async function createBundle(
|
||||
// Ensure node_modules exists in the app directory
|
||||
await ensureNodeModules(appDir);
|
||||
|
||||
// Load framework-specific plugins (svelte, vue) based on package.json
|
||||
// Load framework-specific plugins (svelte) based on package.json
|
||||
const frameworkPlugins = await createFrameworkPlugins(appDir);
|
||||
|
||||
// Ensure output directory exists
|
||||
|
||||
@@ -12,10 +12,10 @@ interface BundleOptions {
|
||||
}
|
||||
|
||||
/** Every entry point a raw app may have, most specific first, with the
|
||||
* framework's preferred one promoted — Svelte and Vue mount from a `.ts`. */
|
||||
* framework's preferred one promoted — Svelte mounts from a `.ts`. */
|
||||
function entryPointFor(appDir: string): string {
|
||||
const frameworks = detectFrameworks(appDir);
|
||||
const candidates = frameworks.svelte || frameworks.vue
|
||||
const candidates = frameworks.svelte
|
||||
? ["index.ts", "index.tsx", "index.js"]
|
||||
: ["index.tsx", "index.ts", "index.js"];
|
||||
const entry = candidates.find((c) => fs.existsSync(path.join(appDir, c)));
|
||||
|
||||
@@ -481,7 +481,7 @@ async function dev(opts: DevOptions, appFolder?: string) {
|
||||
|
||||
// Detect frameworks to determine default entry point
|
||||
const frameworks = detectFrameworks(process.cwd());
|
||||
const defaultEntry = frameworks.svelte || frameworks.vue
|
||||
const defaultEntry = frameworks.svelte
|
||||
? "index.ts"
|
||||
: "index.tsx";
|
||||
const entryPoint = opts.entry ?? defaultEntry;
|
||||
@@ -577,7 +577,7 @@ async function dev(opts: DevOptions, appFolder?: string) {
|
||||
|
||||
const buildOptions = getDevBuildOptions(entryPoint, frameworks.svelte);
|
||||
|
||||
// Load framework-specific plugins (svelte, vue) based on package.json
|
||||
// Load framework-specific plugins (svelte) based on package.json
|
||||
const frameworkPlugins = await createFrameworkPlugins(appDir);
|
||||
|
||||
const wmillPlugin = {
|
||||
@@ -1602,7 +1602,7 @@ const command = new Command()
|
||||
})
|
||||
.option(
|
||||
"--entry <entry:string>",
|
||||
"Entry point file (default: index.ts for Svelte/Vue, index.tsx otherwise)",
|
||||
"Entry point file (default: index.ts for Svelte, index.tsx otherwise)",
|
||||
)
|
||||
.option("--no-open", "Don't automatically open the browser")
|
||||
.option(
|
||||
|
||||
@@ -114,7 +114,7 @@ async function validateBuild(
|
||||
|
||||
// Try to create a bundle - this will validate that all dependencies are in place
|
||||
const frameworks = detectFrameworks(appDir);
|
||||
const entryFile = frameworks.svelte || frameworks.vue ? "index.ts" : "index.tsx";
|
||||
const entryFile = frameworks.svelte ? "index.ts" : "index.tsx";
|
||||
const entryPoint = path.join(appDir, entryFile);
|
||||
await createBundle({
|
||||
entryPoint,
|
||||
|
||||
@@ -85,21 +85,6 @@ const app = mount(App, { target: document.getElementById("root")! });
|
||||
export default app;
|
||||
`;
|
||||
|
||||
const appVue = `<template>
|
||||
<h1>Hello {{ msg }}</h1>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
const msg = ref('world');
|
||||
</script>`;
|
||||
|
||||
const indexVue = `import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import "./index.css";
|
||||
|
||||
createApp(App).mount('#root')`;
|
||||
|
||||
const indexCss = `body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
@@ -171,21 +156,6 @@ const templates: Record<string, FrameworkTemplate> = {
|
||||
"svelte": "^5.55.5",
|
||||
"windmill-client": "^1"
|
||||
}
|
||||
}`,
|
||||
},
|
||||
},
|
||||
vue: {
|
||||
name: "Vue 3",
|
||||
files: {
|
||||
"/index.ts": indexVue,
|
||||
"/App.vue": appVue,
|
||||
"/index.css": indexCss,
|
||||
"/package.json": `{
|
||||
"dependencies": {
|
||||
"core-js": "3.26.1",
|
||||
"vue": "3.5.13",
|
||||
"windmill-client": "^1"
|
||||
}
|
||||
}`,
|
||||
},
|
||||
},
|
||||
@@ -251,7 +221,7 @@ interface DataConfig {
|
||||
schema?: string;
|
||||
}
|
||||
|
||||
type FrameworkKey = "react19" | "react18" | "svelte5" | "vue";
|
||||
type FrameworkKey = "react19" | "react18" | "svelte5";
|
||||
|
||||
interface NewAppOptions extends GlobalOptions {
|
||||
/** App summary (short description). Skips the prompt when provided. */
|
||||
@@ -384,7 +354,7 @@ async function newApp(opts: NewAppOptions) {
|
||||
}
|
||||
|
||||
// Ask for framework (skipped if --framework is provided)
|
||||
const VALID_FRAMEWORKS: FrameworkKey[] = ["react19", "react18", "svelte5", "vue"];
|
||||
const VALID_FRAMEWORKS: FrameworkKey[] = ["react19", "react18", "svelte5"];
|
||||
let framework: string;
|
||||
if (opts.framework !== undefined) {
|
||||
if (!VALID_FRAMEWORKS.includes(opts.framework)) {
|
||||
@@ -403,7 +373,6 @@ async function newApp(opts: NewAppOptions) {
|
||||
{ name: "React 19 (Recommended)", value: "react19" },
|
||||
{ name: "React 18", value: "react18" },
|
||||
{ name: "Svelte 5", value: "svelte5" },
|
||||
{ name: "Vue 3", value: "vue" },
|
||||
],
|
||||
});
|
||||
}
|
||||
@@ -900,7 +869,7 @@ const command = new Command()
|
||||
)
|
||||
.option(
|
||||
"--framework <framework:string>",
|
||||
"Framework template: react19 | react18 | svelte5 | vue. Skips the prompt when provided. Triggers non-interactive mode."
|
||||
"Framework template: react19 | react18 | svelte5. Skips the prompt when provided. Triggers non-interactive mode."
|
||||
)
|
||||
.option(
|
||||
"--datatable <datatable:string>",
|
||||
|
||||
@@ -461,7 +461,7 @@ export async function pushRawApp(
|
||||
log.info(colors.yellow.bold(`Creating raw app ${remotePath} bundle...`));
|
||||
// Detect frameworks to determine entry point
|
||||
const frameworks = detectFrameworks(localPath);
|
||||
const entryFile = frameworks.svelte || frameworks.vue
|
||||
const entryFile = frameworks.svelte
|
||||
? "index.ts"
|
||||
: "index.tsx";
|
||||
const entryPoint = localPath + entryFile;
|
||||
|
||||
Generated
+8
-8
@@ -6351,7 +6351,7 @@ You need three values to run the command:
|
||||
|
||||
1. **summary** — a short description of the app
|
||||
2. **path** — the windmill path, e.g. \`f/folder/my_app\` or \`u/username/my_app\`
|
||||
3. **framework** — one of \`react19\` (recommended), \`react18\`, \`svelte5\`, \`vue\`
|
||||
3. **framework** — one of \`react19\` (recommended), \`react18\`, \`svelte5\`
|
||||
|
||||
**summary and path**: if the user's request did not supply them, ask. Do not guess values and do not invent paths.
|
||||
|
||||
@@ -6423,7 +6423,7 @@ my_app{{RAW_APP_SUFFIX}}/
|
||||
├── DATATABLES.md # Database schemas (run 'wmill app generate-agents' to refresh)
|
||||
├── raw_app.yaml # App configuration (summary, path, data settings)
|
||||
├── index.tsx # Frontend entry point
|
||||
├── App.tsx # Main React/Svelte/Vue component
|
||||
├── App.tsx # Main React/Svelte component
|
||||
├── index.css # Styles
|
||||
├── package.json # Frontend dependencies
|
||||
├── wmill.ts # Auto-generated backend type definitions (DO NOT EDIT)
|
||||
@@ -6581,7 +6581,7 @@ For the rest, tell the user which command fits their intent and let them run it
|
||||
|
||||
# Windmill Raw Apps
|
||||
|
||||
Raw apps let you build custom frontends with React, Svelte, or Vue that connect to Windmill backend runnables and datatables.
|
||||
Raw apps let you build custom frontends with React or Svelte that connect to Windmill backend runnables and datatables.
|
||||
|
||||
## App shape
|
||||
|
||||
@@ -6597,7 +6597,7 @@ A new app gets the setup Windmill's own new-app dialog gives one: a framework, t
|
||||
|
||||
### Framework
|
||||
|
||||
Use the framework the user named. When they named none, follow what there is to follow: the app being edited, or the apps the request points at. With nothing to follow, use **React 19**, the recommended default, **without asking**. React 18, Svelte 5 and Vue are for when they are asked for.
|
||||
Use the framework the user named. When they named none, follow what there is to follow: the app being edited, or the apps the request points at. With nothing to follow, use **React 19**, the recommended default, **without asking**. React 18 and Svelte 5 are for when they are asked for. These three are the only frameworks the raw app bundler builds. When the user asks to build or convert an app in another framework, write no code in it: say it is not available and offer these three instead.
|
||||
|
||||
### Data setup
|
||||
|
||||
@@ -6627,7 +6627,7 @@ Then build what was chosen: the data table and schema become the app's \`data.da
|
||||
|
||||
### Entrypoint
|
||||
|
||||
The entrypoint is \`index.tsx\` for React and \`index.ts\` for Svelte and Vue. It is both the bundling entrypoint (the bundler is esbuild) and the **mount** entrypoint: the preview executes the bundle against an empty \`<div id="root">\` and auto-renders nothing, so the entrypoint must mount a top-level \`App\` itself. Keep the UI in \`App.tsx\` / \`App.svelte\` / \`App.vue\` and keep the entrypoint as the mount shim.
|
||||
The entrypoint is \`index.tsx\` for React and \`index.ts\` for Svelte. It is both the bundling entrypoint (the bundler is esbuild) and the **mount** entrypoint: the preview executes the bundle against an empty \`<div id="root">\` and auto-renders nothing, so the entrypoint must mount a top-level \`App\` itself. Keep the UI in \`App.tsx\` / \`App.svelte\` and keep the entrypoint as the mount shim.
|
||||
|
||||
React (\`index.tsx\`):
|
||||
|
||||
@@ -6639,7 +6639,7 @@ import App from './App'
|
||||
createRoot(document.getElementById('root')!).render(<App />)
|
||||
\`\`\`
|
||||
|
||||
Svelte (\`index.ts\`): \`mount(App, { target: document.getElementById('root')! })\`. Vue (\`index.ts\`): \`createApp(App).mount('#root')\`.
|
||||
Svelte (\`index.ts\`): \`mount(App, { target: document.getElementById('root')! })\`.
|
||||
|
||||
**Never replace the entrypoint with a bare component** (\`export default function App() { ... }\` and no mount call). A component that is defined but never mounted renders a blank screen with **no error thrown** — it never executes, so nothing reaches the console or the error overlay. If an app renders blank, check that the entrypoint still mounts \`App\` into \`#root\`.
|
||||
|
||||
@@ -8102,7 +8102,7 @@ app related commands
|
||||
- \`app dev [app_folder:string]\` - Start a development server for building apps with live reload and hot module replacement
|
||||
- \`--port <port:number>\` - Port to run the dev server on (will find next available port if occupied)
|
||||
- \`--host <host:string>\` - Host to bind the dev server to
|
||||
- \`--entry <entry:string>\` - Entry point file (default: index.ts for Svelte/Vue, index.tsx otherwise)
|
||||
- \`--entry <entry:string>\` - Entry point file (default: index.ts for Svelte, index.tsx otherwise)
|
||||
- \`--no-open\` - Don't automatically open the browser
|
||||
- \`--recording\` - Frame the app in a shell with a Record button, to capture a replayable session recording of the app under development
|
||||
- \`app lint [app_folder:string]\` - Lint a raw app folder to validate structure and buildability
|
||||
@@ -8113,7 +8113,7 @@ app related commands
|
||||
- \`app new\` - create a new raw app from a template
|
||||
- \`--summary <summary:string>\` - App summary (short description). Skips the prompt when provided. Triggers non-interactive mode.
|
||||
- \`--path <path:string>\` - App path (e.g., f/folder/my_app or u/username/my_app). Skips the prompt when provided. Triggers non-interactive mode.
|
||||
- \`--framework <framework:string>\` - Framework template: react19 | react18 | svelte5 | vue. Skips the prompt when provided. Triggers non-interactive mode.
|
||||
- \`--framework <framework:string>\` - Framework template: react19 | react18 | svelte5. Skips the prompt when provided. Triggers non-interactive mode.
|
||||
- \`--datatable <datatable:string>\` - Datatable to wire up. Without this flag in non-interactive mode, no datatable is configured.
|
||||
- \`--schema <schema:string>\` - Schema to use with --datatable. If it doesn't exist yet, a sql_to_apply/ migration (CREATE SCHEMA IF NOT EXISTS) is written for it.
|
||||
- \`--overwrite\` - Overwrite the target directory if it already exists, without prompting.
|
||||
|
||||
Reference in New Issue
Block a user