Strip the Chatwoot frontend down to a standalone Vite + Vue 3 SPA that talks directly to the GoChat Go backend — no Ruby/Rails required. Deleted from frontend/ (all Rails-only): - Ruby: Gemfile, Gemfile.lock, config.ru, Capfile, Rakefile, bin/ (binstubs), config/ (application/boot/environment/routes/initializers/environments/locales), app/helpers/ (*.rb), app/views/ (ERB/jbuilder), app/assets/ (administrate scss) - Enterprise overlay (enterprise/ — pure Ruby/jbuilder, zero JS) - Infra: Procfile*, docker-compose*, docker/, deployment/, swagger/, .circleci/, .devcontainer/, .qlty/, .codegraph/, .windsurf/, .github/, .vscode/, .husky/ - Lint/config: .rubocop.yml, .scss-lint.yml, .rspec, rubocop/, .bundler-audit.yml, .annotaterb.yml, .all-contributorsrc, crowdin.yml, histoire.config.ts, etc. - Stale frontend/pnpm-lock.yaml (replaced by root workspace lockfile) Kept (frontend build essentials): - app/javascript/ (all Vue SPA source — dashboard/widget/sdk/portal/superadmin/survey/v3) - package.json, vite.config.ts, tailwind.config.js, postcss.config.js - theme/, vitest.setup.js, .prettierrc Key decoupling changes: - vite.config.ts: removed vite-plugin-ruby, added explicit rollupOptions.input for all 7 entrypoints + dev-server proxy (/api,/platform,/cable,/health → GoChat) - index.html: created static SPA entry that injects window.chatwootConfig / window.globalConfig (previously done by Rails ERB vueapp.html.erb) - package.json: renamed @gochat/frontend, removed vite-plugin-ruby/histoire/husky, scripts now use plain vite dev/build - tailwind.config.js: removed enterprise ERB content paths - markdownEmbeds.js: relocated markdown_embeds.yml into app/javascript/dashboard/config/ Root pnpm workspace: - package.json: dev:backend (air), dev:frontend (vite), dev (concurrently) - pnpm-workspace.yaml: frontend as workspace package - .gitignore: node_modules/, frontend/dist/, frontend build artifacts Verified: pnpm install + vite build succeeds (4446 modules, all 7 entrypoints produce JS+CSS chunks in dist/).
161 lines
4.9 KiB
TypeScript
161 lines
4.9 KiB
TypeScript
/// <reference types="vitest" />
|
|
|
|
/**
|
|
* GoChat frontend Vite config — decoupled from Rails.
|
|
*
|
|
* Originally Chatwoot used vite-plugin-ruby which auto-discovers entrypoints
|
|
* from app/javascript/entrypoints/ via config/vite.json. We replaced it with
|
|
* explicit rollupOptions.input so the frontend builds as a standalone Vite app
|
|
* that talks to the GoChat Go backend (no Ruby/Rails required).
|
|
*
|
|
* Entry points (mirrors app/javascript/entrypoints/):
|
|
* dashboard — agent dashboard SPA (mounted at #app via /app/*)
|
|
* widget — customer-facing web widget
|
|
* portal — help-center portal
|
|
* sdk — standalone SDK (library mode, built separately)
|
|
* superadmin — super admin panel
|
|
* survey — CSAT survey form
|
|
* v3app — v3 login/signup flow
|
|
*/
|
|
import { defineConfig } from 'vite';
|
|
import path from 'path';
|
|
import vue from '@vitejs/plugin-vue';
|
|
import yaml from '@rollup/plugin-yaml';
|
|
|
|
const isLibraryMode = process.env.BUILD_MODE === 'library';
|
|
const isTestMode = process.env.TEST === 'true';
|
|
|
|
const vueOptions = {
|
|
template: {
|
|
compilerOptions: {
|
|
isCustomElement: (tag: string) => ['ninja-keys'].includes(tag),
|
|
},
|
|
},
|
|
};
|
|
|
|
const resolveAliases = {
|
|
vue: 'vue/dist/vue.esm-bundler.js',
|
|
components: path.resolve(__dirname, './app/javascript/dashboard/components'),
|
|
next: path.resolve(__dirname, './app/javascript/dashboard/components-next'),
|
|
v3: path.resolve(__dirname, './app/javascript/v3'),
|
|
dashboard: path.resolve(__dirname, './app/javascript/dashboard'),
|
|
helpers: path.resolve(__dirname, './app/javascript/shared/helpers'),
|
|
shared: path.resolve(__dirname, './app/javascript/shared'),
|
|
survey: path.resolve(__dirname, './app/javascript/survey'),
|
|
widget: path.resolve(__dirname, './app/javascript/widget'),
|
|
assets: path.resolve(__dirname, './app/javascript/dashboard/assets'),
|
|
};
|
|
|
|
// Entrypoints that vite-plugin-ruby used to auto-discover
|
|
const entrypoints = {
|
|
dashboard: path.resolve(__dirname, './app/javascript/entrypoints/dashboard.js'),
|
|
widget: path.resolve(__dirname, './app/javascript/entrypoints/widget.js'),
|
|
portal: path.resolve(__dirname, './app/javascript/entrypoints/portal.js'),
|
|
superadmin: path.resolve(__dirname, './app/javascript/entrypoints/superadmin.js'),
|
|
superadmin_pages: path.resolve(__dirname, './app/javascript/entrypoints/superadmin_pages.js'),
|
|
survey: path.resolve(__dirname, './app/javascript/entrypoints/survey.js'),
|
|
v3app: path.resolve(__dirname, './app/javascript/entrypoints/v3app.js'),
|
|
};
|
|
|
|
let plugins = [vue(vueOptions), yaml()];
|
|
|
|
if (isLibraryMode) {
|
|
plugins = [];
|
|
} else if (isTestMode) {
|
|
plugins = [vue(vueOptions), yaml()];
|
|
}
|
|
|
|
export default defineConfig({
|
|
plugins,
|
|
css: {
|
|
preprocessorOptions: {
|
|
scss: {
|
|
api: 'modern-compiler',
|
|
},
|
|
},
|
|
},
|
|
// Dev server proxies API + WebSocket to the GoChat Go backend
|
|
server: {
|
|
port: 3036,
|
|
proxy: {
|
|
'/api': {
|
|
target: process.env.VITE_API_HOST || 'http://127.0.0.1:3000',
|
|
changeOrigin: true,
|
|
},
|
|
'/platform': {
|
|
target: process.env.VITE_API_HOST || 'http://127.0.0.1:3000',
|
|
changeOrigin: true,
|
|
},
|
|
'/cable': {
|
|
target: process.env.VITE_API_HOST || 'http://127.0.0.1:3000',
|
|
ws: true,
|
|
changeOrigin: true,
|
|
},
|
|
'/health': {
|
|
target: process.env.VITE_API_HOST || 'http://127.0.0.1:3000',
|
|
changeOrigin: true,
|
|
},
|
|
},
|
|
},
|
|
build: {
|
|
outDir: 'dist',
|
|
rollupOptions: {
|
|
input: isLibraryMode ? undefined : entrypoints,
|
|
output: {
|
|
// [NOTE] when not in library mode, no new keys will be added or overwritten
|
|
...(isLibraryMode
|
|
? {
|
|
dir: 'dist/sdk',
|
|
entryFileNames: (chunkInfo: { name: string }) => {
|
|
if (chunkInfo.name === 'sdk') {
|
|
return 'js/sdk.js';
|
|
}
|
|
return '[name].js';
|
|
},
|
|
}
|
|
: {}),
|
|
inlineDynamicImports: isLibraryMode, // Disable code-splitting for SDK
|
|
},
|
|
},
|
|
lib: isLibraryMode
|
|
? {
|
|
entry: path.resolve(__dirname, './app/javascript/entrypoints/sdk.js'),
|
|
formats: ['iife'] as const, // IIFE format for single file
|
|
name: 'sdk',
|
|
}
|
|
: undefined,
|
|
},
|
|
resolve: {
|
|
alias: resolveAliases,
|
|
},
|
|
test: {
|
|
environment: 'jsdom',
|
|
include: ['app/**/*.{test,spec}.?(c|m)[jt]s?(x)'],
|
|
coverage: {
|
|
reporter: ['lcov', 'text'],
|
|
include: ['app/**/*.js', 'app/**/*.vue'],
|
|
exclude: [
|
|
'app/**/*.@(spec|stories|routes).js',
|
|
'**/specs/**/*',
|
|
'**/i18n/**/*',
|
|
],
|
|
},
|
|
globals: true,
|
|
outputFile: 'coverage/sonar-report.xml',
|
|
pool: 'threads',
|
|
poolOptions: {
|
|
threads: {
|
|
singleThread: false,
|
|
},
|
|
},
|
|
server: {
|
|
deps: {
|
|
inline: ['tinykeys', '@material/mwc-icon'],
|
|
},
|
|
},
|
|
setupFiles: ['fake-indexeddb/auto', 'vitest.setup.js'],
|
|
mockReset: true,
|
|
clearMocks: true,
|
|
},
|
|
});
|