Mostly set up

This commit is contained in:
The Jared Wilcurt 2019-07-08 09:32:03 -04:00
parent 2cf9a5b16f
commit 620b4f3ddb
30 changed files with 17386 additions and 57 deletions

2
.browserslistrc Normal file
View File

@ -0,0 +1,2 @@
> 1%
last 2 versions

25
.editorconfig Normal file
View File

@ -0,0 +1,25 @@
# EditorConfig is awesome: http://EditorConfig.org
# Top-most EditorConfig file
root = true
# defaults for all files
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
# Markdown files uses two trailing spaces to indicate a <br>
[*.{md}]
trim_trailing_whitespace = false
# 4 space indentation
[*.{sass,scss,css}]
indent_size = 4
# 2 space indentation
[*.{html,json}]
indent_size = 2

188
.eslintrc.js Normal file
View File

@ -0,0 +1,188 @@
module.exports = {
root: true,
parserOptions: {
parser: 'babel-eslint',
ecmaVersion: 8,
sourceType: 'module'
},
env: {
browser: true,
node: true
},
globals: {
jsdom: true,
Promise: true,
nw: true,
Vue: true,
Vuex: true,
VueRouter: true,
httpVueLoader: true,
store: true,
router: true
},
plugins: [
'jest',
// required to lint *.vue files
'vue'
],
extends: [
'eslint:recommended',
'plugin:vue/recommended',
'plugin:jest/recommended'
],
rules: {
'arrow-parens': ['off'],
'brace-style': [
'error',
'1tbs',
{
'allowSingleLine': true
}
],
'comma-dangle': ['error', 'never'],
'comma-spacing': [
'error',
{
'before': false,
'after': true
}
],
'comma-style': ['error', 'last'],
'curly': ['error'],
// allow async-await
'generator-star-spacing': ['off'],
// 2 space indentation to match .editorconfig
'indent': [
'error',
2,
{
'SwitchCase': 1
}
],
'keyword-spacing': [
'error',
{
'before': true,
'after': true
}
],
// allow debugger during development
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-multi-spaces': ['error'],
'no-restricted-syntax': ['error', 'Property[method="true"]'],
'no-unused-vars': ['error'],
'no-undef': ['error'],
// Only allow let and const, no var
'no-var': ['error'],
'object-curly-spacing': ['error', 'always'],
'one-var': ['error', 'never'],
'quotes': ['error', 'single'],
'semi': ['error', 'always'],
'space-before-blocks': ['error', 'always'],
'space-before-function-paren': ['error', 'always'],
'space-in-parens': ['error', 'never'],
'space-infix-ops': ['error'],
'spaced-comment': ['error', 'always'],
// Vue Linter Options
'vue/attribute-hyphenation': ['error', 'never'],
'vue/attributes-order': [
'error',
{
'order': [
// 'v-for item in items'
'LIST_RENDERING',
// 'v-if', 'v-else-if', 'v-else', 'v-show', 'v-cloak'
'CONDITIONALS',
// 'v-once', 'v-pre'
'RENDER_MODIFIERS',
// 'v-model', 'v-bind', ':property="foo"'
'BINDING',
// 'v-text', 'v-html'
'CONTENT',
// 'is'
'DEFINITION',
// 'id'
'GLOBAL',
// 'customProp="foo"', 'class', 'type', 'value' etc
'OTHER_ATTR',
// '@click="functionCall"', 'v-on="event"'
'EVENTS',
// 'slot', 'key', 'ref'
'UNIQUE'
]
}
],
'vue/html-closing-bracket-newline': [
'error',
{
'singleline': 'never',
'multiline': 'always'
}
],
'vue/html-closing-bracket-spacing': [
'error',
{
'startTag': 'never',
'endTag': 'never',
'selfClosingTag': 'always'
}
],
'vue/html-indent': [
'error',
2,
{
'attribute': 1,
'closeBracket': 0
}
],
'vue/html-self-closing': [
'error',
{
'html': {
'void': 'always',
'normal': 'never',
'component': 'always'
}
}
],
'vue/max-attributes-per-line': [
'error',
{
'singleline': 3,
'multiline': {
'max': 1,
'allowFirstLine': false
}
}
],
'vue/name-property-casing': ['error', 'PascalCase'],
'vue/order-in-components': [
'error',
{
'order': [
'el',
'name',
['template', 'render'],
'parent',
'functional',
['delimiters', 'comments'],
['components', 'directives'],
'extends',
'mixins',
'inheritAttrs',
'model',
['props', 'propsData'],
'data',
'methods',
'computed',
'filters',
'watch',
'LIFECYCLE_HOOKS',
'renderError'
]
}
],
'vue/prop-name-casing': ['error', 'camelCase']
}
};

