diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 6fd0fb99..1035da49 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -35,3 +35,5 @@ Wails is what it is because of the time and effort given by these great people. * [Dmitry Gomzyakov](https://github.com/kyoto44) * [Arthur Wiebe](https://github.com/artooro) * [Ilgıt Yıldırım](https://github.com/ilgityildirim) + * [Altynbek](https://github.com/gelleson) + * [Kyle](https://github.com/kmuchmore) diff --git a/cmd/helpers.go b/cmd/helpers.go index 85742b5c..48103c48 100644 --- a/cmd/helpers.go +++ b/cmd/helpers.go @@ -143,6 +143,7 @@ func BuildDocker(binaryName string, buildMode string, projectOptions *ProjectOpt "-v", fmt.Sprintf("%s:/build", filepath.Join(fs.Cwd(), "build")), "-v", fmt.Sprintf("%s:/source", fs.Cwd()), "-e", fmt.Sprintf("LOCAL_USER_ID=%v", userid), + "-e", fmt.Sprintf("FLAG_TAGS=%s", projectOptions.Tags), "-e", fmt.Sprintf("FLAG_LDFLAGS=%s", ldFlags(projectOptions, buildMode)), "-e", "FLAG_V=false", "-e", "FLAG_X=false", @@ -248,6 +249,10 @@ func BuildNative(binaryName string, forceRebuild bool, buildMode string, project buildCommand.AddSlice([]string{"-ldflags", ldFlags(projectOptions, buildMode)}) + if projectOptions.Tags != "" { + buildCommand.AddSlice([]string{"--tags", projectOptions.Tags}) + } + if projectOptions.Verbose { fmt.Printf("Command: %v\n", buildCommand.AsSlice()) } @@ -535,6 +540,9 @@ func InstallProdRuntime(projectDir string, projectOptions *ProjectOptions) error func ServeProject(projectOptions *ProjectOptions, logger *Logger) error { go func() { time.Sleep(2 * time.Second) + if projectOptions.Platform == "windows" { + logger.Yellow("*** Please note: Windows builds use mshtml which is only compatible with IE11. We strongly recommend only using IE11 when running 'wails serve'! For more information, please read https://wails.app/guides/windows/ ***") + } logger.Green(">>>>> To connect, you will need to run '" + projectOptions.FrontEnd.Serve + "' in the '" + projectOptions.FrontEnd.Dir + "' directory <<<<<") }() location, err := filepath.Abs(filepath.Join("build", projectOptions.BinaryName)) diff --git a/cmd/project.go b/cmd/project.go index dc2f053b..ffb72154 100644 --- a/cmd/project.go +++ b/cmd/project.go @@ -6,6 +6,7 @@ import ( "io/ioutil" "os" "path/filepath" + "runtime" "sort" "strings" @@ -150,6 +151,7 @@ type ProjectOptions struct { Template string `json:"-"` BinaryName string `json:"binaryname"` FrontEnd *frontend `json:"frontend,omitempty"` + Tags string `json:"tags"` NPMProjectName string `json:"-"` system *SystemHelper log *Logger @@ -163,6 +165,23 @@ type ProjectOptions struct { Architecture string LdFlags string GoPath string + + // Supported platforms + Platforms []string `json:"platforms,omitempty"` +} + +// PlatformSupported returns true if the template is supported +// on the current platform +func (po *ProjectOptions) PlatformSupported() bool { + + // Default is all platforms supported + if len(po.Platforms) == 0 { + return true + } + + // Check that the platform is in the list + platformsSupported := slicer.String(po.Platforms) + return platformsSupported.Contains(runtime.GOOS) } // Defaults sets the default project template @@ -233,13 +252,16 @@ func (po *ProjectOptions) PromptForInputs() error { for _, k := range keys { templateDetail := templateDetails[k] templateList.Add(templateDetail) + if !templateDetail.Metadata.PlatformSupported() { + templateDetail.Metadata.Name = "* " + templateDetail.Metadata.Name + } options.Add(fmt.Sprintf("%s - %s", templateDetail.Metadata.Name, templateDetail.Metadata.ShortDescription)) } templateIndex := 0 if len(options.AsSlice()) > 1 { - templateIndex = PromptSelection("Please select a template", options.AsSlice(), 0) + templateIndex = PromptSelection("Please select a template (* means unsupported on current platform)", options.AsSlice(), 0) } if len(templateList.AsSlice()) == 0 { @@ -250,6 +272,10 @@ func (po *ProjectOptions) PromptForInputs() error { po.selectedTemplate = templateList.AsSlice()[templateIndex].(*TemplateDetails) } + po.selectedTemplate.Metadata.Name = strings.TrimPrefix(po.selectedTemplate.Metadata.Name, "* ") + if !po.selectedTemplate.Metadata.PlatformSupported() { + println("WARNING: This template is unsupported on this platform!") + } fmt.Println("Template: " + po.selectedTemplate.Metadata.Name) // Setup NPM Project name @@ -372,5 +398,9 @@ func processTemplateMetadata(templateMetadata *TemplateMetadata, po *ProjectOpti } po.FrontEnd.Serve = templateMetadata.Serve } + + // Save platforms + po.Platforms = templateMetadata.Platforms + return nil } diff --git a/cmd/templates.go b/cmd/templates.go index f2ee736b..fe1c68d2 100644 --- a/cmd/templates.go +++ b/cmd/templates.go @@ -7,6 +7,7 @@ import ( "io/ioutil" "log" "path/filepath" + "runtime" "strings" "text/template" @@ -29,6 +30,26 @@ type TemplateMetadata struct { Bridge string `json:"bridge"` WailsDir string `json:"wailsdir"` TemplateDependencies []*TemplateDependency `json:"dependencies,omitempty"` + + // 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 @@ -128,11 +149,11 @@ func (t *TemplateHelper) GetTemplateDetails() (map[string]*TemplateDetails, erro result[name] = &TemplateDetails{ Path: dir, } - _ = &TemplateMetadata{} metadata, err := t.LoadMetadata(dir) if err != nil { return nil, err } + result[name].Metadata = metadata if metadata.Name != "" { result[name].Name = metadata.Name diff --git a/cmd/templates/vue3-full/frontend/.browserslistrc b/cmd/templates/vue3-full/frontend/.browserslistrc new file mode 100644 index 00000000..214388fe --- /dev/null +++ b/cmd/templates/vue3-full/frontend/.browserslistrc @@ -0,0 +1,3 @@ +> 1% +last 2 versions +not dead diff --git a/cmd/templates/vue3-full/frontend/.eslintrc.js b/cmd/templates/vue3-full/frontend/.eslintrc.js new file mode 100644 index 00000000..bf594a12 --- /dev/null +++ b/cmd/templates/vue3-full/frontend/.eslintrc.js @@ -0,0 +1,29 @@ +module.exports = { + root: true, + env: { + node: true + }, + 'extends': [ + 'plugin:vue/vue3-essential', + 'eslint:recommended', + '@vue/typescript/recommended' + ], + parserOptions: { + ecmaVersion: 2020 + }, + rules: { + 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', + 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off' + }, + overrides: [ + { + files: [ + '**/__tests__/*.{j,t}s?(x)', + '**/tests/unit/**/*.spec.{j,t}s?(x)' + ], + env: { + mocha: true + } + } + ] +} diff --git a/cmd/templates/vue3-full/frontend/.gitignore b/cmd/templates/vue3-full/frontend/.gitignore new file mode 100644 index 00000000..185e6631 --- /dev/null +++ b/cmd/templates/vue3-full/frontend/.gitignore @@ -0,0 +1,21 @@ +.DS_Store +node_modules +/dist + +# local env files +.env.local +.env.*.local + +# Log files +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw* diff --git a/cmd/templates/vue3-full/frontend/README.md b/cmd/templates/vue3-full/frontend/README.md new file mode 100644 index 00000000..6953f66d --- /dev/null +++ b/cmd/templates/vue3-full/frontend/README.md @@ -0,0 +1,35 @@ +# vue basic + +## Project setup + +``` +npm install +``` + +### Compiles and hot-reloads for development + +``` +npm run serve +``` + +### Compiles and minifies for production + +``` +npm run build +``` + +### Run your tests + +``` +npm run test +``` + +### Lints and fixes files + +``` +npm run lint +``` + +### Customize configuration + +See [Configuration Reference](https://cli.vuejs.org/config/). diff --git a/cmd/templates/vue3-full/frontend/package.json.template b/cmd/templates/vue3-full/frontend/package.json.template new file mode 100644 index 00000000..cadd160a --- /dev/null +++ b/cmd/templates/vue3-full/frontend/package.json.template @@ -0,0 +1,37 @@ +{ + "name": "{{.NPMProjectName}}", + "author": "{{.Author.Name}}<{{.Author.Email}}>", + "private": true, + "scripts": { + "serve": "vue-cli-service serve", + "build": "vue-cli-service build", + "test:unit": "vue-cli-service test:unit", + "lint": "vue-cli-service lint" + }, + "dependencies": { + "vue": "^3.0.0-0", + "vue-router": "^4.0.0-0", + "regenerator-runtime": "^0.13.7", + "@wailsapp/runtime": "^1.1.1" + }, + "devDependencies": { + "@types/chai": "^4.2.12", + "@types/mocha": "^8.0.3", + "@typescript-eslint/eslint-plugin": "^4.3.0", + "@typescript-eslint/parser": "^4.3.0", + "@vue/cli-plugin-eslint": "~4.5.6", + "@vue/cli-plugin-router": "~4.5.6", + "@vue/cli-plugin-typescript": "~4.5.6", + "@vue/cli-plugin-unit-mocha": "~4.5.6", + "@vue/cli-service": "~4.5.6", + "@vue/compiler-sfc": "^3.0.0", + "@vue/eslint-config-typescript": "^5.1.0", + "@vue/test-utils": "^2.0.0-0", + "chai": "^4.2.0", + "eslint": "^7.10.0", + "eslint-plugin-vue": "^7.0.0", + "node-sass": "^4.14.1", + "sass-loader": "^10.0.2", + "typescript": "~4.0.3" + } +} diff --git a/cmd/templates/vue3-full/frontend/src/App.vue b/cmd/templates/vue3-full/frontend/src/App.vue new file mode 100644 index 00000000..6939bbb9 --- /dev/null +++ b/cmd/templates/vue3-full/frontend/src/App.vue @@ -0,0 +1,32 @@ + + + diff --git a/cmd/templates/vue3-full/frontend/src/assets/appicon.png b/cmd/templates/vue3-full/frontend/src/assets/appicon.png new file mode 100644 index 00000000..9f22be34 Binary files /dev/null and b/cmd/templates/vue3-full/frontend/src/assets/appicon.png differ diff --git a/cmd/templates/vue3-full/frontend/src/components/HelloWorld.vue b/cmd/templates/vue3-full/frontend/src/components/HelloWorld.vue new file mode 100644 index 00000000..92d381cd --- /dev/null +++ b/cmd/templates/vue3-full/frontend/src/components/HelloWorld.vue @@ -0,0 +1,34 @@ + + + + + + diff --git a/cmd/templates/vue3-full/frontend/src/main.ts b/cmd/templates/vue3-full/frontend/src/main.ts new file mode 100644 index 00000000..0d2c5a90 --- /dev/null +++ b/cmd/templates/vue3-full/frontend/src/main.ts @@ -0,0 +1,8 @@ +import { createApp } from 'vue'; +import App from './App.vue'; +import router from './router'; +import * as Wails from '@wailsapp/runtime'; + +Wails.Init(() => { + createApp(App).use(router).mount('#app'); +}); diff --git a/cmd/templates/vue3-full/frontend/src/router/index.ts b/cmd/templates/vue3-full/frontend/src/router/index.ts new file mode 100644 index 00000000..73c74028 --- /dev/null +++ b/cmd/templates/vue3-full/frontend/src/router/index.ts @@ -0,0 +1,27 @@ +import { createRouter, createMemoryHistory, RouteRecordRaw } from 'vue-router' +import Home from '../views/Home.vue' +import About from '../views/About.vue' + +const routes: Array = [ + { + path: '/', + name: 'Home', + component: Home + }, + { + path: '/about', + name: 'About', + // route level code-splitting + // this generates a separate chunk (about.[hash].js) for this route + // which is lazy-loaded when the route is visited. + // component: () => import(/* webpackChunkName: "about" */ '../views/About.vue') + component: About + } +] + +const router = createRouter({ + history: createMemoryHistory(), + routes +}) + +export default router diff --git a/cmd/templates/vue3-full/frontend/src/shims-vue.d.ts b/cmd/templates/vue3-full/frontend/src/shims-vue.d.ts new file mode 100644 index 00000000..32a1b5cd --- /dev/null +++ b/cmd/templates/vue3-full/frontend/src/shims-vue.d.ts @@ -0,0 +1,5 @@ +declare module '*.vue' { + import { defineComponent } from 'vue' + const component: ReturnType + export default component +} diff --git a/cmd/templates/vue3-full/frontend/src/views/About.vue b/cmd/templates/vue3-full/frontend/src/views/About.vue new file mode 100644 index 00000000..3fa28070 --- /dev/null +++ b/cmd/templates/vue3-full/frontend/src/views/About.vue @@ -0,0 +1,5 @@ + diff --git a/cmd/templates/vue3-full/frontend/src/views/Home.vue b/cmd/templates/vue3-full/frontend/src/views/Home.vue new file mode 100644 index 00000000..cc6aac4c --- /dev/null +++ b/cmd/templates/vue3-full/frontend/src/views/Home.vue @@ -0,0 +1,40 @@ + + + diff --git a/cmd/templates/vue3-full/frontend/tests/unit/example.spec.ts b/cmd/templates/vue3-full/frontend/tests/unit/example.spec.ts new file mode 100644 index 00000000..bbb728f1 --- /dev/null +++ b/cmd/templates/vue3-full/frontend/tests/unit/example.spec.ts @@ -0,0 +1,14 @@ +import { expect } from 'chai'; +import { describe, it } from 'mocha'; +import { shallowMount } from '@vue/test-utils'; +import HelloWorld from '@/components/HelloWorld.vue'; + +describe('HelloWorld.vue', () => { + it('renders props.msg when passed', () => { + const msg = 'new message'; + const wrapper = shallowMount(HelloWorld, { + props: { msg } + }); + expect(wrapper.text()).to.include(msg); + }); +}); diff --git a/cmd/templates/vue3-full/frontend/tsconfig.json b/cmd/templates/vue3-full/frontend/tsconfig.json new file mode 100644 index 00000000..e4ba95a1 --- /dev/null +++ b/cmd/templates/vue3-full/frontend/tsconfig.json @@ -0,0 +1,41 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "esnext", + "strict": true, + "jsx": "preserve", + "importHelpers": true, + "moduleResolution": "node", + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "sourceMap": true, + "baseUrl": ".", + "types": [ + "webpack-env", + "mocha", + "chai" + ], + "paths": { + "@/*": [ + "src/*" + ] + }, + "lib": [ + "esnext", + "dom", + "dom.iterable", + "scripthost" + ] + }, + "include": [ + "src/**/*.ts", + "src/**/*.tsx", + "src/**/*.vue", + "tests/**/*.ts", + "tests/**/*.tsx" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/cmd/templates/vue3-full/frontend/vue.config.js b/cmd/templates/vue3-full/frontend/vue.config.js new file mode 100644 index 00000000..8dcf3e33 --- /dev/null +++ b/cmd/templates/vue3-full/frontend/vue.config.js @@ -0,0 +1,42 @@ +let cssConfig = {}; + +if (process.env.NODE_ENV == 'production') { + cssConfig = { + extract: { + filename: '[name].css', + chunkFilename: '[name].css' + } + }; +} + +module.exports = { + chainWebpack: config => { + let limit = 9999999999999999; + config.module + .rule('images') + .test(/\.(png|gif|jpg)(\?.*)?$/i) + .use('url-loader') + .loader('url-loader') + .tap(options => Object.assign(options, { limit: limit })); + config.module + .rule('fonts') + .test(/\.(woff2?|eot|ttf|otf|svg)(\?.*)?$/i) + .use('url-loader') + .loader('url-loader') + .options({ + limit: limit + }); + }, + css: cssConfig, + configureWebpack: { + output: { + filename: '[name].js' + }, + optimization: { + splitChunks: false + } + }, + devServer: { + disableHostCheck: true + } +}; diff --git a/cmd/templates/vue3-full/go.mod.template b/cmd/templates/vue3-full/go.mod.template new file mode 100644 index 00000000..78038106 --- /dev/null +++ b/cmd/templates/vue3-full/go.mod.template @@ -0,0 +1,5 @@ +module {{.BinaryName}} + +require ( + github.com/wailsapp/wails {{.WailsVersion}} +) \ No newline at end of file diff --git a/cmd/templates/vue3-full/main.go.template b/cmd/templates/vue3-full/main.go.template new file mode 100644 index 00000000..e2262bd1 --- /dev/null +++ b/cmd/templates/vue3-full/main.go.template @@ -0,0 +1,27 @@ +package main + +import ( + "github.com/leaanthony/mewn" + "github.com/wailsapp/wails" +) + +func basic() string { + return "Hello World!" +} + +func main() { + + js := mewn.String("./frontend/dist/app.js") + css := mewn.String("./frontend/dist/app.css") + + app := wails.CreateApp(&wails.AppConfig{ + Width: 1024, + Height: 768, + Title: "{{.Name}}", + JS: js, + CSS: css, + Colour: "#131313", + }) + app.Bind(basic) + app.Run() +} diff --git a/cmd/templates/vue3-full/template.json b/cmd/templates/vue3-full/template.json new file mode 100755 index 00000000..a4e0cb52 --- /dev/null +++ b/cmd/templates/vue3-full/template.json @@ -0,0 +1,15 @@ +{ + "name": "Vue3 Full", + "version": "1.0.0", + "shortdescription": "Vue 3, Vuex, Vue-router, and Webpack4", + "description": "Vue3.0.0 Vuex, Vue-router, and Webpack 4", + "install": "npm install", + "build": "npm run build", + "author": "Kyle Muchmore ", + "created": "2020-09-24 21:18:55.09417 +0000 UTC m=+90.125590001", + "frontenddir": "frontend", + "serve": "npm run serve", + "bridge": "src", + "wailsdir": "", + "platforms": ["linux", "darwin"] +} diff --git a/cmd/wails/4_build.go b/cmd/wails/4_build.go index 22246c5c..24f2f275 100644 --- a/cmd/wails/4_build.go +++ b/cmd/wails/4_build.go @@ -31,6 +31,7 @@ func init() { var verbose = false var platform = "" var ldflags = "" + var tags = "" buildSpinner := spinner.NewSpinner() buildSpinner.SetSpinSpeed(50) @@ -44,7 +45,8 @@ func init() { BoolFlag("verbose", "Verbose output", &verbose). StringFlag("t", "Generate Typescript definitions to given file (at runtime)", &typescriptFilename). StringFlag("ldflags", "Extra options for -ldflags", &ldflags). - StringFlag("gopath", "Specify your GOPATH location. Mounted to /go during cross-compilation.", &gopath) + StringFlag("gopath", "Specify your GOPATH location. Mounted to /go during cross-compilation.", &gopath). + StringFlag("tags", "Build tags to pass to the go compiler (quoted and space separated)", &tags) var b strings.Builder for _, plat := range getSupportedPlatforms() { @@ -78,6 +80,11 @@ func init() { return fmt.Errorf("Unable to find 'project.json'. Please check you are in a Wails project directory") } + // Check that this platform is supported + if !projectOptions.PlatformSupported() { + logger.Yellow("WARNING: This project is unsupported on %s - it probably won't work!\n Valid platforms: %s\n", runtime.GOOS, strings.Join(projectOptions.Platforms, ", ")) + } + // Set cross-compile projectOptions.Platform = runtime.GOOS if len(platform) > 0 { @@ -101,6 +108,9 @@ func init() { projectOptions.LdFlags = ldflags projectOptions.GoPath = gopath + // Add tags + projectOptions.Tags = tags + // Validate config // Check if we have a frontend err = cmd.ValidateFrontendConfig(projectOptions) @@ -184,6 +194,10 @@ func init() { return err } + if projectOptions.Platform == "windows" { + logger.Yellow("*** Please note: Windows builds use mshtml which is only compatible with IE11. For more information, please read https://wails.app/guides/windows/ ***") + } + logger.Yellow("Awesome! Project '%s' built!", projectOptions.Name) return nil diff --git a/cmd/wails/6_serve.go b/cmd/wails/6_serve.go index e2165a9f..1ea93c36 100644 --- a/cmd/wails/6_serve.go +++ b/cmd/wails/6_serve.go @@ -70,6 +70,7 @@ func init() { } logger.Yellow("Awesome! Project '%s' built!", projectOptions.Name) + return cmd.ServeProject(projectOptions, logger) }) }