diff --git a/AGENTS.md b/AGENTS.md index 296ca2b..99bca4c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -82,7 +82,7 @@ CF custom tag resolution. `` requires a file literally named `Island. └──────────────────────────────────────┘ ▲ │ Vite plugin (coldspa/vite/plugin.js) - │ owns: framework sub-plugins, client-entry shims, + │ owns: client-entry shims, │ manifest, allowed hosts, hmr.host ▼ Vite dev server (or built dist/) @@ -96,7 +96,7 @@ CF custom tag resolution. `` requires a file literally named `Island. 2. **One bootstrap entry point.** All CFML-side side effects (config, process spawn, reload hooks) live in [coldspa/Bootstrap.cfc](coldspa/Bootstrap.cfc). `Application.cfc` (consumers') and `ModuleConfig.cfc` (ColdBox) should only *delegate* to it. Don't fork the logic. -3. **Optional peer deps.** Every framework package (`vue`, `react`, `react-dom`, `@vitejs/plugin-vue`, `@vitejs/plugin-react`) is declared as an *optional* peer dependency. The Vite plugin and SSR sidecar must `await import(...)` them lazily and only fail when the consumer actually opts into that framework. +3. **Optional peer deps.** Runtime framework packages (`vue`, `react`, `react-dom`) are declared as *optional* peer dependencies. The Vite plugin must not auto-import `@vitejs/plugin-vue` or `@vitejs/plugin-react`; consumers import and configure those in their own Vite configs. The SSR sidecar must `await import(...)` runtime framework packages lazily and only fail when the consumer actually opts into that framework. 4. **No Node inside the CF container.** Common Docker setup: CF in one container, Node in another (or on the host). `ProcessManager` probes for `npm` on PATH and silently skips spawning if it's missing, so `Bootstrap.onApplicationStart` is safe to call inside the CF container. The escape hatch for supervised setups is `COLDSPA_NO_BOOTSTRAP=1`. See [docs/docker.md](docs/docker.md). diff --git a/README.md b/README.md index d65c28f..36406c9 100644 --- a/README.md +++ b/README.md @@ -68,18 +68,22 @@ ColdBox users can skip this — the bundled `ModuleConfig.cfc` registers the cus ```js // vite.config.js import { defineConfig } from 'vite'; +import vue from '@vitejs/plugin-vue'; import coldspa from 'coldspa/vite'; export default defineConfig({ plugins: [ + vue(), coldspa({ - frameworks: ['vue'], // or ['vue', 'react'] + frameworks: ['vue'], globs: { vue: '/src/**/*.vue' } // where your components live }) ] }); ``` +Use the normal framework Vite plugin for every framework you enable. For React, install and add `@vitejs/plugin-react`; for mixed Vue + React configs, include both `vue()` and `react()` before `coldspa(...)`. + ## Use it from CFML ```cfml @@ -108,4 +112,3 @@ That's it — start your CF server and load the page. `coldspa.Bootstrap` auto-s - [Slots](docs/slots.md) — default + named slots, `cf_Slot`, gotchas - [Configuration](docs/configuration.md) — `coldspa.config.json`, env vars - [Docker & cross-host setups](docs/docker.md) — running CF and Vite on different hosts - diff --git a/coldspa/vite/plugin.js b/coldspa/vite/plugin.js index 8c0ae66..83f0999 100644 --- a/coldspa/vite/plugin.js +++ b/coldspa/vite/plugin.js @@ -1,23 +1,34 @@ // Coldspa Vite plugin. // // Encapsulates everything Vite needs to support cf_Island: -// - Loads framework sub-plugins (@vitejs/plugin-vue, @vitejs/plugin-react) // - Registers the matching client-entry shims as build inputs // - Sets manifest output, base path, dev server port/CORS for the CF integration // - Forces preserveEntrySignatures so the entry chunks aren't tree-shaken away // - Substitutes the user's component glob into the client entries at transform time // // Usage (simple): -// coldspa({ frameworks: ['vue'] }) +// import vue from '@vitejs/plugin-vue'; +// +// plugins: [ +// vue(), +// coldspa({ frameworks: ['vue'] }) +// ] // // Usage (custom component locations): -// coldspa({ -// frameworks: ['vue', 'react'], -// globs: { -// vue: '/app/**/*.vue', -// react: ['/app/**/*.jsx', '/app/**/*.tsx'] -// } -// }) +// import vue from '@vitejs/plugin-vue'; +// import react from '@vitejs/plugin-react'; +// +// plugins: [ +// vue(), +// react(), +// coldspa({ +// frameworks: ['vue', 'react'], +// globs: { +// vue: '/app/**/*.vue', +// react: ['/app/**/*.jsx', '/app/**/*.tsx'] +// } +// }) +// ] // // Options: // frameworks string[] default ['vue'] @@ -76,21 +87,6 @@ const DEFAULT_GLOBS = { const GLOB_PLACEHOLDER = '__COLDSPA_GLOB__'; -async function loadFrameworkPlugin(name) { - switch (name) { - case 'vue': { - const mod = await import('@vitejs/plugin-vue'); - return mod.default(); - } - case 'react': { - const mod = await import('@vitejs/plugin-react'); - return mod.default(); - } - default: - throw new Error(`[coldspa] Unknown framework "${name}". Supported: vue, react.`); - } -} - // Renders a glob option as the literal source text of an import.meta.glob argument: // "/src/**/*.vue" -> '/src/**/*.vue' // ["/a/**", "/b/**"] -> ["/a/**","/b/**"] @@ -208,10 +204,7 @@ export default function coldspa(options = {}) { // unhashed flat files into dist-ssr/ so the Node sidecar can require them. const isSsrBuild = process.env.COLDSPA_SSR === '1'; - const subPluginsPromise = Promise.all(frameworks.map(loadFrameworkPlugin)); - return [ - subPluginsPromise.then(plugins => plugins), { name: 'coldspa', diff --git a/docs/getting-started.md b/docs/getting-started.md index 6a7afc0..ae25f1d 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -68,34 +68,37 @@ Logs from the spawned processes land in `WEB-INF/coldspa-logs/{vite,ssr,manager} Coldspa uses Vite for asset bundling and a small Node sidecar for server-side rendering. ```bash -npm install coldspa vite vue # Vue +npm install coldspa vite vue @vitejs/plugin-vue # Vue # or npm install coldspa vite react react-dom @vitejs/plugin-react # React ``` -Coldspa lists every framework package as an **optional peer dependency**, so you only install what you actually use. +Coldspa lists every runtime framework package as an **optional peer dependency**, so you only install what you actually use. Install the matching Vite framework plugin in your app because your Vite config owns that setup. ## 4. Configure Vite ```js // vite.config.js import { defineConfig } from 'vite'; +import vue from '@vitejs/plugin-vue'; import coldspa from 'coldspa/vite'; export default defineConfig({ plugins: [ + vue(), coldspa({ - frameworks: ['vue'], // or ['vue', 'react'] + frameworks: ['vue'], globs: { - vue: '/src/**/*.vue', - react: '/src/**/*.{jsx,tsx}' + vue: '/src/**/*.vue' } }) ] }); ``` -The plugin handles framework sub-plugins, client-entry shims, manifest output, and dev-server host binding for you. +For React, import `react` from `@vitejs/plugin-react`, add `react()` to `plugins`, and set `frameworks: ['react']`. For mixed Vue + React configs, include both framework plugins before `coldspa(...)`. + +The Coldspa plugin handles client-entry shims, manifest output, and dev-server host binding for you. ## 5. Add `package.json` scripts diff --git a/package-lock.json b/package-lock.json index 2f587d4..76fb6df 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,20 +24,12 @@ "vue": "^3.4.0" }, "peerDependencies": { - "@vitejs/plugin-react": "^4.0.0", - "@vitejs/plugin-vue": "^5.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0", "vite": "^5.0.0 || ^6.0.0", "vue": "^3.4.0" }, "peerDependenciesMeta": { - "@vitejs/plugin-react": { - "optional": true - }, - "@vitejs/plugin-vue": { - "optional": true - }, "react": { "optional": true }, diff --git a/package.json b/package.json index 2788292..dbf32b0 100644 --- a/package.json +++ b/package.json @@ -55,16 +55,12 @@ "website:build": "cd website && astro build" }, "peerDependencies": { - "@vitejs/plugin-react": "^4.0.0", - "@vitejs/plugin-vue": "^5.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0", "vite": "^5.0.0 || ^6.0.0", "vue": "^3.4.0" }, "peerDependenciesMeta": { - "@vitejs/plugin-react": { "optional": true }, - "@vitejs/plugin-vue": { "optional": true }, "react": { "optional": true }, "react-dom": { "optional": true }, "vue": { "optional": true } @@ -82,4 +78,3 @@ "vue": "^3.4.0" } } - diff --git a/vite.config.js b/vite.config.js index dc543e0..04c294d 100644 --- a/vite.config.js +++ b/vite.config.js @@ -4,10 +4,14 @@ // For a single-framework dev server, use vite.config.vue.js or // vite.config.react.js via `npm run vite:vue` / `npm run vite:react`. import { defineConfig } from 'vite'; +import vue from '@vitejs/plugin-vue'; +import react from '@vitejs/plugin-react'; import coldspa from 'coldspa/vite'; export default defineConfig({ plugins: [ + vue(), + react(), coldspa({ frameworks: ['vue', 'react'], globs: { @@ -17,4 +21,3 @@ export default defineConfig({ }) ] }); - diff --git a/vite.config.react.js b/vite.config.react.js index 3475402..6776e10 100644 --- a/vite.config.react.js +++ b/vite.config.react.js @@ -1,10 +1,12 @@ // React-only Vite config for the demos/react island demo. // Run with: npm run vite:react import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; import coldspa from 'coldspa/vite'; export default defineConfig({ plugins: [ + react(), coldspa({ frameworks: ['react'], globs: { diff --git a/vite.config.vue.js b/vite.config.vue.js index 20c8ce5..395d287 100644 --- a/vite.config.vue.js +++ b/vite.config.vue.js @@ -1,10 +1,12 @@ // Vue-only Vite config for the demos/vue island demo. // Run with: npm run vite:vue import { defineConfig } from 'vite'; +import vue from '@vitejs/plugin-vue'; import coldspa from 'coldspa/vite'; export default defineConfig({ plugins: [ + vue(), coldspa({ frameworks: ['vue'], globs: { diff --git a/website/src/content/docs/index.mdx b/website/src/content/docs/index.mdx index 24d6021..c6fce44 100644 --- a/website/src/content/docs/index.mdx +++ b/website/src/content/docs/index.mdx @@ -33,7 +33,7 @@ Mount Vue or React components into CFML pages with server-side rendering and pro Choose `load`, `idle`, `visible`, or `client` per island. Below-the-fold widgets stay dormant until the user scrolls. - HMR in dev, hashed bundles + manifest in prod. One Vite plugin (`coldspa/vite`) wires up component globs, client entries, and the SSR build. + HMR in dev, hashed bundles + manifest in prod. `coldspa/vite` wires up component globs, client entries, and the SSR build alongside your normal framework plugins. Both frameworks supported out of the box, and you can mix them on the same page. @@ -42,4 +42,3 @@ Mount Vue or React components into CFML pages with server-side rendering and pro Default and named slots come from CFML markup -- including `` queries, ``, and conditionals. -