80
.gitignore vendored
View File

@ -1,61 +1,27 @@
# Logs
logs
*.log
.DS_Store
node_modules
/dist/
/dist-vue/
/tests/e2e/videos/
/tests/e2e/screenshots/
/tests/e2e/reports/
selenium-debug.log
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# next.js build output
.next
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

View File

@ -1,2 +1,45 @@
# nw-vue-cli-example
NW.js + Vue-CLI 3 example
![A screenshot of the default app running on Windows](screenshot.png)
* Vue
* Vue-DevTools
* Babel
* ESLint
* Jest
* Nightwatch (not working)
## Running Locally for development
1. `npm install`
1. `npm start`
1. An empty window will pop up while Webpack warm ups
1. Once Webpack is running, refresh the window and you're golden
## Building for distribution
1. `npm run build:clean` will delete your `./dist` and `./dist-vue` folders
1. `npm run build:vue` will build just your Vue app for distribution (`./dist-vue`)
1. `npm run build:nw` will build just your NW.js app (`./dist`)
1. `npm run build` is your all-in-one command. It will clean out the old dist folders and build your Vue and NW.js app
# **IMPORTANT NOTE ABOUT BUILDS!!!**
They take a long time. If you do `npm run build` expect it to take 10-15 minutes. This can be adjusted by changing the build params in the `package.json`. The more platforms and build types, the longer it takes.
## Automated quality enforcment
1. **Linting:** `npm run lint` - Uses rules in `./eslint.json`
1. **Unit tests:** `npm run test:unit` - [Jest](https://jestjs.io).
1. **End-to-end:** `npm run test:e2e` - Accepting PR to make tests run in NW.js. - [Nightwatch](https://nightwatchjs.org).
### Customize configuration
See [Vue-CLI documentation](https://cli.vuejs.org/config).

5
babel.config.js Normal file
View File

@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/app'
]
};

30
jest.config.js Normal file
View File

@ -0,0 +1,30 @@
module.exports = {
moduleFileExtensions: [
'js',
'jsx',
'json',
'vue'
],
transform: {
'^.+\\.vue$': 'vue-jest',
'.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
'^.+\\.jsx?$': 'babel-jest'
},
transformIgnorePatterns: [
'/node_modules/'
],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1'
},
snapshotSerializers: [
'jest-serializer-vue'
],
testMatch: [
'**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)'
],
testURL: 'http://localhost/',
watchPlugins: [
'jest-watch-typeahead/filename',
'jest-watch-typeahead/testname'
]
};

21
nightwatch.conf.js Normal file
View File

@ -0,0 +1,21 @@
// eslint-disable-next-line no-unused-vars
let nwBinary = 'nwjs/nw.exe';
let driver = 'nwjs/chromedriver.exe';
if (process.platform === 'linux') {
nwBinary = 'nwjs/nw';
driver = 'nwjs/chromedriver';
}
if (process.platform === 'darwin') {
nwBinary = 'nwjs.app/contents/MacOS/nwjs';
driver = 'chromedriver';
}
nwBinary = './node_modules/nw/' + nwBinary;
driver = './node_modules/nw/' + driver;
module.exports = (function (settings) {
settings.webdriver.server_path = driver;
settings.selenium.cli_args['webdriver.chrome.driver'] = driver;
return settings;
})(require('./nightwatch.json'));

