Files

271 lines
7.4 KiB
Go
Raw Permalink Normal View History

2018-12-26 16:27:07 +11:00
package cmd
import (
"bytes"
"encoding/json"
2019-05-20 18:44:14 +10:00
"fmt"
2019-05-18 07:15:15 +10:00
"io/ioutil"
2019-03-02 12:54:10 +11:00
"log"
2018-12-26 16:27:07 +11:00
"path/filepath"
2020-10-29 20:18:52 +11:00
"runtime"
2018-12-26 16:27:07 +11:00
"strings"
2019-01-08 17:48:37 +11:00
"text/template"
2019-03-02 12:54:10 +11:00
2019-05-20 18:44:14 +10:00
"github.com/kennygrant/sanitize"
2019-03-02 12:54:10 +11:00
"github.com/leaanthony/slicer"
2018-12-26 16:27:07 +11:00
)
2019-03-02 12:54:10 +11:00
// TemplateMetadata holds all the metadata for a Wails template
type TemplateMetadata struct {
Name string `json:"name"`
Version string `json:"version"`
ShortDescription string `json:"shortdescription"`
Description string `json:"description"`
Install string `json:"install"`
Build string `json:"build"`
Author string `json:"author"`
Created string `json:"created"`
FrontendDir string `json:"frontenddir"`
Serve string `json:"serve"`
Bridge string `json:"bridge"`
WailsDir string `json:"wailsdir"`
TemplateDependencies []*TemplateDependency `json:"dependencies,omitempty"`
2020-10-29 20:18:52 +11:00
// List of platforms that this template is supported on.
// No value means all platforms. A platform name is the same string
// as `runtime.GOOS` will return, eg: "darwin". NOTE: This is
// case sensitive.
Platforms []string `json:"platforms,omitempty"`
}
// PlatformSupported returns true if this template supports the
// currently running platform
func (m *TemplateMetadata) PlatformSupported() bool {
// Default is all platforms supported
if len(m.Platforms) == 0 {
return true
}
// Check that the platform is in the list
platformsSupported := slicer.String(m.Platforms)
return platformsSupported.Contains(runtime.GOOS)
}
// TemplateDependency defines a binary dependency for the template
// EG: ng for angular
type TemplateDependency struct {
Bin string `json:"bin"`
Help string `json:"help"`
2018-12-26 16:27:07 +11:00
}
2019-03-02 12:54:10 +11:00
// TemplateDetails holds information about a specific template
type TemplateDetails struct {
2019-05-18 07:15:15 +10:00
Name string
2019-03-02 12:54:10 +11:00
Path string
Metadata *TemplateMetadata
2019-05-18 07:15:15 +10:00
fs *FSHelper
2019-03-02 12:54:10 +11:00
}
// TemplateHelper is a utility object to help with processing templates
type TemplateHelper struct {
2019-05-18 07:15:15 +10:00
templateDir *Dir
fs *FSHelper
metadataFilename string
2018-12-26 16:27:07 +11:00
}
2019-01-08 07:58:46 +11:00
// NewTemplateHelper creates a new template helper
2018-12-26 16:27:07 +11:00
func NewTemplateHelper() *TemplateHelper {
2019-05-18 07:15:15 +10:00
templateDir, err := fs.LocalDir("./templates")
if err != nil {
log.Fatal("Unable to find the template directory. Please reinstall Wails.")
}
2018-12-26 16:27:07 +11:00
2019-03-02 12:54:10 +11:00
return &TemplateHelper{
2019-05-18 07:15:15 +10:00
templateDir: templateDir,
metadataFilename: "template.json",
2018-12-26 16:27:07 +11:00
}
}
2019-10-08 06:12:08 +11:00
// IsValidTemplate returns true if the given template name resides on disk
2019-05-20 18:44:14 +10:00
func (t *TemplateHelper) IsValidTemplate(templateName string) bool {
pathToTemplate := filepath.Join(t.templateDir.fullPath, templateName)
return t.fs.DirExists(pathToTemplate)
}
// SanitizeFilename sanitizes the given string to make a valid filename
func (t *TemplateHelper) SanitizeFilename(name string) string {
return sanitize.Name(name)
}
// CreateNewTemplate creates a new template based on the given directory name and string
func (t *TemplateHelper) CreateNewTemplate(dirname string, details *TemplateMetadata) (string, error) {
// Check if this template has already been created
if t.IsValidTemplate(dirname) {
return "", fmt.Errorf("cannot create template in directory '%s' - already exists", dirname)
}
targetDir := filepath.Join(t.templateDir.fullPath, dirname)
err := t.fs.MkDir(targetDir)
if err != nil {
return "", err
}
targetMetadata := filepath.Join(targetDir, t.metadataFilename)
err = t.fs.SaveAsJSON(details, targetMetadata)
return targetDir, err
}
2019-05-18 07:15:15 +10:00
// LoadMetadata loads the template's 'metadata.json' file
func (t *TemplateHelper) LoadMetadata(dir string) (*TemplateMetadata, error) {
templateFile := filepath.Join(dir, t.metadataFilename)
result := &TemplateMetadata{}
if !t.fs.FileExists(templateFile) {
return nil, nil
}
rawJSON, err := ioutil.ReadFile(templateFile)
if err != nil {
return nil, err
}
err = json.Unmarshal(rawJSON, &result)
return result, err
}
// GetTemplateDetails returns a map of Template structs containing details
// of the found templates
func (t *TemplateHelper) GetTemplateDetails() (map[string]*TemplateDetails, error) {
// Get the subdirectory details
templateDirs, err := t.templateDir.GetSubdirs()
if err != nil {
return nil, err
}
result := make(map[string]*TemplateDetails)
for name, dir := range templateDirs {
result[name] = &TemplateDetails{
Path: dir,
}
metadata, err := t.LoadMetadata(dir)
if err != nil {
return nil, err
}
2020-10-29 20:18:52 +11:00
2019-05-18 07:15:15 +10:00
result[name].Metadata = metadata
if metadata.Name != "" {
result[name].Name = metadata.Name
} else {
// Ignore bad templates?
result[name] = nil
}
}
return result, nil
}
// GetTemplateFilenames returns all the filenames of the given template
func (t *TemplateHelper) GetTemplateFilenames(template *TemplateDetails) (*slicer.StringSlicer, error) {
// Get the subdirectory details
2019-05-22 07:25:42 +10:00
templateDir, err := t.fs.Directory(template.Path)
2019-05-18 07:15:15 +10:00
if err != nil {
return nil, err
}
return templateDir.GetAllFilenames()
}
2019-01-08 07:58:46 +11:00
// InstallTemplate installs the template given in the project options to the
// project path given
2018-12-26 16:27:07 +11:00
func (t *TemplateHelper) InstallTemplate(projectPath string, projectOptions *ProjectOptions) error {
// Check dependencies before installing
dependencies := projectOptions.selectedTemplate.Metadata.TemplateDependencies
if dependencies != nil {
programHelper := NewProgramHelper()
logger := NewLogger()
errors := []string{}
for _, dep := range dependencies {
program := programHelper.FindProgram(dep.Bin)
if program == nil {
errors = append(errors, dep.Help)
}
}
if len(errors) > 0 {
mainError := "template dependencies not installed"
if len(errors) == 1 {
mainError = errors[0]
} else {
for _, error := range errors {
logger.Red(error)
}
}
return fmt.Errorf(mainError)
}
}
2018-12-26 16:27:07 +11:00
// Get template files
2019-05-18 07:15:15 +10:00
templateFilenames, err := t.GetTemplateFilenames(projectOptions.selectedTemplate)
if err != nil {
return err
}
2019-03-02 12:54:10 +11:00
templatePath := projectOptions.selectedTemplate.Path
2018-12-26 16:27:07 +11:00
2019-06-26 19:46:51 +10:00
// Save the version
projectOptions.WailsVersion = Version
2019-05-18 07:15:15 +10:00
templateJSONFilename := filepath.Join(templatePath, t.metadataFilename)
2018-12-26 16:27:07 +11:00
2019-03-02 12:54:10 +11:00
templateFiles := templateFilenames.Filter(func(filename string) bool {
2019-05-11 17:04:42 +10:00
filename = filepath.FromSlash(filename)
2019-03-02 12:54:10 +11:00
return strings.HasPrefix(filename, templatePath) && filename != templateJSONFilename
2018-12-26 16:27:07 +11:00
})
2019-03-02 12:54:10 +11:00
templateFiles.Each(func(templateFile string) {
2018-12-26 16:27:07 +11:00
2019-03-02 12:54:10 +11:00
// Setup filenames
relativeFilename := strings.TrimPrefix(templateFile, templatePath)[1:]
targetFilename, err := filepath.Abs(filepath.Join(projectOptions.OutputDirectory, relativeFilename))
2018-12-26 16:27:07 +11:00
if err != nil {
2019-03-02 12:54:10 +11:00
return
2018-12-26 16:27:07 +11:00
}
2019-05-18 07:15:15 +10:00
filedata, err := t.fs.LoadAsBytes(templateFile)
if err != nil {
return
}
2018-12-26 16:27:07 +11:00
2019-03-02 12:54:10 +11:00
// If file is a template, process it
if strings.HasSuffix(templateFile, ".template") {
2019-05-18 07:15:15 +10:00
templateData := string(filedata)
2019-03-02 12:54:10 +11:00
tmpl := template.New(templateFile)
tmpl.Parse(templateData)
2018-12-26 16:27:07 +11:00
var tpl bytes.Buffer
err = tmpl.Execute(&tpl, projectOptions)
if err != nil {
2019-03-02 12:54:10 +11:00
return
2018-12-26 16:27:07 +11:00
}
2019-03-02 12:54:10 +11:00
// Remove template suffix
targetFilename = strings.TrimSuffix(targetFilename, ".template")
// Set the filedata to the template result
filedata = tpl.Bytes()
2018-12-26 16:27:07 +11:00
}
2019-03-02 12:54:10 +11:00
// Normal file, just copy it
err = fs.CreateFile(targetFilename, filedata)
if err != nil {
return
}
})
if err != nil {
return err
2018-12-26 16:27:07 +11:00
}
return nil
}