├── .gitignore
├── README.md
├── meta.js
└── template
├── .gitignore
├── README.md
├── app
├── electron.js
├── icons
│ ├── icon.bmp
│ ├── icon.html
│ ├── icon.icns
│ ├── icon.ico
│ └── icon.png
├── index.ejs
├── package.json
├── shell
│ ├── VS2015-Init.reg
│ └── powershell.reg
├── src
│ ├── App.vue
│ ├── components
│ │ ├── mainPage.vue
│ │ └── pages
│ │ │ ├── assets
│ │ │ ├── icons
│ │ │ │ └── ic_play.svg
│ │ │ └── style
│ │ │ │ └── main-style.scss
│ │ │ ├── fonts
│ │ │ ├── font.html
│ │ │ ├── icomoon.eot
│ │ │ ├── icomoon.svg
│ │ │ ├── icomoon.ttf
│ │ │ └── icomoon.woff
│ │ │ ├── helloPage.vue
│ │ │ └── mixins
│ │ │ └── locale.js
│ ├── locale
│ │ ├── format.js
│ │ ├── index.js
│ │ └── lang
│ │ │ ├── de.js
│ │ │ ├── en.js
│ │ │ ├── pt.js
│ │ │ └── zh-CN.js
│ ├── main.js
│ ├── routes.js
│ ├── sections
│ │ ├── forkJs.js
│ │ └── helloJs.js
│ └── vuex
│ │ └── store.js
└── update.js
├── config.js
├── package.json
├── tasks
├── install.js
├── mac
│ ├── background.png
│ └── installer.js
├── package.js
├── runner.js
├── util.js
└── win
│ ├── installer.js
│ ├── issConfig.json
│ └── issTemplate.js
└── webpack.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
3 | npm-debug.log.*
4 |
5 | .DS_Store
6 | .idea
7 | thumbs.db
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Overview
2 |
3 | > 一个基于 webpack 的 electron 代码模板
4 |
5 | * 包含支持 vue 框架示例
6 | * 使用 webpack 打包 vue 框架文件.
7 | * 包含编译 native addons 示例
8 | * 编译采用 tasks/install.js 中执行命令行方式解决, postinstall 使用 npm i --build-from-source 参数的添加是为了解决 amazonaws 被墙从而导致 node 文件无法下载, npm 超时问题.
9 | * 包含打包 native addons 示例
10 | * webpack 的 node-loader 模块, 在代码中将 node 文件识别为绝对路径, 无法将含有 native addons 的工程正确打包为可执行程序. 本例将 app/node_modules 也打包进 app.asar 中, 并将 require 代码写在 index.ejs 中 head 代码块中以此来解决打包问题.
11 | * 包含使用 fork 函数的示例
12 | * webpack 不识别 fork 函数, 进而无法对 fork 的文件进行打包, 本例采用多目标打包配置, 解决 fork 文件打包问题.
13 | * 包含一个多国语模块
14 | * 在 app/src/locale/lang 目录下可以添加任意语言对应翻译, vue 文件中包含示例.
15 | * 包含多平台文件打包示例
16 | * win
17 | * 打包生成程序为 exe 安装文件.
18 | * 依赖 [inno setup](http://www.jrsoftware.org/isinfo.php).
19 | * mac
20 | * 打包生成程序为 dmg 安装文件.
21 | * 打包生成程序为 zip 升级文件.
22 | * 包含多平台程序升级示例
23 | * electron 自带 autoUpdater 采用 Squirrel 框架, 不太友好, 故重构了一个较为简单的升级模块.
24 | * win
25 | * 生成一个 exe 安装包, 主程序请求升级并下载该安装包, 退出后执行 'update_v1.0.1.exe /silent /mergetasks=runapp,!desktopicon,!startmenuicon', 完成升级过程.
26 | * mac
27 | * 主程序请求升级并下载解压升级程序, 将解压文件通过 bash shell 直接覆盖原程序, 完成升级过程.
28 | * 包含两种调试模式
29 | * developmentHot 模式适合前期阶段, 支持热更新, 快速开发.
30 | * developmentPack 模式适合打包前测试阶段, 模拟打包之前的程序结构, 更快定位打包后可能出现的问题.
31 |
32 | ## Installation
33 |
34 | * node
35 | * npm
36 | * vue-cli
37 | * npm i vue-cli -g
38 |
39 |
40 | ## Build Setup
41 |
42 | ``` bash
43 | # init project
44 | vue init llwslc/electron-webpack my-project
45 |
46 | # install dependencies
47 | cd my-project
48 | npm i
49 |
50 | # run your app
51 | npm run dev
52 | ```
53 |
--------------------------------------------------------------------------------
/meta.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | module.exports = {
4 | prompts: {
5 | name: {
6 | type: 'editor',
7 | message: 'Application Name'
8 | },
9 | description: {
10 | type: 'editor',
11 | message: 'Project Description',
12 | default: 'An electron-webpack project'
13 | },
14 | companyname: {
15 | type: 'editor',
16 | message: 'Company Name',
17 | },
18 | rebuild: {
19 | type: 'confirm',
20 | message: 'Use Native Addons (rebuild flag)?',
21 | default: true
22 | },
23 | fork: {
24 | type: 'confirm',
25 | message: 'Use child_process.fork (fork flag)?',
26 | default: true
27 | },
28 | update: {
29 | type: 'confirm',
30 | message: 'Use update framework (update flag)?',
31 | default: true
32 | },
33 | installer: {
34 | type: 'confirm',
35 | message: 'Make installer (installer flag)?',
36 | default: true
37 | },
38 | },
39 | filters: {
40 | 'tasks/install.js': 'rebuild',
41 | 'app/src/sections/forkJs.js': 'fork',
42 | 'app/update.js': 'update',
43 | 'tasks/mac/*': 'installer',
44 | 'tasks/win/*': 'installer',
45 | },
46 | completeMessage: `---
47 |
48 | All set. More configurations can be made at \x1b[33m{{destDirName}}/config.js\x1b[0m.
49 |
50 | Next steps:
51 | 1.
52 | \x1B[32mcd {{destDirName}}\x1b[0m
53 | 2.
54 | \x1B[32mnpm i\x1b[0m
55 | 3.
56 | If \x1B[1mrebuild\x1b[0m flag is true. Need to be modified at \x1b[33m{{destDirName}}/tasks/install.js\x1b[0m.
57 | If \x1B[1mfork\x1b[0m flag is true. Need to be modified at \x1b[33m{{destDirName}}/app/src/sections/forkJs.js\x1b[0m.
58 | If \x1B[1mupdate\x1b[0m flag is true. Need to be modified at \x1b[33m{{destDirName}}/app/electron.js:57 (update.setFeedURL)\x1b[0m.
59 | If \x1B[1minstaller\x1b[0m flag is true. Need to be modified at \x1b[33m{{destDirName}}/tasks/win/vdprojConfig.json\x1b[0m.
60 | 4.
61 | \x1B[32mnpm run dev\x1b[0m`
62 | }
63 |
--------------------------------------------------------------------------------
/template/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | app/dist/*
3 | packages/*
4 |
5 | node_modules
6 | npm-debug.log
7 | npm-debug.log.*
8 |
9 | .DS_Store
10 | .idea
11 | thumbs.db
12 |
--------------------------------------------------------------------------------
/template/README.md:
--------------------------------------------------------------------------------
1 | # {{ name }}
2 |
3 | > {{ description }}
4 |
5 | ## Installation
6 |
7 | * node
8 | * npm
9 | {{#if rebuild}}
10 | * node-gyp
11 | * npm i node-gyp -g
12 | {{/if}}
13 |
14 | ## Build Setup
15 |
16 | ``` bash
17 | # install dependencies
18 | npm install
19 |
20 | # serve with hot reload at localhost:8080
21 | npm run dev
22 |
23 | # run packed project for development
24 | npm run pack
25 |
26 | # run electron without webpack for development
27 | npm run electron
28 |
29 | # build electron app for production
30 | npm run package
31 |
32 | # just run webpack for production
33 | npm run pack:just
34 |
35 | # just run electron packager for production
36 | npm run package:just
37 |
38 | # just run inno setup for production
39 | npm run installer:just
40 | ```
41 |
--------------------------------------------------------------------------------
/template/app/electron.js:
--------------------------------------------------------------------------------
1 |
2 | const electron = require('electron');
3 | const path = require('path');
4 | const app = electron.app;
5 | const dialog = electron.dialog;
6 | const BrowserWindow = electron.BrowserWindow;
7 |
8 | let mainWindow;
9 | let config = {};
10 |
11 | if (!!process.env.NODE_ENV)
12 | {
13 | // null
14 | }
15 | else
16 | {
17 | process.env.NODE_ENV = 'production';
18 | }
19 |
20 | if (process.env.NODE_ENV === 'developmentHot')
21 | {
22 | config = require('../config');
23 | config.url = `http://localhost:${config.port}`;
24 | }
25 | else
26 | {
27 | config.devtron = false;
28 | config.url = `file://${__dirname}/dist/index.html`;
29 | }
30 |
31 | function createWindow()
32 | {
33 | /**
34 | * Initial window options
35 | */
36 | mainWindow = new BrowserWindow({
37 | minHeight: 600,
38 | minWidth: 800
39 | });
40 |
41 | mainWindow.maximize();
42 |
43 | if (process.env.NODE_ENV.indexOf('development') !== -1)
44 | {
45 | BrowserWindow.addDevToolsExtension(path.join(__dirname, '../node_modules/devtron'));
46 | BrowserWindow.addDevToolsExtension(path.join(__dirname, '../node_modules/vue-devtools'));
47 |
48 | mainWindow.webContents.openDevTools();
49 | }
50 |
51 | if (process.env.NODE_ENV === 'production')
52 | {
53 | {{#if update}}
54 | const UpdateObj = require('./update');
55 | let update = new UpdateObj();
56 | update.setFeedURL('http://localhost');
57 | update.checkLocalUpdates();
58 | {{/if}}
59 | if (process.argv[1] === 'debug')
60 | {
61 | process.env.DEBUG = true;
62 | }
63 | }
64 |
65 | mainWindow.loadURL(config.url);
66 |
67 | mainWindow.on('closed', function ()
68 | {
69 | mainWindow = null;
70 | });
71 |
72 | console.log('mainWindow opened');
73 | }
74 |
75 | app.on('open-file', (e, path) =>
76 | {
77 | // dialog.showErrorBox('openFile', path);
78 | });
79 |
80 | app.once('ready', createWindow);
81 |
82 | app.on('window-all-closed', function ()
83 | {
84 | app.quit();
85 | });
86 |
87 | app.on('activate', function ()
88 | {
89 | if (mainWindow === null)
90 | {
91 | createWindow();
92 | }
93 | });
94 |
--------------------------------------------------------------------------------
/template/app/icons/icon.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/llwslc/electron-webpack/93a829abd54d1026aea8a76d7a2096e69f83654f/template/app/icons/icon.bmp
--------------------------------------------------------------------------------
/template/app/icons/icon.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | icon 转换
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/template/app/icons/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/llwslc/electron-webpack/93a829abd54d1026aea8a76d7a2096e69f83654f/template/app/icons/icon.icns
--------------------------------------------------------------------------------
/template/app/icons/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/llwslc/electron-webpack/93a829abd54d1026aea8a76d7a2096e69f83654f/template/app/icons/icon.ico
--------------------------------------------------------------------------------
/template/app/icons/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/llwslc/electron-webpack/93a829abd54d1026aea8a76d7a2096e69f83654f/template/app/icons/icon.png
--------------------------------------------------------------------------------
/template/app/index.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | <%= htmlWebpackPlugin.options.title %>
6 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/template/app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "{{ name }}",
3 | "description": "{{ description }}",
4 | "version": "0.0.0",
5 | "author": "{{ author }}",
6 | "main": "electron.js",
7 | "dependencies": {
8 | "async": "^2.1.4",
9 | "jquery": "^3.1.1",
10 | "vue": "^2.1.6",
11 | "vue-router": "^2.1.1",
12 | "vuex": "^2.1.1"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/template/app/shell/VS2015-Init.reg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/llwslc/electron-webpack/93a829abd54d1026aea8a76d7a2096e69f83654f/template/app/shell/VS2015-Init.reg
--------------------------------------------------------------------------------
/template/app/shell/powershell.reg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/llwslc/electron-webpack/93a829abd54d1026aea8a76d7a2096e69f83654f/template/app/shell/powershell.reg
--------------------------------------------------------------------------------
/template/app/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/template/app/src/components/mainPage.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
33 |
--------------------------------------------------------------------------------
/template/app/src/components/pages/assets/icons/ic_play.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/template/app/src/components/pages/assets/style/main-style.scss:
--------------------------------------------------------------------------------
1 | //////////////
2 | // reset //
3 | //////////////
4 |
5 | * {
6 | margin: 0;
7 | padding: 0;
8 | list-style: none;
9 | }
10 |
11 | html,
12 | body {
13 | height: 100%;
14 | background-color: #fff;
15 | overflow: hidden;
16 | }
17 |
18 | ///////////////
19 | // fonts //
20 | ///////////////
21 |
22 | body {
23 | padding-left: 36px;
24 | font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;
25 | }
26 |
27 | @font-face {
28 | font-family: 'icomoon';
29 | src: url('fonts/icomoon.eot');
30 | src: url('fonts/icomoon.eot#iefix') format('embedded-opentype'),
31 | url('fonts/icomoon.ttf') format('truetype'),
32 | url('fonts/icomoon.woff') format('woff'),
33 | url('fonts/icomoon.svg#icomoon') format('svg');
34 | font-weight: normal;
35 | font-style: normal;
36 | }
37 |
38 | [class^="icon-"], [class*=" icon-"] {
39 | /* use !important to prevent issues with browser extensions that change fonts */
40 | font-family: 'icomoon' !important;
41 | speak: none;
42 | font-style: normal;
43 | font-weight: normal;
44 | font-variant: normal;
45 | text-transform: none;
46 | line-height: 1;
47 | font-size: 18px;
48 |
49 | /* Better Font Rendering =========== */
50 | -webkit-font-smoothing: antialiased;
51 | -moz-osx-font-smoothing: grayscale;
52 | }
53 |
54 | .icon-mplay:before {
55 | content: "\e904";
56 | }
57 | .icon-mstop:before {
58 | content: "\e905";
59 | }
60 | .icon-available:before {
61 | content: "\e900";
62 | }
63 | .icon-occupied:before {
64 | content: "\e901";
65 | }
66 | .icon-pause:before {
67 | content: "\e902";
68 | }
69 | .icon-play:before {
70 | content: "\e903";
71 | }
72 |
73 |
--------------------------------------------------------------------------------
/template/app/src/components/pages/fonts/font.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | svg 转 font
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/template/app/src/components/pages/fonts/icomoon.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/llwslc/electron-webpack/93a829abd54d1026aea8a76d7a2096e69f83654f/template/app/src/components/pages/fonts/icomoon.eot
--------------------------------------------------------------------------------
/template/app/src/components/pages/fonts/icomoon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/template/app/src/components/pages/fonts/icomoon.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/llwslc/electron-webpack/93a829abd54d1026aea8a76d7a2096e69f83654f/template/app/src/components/pages/fonts/icomoon.ttf
--------------------------------------------------------------------------------
/template/app/src/components/pages/fonts/icomoon.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/llwslc/electron-webpack/93a829abd54d1026aea8a76d7a2096e69f83654f/template/app/src/components/pages/fonts/icomoon.woff
--------------------------------------------------------------------------------
/template/app/src/components/pages/helloPage.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | \{{ t('el.tree.emptyText') }}
4 |
5 | \{{ hello }}
6 |
7 |
8 |
9 |
53 |
54 |
57 |
--------------------------------------------------------------------------------
/template/app/src/components/pages/mixins/locale.js:
--------------------------------------------------------------------------------
1 | import { t } from '../../../locale';
2 |
3 | export default {
4 | methods: {
5 | t(...args) {
6 | return t.apply(this, args);
7 | }
8 | }
9 | };
10 |
--------------------------------------------------------------------------------
/template/app/src/locale/format.js:
--------------------------------------------------------------------------------
1 | /**
2 | * String format template
3 | * - Inspired:
4 | * https://github.com/Matt-Esch/string-template/index.js
5 | */
6 |
7 | const RE_NARGS = /(%|)\{([0-9a-zA-Z_]+)\}/g;
8 |
9 | export default function(Vue) {
10 | const { hasOwn } = Vue.util;
11 |
12 | /**
13 | * template
14 | *
15 | * @param {String} string
16 | * @param {Array} ...args
17 | * @return {String}
18 | */
19 |
20 | function template(string, ...args) {
21 | if (args.length === 1 && typeof args[0] === 'object') {
22 | args = args[0];
23 | }
24 |
25 | if (!args || !args.hasOwnProperty) {
26 | args = {};
27 | }
28 |
29 | return string.replace(RE_NARGS, (match, prefix, i, index) => {
30 | let result;
31 |
32 | if (string[index - 1] === '{' &&
33 | string[index + match.length] === '}') {
34 | return i;
35 | } else {
36 | result = hasOwn(args, i) ? args[i] : null;
37 | if (result === null || result === undefined) {
38 | return '';
39 | }
40 |
41 | return result;
42 | }
43 | });
44 | }
45 |
46 | return template;
47 | }
48 |
--------------------------------------------------------------------------------
/template/app/src/locale/index.js:
--------------------------------------------------------------------------------
1 |
2 | let lang = 'zh-CN';
3 |
4 | import Vue from 'vue';
5 | import Format from './format';
6 |
7 | const format = Format(Vue);
8 | let langObj = {};
9 |
10 | export const t = function(path, options) {
11 | const array = path.split('.');
12 | let current = langObj;
13 |
14 | for (var i = 0, j = array.length; i < j; i++) {
15 | var property = array[i];
16 | var value = current[property];
17 | if (i === j - 1) return format(value, options);
18 | if (!value) return '';
19 | current = value;
20 | }
21 | return '';
22 | };
23 |
24 | export const use = function(l) {
25 | lang = l || lang;
26 | langObj = require(`./lang/${lang}`).default;
27 | };
28 | export default { use, t };
29 |
--------------------------------------------------------------------------------
/template/app/src/locale/lang/de.js:
--------------------------------------------------------------------------------
1 | export default {
2 | el: {
3 | datepicker: {
4 | now: 'Jetzt',
5 | today: 'Heute',
6 | cancel: 'Abbrechen',
7 | clear: 'Leeren',
8 | confirm: 'OK',
9 | selectDate: 'Datum wählen',
10 | selectTime: 'Uhrzeit wählen',
11 | startDate: 'Startdatum',
12 | startTime: 'Startzeit',
13 | endDate: 'Enddatum',
14 | endTime: 'Endzeit',
15 | day: 'Tag',
16 | week: 'Woche',
17 | month: 'Monat',
18 | year: 'Jahr',
19 | month1: 'Januar',
20 | month2: 'Februar',
21 | month3: 'März',
22 | month4: 'April',
23 | month5: 'Mai',
24 | month6: 'Juni',
25 | month7: 'Juli',
26 | month8: 'August',
27 | month9: 'September',
28 | month10: 'Oktober',
29 | month11: 'November',
30 | month12: 'Dezember',
31 | weeks: {
32 | sun: 'So',
33 | mon: 'Mo',
34 | tue: 'Di',
35 | wed: 'Mi',
36 | thu: 'Do',
37 | fri: 'Fr',
38 | sat: 'Sa'
39 | },
40 | months: {
41 | jan: 'Jan',
42 | feb: 'Feb',
43 | mar: 'Mär',
44 | apr: 'Apr',
45 | may: 'Mai',
46 | jun: 'Jun',
47 | jul: 'Jul',
48 | aug: 'Aug',
49 | sep: 'Sep',
50 | oct: 'Okt',
51 | nov: 'Nov',
52 | dec: 'Dez'
53 | }
54 | },
55 | select: {
56 | loading: 'Lädt.',
57 | noMatch: 'Nichts gefunden.',
58 | noData: 'Keine Datei',
59 | placeholder: 'Datei wählen'
60 | },
61 | pagination: {
62 | goto: 'Gehe zu',
63 | pagesize: 'pro Seite',
64 | total: 'Gesamt {total}',
65 | pageClassifier: ''
66 | },
67 | messagebox: {
68 | confirm: 'OK',
69 | cancel: 'Abbrechen',
70 | error: 'Fehler'
71 | },
72 | upload: {
73 | delete: 'Löschen',
74 | preview: 'Vorschau',
75 | continue: 'Fortsetzen'
76 | },
77 | table: {
78 | emptyText: 'Keine Daten',
79 | confirmFilter: 'Anwenden',
80 | resetFilter: 'Zurücksetzen',
81 | clearFilter: 'Alles '
82 | },
83 | tree: {
84 | emptyText: 'Keine Daten'
85 | }
86 | }
87 | };
88 |
--------------------------------------------------------------------------------
/template/app/src/locale/lang/en.js:
--------------------------------------------------------------------------------
1 | export default {
2 | el: {
3 | datepicker: {
4 | now: 'Now',
5 | today: 'Today',
6 | cancel: 'Cancel',
7 | clear: 'Clear',
8 | confirm: 'OK',
9 | selectDate: 'Select date',
10 | selectTime: 'Select time',
11 | startDate: 'Start Date',
12 | startTime: 'Start Time',
13 | endDate: 'End Date',
14 | endTime: 'End Time',
15 | year: '',
16 | month1: 'January',
17 | month2: 'February',
18 | month3: 'March',
19 | month4: 'April',
20 | month5: 'May',
21 | month6: 'June',
22 | month7: 'July',
23 | month8: 'August',
24 | month9: 'September',
25 | month10: 'October',
26 | month11: 'November',
27 | month12: 'December',
28 | // week: 'week',
29 | weeks: {
30 | sun: 'Sun',
31 | mon: 'Mon',
32 | tue: 'Tue',
33 | wed: 'Wed',
34 | thu: 'Thu',
35 | fri: 'Fri',
36 | sat: 'Sat'
37 | },
38 | months: {
39 | jan: 'Jan',
40 | feb: 'Feb',
41 | mar: 'Mar',
42 | apr: 'Apr',
43 | may: 'May',
44 | jun: 'Jun',
45 | jul: 'Jul',
46 | aug: 'Aug',
47 | sep: 'Sep',
48 | oct: 'Oct',
49 | nov: 'Nov',
50 | dec: 'Dec'
51 | }
52 | },
53 | select: {
54 | loading: 'Loading',
55 | noMatch: 'No matching data',
56 | noData: 'No data',
57 | placeholder: 'Select'
58 | },
59 | pagination: {
60 | goto: 'Go to',
61 | pagesize: '/page',
62 | total: 'Total {total}',
63 | pageClassifier: ''
64 | },
65 | messagebox: {
66 | title: 'Message',
67 | confirm: 'OK',
68 | cancel: 'Cancel',
69 | error: 'Illegal input'
70 | },
71 | upload: {
72 | delete: 'Delete',
73 | preview: 'Preview',
74 | continue: 'Continue'
75 | },
76 | table: {
77 | emptyText: 'No Data',
78 | confirmFilter: 'Confirm',
79 | resetFilter: 'Reset',
80 | clearFilter: 'All'
81 | },
82 | tree: {
83 | emptyText: 'No Data'
84 | }
85 | }
86 | };
87 |
--------------------------------------------------------------------------------
/template/app/src/locale/lang/pt.js:
--------------------------------------------------------------------------------
1 | export default {
2 | el: {
3 | datepicker: {
4 | now: 'Agora',
5 | today: 'Hoje',
6 | cancel: 'Cancelar',
7 | clear: 'Limpar',
8 | confirm: 'Confirmar',
9 | selectDate: 'Selecione a data',
10 | selectTime: 'Selecione a hora',
11 | startDate: 'Data de inicio',
12 | startTime: 'Hora de inicio',
13 | endDate: 'Data de fim',
14 | endTime: 'Hora de fim',
15 | year: 'Ano',
16 | month1: 'Janeiro',
17 | month2: 'Fevereiro',
18 | month3: 'Março',
19 | month4: 'Abril',
20 | month5: 'Maio',
21 | month6: 'Junho',
22 | month7: 'Julho',
23 | month8: 'Agosto',
24 | month9: 'Setembro',
25 | month10: 'Outubro',
26 | month11: 'Novembro',
27 | month12: 'Dezembro',
28 | // week: 'semana',
29 | weeks: {
30 | sun: 'Dom',
31 | mon: 'Seg',
32 | tue: 'Ter',
33 | wed: 'Qua',
34 | thu: 'Qui',
35 | fri: 'Sex',
36 | sat: 'Sab'
37 | },
38 | months: {
39 | jan: 'Jan',
40 | feb: 'Fev',
41 | mar: 'Mar',
42 | apr: 'Abr',
43 | may: 'Mai',
44 | jun: 'Jun',
45 | jul: 'Jul',
46 | aug: 'Ago',
47 | sep: 'Set',
48 | oct: 'Out',
49 | nov: 'Nov',
50 | dec: 'Dez'
51 | }
52 | },
53 | select: {
54 | loading: 'A carregar',
55 | noMatch: 'Sem correspondência',
56 | noData: 'Sem dados',
57 | placeholder: 'Selecione'
58 | },
59 | pagination: {
60 | goto: 'Ir para',
61 | pagesize: '/pagina',
62 | total: 'Total {total}',
63 | pageClassifier: ''
64 | },
65 | messagebox: {
66 | confirm: 'Confirmar',
67 | cancel: 'Cancelar',
68 | error: 'Erro!'
69 | },
70 | upload: {
71 | delete: 'Apagar',
72 | preview: 'Previsualizar',
73 | continue: 'Continuar'
74 | },
75 | table: {
76 | emptyText: 'Sem dados',
77 | confirmFilter: 'Confirmar',
78 | resetFilter: 'Limpar',
79 | clearFilter: 'Todos'
80 | },
81 | tree: {
82 | emptyText: 'Sem dados'
83 | }
84 | }
85 | };
86 |
--------------------------------------------------------------------------------
/template/app/src/locale/lang/zh-CN.js:
--------------------------------------------------------------------------------
1 | export default {
2 | el: {
3 | datepicker: {
4 | now: '此刻',
5 | today: '今天',
6 | cancel: '取消',
7 | clear: '清空',
8 | confirm: '确定',
9 | selectDate: '选择日期',
10 | selectTime: '选择时间',
11 | startDate: '开始日期',
12 | startTime: '开始时间',
13 | endDate: '结束日期',
14 | endTime: '结束时间',
15 | year: '年',
16 | month1: '1 月',
17 | month2: '2 月',
18 | month3: '3 月',
19 | month4: '4 月',
20 | month5: '5 月',
21 | month6: '6 月',
22 | month7: '7 月',
23 | month8: '8 月',
24 | month9: '9 月',
25 | month10: '10 月',
26 | month11: '11 月',
27 | month12: '12 月',
28 | // week: '周次',
29 | weeks: {
30 | sun: '日',
31 | mon: '一',
32 | tue: '二',
33 | wed: '三',
34 | thu: '四',
35 | fri: '五',
36 | sat: '六'
37 | },
38 | months: {
39 | jan: '一月',
40 | feb: '二月',
41 | mar: '三月',
42 | apr: '四月',
43 | may: '五月',
44 | jun: '六月',
45 | jul: '七月',
46 | aug: '八月',
47 | sep: '九月',
48 | oct: '十月',
49 | nov: '十一月',
50 | dec: '十二月'
51 | }
52 | },
53 | select: {
54 | loading: '加载中',
55 | noMatch: '无匹配数据',
56 | noData: '无数据',
57 | placeholder: '请选择'
58 | },
59 | pagination: {
60 | goto: '前往',
61 | pagesize: '条/页',
62 | total: '共 {total} 条',
63 | pageClassifier: '页'
64 | },
65 | messagebox: {
66 | title: '提示',
67 | confirm: '确定',
68 | cancel: '取消',
69 | error: '输入的数据不合法!'
70 | },
71 | upload: {
72 | delete: '删除',
73 | preview: '查看图片',
74 | continue: '继续上传'
75 | },
76 | table: {
77 | emptyText: '暂无数据',
78 | confirmFilter: '筛选',
79 | resetFilter: '重置',
80 | clearFilter: '全部'
81 | },
82 | tree: {
83 | emptyText: '暂无数据'
84 | }
85 | }
86 | };
87 |
--------------------------------------------------------------------------------
/template/app/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import Router from 'vue-router';
3 |
4 | import App from './App';
5 | import routes from './routes';
6 | import Locale from './locale';
7 |
8 | Locale.use('zh-CN');
9 |
10 | Vue.use(Router);
11 | Vue.config.debug = true;
12 |
13 | const router = new Router({
14 | scrollBehavior: () => ({ y: 0 }),
15 | routes
16 | });
17 |
18 | new Vue({
19 | router,
20 | ...App
21 | }).$mount('#app');
22 |
--------------------------------------------------------------------------------
/template/app/src/routes.js:
--------------------------------------------------------------------------------
1 | export default [
2 | {
3 | path: '/',
4 | name: 'main-page',
5 | component: require('./components/mainPage'),
6 | },
7 | {
8 | path: '*',
9 | redirect: '/',
10 | },
11 | ];
12 |
--------------------------------------------------------------------------------
/template/app/src/sections/forkJs.js:
--------------------------------------------------------------------------------
1 |
2 | global.fs = require('fs');
3 | global.path = require('path');
4 | global.readline = require('readline');
5 |
6 | global.async = require('async');
7 |
8 | if (typeof __DEFINE_NATIVE_REQUIRE__ === 'undefined')
9 | {
10 | global.__non_webpack_require__ = require;
11 | }
12 |
13 | {{#if rebuild}}
14 | global.NativeAddonObj = __non_webpack_require__('NativeAddon.node');
15 | {{/if}}
16 |
17 | process.on("message", function (msg)
18 | {
19 | process.send(true);
20 | });
21 |
22 |
--------------------------------------------------------------------------------
/template/app/src/sections/helloJs.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | module.exports = {
4 | hello: 'word'
5 | }
6 |
--------------------------------------------------------------------------------
/template/app/src/vuex/store.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import Vuex from 'vuex';
3 |
4 | Vue.use(Vuex);
5 |
6 | export default new Vuex.Store({
7 | state: {
8 | // 配置
9 | config: {},
10 | },
11 | mutations: {
12 | init: state => {
13 | state.config = {};
14 | },
15 | },
16 | })
17 |
--------------------------------------------------------------------------------
/template/app/update.js:
--------------------------------------------------------------------------------
1 |
2 | const electron = require('electron');
3 | const app = electron.app;
4 | const dialog = electron.dialog;
5 | const http = require('http');
6 | const fs = require('fs');
7 | const path = require('path');
8 | const os = require('os');
9 | const childProcess = require('child_process');
10 | const async = require('async');
11 | const pkgInfo = require('./package.json');
12 |
13 | var UpdateObj = function ()
14 | {
15 | var self = this;
16 |
17 | self.name = pkgInfo.name;
18 | self.version = pkgInfo.version;
19 | self.platform = os.platform();
20 |
21 | self.feedURL = '';
22 | self.updateURL = '';
23 | self.updateVer = '';
24 | self.updateMD5 = '';
25 | self.updatePath = '';
26 | self.updateTmpPath = '';
27 |
28 | self.getVerNum = function (ver)
29 | {
30 | var verArr = ver.split('.');
31 | var num = 0;
32 | num += parseInt(verArr[0]) * 1000 * 1000;
33 | num += parseInt(verArr[1]) * 1000;
34 | num += parseInt(verArr[2]);
35 | return num;
36 | };
37 |
38 | self.cleanup = function (ver)
39 | {
40 | var needCleanup = false;
41 | var curVer = self.getVerNum(self.version);
42 | var fileVer = self.getVerNum(ver);
43 |
44 | if (fileVer <= curVer)
45 | {
46 | needCleanup = true;
47 | }
48 |
49 | return needCleanup;
50 | };
51 |
52 | self.updateButtons = [];
53 | self.updateMessage = '发现新版本, 点击"重启升级"后即可完成升级!';
54 |
55 | if (self.platform === 'win32')
56 | {
57 | self.updateButtons = ['重启升级', '取消'];
58 | self.updateResponse = 0;
59 |
60 | self.appDataDir = path.join(process.env.APPDATA, pkgInfo.name);
61 | }
62 | else if (self.platform === 'darwin')
63 | {
64 | self.updateButtons = ['取消', '重启升级'];
65 | self.updateResponse = 1;
66 |
67 | self.appDataDir = path.join(process.env.HOME, '/Library/Application Support', pkgInfo.name);
68 | }
69 | else
70 | {
71 | // null
72 | }
73 |
74 | if (!fs.existsSync(self.appDataDir))
75 | {
76 | fs.mkdirSync(self.appDataDir);
77 | }
78 |
79 | var files = fs.readdirSync(self.appDataDir);
80 | files.forEach(function (file)
81 | {
82 | var filePath = path.join(self.appDataDir, file);
83 |
84 | if (/^update_v([\d.]+)\.(exe|zip)$/.test(file))
85 | {
86 | var exeVersion = path.basename(file, path.extname(file)).split('_v')[1];
87 |
88 | if (self.cleanup(exeVersion))
89 | {
90 | fs.unlink(filePath, (err) => {});
91 | }
92 | else
93 | {
94 | self.updatePath = filePath;
95 | }
96 | }
97 |
98 | if (/^temp_v([\d.]+)\.(exe|zip)$/.test(file))
99 | {
100 | fs.unlink(filePath, (err) => {});
101 | }
102 | });
103 |
104 | self.setFeedURL = function (url)
105 | {
106 | self.feedURL = url;
107 | };
108 |
109 | self.getFeedURL = function ()
110 | {
111 | return self.feedURL;
112 | };
113 |
114 | self.checkLocalUpdates = function ()
115 | {
116 | if (fs.existsSync(self.updatePath))
117 | {
118 | dialog.showMessageBox({
119 | type: 'none',
120 | buttons: self.updateButtons,
121 | title: '发现新版本',
122 | message: self.updateMessage,
123 | }, function (response)
124 | {
125 | if (response == self.updateResponse)
126 | {
127 | self.quitAndInstall();
128 | }
129 | });
130 | }
131 | else
132 | {
133 | self.checkServerUpdates();
134 | }
135 | };
136 |
137 | self.checkServerUpdates = function ()
138 | {
139 | async.waterfall([
140 | function (cb)
141 | {
142 | var url = `${self.feedURL}/update?`;
143 | url += `platform=${self.platform}&`;
144 | url += `version=${self.version}&`;
145 | url += `app=${self.name}`;
146 |
147 | http.get(url, function (response)
148 | {
149 | var statusCode = response.statusCode;
150 | var contentType = response.headers['content-type'];
151 |
152 | if (statusCode != 200)
153 | {
154 | cb(statusCode, null);
155 | }
156 | else
157 | {
158 | if (!/^application\/json/.test(contentType))
159 | {
160 | cb(statusCode, null);
161 | }
162 | else
163 | {
164 | response.setEncoding('utf8');
165 | var rawData = '';
166 | response.on('data', (chunk) => rawData += chunk);
167 | response.on('end', function ()
168 | {
169 | try
170 | {
171 | var parsedData = JSON.parse(rawData);
172 | self.updateURL = parsedData.url;
173 | self.updateVer = parsedData.version;
174 | self.updateMD5 = parsedData.md5;
175 |
176 | cb(null, null);
177 | }
178 | catch (e)
179 | {
180 | cb(statusCode, null);
181 | }
182 | });
183 | }
184 | }
185 | }).on('error', function (err)
186 | {
187 | cb(err, null);
188 | });
189 | },
190 | function (res, cb)
191 | {
192 | http.get(self.updateURL, function (response)
193 | {
194 | var statusCode = response.statusCode;
195 | var contentType = response.headers['content-type'];
196 |
197 | if (statusCode != 200)
198 | {
199 | cb(statusCode, null);
200 | }
201 | else
202 | {
203 | var downloadFlag = false;
204 | if (/^application\/x-msdownload/.test(contentType))
205 | {
206 | self.updateTmpPath = path.join(self.appDataDir, `temp_v${self.updateVer}.exe`);
207 | self.updatePath = path.join(self.appDataDir, `update_v${self.updateVer}.exe`);
208 | downloadFlag = true;
209 | }
210 | else if (/^application\/zip/.test(contentType))
211 | {
212 | self.updateTmpPath = path.join(self.appDataDir, `temp_v${self.updateVer}.zip`);
213 | self.updatePath = path.join(self.appDataDir, `update_v${self.updateVer}.zip`);
214 | downloadFlag = true;
215 | }
216 | else
217 | {
218 | cb(statusCode, null);
219 | }
220 |
221 | if (downloadFlag)
222 | {
223 | response.pipe(fs.createWriteStream(self.updateTmpPath));
224 |
225 | response.on('end', function ()
226 | {
227 | fs.renameSync(self.updateTmpPath, self.updatePath);
228 | cb(null, null);
229 | });
230 | }
231 | }
232 | }).on('error', function (err)
233 | {
234 | cb(err, null);
235 | });
236 | }
237 | ], (err, res) => {});
238 | };
239 |
240 | self.quitAndInstall = function ()
241 | {
242 | var exec = childProcess.exec;
243 | if (self.platform === 'win32')
244 | {
245 | if (fs.existsSync(self.updatePath))
246 | {
247 | exec(`start ${self.updatePath} /silent /mergetasks=runapp,!desktopicon,!startmenuicon`, {encoding: 'binary'});
248 | app.exit(0);
249 | }
250 | else
251 | {
252 | dialog.showErrorBox('升级失败', '升级程序已损坏,请重新下载完整程序安装');
253 | }
254 | }
255 | else if (self.platform === 'darwin')
256 | {
257 | var unzipPath = path.join(process.argv[0], '../../..');
258 | var unzip = exec(`unzip -o '${self.updatePath}' -d '${unzipPath}'`, {encoding: 'binary'});
259 | unzip.on('exit', function ()
260 | {
261 | exec(`rm '${self.updatePath}'`);
262 | app.relaunch({args: process.argv.slice(1).concat(['--relaunch'])});
263 | app.exit(0);
264 | });
265 | }
266 | else
267 | {
268 | // null
269 | }
270 | };
271 | };
272 |
273 |
274 | module.exports = UpdateObj;
275 |
--------------------------------------------------------------------------------
/template/config.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const path = require('path')
4 |
5 | let config = {
6 | // Name of electron app
7 | // Will be used in production builds
8 | name: '{{ name }}',
9 |
10 | // webpack-dev-server port
11 | port: 8080,
12 |
13 | // electron-packager options
14 | building: {
15 | arch: 'x64',
16 | asar: true,
17 | dir: path.join(__dirname, 'app'),
18 | icon: path.join(__dirname, 'app/icons/icon'),
19 | ignore: /^\/(shell|src|index\.ejs)/,
20 | prune: false,
21 | overwrite: true,
22 | win32metadata: {
23 | {{#if companyname}}
24 | CompanyName: '{{ companyname }}',
25 | {{/if}}
26 | FileDescription: '{{ name }}',
27 | },
28 | platform: require('os').platform(),
29 | out: path.join(__dirname, 'packages'),
30 | extendInfo: {
31 | CFBundleDocumentTypes: [
32 | {
33 | CFBundleTypeExtensions: ['dat']
34 | }]
35 | },
36 | }
37 | }
38 |
39 | config.building.name = config.name
40 |
41 | module.exports = config
42 |
--------------------------------------------------------------------------------
/template/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "{{ name }}",
3 | "description": "{{ description }}",
4 | "version": "0.0.0",
5 | "author": "llwslc ",
6 | "scripts": {
7 | {{#if rebuild}}
8 | "postinstall": "cd app && npm i --build-from-source && node ../tasks/install.js",
9 | {{else}}
10 | "postinstall": "cd app && npm i",
11 | {{/if}}
12 | "dev": "node tasks/runner.js dev",
13 | "pack": "node tasks/runner.js pack",
14 | "electron": "node tasks/runner.js electron",
15 | "package": "node tasks/package.js",
16 | "pack:just": "node tasks/package.js pack:just",
17 | {{#if installer}}
18 | "installer:just": "node tasks/package.js installer:just",
19 | {{/if}}
20 | "package:just": "node tasks/package.js package:just"
21 | },
22 | "devDependencies": {
23 | "async": "^2.1.4",
24 | "babel-core": "^6.21.0",
25 | "babel-loader": "^6.2.10",
26 | "babel-plugin-transform-runtime": "^6.15.0",
27 | "babel-preset-es2015": "^6.18.0",
28 | "babel-preset-stage-0": "^6.16.0",
29 | "cross-env": "^3.1.3",
30 | "css-loader": "^0.26.1",
31 | "devtron": "^1.4.0",
32 | "electron": "^1.4.13",
33 | "electron-packager": "^8.4.0",
34 | "extract-text-webpack-plugin": "^2.0.0-beta.4",
35 | "file-loader": "^0.9.0",
36 | "html-webpack-plugin": "^2.24.1",
37 | "iconv-lite": "^0.4.15",
38 | {{#if installer}}
39 | "innosetup-compiler": "^5.5.9",
40 | {{/if}}
41 | "json-loader": "^0.5.4",
42 | "node-sass": "^4.1.1",
43 | "sass-loader": "^4.1.0",
44 | "style-loader": "^0.13.1",
45 | "tree-kill": "^1.1.0",
46 | "url-loader": "^0.5.7",
47 | "vue": "^2.1.6",
48 | "vue-devtools": "^2.3.1-beta1",
49 | "vue-loader": "^10.0.2",
50 | "vue-style-loader": "^1.0.0",
51 | "vue-template-compiler": "^2.1.6",
52 | "webpack": "^2.2.0",
53 | "webpack-dev-server": "^2.2.0"
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/template/tasks/install.js:
--------------------------------------------------------------------------------
1 |
2 | const exec = require('child_process').exec;
3 | const async = require('async');
4 | const util = require('./util');
5 |
6 | var electronVersion = ``;
7 | var buildAddons = `node-gyp rebuild --target=${electronVersion} --arch=x64 --dist-url=https://atom.io/download/atom-shell`;
8 |
9 | var npmExecAsync = function (cmd, cb)
10 | {
11 | util.execAsync('npm', cmd, util.BLUE, cb);
12 | };
13 |
14 | async.waterfall([
15 | function (cb)
16 | {
17 | exec(`cd .. && electron -v`, (error, stdout, stderr) =>
18 | {
19 | if (error)
20 | {
21 | cb(error, null);
22 | }
23 | else
24 | {
25 | electronVersion = stdout.match(/\d+.\d+.\d+/g);
26 | buildAddons = `node-gyp rebuild --target=${electronVersion[0]} --arch=x64 --dist-url=https://atom.io/download/atom-shell`;
27 | cb(null, null);
28 | }
29 | });
30 | },
31 | function (res, cb)
32 | {
33 | npmExecAsync(`cd node_modules/NativeAddonPackageName && ${buildAddons}`, cb);
34 | },
35 | function (res, cb)
36 | {
37 | npmExecAsync(`cd ../../NativeAddonProjectDir && ${buildAddons}`, cb);
38 | },
39 | function (res, cb)
40 | {
41 | util.copyAsync(`../../NativeAddonProjectDir/build/Release/NativeAddonProject.node`, `node_modules/NativeAddonProject.node`, cb);
42 | },
43 | ], function (err, res)
44 | {
45 | if (err)
46 | {
47 | util.colFormat(err, util.RED);
48 | }
49 | else
50 | {
51 | util.colFormat(`success install`);
52 | }
53 | });
54 |
--------------------------------------------------------------------------------
/template/tasks/mac/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/llwslc/electron-webpack/93a829abd54d1026aea8a76d7a2096e69f83654f/template/tasks/mac/background.png
--------------------------------------------------------------------------------
/template/tasks/mac/installer.js:
--------------------------------------------------------------------------------
1 |
2 | const fs = require('fs');
3 | const os = require('os');
4 | const platform = os.platform();
5 | const arch = os.arch();
6 | const async = require('async');
7 | const util = require('../util');
8 | const pkgInfo = require('../../app/package.json');
9 |
10 |
11 | var installer = function ()
12 | {
13 | var self = this;
14 |
15 |
16 | self.appFileName = `${pkgInfo.name}-${platform}-${arch}`;
17 | self.appFilePath = `./packages/${self.appFileName}`;
18 | self.appContentsPath = `${self.appFilePath}/${pkgInfo.name}.app`;
19 | self.targetName = `${self.appFileName}-v${pkgInfo.version}`;
20 |
21 | self.createDmg = function (callback)
22 | {
23 | util.colFormat('Create dmg...\n');
24 |
25 | var rwDmgPath = `${self.appFilePath}/../rw.dmg`;
26 | var ultDmgPath = `${self.appFilePath}/../${self.targetName}.dmg`;
27 | var volumesPath = `/Volumes/${pkgInfo.name}`;
28 |
29 | var dmgExecAsync = function (cmd, cb)
30 | {
31 | util.execAsync('dmg', cmd, util.BLUE, cb);
32 | }
33 |
34 | async.waterfall([
35 | function (cb)
36 | {
37 | dmgExecAsync(`rm -f ${self.appFilePath}/LICENSE`, cb);
38 | },
39 | function (res, cb)
40 | {
41 | dmgExecAsync(`rm -f ${self.appFilePath}/LICENSES.chromium.html`, cb);
42 | },
43 | function (res, cb)
44 | {
45 | dmgExecAsync(`rm -f ${self.appFilePath}/version`, cb);
46 | },
47 | function (res, cb)
48 | {
49 | dmgExecAsync(`rm -f ${self.appFilePath}/Applications && ln -s /Applications ${self.appFilePath}/Applications`, cb);
50 | },
51 | function (res, cb)
52 | {
53 | if (!fs.existsSync(`${self.appFilePath}/.background`))
54 | {
55 | fs.mkdirSync(`${self.appFilePath}/.background`);
56 | }
57 | cb (null, null);
58 | },
59 | function (res, cb)
60 | {
61 | util.copyAsync(`./tasks/mac/background.png`, `${self.appFilePath}/.background/background.png`, cb);
62 | },
63 | function (res, cb)
64 | {
65 | util.copyAsync(`./app/icons/icon.icns`, `${self.appFilePath}/.volumeIcon.icns`, cb);
66 | },
67 | function (res, cb)
68 | {
69 | dmgExecAsync(`rm -f ${rwDmgPath} && rm -f ${ultDmgPath}`, cb);
70 | },
71 | function (res, cb)
72 | {
73 | dmgExecAsync(`hdiutil create -srcfolder ${self.appFilePath} -volname ${pkgInfo.name} -fs HFS+ -fsargs "-c c=64,a=16,e=16" -format UDRW ${rwDmgPath}`, cb);
74 | },
75 | function (res, cb)
76 | {
77 | dmgExecAsync(`(test -d ${volumesPath} && hdiutil detach ${volumesPath}) || :`, cb);
78 | },
79 | function (res, cb)
80 | {
81 | dmgExecAsync(`hdiutil attach -readwrite -noverify -noautoopen ${rwDmgPath}`, cb);
82 | },
83 | function (res, cb)
84 | {
85 | dmgExecAsync(`SetFile -c icnC ${volumesPath}/.volumeIcon.icns`, cb);
86 | },
87 | function (res, cb)
88 | {
89 | dmgExecAsync(`SetFile -a C ${volumesPath}`, cb);
90 | },
91 | function (res, cb)
92 | {
93 | var bgWidth = 550;
94 | var bgHeigth = 310;
95 | var appX = 143;
96 | var appY = 157;
97 | var dropX = 407;
98 | var dropY = 157;
99 | var iconSize = 128;
100 | var textSize = 16;
101 |
102 | var AppleScriptStr = `
103 | tell application "Finder"
104 | tell disk "${pkgInfo.name}"
105 |
106 | open
107 |
108 | set titleHeight to 20
109 | set windowWidth to ${bgWidth}
110 | set windowHeight to ${bgHeigth}
111 |
112 | tell container window
113 | set current view to icon view
114 | set toolbar visible to false
115 | set statusbar visible to false
116 | set the bounds to {0, 0, windowWidth, (windowHeight + titleHeight)}
117 | set position of every item to {0, windowHeight * 2}
118 | set position of item "${pkgInfo.name}.app" to {${appX}, ${appY}}
119 | set position of item "Applications" to {${dropX}, ${dropY}}
120 | end tell
121 |
122 | set opts to the icon view options of container window
123 | tell opts
124 | set icon size to ${iconSize}
125 | set text size to ${textSize}
126 | set arrangement to not arranged
127 | end tell
128 |
129 | set background picture of opts to file ".background:background.png"
130 |
131 | close
132 |
133 | --give the finder some time to write the .DS_Store file
134 | delay 3
135 |
136 | set dsStore to "${volumesPath}/.DS_STORE"
137 | set waitTime to 0
138 | set ejectMe to false
139 | repeat while ejectMe is false
140 | delay 1
141 | set waitTime to waitTime + 1
142 |
143 | if (do shell script "[ -f " & dsStore & " ]; echo $?") = "0" then set ejectMe to true
144 | end repeat
145 | log "waited " & waitTime & " seconds for .DS_STORE to be created."
146 |
147 | end tell
148 | end tell`;
149 | dmgExecAsync(`osascript -e '${AppleScriptStr}'`, cb);
150 | },
151 | function (res, cb)
152 | {
153 | dmgExecAsync(`hdiutil detach ${volumesPath}`, cb);
154 | },
155 | function (res, cb)
156 | {
157 | dmgExecAsync(`hdiutil convert ${rwDmgPath} -format UDZO -imagekey zlib-level=9 -o ${ultDmgPath}`, cb);
158 | },
159 | function (res, cb)
160 | {
161 | dmgExecAsync(`rm -f ${rwDmgPath}`, cb);
162 | },
163 | ], function (err, res)
164 | {
165 | callback(err, null);
166 | });
167 | };
168 |
169 | self.createZip = function (callback)
170 | {
171 | util.colFormat('');
172 | util.colFormat('Create zip...\n');
173 |
174 | var zipFilePath = `../../${self.targetName}-update.zip`;
175 |
176 | util.execAsync(`zip`, `cd ${self.appContentsPath} && zip -ry ${zipFilePath} *`, util.YELLOW, function (err, res)
177 | {
178 | util.colFormat('');
179 | util.logFormat('zip', 'DONE', util.YELLOW);
180 | util.colFormat('');
181 |
182 | callback(err, null);
183 | });
184 | };
185 |
186 | self.create = function ()
187 | {
188 | if (process.argv[2] == 'package:just')
189 | {
190 | return;
191 | }
192 |
193 | async.waterfall([
194 | function (cb)
195 | {
196 | self.createDmg(cb);
197 | },
198 | function (res, cb)
199 | {
200 | self.createZip(cb);
201 | },
202 | ], function (err, res)
203 | {
204 | if (err)
205 | {
206 | util.colFormat(err, util.RED);
207 | }
208 | else
209 | {
210 | util.colFormat(`Success create mac installer`);
211 | }
212 | });
213 | };
214 | };
215 |
216 |
217 | module.exports = installer;
218 |
--------------------------------------------------------------------------------
/template/tasks/package.js:
--------------------------------------------------------------------------------
1 |
2 | const exec = require('child_process').exec;
3 | const os = require('os');
4 | const platform = os.platform();
5 | const fs = require('fs');
6 | const path = require('path');
7 | const packager = require('electron-packager');
8 | const util = require('./util');
9 |
10 | var delDirCmd = '';
11 | {{#if installer}}
12 | var installer = '';
13 | {{/if}}
14 |
15 | if (platform === 'win32')
16 | {
17 | delDirCmd = 'rmdir /s/q';
18 | {{#if installer}}
19 | installer = require('./win/installer.js');
20 | {{/if}}
21 | }
22 | else if (platform === 'darwin')
23 | {
24 | delDirCmd = 'rm -rf';
25 | {{#if installer}}
26 | installer = require('./mac/installer.js');
27 | {{/if}}
28 | }
29 | else
30 | {
31 | util.colFormat('Unable to determine the current operating system...\n', util.RED);
32 | return;
33 | }
34 |
35 | /**
36 | * Delete dir
37 | */
38 | var del = function (mPath, cb)
39 | {
40 | mPath = path.resolve(__dirname, mPath)
41 | if (fs.existsSync(mPath))
42 | {
43 | if (!fs.statSync(mPath).isDirectory())
44 | {
45 | util.colFormat(`${mPath} not directiry!`, util.RED);
46 | return;
47 | }
48 | }
49 | else
50 | {
51 | cb();
52 | return;
53 | }
54 |
55 | util.colFormat(`Delete ${mPath} dir...\n`);
56 |
57 | util.execAsync(`delDir`, `${delDirCmd} ${mPath}`, util.BLUE, function (err, res)
58 | {
59 | if (!!err)
60 | {
61 | util.colFormat(`try run!`, util.RED);
62 | return;
63 | }
64 | else
65 | {
66 | cb();
67 | }
68 | });
69 | };
70 |
71 | /**
72 | * Build webpack in production
73 | */
74 | var pack = function ()
75 | {
76 | del('../app/dist', function ()
77 | {
78 | util.execAsync(`webpack`, `${util.rlsEnv} webpack --progress --colors --hide-modules`, util.YELLOW, (err, res) => packageApp());
79 | });
80 | };
81 |
82 | /**
83 | * Use electron-packager to package electron app
84 | */
85 | var packageApp = function ()
86 | {
87 | if (process.argv[2] == 'pack:just')
88 | {
89 | return;
90 | }
91 |
92 | exec('set ELECTRON_MIRROR=https://npm.taobao.org/mirrors/electron/');
93 |
94 | del('../packages', function ()
95 | {
96 | let options = require('../config').building;
97 |
98 | util.colFormat('Building electron app...\n');
99 | packager(options, function (err, appPaths)
100 | {
101 | if (err)
102 | {
103 | util.errFormat('electron-packager', 'Error from `electron-packager` when building app...', util.BLUE);
104 | util.errFormat('electron-packager', err, util.BLUE);
105 | }
106 | else
107 | {
108 | util.colFormat('');
109 | util.logFormat('electron-packager', 'Build successful!', util.BLUE);
110 | util.colFormat('');
111 | {{#if installer}}
112 | if (process.argv[2] == 'package:just')
113 | {
114 | return;
115 | }
116 |
117 | new installer().create();
118 | {{/if}}
119 | }
120 | });
121 | });
122 | };
123 |
124 | if (process.argv[2] == 'pack:just')
125 | {
126 | pack();
127 | }
128 | else if (process.argv[2] == 'package:just')
129 | {
130 | packageApp();
131 | }
132 | {{#if installer}}
133 | else if (process.argv[2] == 'installer:just')
134 | {
135 | new installer().create();
136 | }
137 | {{/if}}
138 | else
139 | {
140 | pack();
141 | }
142 |
--------------------------------------------------------------------------------
/template/tasks/runner.js:
--------------------------------------------------------------------------------
1 |
2 | const config = require('../config');
3 | const exec = require('child_process').exec;
4 | const treeKill = require('tree-kill');
5 | const util = require('./util');
6 |
7 | let isElectronOpen = false;
8 |
9 | let children = [];
10 |
11 | var run = function (command, color, name)
12 | {
13 | let child = exec(command);
14 |
15 | child.stdout.on('data', data =>
16 | {
17 | util.logFormat(name, data, color);
18 |
19 | if (/Compiled successfully/g.test(data.toString()) && !isElectronOpen)
20 | {
21 | util.colFormat(`Starting electron...\n`, util.BLUE);
22 | run(`${util.hotEnv} electron app/electron.js`, util.BLUE, 'electron');
23 | isElectronOpen = true;
24 | }
25 | });
26 |
27 | child.stderr.on('data', data => util.errFormat(name, data, color));
28 | child.on('exit', code => exit(code));
29 |
30 | children.push(child);
31 | };
32 |
33 | var exit = function (code)
34 | {
35 | if (process.argv[2] == 'pack')
36 | {
37 | if (!isElectronOpen)
38 | {
39 | util.colFormat(`Starting electron...\n`, util.BLUE);
40 | run(`${util.packEnv} electron app/electron.js`, util.BLUE, 'electron');
41 | isElectronOpen = true;
42 | }
43 | else
44 | {
45 | children.forEach(child =>
46 | {
47 | treeKill(child.pid);
48 | });
49 | }
50 | }
51 | else
52 | {
53 | children.forEach(child =>
54 | {
55 | treeKill(child.pid);
56 | });
57 | }
58 | };
59 |
60 | if (process.argv[2] == 'dev')
61 | {
62 | util.colFormat(`Starting webpack-dev-server...\n`, util.YELLOW);
63 | run(`${util.hotEnv} webpack-dev-server --inline --hot --colors --port ${config.port} --content-base app/dist`, util.YELLOW, 'webpack');
64 | }
65 | else if (process.argv[2] == 'pack')
66 | {
67 | util.colFormat(`Starting webpack...\n`, util.YELLOW);
68 | run(`${util.packEnv} webpack --progress --colors --hide-modules`, util.YELLOW, 'webpack');
69 | }
70 | else if (process.argv[2] == 'electron')
71 | {
72 | util.colFormat(`Starting electron...\n`, util.BLUE);
73 | run(`${util.packEnv} electron app/electron.js`, util.BLUE, 'electron');
74 | isElectronOpen = true;
75 | }
76 | else
77 | {
78 | util.colFormat(`Nothing to do...\n`, util.YELLOW);
79 | }
80 |
--------------------------------------------------------------------------------
/template/tasks/util.js:
--------------------------------------------------------------------------------
1 |
2 | const exec = require('child_process').exec;
3 | const fs = require('fs');
4 | const platform = require('os').platform();
5 | const iconv = require('iconv-lite');
6 |
7 | let styles = {
8 | bold : ['\x1B[1m', '\x1B[22m'],
9 | italic : ['\x1B[3m', '\x1B[23m'],
10 | underline : ['\x1B[4m', '\x1B[24m'],
11 | inverse : ['\x1B[7m', '\x1B[27m'],
12 | black : ['\x1B[30m', '\x1B[39m'],
13 | red : ['\x1B[31m', '\x1B[39m'],
14 | green : ['\x1B[32m', '\x1B[39m'],
15 | yellow : ['\x1B[33m', '\x1B[39m'],
16 | blue : ['\x1B[34m', '\x1B[39m'],
17 | magenta : ['\x1B[35m', '\x1B[39m'],
18 | cyan : ['\x1B[36m', '\x1B[39m'],
19 | white : ['\x1B[37m', '\x1B[39m'],
20 | redBG : ['\x1B[41m', '\x1B[49m'],
21 | greenBG : ['\x1B[42m', '\x1B[49m'],
22 | yellowBG : ['\x1B[43m', '\x1B[49m'],
23 | blueBG : ['\x1B[44m', '\x1B[49m'],
24 | magentaBG : ['\x1B[45m', '\x1B[49m'],
25 | cyanBG : ['\x1B[46m', '\x1B[49m'],
26 | whiteBG : ['\x1B[47m', '\x1B[49m'],
27 | grey : ['\x1B[90m', '\x1B[39m'],
28 | end : ['\x1b[0m']
29 | };
30 |
31 | let RED = styles.red[0];
32 | let YELLOW = styles.yellow[0];
33 | let BLUE = styles.blue[0];
34 | let RED_BG = styles.redBG[0];
35 | let END = styles.end[0];
36 |
37 | var Encoding = 'utf8';
38 | if (platform === 'win32')
39 | {
40 | Encoding = 'GBK';
41 | }
42 |
43 | let hotEnv = 'cross-env NODE_ENV=developmentHot';
44 | let packEnv = 'cross-env NODE_ENV=developmentPack';
45 | let rlsEnv = 'cross-env NODE_ENV=production';
46 |
47 | var repeat = function (str, times)
48 | {
49 | return (new Array(times + 1)).join(str);
50 | };
51 |
52 | var format = function (pre, data, col)
53 | {
54 | if (!!!col) col = YELLOW;
55 |
56 | data = data.replace(/\n$/g, '');
57 | data = data.replace(/\x08/g, '');
58 | data = data.replace(/^\x20+/g, '');
59 | data = data.replace(/\n/g, '\n' + repeat(' ', pre.length + 2));
60 | // for webpack compiling log
61 | if (new RegExp("^webpack").test(pre))
62 | {
63 | data = data.replace(/([0-9]+%)/g, '\n' + repeat(' ', pre.length + 2) + '$1');
64 | data = data.replace(/^\n\x20+/g, '');
65 | }
66 |
67 | var decodedBody = iconv.decode(Buffer(data, 'binary'), Encoding);
68 |
69 | return decodedBody;
70 | };
71 |
72 | var logFormat = function (pre, data, col)
73 | {
74 | var log = format(pre, data, col);
75 | if (!!log)
76 | {
77 | console.log(`${col}${pre}${END} ${log}`);
78 | }
79 | };
80 |
81 | var errFormat = function (pre, data, col)
82 | {
83 | var errPre = pre + ' !!!';
84 | var log = format(errPre, data, col);
85 | if (!!log)
86 | {
87 | console.log(`${col}${pre}${END} ${RED_BG}!!!${END} ${log}`);
88 | }
89 | };
90 |
91 | var colFormat = function (data, col)
92 | {
93 | if (!!!col) col = END;
94 | var decodedBody = iconv.decode(Buffer(data, 'binary'), Encoding);
95 | console.log(`${col}${decodedBody}${END}`);
96 | };
97 |
98 | var execAsync = function (pre, cmd, col, cb)
99 | {
100 | let child = exec(cmd, {encoding: 'binary'});
101 | child.stdout.on('data', data => logFormat(pre, data, col));
102 | child.stderr.on('data', data => errFormat(pre, data, col));
103 | child.on('exit', code => {
104 | if (code != 0)
105 | {
106 | cb(`code : ${code}\nerror cmd: ${cmd}`, null);
107 | }
108 | else
109 | {
110 | cb(null, null);
111 | }
112 | });
113 | };
114 |
115 | var copyAsync = function (source, target, cb)
116 | {
117 | var writer = fs.createWriteStream(target);
118 | var reader = fs.createReadStream(source);
119 | reader.pipe(writer);
120 | writer.on(`finish`, () => {
121 | colFormat(`copy ${source} to ${target} finish`);
122 | cb(null, null);
123 | });
124 | };
125 |
126 |
127 | module.exports = { RED, YELLOW, BLUE, logFormat, errFormat, colFormat, execAsync, copyAsync, hotEnv, packEnv, rlsEnv };
128 |
--------------------------------------------------------------------------------
/template/tasks/win/installer.js:
--------------------------------------------------------------------------------
1 |
2 | const path = require('path');
3 | const os = require('os');
4 | const platform = os.platform();
5 | const arch = os.arch();
6 | const inno = require(`innosetup-compiler`);
7 | const issTemplate = require(`./issTemplate`);
8 | const util = require(`../util`);
9 | const pkgInfo = require('../../app/package.json');
10 |
11 |
12 | var installer = function ()
13 | {
14 | var self = this;
15 |
16 | self.appFileName = `${pkgInfo.name}-${platform}-${arch}`;
17 | self.appIssPath = `.\\packages\\${self.appFileName}.iss`;
18 |
19 | self.create = function ()
20 | {
21 | util.colFormat('Create iis...\n');
22 | new issTemplate(self.appFileName, self.appIssPath);
23 |
24 | inno(self.appIssPath, {}, function (err)
25 | {
26 | if (err)
27 | {
28 | util.colFormat(err, util.RED);
29 | }
30 | else
31 | {
32 | util.colFormat(`Success create win installer`);
33 | }
34 | });
35 | }
36 | };
37 |
38 |
39 | module.exports = installer;
40 |
--------------------------------------------------------------------------------
/template/tasks/win/issConfig.json:
--------------------------------------------------------------------------------
1 |
2 | {
3 | "AppId": "00000000-0000-0000-0000-000000000000",
4 | {{#if companyname}}
5 | "AppPublisher": "{{ companyname }}",
6 | {{else}}
7 | "AppPublisher": "Github",
8 | {{/if}}
9 | "AppSupportPhone": "010-88888888",
10 | "AppPublisherURL": "http://localhost",
11 | "AppSupportURL": "http://localhost",
12 | "AppUpdatesURL": "http://localhost"
13 | }
--------------------------------------------------------------------------------
/template/tasks/win/issTemplate.js:
--------------------------------------------------------------------------------
1 |
2 | const fs = require(`fs`);
3 | const pkgInfo = require('../../app/package.json');
4 | const issConfig = require('./issConfig.json');
5 |
6 | issConfig.AppName = pkgInfo.name;
7 | issConfig.AppVersion = pkgInfo.version;
8 |
9 | var createIssFile = function (appFileName, appIssPath)
10 | {
11 | var self = this;
12 |
13 | var OutputDir = `..\\`;
14 | var SourceDir = `.\\${appFileName}`;
15 | var setupIco = `.\\app\\icons\\icon.ico`;
16 | var setupImg = `.\\app\\icons\\icon.bmp`;
17 |
18 | var iisStr = `
19 | [Setup]
20 | AppId=${issConfig.AppId}
21 | AppName=${issConfig.AppName}
22 | AppVersion=${issConfig.AppVersion}
23 | AppPublisher=${issConfig.AppPublisher}, Inc.
24 | AppSupportPhone=${issConfig.AppSupportPhone}
25 | AppPublisherURL=${issConfig.AppPublisherURL}
26 | AppSupportURL=${issConfig.AppSupportURL}
27 | AppUpdatesURL=${issConfig.AppUpdatesURL}
28 | DefaultDirName={pf}\\${issConfig.AppPublisher}\\${issConfig.AppName}
29 | DefaultGroupName=${issConfig.AppPublisher}
30 | OutputBaseFilename=${appFileName}-v${issConfig.AppVersion}
31 | AppMutex=${issConfig.AppName}
32 | OutputDir=${OutputDir}
33 | SourceDir=${SourceDir}
34 | Compression=lzma2
35 | SolidCompression=yes
36 | AppVerName=${issConfig.AppName}
37 | UninstallDisplayIcon={app}\\${issConfig.AppName}.exe
38 | ${fs.existsSync(setupIco) ? `SetupIconFile=..\\..\\` + setupIco : ``}
39 | ${fs.existsSync(setupImg) ? `WizardSmallImageFile=..\\..\\` + setupImg : ``}
40 | DisableProgramGroupPage=yes
41 | ArchitecturesAllowed=x64
42 | ArchitecturesInstallIn64BitMode=x64
43 |
44 | [Tasks]
45 | Name: desktopicon; Description: "Create a &desktop icon"; GroupDescription: "Additional icons:"
46 | Name: startmenuicon; Description: "Create a &startmenu icon"; GroupDescription: "Additional icons:"
47 | Name: runapp; Description: "no"; GroupDescription: "no"; Check: WizardSilent
48 |
49 | [Files]
50 | Source: "*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
51 |
52 | [Icons]
53 | Name: "{commondesktop}\\${issConfig.AppName}"; Filename: "{app}\\${issConfig.AppName}.exe"; Tasks: desktopicon
54 | Name: "{commonstartmenu}\\${issConfig.AppName}"; Filename: "{app}\\${issConfig.AppName}.exe"; Tasks: startmenuicon
55 |
56 | [Run]
57 | Filename: "{app}\\${issConfig.AppName}.exe"; Description: "Launch ${issConfig.AppName}"; Tasks: runapp; Flags: nowait postinstall; Check: WizardSilent
58 | Filename: "{app}\\${issConfig.AppName}.exe"; Description: "Launch ${issConfig.AppName}"; Flags: nowait postinstall; Check: WizardNotSilent
59 |
60 | [Code]
61 | function WizardNotSilent(): Boolean;
62 | begin
63 | Result := not WizardSilent();
64 | end;
65 | `;
66 |
67 | fs.appendFileSync(appIssPath, iisStr, {flag: `w`});
68 | }
69 |
70 |
71 | module.exports = createIssFile;
72 |
--------------------------------------------------------------------------------
/template/webpack.config.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const path = require('path')
4 | const settings = require('./config.js')
5 | const webpack = require('webpack')
6 |
7 | const ExtractTextPlugin = require('extract-text-webpack-plugin')
8 | const HtmlWebpackPlugin = require('html-webpack-plugin')
9 |
10 | let indexConfig = {
11 | target: 'electron-renderer',
12 | devtool: '#eval-source-map',
13 | entry: {
14 | build: path.join(__dirname, './app/src/main.js')
15 | },
16 | module: {
17 | rules: [
18 | {
19 | test: /\.vue$/,
20 | loader: 'vue-loader',
21 | options: {
22 | loaders: {
23 | 'scss': 'vue-style-loader!css-loader!sass-loader',
24 | 'sass': 'vue-style-loader!css-loader!sass-loader?indentedSyntax'
25 | }
26 | }
27 | },
28 | {
29 | test: /\.js$/,
30 | loader: 'babel-loader',
31 | exclude: /node_modules/,
32 | options: {
33 | presets: ['es2015', 'stage-0'],
34 | plugins: ['transform-runtime']
35 | }
36 | },
37 | {
38 | test: /\.json$/,
39 | loader: 'json-loader'
40 | },
41 | {
42 | test: /\.css$/,
43 | loader: ExtractTextPlugin.extract({
44 | fallback: "style-loader",
45 | use: "css-loader"
46 | })
47 | },
48 | {
49 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
50 | loader: 'url-loader',
51 | options: {
52 | limit: 10000,
53 | name: 'imgs/[name].[hash:7].[ext]'
54 | }
55 | },
56 | {
57 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
58 | loader: 'url-loader',
59 | options: {
60 | limit: 10000,
61 | name: 'fonts/[name].[hash:7].[ext]'
62 | }
63 | }
64 | ]
65 | },
66 | plugins: [
67 | new ExtractTextPlugin('styles.css'),
68 | new HtmlWebpackPlugin({
69 | filename: 'index.html',
70 | template: './app/index.ejs',
71 | title: settings.name
72 | }),
73 | new webpack.ProvidePlugin({
74 | }),
75 | new webpack.NoEmitOnErrorsPlugin()
76 | ],
77 | node: {
78 | __dirname: true
79 | },
80 | resolve: {
81 | extensions: ['.js', '.vue'],
82 | modules: [path.resolve(__dirname, "app"), "node_modules"]
83 | },
84 | output: {
85 | filename: '[name].js',
86 | path: path.join(__dirname, './app/dist')
87 | }
88 | }
89 |
90 |
91 | let configArr = []
92 | configArr.push(indexConfig)
93 |
94 | {{#if fork}}
95 | let forkConfig = {
96 | target: 'electron',
97 | devtool: '#eval-source-map',
98 | entry: {
99 | fork: path.join(__dirname, './app/src/sections/forkJs.js')
100 | },
101 | module: {
102 | rules: [
103 | {
104 | test: /\.js$/,
105 | loader: 'babel-loader',
106 | exclude: /node_modules/,
107 | options: {
108 | presets: ['es2015', 'stage-0'],
109 | plugins: ['transform-runtime']
110 | }
111 | }
112 | ]
113 | },
114 | plugins: [],
115 | node: {
116 | __dirname: true
117 | },
118 | resolve: {
119 | extensions: ['.js'],
120 | modules: [path.resolve(__dirname, "app"), "node_modules"]
121 | },
122 | output: {
123 | filename: '[name].js',
124 | path: path.join(__dirname, './app/dist')
125 | }
126 | }
127 |
128 | if (process.env.NODE_ENV === 'production') {
129 | forkConfig.devtool = ''
130 | forkConfig.plugins.push(
131 | new webpack.LoaderOptionsPlugin({
132 | minimize: true
133 | })
134 | )
135 | }
136 |
137 | if (process.env.NODE_ENV !== 'developmentHot') {
138 | forkConfig.plugins.push(
139 | new webpack.DefinePlugin({
140 | __DEFINE_NATIVE_REQUIRE__: JSON.stringify(true)
141 | })
142 | )
143 | configArr.push(forkConfig)
144 | }
145 | {{/if}}
146 |
147 |
148 | if (process.env.NODE_ENV === 'production') {
149 | indexConfig.devtool = ''
150 | indexConfig.plugins.push(
151 | new webpack.LoaderOptionsPlugin({
152 | minimize: true
153 | })
154 | )
155 | }
156 |
157 |
158 | module.exports = configArr
159 |
--------------------------------------------------------------------------------