2
nightwatch.json Normal file
View File

@ -0,0 +1,2 @@
{
}

16552
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

113
package.json Normal file
View File

@ -0,0 +1,113 @@
{
"name": "nw-vue",
"version": "1.0.0",
"main": "http://localhost:8964",
"node-remote": "http://localhost:8964",
"node-main": "",
"window": {
"width": 960,
"height": 600,
"min_width": 700,
"min_height": 500,
"icon": "src/assets/vue.png"
},
"private": true,
"scripts": {
"start": "concurrently \"npm run serve\" \"nw .\"",
"serve": "vue-cli-service serve --port=8964",
"build": "npm run build:clean && npm run build:vue && npm run build:nw",
"build:clean": "rimraf ./dist-vue ./dist",
"build:vue": "vue-cli-service build --modern --dest ./dist-vue",
"build:nw": "build --concurrent --tasks win-x86 --mirror https://dl.nwjs.io/ .",
"build:nw2": "build --concurrent --tasks win-x86,linux-x86,linux-x64,mac-x64 --mirror https://dl.nwjs.io/ .",
"run:win": "dist\\nw-vue-1.0.0-win-x86\\nw-vue.exe",
"lint": "vue-cli-service lint --no-fix",
"fix": "vue-cli-service lint --fix",
"test:e2e": "vue-cli-service test:e2e",
"test:unit": "vue-cli-service test:unit"
},
"dependencies": {
"express": "^4.17.1"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^3.9.0",
"@vue/cli-plugin-e2e-nightwatch": "^3.9.0",
"@vue/cli-plugin-eslint": "^3.9.0",
"@vue/cli-plugin-unit-jest": "^3.9.0",
"@vue/cli-service": "^3.9.0",
"@vue/test-utils": "1.0.0-beta.29",
"babel-core": "7.0.0-bridge.0",
"babel-eslint": "^10.0.1",
"babel-jest": "^23.6.0",
"concurrently": "^4.1.1",
"core-js": "^2.6.5",
"eslint": "^5.16.0",
"eslint-plugin-jest": "^22.7.2",
"eslint-plugin-vue": "^5.0.0",
"nw": "0.39.2-sdk",
"nw-vue-devtools": "^1.3.0",
"nwjs-builder-phoenix": "^1.15.0",
"nwjs-types": "^1.0.0",
"rimraf": "^2.6.3",
"vue": "^2.6.10",
"vue-template-compiler": "^2.6.10"
},
"chromium-args": "--load-extension='./node_modules/nw-vue-devtools/extension'",
"build": {
"nwVersion": "v0.39.2",
"nwFlavor": "normal",
"targets": [
"zip",
"nsis7z"
],
"files": [
"**/*"
],
"excludes": [
"e2e/**/*",
"src/**/*",
"tests/**/*",
"public/**/*"
".browserslistrc",
".eslintrc",
".gitignore",
".editorconfig",
"babel.config.js",
"cypress.json",
"jest.config.js",
"nightwatch.conf.js",
"nightwatch.json",
"package-lock.json",
"screenshot.png",
"selenium-debug.log",
"postcss.config.js",
"vue.config.js"
],
"strippedProperties": [
"chromium-args",
"scripts",
"devDependencies",
"build"
],
"overriddenProperties": {
"main": "http://localhost:8965",
"node-remote": "http://localhost:8965",
"node-main": "server.js"
},
"win": {
"icon": "public/icon.ico"
},
"mac": {
"icon": "public/icon.icns"
},
"nsis": {
"icon": "public/icon.ico",
"unIcon": "public/icon.ico",
"languages": [
"English"
],
"diffUpdaters": false,
"hashCalculation": true
}
}
}

5
postcss.config.js Normal file
View File

@ -0,0 +1,5 @@
module.exports = {
plugins: {
autoprefixer: {}
}
};

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
public/icon.icns Normal file

Binary file not shown.

BIN
public/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
public/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

14
public/index.html Normal file
View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>NwVue</title>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

