commit 94ca31d58acb6c0e8f9b41846afeea687a01758d Author: Jocs Date: Mon Nov 13 00:05:57 2017 +0800 first commit diff --git a/.babelrc b/.babelrc new file mode 100644 index 00000000..386f3224 --- /dev/null +++ b/.babelrc @@ -0,0 +1,39 @@ +{ + "comments": false, + "env": { + "test": { + "presets": [ + ["env", { + "targets": { "node": 7 } + }], + "stage-0" + ], + "plugins": ["istanbul"] + }, + "main": { + "presets": [ + ["env", { + "targets": { "node": 7 } + }], + "stage-0" + ] + }, + "renderer": { + "presets": [ + ["env", { + "modules": false + }], + "stage-0" + ] + }, + "web": { + "presets": [ + ["env", { + "modules": false + }], + "stage-0" + ] + } + }, + "plugins": ["transform-runtime"] +} diff --git a/.electron-vue/build.js b/.electron-vue/build.js new file mode 100644 index 00000000..2d96f17e --- /dev/null +++ b/.electron-vue/build.js @@ -0,0 +1,130 @@ +'use strict' + +process.env.NODE_ENV = 'production' + +const { say } = require('cfonts') +const chalk = require('chalk') +const del = require('del') +const { spawn } = require('child_process') +const webpack = require('webpack') +const Multispinner = require('multispinner') + + +const mainConfig = require('./webpack.main.config') +const rendererConfig = require('./webpack.renderer.config') +const webConfig = require('./webpack.web.config') + +const doneLog = chalk.bgGreen.white(' DONE ') + ' ' +const errorLog = chalk.bgRed.white(' ERROR ') + ' ' +const okayLog = chalk.bgBlue.white(' OKAY ') + ' ' +const isCI = process.env.CI || false + +if (process.env.BUILD_TARGET === 'clean') clean() +else if (process.env.BUILD_TARGET === 'web') web() +else build() + +function clean () { + del.sync(['build/*', '!build/icons', '!build/icons/icon.*']) + console.log(`\n${doneLog}\n`) + process.exit() +} + +function build () { + greeting() + + del.sync(['dist/electron/*', '!.gitkeep']) + + const tasks = ['main', 'renderer'] + const m = new Multispinner(tasks, { + preText: 'building', + postText: 'process' + }) + + let results = '' + + m.on('success', () => { + process.stdout.write('\x1B[2J\x1B[0f') + console.log(`\n\n${results}`) + console.log(`${okayLog}take it away ${chalk.yellow('`electron-builder`')}\n`) + process.exit() + }) + + pack(mainConfig).then(result => { + results += result + '\n\n' + m.success('main') + }).catch(err => { + m.error('main') + console.log(`\n ${errorLog}failed to build main process`) + console.error(`\n${err}\n`) + process.exit(1) + }) + + pack(rendererConfig).then(result => { + results += result + '\n\n' + m.success('renderer') + }).catch(err => { + m.error('renderer') + console.log(`\n ${errorLog}failed to build renderer process`) + console.error(`\n${err}\n`) + process.exit(1) + }) +} + +function pack (config) { + return new Promise((resolve, reject) => { + webpack(config, (err, stats) => { + if (err) reject(err.stack || err) + else if (stats.hasErrors()) { + let err = '' + + stats.toString({ + chunks: false, + colors: true + }) + .split(/\r?\n/) + .forEach(line => { + err += ` ${line}\n` + }) + + reject(err) + } else { + resolve(stats.toString({ + chunks: false, + colors: true + })) + } + }) + }) +} + +function web () { + del.sync(['dist/web/*', '!.gitkeep']) + webpack(webConfig, (err, stats) => { + if (err || stats.hasErrors()) console.log(err) + + console.log(stats.toString({ + chunks: false, + colors: true + })) + + process.exit() + }) +} + +function greeting () { + const cols = process.stdout.columns + let text = '' + + if (cols > 85) text = 'lets-build' + else if (cols > 60) text = 'lets-|build' + else text = false + + if (text && !isCI) { + say(text, { + colors: ['yellow'], + font: 'simple3d', + space: false + }) + } else console.log(chalk.yellow.bold('\n lets-build')) + console.log() +} diff --git a/.electron-vue/dev-client.js b/.electron-vue/dev-client.js new file mode 100644 index 00000000..2913ea4b --- /dev/null +++ b/.electron-vue/dev-client.js @@ -0,0 +1,40 @@ +const hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true') + +hotClient.subscribe(event => { + /** + * Reload browser when HTMLWebpackPlugin emits a new index.html + * + * Currently disabled until jantimon/html-webpack-plugin#680 is resolved. + * https://github.com/SimulatedGREG/electron-vue/issues/437 + * https://github.com/jantimon/html-webpack-plugin/issues/680 + */ + // if (event.action === 'reload') { + // window.location.reload() + // } + + /** + * Notify `mainWindow` when `main` process is compiling, + * giving notice for an expected reload of the `electron` process + */ + if (event.action === 'compiling') { + document.body.innerHTML += ` + + +
+ Compiling Main Process... +
+ ` + } +}) diff --git a/.electron-vue/dev-runner.js b/.electron-vue/dev-runner.js new file mode 100644 index 00000000..275e4c42 --- /dev/null +++ b/.electron-vue/dev-runner.js @@ -0,0 +1,178 @@ +'use strict' + +const chalk = require('chalk') +const electron = require('electron') +const path = require('path') +const { say } = require('cfonts') +const { spawn } = require('child_process') +const webpack = require('webpack') +const WebpackDevServer = require('webpack-dev-server') +const webpackHotMiddleware = require('webpack-hot-middleware') + +const mainConfig = require('./webpack.main.config') +const rendererConfig = require('./webpack.renderer.config') + +let electronProcess = null +let manualRestart = false +let hotMiddleware + +function logStats (proc, data) { + let log = '' + + log += chalk.yellow.bold(`┏ ${proc} Process ${new Array((19 - proc.length) + 1).join('-')}`) + log += '\n\n' + + if (typeof data === 'object') { + data.toString({ + colors: true, + chunks: false + }).split(/\r?\n/).forEach(line => { + log += ' ' + line + '\n' + }) + } else { + log += ` ${data}\n` + } + + log += '\n' + chalk.yellow.bold(`┗ ${new Array(28 + 1).join('-')}`) + '\n' + + console.log(log) +} + +function startRenderer () { + return new Promise((resolve, reject) => { + rendererConfig.entry.renderer = [path.join(__dirname, 'dev-client')].concat(rendererConfig.entry.renderer) + + const compiler = webpack(rendererConfig) + hotMiddleware = webpackHotMiddleware(compiler, { + log: false, + heartbeat: 2500 + }) + + compiler.plugin('compilation', compilation => { + compilation.plugin('html-webpack-plugin-after-emit', (data, cb) => { + hotMiddleware.publish({ action: 'reload' }) + cb() + }) + }) + + compiler.plugin('done', stats => { + logStats('Renderer', stats) + }) + + const server = new WebpackDevServer( + compiler, + { + contentBase: path.join(__dirname, '../'), + quiet: true, + setup (app, ctx) { + app.use(hotMiddleware) + ctx.middleware.waitUntilValid(() => { + resolve() + }) + } + } + ) + + server.listen(9080) + }) +} + +function startMain () { + return new Promise((resolve, reject) => { + mainConfig.entry.main = [path.join(__dirname, '../src/main/index.dev.js')].concat(mainConfig.entry.main) + + const compiler = webpack(mainConfig) + + compiler.plugin('watch-run', (compilation, done) => { + logStats('Main', chalk.white.bold('compiling...')) + hotMiddleware.publish({ action: 'compiling' }) + done() + }) + + compiler.watch({}, (err, stats) => { + if (err) { + console.log(err) + return + } + + logStats('Main', stats) + + if (electronProcess && electronProcess.kill) { + manualRestart = true + process.kill(electronProcess.pid) + electronProcess = null + startElectron() + + setTimeout(() => { + manualRestart = false + }, 5000) + } + + resolve() + }) + }) +} + +function startElectron () { + electronProcess = spawn(electron, ['--inspect=5858', path.join(__dirname, '../dist/electron/main.js')]) + + electronProcess.stdout.on('data', data => { + electronLog(data, 'blue') + }) + electronProcess.stderr.on('data', data => { + electronLog(data, 'red') + }) + + electronProcess.on('close', () => { + if (!manualRestart) process.exit() + }) +} + +function electronLog (data, color) { + let log = '' + data = data.toString().split(/\r?\n/) + data.forEach(line => { + log += ` ${line}\n` + }) + if (/[0-9A-z]+/.test(log)) { + console.log( + chalk[color].bold('┏ Electron -------------------') + + '\n\n' + + log + + chalk[color].bold('┗ ----------------------------') + + '\n' + ) + } +} + +function greeting () { + const cols = process.stdout.columns + let text = '' + + if (cols > 104) text = 'electron-vue' + else if (cols > 76) text = 'electron-|vue' + else text = false + + if (text) { + say(text, { + colors: ['yellow'], + font: 'simple3d', + space: false + }) + } else console.log(chalk.yellow.bold('\n electron-vue')) + console.log(chalk.blue(' getting ready...') + '\n') +} + +function init () { + greeting() + + Promise.all([startRenderer(), startMain()]) + .then(() => { + startElectron() + }) + .catch(err => { + console.error(err) + }) +} + +init() diff --git a/.electron-vue/webpack.main.config.js b/.electron-vue/webpack.main.config.js new file mode 100644 index 00000000..ad541e42 --- /dev/null +++ b/.electron-vue/webpack.main.config.js @@ -0,0 +1,83 @@ +'use strict' + +process.env.BABEL_ENV = 'main' + +const path = require('path') +const { dependencies } = require('../package.json') +const webpack = require('webpack') + +const BabiliWebpackPlugin = require('babili-webpack-plugin') + +let mainConfig = { + entry: { + main: path.join(__dirname, '../src/main/index.js') + }, + externals: [ + ...Object.keys(dependencies || {}) + ], + module: { + rules: [ + { + test: /\.(js)$/, + enforce: 'pre', + exclude: /node_modules/, + use: { + loader: 'eslint-loader', + options: { + formatter: require('eslint-friendly-formatter') + } + } + }, + { + test: /\.js$/, + use: 'babel-loader', + exclude: /node_modules/ + }, + { + test: /\.node$/, + use: 'node-loader' + } + ] + }, + node: { + __dirname: process.env.NODE_ENV !== 'production', + __filename: process.env.NODE_ENV !== 'production' + }, + output: { + filename: '[name].js', + libraryTarget: 'commonjs2', + path: path.join(__dirname, '../dist/electron') + }, + plugins: [ + new webpack.NoEmitOnErrorsPlugin() + ], + resolve: { + extensions: ['.js', '.json', '.node'] + }, + target: 'electron-main' +} + +/** + * Adjust mainConfig for development settings + */ +if (process.env.NODE_ENV !== 'production') { + mainConfig.plugins.push( + new webpack.DefinePlugin({ + '__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"` + }) + ) +} + +/** + * Adjust mainConfig for production settings + */ +if (process.env.NODE_ENV === 'production') { + mainConfig.plugins.push( + new BabiliWebpackPlugin(), + new webpack.DefinePlugin({ + 'process.env.NODE_ENV': '"production"' + }) + ) +} + +module.exports = mainConfig diff --git a/.electron-vue/webpack.renderer.config.js b/.electron-vue/webpack.renderer.config.js new file mode 100644 index 00000000..f52186e2 --- /dev/null +++ b/.electron-vue/webpack.renderer.config.js @@ -0,0 +1,178 @@ +'use strict' + +process.env.BABEL_ENV = 'renderer' + +const path = require('path') +const { dependencies } = require('../package.json') +const webpack = require('webpack') + +const BabiliWebpackPlugin = require('babili-webpack-plugin') +const CopyWebpackPlugin = require('copy-webpack-plugin') +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const HtmlWebpackPlugin = require('html-webpack-plugin') + +/** + * List of node_modules to include in webpack bundle + * + * Required for specific packages like Vue UI libraries + * that provide pure *.vue files that need compiling + * https://simulatedgreg.gitbooks.io/electron-vue/content/en/webpack-configurations.html#white-listing-externals + */ +let whiteListedModules = ['vue'] + +let rendererConfig = { + devtool: '#cheap-module-eval-source-map', + entry: { + renderer: path.join(__dirname, '../src/renderer/main.js') + }, + externals: [ + ...Object.keys(dependencies || {}).filter(d => !whiteListedModules.includes(d)) + ], + module: { + rules: [ + { + test: /\.(js|vue)$/, + enforce: 'pre', + exclude: /node_modules/, + use: { + loader: 'eslint-loader', + options: { + formatter: require('eslint-friendly-formatter') + } + } + }, + { + test: /\.css$/, + use: ExtractTextPlugin.extract({ + fallback: 'style-loader', + use: 'css-loader' + }) + }, + { + test: /\.html$/, + use: 'vue-html-loader' + }, + { + test: /\.js$/, + use: 'babel-loader', + exclude: /node_modules/ + }, + { + test: /\.node$/, + use: 'node-loader' + }, + { + test: /\.vue$/, + use: { + loader: 'vue-loader', + options: { + extractCSS: process.env.NODE_ENV === 'production', + loaders: { + sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1', + scss: 'vue-style-loader!css-loader!sass-loader' + } + } + } + }, + { + test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, + use: { + loader: 'url-loader', + query: { + limit: 10000, + name: 'imgs/[name]--[folder].[ext]' + } + } + }, + { + test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: 'media/[name]--[folder].[ext]' + } + }, + { + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, + use: { + loader: 'url-loader', + query: { + limit: 10000, + name: 'fonts/[name]--[folder].[ext]' + } + } + } + ] + }, + node: { + __dirname: process.env.NODE_ENV !== 'production', + __filename: process.env.NODE_ENV !== 'production' + }, + plugins: [ + new ExtractTextPlugin('styles.css'), + new HtmlWebpackPlugin({ + filename: 'index.html', + template: path.resolve(__dirname, '../src/index.ejs'), + minify: { + collapseWhitespace: true, + removeAttributeQuotes: true, + removeComments: true + }, + nodeModules: process.env.NODE_ENV !== 'production' + ? path.resolve(__dirname, '../node_modules') + : false + }), + new webpack.HotModuleReplacementPlugin(), + new webpack.NoEmitOnErrorsPlugin() + ], + output: { + filename: '[name].js', + libraryTarget: 'commonjs2', + path: path.join(__dirname, '../dist/electron') + }, + resolve: { + alias: { + '@': path.join(__dirname, '../src/renderer'), + 'vue$': 'vue/dist/vue.esm.js' + }, + extensions: ['.js', '.vue', '.json', '.css', '.node'] + }, + target: 'electron-renderer' +} + +/** + * Adjust rendererConfig for development settings + */ +if (process.env.NODE_ENV !== 'production') { + rendererConfig.plugins.push( + new webpack.DefinePlugin({ + '__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"` + }) + ) +} + +/** + * Adjust rendererConfig for production settings + */ +if (process.env.NODE_ENV === 'production') { + rendererConfig.devtool = '' + + rendererConfig.plugins.push( + new BabiliWebpackPlugin(), + new CopyWebpackPlugin([ + { + from: path.join(__dirname, '../static'), + to: path.join(__dirname, '../dist/electron/static'), + ignore: ['.*'] + } + ]), + new webpack.DefinePlugin({ + 'process.env.NODE_ENV': '"production"' + }), + new webpack.LoaderOptionsPlugin({ + minimize: true + }) + ) +} + +module.exports = rendererConfig diff --git a/.electron-vue/webpack.web.config.js b/.electron-vue/webpack.web.config.js new file mode 100644 index 00000000..010883a4 --- /dev/null +++ b/.electron-vue/webpack.web.config.js @@ -0,0 +1,139 @@ +'use strict' + +process.env.BABEL_ENV = 'web' + +const path = require('path') +const webpack = require('webpack') + +const BabiliWebpackPlugin = require('babili-webpack-plugin') +const CopyWebpackPlugin = require('copy-webpack-plugin') +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const HtmlWebpackPlugin = require('html-webpack-plugin') + +let webConfig = { + devtool: '#cheap-module-eval-source-map', + entry: { + web: path.join(__dirname, '../src/renderer/main.js') + }, + module: { + rules: [ + { + test: /\.(js|vue)$/, + enforce: 'pre', + exclude: /node_modules/, + use: { + loader: 'eslint-loader', + options: { + formatter: require('eslint-friendly-formatter') + } + } + }, + { + test: /\.css$/, + use: ExtractTextPlugin.extract({ + fallback: 'style-loader', + use: 'css-loader' + }) + }, + { + test: /\.html$/, + use: 'vue-html-loader' + }, + { + test: /\.js$/, + use: 'babel-loader', + include: [ path.resolve(__dirname, '../src/renderer') ], + exclude: /node_modules/ + }, + { + test: /\.vue$/, + use: { + loader: 'vue-loader', + options: { + extractCSS: true, + loaders: { + sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1', + scss: 'vue-style-loader!css-loader!sass-loader' + } + } + } + }, + { + test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, + use: { + loader: 'url-loader', + query: { + limit: 10000, + name: 'imgs/[name].[ext]' + } + } + }, + { + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, + use: { + loader: 'url-loader', + query: { + limit: 10000, + name: 'fonts/[name].[ext]' + } + } + } + ] + }, + plugins: [ + new ExtractTextPlugin('styles.css'), + new HtmlWebpackPlugin({ + filename: 'index.html', + template: path.resolve(__dirname, '../src/index.ejs'), + minify: { + collapseWhitespace: true, + removeAttributeQuotes: true, + removeComments: true + }, + nodeModules: false + }), + new webpack.DefinePlugin({ + 'process.env.IS_WEB': 'true' + }), + new webpack.HotModuleReplacementPlugin(), + new webpack.NoEmitOnErrorsPlugin() + ], + output: { + filename: '[name].js', + path: path.join(__dirname, '../dist/web') + }, + resolve: { + alias: { + '@': path.join(__dirname, '../src/renderer'), + 'vue$': 'vue/dist/vue.esm.js' + }, + extensions: ['.js', '.vue', '.json', '.css'] + }, + target: 'web' +} + +/** + * Adjust webConfig for production settings + */ +if (process.env.NODE_ENV === 'production') { + webConfig.devtool = '' + + webConfig.plugins.push( + new BabiliWebpackPlugin(), + new CopyWebpackPlugin([ + { + from: path.join(__dirname, '../static'), + to: path.join(__dirname, '../dist/web/static'), + ignore: ['.*'] + } + ]), + new webpack.DefinePlugin({ + 'process.env.NODE_ENV': '"production"' + }), + new webpack.LoaderOptionsPlugin({ + minimize: true + }) + ) +} + +module.exports = webConfig diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..d8b0d0ae --- /dev/null +++ b/.eslintignore @@ -0,0 +1,3 @@ +test/unit/coverage/** +test/unit/*.js +test/e2e/*.js diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..90cf05bb --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,26 @@ +module.exports = { + root: true, + parser: 'babel-eslint', + parserOptions: { + sourceType: 'module' + }, + env: { + browser: true, + node: true + }, + extends: 'standard', + globals: { + __static: true + }, + plugins: [ + 'html' + ], + 'rules': { + // allow paren-less arrow functions + 'arrow-parens': 0, + // allow async-await + 'generator-star-spacing': 0, + // allow debugger during development + 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0 + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..57957029 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +.DS_Store +dist/electron/* +dist/web/* +build/* +!build/icons +coverage +node_modules/ +npm-debug.log +npm-debug.log.* +thumbs.db +!.gitkeep diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..11085506 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,43 @@ +# Commented sections below can be used to run tests on the CI server +# https://simulatedgreg.gitbooks.io/electron-vue/content/en/testing.html#on-the-subject-of-ci-testing +osx_image: xcode8.3 +sudo: required +dist: trusty +language: c +matrix: + include: + - os: osx + - os: linux + env: CC=clang CXX=clang++ npm_config_clang=1 + compiler: clang +cache: + directories: + - node_modules + - "$HOME/.electron" + - "$HOME/.cache" +addons: + apt: + packages: + - libgnome-keyring-dev + - icnsutils + #- xvfb +before_install: +- mkdir -p /tmp/git-lfs && curl -L https://github.com/github/git-lfs/releases/download/v1.2.1/git-lfs-$([ + "$TRAVIS_OS_NAME" == "linux" ] && echo "linux" || echo "darwin")-amd64-1.2.1.tar.gz + | tar -xz -C /tmp/git-lfs --strip-components 1 && /tmp/git-lfs/git-lfs pull +- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install --no-install-recommends -y icnsutils graphicsmagick xz-utils; fi +install: +#- export DISPLAY=':99.0' +#- Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & +- nvm install 7 +- curl -o- -L https://yarnpkg.com/install.sh | bash +- source ~/.bashrc +- npm install -g xvfb-maybe +- yarn +script: +#- xvfb-maybe node_modules/.bin/karma start test/unit/karma.conf.js +#- yarn run pack && xvfb-maybe node_modules/.bin/mocha test/e2e +- yarn run build +branches: + only: + - master diff --git a/README.md b/README.md new file mode 100644 index 00000000..c12c4866 --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ +# aganippe + +> A markdown editor + +#### Build Setup + +``` bash +# install dependencies +npm install + +# serve with hot reload at localhost:9080 +npm run dev + +# build electron application for production +npm run build + +# run unit & end-to-end tests +npm test + + +# lint all JS/Vue component files in `src/` +npm run lint + +``` + +--- + +This project was generated with [electron-vue](https://github.com/SimulatedGREG/electron-vue)@[f5d9648](https://github.com/SimulatedGREG/electron-vue/tree/f5d9648e169a3efef53159823cc7a4c7eb7221d1) using [vue-cli](https://github.com/vuejs/vue-cli). Documentation about the original structure can be found [here](https://simulatedgreg.gitbooks.io/electron-vue/content/index.html). diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000..0bff3e06 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,33 @@ +# Commented sections below can be used to run tests on the CI server +# https://simulatedgreg.gitbooks.io/electron-vue/content/en/testing.html#on-the-subject-of-ci-testing +version: 0.1.{build} + +branches: + only: + - master + +image: Visual Studio 2017 +platform: + - x64 + +cache: + - node_modules + - '%APPDATA%\npm-cache' + - '%USERPROFILE%\.electron' + - '%USERPROFILE%\AppData\Local\Yarn\cache' + +init: + - git config --global core.autocrlf input + +install: + - ps: Install-Product node 8 x64 + - choco install yarn --ignore-dependencies + - git reset --hard HEAD + - yarn + - node --version + +build_script: + #- yarn test + - yarn build + +test: off diff --git a/build/icons/256x256.png b/build/icons/256x256.png new file mode 100644 index 00000000..3d67e9d6 Binary files /dev/null and b/build/icons/256x256.png differ diff --git a/build/icons/icon.icns b/build/icons/icon.icns new file mode 100644 index 00000000..a65f91bc Binary files /dev/null and b/build/icons/icon.icns differ diff --git a/build/icons/icon.ico b/build/icons/icon.ico new file mode 100644 index 00000000..87027290 Binary files /dev/null and b/build/icons/icon.ico differ diff --git a/dist/electron/.gitkeep b/dist/electron/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/dist/web/.gitkeep b/dist/web/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/package.json b/package.json new file mode 100644 index 00000000..24554600 --- /dev/null +++ b/package.json @@ -0,0 +1,123 @@ +{ + "name": "aganippe", + "version": "0.0.0", + "author": "Jocs ", + "description": "A markdown editor", + "license": null, + "main": "./dist/electron/main.js", + "scripts": { + "build": "node .electron-vue/build.js && electron-builder", + "build:dir": "node .electron-vue/build.js && electron-builder --dir", + "build:clean": "cross-env BUILD_TARGET=clean node .electron-vue/build.js", + "build:web": "cross-env BUILD_TARGET=web node .electron-vue/build.js", + "dev": "node .electron-vue/dev-runner.js", + "e2e": "npm run pack && mocha test/e2e", + "lint": "eslint --ext .js,.vue -f ./node_modules/eslint-friendly-formatter src test", + "lint:fix": "eslint --ext .js,.vue -f ./node_modules/eslint-friendly-formatter --fix src test", + "pack": "npm run pack:main && npm run pack:renderer", + "pack:main": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.main.config.js", + "pack:renderer": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.renderer.config.js", + "test": "npm run unit && npm run e2e", + "unit": "karma start test/unit/karma.conf.js", + "postinstall": "npm run lint:fix" + }, + "build": { + "productName": "aganippe", + "appId": "org.simulatedgreg.electron-vue", + "directories": { + "output": "build" + }, + "files": [ + "dist/electron/**/*" + ], + "dmg": { + "contents": [ + { + "x": 410, + "y": 150, + "type": "link", + "path": "/Applications" + }, + { + "x": 130, + "y": 150, + "type": "file" + } + ] + }, + "mac": { + "icon": "build/icons/icon.icns" + }, + "win": { + "icon": "build/icons/icon.ico" + }, + "linux": { + "icon": "build/icons" + } + }, + "dependencies": { + "vue": "^2.3.3", + "axios": "^0.16.1", + "vue-electron": "^1.0.6", + "vuex": "^2.3.1" + }, + "devDependencies": { + "babel-core": "^6.25.0", + "babel-loader": "^7.1.1", + "babel-plugin-transform-runtime": "^6.23.0", + "babel-preset-env": "^1.6.0", + "babel-preset-stage-0": "^6.24.1", + "babel-register": "^6.24.1", + "babili-webpack-plugin": "^0.1.2", + "cfonts": "^1.1.3", + "chalk": "^2.1.0", + "copy-webpack-plugin": "^4.0.1", + "cross-env": "^5.0.5", + "css-loader": "^0.28.4", + "del": "^3.0.0", + "devtron": "^1.4.0", + "electron": "^1.7.5", + "electron-debug": "^1.4.0", + "electron-devtools-installer": "^2.2.0", + "electron-builder": "^19.19.1", + "babel-eslint": "^7.2.3", + "eslint": "^4.4.1", + "eslint-friendly-formatter": "^3.0.0", + "eslint-loader": "^1.9.0", + "eslint-plugin-html": "^3.1.1", + "eslint-config-standard": "^10.2.1", + "eslint-plugin-import": "^2.7.0", + "eslint-plugin-node": "^5.1.1", + "eslint-plugin-promise": "^3.5.0", + "eslint-plugin-standard": "^3.0.1", + "extract-text-webpack-plugin": "^3.0.0", + "file-loader": "^0.11.2", + "html-webpack-plugin": "^2.30.1", + "inject-loader": "^3.0.0", + "karma": "^1.3.0", + "karma-chai": "^0.1.0", + "karma-coverage": "^1.1.1", + "karma-electron": "^5.1.1", + "karma-mocha": "^1.2.0", + "karma-sourcemap-loader": "^0.3.7", + "karma-spec-reporter": "^0.0.31", + "karma-webpack": "^2.0.1", + "webpack-merge": "^4.1.0", + "require-dir": "^0.3.0", + "spectron": "^3.7.1", + "babel-plugin-istanbul": "^4.1.1", + "chai": "^4.0.0", + "mocha": "^3.0.2", + "multispinner": "^0.2.1", + "node-loader": "^0.6.0", + "style-loader": "^0.18.2", + "url-loader": "^0.5.9", + "vue-html-loader": "^1.2.4", + "vue-loader": "^13.0.5", + "vue-style-loader": "^3.0.1", + "vue-template-compiler": "^2.4.2", + "webpack": "^3.5.2", + "webpack-dev-server": "^2.7.1", + "webpack-hot-middleware": "^2.18.2" + } +} diff --git a/src/index.ejs b/src/index.ejs new file mode 100644 index 00000000..e4307e0d --- /dev/null +++ b/src/index.ejs @@ -0,0 +1,22 @@ + + + + + aganippe + <% if (htmlWebpackPlugin.options.nodeModules) { %> + + + <% } %> + + +
+ + + + + + diff --git a/src/main/index.dev.js b/src/main/index.dev.js new file mode 100644 index 00000000..ae70ccf7 --- /dev/null +++ b/src/main/index.dev.js @@ -0,0 +1,27 @@ +/** + * This file is used specifically and only for development. It installs + * `electron-debug` & `vue-devtools`. There shouldn't be any need to + * modify this file, but it can be used to extend your development + * environment. + */ + +/* eslint-disable */ + +// Set environment for development +process.env.NODE_ENV = 'development' + +// Install `electron-debug` with `devtron` +require('electron-debug')({ showDevTools: true }) + +// Install `vue-devtools` +require('electron').app.on('ready', () => { + let installExtension = require('electron-devtools-installer') + installExtension.default(installExtension.VUEJS_DEVTOOLS) + .then(() => {}) + .catch(err => { + console.log('Unable to install `vue-devtools`: \n', err) + }) +}) + +// Require `main` process to boot app +require('./index') diff --git a/src/main/index.js b/src/main/index.js new file mode 100644 index 00000000..f6dbfe48 --- /dev/null +++ b/src/main/index.js @@ -0,0 +1,67 @@ +'use strict' + +import { app, BrowserWindow } from 'electron' + +/** + * Set `__static` path to static files in production + * https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-static-assets.html + */ +if (process.env.NODE_ENV !== 'development') { + global.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\') +} + +let mainWindow +const winURL = process.env.NODE_ENV === 'development' + ? `http://localhost:9080` + : `file://${__dirname}/index.html` + +function createWindow () { + /** + * Initial window options + */ + mainWindow = new BrowserWindow({ + height: 563, + useContentSize: true, + width: 1000 + }) + + mainWindow.loadURL(winURL) + + mainWindow.on('closed', () => { + mainWindow = null + }) +} + +app.on('ready', createWindow) + +app.on('window-all-closed', () => { + if (process.platform !== 'darwin') { + app.quit() + } +}) + +app.on('activate', () => { + if (mainWindow === null) { + createWindow() + } +}) + +/** + * Auto Updater + * + * Uncomment the following code below and install `electron-updater` to + * support auto updating. Code Signing with a valid certificate is required. + * https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-electron-builder.html#auto-updating + */ + +/* +import { autoUpdater } from 'electron-updater' + +autoUpdater.on('update-downloaded', () => { + autoUpdater.quitAndInstall() +}) + +app.on('ready', () => { + if (process.env.NODE_ENV === 'production') autoUpdater.checkForUpdates() +}) + */ diff --git a/src/renderer/App.vue b/src/renderer/App.vue new file mode 100644 index 00000000..2bd28b91 --- /dev/null +++ b/src/renderer/App.vue @@ -0,0 +1,20 @@ + + + + + diff --git a/src/renderer/assets/.gitkeep b/src/renderer/assets/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/src/renderer/assets/logo.png b/src/renderer/assets/logo.png new file mode 100644 index 00000000..63736e2c Binary files /dev/null and b/src/renderer/assets/logo.png differ diff --git a/src/renderer/components/Editor.vue b/src/renderer/components/Editor.vue new file mode 100644 index 00000000..4c18bcde --- /dev/null +++ b/src/renderer/components/Editor.vue @@ -0,0 +1,36 @@ + + + + + diff --git a/src/renderer/editor/index.js b/src/renderer/editor/index.js new file mode 100644 index 00000000..cfd012b0 --- /dev/null +++ b/src/renderer/editor/index.js @@ -0,0 +1,12 @@ +class Aganippe { + constructor (container, options) { + this.container = container + this.init() + } + init () { + const { container } = this + container.setAttribute('contenteditable', true) + } +} + +export default Aganippe diff --git a/src/renderer/editor/theme.css b/src/renderer/editor/theme.css new file mode 100644 index 00000000..ef1e9598 --- /dev/null +++ b/src/renderer/editor/theme.css @@ -0,0 +1,3 @@ +p { + margin: 10px 0; +} \ No newline at end of file diff --git a/src/renderer/main.js b/src/renderer/main.js new file mode 100644 index 00000000..c4ac0fbe --- /dev/null +++ b/src/renderer/main.js @@ -0,0 +1,16 @@ +import Vue from 'vue' +import axios from 'axios' + +import App from './App' +import store from './store' + +if (!process.env.IS_WEB) Vue.use(require('vue-electron')) +Vue.http = Vue.prototype.$http = axios +Vue.config.productionTip = false + +/* eslint-disable no-new */ +new Vue({ + components: { App }, + store, + template: '' +}).$mount('#app') diff --git a/src/renderer/store/index.js b/src/renderer/store/index.js new file mode 100644 index 00000000..913f0a2f --- /dev/null +++ b/src/renderer/store/index.js @@ -0,0 +1,11 @@ +import Vue from 'vue' +import Vuex from 'vuex' + +import modules from './modules' + +Vue.use(Vuex) + +export default new Vuex.Store({ + modules, + strict: process.env.NODE_ENV !== 'production' +}) diff --git a/src/renderer/store/modules/Counter.js b/src/renderer/store/modules/Counter.js new file mode 100644 index 00000000..44a8ab4c --- /dev/null +++ b/src/renderer/store/modules/Counter.js @@ -0,0 +1,25 @@ +const state = { + main: 0 +} + +const mutations = { + DECREMENT_MAIN_COUNTER (state) { + state.main-- + }, + INCREMENT_MAIN_COUNTER (state) { + state.main++ + } +} + +const actions = { + someAsyncTask ({ commit }) { + // do something async + commit('INCREMENT_MAIN_COUNTER') + } +} + +export default { + state, + mutations, + actions +} diff --git a/src/renderer/store/modules/index.js b/src/renderer/store/modules/index.js new file mode 100644 index 00000000..428c6be2 --- /dev/null +++ b/src/renderer/store/modules/index.js @@ -0,0 +1,14 @@ +/** + * The file enables `@/store/index.js` to import all vuex modules + * in a one-shot manner. There should not be any reason to edit this file. + */ + +const files = require.context('.', false, /\.js$/) +const modules = {} + +files.keys().forEach(key => { + if (key === './index.js') return + modules[key.replace(/(\.\/|\.js)/g, '')] = files(key).default +}) + +export default modules diff --git a/static/.gitkeep b/static/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/test/.eslintrc b/test/.eslintrc new file mode 100644 index 00000000..3f26d66a --- /dev/null +++ b/test/.eslintrc @@ -0,0 +1,11 @@ +{ + "env": { + "mocha": true + }, + "globals": { + "assert": true, + "expect": true, + "should": true, + "__static": true + } +} diff --git a/test/e2e/index.js b/test/e2e/index.js new file mode 100644 index 00000000..af4b0e7e --- /dev/null +++ b/test/e2e/index.js @@ -0,0 +1,18 @@ +'use strict' + +// Set BABEL_ENV to use proper env config +process.env.BABEL_ENV = 'test' + +// Enable use of ES6+ on required files +require('babel-register')({ + ignore: /node_modules/ +}) + +// Attach Chai APIs to global scope +const { expect, should, assert } = require('chai') +global.expect = expect +global.should = should +global.assert = assert + +// Require all JS files in `./specs` for Mocha to consume +require('require-dir')('./specs') diff --git a/test/e2e/specs/Launch.spec.js b/test/e2e/specs/Launch.spec.js new file mode 100644 index 00000000..f9f95339 --- /dev/null +++ b/test/e2e/specs/Launch.spec.js @@ -0,0 +1,13 @@ +import utils from '../utils' + +describe('Launch', function () { + beforeEach(utils.beforeEach) + afterEach(utils.afterEach) + + it('shows the proper application title', function () { + return this.app.client.getTitle() + .then(title => { + expect(title).to.equal('aganippe') + }) + }) +}) diff --git a/test/e2e/utils.js b/test/e2e/utils.js new file mode 100644 index 00000000..7d4e0dad --- /dev/null +++ b/test/e2e/utils.js @@ -0,0 +1,23 @@ +import electron from 'electron' +import { Application } from 'spectron' + +export default { + afterEach () { + this.timeout(10000) + + if (this.app && this.app.isRunning()) { + return this.app.stop() + } + }, + beforeEach () { + this.timeout(10000) + this.app = new Application({ + path: electron, + args: ['dist/electron/main.js'], + startTimeout: 10000, + waitTimeout: 10000 + }) + + return this.app.start() + } +} diff --git a/test/unit/index.js b/test/unit/index.js new file mode 100644 index 00000000..f07be986 --- /dev/null +++ b/test/unit/index.js @@ -0,0 +1,13 @@ +import Vue from 'vue' +Vue.config.devtools = false +Vue.config.productionTip = false + +// require all test files (files that ends with .spec.js) +const testsContext = require.context('./specs', true, /\.spec$/) +testsContext.keys().forEach(testsContext) + +// require all src files except main.js for coverage. +// you can also change this to match only the subset of files that +// you want coverage for. +const srcContext = require.context('../../src/renderer', true, /^\.\/(?!main(\.js)?$)/) +srcContext.keys().forEach(srcContext) diff --git a/test/unit/karma.conf.js b/test/unit/karma.conf.js new file mode 100644 index 00000000..62040114 --- /dev/null +++ b/test/unit/karma.conf.js @@ -0,0 +1,62 @@ +'use strict' + +const path = require('path') +const merge = require('webpack-merge') +const webpack = require('webpack') + +const baseConfig = require('../../.electron-vue/webpack.renderer.config') +const projectRoot = path.resolve(__dirname, '../../src/renderer') + +// Set BABEL_ENV to use proper preset config +process.env.BABEL_ENV = 'test' + +let webpackConfig = merge(baseConfig, { + devtool: '#inline-source-map', + plugins: [ + new webpack.DefinePlugin({ + 'process.env.NODE_ENV': '"testing"' + }) + ] +}) + +// don't treat dependencies as externals +delete webpackConfig.entry +delete webpackConfig.externals +delete webpackConfig.output.libraryTarget + +// apply vue option to apply isparta-loader on js +webpackConfig.module.rules + .find(rule => rule.use.loader === 'vue-loader').use.options.loaders.js = 'babel-loader' + +module.exports = config => { + config.set({ + browsers: ['visibleElectron'], + client: { + useIframe: false + }, + coverageReporter: { + dir: './coverage', + reporters: [ + { type: 'lcov', subdir: '.' }, + { type: 'text-summary' } + ] + }, + customLaunchers: { + 'visibleElectron': { + base: 'Electron', + flags: ['--show'] + } + }, + frameworks: ['mocha', 'chai'], + files: ['./index.js'], + preprocessors: { + './index.js': ['webpack', 'sourcemap'] + }, + reporters: ['spec', 'coverage'], + singleRun: true, + webpack: webpackConfig, + webpackMiddleware: { + noInfo: true + } + }) +} diff --git a/test/unit/specs/LandingPage.spec.js b/test/unit/specs/LandingPage.spec.js new file mode 100644 index 00000000..7cea66ff --- /dev/null +++ b/test/unit/specs/LandingPage.spec.js @@ -0,0 +1,13 @@ +import Vue from 'vue' +import LandingPage from '@/components/LandingPage' + +describe('LandingPage.vue', () => { + it('should render correct contents', () => { + const vm = new Vue({ + el: document.createElement('div'), + render: h => h(LandingPage) + }).$mount() + + expect(vm.$el.querySelector('.title').textContent).to.contain('Welcome to your new project!') + }) +})