mirror of
https://github.com/wailsapp/wails.git
synced 2026-03-14 22:55:48 +01:00
Support template generation
This commit is contained in:
parent
773dca77d4
commit
59a8ecc5b2
35 changed files with 667 additions and 80 deletions
|
|
@ -34,6 +34,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Added `init()` method in runtime to allow manual initialisation of the runtime by [@leaanthony](https://github.com/leaanthony)
|
||||
- Added `WindowDidMoveDebounceMS` option to Window's WindowOptions by [@leaanthony](https://github.com/leaanthony)
|
||||
- Added Single Instance feature by [@leaanthony](https://github.com/leaanthony). Based on the [v2 PR](https://github.com/wailsapp/wails/pull/2951) by @APshenkin.
|
||||
- Added template generation using `wails3 generate template` command by [@leaanthony](https://github.com/leaanthony)
|
||||
|
||||
### Fixed
|
||||
|
||||
|
|
|
|||
293
docs/src/content/docs/guides/custom-templates.mdx
Normal file
293
docs/src/content/docs/guides/custom-templates.mdx
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
---
|
||||
title: Creating Custom Templates
|
||||
description: Learn how to create and customise your own Wails v3 templates
|
||||
sidebar:
|
||||
order: 50
|
||||
---
|
||||
|
||||
This guide will walk you through the process of creating a custom template for Wails v3.
|
||||
|
||||
## Why would I make a custom template?
|
||||
|
||||
Wails comes with a number of pre-configured templates that allow you to get your application up and running quickly. But if you need a more customised setup, you can create your own template to suit your needs. This can then be shared with the Wails community for others to use.
|
||||
|
||||
### 1. Generating a Template
|
||||
|
||||
To create a custom template, you can use the `wails generate template` command:
|
||||
|
||||
```bash
|
||||
wails3 generate template -name mytemplate
|
||||
```
|
||||
|
||||
This will create a new directory called "mytemplate" in your current directory.
|
||||
|
||||
The `wails3 generate template` command supports the following options:
|
||||
|
||||
| Option | Description | Default |
|
||||
|----------------|---------------------------------------------------|-------------------|
|
||||
| `-name` | The name of your template (required) | - |
|
||||
| `-frontend` | Path to an existing frontend directory to include | - |
|
||||
| `-author` | The author of the template | - |
|
||||
| `-description` | A description of the template | - |
|
||||
| `-helpurl` | URL for template documentation | - |
|
||||
| `-dir` | Directory to generate the template in | Current directory |
|
||||
| `-version` | Template version | v0.0.1 |
|
||||
|
||||
For example, to create a template with all options:
|
||||
|
||||
```bash
|
||||
wails3 generate template \
|
||||
-name "My Custom Template" \
|
||||
-frontend ./my-existing-frontend \
|
||||
-author "Your Name" \
|
||||
-description "A template with my preferred setup" \
|
||||
-helpurl "https://github.com/yourusername/template-docs" \
|
||||
-dir ./templates \
|
||||
-version "v1.0.0"
|
||||
```
|
||||
|
||||
:::tip
|
||||
Using the `-frontend` option will copy an existing web frontend project into the template.
|
||||
:::
|
||||
|
||||
### 2. Configure Template Metadata
|
||||
|
||||
If you didn't specify the template configuration when generating the template, you can update the `template.json` file in the template directory:
|
||||
|
||||
```json5
|
||||
{
|
||||
"name": "Your Template Name", // Display name of your template
|
||||
"shortname": "template-shortname", // Used when referencing your template
|
||||
"author": "Your Name", // Template author
|
||||
"description": "Template description", // Template description
|
||||
"helpurl": "https://your-docs.com", // Documentation URL
|
||||
"version": "v0.0.1", // Template version
|
||||
"schema": 3 // Must be kept as 3 for Wails v3
|
||||
}
|
||||
```
|
||||
:::caution
|
||||
The `schema` field must remain set to `3` for compatibility with Wails v3.
|
||||
:::
|
||||
|
||||
### 3. Set Up Build Tasks
|
||||
|
||||
In the `build` directory is `Taskfile.yml` where you can define your template's build process.
|
||||
This file uses [Task](https://taskfile.dev) for build automation. The key steps are:
|
||||
|
||||
```yaml
|
||||
tasks:
|
||||
install:frontend:deps:
|
||||
summary: Install frontend dependencies
|
||||
dir: frontend
|
||||
sources:
|
||||
- package.json
|
||||
- package-lock.json
|
||||
generates:
|
||||
- node_modules/*
|
||||
preconditions:
|
||||
- sh: npm version
|
||||
msg: "Looks like npm isn't installed. Npm is part of the Node installer: https://nodejs.org/en/download/"
|
||||
cmds:
|
||||
- npm install
|
||||
|
||||
build:frontend:
|
||||
summary: Build the frontend project
|
||||
dir: frontend
|
||||
sources:
|
||||
- "**/*"
|
||||
generates:
|
||||
- dist/*
|
||||
deps:
|
||||
- task: install:frontend:deps
|
||||
- task: generate:bindings
|
||||
cmds:
|
||||
- npm run build -q
|
||||
|
||||
|
||||
```
|
||||
|
||||
### 4. Frontend Setup
|
||||
|
||||
If you did not use `-frontend` when generating the template, you need to add frontend files to your template.
|
||||
|
||||
There are a number of ways to set up your frontend: starting from scratch or using an existing framework.
|
||||
|
||||
import { Tabs, TabItem } from '@astrojs/starlight/components';
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Start from Scratch">
|
||||
If you want to start from scratch, you can create your frontend project just like you would for any web application.
|
||||
The `frontend` directory in your template is just a regular directory where you can set up your preferred
|
||||
development environment. You might want to use build tools like Vite, webpack, or even just plain HTML, CSS, and
|
||||
JavaScript - it's entirely up to you!
|
||||
|
||||
For example, if you're using Vite, you could navigate to the `frontend` directory and run:
|
||||
|
||||
```bash
|
||||
npm create vite@latest .
|
||||
```
|
||||
|
||||
Then follow the prompts to set up your project exactly how you want it. The key thing to remember is that this is just a regular frontend project - you can use any tools, frameworks, or libraries you're familiar with.
|
||||
</TabItem>
|
||||
<TabItem label="Use Existing Framework">
|
||||
For this example, we'll use [Vite](https://vitejs.dev/) to set up a React frontend project:
|
||||
|
||||
```bash
|
||||
npm create vite@latest frontend -- --template react
|
||||
cd frontend
|
||||
npm install
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
||||
Now you have the frontend files in place, update `common/Taskfile.yml` with the appropriate commands:
|
||||
```yaml
|
||||
tasks:
|
||||
install:frontend:deps:
|
||||
summary: Install frontend dependencies
|
||||
dir: frontend
|
||||
sources:
|
||||
- package.json
|
||||
- package-lock.json
|
||||
generates:
|
||||
- node_modules/*
|
||||
preconditions:
|
||||
- sh: npm version
|
||||
msg: "Looks like npm isn't installed. Npm is part of the Node installer: https://nodejs.org/en/download/"
|
||||
cmds:
|
||||
- npm install
|
||||
|
||||
build:frontend:
|
||||
summary: Build the frontend project
|
||||
dir: frontend
|
||||
sources:
|
||||
- "**/*"
|
||||
generates:
|
||||
- dist/*
|
||||
deps:
|
||||
- task: install:frontend:deps
|
||||
- task: generate:bindings
|
||||
cmds:
|
||||
- npm run build -q
|
||||
```
|
||||
|
||||
:::note
|
||||
For this example, the default Tasks do not need updating as they use the standard `npm install` and `npm run build` commands.
|
||||
:::
|
||||
|
||||
### 5. Configure the Go Application
|
||||
|
||||
The default files in the template directory are sufficient to get users started. However, you may want to provide some additional functionality to demonstrate your template's capabilities. The best way to do this is to rename `main.go.tmpl` to `main.go` and edit it like any other Go file. Once finished, ensure you rename it back to `main.go.tmpl` before committing your changes. If you do not care about having a templated `main.go` file (the default template injests the project name into the `Name` field of the application), you can skip this step.
|
||||
|
||||
#### Template Variables
|
||||
|
||||
Wails uses Go's templating engine to process files with the `.tmpl` extension. During template generation, several variables are available for use in your template files:
|
||||
|
||||
| Variable | Description | Example |
|
||||
|----------------------|----------------------------------|-----------------------------------|
|
||||
| `Name` | The name of the project | `"MyApp"` |
|
||||
| `BinaryName` | The name of the generated binary | `"myapp"` |
|
||||
| `ProductName` | The product name | `"My Application"` |
|
||||
| `ProductDescription` | Description of the product | `"An awesome application"` |
|
||||
| `ProductVersion` | Version of the product | `"1.0.0"` |
|
||||
| `ProductCompany` | Company name | `"My Company Ltd"` |
|
||||
| `ProductCopyright` | Copyright information | `"Copyright 2024 My Company Ltd"` |
|
||||
| `ProductComments` | Additional product comments | `"Built with Wails"` |
|
||||
| `ProductIdentifier` | Unique product identifier | `"com.mycompany.myapp"` |
|
||||
| `Typescript` | Whether TypeScript is being used | `true` or `false` |
|
||||
| `WailsVersion` | The version of Wails being used | `"3.0.0"` |
|
||||
|
||||
You can use these variables in your template files using Go's template syntax:
|
||||
|
||||
```go
|
||||
// main.go.tmpl
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/v3/pkg/application"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := application.New(application.Options{
|
||||
Name: "{{.ProductName}}",
|
||||
Description: "{{.ProductDescription}}",
|
||||
})
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
:::tip
|
||||
Templating can be applied to any file in your template, even html files, so long as the filename has `.tmpl` in its name.
|
||||
:::
|
||||
|
||||
### 6. Testing Your Template
|
||||
|
||||
To test your template:
|
||||
|
||||
1. Generate a project using your template: `wails3 init -n testproject -t path/to/your/template`
|
||||
2. Run `wails3 build` to generate the production build and make sure the binary in `bin` runs correctly
|
||||
3. Run `wails3 dev` to start the development server.
|
||||
4. Test that changes to the frontend code are reflected in the application.
|
||||
5. Test that changes to the Go code rebuild and relaunch the application
|
||||
|
||||
### 7. Sharing Your Template
|
||||
|
||||
Once your template is ready, you can share it with the community by hosting it on GitHub. Here's how:
|
||||
|
||||
1. Create a new GitHub repository for your template
|
||||
2. Push your template code to the repository
|
||||
3. Tag your releases using semantic versioning (e.g., v1.0.0)
|
||||
|
||||
Users can then use your template directly from GitHub using the HTTPS URL:
|
||||
|
||||
```bash
|
||||
wails3 init -n myapp -t https://github.com/yourusername/your-template
|
||||
```
|
||||
|
||||
You can also specify a particular version using the URL format:
|
||||
|
||||
```bash
|
||||
# Use a specific version tag
|
||||
wails3 init -n myapp -t https://github.com/yourusername/your-template/releases/tag/v1.0.0
|
||||
|
||||
# Use a specific branch
|
||||
wails3 init -n myapp -t https://github.com/yourusername/your-template/tree/main
|
||||
```
|
||||
|
||||
To test your template before sharing:
|
||||
|
||||
1. Push your changes to GitHub
|
||||
2. Create a new test project using the HTTPS URL:
|
||||
```bash
|
||||
wails3 init -n testapp -t https://github.com/yourusername/your-template
|
||||
```
|
||||
3. Verify that all files are correctly generated
|
||||
4. Test the build and development workflow as described in the testing section
|
||||
|
||||
:::note
|
||||
Make sure your repository is public if you want others to use your template.
|
||||
:::
|
||||
|
||||
For more information, visit the [Wails documentation](https://wails.io)
|
||||
|
||||
|
||||
## Best Practices
|
||||
|
||||
Let's talk about some key practices that will help make your template more useful and maintainable. Here are the main areas to focus on:
|
||||
|
||||
1. **Make Your Template Easy to Understand**
|
||||
- Write a clear, helpful README.md that gets users started quickly
|
||||
- Add comments in your config files to explain the "why" behind your choices
|
||||
- Show examples of common customisations - users love to see real-world use cases!
|
||||
|
||||
2. **Keep Dependencies Happy**
|
||||
- Stay on top of your frontend package updates
|
||||
- Lock down specific versions in package.json to avoid surprises
|
||||
- Let users know upfront what they'll need to have installed
|
||||
|
||||
3. **Love Your Template**
|
||||
- Keep it fresh with regular updates
|
||||
- Give it a thorough test drive before sharing
|
||||
- Share it with the Wails community - we'd love to see what you create!
|
||||
|
|
@ -52,6 +52,7 @@ func main() {
|
|||
generate.NewSubCommandFunction("syso", "Generate Windows .syso file", commands.GenerateSyso)
|
||||
generate.NewSubCommandFunction("runtime", "Generate the pre-built version of the runtime", commands.GenerateRuntime)
|
||||
generate.NewSubCommandFunction("webview2bootstrapper", "Generate WebView2 bootstrapper", commands.GenerateWebView2Bootstrapper)
|
||||
generate.NewSubCommandFunction("template", "Generate a new template", commands.GenerateTemplate)
|
||||
|
||||
update := app.NewSubCommand("update", "Update tools")
|
||||
update.NewSubCommandFunction("build-assets", "Updates the build assets using the given config file", commands.UpdateBuildAssets)
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ require (
|
|||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||
github.com/lithammer/fuzzysearch v1.1.5 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/mattn/go-zglob v0.0.6 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
|
|
|
|||
|
|
@ -212,8 +212,8 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
|
|||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/mattn/go-zglob v0.0.6 h1:mP8RnmCgho4oaUYDIDn6GNxYk+qJGUs8fJLn+twYj2A=
|
||||
|
|
@ -303,8 +303,6 @@ github.com/tc-hib/winres v0.3.1 h1:CwRjEGrKdbi5CvZ4ID+iyVhgyfatxFoizjPhzez9Io4=
|
|||
github.com/tc-hib/winres v0.3.1/go.mod h1:C/JaNhH3KBvhNKVbvdlDWkbMDO9H4fKKDaN7/07SSuk=
|
||||
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
|
||||
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/wailsapp/go-webview2 v1.0.18 h1:SSSCoLA+MYikSp1U0WmvELF/4c3x5kH8Vi31TKyZ4yk=
|
||||
github.com/wailsapp/go-webview2 v1.0.18/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc=
|
||||
github.com/wailsapp/go-webview2 v1.0.19-0.20241227010006-11c6e567b916 h1:W0UQJWILiXJOOCg7ec5xJNqxkg4Ced5KCGO4tFAf13w=
|
||||
github.com/wailsapp/go-webview2 v1.0.19-0.20241227010006-11c6e567b916/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc=
|
||||
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
|
||||
|
|
|
|||
9
v3/internal/commands/generate_template.go
Normal file
9
v3/internal/commands/generate_template.go
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/v3/internal/templates"
|
||||
)
|
||||
|
||||
func GenerateTemplate(options *templates.BaseTemplate) error {
|
||||
return templates.GenerateTemplate(options)
|
||||
}
|
||||
|
|
@ -122,10 +122,6 @@ func Init(options *flags.Init) error {
|
|||
|
||||
options.ProjectName = sanitizeFileName(options.ProjectName)
|
||||
|
||||
if !templates.ValidTemplateName(options.TemplateName) {
|
||||
return fmt.Errorf("invalid template name: %s. Use -l flag to list valid templates", options.TemplateName)
|
||||
}
|
||||
|
||||
err := templates.Install(options)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -17,4 +17,5 @@ type Init struct {
|
|||
ProductCopyright string `description:"The copyright notice" default:"\u00a9 now, My Company"`
|
||||
ProductComments string `description:"Comments to add to the generated files" default:"This is a comment"`
|
||||
ProductIdentifier string `description:"The product identifier, e.g com.mycompany.myproduct"`
|
||||
SkipWarning bool `name:"s" description:"Skips the warning message when using remote templates"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -160,6 +160,45 @@ func COPYDIR(src string, dst string) {
|
|||
unindent()
|
||||
}
|
||||
|
||||
// COPYDIR2 recursively copies a directory tree, attempting to preserve permissions.
|
||||
// Source directory must exist, destination directory can exist.
|
||||
// Symlinks are ignored and skipped.
|
||||
// Credit: https://gist.github.com/r0l1/92462b38df26839a3ca324697c8cba04
|
||||
func COPYDIR2(src string, dst string) {
|
||||
log("COPYDIR %s -> %s", src, dst)
|
||||
src = filepath.Clean(src)
|
||||
dst = filepath.Clean(dst)
|
||||
|
||||
si, err := os.Stat(src)
|
||||
checkError(err)
|
||||
if !si.IsDir() {
|
||||
checkError(fmt.Errorf("source is not a directory"))
|
||||
}
|
||||
|
||||
indent()
|
||||
MKDIR(dst)
|
||||
|
||||
entries, err := os.ReadDir(src)
|
||||
checkError(err)
|
||||
|
||||
for _, entry := range entries {
|
||||
srcPath := filepath.Join(src, entry.Name())
|
||||
dstPath := filepath.Join(dst, entry.Name())
|
||||
|
||||
if entry.IsDir() {
|
||||
COPYDIR(srcPath, dstPath)
|
||||
} else {
|
||||
// Skip symlinks.
|
||||
if entry.Type()&os.ModeSymlink != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
COPY(srcPath, dstPath)
|
||||
}
|
||||
}
|
||||
unindent()
|
||||
}
|
||||
|
||||
func SYMLINK(source string, target string) {
|
||||
// trim string to first 30 chars
|
||||
var trimTarget = target
|
||||
|
|
|
|||
3
v3/internal/templates/base/NEXTSTEPS.md
Normal file
3
v3/internal/templates/base/NEXTSTEPS.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# Next Steps
|
||||
|
||||
For a full guide on how to create templates, see [Creating Custom Templates](https://v3alpha.wails.io/guides/custom-templates).
|
||||
24
v3/internal/templates/base/frontend/.gitignore
vendored
Normal file
24
v3/internal/templates/base/frontend/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
35
v3/internal/templates/base/frontend/index.html
Normal file
35
v3/internal/templates/base/frontend/index.html
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<link rel="icon" type="image/svg+xml" href="/wails.png"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<link rel="stylesheet" href="/style.css"/>
|
||||
<title>Wails App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div>
|
||||
<a wml-openURL="https://wails.io">
|
||||
<img src="/wails.png" class="logo" alt="Wails logo"/>
|
||||
</a>
|
||||
<a wml-openURL="https://developer.mozilla.org/en-US/docs/Web/JavaScript">
|
||||
<img src="/javascript.svg" class="logo vanilla" alt="JavaScript logo"/>
|
||||
</a>
|
||||
</div>
|
||||
<h1>Wails + Javascript</h1>
|
||||
<div class="card">
|
||||
<div class="result" id="result">Please enter your name below 👇</div>
|
||||
<div class="input-box" id="input">
|
||||
<input class="input" id="name" type="text" autocomplete="off"/>
|
||||
<button class="btn" onclick="doGreet()">Greet</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<div><p>Click on the Wails logo to learn more</p></div>
|
||||
<div><p id="time">Listening for Time event...</p></div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
21
v3/internal/templates/base/frontend/main.js
Normal file
21
v3/internal/templates/base/frontend/main.js
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import {GreetService} from "./bindings/changeme";
|
||||
import {Events} from "@wailsio/runtime";
|
||||
|
||||
const resultElement = document.getElementById('result');
|
||||
const timeElement = document.getElementById('time');
|
||||
|
||||
window.doGreet = () => {
|
||||
let name = document.getElementById('name').value;
|
||||
if (!name) {
|
||||
name = 'anonymous';
|
||||
}
|
||||
GreetService.Greet(name).then((result) => {
|
||||
resultElement.innerText = result;
|
||||
}).catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
|
||||
Events.On('time', (time) => {
|
||||
timeElement.innerText = time.data;
|
||||
});
|
||||
16
v3/internal/templates/base/frontend/package.json
Normal file
16
v3/internal/templates/base/frontend/package.json
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"name": "frontend",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build --minify false --mode development",
|
||||
"build:prod": "vite build --mode production",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vite": "^5.0.0",
|
||||
"@wailsio/runtime": "latest"
|
||||
}
|
||||
}
|
||||
|
|
@ -4,5 +4,6 @@
|
|||
"author": "Lea Anthony",
|
||||
"description": "Lit + TS + Vite development server",
|
||||
"helpurl": "https://wails.io",
|
||||
"version": 3
|
||||
"version": "v0.0.1",
|
||||
"schema": 3
|
||||
}
|
||||
|
|
@ -4,5 +4,6 @@
|
|||
"author": "Lea Anthony",
|
||||
"description": "Lit + Vite development server",
|
||||
"helpurl": "https://wails.io",
|
||||
"version": 3
|
||||
"version": "v0.0.1",
|
||||
"schema": 3
|
||||
}
|
||||
|
|
@ -4,5 +4,6 @@
|
|||
"author": "Lea Anthony",
|
||||
"description": "Preact + TS + Vite development server",
|
||||
"helpurl": "https://wails.io",
|
||||
"version": 3
|
||||
"version": "v0.0.1",
|
||||
"schema": 3
|
||||
}
|
||||
|
|
@ -4,5 +4,6 @@
|
|||
"author": "Lea Anthony",
|
||||
"description": "Preact + Vite development server",
|
||||
"helpurl": "https://wails.io",
|
||||
"version": 3
|
||||
"version": "v0.0.1",
|
||||
"schema": 3
|
||||
}
|
||||
|
|
@ -4,5 +4,6 @@
|
|||
"author": "Lea Anthony",
|
||||
"description": "Qwik + TS + Vite development server",
|
||||
"helpurl": "https://wails.io",
|
||||
"version": 3
|
||||
"version": "v0.0.1",
|
||||
"schema": 3
|
||||
}
|
||||
|
|
@ -4,5 +4,6 @@
|
|||
"author": "Lea Anthony",
|
||||
"description": "Qwik + Vite development server",
|
||||
"helpurl": "https://wails.io",
|
||||
"version": 3
|
||||
"version": "v0.0.1",
|
||||
"schema": 3
|
||||
}
|
||||
|
|
@ -4,5 +4,6 @@
|
|||
"author": "Lea Anthony",
|
||||
"description": "React + TS + SWC + Vite development server",
|
||||
"helpurl": "https://wails.io",
|
||||
"version": 3
|
||||
"version": "v0.0.1",
|
||||
"schema": 3
|
||||
}
|
||||
|
|
@ -4,5 +4,6 @@
|
|||
"author": "Lea Anthony",
|
||||
"description": "React + SWC + Vite development server",
|
||||
"helpurl": "https://wails.io",
|
||||
"version": 3
|
||||
"version": "v0.0.1",
|
||||
"schema": 3
|
||||
}
|
||||
|
|
@ -4,5 +4,6 @@
|
|||
"author": "Lea Anthony",
|
||||
"description": "React + Vite development server",
|
||||
"helpurl": "https://wails.io",
|
||||
"version": 3
|
||||
"version": "v0.0.1",
|
||||
"schema": 3
|
||||
}
|
||||
|
|
@ -4,5 +4,6 @@
|
|||
"author": "Lea Anthony",
|
||||
"description": "React + Vite development server",
|
||||
"helpurl": "https://wails.io",
|
||||
"version": 3
|
||||
"version": "v0.0.1",
|
||||
"schema": 3
|
||||
}
|
||||
|
|
@ -4,5 +4,6 @@
|
|||
"author": "Lea Anthony",
|
||||
"description": "Solid + TS + Vite development server",
|
||||
"helpurl": "https://wails.io",
|
||||
"version": 3
|
||||
"version": "v0.0.1",
|
||||
"schema": 3
|
||||
}
|
||||
|
|
@ -4,5 +4,6 @@
|
|||
"author": "Lea Anthony",
|
||||
"description": "Solid + Vite development server",
|
||||
"helpurl": "https://wails.io",
|
||||
"version": 3
|
||||
"version": "v0.0.1",
|
||||
"schema": 3
|
||||
}
|
||||
|
|
@ -4,5 +4,6 @@
|
|||
"author": "Lea Anthony",
|
||||
"description": "Svelte + TS + Vite development server",
|
||||
"helpurl": "https://wails.io",
|
||||
"version": 3
|
||||
"version": "v0.0.1",
|
||||
"schema": 3
|
||||
}
|
||||
|
|
@ -4,5 +4,6 @@
|
|||
"author": "Lea Anthony",
|
||||
"description": "Svelte + Vite development server",
|
||||
"helpurl": "https://wails.io",
|
||||
"version": 3
|
||||
"version": "v0.0.1",
|
||||
"schema": 3
|
||||
}
|
||||
|
|
@ -4,5 +4,6 @@
|
|||
"author": "Atterpac",
|
||||
"description": "SvelteKit + TS + Vite development server",
|
||||
"helpurl": "https://wails.io",
|
||||
"version": 3
|
||||
"version": "v0.0.1",
|
||||
"schema": 3
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,5 +4,6 @@
|
|||
"author": "Atterpac",
|
||||
"description": "SvelteKit + Vite development server",
|
||||
"helpurl": "https://wails.io",
|
||||
"version": 3
|
||||
"version": "v0.0.1",
|
||||
"schema": 3
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,11 +5,12 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/wailsapp/wails/v3/internal/buildinfo"
|
||||
"github.com/wailsapp/wails/v3/internal/s"
|
||||
"github.com/wailsapp/wails/v3/internal/version"
|
||||
"io/fs"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/go-git/go-git/v5"
|
||||
|
|
@ -102,27 +103,44 @@ func getLocalTemplate(templateName string) (*Template, error) {
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
template, err = parseTemplate(os.DirFS(templateName), templateName)
|
||||
template, err = parseTemplate(os.DirFS(templateName), "")
|
||||
if err != nil {
|
||||
println("err2 = ", err.Error())
|
||||
return nil, err
|
||||
}
|
||||
template.source = sourceLocal
|
||||
|
||||
return &template, nil
|
||||
}
|
||||
|
||||
type BaseTemplate struct {
|
||||
Name string `json:"name" description:"The name of the template"`
|
||||
ShortName string `json:"shortname" description:"The short name of the template"`
|
||||
Author string `json:"author" description:"The author of the template"`
|
||||
Description string `json:"description" description:"The template description"`
|
||||
HelpURL string `json:"helpurl" description:"The help url for the template"`
|
||||
Version string `json:"version" description:"The version of the template" default:"v0.0.1"`
|
||||
Dir string `json:"-" description:"The directory to generate the template" default:"."`
|
||||
Frontend string `json:"-" description:"The frontend directory to migrate"`
|
||||
}
|
||||
|
||||
type source int
|
||||
|
||||
const (
|
||||
sourceInternal source = 1
|
||||
sourceLocal source = 2
|
||||
sourceRemote source = 3
|
||||
)
|
||||
|
||||
// Template holds data relating to a template including the metadata stored in template.yaml
|
||||
type Template struct {
|
||||
|
||||
// Template details
|
||||
Name string `json:"name"`
|
||||
ShortName string `json:"shortname"`
|
||||
Author string `json:"author"`
|
||||
Description string `json:"description"`
|
||||
HelpURL string `json:"helpurl"`
|
||||
Version int8 `json:"version"`
|
||||
BaseTemplate
|
||||
Schema uint8 `json:"schema"`
|
||||
|
||||
// Other data
|
||||
FS fs.FS `json:"-"`
|
||||
FS fs.FS `json:"-"`
|
||||
source source
|
||||
tempDir string
|
||||
}
|
||||
|
||||
func parseTemplate(template fs.FS, templateName string) (Template, error) {
|
||||
|
|
@ -142,10 +160,10 @@ func parseTemplate(template fs.FS, templateName string) (Template, error) {
|
|||
result.FS = template
|
||||
|
||||
// We need to do a version check here
|
||||
if result.Version == 0 {
|
||||
if result.Schema == 0 {
|
||||
return result, fmt.Errorf("template not supported by wails 3. This template is probably for wails 2")
|
||||
}
|
||||
if result.Version != 3 {
|
||||
if result.Schema != 3 {
|
||||
return result, fmt.Errorf("template version %d is not supported by wails 3. Ensure 'version' is set to 3 in the `template.json` file", result.Version)
|
||||
}
|
||||
|
||||
|
|
@ -175,31 +193,28 @@ func gitclone(uri string) (string, error) {
|
|||
|
||||
}
|
||||
|
||||
func getRemoteTemplate(uri string) (template *Template, err error) {
|
||||
func getRemoteTemplate(uri string) (*Template, error) {
|
||||
// git clone to temporary dir
|
||||
var tempDir string
|
||||
tempDir, err = gitclone(uri)
|
||||
|
||||
defer func(path string) {
|
||||
_ = os.RemoveAll(path)
|
||||
}(tempDir)
|
||||
tempDir, err := gitclone(uri)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Remove the .git directory
|
||||
err = os.RemoveAll(filepath.Join(tempDir, ".git"))
|
||||
if err != nil {
|
||||
return
|
||||
return nil, err
|
||||
}
|
||||
|
||||
templateFS := os.DirFS(tempDir)
|
||||
var parsedTemplate Template
|
||||
parsedTemplate, err = parseTemplate(templateFS, "")
|
||||
if err != nil {
|
||||
return
|
||||
return nil, err
|
||||
}
|
||||
parsedTemplate.tempDir = tempDir
|
||||
parsedTemplate.source = sourceRemote
|
||||
return &parsedTemplate, nil
|
||||
}
|
||||
|
||||
|
|
@ -254,21 +269,20 @@ func Install(options *flags.Init) error {
|
|||
}()
|
||||
|
||||
var template *Template
|
||||
template, err = getInternalTemplate(options.TemplateName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if template == nil {
|
||||
|
||||
if ValidTemplateName(options.TemplateName) {
|
||||
template, err = getInternalTemplate(options.TemplateName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
template, err = getLocalTemplate(options.TemplateName)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if template == nil {
|
||||
template, err = getRemoteTemplate(options.TemplateName)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if template == nil {
|
||||
template, err = getRemoteTemplate(options.TemplateName)
|
||||
}
|
||||
}
|
||||
|
||||
if template == nil {
|
||||
|
|
@ -286,6 +300,13 @@ func Install(options *flags.Init) error {
|
|||
}
|
||||
}
|
||||
|
||||
if template.source == sourceRemote && !options.SkipWarning {
|
||||
var confirmed = confirmRemote(template)
|
||||
if !confirmed {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
pterm.Printf("Creating project\n")
|
||||
pterm.Printf("----------------\n\n")
|
||||
table := pterm.TableData{
|
||||
|
|
@ -293,26 +314,74 @@ func Install(options *flags.Init) error {
|
|||
{"Project Directory", filepath.FromSlash(options.ProjectDir)},
|
||||
{"Template", template.Name},
|
||||
{"Template Source", template.HelpURL},
|
||||
{"Template Version", template.Version},
|
||||
}
|
||||
err = pterm.DefaultTable.WithData(table).Render()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tfs, err := fs.Sub(template.FS, options.TemplateName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
common, err := fs.Sub(templates, "_common")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = gosod.New(common).Extract(options.ProjectDir, templateData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = gosod.New(tfs).Extract(options.ProjectDir, templateData)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
switch template.source {
|
||||
case sourceInternal:
|
||||
tfs, err := fs.Sub(template.FS, options.TemplateName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
common, err := fs.Sub(templates, "_common")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = gosod.New(common).Extract(options.ProjectDir, templateData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = gosod.New(tfs).Extract(options.ProjectDir, templateData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case sourceLocal, sourceRemote:
|
||||
data := struct {
|
||||
TemplateOptions
|
||||
Dir string
|
||||
Name string
|
||||
BinaryName string
|
||||
ProductName string
|
||||
ProductDescription string
|
||||
ProductVersion string
|
||||
ProductCompany string
|
||||
ProductCopyright string
|
||||
ProductComments string
|
||||
ProductIdentifier string
|
||||
Silent bool
|
||||
Typescript bool
|
||||
}{
|
||||
Name: options.ProjectName,
|
||||
Silent: true,
|
||||
ProductCompany: options.ProductCompany,
|
||||
ProductName: options.ProductName,
|
||||
ProductDescription: options.ProductDescription,
|
||||
ProductVersion: options.ProductVersion,
|
||||
ProductIdentifier: options.ProductIdentifier,
|
||||
ProductCopyright: options.ProductCopyright,
|
||||
ProductComments: options.ProductComments,
|
||||
Typescript: templateData.UseTypescript,
|
||||
TemplateOptions: templateData,
|
||||
}
|
||||
// If options.ProjectDir does not exist, create it
|
||||
if _, err := os.Stat(options.ProjectDir); os.IsNotExist(err) {
|
||||
err = os.Mkdir(options.ProjectDir, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = gosod.New(template.FS).Extract(options.ProjectDir, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if template.tempDir != "" {
|
||||
s.RMDIR(template.tempDir)
|
||||
}
|
||||
}
|
||||
|
||||
// Change to project directory
|
||||
|
|
@ -320,14 +389,75 @@ func Install(options *flags.Init) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Run `go mod tidy`
|
||||
err = exec.Command("go", "mod", "tidy").Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pterm.Printf("\nProject '%s' created successfully.\n", options.ProjectName)
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func GenerateTemplate(options *BaseTemplate) error {
|
||||
if options.Name == "" {
|
||||
return fmt.Errorf("please provide a template name using the -name flag")
|
||||
}
|
||||
|
||||
// Get current directory
|
||||
baseOutputDir, err := filepath.Abs(options.Dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
outDir := filepath.Join(baseOutputDir, options.Name)
|
||||
|
||||
// Extract base files
|
||||
_, filename, _, _ := runtime.Caller(0)
|
||||
basePath := filepath.Join(filepath.Dir(filename), "_common")
|
||||
s.COPYDIR2(basePath, outDir)
|
||||
s.RMDIR(filepath.Join(outDir, "build"))
|
||||
|
||||
// Copy frontend
|
||||
targetFrontendPath := filepath.Join(outDir, "frontend")
|
||||
sourceFrontendPath := options.Frontend
|
||||
if sourceFrontendPath == "" {
|
||||
sourceFrontendPath = filepath.Join(filepath.Dir(filename), "base", "frontend")
|
||||
}
|
||||
s.COPYDIR2(sourceFrontendPath, targetFrontendPath)
|
||||
|
||||
// Copy files from relative directory ../commands/build_assets
|
||||
// Get the path to THIS file
|
||||
assetPath := filepath.Join(filepath.Dir(filename), "..", "commands", "build_assets")
|
||||
assetdir := filepath.Join(outDir, "build")
|
||||
|
||||
s.COPYDIR2(assetPath, assetdir)
|
||||
|
||||
// Copy the template NEXTSTEPS.md
|
||||
s.COPY(filepath.Join(filepath.Dir(filename), "base", "NEXTSTEPS.md"), filepath.Join(outDir, "NEXTSTEPS.md"))
|
||||
|
||||
// Write the template.json file
|
||||
templateJSON := filepath.Join(outDir, "template.json")
|
||||
// Marshall
|
||||
optionsJSON, err := json.MarshalIndent(&Template{
|
||||
BaseTemplate: *options,
|
||||
Schema: 3,
|
||||
}, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.WriteFile(templateJSON, optionsJSON, 0o755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Successfully generated template in %s\n", outDir)
|
||||
return nil
|
||||
}
|
||||
|
||||
func confirmRemote(template *Template) bool {
|
||||
pterm.Println(pterm.LightRed("\n--- REMOTE TEMPLATES ---"))
|
||||
|
||||
// Create boxes with the title positioned differently and containing different content
|
||||
pterm.Println(pterm.LightYellow("You are creating a project using a remote template.\nThe Wails project takes no responsibility for 3rd party templates.\nOnly use remote templates that you trust."))
|
||||
|
||||
result, _ := pterm.DefaultInteractiveConfirm.WithConfirmText("Are you sure you want to continue?").WithConfirmText("y").WithRejectText("n").Show()
|
||||
|
||||
return result
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,5 +4,6 @@
|
|||
"author": "Lea Anthony",
|
||||
"description": "Vanilla + TS + Vite development server",
|
||||
"helpurl": "https://wails.io",
|
||||
"version": 3
|
||||
"version": "v0.0.1",
|
||||
"schema": 3
|
||||
}
|
||||
|
|
@ -4,5 +4,6 @@
|
|||
"author": "Lea Anthony",
|
||||
"description": "Vanilla + Vite development server",
|
||||
"helpurl": "https://wails.io",
|
||||
"version": 3
|
||||
"version": "v0.0.1",
|
||||
"schema": 3
|
||||
}
|
||||
|
|
@ -4,5 +4,6 @@
|
|||
"author": "Lea Anthony",
|
||||
"description": "Vue + TS + Vite development server",
|
||||
"helpurl": "https://wails.io",
|
||||
"version": 3
|
||||
"version": "v0.0.1",
|
||||
"schema": 3
|
||||
}
|
||||
|
|
@ -4,5 +4,6 @@
|
|||
"author": "Lea Anthony",
|
||||
"description": "Vue + Vite development server",
|
||||
"helpurl": "https://wails.io",
|
||||
"version": 3
|
||||
"version": "v0.0.1",
|
||||
"schema": 3
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue