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

feat: add a middleware integration option #9

Closed
wants to merge 3 commits into from
Closed
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
18 changes: 18 additions & 0 deletions examples/middlware/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
}
28 changes: 28 additions & 0 deletions examples/middlware/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
pnpm-lock.yaml
dist/*
!dist/README.md
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

tmp
62 changes: 62 additions & 0 deletions examples/middlware/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Example

This application is created as [described here](https://vitejs.dev/guide/):

```sh
npm create vite@latest example -- --template react-ts
```

## Configure Vite

We changed the `vite.config.ts` to add the generation of the manifest file and made sure to overwrite the main entry point. Here's how the `vite.config.ts` looks after the changes:

```ts
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'

// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
build: {
// generates .vite/manifest.json in outDir
manifest: true,

rollupOptions: {
// overwrite default .html entry
input: "/src/main.tsx",
},
},
})
```

## Server side

We then added the [`main.go`](https://github.com/olivere/vite/tree/main/examples/basic/main.go).

### Development mode

If you want to try development mode, first run a new console and do `npm run dev` in the background: It should start the Vite development server on `http://localhost:5173`.

Now run the Go code as:

```sh
$ go run main.go -dev
Listening on on http://127.0.0.1:62002
```

Open up the URL in your browser and you should see the React app, being rendered by a Go HTML template. Not convinced? Open up development mode and go to the `Console`. You should see a message there, which was embedded by the Go code that rendered the HTML.

Notice that you can now change the HTML and JavaScript/TypeScript code, and Hot Module Reload (HMR) should run just fine and update the page inline.

### Production mode

First make sure to run `npm run build` before using production mode, as the Go code relies on embedding the `dist` directory into the Go binary.

Next, simply run the Go code:

```sh
$ go run main.go
Listening on on http://127.0.0.1:61736
```

Open the URL in your browser, and you're seeing a Go template being rendered with an underlying React app.
8 changes: 8 additions & 0 deletions examples/middlware/dist/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# `dist` Directory

This directory is used to store the built output from the Vite build process.

In development mode, this directory might be empty if the build process has not
yet been run. The Go application requires at least one file to be present in
this directory to embed its contents using `go:embed`. This README file ensures
the directory is not empty.
13 changes: 13 additions & 0 deletions examples/middlware/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
21 changes: 21 additions & 0 deletions examples/middlware/index.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!doctype html>
<html lang="en" class="h-full scroll-smooth">
<head>
<meta charset="UTF-8" />
<title>My Go Application</title>
<link rel="icon" href="/static/favicon.ico" type="image/x-icon">
</head>
<body class="min-h-screen antialiased">
<header>My Go Template Header</header>
<main>
<div id="root"></div>
<noscript>
<div>
<h2>JavaScript Required</h2>
<p>This application requires JavaScript to run. Please enable JavaScript in your browser settings and reload the page.</p>
</div>
</noscript>
</main>
<footer>My Go Template Footer</footer>
</body>
</html>
128 changes: 128 additions & 0 deletions examples/middlware/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package main

import (
"embed"
"flag"
"fmt"
"html/template"
"io/fs"
"log"
"net"
"net/http"
"os"

"github.com/olivere/vite"
)

//go:embed all:dist
var dist embed.FS

//go:embed index.tmpl
var goIndex embed.FS

func main() {
var isDev = flag.Bool("dev", false, "run in development mode")

flag.Parse()

// The following block sets up the environment and configuration for a Go
// application integrated with Vite. It determines whether the application
// should run in development or production mode based on the 'isDev' flag

var viteAssetsDir string
var viteAssetsURL string
var viteFS fs.FS

if *isDev {
viteAssetsDir = "src/assets"
viteAssetsURL = "/src/assets/"
viteFS = os.DirFS(".")
} else {
viteAssetsDir = "dist/assets"
viteAssetsURL = "/assets/"
fs, err := fs.Sub(dist, "dist")
if err != nil {
panic(err)
}
viteFS = fs
}

mux := http.NewServeMux()

// Serve assets that Vite would treat as 'public' assets.
//
// In this example, the 'static' directory is used as a replacement for
// Vite's default 'public' folder, and 'publicDir' is disabled in the Vite
// config. We're using the 'static' directory to achieve similar
// functionality, but available to the Go backend and Vite.
//
// To use a static asset in our Vite frontend, we import it like this:
//
// import viteLogo from '/static/vite.svg'

staticAssets := http.FileServer(http.Dir("static"))

mux.Handle("/static/", http.StripPrefix("/static/", staticAssets))

// Serve Vite-managed assets from the Go backend, accommodating both
// development and production environments.
//
// Usage in Vite remains the same as in a standard Vite setup. The Go backend
// will serve the assets from the correct location based on the environment.

viteAssets := http.FileServer(http.Dir(viteAssetsDir))

mux.Handle(viteAssetsURL, http.StripPrefix(viteAssetsURL, viteAssets))

// This block demonstrates the setup and usage of `vite.Middleware` in a Go
// web application.
//
// The Middleware is then applied to the root route ("/") of an HTTP mux,
// where it processes incoming requests before rendering a "index.tmpl"
// template.
//
// This setup allows a minimal integration of Vite with the Go web server,
// handling both development and production environments.

viteMiddleware, err := vite.NewMiddleware(vite.Config{
FS: viteFS,
IsDev: *isDev,
ViteURL: "http://localhost:5173",
})
if err != nil {
panic(err)
}

mux.HandleFunc("/", viteMiddleware.Use(func(w http.ResponseWriter, r *http.Request) {
tmpl, err := template.New("index.tmpl").ParseFS(goIndex, "index.tmpl")

if err != nil {
panic(err)
}

if err = tmpl.Execute(w, nil); err != nil {
panic(err)
}
}))

// Start a listener.
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
var err1 error
if l, err1 = net.Listen("tcp6", "[::1]:0"); err1 != nil {
panic(fmt.Errorf("starting HTTP server: %v", err))
}
}

// Create a new server.
server := http.Server{
Handler: mux,
}

log.Printf("Listening on on http://%s", l.Addr())

// Start the server.
if err := server.Serve(l); err != nil {
panic(err)
}
}
Loading