BIN
screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

4
server.js Normal file
View File

@ -0,0 +1,4 @@
const express = require('express');
const app = express();
app.use(express.static('./dist-vue'));
app.listen(8965);

41
src/App.vue Normal file
View File

@ -0,0 +1,41 @@
<template>
<div id="app">
<img
src="@/assets/vue.png"
alt="Vue.js logo"
title="Vue.js"
class="logo"
/>
<img
src="@/assets/nw.png"
alt="NW.js logo"
title="NW.js"
class="logo"
/>
<HelloWorld msg="Welcome to your Vue.js Desktop App in NW.js!" />
</div>
</template>
<script>
import HelloWorld from '@/components/HelloWorld.vue';
export default {
name: 'App',
components: {
HelloWorld
}
};
</script>
<style>
#app {
margin-top: 60px;
color: #2C3E50;
font-family: 'Avenir', sans-serif;
text-align: center;
}
.logo {
max-height: 140px;
margin: 0px 10px;
}
</style>

BIN
src/assets/nw.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

BIN
src/assets/vue.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -0,0 +1,183 @@
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<h3 data-test="versions">
You are using
Vue.js (v{{ vueVersion }}),
NW.js (v{{ versions.nw }}-{{ versions['nw-flavor'] }}),
Node.js (v{{ versions.node }}),
and
Chromium (v{{ versions.chromium }}).
</h3>
<button v-if="devMode" @click="toggleDevTools">
<template v-if="showDevTools">
Show
</template>
<template v-else>
Hide
</template>
Developer Tools
</button>
<p>You can use the resources below to find more information around building your Vue App.</p>
<LinkList :links="welcomeLinks" />
<h3>Installed CLI Plugins</h3>
<LinkList :links="pluginLinks" />
<h3>Essential Links</h3>
<LinkList :links="essentialLinks" />
<h3>Ecosystem</h3>
<LinkList :links="ecosystemLinks" />
</div>
</template>
<script>
import Vue from 'vue';
import LinkList from '@/components/LinkList.vue';
export default {
name: 'HelloWorld',
components: {
LinkList
},
props: {
msg: {
type: String,
default: 'Welcome to your Vue Desktop App in NW.js!'
}
},
data: function () {
return {
showDevTools: true,
welcomeLinks: [
{
name: 'nw-vue-cli-example',
url: 'https://github.com/nwutils/nw-vue-cli-example'
},
{
name: 'NW.js',
url: 'https://nwjs.io'
},
{
name: 'nw-vue-devtools',
url: 'https://github.com/nwutils/nw-vue-devtools'
},
{
name: 'nwjs-builder-phoenix',
url: 'https://github.com/evshiron/nwjs-builder-phoenix'
},
{
name: 'vue-cli',
url: 'https://cli.vuejs.org'
}
],
pluginLinks: [
{
name: 'babel',
url: 'https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel'
},
{
name: 'eslint',
url: 'https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint'
},
{
name: 'unit-jest',
url: 'https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-unit-jest'
},
{
name: 'e2e-cypress',
url: 'https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-e2e-cypress'
}
],
essentialLinks: [
{
name: 'Core Docs',
url: 'https://vuejs.org'
},
{
name: 'Forum',
url: 'https://forum.vuejs.org'
},
{
name: 'Community Chat',
url: 'https://chat.vuejs.org'
},
{
name: 'Twitter',
url: 'https://twitter.com/vuejs'
},
{
name: 'News',
url: 'https://news.vuejs.org'
}
],
ecosystemLinks: [
{
name: 'vue-router',
url: 'https://router.vuejs.org'
},
{
name: 'vuex',
url: 'https://vuex.vuejs.org'
},
{
name: 'vue-devtools',
url: 'https://github.com/vuejs/vue-devtools#vue-devtools'
},
{
name: 'vue-loader',
url: 'https://vue-loader.vuejs.org'
},
{
name: 'awesome-vue',
url: 'https://github.com/vuejs/awesome-vue'
}
]
};
},
methods: {
toggleDevTools: function () {
if (this.showDevTools) {
nw.Window.get().showDevTools();
} else {
nw.Window.get().closeDevTools();
}
this.showDevTools = !this.showDevTools;
}
},
computed: {
devMode: function () {
return process.env.NODE_ENV === 'development';
},
versions: function () {
return window.process.versions;
},
vueVersion: function () {
return Vue.version;
}
}
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0px 0px;
}
button {
background: linear-gradient(0deg, #2EB277, #65E6AC);
border-width: 3.4px;
border-color: #35495E;
border-radius: 8px;
margin: 22px 0px 6px 0px;
padding: 11px 17px;
color: #050709;
font-family: 'Trebuchet MS', Verdana, sans-serif;
font-size: 19px;
font-weight: bold;
}
</style>

View File

@ -0,0 +1,64 @@
<template>
<ul v-if="links">
<li
v-for="(link, linkIndex) in links"
:title="link.url.replace('https://', '')"
:key="'eco' + linkIndex"
>
<a
href="#"
@click.prevent="open(link.url)"
>
{{ link.name }}
</a>
</li>
</ul>
</template>
<script>
export default {
name: 'LinkList',
props: {
links: {
type: Array,
required: false,
default: null,
validator: function (links) {
let valid = true;
links.forEach(function (link) {
if (
!link.name ||
!link.url ||
!link.name.length ||
!link.url.length ||
typeof(link.name) !== 'string' ||
typeof(link.url) !== 'string'
) {
valid = false;
}
});
return valid;
}
}
},
methods: {
open: function (url) {
nw.Shell.openExternal(url);
}
}
};
</script>
<style scoped>
ul {
padding: 0px;
list-style-type: none;
}
li {
display: inline-block;
margin: 0px 10px;
}
a {
color: #42B983;
}
</style>

11
src/main.js Normal file
View File

@ -0,0 +1,11 @@
import Vue from 'vue';
import App from './App.vue';
Vue.config.productionTip = false;
// eslint-ignore-next-line no-unused-vars
const app = new Vue({
render: function (hyperscript) {
return hyperscript(App);
}
}).$mount('#app');

View File

@ -0,0 +1,25 @@
// A custom Nightwatch assertion.
// The assertion name is the filename.
// Example usage:
//
// browser.assert.elementCount(selector, count)
//
// For more information on custom assertions see:
// http://nightwatchjs.org/guide#writing-custom-assertions
exports.assertion = function elementCount (selector, count) {
this.message = 'Testing if element "' + selector + '" has count: ' + count;
this.expected = count;
this.pass = (value) => {
return value === count;
};
this.value = (result) => {
return result.value;
};
function evaluator (_selector) {
return document.querySelectorAll(_selector).length;
}
this.command = (cb) => {
return this.api.execute(evaluator, [selector], cb);
};
};

15
tests/e2e/specs/test.js Normal file
View File

@ -0,0 +1,15 @@
// For authoring Nightwatch tests, see
// http://nightwatchjs.org/guide#usage
module.exports = {
'default e2e tests': (browser) => {
browser
.url(process.env.VUE_DEV_SERVER_URL)
.waitForElementVisible('#app', 5000)
.assert.elementPresent('.hello')
.assert.containsText('h1', 'Welcome to your Vue.js Desktop App in NW.js!')
.assert.containsText('[data-test="versions]"', 'You are using Vue.js (v2.6.10), NW.js (v0.39.2-sdk), Node.js (v12.4.0), and Chromium (v75.0.3770.100).')
.assert.elementCount('img', 2)
.end();
}
};

5
tests/unit/.eslintrc.js Normal file
View File

@ -0,0 +1,5 @@
module.exports = {
env: {
jest: true
}
};

View File

@ -0,0 +1,12 @@
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, {
propsData: { msg }
});
expect(wrapper.text()).toMatch(msg);
});
});

3
vue.config.js Normal file
View File

@ -0,0 +1,3 @@
module.exports = {
lintOnSave: false
};