mirror of
https://github.com/taigrr/wails.git
synced 2026-04-16 19:55:05 -07:00
Move templates to CLI dir. Use go:embed
This commit is contained in:
@@ -6,9 +6,10 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/initialise/templates"
|
||||
|
||||
"github.com/leaanthony/clir"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/wailsapp/wails/v2/internal/templates"
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
"github.com/wailsapp/wails/v2/pkg/git"
|
||||
)
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Wails: Debug {{.ProjectName}} (Desktop)",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "exec",
|
||||
"program": "${workspaceFolder}/{{.PathToDesktopBinary}}",
|
||||
"preLaunchTask": "build_desktop",
|
||||
"cwd": "",
|
||||
"env": {},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "Wails: Debug {{.ProjectName}} (Server)",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "exec",
|
||||
"program": "${workspaceFolder}/{{.PathToServerBinary}}",
|
||||
"preLaunchTask": "build_server",
|
||||
"cwd": "",
|
||||
"env": {},
|
||||
"args": []
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "build_desktop",
|
||||
"type": "shell",
|
||||
"options": {
|
||||
"cwd": "{{.TargetDir}}"
|
||||
},
|
||||
"command": "wails build"
|
||||
},
|
||||
{
|
||||
"label": "build_server",
|
||||
"type": "shell",
|
||||
"options": {
|
||||
"cwd": "{{.TargetDir}}"
|
||||
},
|
||||
"command": "wails build -t server"
|
||||
},
|
||||
|
||||
]
|
||||
}
|
||||
|
||||
331
v2/cmd/wails/internal/commands/initialise/templates/templates.go
Normal file
331
v2/cmd/wails/internal/commands/initialise/templates/templates.go
Normal file
@@ -0,0 +1,331 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
gofs "io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/leaanthony/debme"
|
||||
"github.com/leaanthony/gosod"
|
||||
"github.com/leaanthony/slicer"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"github.com/wailsapp/wails/v2/internal/fs"
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
)
|
||||
|
||||
//go:embed templates
|
||||
var templates embed.FS
|
||||
|
||||
//go:embed ides/*
|
||||
var ides embed.FS
|
||||
|
||||
// Cahce for the templates
|
||||
// We use this because we need different views of the same data
|
||||
var templateCache []Template = nil
|
||||
|
||||
// Data contains the data we wish to embed during template installation
|
||||
type Data struct {
|
||||
ProjectName string
|
||||
BinaryName string
|
||||
WailsVersion string
|
||||
NPMProjectName string
|
||||
AuthorName string
|
||||
AuthorEmail string
|
||||
AuthorNameAndEmail string
|
||||
WailsDirectory string
|
||||
}
|
||||
|
||||
// Options for installing a template
|
||||
type Options struct {
|
||||
ProjectName string
|
||||
TemplateName string
|
||||
BinaryName string
|
||||
TargetDir string
|
||||
Logger *clilogger.CLILogger
|
||||
GenerateVSCode bool
|
||||
PathToDesktopBinary string
|
||||
PathToServerBinary string
|
||||
InitGit bool
|
||||
AuthorName string
|
||||
AuthorEmail string
|
||||
}
|
||||
|
||||
// Template holds data relating to a template
|
||||
// including the metadata stored in template.json
|
||||
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"`
|
||||
|
||||
// Other data
|
||||
FS gofs.FS `json:"-"`
|
||||
}
|
||||
|
||||
func parseTemplate(template gofs.FS) (Template, error) {
|
||||
var result Template
|
||||
data, err := gofs.ReadFile(template, "template.json")
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
err = json.Unmarshal(data, &result)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
result.FS = template
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// TemplateShortNames returns a slicer of short template names
|
||||
func TemplateShortNames() (*slicer.StringSlicer, error) {
|
||||
|
||||
var result slicer.StringSlicer
|
||||
|
||||
// If the cache isn't loaded, load it
|
||||
if templateCache == nil {
|
||||
err := loadTemplateCache()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, template := range templateCache {
|
||||
result.Add(template.ShortName)
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// List returns the list of available templates
|
||||
func List() ([]Template, error) {
|
||||
|
||||
// If the cache isn't loaded, load it
|
||||
if templateCache == nil {
|
||||
err := loadTemplateCache()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return templateCache, nil
|
||||
}
|
||||
|
||||
// getTemplateByShortname returns the template with the given short name
|
||||
func getTemplateByShortname(shortname string) (Template, error) {
|
||||
|
||||
var result Template
|
||||
|
||||
// If the cache isn't loaded, load it
|
||||
if templateCache == nil {
|
||||
err := loadTemplateCache()
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, template := range templateCache {
|
||||
if template.ShortName == shortname {
|
||||
return template, nil
|
||||
}
|
||||
}
|
||||
|
||||
return result, fmt.Errorf("shortname '%s' is not a valid template shortname", shortname)
|
||||
}
|
||||
|
||||
// Loads the template cache
|
||||
func loadTemplateCache() error {
|
||||
|
||||
templatesFS, err := debme.FS(templates, "templates")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get directories
|
||||
files, err := templatesFS.ReadDir(".")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Reset cache
|
||||
templateCache = []Template{}
|
||||
|
||||
for _, file := range files {
|
||||
if file.IsDir() {
|
||||
templateFS, err := templatesFS.FS(file.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
template, err := parseTemplate(templateFS)
|
||||
if err != nil {
|
||||
// Cannot parse this template, continue
|
||||
continue
|
||||
}
|
||||
templateCache = append(templateCache, template)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Install the given template
|
||||
func Install(options *Options) error {
|
||||
// Get cwd
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Did the user want to install in current directory?
|
||||
if options.TargetDir == "" {
|
||||
|
||||
// If the current directory is empty, use it
|
||||
isEmpty, err := fs.DirIsEmpty(cwd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isEmpty {
|
||||
// Yes - use cwd
|
||||
options.TargetDir = cwd
|
||||
} else {
|
||||
options.TargetDir = filepath.Join(cwd, options.ProjectName)
|
||||
if fs.DirExists(options.TargetDir) {
|
||||
return fmt.Errorf("cannot create project directory. Dir exists: %s", options.TargetDir)
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// Get the absolute path of the given directory
|
||||
targetDir, err := filepath.Abs(filepath.Join(cwd, options.TargetDir))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
options.TargetDir = targetDir
|
||||
if !fs.DirExists(options.TargetDir) {
|
||||
err := fs.Mkdir(options.TargetDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get template
|
||||
template, err := getTemplateByShortname(options.TemplateName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Use Gosod to install the template
|
||||
installer := gosod.New(template.FS)
|
||||
|
||||
// Ignore template.json files
|
||||
installer.IgnoreFile("template.json")
|
||||
|
||||
// Setup the data.
|
||||
// We use the directory name for the binary name, like Go
|
||||
BinaryName := filepath.Base(options.TargetDir)
|
||||
NPMProjectName := strings.ToLower(strings.ReplaceAll(BinaryName, " ", ""))
|
||||
localWailsDirectory := fs.RelativePath("../../../../../..")
|
||||
templateData := &Data{
|
||||
ProjectName: options.ProjectName,
|
||||
BinaryName: filepath.Base(options.TargetDir),
|
||||
NPMProjectName: NPMProjectName,
|
||||
WailsDirectory: localWailsDirectory,
|
||||
AuthorEmail: options.AuthorEmail,
|
||||
AuthorName: options.AuthorName,
|
||||
}
|
||||
|
||||
// Create a formatted name and email combo.
|
||||
if options.AuthorName != "" {
|
||||
templateData.AuthorNameAndEmail = options.AuthorName + " "
|
||||
}
|
||||
if options.AuthorEmail != "" {
|
||||
templateData.AuthorNameAndEmail += "<" + options.AuthorEmail + ">"
|
||||
}
|
||||
templateData.AuthorNameAndEmail = strings.TrimSpace(templateData.AuthorNameAndEmail)
|
||||
|
||||
// Extract the template
|
||||
err = installer.Extract(options.TargetDir, templateData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = generateIDEFiles(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// OutputList prints the list of available tempaltes to the given logger
|
||||
func OutputList(logger *clilogger.CLILogger) error {
|
||||
templates, err := List()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
table := tablewriter.NewWriter(logger.Writer)
|
||||
table.SetHeader([]string{"Template", "Short Name", "Description"})
|
||||
table.SetAutoWrapText(false)
|
||||
table.SetAutoFormatHeaders(true)
|
||||
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
|
||||
table.SetAlignment(tablewriter.ALIGN_LEFT)
|
||||
table.SetCenterSeparator("")
|
||||
table.SetColumnSeparator("")
|
||||
table.SetRowSeparator("")
|
||||
table.SetHeaderLine(false)
|
||||
table.SetBorder(false)
|
||||
table.SetTablePadding("\t") // pad with tabs
|
||||
table.SetNoWhiteSpace(true)
|
||||
for _, template := range templates {
|
||||
table.Append([]string{template.Name, template.ShortName, template.Description})
|
||||
}
|
||||
table.Render()
|
||||
return nil
|
||||
}
|
||||
|
||||
func generateIDEFiles(options *Options) error {
|
||||
|
||||
if options.GenerateVSCode {
|
||||
return generateVSCodeFiles(options)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func generateVSCodeFiles(options *Options) error {
|
||||
|
||||
targetDir := filepath.Join(options.TargetDir, ".vscode")
|
||||
source, err := debme.FS(ides, "ides/vscode")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Use gosod to install the template
|
||||
installer := gosod.New(source)
|
||||
|
||||
binaryName := filepath.Base(options.TargetDir)
|
||||
if runtime.GOOS == "windows" {
|
||||
// yay windows
|
||||
binaryName += ".exe"
|
||||
}
|
||||
|
||||
options.PathToDesktopBinary = filepath.Join("build", runtime.GOOS, "desktop", binaryName)
|
||||
options.PathToServerBinary = filepath.Join("build", runtime.GOOS, "server", binaryName)
|
||||
|
||||
err = installer.Extract(targetDir, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/wailsapp/wails/v2"
|
||||
)
|
||||
|
||||
// Basic application struct
|
||||
type Basic struct {
|
||||
runtime *wails.Runtime
|
||||
}
|
||||
|
||||
// NewBasic creates a new Basic application struct
|
||||
func NewBasic() *Basic {
|
||||
return &Basic{}
|
||||
}
|
||||
|
||||
// startup is called at application startup
|
||||
func (b *Basic) startup(runtime *wails.Runtime) {
|
||||
// Perform your setup here
|
||||
b.runtime = runtime
|
||||
runtime.Window.SetTitle("{{.ProjectName}}")
|
||||
}
|
||||
|
||||
// shutdown is called at application termination
|
||||
func (b *Basic) shutdown() {
|
||||
// Perform your teardown here
|
||||
}
|
||||
|
||||
// Greet returns a greeting for the given name
|
||||
func (b *Basic) Greet(name string) string {
|
||||
return fmt.Sprintf("Hello %s!", name)
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<link rel="stylesheet" href="/main.css">
|
||||
</head>
|
||||
|
||||
<body data-wails-drag>
|
||||
<div id="logo"></div>
|
||||
<div id="input">
|
||||
<input id="name" type="text"></input>
|
||||
<button onclick="greet()">Greet</button>
|
||||
</div>
|
||||
<div id="result"></div>
|
||||
|
||||
<script src="/main.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,16 @@
|
||||
// Get input + focus
|
||||
let nameElement = document.getElementById("name");
|
||||
nameElement.focus();
|
||||
|
||||
// Setup the greet function
|
||||
window.greet = function () {
|
||||
|
||||
// Get name
|
||||
let name = nameElement.value;
|
||||
|
||||
// Call Basic.Greet(name)
|
||||
window.backend.main.Basic.Greet(name).then((result) => {
|
||||
// Update result with data back from Basic.Greet()
|
||||
document.getElementById("result").innerText = result;
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
module test
|
||||
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-alpha
|
||||
)
|
||||
|
||||
replace github.com/wailsapp/wails/v2 v2.0.0-alpha => {{.WailsDirectory}}
|
||||
@@ -0,0 +1,39 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/wailsapp/wails/v2"
|
||||
"github.com/wailsapp/wails/v2/pkg/logger"
|
||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
"github.com/wailsapp/wails/v2/pkg/options/mac"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// Create application with options
|
||||
app := NewBasic()
|
||||
|
||||
err := wails.Run(&options.App{
|
||||
Title: "{{.ProjectName}}",
|
||||
Width: 800,
|
||||
Height: 600,
|
||||
DisableResize: true,
|
||||
Mac: &mac.Options{
|
||||
WebviewIsTransparent: true,
|
||||
WindowBackgroundIsTranslucent: true,
|
||||
TitleBar: mac.TitleBarHiddenInset(),
|
||||
Menu: menu.DefaultMacMenu(),
|
||||
},
|
||||
LogLevel: logger.DEBUG,
|
||||
Startup: app.startup,
|
||||
Shutdown: app.shutdown,
|
||||
Bind: []interface{}{
|
||||
app,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "Vanilla HTML/JS/CSS",
|
||||
"shortname": "vanilla",
|
||||
"author": "Lea Anthony<lea.anthony@gmail.com>",
|
||||
"description": "A simple template using only HTML/CSS/JS",
|
||||
"helpurl": "https://wails.app/help/templates/vanilla"
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "{{.ProjectName}}",
|
||||
"outputfilename": "{{.BinaryName}}",
|
||||
"html": "frontend/src/index.html",
|
||||
"author": {
|
||||
"name": "{{.AuthorName}}",
|
||||
"email": "{{.AuthorEmail}}"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/matryer/is"
|
||||
)
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
|
||||
is2 := is.New(t)
|
||||
templates, err := List()
|
||||
is2.NoErr(err)
|
||||
|
||||
println("Found these templates:")
|
||||
for _, template := range templates {
|
||||
fmt.Printf("%+v\n", template)
|
||||
}
|
||||
}
|
||||
|
||||
func TestShortname(t *testing.T) {
|
||||
|
||||
is2 := is.New(t)
|
||||
|
||||
template, err := getTemplateByShortname("vanilla")
|
||||
is2.NoErr(err)
|
||||
|
||||
println("Found this template:")
|
||||
fmt.Printf("%+v\n", template)
|
||||
}
|
||||
|
||||
func TestInstall(t *testing.T) {
|
||||
|
||||
is2 := is.New(t)
|
||||
|
||||
options := &Options{
|
||||
ProjectName: "test",
|
||||
TemplateName: "vanilla",
|
||||
AuthorName: "Lea Anthony",
|
||||
AuthorEmail: "lea.anthony@gmail.com",
|
||||
}
|
||||
|
||||
err := Install(options)
|
||||
is2.NoErr(err)
|
||||
}
|
||||
Reference in New Issue
Block a user