Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Assets url prefix support #17

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ type Config struct {
//
// [Scaffolding Your First Vite Project]: https://vitejs.dev/guide/#scaffolding-your-first-vite-project
ViteTemplate Scaffolding

// AssetsURLPrefix is the URL prefix for serving asset files, such as JavaScript,
// CSS, and other static resources. This is used only in production mode to
// construct the paths for assets based on the Vite manifest. It is useful
// when you have multiple builds served from different base paths, such as
// "example.com/admin/assets" for an admin panel and "example.com/assets" for
// the main application.
//
// In development mode, the `ViteURL` parameter defines the base URL for assets,
// making this parameter unnecessary.
// If not specified, the default prefix is "".
AssetsURLPrefix string
}

// Scaffolding represents various templates provided by Vite that can be used
Expand Down
31 changes: 31 additions & 0 deletions examples/multiple-vite-apps/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
.PHONY: start-frontend-app start-frontend-admin start-frontend-all start-backend start-all build-frontend-app build-frontend-admin build-frontend-all build-backend build-all

start-frontend-app:
cd frontend/app && npm run dev

start-frontend-admin:
cd frontend/admin-app && VITE_BASE_URL=/admin/ npm run dev

start-frontend-all:
$(MAKE) --no-print-directory -j2 start-frontend-app start-frontend-admin

start-backend:
go run main.go config.go render.go

start-all:
$(MAKE) --no-print-directory -j2 start-frontend-all start-backend

build-frontend-app:
cd frontend/app && npm run build

build-frontend-admin:
cd frontend/admin-app && npm run build

build-frontend-all:
$(MAKE) --no-print-directory -j2 build-frontend-app build-frontend-admin

build-backend:
go build -o backend main.go config.go render.go

build-all:
$(MAKE) --no-print-directory -j2 build-frontend-all build-backend
132 changes: 132 additions & 0 deletions examples/multiple-vite-apps/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# Vite Multi-App Example

This repository demonstrates how to set up and serve two separate Vite applications integrated into a Go backend.

## Overview

We have two Vite-based frontend applications:

1. **Admin App**: Located in `frontend/admin-app` (Vite + ReactTS).
2. **Main App**: Located in `frontend/app` (Vite + Vue.js).

Each application is served with its own Vite development server during development and shares a common Go backend in production.

## Setting Up Vite Tags in Go

To generate Vite tags for embedding the frontend assets, we use two separate functions: `ViteAppTags` and `ViteAdminTags`.

### Example Configuration for `ViteAppTags`

This function generates tags for the **Main App**:

```go
func ViteAppTags() template.HTML {
var err error
assetsApp, _ := fs.Sub(app.DistFS, "app")
viteConfig := vite.Config{
IsDev: app.InDevelopment,
ViteURL: "http://localhost:5173", // Development URL for 'app'
ViteEntry: "src/main.tsx",
ViteTemplate: vite.ReactTs,
FS: assetsApp,
}

viteFragment, err := vite.HTMLFragment(viteConfig)
if err != nil {
log.Printf("Vite fragment error: %v", err)
return "<!-- Vite fragment error -->"
}

return viteFragment.Tags
}
```

### Example Configuration for `ViteAdminTags`

This function generates tags for the **Admin App**:

```go
func ViteAdminTags() template.HTML {
var err error
assetsApp, _ := fs.Sub(app.DistFS, "admin-app")
viteConfig := vite.Config{
IsDev: app.InDevelopment,
ViteURL: "http://localhost:5174/admin", // Development URL for 'admin'
ViteEntry: "src/main.js",
ViteTemplate: vite.Vue,
FS: assetsApp,
AssetsURLPrefix: "/admin-app", // Custom prefix
}

viteFragment, err := vite.HTMLFragment(viteConfig)
if err != nil {
log.Printf("Vite fragment error: %v", err)
return "<!-- Vite fragment error -->"
}

return viteFragment.Tags
}
```

### Key Notes

1. **Different Vite URLs**: Each application has a unique `ViteURL` during development:
- Main App: `http://localhost:5173`
- Admin App: `http://localhost:5174/admin`

2. **Custom `AssetsURLPrefix`**: The `ViteAdminTags` function includes an `AssetsURLPrefix` for the **Admin App**, which modifies asset paths in production. For example:
```html
<script type="module" src="/admin-app/assets/main-DVg2CzpX.js"></script>
<link rel="modulepreload" href="/admin-app/assets/main-DVg2CzpX.js">
```

3. **Serving Assets in Production**: Custom prefixes require a handler to serve the assets. In `main.go`, this is achieved as follows:

```go
assetsAdminApp, _ := fs.Sub(app.DistFS, "admin-app")
assetsAdminAppFS := http.FileServer(http.FS(assetsAdminApp))
mux.Handle("/admin-app/assets/", http.StripPrefix("/admin-app", assetsAdminAppFS))
```

Here, `app.DistFS` points to the directory containing the `dist` files. You can use any file system, such as `os.DirFS` or `embed`.

## Running the Application

### Development Mode

To run the application in development:

1. Start all services:
```sh
make start-all
```

2. Open the browser:
- Main App: [http://localhost:8080/](http://localhost:8080/) → `Welcome, User!`
- Admin App: [http://localhost:8080/admin](http://localhost:8080/admin) → `Welcome, Admin!`

### Production Mode

To build and run the application in production:

1. Build the assets:
```sh
make build-all
```

2. Run the backend:
```sh
APP_ENV=production ./backend
```

3. Open the browser:
- Main App: [http://localhost:8080/](http://localhost:8080/)
- Admin App: [http://localhost:8080/admin](http://localhost:8080/admin)

## Additional Notes

- The `make start-all` and `make build-all` commands manage both Vite apps and the backend.
- Hot Module Reloading (HMR) is supported during development for both apps.
- In production, static files are served directly from the Go binary or filesystem.

Enjoy your multi-app setup with Vite and Go!
14 changes: 14 additions & 0 deletions examples/multiple-vite-apps/admin.gohtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!doctype html>
<html lang="en" class="h-full scroll-smooth">

<head>
<meta charset="UTF-8" />
<title>{{.Title}}</title>
{{ viteTags }}
</head>

<body class="min-h-screen antialiased">
<div id="app"></div>
</body>

</html>
11 changes: 11 additions & 0 deletions examples/multiple-vite-apps/app.gohtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!doctype html>
<html lang="en" class="h-full scroll-smooth">
<head>
<meta charset="UTF-8" />
<title>{{.Title}}</title>
{{ viteTags }}
</head>
<body class="min-h-screen antialiased">
<div id="root"></div>
</body>
</html>
Binary file added examples/multiple-vite-apps/backend
Binary file not shown.
23 changes: 23 additions & 0 deletions examples/multiple-vite-apps/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package main

import (
"io/fs"
"os"
)

type AppConfig struct {
DistFS fs.FS
Environment string
InProduction bool
InStaging bool
InDevelopment bool
}

func (app *AppConfig) LoadConfig() error {
app.Environment = os.Getenv("APP_ENV")
app.InProduction = app.Environment == "production"
app.InStaging = app.Environment == "staging"
app.InDevelopment = app.Environment == ""
app.DistFS = os.DirFS("dist")
return nil
}
Empty file.
6 changes: 6 additions & 0 deletions examples/multiple-vite-apps/frontend/admin-app/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue}]
charset = utf-8
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
30 changes: 30 additions & 0 deletions examples/multiple-vite-apps/frontend/admin-app/.gitignore
Original file line number Diff line number Diff line change
@@ -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?

*.tsbuildinfo
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

{
"$schema": "https://json.schemastore.org/prettierrc",
"semi": false,
"singleQuote": true,
"printWidth": 100
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"recommendations": [
"Vue.volar",
"vitest.explorer",
"dbaeumer.vscode-eslint",
"EditorConfig.EditorConfig",
"esbenp.prettier-vscode"
]
}
41 changes: 41 additions & 0 deletions examples/multiple-vite-apps/frontend/admin-app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# admin-app

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).

## Customize configuration

See [Vite Configuration Reference](https://vite.dev/config/).

## Project Setup

```sh
npm install
```

### Compile and Hot-Reload for Development

```sh
npm run dev
```

### Compile and Minify for Production

```sh
npm run build
```

### Run Unit Tests with [Vitest](https://vitest.dev/)

```sh
npm run test:unit
```

### Lint with [ESLint](https://eslint.org/)

```sh
npm run lint
```
25 changes: 25 additions & 0 deletions examples/multiple-vite-apps/frontend/admin-app/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import js from '@eslint/js'
import pluginVue from 'eslint-plugin-vue'
import pluginVitest from '@vitest/eslint-plugin'
import skipFormatting from '@vue/eslint-config-prettier/skip-formatting'

export default [
{
name: 'app/files-to-lint',
files: ['**/*.{js,mjs,jsx,vue}'],
},

{
name: 'app/files-to-ignore',
ignores: ['**/dist/**', '**/dist-ssr/**', '**/coverage/**'],
},

js.configs.recommended,
...pluginVue.configs['flat/essential'],

{
...pluginVitest.configs.recommended,
files: ['src/**/__tests__/*'],
},
skipFormatting,
]
13 changes: 13 additions & 0 deletions examples/multiple-vite-apps/frontend/admin-app/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
8 changes: 8 additions & 0 deletions examples/multiple-vite-apps/frontend/admin-app/jsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
},
"exclude": ["node_modules", "dist"]
}
Loading