├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── athena.jpg ├── bin ├── ath2 ├── ath2-app ├── ath2-build ├── ath2-component ├── ath2-init ├── ath2-module ├── ath2-page ├── ath2-publish ├── ath2-serve └── ath2-update ├── package.json ├── src ├── build │ ├── build.js │ ├── index.js │ └── serve.js ├── config │ ├── base.conf.js │ ├── browser_list.js │ ├── build.conf.js │ ├── dev.conf.js │ ├── devServer.conf.js │ ├── dll.conf.js │ ├── h5_template.conf.js │ ├── postcss.conf.js │ ├── prod.conf.js │ └── sitemap_template.ejs ├── create │ ├── app.js │ ├── base.js │ ├── component.js │ ├── module.js │ └── page.js ├── publish │ └── index.js └── util │ ├── format_webpack_message.js │ ├── index.js │ └── open.js ├── templates ├── complete │ ├── app │ │ ├── app-conf │ │ ├── config │ │ │ ├── dev │ │ │ ├── index │ │ │ └── prod │ │ ├── editorconfig │ │ ├── eslintconfig │ │ ├── gitignore │ │ ├── index.js │ │ ├── jsconfigjson │ │ └── packagejson │ ├── component │ │ ├── component.css │ │ ├── index.js │ │ ├── nerv │ │ │ └── component.js │ │ ├── react │ │ │ └── component.js │ │ └── vue │ │ │ └── component.vue │ ├── index.js │ ├── module │ │ ├── index.js │ │ └── mod-conf │ └── page │ │ ├── index.js │ │ ├── nerv │ │ └── page.js │ │ ├── page.css │ │ ├── page.html │ │ ├── react │ │ └── page.js │ │ └── vue │ │ ├── page.js │ │ └── page.vue ├── h5 │ ├── app │ │ ├── app-conf │ │ ├── editorconfig │ │ ├── gitignore │ │ ├── index.js │ │ ├── jsconfigjson │ │ └── packagejson │ ├── component │ │ ├── component.js │ │ └── index.js │ └── index.js └── simple │ ├── app │ ├── app-conf │ ├── config │ │ ├── dev │ │ ├── index │ │ └── prod │ ├── editorconfig │ ├── eslintconfig │ ├── gitignore │ ├── globalcss │ ├── index.js │ ├── indexhtml │ ├── jsconfigjson │ ├── nerv │ │ └── index │ ├── packagejson │ ├── react │ │ └── index │ └── vue │ │ ├── app │ │ └── index │ ├── component │ ├── component.css │ ├── index.js │ ├── nerv │ │ └── component.js │ ├── react │ │ └── component.js │ └── vue │ │ └── component.vue │ ├── index.js │ └── view │ ├── index.js │ ├── nerv │ └── view.js │ ├── react │ └── view.js │ ├── view.css │ └── vue │ └── view.vue ├── test.txt └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015-loose", "stage-0", "react"], 3 | "plugins": ["react-hot-loader/babel"] 4 | } 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/o2team/athena2/cada78ddcc7b0b9960ca15911a6fc8ecdb86162e/.eslintignore -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['standard'], 3 | parserOptions: { 4 | ecmaFeatures: { 5 | classes: true 6 | } 7 | }, 8 | env: { 9 | node: true, 10 | es6: true 11 | }, 12 | rules: { 13 | 'no-unused-expressions': 0, 14 | 'prefer-const': ['error'] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | node_modules 3 | npm-debug.log 4 | .DS_Store 5 | .vscode/ 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .DS_Store 3 | npm-debug.log 4 | node_modules 5 | .vscode 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 o2team 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Athena2 2 | === 3 | [![node version](https://img.shields.io/node/v/athena-html.svg?style=flat-square)](http://nodejs.org) 4 | [![npm](https://img.shields.io/npm/v/athena2.svg?style=flat-square)](https://www.npmjs.com/package/athena-html) 5 | [![npm](https://img.shields.io/npm/dm/athena2.svg?style=flat-square)](https://npmjs.com/package/athena-html) 6 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](https://raw.githubusercontent.com/o2team/athena/master/LICENSE) 7 | [![GitHub stars](https://img.shields.io/github/stars/o2team/athena2.svg?style=flat-square)](https://github.com/o2team/athena/stargazers) 8 | 9 | > O2Team构建项目流程工具,基于[Webpack3](https://webpack.js.org/)打包,提供多页、单页、H5三种模板选择,支持Nerv、React、Vue三大框架编译。 10 | > 11 | > 一次安装,到处运行 12 | 13 | 14 | 15 | ## 功能一览 16 | 17 | ### 创建项目 18 | 19 | - [x] 生成项目、模块、页面、组件文件结构 20 | - [x] 提供多页、单页、H5三大模板 21 | - [x] 支持[Nerv](https://nerv.aotu.io/)、[React](https://reactjs.org/)、[Vue](https://vuejs.org/index.html) 热门前端框架 22 | 23 | ### 编译预览 24 | 25 | - [x] 轻量组件化功能 26 | - [x] Sass 编译 27 | - [x] CSS合并压缩 28 | - [x] CSS prefix,px转rem 29 | - [x] JS合并压缩 30 | - [x] 自动生成雪碧图,自动多倍图 31 | - [x] 自动获取图片宽高 32 | - [x] 文件内联,自定义图片转base64 33 | - [x] 图片压缩 34 | - [x] 字体压缩 35 | - [x] 文件MD5戳 36 | - [x] 本地预览 37 | 38 | ### 项目部署 39 | 40 | - [x] 资源定位(图片等资源路径替换) 41 | - [x] 生成CSS页面片 42 | - [x] 部署到预览机和开发机 43 | 44 | ## 安装 45 | 46 | 基于``node``,请确保已具备较新的node环境(>=4.0.0),推荐使用node版本管理工具[nvm](https://github.com/creationix/nvm),这样不仅可以很方便地切换node版本,而且全局安装时候也不用加sudo了。 47 | 48 | ### 安装本项目 **athena2** 49 | 50 | ``` 51 | $ [sudo] npm install -g athena2 52 | ``` 53 | 54 | 由于国外源实在太慢,建议使用国内源来安装 55 | 56 | ``` 57 | $ [sudo] npm i -g athena2 --registry=http://registry.npm.taobao.org --disturl=http://npm.taobao.org/mirrors/node 58 | ``` 59 | 60 | 目前已支持**sass**文件的编译 61 | 62 | 63 | ## 快速开始 64 | 65 | 基于命令 ``athena2``,同时提供了简写``ath2`` 66 | 67 | ### 初始化 68 | 69 | 首先需要初始化Athena2,在这一步会有初始化工作目录、输入用户名等操作 70 | 71 | ``` 72 | $ ath2 init 73 | ``` 74 | 75 | ### 生成新项目 76 | 77 | #### 生成一个新的项目目录 78 | 79 | ``` 80 | $ ath2 a [项目名称] 81 | ``` 82 | 83 | 同时提供了通过携带参数快速创建项目的命令 84 | 85 | ``` 86 | $ ath2 a --name test --description 测试 --sass --template complete --framework nerv 87 | ``` 88 | 89 | #### 快速生成H5应用目录 90 | ``` 91 | $ ath2 a --h5 [h5 template] 92 | ``` 93 | 94 | #### 参数说明 95 | 参数 `--name` 指定项目名称 96 | 97 | 参数 `--description` 指定项目描述 98 | 99 | 参数 `--template` 指定项目使用的模板,输入模板名称,支持complete(多页)、simple(单页)、h5(H5活动)三种应用模式。 100 | 101 | 参数 `--framework` 指定项目使用的框架,输入框架名称,支持Nerv、React、Vue三大框架。 102 | 103 | 参数 `--sass` 指定项目使用 `sass` 104 | 105 | 每个参数都是可缺省的。 106 | 107 | 然后根据提示一步一步来,将会自动生成项目的结构和所需文件代码 108 | 109 | ### 创建模块 110 | *注:单页应用及H5无此功能* 111 | 112 | #### 创建命令 113 | ``` 114 | $ ath2 m [模块名] 115 | ``` 116 | 多页应用每个模块相当于一个页面 117 | 118 | 同时提供了通过携带参数快速创建模块的命令 119 | 120 | ``` 121 | $ ath2 m --name hhh --description 测试 122 | ``` 123 | 124 | #### 参数说明 125 | 参数 `--name` 指定模块名称 126 | 127 | 参数 `--description` 指定模块描述 128 | 129 | 每个参数都是可缺省的 130 | 131 | 然后根据提示一步一步来,将会自动生成项目的结构和所需文件代码 132 | 133 | ### 创建页面 134 | *注:H5应用无此功能* 135 | #### 创建命令 136 | ``` 137 | $ ath2 pa [页面名] 138 | ``` 139 | 多页应用需**进入到该模块** 下,执行以上命令,单页应用直接在根目录下执行 140 | 141 | 同时提供了通过携带参数快速创建页面的命令 142 | 143 | ``` 144 | $ ath2 pa --name hello --description 测试 145 | ``` 146 | 147 | 参数 `--name` 指定页面名称 148 | 149 | 参数 `--description` 指定页面描述 150 | 151 | 每个参数都是可缺省的 152 | 153 | 然后根据提示一步一步来,将会自动生成athena2的page目录和模板 154 | 155 | ### 创建组件 156 | 157 | H5应用每个组件相当于一个js类 158 | 159 | #### 创建命令 160 | ``` 161 | $ ath2 c [组件名] 162 | ``` 163 | 多页应用需**进入到该模块** 下,执行以上命令,单页应用直接在根目录下执行 164 | 165 | 同时提供了通过携带参数快速创建组件的命令 166 | 167 | ``` 168 | $ ath2 c --name topbar --description 测试 169 | ``` 170 | 171 | #### 参数说明 172 | 参数 `--name` 指定组件名称 173 | 174 | 参数 `--description` 指定组件描述 175 | 176 | 每个参数都是可缺省的 177 | 178 | 然后根据提示一步一步来,将会自动生成athena2的component目录和模板 179 | 180 | ### 编译 181 | ``` 182 | // 编译所有模块 183 | $ ath2 s 184 | // 多页应用同时编译多个模块 185 | $ ath2 s mod1 mod2 186 | ``` 187 | ### 打包 188 | ``` 189 | // 打包所有模块 190 | $ ath2 build 191 | // 多页应用同时打包多个模块 192 | $ ath2 build mod1 mod2 193 | // H5支持打包生成zip压缩包 194 | $ ath2 build --zip 195 | ``` 196 | 197 | ## 自定义配置 198 | 199 | ### 基础配置 200 | 项目根目录下的config目录即配置文件目录,里面包含 201 | - 公共配置文件:`index.js` 202 | - 编译配置文件:`dev.js` 203 | - 打包配置文件:`prod.js` 204 | 可根据需要自行配置相关参数,如publicPath、sourceRoot、outputRoot、port、host等。建议如无特殊情况,可不修改基础配置 205 | ### Webpack配置 206 | 如需添加Webpack其他配置,可在相应配置文件中添加`webpack`参数,填入相关配置 207 | 208 | 例如:添加`json-loader` 209 | 210 | ``` 211 | module.exports = { 212 | ... 213 | webpack: { 214 | module: { 215 | rules: [ 216 | { 217 | test: /\.json$/, 218 | use: 'json-loader' 219 | } 220 | ] 221 | } 222 | } 223 | } 224 | 225 | ``` 226 | 227 | ### Postcss配置 228 | Postcss相关配置可在相应配置文件的`module`>`postcss`中填入 229 | 230 | #### 自动css前缀 autoprefixer 231 | 该功能默认开启,如不需要,配置`enable: false` 232 | 233 | *更多配置参数详见:https://github.com/postcss/autoprefixer* 234 | 235 | ``` 236 | module.exports = { 237 | ... 238 | webpack: {}, 239 | module: { 240 | postcss: { 241 | autoprefix: { 242 | enable: true, 243 | // 其他options 244 | } 245 | } 246 | } 247 | } 248 | ``` 249 | 250 | #### REM转换 251 | 该功能H5应用默认开启 252 | 253 | ``` 254 | module.exports = { 255 | ... 256 | webpack: {}, 257 | module: { 258 | postcss: { 259 | pxtorem: { 260 | enable: true, 261 | // 其他options 262 | } 263 | } 264 | } 265 | } 266 | ``` 267 | 268 | *更多配置参数详见:https://github.com/ant-tool/postcss-plugin-px2rem* 269 | 270 | #### 获取图片宽高 assets 271 | 272 | 该功能H5应用默认开启 273 | 274 | 自动获取1倍图宽高 275 | 276 | body { 277 | width: width('images/foobar.png'); /* 320px */ 278 | height: height('images/foobar.png'); /* 240px */ 279 | background-size: size('images/foobar.png'); /* 320px 240px */ 280 | } 281 | 282 | 自动获取多倍图宽高 283 | 284 | body { 285 | width: width('images/foobar.png', 2); /* 160px */ 286 | height: height('images/foobar.png', 2); /* 120px */ 287 | background-size: size('images/foobar.png', 2); /* 160px 120px */ 288 | } 289 | 290 | ``` 291 | module.exports = { 292 | ... 293 | webpack: {}, 294 | module: { 295 | postcss: { 296 | assets: { 297 | enable: true, 298 | // 其他options 299 | } 300 | } 301 | } 302 | } 303 | ``` 304 | 305 | *更多配置参数详见:https://github.com/assetsjs/postcss-assets* 306 | 307 | #### 雪碧图 sprites 308 | 309 | 该功能H5应用默认开启,会将img/images目录下的按文件夹划分的所有图片合成一张雪碧图,例如 310 | 311 | /* Input */ 312 | .comment { background: url(img/icon/ico-comment.png) no-repeat 0 0; } 313 | .bubble { background: url(img/icon/ico-bubble.png) no-repeat 0 0; } 314 | 315 | /* ---------------- */ 316 | 317 | /* Output */ 318 | .comment { background-image: url(img/sprite.icon.png); background-position: 0 0; } 319 | .bubble { background-image: url(img/sprite.icon.png); background-position: 0 -50px; } 320 | 321 | **使用多倍图时需按文件夹划分图片,文件夹命名格式如:`name@2x`。使用了pxtorem功能无需按文件夹划分多倍图** 322 | 323 | ``` 324 | // prod.js 325 | module.exports = { 326 | ... 327 | webpack: {}, 328 | module: { 329 | postcss: { 330 | sprites: { 331 | enable: true, 332 | // 其他options 333 | } 334 | } 335 | } 336 | } 337 | ``` 338 | 339 | *更多配置参数详见:https://github.com/2createStudio/postcss-sprites* 340 | 341 | 342 | ### 其他功能 343 | #### 图片压缩 344 | 图片压缩功能主要在打包时应用,H5应用默认开启该项功能 345 | ``` 346 | // prod.js 347 | module.exports = { 348 | ... 349 | webpack: {}, 350 | module: { 351 | imageMin: { 352 | enable: true, 353 | // 其他options 354 | } 355 | } 356 | } 357 | ``` 358 | 359 | *更多配置参数详见:https://github.com/tcoopman/image-webpack-loader* 360 | 361 | #### Base64 limit 362 | 该功能主要配置图片、文字转base64时的limit尺寸,默认为2000 363 | ``` 364 | module.exports = { 365 | ... 366 | webpack: {}, 367 | module: { 368 | base64: { 369 | imageLimit: 8000, 370 | fontLimit: 000 371 | } 372 | } 373 | } 374 | ``` 375 | 376 | ## LICENCE 377 | 378 | MIT License 379 | 380 | Copyright (c) 2017 o2team 381 | 382 | Permission is hereby granted, free of charge, to any person obtaining a copy 383 | of this software and associated documentation files (the "Software"), to deal 384 | in the Software without restriction, including without limitation the rights 385 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 386 | copies of the Software, and to permit persons to whom the Software is 387 | furnished to do so, subject to the following conditions: 388 | 389 | The above copyright notice and this permission notice shall be included in all 390 | copies or substantial portions of the Software. 391 | 392 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 393 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 394 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 395 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 396 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 397 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 398 | SOFTWARE. 399 | -------------------------------------------------------------------------------- /athena.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/o2team/athena2/cada78ddcc7b0b9960ca15911a6fc8ecdb86162e/athena.jpg -------------------------------------------------------------------------------- /bin/ath2: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const program = require('commander') 4 | 5 | const { 6 | getAthenaVersion 7 | } = require('../src/util') 8 | 9 | program 10 | .version(getAthenaVersion()) 11 | .usage(' [options]') 12 | .command('init [url]', 'Init Athena') 13 | .command('app [appName]', 'Create an app').alias('a') 14 | .command('module [name]', 'Create a module').alias('m') 15 | .command('page [name]', 'Create a page').alias('pa') 16 | .command('component [name]', 'Create a component').alias('c') 17 | .command('serve [modNames]', 'Development preview').alias('s') 18 | .command('build [modNames]', 'Build for production').alias('b') 19 | .command('update [version]', 'Update Athena2').alias('up') 20 | .command('publish', 'Publish your project').alias('pu') 21 | .parse(process.argv) 22 | -------------------------------------------------------------------------------- /bin/ath2-app: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const program = require('commander') 4 | 5 | const App = require('../src/create/app') 6 | 7 | program 8 | .usage(' [options]') 9 | .option('--name [appName]', 'App name') 10 | .option('--description [appDescription]', 'App description') 11 | .option('--platform [appPlatform]', 'App platform') 12 | .option('--framework [framework]', 'Choose your favorite framework') 13 | .option('--template [templateName]', 'Set template to use') 14 | .option('--h5 [h5TplName]', 'Set H5 template to use') 15 | .option('--sass', 'Use Sass') 16 | .option('--debug', 'Debug info') 17 | .parse(process.argv) 18 | 19 | const args = program.args 20 | 21 | const appName = args[0] || program.name 22 | 23 | const description = program.description || '' 24 | const { framework, template, platform, sass, h5 } = program 25 | 26 | const app = new App({ 27 | appName, 28 | description, 29 | framework, 30 | template, 31 | platform, 32 | sass, 33 | h5 34 | }) 35 | 36 | app.create() 37 | -------------------------------------------------------------------------------- /bin/ath2-build: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const program = require('commander') 4 | const chalk = require('chalk') 5 | 6 | const { 7 | printAthenaVersion, 8 | getConfig 9 | } = require('../src/util') 10 | 11 | printAthenaVersion() 12 | 13 | program 14 | .option('--debug', 'Debug info') 15 | .option('--zip', 'Archive directory') 16 | .parse(process.argv) 17 | 18 | const args = program.args 19 | const { debug, zip } = program 20 | 21 | console.log(chalk.green(`Allo ${chalk.bold(getConfig().username)}! Starting build app...`)) 22 | console.log() 23 | 24 | const build = require('../src/build/build') 25 | process.env.NODE_ENV = 'production' 26 | 27 | build(args, { debug, zip }) 28 | -------------------------------------------------------------------------------- /bin/ath2-component: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const program = require('commander') 4 | const chalk = require('chalk') 5 | 6 | const Component = require('../src/create/component') 7 | 8 | const { getConf } = require('../src/build') 9 | 10 | program 11 | .usage(' [options]') 12 | .option('--name [componentName]', 'component name') 13 | .option('--description [componentDescription]', 'component description') 14 | .option('--debug', 'Debug info') 15 | .parse(process.argv) 16 | 17 | const args = program.args 18 | 19 | const componentName = args[0] || program.name 20 | 21 | const description = program.description || '' 22 | 23 | const conf = getConf() 24 | const appConf = conf.appConf 25 | const moduleConf = conf.moduleConf 26 | 27 | if (!appConf && !moduleConf) { 28 | console.log(chalk.red(`Is not a Athena App module, please check if the file ${chalk.bold('mod.conf.js')} actually exists!😢`)) 29 | process.exit(1) 30 | } else if (appConf && !moduleConf) { 31 | if (appConf.template === 'complete') { 32 | console.log(chalk.red(`Current dir is an app, please go to the module dir!😢`)) 33 | process.exit(1) 34 | } 35 | } 36 | 37 | const appName = appConf.app 38 | const template = appConf.template 39 | const framework = appConf.framework 40 | const sass = appConf.sass 41 | 42 | const component = new Component({ 43 | appName, 44 | template, 45 | framework, 46 | componentName, 47 | description, 48 | sass 49 | }) 50 | 51 | component.create() 52 | -------------------------------------------------------------------------------- /bin/ath2-init: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const program = require('commander') 4 | const chalk = require('chalk') 5 | const inquirer = require('inquirer') 6 | 7 | const { 8 | printAthenaVersion, 9 | getSystemUsername, 10 | getConfig, 11 | setConfig 12 | } = require('../src/util') 13 | 14 | const config = getConfig() 15 | const systemUsername = getSystemUsername() 16 | 17 | program 18 | .parse(process.argv) 19 | 20 | const args = program.args 21 | let url = args[0] 22 | 23 | printAthenaVersion() 24 | console.log(chalk.green(`Allo ${chalk.green.bold(config.username || systemUsername)}! Happy to work now!🤡~`)) 25 | if (!url) { 26 | url = process.cwd() 27 | } 28 | if (!config.username) { 29 | inquirer.prompt([{ 30 | type: 'confirm', 31 | name: 'useSystemName', 32 | message: `Do you want to use this name (${systemUsername})?`, 33 | default: true 34 | }]).then(answers => new Promise((resolve, reject) => { 35 | if (answers.useSystemName) { 36 | config.username = systemUsername 37 | return reject(new Error()) 38 | } 39 | resolve() 40 | })).then(() => inquirer.prompt([{ 41 | type: 'input', 42 | name: 'setNewName', 43 | message: 'Give me your username' 44 | }]), () => ({ setNewName: systemUsername })).then(answers => { 45 | const username = answers.setNewName 46 | setWorkspace({ username }) 47 | }) 48 | } else { 49 | inquirer.prompt([{ 50 | type: 'confirm', 51 | name: 'updateUsername', 52 | message: `Do you want to update your name (${systemUsername})?`, 53 | default: false 54 | }]).then(answers => new Promise((resolve, reject) => { 55 | if (answers.updateUsername) { 56 | return resolve() 57 | } 58 | reject(new Error()) 59 | })).then(() => inquirer.prompt([{ 60 | type: 'input', 61 | name: 'setNewName', 62 | message: 'Give me your username' 63 | }]), () => ({ setNewName: config.username })).then(answers => { 64 | const username = answers.setNewName 65 | setWorkspace({ username }) 66 | }) 67 | } 68 | 69 | function setWorkspace ({ username }) { 70 | config.username = username 71 | if (!config.workspace) { 72 | config.workspace = url 73 | setConfig(config) 74 | console.log() 75 | console.log(chalk.gray(`Now ${chalk.gray.bold('Athena')} will set the directory(${url}) to be workspace!`)) 76 | } else if (config.workspace !== url) { 77 | inquirer.prompt({ 78 | type: 'confirm', 79 | name: 'updateWorkspace', 80 | message: `Update the old workspace ${chalk.gray(config.workspace)} ?`, 81 | default: false 82 | }).then(answers => { 83 | if (answers.updateWorkspace) { 84 | config.workspace = url 85 | setConfig(config) 86 | console.log() 87 | console.log(chalk.gray(`Now ${chalk.gray.bold('Athena')} will set the directory(${url}) to be workspace!`)) 88 | } else { 89 | setConfig(config) 90 | console.log() 91 | console.log(chalk.gray(`The ${chalk.gray.bold('Athena')} workspace is still ${config.workspace}!`)) 92 | } 93 | }) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /bin/ath2-module: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const program = require('commander') 4 | const chalk = require('chalk') 5 | 6 | const Module = require('../src/create/module') 7 | 8 | const { getConf } = require('../src/build') 9 | 10 | program 11 | .usage(' [options]') 12 | .option('--name [moduleName]', 'Module name') 13 | .option('--description [moduleDescription]', 'Module description') 14 | .option('--debug', 'Debug info') 15 | .parse(process.argv) 16 | 17 | const args = program.args 18 | 19 | const moduleName = args[0] || program.name 20 | 21 | const description = program.description || '' 22 | 23 | const conf = getConf() 24 | const appConf = conf.appConf 25 | const moduleConf = conf.moduleConf 26 | 27 | if (appConf && appConf.template !== 'complete') { 28 | if (appConf.template === 'simple') { 29 | console.log(chalk.red(`Is a Simple App, please use command ${chalk.bold('ath2 pa')} or ${chalk.bold('ath2 c')}`)) 30 | } else { 31 | console.log(chalk.red(`Is a H5 App, please use command ${chalk.bold('ath2 c')}`)) 32 | } 33 | process.exit(1) 34 | } 35 | if (!appConf) { 36 | console.log(chalk.red(`Is not a Athena App, please check if the file ${chalk.bold('app.conf.js')} actually exists!😢`)) 37 | process.exit(1) 38 | } else if (appConf && moduleConf) { 39 | console.log(chalk.red(`Current dir is a module, please go back to the App root dir!😢`)) 40 | process.exit(1) 41 | } 42 | 43 | const appName = appConf.app 44 | const template = appConf.template 45 | 46 | const appModule = new Module({ 47 | appName, 48 | template, 49 | moduleName, 50 | description 51 | }) 52 | 53 | appModule.create() 54 | -------------------------------------------------------------------------------- /bin/ath2-page: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const program = require('commander') 4 | const chalk = require('chalk') 5 | 6 | const Page = require('../src/create/page') 7 | 8 | const { getConf } = require('../src/build') 9 | 10 | program 11 | .usage(' [options]') 12 | .option('--name [pageName]', 'Page name') 13 | .option('--description [pageDescription]', 'Page description') 14 | .option('--debug', 'Debug info') 15 | .parse(process.argv) 16 | 17 | const args = program.args 18 | 19 | const pageName = args[0] || program.name 20 | 21 | const description = program.description || '' 22 | 23 | const conf = getConf() 24 | const appConf = conf.appConf 25 | const moduleConf = conf.moduleConf 26 | 27 | if (!appConf && !moduleConf) { 28 | console.log(chalk.red(`Is not a Athena App module, please check if the file ${chalk.bold('mod.conf.js')} actually exists!😢`)) 29 | process.exit(1) 30 | } 31 | if (appConf && !moduleConf) { 32 | if (appConf.template === 'complete') { 33 | console.log(chalk.red(`Current dir is an app, please go to the module dir!😢`)) 34 | process.exit(1) 35 | } 36 | } 37 | 38 | const appName = appConf.app 39 | const template = appConf.template 40 | const framework = appConf.framework 41 | const sass = appConf.sass 42 | 43 | const page = new Page({ 44 | appName, 45 | template, 46 | framework, 47 | pageName, 48 | description, 49 | sass 50 | }) 51 | 52 | page.create() 53 | -------------------------------------------------------------------------------- /bin/ath2-publish: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const program = require('commander') 4 | const chalk = require('chalk') 5 | const FTPS = require('ftps') 6 | const shelljs = require('shelljs') 7 | const ora = require('ora') 8 | 9 | const { getConf } = require('../src/build') 10 | 11 | const { 12 | getAthenaVersion, 13 | shouldUseCnpm 14 | } = require('../src/util') 15 | 16 | program 17 | .option('--debug', 'Debug info') 18 | .parse(process.argv) 19 | 20 | const args = program.args 21 | 22 | // temp testing 23 | // 24 | // const ftps = new FTPS({ 25 | // host: 'labs.qiang.it', // required 26 | // username: 'labs', // Optional. Use empty username for anonymous access. 27 | // password: 'labslabslabs', // Required if username is not empty, except when requiresPassword: false 28 | // protocol: 'sftp', // Optional, values : 'ftp', 'sftp', 'ftps', ... default: 'ftp' 29 | // // protocol is added on beginning of host, ex : sftp://domain.com in this case 30 | // port: 22, // Optional 31 | // // port is added to the end of the host, ex: sftp://domain.com:22 in this case 32 | // escape: true, // optional, used for escaping shell characters (space, $, etc.), default: true 33 | // retries: 2, // Optional, defaults to 1 (1 = no retries, 0 = unlimited retries) 34 | // timeout: 1000, // Optional, Time before failing a connection attempt. Defaults to 10 35 | // retryInterval: 5, // Optional, Time in seconds between attempts. Defaults to 5 36 | // retryMultiplier: 1, // Optional, Multiplier by which retryInterval is multiplied each time new attempt fails. Defaults to 1 37 | // requiresPassword: true, // Optional, defaults to true 38 | // autoConfirm: true, // Optional, is used to auto confirm ssl questions on sftp or fish protocols, defaults to false 39 | // cwd: '' // Optional, defaults to the directory from where the script is executed 40 | // }) 41 | 42 | // ftps.cd('/usr/share/nginx/html/public/labs/athena2_temp').put('./test.txt').exec((err, res) => { 43 | // console.log(res); 44 | // console.log("ok"); 45 | // }) 46 | 47 | const publish = require('../src/publish') 48 | process.env.NODE_ENV = 'production' 49 | publish() 50 | -------------------------------------------------------------------------------- /bin/ath2-serve: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const program = require('commander') 4 | const chalk = require('chalk') 5 | 6 | const { 7 | printAthenaVersion, 8 | getConfig 9 | } = require('../src/util') 10 | 11 | printAthenaVersion() 12 | 13 | program 14 | .option('--debug', 'Debug info') 15 | .parse(process.argv) 16 | 17 | const args = program.args 18 | const { debug } = program 19 | 20 | console.log(chalk.green(`Allo ${chalk.bold(getConfig().username)}! Starting development mode...`)) 21 | console.log() 22 | 23 | const serve = require('../src/build/serve') 24 | process.env.NODE_ENV = 'development' 25 | 26 | serve(args, { debug }) 27 | -------------------------------------------------------------------------------- /bin/ath2-update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const program = require('commander') 4 | const chalk = require('chalk') 5 | const request = require('request') 6 | const shelljs = require('shelljs') 7 | const ora = require('ora') 8 | 9 | const { getConf } = require('../src/build') 10 | 11 | const { 12 | getAthenaVersion, 13 | shouldUseCnpm 14 | } = require('../src/util') 15 | 16 | 17 | const api = 'http://localhost:3000/ath2/v' 18 | 19 | program 20 | .usage(' [options]') 21 | .option('--version', 'Choosen version') 22 | .option('--debug', 'Debug info') 23 | .parse(process.argv) 24 | 25 | const args = program.args 26 | 27 | const version = args[0] || program.version 28 | 29 | function addNewVersion () { 30 | const params = { 31 | version: '0.0.9-alpha.1' 32 | } 33 | request.post(api, { 34 | form: params, 35 | timeout: 5000 36 | }) 37 | } 38 | function checkAthenaVersion () { 39 | request.get(api, (err, res, body) => { 40 | if (err) { 41 | console.log(chalk.red("Can not get the latest athena2's version😢")) 42 | return 43 | } 44 | if (res.statusCode === 200 || res.statusCode === 201) { 45 | const curVersion = getAthenaVersion() 46 | const latestVersion = JSON.parse(body).version 47 | const ifLatest = curVersion === latestVersion 48 | if (ifLatest) { 49 | console.log(chalk.green("Now it's the latest version")) 50 | } else { 51 | const command = `${shouldUseCnpm() ? 'cnpm' : 'npm'} i -g athena2@${latestVersion}` 52 | const installSpinner = ora(`Executing ${chalk.cyan.bold(command)}, it will take some time...`).start() 53 | const install = shelljs.exec(command, { silent: true }) 54 | if (install.code === 0) { 55 | installSpinner.color = 'green' 56 | installSpinner.succeed('Install success') 57 | console.log(`${install.stderr}${install.stdout}`) 58 | } else { 59 | installSpinner.color = 'red' 60 | installSpinner.fail(chalk.red('Auto update failed! Please update the athena2 manually')) 61 | console.log(`${install.stderr}${install.stdout}`) 62 | } 63 | } 64 | } 65 | }) 66 | } 67 | 68 | checkAthenaVersion() 69 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "athena2", 3 | "version": "0.0.18", 4 | "description": "A build tool for front end", 5 | "main": "index.js", 6 | "scripts": { 7 | "precommit": "lint-staged", 8 | "lint:fix": "eslint --fix bin src test", 9 | "lint": "eslint bin src test" 10 | }, 11 | "lint-staged": { 12 | "*.js": [ 13 | "eslint --fix", 14 | "git add" 15 | ] 16 | }, 17 | "bin": { 18 | "ath2": "bin/ath2" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "git+https://github.com/o2team/athena2.git" 23 | }, 24 | "keywords": [ 25 | "build-tool" 26 | ], 27 | "author": "luckyadam", 28 | "license": "MIT", 29 | "engines": { 30 | "node": ">=6.0.0" 31 | }, 32 | "bugs": { 33 | "url": "https://github.com/o2team/athena2/issues" 34 | }, 35 | "homepage": "https://github.com/o2team/athena2#readme", 36 | "dependencies": { 37 | "@babel/core": "7.6.0", 38 | "@babel/plugin-proposal-export-default-from": "^7.5.2", 39 | "@babel/plugin-transform-react-jsx": "^7.7.4", 40 | "archiver": "2.1.0", 41 | "autoprefixer": "9.7.1", 42 | "babel-loader": "8.0.6", 43 | "babel-plugin-macros": "^2.6.1", 44 | "babel-plugin-named-asset-import": "^0.3.4", 45 | "babel-plugin-transform-es3-member-expression-literals": "^6.22.0", 46 | "babel-plugin-transform-es3-property-literals": "^6.22.0", 47 | "babel-preset-env": "1.6.0", 48 | "babel-preset-react-app": "^9.0.2", 49 | "chalk": "2.4.2", 50 | "clean-webpack-plugin": "0.1.17", 51 | "commander": "4.0.0", 52 | "css-loader": "3.2.0", 53 | "es3ify-loader": "0.2.0", 54 | "exports-loader": "0.7.0", 55 | "file-loader": "4.2.0", 56 | "fs-extra": "8.1.0", 57 | "ftps": "1.1.1", 58 | "git-clone": "0.1.0", 59 | "hot-module-accept": "1.1.2", 60 | "html-loader": "0.5.5", 61 | "html-webpack-harddisk-plugin": "1.0.1", 62 | "html-webpack-plugin": "3.2.0", 63 | "inquirer": "7.0.0", 64 | "lodash": "4.17.15", 65 | "mem-fs": "1.1.3", 66 | "mem-fs-editor": "3.0.2", 67 | "mini-css-extract-plugin": "0.8.0", 68 | "node-sass": "4.13.0", 69 | "ora": "4.0.2", 70 | "postcss-assets": "5.0.0", 71 | "postcss-flexbugs-fixes": "4.1.0", 72 | "postcss-loader": "3.0.0", 73 | "postcss-plugin-px2rem": "0.8.1", 74 | "postcss-sprites": "4.2.1", 75 | "request": "2.88.0", 76 | "sass-loader": "8.0.0", 77 | "script-loader": "0.7.2", 78 | "semver": "6.3.0", 79 | "shelljs": "0.8.3", 80 | "style-loader": "1.0.0", 81 | "terser-webpack-plugin": "3.0.6", 82 | "url-loader": "2.2.0", 83 | "uuid": "3.3.3", 84 | "vue-loader": "15.7.2", 85 | "vue-template-compiler": "2.6.10", 86 | "webpack": "4.41.2", 87 | "webpack-dev-server": "3.9.0", 88 | "webpack-manifest-plugin": "2.2.0", 89 | "webpack-merge": "4.2.2", 90 | "zepto": "1.2.0" 91 | }, 92 | "devDependencies": { 93 | "eslint": "^6.1.0", 94 | "eslint-config-standard": "^14.1.0", 95 | "eslint-plugin-import": "^2.18.2", 96 | "eslint-plugin-node": "^10.0.0", 97 | "eslint-plugin-promise": "^4.2.1", 98 | "eslint-plugin-standard": "^4.0.1", 99 | "husky": "^3.0.9", 100 | "lint-staged": "^9.4.2" 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/build/build.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra') 2 | const path = require('path') 3 | const chalk = require('chalk') 4 | const webpack = require('webpack') 5 | const webpackMerge = require('webpack-merge') 6 | const HtmlWebpackPlugin = require('html-webpack-plugin') 7 | const ora = require('ora') 8 | const archiver = require('archiver') 9 | 10 | const { getRootPath, isEmptyObject, urlJoin } = require('../util') 11 | const formatWebpackMessage = require('../util/format_webpack_message') 12 | 13 | const { 14 | getConf, 15 | getAppBuildConfig, 16 | getEntry, 17 | getPageHtml, 18 | createCompiler, 19 | BUILD_APP, 20 | BUILD_MODULE, 21 | BUILD_NONE 22 | } = require('./index') 23 | 24 | module.exports = function build (args, options) { 25 | const conf = getConf() 26 | conf.args = args 27 | switch (conf.buildType) { 28 | case BUILD_APP: 29 | buildApp(conf, options) 30 | break 31 | case BUILD_MODULE: 32 | buildModule(conf, options) 33 | break 34 | case BUILD_NONE: 35 | console.log(chalk.red('✖ Build error, the current directory is not an app or a module!')) 36 | console.log(chalk.bold('GoodBye!')) 37 | break 38 | } 39 | } 40 | 41 | function buildCore (conf, options) { 42 | const buildSpinner = ora('Starting build, please wait🤡~').start() 43 | const appConf = conf.appConf 44 | const buildConfig = getAppBuildConfig(conf.appPath) 45 | const { 46 | publicPath, 47 | outputRoot, 48 | chunkDirectory 49 | } = buildConfig 50 | conf.buildConfig = buildConfig 51 | conf.moduleList && conf.moduleList.forEach(item => { 52 | fs.removeSync(path.join(outputRoot, item)) 53 | }) 54 | const entry = getEntry(conf) 55 | if (isEmptyObject(entry)) { 56 | buildSpinner.fail(chalk.red(`No file to build, please check if the ${chalk.bold('page')} directories are empty!`)) 57 | console.log(chalk.bold('GoodBye!')) 58 | process.exit(1) 59 | } 60 | const { template, framework, platform } = appConf 61 | let customWebpackConf 62 | if (template === 'h5') { 63 | const h5TemplateConf = require('../config/h5_template.conf')(webpack, buildConfig) 64 | const h5TemplateWebpackConf = webpackMerge(h5TemplateConf.BASE, h5TemplateConf.PROD) 65 | customWebpackConf = webpackMerge(h5TemplateWebpackConf, buildConfig.webpack) 66 | } else { 67 | customWebpackConf = buildConfig.webpack 68 | } 69 | const webpackBaseConf = require('../config/base.conf')(conf.appPath, buildConfig, template, platform, framework) 70 | const webpackProdConf = require('../config/prod.conf')(conf.appPath, buildConfig, template, platform, framework) 71 | let webpackConf = webpackMerge(webpackBaseConf, webpackProdConf) 72 | const htmlPages = getPageHtml(conf) 73 | webpackConf.entry = entry 74 | webpackConf.output = { 75 | path: path.join(conf.appPath, outputRoot), 76 | filename: 'js/[name].js', 77 | publicPath, 78 | chunkFilename: `${chunkDirectory}/[name].chunk.js` 79 | } 80 | if (appConf.template === 'complete') { 81 | webpackConf.plugins.push(new HtmlWebpackPlugin({ 82 | title: conf.appConf.app, 83 | filename: 'index.html', 84 | template: path.join(getRootPath(), 'src', 'config', 'sitemap_template.ejs'), 85 | alwaysWriteToDisk: true, 86 | data: { 87 | htmlPages 88 | } 89 | })) 90 | } 91 | if (appConf.template === 'complete') { 92 | for (const mod in htmlPages) { 93 | for (const page in htmlPages[mod]) { 94 | const pageItem = htmlPages[mod][page] 95 | webpackConf.plugins.push(new HtmlWebpackPlugin({ 96 | filename: `${mod}/${pageItem.filename}`, 97 | template: pageItem.filepath, 98 | alwaysWriteToDisk: true, 99 | chunks: [`${mod}/${page}`] 100 | })) 101 | } 102 | } 103 | } else { 104 | webpackConf.plugins.push(new HtmlWebpackPlugin({ 105 | filename: 'index.html', 106 | template: htmlPages.index, 107 | alwaysWriteToDisk: true, 108 | chunks: 'index.js' 109 | })) 110 | } 111 | webpackConf = webpackMerge(webpackConf, customWebpackConf) 112 | const compiler = createCompiler(webpack, webpackConf) 113 | buildCompilerRun(compiler, buildSpinner, conf, options) 114 | } 115 | 116 | function buildCompilerRun (compiler, buildSpinner, conf, options) { 117 | compiler.run((err, stats) => { 118 | if (err) { 119 | return printBuildError(err) 120 | } 121 | const { errors, warnings } = formatWebpackMessage(stats.toJson({}, true)) 122 | const isSuccess = !errors.length && !warnings.length 123 | if (isSuccess) { 124 | buildSpinner.succeed(chalk.green('Compile successfully!\n')) 125 | process.stdout.write(stats.toString({ 126 | colors: true, 127 | modules: false, 128 | children: false, 129 | chunks: false, 130 | chunkModules: false 131 | }) + '\n') 132 | if (options.zip) { 133 | buildArchive(conf) 134 | } 135 | return 136 | } 137 | if (errors.length) { 138 | errors.splice(1) 139 | buildSpinner.fail(chalk.red('Compile failed!\n')) 140 | return printBuildError(new Error(errors.join('\n\n'))) 141 | } 142 | if (warnings.length) { 143 | buildSpinner.warn(chalk.yellow('Compiled with warnings.\n')) 144 | console.log(warnings.join('\n\n')) 145 | console.log( 146 | '\nSearch for the ' + 147 | chalk.underline(chalk.yellow('keywords')) + 148 | ' to learn more about each warning.' 149 | ) 150 | console.log( 151 | 'To ignore, add ' + 152 | chalk.cyan('// eslint-disable-next-line') + 153 | ' to the line before.\n' 154 | ) 155 | } 156 | }) 157 | } 158 | 159 | function buildArchive (conf) { 160 | console.log() 161 | const archiveSpinner = ora('Archive begin ...').start() 162 | const archivePath = conf.buildConfig.outputRoot 163 | const archiveType = 'zip' 164 | const outputPath = path.join(conf.appPath, `${conf.appConf.app}.${archiveType}`) 165 | const output = fs.createWriteStream(outputPath) 166 | const archive = archiver(archiveType) 167 | output.on('close', function () { 168 | archiveSpinner.succeed(chalk.green(`Archive finished, output: ${(archive.pointer() / 1024).toFixed(1)}kb`)) 169 | }) 170 | archive.on('warning', function (err) { 171 | if (err.code === 'ENOENT') { 172 | archiveSpinner.warn(chalk.yellow(`Archive warn: ${err.toString()}`)) 173 | } else { 174 | archiveSpinner.fail(chalk.red(`Archive error: ${err.toString()}`)) 175 | } 176 | }) 177 | archive.on('error', function (err) { 178 | archiveSpinner.fail(chalk.red(`Archive error: ${err.message}`)) 179 | }) 180 | archive.pipe(output) 181 | archive.directory(archivePath, false) 182 | archive.finalize() 183 | } 184 | 185 | function printBuildError (err) { 186 | const message = err != null && err.message 187 | const stack = err != null && err.stack 188 | if (stack && typeof message === 'string' && message.indexOf('from UglifyJs') !== -1) { 189 | try { 190 | const matched = /(.+)\[(.+):(.+),(.+)\]\[.+\]/.exec(stack) 191 | if (!matched) { 192 | throw new Error('Using errors for control flow is bad.') 193 | } 194 | const problemPath = matched[2] 195 | const line = matched[3] 196 | const column = matched[4] 197 | console.log( 198 | 'Failed to minify the code from this file: \n\n', 199 | chalk.yellow( 200 | `\t${problemPath}:${line}${column !== '0' ? ':' + column : ''}` 201 | ), 202 | '\n' 203 | ) 204 | } catch (ignored) { 205 | console.log('Failed to minify the bundle.', err) 206 | } 207 | } else { 208 | console.log((message || err) + '\n') 209 | } 210 | console.log() 211 | } 212 | 213 | function buildApp (conf, options) { 214 | if (conf.appConf.template === 'complete') { 215 | conf.moduleList = conf.args 216 | delete conf.args 217 | if (!conf.moduleList || !conf.moduleList.length) { 218 | conf.moduleList = conf.appConf.moduleList 219 | } 220 | console.log(`Current building modules ${chalk.bold(conf.moduleList.join(' '))}!`) 221 | } 222 | buildCore(conf, options) 223 | } 224 | 225 | function buildModule (conf, options) { 226 | const moduleConf = conf.moduleConf 227 | conf.moduleList = [moduleConf.module] 228 | delete conf.args 229 | console.log(`Current building module ${chalk.bold(conf.moduleList[0])}!`) 230 | buildCore(conf, options) 231 | } 232 | -------------------------------------------------------------------------------- /src/build/index.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const url = require('url') 3 | const fs = require('fs-extra') 4 | const chalk = require('chalk') 5 | const _ = require('lodash') 6 | 7 | const { getLocalIp, isPrivate } = require('../util') 8 | 9 | exports.BUILD_MODULE = 'module' 10 | exports.BUILD_APP = 'app' 11 | exports.BUILD_NONE = 'none' 12 | 13 | const IGNORE_FILE_REG = /(^|\/)\.[^/.]/g 14 | 15 | exports.getConf = function () { 16 | const rootPath = process.cwd() 17 | let appConf = null 18 | let moduleConf = null 19 | let buildType = exports.BUILD_NONE 20 | 21 | let appPath = null 22 | let appConfPath = null 23 | let modulePath = null 24 | let moduleConfPath = null 25 | if (fs.existsSync('app.conf.js')) { 26 | appPath = rootPath 27 | appConfPath = path.join(appPath, 'app.conf.js') 28 | } 29 | 30 | if (fs.existsSync('mod.conf.js')) { 31 | modulePath = rootPath 32 | moduleConfPath = path.join(modulePath, 'mod.conf.js') 33 | } 34 | if (appConfPath && fs.existsSync(appConfPath)) { 35 | appConf = require(appConfPath) 36 | buildType = exports.BUILD_APP 37 | } else if (moduleConfPath && fs.existsSync(moduleConfPath)) { 38 | moduleConf = require(moduleConfPath) 39 | appPath = path.resolve(modulePath, '../..') 40 | appConfPath = path.join(appPath, 'app.conf.js') 41 | appConf = require(appConfPath) 42 | buildType = exports.BUILD_MODULE 43 | } else { 44 | buildType = exports.BUILD_NONE 45 | } 46 | 47 | return { 48 | appConf: appConf, 49 | moduleConf: moduleConf, 50 | buildType: buildType, 51 | appPath: appPath, 52 | modulePath: modulePath 53 | } 54 | } 55 | 56 | exports.getAppBuildConfig = function (appPath) { 57 | const buildConfig = require(path.join(appPath, 'config'))(_.merge) 58 | const defaultConfig = require('../config/build.conf') 59 | return _.merge(defaultConfig, buildConfig) 60 | } 61 | 62 | exports.getEntry = function ({ appConf, appPath, moduleList = [], buildConfig = {} }) { 63 | if (!moduleList.length) { 64 | moduleList = appConf.moduleList 65 | } 66 | const entry = {} 67 | const sourceRoot = buildConfig.sourceRoot 68 | 69 | if (moduleList) { 70 | moduleList.forEach(mod => { 71 | const pagePath = path.join(appPath, sourceRoot, mod, 'page') 72 | const pageDirInfo = fs.readdirSync(pagePath).filter(item => !IGNORE_FILE_REG.test(item)) 73 | pageDirInfo.forEach(item => { 74 | const ext = path.extname(item) 75 | if (!ext.length) { 76 | let entryPath = path.join(pagePath, item, `${item}.js`) 77 | if (!fs.existsSync(entryPath)) { 78 | entryPath = path.join(pagePath, item, 'index.js') 79 | } 80 | if (fs.existsSync(entryPath)) { 81 | entry[`${mod}/${item}`] = [ 82 | entryPath 83 | ] 84 | } 85 | } 86 | }) 87 | }) 88 | } else { 89 | const simpleEntry = path.join(appPath, sourceRoot, 'index.js') 90 | if (fs.existsSync(simpleEntry)) entry.index = new Array(simpleEntry) 91 | } 92 | 93 | return entry 94 | } 95 | 96 | exports.getPageHtml = function ({ appConf, appPath, moduleList = [], buildConfig = {} }) { 97 | if (!moduleList.length) { 98 | moduleList = appConf.moduleList 99 | } 100 | const pageHtml = {} 101 | const sourceRoot = buildConfig.sourceRoot 102 | if (moduleList) { 103 | moduleList.forEach(mod => { 104 | const pagePath = path.join(appPath, sourceRoot, mod, 'page') 105 | const pageDirInfo = fs.readdirSync(pagePath).filter(item => !IGNORE_FILE_REG.test(item)) 106 | if (!pageHtml[mod]) { 107 | pageHtml[mod] = {} 108 | } 109 | pageDirInfo.forEach(item => { 110 | const ext = path.extname(item) 111 | if (!ext.length) { 112 | let filename = `${item}.html` 113 | let pageHtmlPath = path.join(pagePath, item, filename) 114 | if (!fs.existsSync(pageHtmlPath)) { 115 | filename = 'index.html' 116 | pageHtmlPath = path.join(pagePath, item, filename) 117 | } 118 | if (fs.existsSync(pageHtmlPath)) { 119 | let title = '' 120 | try { 121 | const htmlContents = String(fs.readFileSync(pageHtmlPath)) 122 | const matchs = htmlContents.match(/]*>([^<]+)<\/title>/) 123 | if (matchs) { 124 | title = matchs[1] 125 | } 126 | } catch (e) { 127 | title = '' 128 | } 129 | pageHtml[mod][item] = { 130 | filepath: pageHtmlPath, 131 | filename, 132 | title 133 | } 134 | } 135 | } 136 | }) 137 | }) 138 | } else { 139 | const simpleHtml = path.join(appPath, sourceRoot, 'index.html') 140 | if (fs.existsSync(simpleHtml)) { 141 | pageHtml.index = simpleHtml 142 | } 143 | } 144 | 145 | return pageHtml 146 | } 147 | 148 | exports.createCompiler = function (webpack, config) { 149 | let compiler 150 | try { 151 | compiler = webpack(config) 152 | } catch (err) { 153 | console.log(err) 154 | console.log(chalk.red('Failed to compile.')) 155 | console.log() 156 | console.log(err.message || err) 157 | console.log() 158 | process.exit(1) 159 | } 160 | return compiler 161 | } 162 | 163 | exports.prepareUrls = function (protocol, host, port) { 164 | const formatUrl = hostname => 165 | url.format({ 166 | protocol, 167 | hostname, 168 | port, 169 | pathname: '/' 170 | }) 171 | 172 | const isUnspecifiedHost = host === '0.0.0.0' || host === '::' 173 | let prettyHost, lanUrlForConfig, lanUrlForTerminal 174 | if (isUnspecifiedHost) { 175 | prettyHost = 'localhost' 176 | try { 177 | lanUrlForConfig = getLocalIp() 178 | if (lanUrlForConfig) { 179 | if (isPrivate(lanUrlForConfig)) { 180 | lanUrlForTerminal = formatUrl(lanUrlForConfig) 181 | } else { 182 | lanUrlForConfig = undefined 183 | } 184 | } 185 | } catch (err) { } 186 | } else { 187 | prettyHost = host 188 | } 189 | const localUrlForTerminal = formatUrl(prettyHost) 190 | const localUrlForBrowser = formatUrl(prettyHost) 191 | return { 192 | lanUrlForConfig, 193 | lanUrlForTerminal, 194 | localUrlForTerminal, 195 | localUrlForBrowser 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/build/serve.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const chalk = require('chalk') 3 | const webpack = require('webpack') 4 | const WebpackDevServer = require('webpack-dev-server') 5 | const webpackMerge = require('webpack-merge') 6 | const HtmlWebpackPlugin = require('html-webpack-plugin') 7 | const HtmlWebpackHarddiskPlugin = require('html-webpack-harddisk-plugin') 8 | const ora = require('ora') 9 | 10 | const { getRootPath, isEmptyObject, formatTime } = require('../util') 11 | const open = require('../util/open') 12 | const formatWebpackMessage = require('../util/format_webpack_message') 13 | 14 | const { 15 | getConf, 16 | getAppBuildConfig, 17 | getEntry, 18 | getPageHtml, 19 | createCompiler, 20 | prepareUrls, 21 | BUILD_APP, 22 | BUILD_MODULE, 23 | BUILD_NONE 24 | } = require('./index') 25 | 26 | module.exports = function serve (args, options) { 27 | const conf = getConf() 28 | conf.args = args 29 | switch (conf.buildType) { 30 | case BUILD_APP: 31 | serveApp(conf, options) 32 | break 33 | case BUILD_MODULE: 34 | serveModule(conf, options) 35 | break 36 | case BUILD_NONE: 37 | console.log(chalk.red('✖ Serve error, the current directory is not an app or a module!')) 38 | console.log(chalk.bold('GoodBye!')) 39 | break 40 | } 41 | } 42 | 43 | function serveCore (conf, options, sample) { 44 | const isSample = sample || false 45 | const serveSpinner = ora(`Starting development server, please wait🤡~`).start() 46 | const appConf = conf.appConf 47 | const buildConfig = getAppBuildConfig(conf.appPath) 48 | const { 49 | protocol, 50 | host, 51 | port, 52 | publicPath, 53 | outputRoot, 54 | chunkDirectory 55 | // staticDirectory 56 | } = buildConfig 57 | conf.buildConfig = buildConfig 58 | const entry = getEntry(conf) 59 | if (isEmptyObject(entry)) { 60 | serveSpinner.fail(chalk.red(`No file to compile, please check if the ${chalk.bold('page')} directories are empty!`)) 61 | console.log(chalk.bold('GoodBye!')) 62 | process.exit(1) 63 | } 64 | const urls = prepareUrls(protocol, host, port) 65 | const { template, framework, platform } = appConf 66 | let customWebpackConf 67 | if (template === 'h5') { 68 | const h5TemplateConf = require('../config/h5_template.conf')(webpack, buildConfig) 69 | const h5TemplateWebpackConf = webpackMerge(h5TemplateConf.BASE, h5TemplateConf.DEV) 70 | customWebpackConf = webpackMerge(h5TemplateWebpackConf, buildConfig.webpack) 71 | } else { 72 | customWebpackConf = buildConfig.webpack 73 | } 74 | const webpackBaseConf = require('../config/base.conf')(conf.appPath, buildConfig, template, platform, framework) 75 | const webpackDevConf = require('../config/dev.conf')(conf.appPath, buildConfig, template, platform, framework) 76 | let webpackConf = webpackMerge(webpackBaseConf, webpackDevConf) 77 | const htmlPages = getPageHtml(conf) 78 | let htmlPlugins 79 | if (!isSample) { 80 | htmlPlugins = [ 81 | new HtmlWebpackPlugin({ 82 | title: conf.appConf.app, 83 | filename: 'index.html', 84 | template: path.join(getRootPath(), 'src', 'config', 'sitemap_template.ejs'), 85 | alwaysWriteToDisk: true, 86 | data: { 87 | htmlPages 88 | } 89 | }) 90 | ] 91 | for (const mod in htmlPages) { 92 | for (const page in htmlPages[mod]) { 93 | const pageItem = htmlPages[mod][page] 94 | htmlPlugins.push(new HtmlWebpackPlugin({ 95 | filename: `${mod}/${pageItem.filename}`, 96 | template: pageItem.filepath, 97 | alwaysWriteToDisk: true, 98 | chunks: [`${mod}/${page}`] 99 | })) 100 | } 101 | } 102 | } else { 103 | htmlPlugins = [ 104 | new HtmlWebpackPlugin({ 105 | template: htmlPages['index'] 106 | }) 107 | ] 108 | } 109 | 110 | htmlPlugins.push(new HtmlWebpackHarddiskPlugin()) 111 | for (const key in entry) { 112 | const entryItem = entry[key] 113 | entryItem.unshift(require.resolve('webpack/hot/dev-server')) 114 | entryItem.unshift(require.resolve('webpack-dev-server/client') + '?/') 115 | } 116 | 117 | webpackConf.entry = entry 118 | const contentBase = path.join(conf.appPath, outputRoot) 119 | webpackConf.output = { 120 | path: contentBase, 121 | filename: '[name].js', 122 | publicPath, 123 | chunkFilename: `${chunkDirectory}/[name].chunk.js` 124 | } 125 | webpackConf.plugins = webpackConf.plugins.concat(htmlPlugins) 126 | webpackConf = webpackMerge(webpackConf, customWebpackConf) 127 | const compiler = createCompiler(webpack, webpackConf) 128 | const webpackDevServerConf = require('../config/devServer.conf')({ 129 | publicPath, 130 | contentBase, 131 | protocol, 132 | host, 133 | publicUrl: urls.lanUrlForConfig 134 | }) 135 | const server = new WebpackDevServer(compiler, webpackDevServerConf) 136 | server.listen(port, host, err => { 137 | if (err) { 138 | return console.log(err) 139 | } 140 | }) 141 | let isFirstCompile = true 142 | compiler.plugin('invalid', filepath => { 143 | console.log(chalk.grey(`[${formatTime()}]Modified: ${filepath}`)) 144 | serveSpinner.text = 'Compiling...🤡~' 145 | serveSpinner.render() 146 | }) 147 | compiler.plugin('done', stats => { 148 | const { errors, warnings } = formatWebpackMessage(stats.toJson({}, true)) 149 | const isSuccess = !errors.length && !warnings.length 150 | if (isSuccess) { 151 | serveSpinner.succeed(chalk.green('Compile successfully!\n')) 152 | } 153 | if (errors.length) { 154 | errors.splice(1) 155 | serveSpinner.fail(chalk.red('Compile failed!\n')) 156 | console.log(errors.join('\n\n')) 157 | console.log() 158 | } 159 | if (isFirstCompile) { 160 | console.log(chalk.cyan('> Listening at ' + urls.lanUrlForTerminal)) 161 | console.log(chalk.cyan('> Listening at ' + urls.localUrlForBrowser)) 162 | console.log() 163 | open(urls.localUrlForBrowser) 164 | isFirstCompile = false 165 | } 166 | }) 167 | } 168 | 169 | function serveApp (conf, options) { 170 | if (conf.appConf.moduleList) { 171 | conf.moduleList = conf.args 172 | delete conf.args 173 | if (!conf.moduleList || !conf.moduleList.length) { 174 | conf.moduleList = conf.appConf.moduleList 175 | } 176 | console.log(`Current building modules ${chalk.bold(conf.moduleList.join(' '))}!`) 177 | serveCore(conf, options) 178 | } else { 179 | if (conf.args.length) { 180 | console.log(`Is a Simple App, please use command ${chalk.bold('ath2 s')}!`) 181 | return 182 | } 183 | serveCore(conf, options, true) 184 | } 185 | } 186 | 187 | function serveModule (conf, options) { 188 | const moduleConf = conf.moduleConf 189 | conf.moduleList = [moduleConf.module] 190 | delete conf.args 191 | console.log(`Current building module ${chalk.bold(conf.moduleList[0])}!`) 192 | serveCore(conf, options) 193 | } 194 | -------------------------------------------------------------------------------- /src/config/base.conf.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const webpack = require('webpack') 3 | 4 | const Util = require('../util') 5 | const browserList = require('./browser_list') 6 | 7 | module.exports = function (appPath, buildConfig, template, platform, framework) { 8 | const { env = {}, defineConstants = {}, staticDirectory, htmlSnippetDirectory = ['component'] } = buildConfig 9 | let imgName, mediaName, fontName, extName 10 | const imgLimit = (buildConfig.module && buildConfig.module.base64 && buildConfig.module.base64.imageLimit) || 2000 11 | const fontLimit = (buildConfig.module && buildConfig.module.base64 && buildConfig.module.base64.fontLimit) || 2000 12 | if (template === 'h5') { 13 | imgName = 'img/[name].[hash:8].[ext]' 14 | mediaName = fontName = extName = 'plugin/[name].[hash:8].[ext]' 15 | } else { 16 | imgName = `${staticDirectory}/images/[name].[hash:8].[ext]` 17 | mediaName = `${staticDirectory}/media/[name].[hash:8].[ext]` 18 | fontName = `${staticDirectory}/fonts/[name].[hash:8].[ext]` 19 | extName = `${staticDirectory}/ext/[name].[hash:8].[ext]` 20 | } 21 | const jsConfUse = [ 22 | { 23 | loader: require.resolve('babel-loader'), 24 | options: { 25 | cacheDirectory: true, 26 | presets: [ 27 | [require('babel-preset-react-app'), { flow: false, typescript: true }] 28 | ], 29 | plugins: [ 30 | require('@babel/plugin-proposal-export-default-from').default 31 | ].concat( 32 | platform === 'pc' ? [ 33 | require('babel-plugin-transform-es3-member-expression-literals'), 34 | require('babel-plugin-transform-es3-property-literals') 35 | ] : [] 36 | ).concat( 37 | framework === 'nerv' ? [ 38 | [require('@babel/plugin-transform-react-jsx').default, { 39 | pragma: 'Nerv.createElement' 40 | }] 41 | ] : [] 42 | ).concat( 43 | framework === 'react' ? [ 44 | [require('@babel/plugin-transform-react-jsx').default] 45 | ] : [] 46 | ) 47 | } 48 | } 49 | ] 50 | const alias = framework === 'nerv' 51 | ? { 52 | '@APP': appPath, 53 | react: 'nervjs', 54 | 'react-dom': 'nervjs' 55 | } 56 | : { 57 | '@APP': appPath 58 | } 59 | if (template !== 'h5' && framework !== 'nerv') { 60 | jsConfUse.push({ 61 | loader: require.resolve('hot-module-accept') 62 | }) 63 | } 64 | return { 65 | module: { 66 | rules: [ 67 | { 68 | oneOf: [ 69 | { 70 | test: /\.js|jsx$/, 71 | exclude: /node_modules/, 72 | use: jsConfUse 73 | }, 74 | { 75 | test: /\.html$/, 76 | include: new RegExp(htmlSnippetDirectory.join('|')), 77 | loader: require.resolve('html-loader') 78 | }, 79 | { 80 | test: /\.vue$/, 81 | loader: require.resolve('vue-loader') 82 | }, 83 | { 84 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, 85 | loader: require.resolve('file-loader'), 86 | options: { 87 | name: mediaName 88 | } 89 | }, 90 | { 91 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 92 | loader: require.resolve('file-loader'), 93 | options: { 94 | limit: fontLimit, 95 | name: fontName 96 | } 97 | }, 98 | { 99 | test: /\.(png|jpe?g|gif|bpm|svg)(\?.*)?$/, 100 | loader: require.resolve('url-loader'), 101 | options: { 102 | limit: imgLimit, 103 | name: imgName 104 | } 105 | }, 106 | { 107 | exclude: /\.js|\.css|\.scss|\.sass|\.html|\.json|\.ejs$/, 108 | loader: require.resolve('url-loader'), 109 | options: { 110 | limit: 2000, 111 | name: extName 112 | } 113 | } 114 | ] 115 | } 116 | ] 117 | }, 118 | resolve: { 119 | modules: [path.join(Util.getRootPath(), 'node_modules'), 'node_modules'], 120 | alias: alias 121 | }, 122 | resolveLoader: { 123 | modules: [path.join(Util.getRootPath(), 'node_modules'), 'node_modules'] 124 | }, 125 | plugins: [ 126 | new webpack.LoaderOptionsPlugin({ 127 | htmlLoader: { 128 | attrs: ['img:src', 'link:href', 'data-src'] 129 | } 130 | }), 131 | new webpack.DefinePlugin(Object.assign({ 132 | 'process.env': env 133 | }, defineConstants)) 134 | ] 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/config/browser_list.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | pc: [ 3 | 'ie >= 8', 4 | 'Chrome >= 21', 5 | 'Firefox >= 1', 6 | 'Edge >= 13', 7 | 'last 3 versions' 8 | ], 9 | mobile: [ 10 | 'Android >= 4', 11 | 'iOS >= 6' 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /src/config/build.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | sourceRoot: 'src', 3 | outputRoot: 'dist', 4 | publicPath: '/', 5 | staticDirectory: 'static', 6 | chunkDirectory: 'chunk', 7 | port: 8080, 8 | host: '0.0.0.0', 9 | protocol: 'http' 10 | } 11 | -------------------------------------------------------------------------------- /src/config/dev.conf.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack') 2 | 3 | const { getPostcssPlugins } = require('./postcss.conf') 4 | 5 | module.exports = function (appPath, buildConfig, template, platform, framework) { 6 | return { 7 | mode: 'development', 8 | devtool: 'cheap-module-eval-source-map', 9 | module: { 10 | rules: [ 11 | { 12 | oneOf: [ 13 | { 14 | test: /\.(css|scss|sass)(\?.*)?$/, 15 | use: [ 16 | require.resolve('style-loader'), 17 | { 18 | loader: require.resolve('css-loader'), 19 | options: { 20 | importLoaders: 1 21 | } 22 | }, 23 | { 24 | loader: require.resolve('postcss-loader'), 25 | options: { 26 | ident: 'postcss', 27 | plugins: () => getPostcssPlugins(buildConfig, platform, template) 28 | } 29 | }, 30 | require.resolve('sass-loader') 31 | ] 32 | } 33 | ] 34 | } 35 | ] 36 | }, 37 | plugins: [ 38 | new webpack.HotModuleReplacementPlugin(), 39 | new webpack.NoEmitOnErrorsPlugin() 40 | ] 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/config/devServer.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function ({publicPath, contentBase, protocol, host, publicUrl}) { 2 | return { 3 | disableHostCheck: process.env.DANGEROUSLY_DISABLE_HOST_CHECK === 'true', 4 | compress: true, 5 | contentBase, 6 | watchContentBase: true, 7 | hot: true, 8 | inline: true, 9 | quiet: true, 10 | publicPath, 11 | // stats: "errors-only", 12 | watchOptions: { 13 | ignored: /node_modules/ 14 | }, 15 | https: protocol === 'https', 16 | host: host, 17 | overlay: true, 18 | historyApiFallback: { 19 | disableDotRule: true 20 | }, 21 | public: publicUrl 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/config/dll.conf.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const webpack = require('webpack') 3 | 4 | module.exports = function (contextPath, buildConfig, libConfig) { 5 | const { env = {}, defineConstants = {} } = buildConfig 6 | const name = libConfig.name || '[name]' 7 | return { 8 | entry: { 9 | vendor: libConfig.libs 10 | }, 11 | module: { 12 | rules: [ 13 | { 14 | enforce: 'post', 15 | test: /\.js|jsx$/, 16 | loader: require.resolve('es3ify-loader') 17 | } 18 | ] 19 | }, 20 | resolve: { 21 | mainFields: ['main'] 22 | }, 23 | output: { 24 | path: contextPath, 25 | filename: `${name}.dll.js`, 26 | library: `${name}_library` 27 | }, 28 | plugins: [ 29 | new webpack.optimize.UglifyJsPlugin({ 30 | beautify: false, 31 | mangle: { 32 | screw_ie8: false, 33 | keep_fnames: true, 34 | properties: false, 35 | keep_quoted: true 36 | }, 37 | compress: { 38 | warnings: false, 39 | screw_ie8: false, 40 | properties: false 41 | }, 42 | output: { 43 | keep_quoted_props: true 44 | }, 45 | comments: false 46 | }), 47 | new webpack.DefinePlugin(Object.assign({ 48 | 'process.env': env 49 | }, defineConstants)), 50 | 51 | new webpack.DllPlugin({ 52 | path: path.join(contextPath, `${name}-manifest.json`), 53 | name: `${name}_library`, 54 | context: contextPath 55 | }) 56 | ] 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/config/h5_template.conf.js: -------------------------------------------------------------------------------- 1 | const zeptoPath = require.resolve('zepto/dist/zepto.min.js') 2 | 3 | module.exports = function (webpack, buildConfig) { 4 | const HeadJavascriptInjectPlugin = createFragment(buildConfig) 5 | return { 6 | BASE: { 7 | module: { 8 | rules: [{ 9 | test: zeptoPath, 10 | use: [{ 11 | loader: 'exports-loader', 12 | options: 'window.$' 13 | }, 'script-loader'] 14 | }] 15 | }, 16 | plugins: [ 17 | // 默认引入zepto 18 | new webpack.ProvidePlugin({ 19 | $: zeptoPath, 20 | Zepto: zeptoPath, 21 | 'window.Zepto': zeptoPath 22 | }), 23 | new HeadJavascriptInjectPlugin() 24 | ] 25 | }, 26 | DEV: { 27 | 28 | }, 29 | PROD: { 30 | 31 | } 32 | } 33 | } 34 | 35 | const createFragment = function (buildConfig) { 36 | const headJavascript = ` 37 | 38 | 119 | ` 120 | 121 | function HeadJavascriptInjectPlugin (options) { 122 | // Configure your plugin with options... 123 | } 124 | 125 | HeadJavascriptInjectPlugin.prototype.apply = function (compiler) { 126 | compiler.plugin('compilation', function (compilation) { 127 | compilation.plugin('html-webpack-plugin-before-html-processing', function (htmlPluginData, callback) { 128 | let html = htmlPluginData.html 129 | html = html.replace('{{{__HEAD_JAVASCRIPT__}}}', headJavascript) 130 | htmlPluginData.html = html 131 | callback(null, htmlPluginData) 132 | }) 133 | }) 134 | } 135 | 136 | return HeadJavascriptInjectPlugin 137 | } 138 | -------------------------------------------------------------------------------- /src/config/postcss.conf.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash') 2 | const postcss = require('postcss') 3 | const autoprefixer = require('autoprefixer') 4 | const pxtorem = require('postcss-plugin-px2rem') 5 | const assets = require('postcss-assets') 6 | const sprites = require('postcss-sprites') 7 | 8 | const browserList = require('./browser_list') 9 | const { isEmptyObject } = require('../util') 10 | 11 | const plugins = [] 12 | 13 | exports.getPostcssPlugins = function (buildConfig = {}, platform = 'pc', template = 'complete') { 14 | const useModuleConf = buildConfig.module || {} 15 | const customPostcssConf = useModuleConf.postcss || {} 16 | const customPlugins = customPostcssConf.plugins || [] 17 | plugins.push(require('postcss-flexbugs-fixes')) 18 | const defaultAutoprefixerConf = { 19 | overrideBrowserslist: browserList[platform], 20 | flexbox: 'no-2009' 21 | } 22 | 23 | const customPostcssAssets = customPostcssConf.assets || {} 24 | if (customPostcssAssets.enable) { 25 | plugins.push(assets(_.merge({ 26 | cache: true 27 | }, customPostcssConf.assets))) 28 | } 29 | 30 | const customSpritesConf = customPostcssConf.sprites || {} 31 | if (customSpritesConf.enable) { 32 | const spritePath = template === 'h5' ? 'dist/img/' : 'dist/static/images/' 33 | const preSpritesConf = { 34 | spritePath: spritePath, 35 | retina: true, 36 | relativeTo: 'rule', 37 | spritesmith: { 38 | algorithm: 'left-right', 39 | padding: 2 40 | }, 41 | verbose: false, 42 | // 将 img 目录下的子目录作为分组,子目录下的 png 图片会合成雪碧图 43 | groupBy: function (image) { 44 | const reg = template === 'h5' 45 | ? /img\/(\S+)\/\S+\.png$/.exec(image.url) 46 | : /images\/(\S+)\/\S+\.png$/.exec(image.url) 47 | const groupName = reg ? reg[1] : reg 48 | image.ratio = 1 49 | if (groupName) { 50 | let ratio = /@(\d+)x$/gi.exec(groupName) 51 | if (ratio) { 52 | ratio = ratio[1] 53 | while (ratio > 10) { 54 | ratio = ratio / 10 55 | } 56 | image.ratio = ratio 57 | } 58 | } 59 | return groupName ? Promise.resolve(groupName) : Promise.reject(new Error(`The image ${image.url} is incorrect.`)) 60 | }, 61 | // 非 img 子目录下面的 png 不合 62 | filterBy: function (image) { 63 | const reg = template === 'h5' 64 | ? /img\/(\S+)\/\S+\.png$/.test(image.url) 65 | : /images\/(\S+)\/\S+\.png$/.test(image.url) 66 | return reg ? Promise.resolve() : Promise.reject(new Error(`The image ${image.url} is incorrect.`)) 67 | } 68 | } 69 | const updateRule = require('postcss-sprites/lib/core').updateRule 70 | if (buildConfig.enableREM) { 71 | preSpritesConf.hooks = { 72 | onUpdateRule: function (rule, token, image) { 73 | updateRule(rule, token, image) 74 | 75 | rule.insertAfter(rule.last, postcss.decl({ 76 | prop: 'background-size', 77 | value: image.spriteWidth / image.ratio + 'px ' + image.spriteHeight / image.ratio + 'px;' 78 | })) 79 | } 80 | } 81 | } 82 | plugins.push(sprites(_.merge(preSpritesConf, customSpritesConf))) 83 | } 84 | 85 | const customAutoprefixerConf = customPostcssConf.autoprefixer || {} 86 | if (isEmptyObject(customAutoprefixerConf) || customAutoprefixerConf.enable) { 87 | plugins.push(autoprefixer(_.merge({}, defaultAutoprefixerConf, customAutoprefixerConf))) 88 | } 89 | 90 | const customPxtoremConf = customPostcssConf.pxtorem || {} 91 | const addPxtoRem = function () { 92 | let preConf = {} 93 | if (buildConfig.designLayoutWidth && buildConfig.baseSize) { 94 | preConf = { 95 | rootValue: buildConfig.designLayoutWidth / buildConfig.baseSize 96 | } 97 | } 98 | plugins.push(pxtorem(_.merge(preConf, customPxtoremConf))) 99 | } 100 | if (template === 'h5') { 101 | if (buildConfig.enableREM && customPxtoremConf.enable) addPxtoRem() 102 | } else { 103 | if (customPxtoremConf.enable) addPxtoRem() 104 | } 105 | 106 | return plugins.concat(customPlugins) 107 | } 108 | -------------------------------------------------------------------------------- /src/config/prod.conf.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const MiniCssExtractPlugin = require('mini-css-extract-plugin') 3 | const ManifestPlugin = require('webpack-manifest-plugin') 4 | const CleanWebpackPlugin = require('clean-webpack-plugin') 5 | const TerserPlugin = require('terser-webpack-plugin') 6 | 7 | const { isEmptyObject } = require('../util') 8 | 9 | const { getPostcssPlugins } = require('./postcss.conf') 10 | 11 | module.exports = function (appPath, buildConfig, template, platform, framework) { 12 | const { sourceMap, output = {}, sourceRoot, outputRoot, library } = buildConfig 13 | const outputCSS = output.css || {} 14 | const cssLoaders = [] 15 | const cssExtractPlugins = [] 16 | const devtool = template === 'h5' ? '' : 'hidden-source-map' 17 | const imgLoaders = [] 18 | const miniCssExtractPluginOption = buildConfig.miniCssExtractPluginOption || {} 19 | buildConfig.module = buildConfig.module || {} 20 | const defaultCSSCompressConf = { 21 | mergeRules: false, 22 | mergeIdents: false, 23 | reduceIdents: false, 24 | discardUnused: false, 25 | minifySelectors: false 26 | } 27 | const defaultJSCompressConf = { 28 | keep_fnames: true, 29 | output: { 30 | comments: false, 31 | keep_quoted_props: true, 32 | quote_keys: true, 33 | beautify: false 34 | }, 35 | warnings: false 36 | } 37 | const compress = Object.assign({}, { 38 | css: defaultCSSCompressConf, 39 | js: defaultJSCompressConf 40 | }, buildConfig.module.compress) 41 | 42 | cssLoaders.push({ 43 | test: /\.(css|scss|sass)(\?.*)?$/, 44 | rules: [{ 45 | use: [ 46 | { 47 | loader: MiniCssExtractPlugin.loader 48 | }, 49 | { 50 | loader: require.resolve('css-loader'), 51 | options: { 52 | importLoaders: 1, 53 | sourceMap 54 | } 55 | }, 56 | { 57 | loader: require.resolve('postcss-loader'), 58 | options: { 59 | ident: 'postcss', 60 | plugins: () => getPostcssPlugins(buildConfig, platform, template) 61 | } 62 | }, 63 | require.resolve('sass-loader') 64 | ] 65 | }] 66 | }) 67 | cssExtractPlugins.push(new MiniCssExtractPlugin(Object.assign({ 68 | filename: 'css/[name].css', 69 | chunkFilename: 'css/[name].css' 70 | }, miniCssExtractPluginOption))) 71 | 72 | const plugins = [ 73 | new CleanWebpackPlugin(path.join(appPath, outputRoot), { 74 | verbose: false, 75 | exclude: [library && library.directory ? library.directory : ''] 76 | }), 77 | ...cssExtractPlugins 78 | ] 79 | 80 | if (template !== 'h5') { 81 | plugins.push( 82 | new ManifestPlugin({ 83 | fileName: 'asset-manifest.json' 84 | }) 85 | ) 86 | } 87 | 88 | return { 89 | mode: 'production', 90 | devtool: devtool, 91 | module: { 92 | rules: [ 93 | { 94 | oneOf: [ 95 | ...cssLoaders, 96 | { 97 | test: /\.(png|jpe?g|gif|bpm|svg)(\?.*)?$/, 98 | use: imgLoaders 99 | } 100 | ] 101 | } 102 | ] 103 | }, 104 | resolve: { 105 | mainFields: ['main'] 106 | }, 107 | plugins: plugins, 108 | optimization: { 109 | minimizer: [ 110 | new TerserPlugin({ 111 | cache: true, 112 | parallel: true, 113 | sourceMap, 114 | terserOptions: Object.assign({}, { 115 | ie8: platform === 'pc' 116 | }, compress.js) 117 | }) 118 | ], 119 | splitChunks: { 120 | name: false 121 | } 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/config/sitemap_template.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= htmlWebpackPlugin.options.title %> 7 | 8 | 9 | 10 |
11 |
12 |

Athena SiteMap

13 |

<%= htmlWebpackPlugin.options.title %>项目

14 |
15 | <% const htmlPages = htmlWebpackPlugin.options.data.htmlPages %> 16 | <% for (const mod in htmlPages) { %> 17 |
18 |

<%= mod %>

19 | 25 |
26 | <% } %> 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /src/create/app.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra') 2 | const path = require('path') 3 | const chalk = require('chalk') 4 | const inquirer = require('inquirer') 5 | const uuid = require('uuid') 6 | 7 | const CreateBase = require('./base') 8 | 9 | const { 10 | shouldUseYarn, 11 | shouldUseCnpm 12 | } = require('../util') 13 | 14 | class App extends CreateBase { 15 | constructor (options) { 16 | super() 17 | this.rootPath = this._rootPath 18 | this.conf = Object.assign({ 19 | appName: null, 20 | description: '', 21 | framework: null, 22 | template: null, 23 | platform: null, 24 | sass: false 25 | }, options) 26 | } 27 | 28 | init () { 29 | console.log(chalk.green(`Allo ${chalk.green.bold(this.username)}! Prepare to create a new app!`)) 30 | console.log('Need help? Go and open issue: https://github.com/o2team/athena2/issues/new') 31 | console.log() 32 | } 33 | 34 | create () { 35 | this.ask() 36 | .then(answers => { 37 | this.askOther(answers.template || this.conf.template) 38 | .then(otherAnswers => { 39 | const date = new Date() 40 | this.conf = Object.assign(this.conf, answers, otherAnswers) 41 | this.conf.appId = uuid.v1() 42 | this.conf.date = `${date.getFullYear()}-${(date.getMonth() + 1)}-${date.getDate()}` 43 | this.write() 44 | }) 45 | }) 46 | } 47 | 48 | ask () { 49 | const prompts = [] 50 | const conf = this.conf 51 | if (typeof conf.appName !== 'string') { 52 | prompts.push({ 53 | type: 'input', 54 | name: 'appName', 55 | message: 'Please give me an app name!', 56 | validate (input) { 57 | if (!input) { 58 | return 'App name can not be empty!' 59 | } 60 | if (fs.existsSync(input)) { 61 | return 'Already existing the app name, choose another name please!' 62 | } 63 | return true 64 | } 65 | }) 66 | } else if (fs.existsSync(conf.appName)) { 67 | prompts.push({ 68 | type: 'input', 69 | name: 'appName', 70 | message: 'Already existing the app, choose another name please!', 71 | validate (input) { 72 | if (!input) { 73 | return 'App name can not be empty!' 74 | } 75 | if (fs.existsSync(input)) { 76 | return 'The app name is still repeated!' 77 | } 78 | return true 79 | } 80 | }) 81 | } 82 | 83 | if (typeof conf.description !== 'string') { 84 | prompts.push({ 85 | type: 'input', 86 | name: 'description', 87 | message: 'Please tell me your app\'s description!' 88 | }) 89 | } 90 | 91 | const platformChoices = [{ 92 | name: 'PC', 93 | value: 'pc' 94 | }, { 95 | name: 'Mobile', 96 | value: 'mobile' 97 | }] 98 | 99 | if (typeof conf.platform !== 'string') { 100 | prompts.push({ 101 | type: 'list', 102 | name: 'platform', 103 | message: 'Please choose your app platform', 104 | choices: platformChoices 105 | }) 106 | } else { 107 | let isPlatformExist = false 108 | platformChoices.forEach(item => { 109 | if (item.value === conf.template) { 110 | isPlatformExist = true 111 | } 112 | }) 113 | if (!isPlatformExist) { 114 | console.log(chalk.red('The platform you choose is not exist!')) 115 | console.log(chalk.red('Currently there are the following platforms to choose from:')) 116 | console.log() 117 | platformChoices.forEach(item => { 118 | console.log(chalk.green(`- ${item.name}`)) 119 | }) 120 | process.exit(1) 121 | } 122 | } 123 | 124 | const templateChoices = [{ 125 | name: 'Complete(Complex project like multi-page application should use this template)', 126 | value: 'complete' 127 | }, { 128 | name: 'Simple(Simple template like single-page application for simple project)', 129 | value: 'simple' 130 | }, { 131 | name: 'H5(Like Interactive game OR Operational activities)', 132 | value: 'h5' 133 | }] 134 | 135 | if (typeof conf.template !== 'string' && !conf.h5) { 136 | prompts.push({ 137 | type: 'list', 138 | name: 'template', 139 | message: 'Please choose your favorite template', 140 | choices: templateChoices 141 | }) 142 | } else if (conf.h5) { 143 | conf.template = 'h5' 144 | } else { 145 | let isTemplateExist = false 146 | templateChoices.forEach(item => { 147 | if (item.value === conf.template) { 148 | isTemplateExist = true 149 | } 150 | }) 151 | if (!isTemplateExist) { 152 | console.log(chalk.red('The template you choose is not exist!')) 153 | console.log(chalk.red('Currently there are the following templates to choose from:')) 154 | console.log() 155 | templateChoices.forEach(item => { 156 | console.log(chalk.green(`- ${item.name}`)) 157 | }) 158 | process.exit(1) 159 | } 160 | } 161 | 162 | return inquirer.prompt(prompts) 163 | } 164 | 165 | askOther (template) { 166 | const newPrompts = [] 167 | const conf = this.conf 168 | const frameworkChoices = [{ 169 | name: 'Nerv', 170 | value: 'nerv' 171 | }, { 172 | name: 'React', 173 | value: 'react' 174 | }, { 175 | name: 'Vue', 176 | value: 'vue' 177 | }] 178 | if (template === 'h5') { 179 | conf.framework = 'base' 180 | if (typeof conf.h5 !== 'string') { 181 | newPrompts.push({ 182 | type: 'input', 183 | name: 'h5', 184 | message: 'Please tell me which h5 template you prepare to use, default to base!', 185 | validate (input) { 186 | if (!input) { 187 | conf.h5 = 'base' 188 | } 189 | return true 190 | } 191 | }) 192 | } 193 | } else { 194 | if (typeof conf.framework !== 'string') { 195 | newPrompts.push({ 196 | type: 'list', 197 | name: 'framework', 198 | message: 'Please choose your favorite framework', 199 | choices: frameworkChoices 200 | }) 201 | } else { 202 | let isFrameworkExist = false 203 | frameworkChoices.forEach(item => { 204 | if (item.value === conf.framework) { 205 | isFrameworkExist = true 206 | } 207 | }) 208 | if (!isFrameworkExist) { 209 | console.log(chalk.red('The framework you choose is not exist!')) 210 | console.log(chalk.red('Currently there are the following frameworks to choose from:')) 211 | console.log() 212 | frameworkChoices.forEach(item => { 213 | console.log(chalk.green(`- ${item.name}`)) 214 | }) 215 | process.exit(1) 216 | } 217 | } 218 | 219 | if (!conf.sass) { 220 | newPrompts.push({ 221 | type: 'confirm', 222 | name: 'sass', 223 | message: 'Do you wanna use sass?' 224 | }) 225 | } 226 | } 227 | 228 | return inquirer.prompt(newPrompts) 229 | } 230 | 231 | write () { 232 | const { template } = this.conf 233 | 234 | const templateCreate = require(path.join(this.templatePath(), template, 'index.js')) 235 | templateCreate.app(this, this.conf, { 236 | shouldUseYarn, 237 | shouldUseCnpm 238 | }) 239 | } 240 | } 241 | 242 | module.exports = App 243 | -------------------------------------------------------------------------------- /src/create/base.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash') 2 | const path = require('path') 3 | const fs = require('fs-extra') 4 | const memFs = require('mem-fs') 5 | const editor = require('mem-fs-editor') 6 | 7 | const { 8 | getConfig, 9 | getSystemUsername, 10 | getRootPath, 11 | setConfig 12 | } = require('../util') 13 | 14 | class CreateBase { 15 | constructor () { 16 | const store = memFs.create() 17 | this.fs = editor.create(store) 18 | this.username = getConfig().username 19 | this.appConfPath = this.destinationPath('app.conf.js') 20 | this._ = _ 21 | 22 | if (!this.username) { 23 | this.username = getSystemUsername() 24 | setConfig({ username: this.username }) 25 | } 26 | this.sourceRoot(path.join(getRootPath())) 27 | this.init() 28 | } 29 | 30 | init () {} 31 | 32 | sourceRoot (rootPath) { 33 | if (typeof rootPath === 'string') { 34 | this._rootPath = path.resolve(rootPath) 35 | } 36 | if (!fs.existsSync(this._rootPath)) { 37 | fs.ensureDirSync(this._rootPath) 38 | } 39 | return this._rootPath 40 | } 41 | 42 | templatePath () { 43 | let filepath = path.join.apply(path, arguments) 44 | if (!path.isAbsolute(filepath)) { 45 | filepath = path.join(this._rootPath, 'templates', filepath) 46 | } 47 | return filepath 48 | } 49 | 50 | destinationRoot (rootPath) { 51 | if (typeof rootPath === 'string') { 52 | this._destinationRoot = path.resolve(rootPath) 53 | if (!fs.existsSync(rootPath)) { 54 | fs.ensureDirSync(rootPath) 55 | } 56 | process.chdir(rootPath) 57 | } 58 | return this._destinationRoot || process.cwd() 59 | } 60 | 61 | destinationPath () { 62 | let filepath = path.join.apply(path, arguments) 63 | if (!path.isAbsolute(filepath)) { 64 | filepath = path.join(this.destinationRoot(), filepath) 65 | } 66 | return filepath 67 | } 68 | 69 | template (template, type, source, dest, data, options) { 70 | if (typeof dest !== 'string') { 71 | options = data 72 | data = dest 73 | dest = source 74 | } 75 | this.fs.copyTpl( 76 | this.templatePath(template, type, source), 77 | this.destinationPath(dest), 78 | _.assign(this, data), 79 | options 80 | ) 81 | return this 82 | } 83 | 84 | copy (template, type, source, dest) { 85 | dest = dest || source 86 | this.template(template, type, source, dest) 87 | return this 88 | } 89 | 90 | writeGitKeepFile (dirname) { 91 | dirname = path.resolve(dirname) 92 | fs.writeFileSync(path.join(dirname, '.gitkeep'), 'Place hold file', 'utf8') 93 | } 94 | 95 | write () {} 96 | } 97 | 98 | module.exports = CreateBase 99 | -------------------------------------------------------------------------------- /src/create/component.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra') 2 | const path = require('path') 3 | const chalk = require('chalk') 4 | const inquirer = require('inquirer') 5 | 6 | const CreateBase = require('./base') 7 | 8 | class Component extends CreateBase { 9 | constructor (options) { 10 | super() 11 | this.conf = Object.assign({ 12 | appName: null, 13 | template: null, 14 | framework: null, 15 | componentName: null, 16 | description: '' 17 | }, options) 18 | } 19 | 20 | init () { 21 | console.log(chalk.green(`Allo ${chalk.green.bold(this.username)}! Prepare to create a new component!`)) 22 | console.log('Need help? Go and open issue: https://github.com/o2team/athena2/issues/new') 23 | console.log() 24 | } 25 | 26 | create () { 27 | this.ask() 28 | .then(answers => { 29 | const date = new Date() 30 | this.conf = Object.assign(this.conf, answers) 31 | this.conf.date = `${date.getFullYear()}-${(date.getMonth() + 1)}-${date.getDate()}` 32 | this.write() 33 | }) 34 | } 35 | 36 | ask () { 37 | const prompts = [] 38 | const conf = this.conf 39 | if (typeof conf.componentName !== 'string') { 40 | prompts.push({ 41 | type: 'input', 42 | name: 'componentName', 43 | message: 'Please give me a component name!', 44 | validate (input) { 45 | if (!input) { 46 | return 'component\'s name can not be empty!' 47 | } 48 | if (fs.existsSync(`component/${input}`) || fs.existsSync(`src/component/${input}`) || fs.existsSync(`src/js/${input}.js`)) { 49 | return 'The component already exist, please give me another name!' 50 | } 51 | return true 52 | } 53 | }) 54 | } else if (fs.existsSync(`component/${conf.componentName}`) || 55 | fs.existsSync(`src/component/${conf.componentName}`) || 56 | fs.existsSync(`src/js/${conf.componentName}.js`)) { 57 | prompts.push({ 58 | type: 'input', 59 | name: 'componentName', 60 | message: 'The component already exist, please give me another name!', 61 | validate (input) { 62 | if (!input) { 63 | return 'component\'s name can not be empty!' 64 | } 65 | if (fs.existsSync(`component/${input}`) || 66 | fs.existsSync(`src/component/${input}`) || 67 | fs.existsSync(`src/js/${input}.js`)) { 68 | return 'You type the component name repeatedly!' 69 | } 70 | return true 71 | } 72 | }) 73 | } 74 | 75 | if (typeof conf.description !== 'string') { 76 | prompts.push({ 77 | type: 'input', 78 | name: 'description', 79 | message: 'Please tell me this component\'s description!' 80 | }) 81 | } 82 | 83 | return inquirer.prompt(prompts) 84 | } 85 | 86 | write () { 87 | const { template } = this.conf 88 | 89 | const templateCreate = require(path.join(this.templatePath(), template, 'index.js')) 90 | templateCreate.component(this, this.conf) 91 | } 92 | } 93 | 94 | module.exports = Component 95 | -------------------------------------------------------------------------------- /src/create/module.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra') 2 | const path = require('path') 3 | const chalk = require('chalk') 4 | const inquirer = require('inquirer') 5 | 6 | const CreateBase = require('./base') 7 | 8 | class Module extends CreateBase { 9 | constructor (options) { 10 | super() 11 | this.conf = Object.assign({ 12 | appName: null, 13 | template: null, 14 | moduleName: null, 15 | description: '' 16 | }, options) 17 | } 18 | 19 | init () { 20 | console.log(chalk.green(`Allo ${chalk.green.bold(this.username)}! Prepare to create a new module!`)) 21 | console.log('Need help? Go and open issue: https://github.com/o2team/athena2/issues/new') 22 | console.log() 23 | } 24 | 25 | create () { 26 | this.ask() 27 | .then(answers => { 28 | const date = new Date() 29 | this.conf = Object.assign(this.conf, answers) 30 | this.conf.date = `${date.getFullYear()}-${(date.getMonth() + 1)}-${date.getDate()}` 31 | this.write() 32 | }) 33 | } 34 | 35 | ask () { 36 | const prompts = [] 37 | const conf = this.conf 38 | if (typeof conf.moduleName !== 'string') { 39 | prompts.push({ 40 | type: 'input', 41 | name: 'moduleName', 42 | message: 'Please give me a module name!', 43 | validate (input) { 44 | if (!input) { 45 | return 'Module name can not be empty!' 46 | } 47 | if (fs.existsSync(`src/${input}`)) { 48 | return 'The module already exist, please give me another name!' 49 | } 50 | return true 51 | } 52 | }) 53 | } else if (fs.existsSync(`src/${conf.moduleName}`)) { 54 | prompts.push({ 55 | type: 'input', 56 | name: 'moduleName', 57 | message: 'The module already exist, please give me another name!', 58 | validate (input) { 59 | if (!input) { 60 | return 'Module name can not be empty!' 61 | } 62 | if (fs.existsSync(`src/${input}`)) { 63 | return 'You type the module name repeatedly!' 64 | } 65 | return true 66 | } 67 | }) 68 | } 69 | 70 | if (typeof conf.description !== 'string') { 71 | prompts.push({ 72 | type: 'input', 73 | name: 'description', 74 | message: 'Please tell me this module\'s description!' 75 | }) 76 | } 77 | 78 | return inquirer.prompt(prompts) 79 | } 80 | 81 | write () { 82 | const { template } = this.conf 83 | const templateCreate = require(path.join(this.templatePath(), template, 'index.js')) 84 | templateCreate.module(this, this.conf) 85 | } 86 | } 87 | 88 | module.exports = Module 89 | -------------------------------------------------------------------------------- /src/create/page.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra') 2 | const path = require('path') 3 | const chalk = require('chalk') 4 | const inquirer = require('inquirer') 5 | 6 | const CreateBase = require('./base') 7 | 8 | class Page extends CreateBase { 9 | constructor (options) { 10 | super() 11 | this.conf = Object.assign({ 12 | appName: null, 13 | template: null, 14 | framework: null, 15 | pageName: null, 16 | description: '' 17 | }, options) 18 | } 19 | 20 | init () { 21 | console.log(chalk.green(`Allo ${chalk.green.bold(this.username)}! Prepare to create a new page!`)) 22 | console.log('Need help? Go and open issue: https://github.com/o2team/athena2/issues/new') 23 | console.log() 24 | } 25 | 26 | create () { 27 | this.ask() 28 | .then(answers => { 29 | const date = new Date() 30 | this.conf = Object.assign(this.conf, answers) 31 | this.conf.date = `${date.getFullYear()}-${(date.getMonth() + 1)}-${date.getDate()}` 32 | this.write() 33 | }) 34 | } 35 | 36 | ask () { 37 | const prompts = [] 38 | const conf = this.conf 39 | if (typeof conf.pageName !== 'string') { 40 | prompts.push({ 41 | type: 'input', 42 | name: 'pageName', 43 | message: 'Please give me a page name!', 44 | validate (input) { 45 | if (!input) { 46 | return 'Page\'s name can not be empty!' 47 | } 48 | if (fs.existsSync(`page/${input}`) || fs.existsSync(`src/view/${input}`)) { 49 | return 'The page already exist, please give me another name!' 50 | } 51 | return true 52 | } 53 | }) 54 | } else if (fs.existsSync(`page/${conf.pageName}`) || fs.existsSync(`src/view/${conf.pageName}`)) { 55 | prompts.push({ 56 | type: 'input', 57 | name: 'pageName', 58 | message: 'The page already exist, please give me another name!', 59 | validate (input) { 60 | if (!input) { 61 | return 'Page\'s name can not be empty!' 62 | } 63 | if (fs.existsSync(`page/${input}`) || fs.existsSync(`src/view/${input}`)) { 64 | return 'You type the page name repeatedly!' 65 | } 66 | return true 67 | } 68 | }) 69 | } 70 | 71 | if (typeof conf.description !== 'string') { 72 | prompts.push({ 73 | type: 'input', 74 | name: 'description', 75 | message: 'Please tell me this page\'s description!' 76 | }) 77 | } 78 | 79 | return inquirer.prompt(prompts) 80 | } 81 | 82 | write () { 83 | const { template } = this.conf 84 | 85 | const templateCreate = require(path.join(this.templatePath(), template, 'index.js')) 86 | templateCreate.page(this, this.conf) 87 | } 88 | } 89 | 90 | module.exports = Page 91 | -------------------------------------------------------------------------------- /src/publish/index.js: -------------------------------------------------------------------------------- 1 | // const fs = require('fs') 2 | // const path = require('path') 3 | const FTPS = require('ftps') 4 | /* 5 | 获取配置项 6 | */ 7 | const { 8 | getConf, 9 | getAppBuildConfig 10 | } = require('../build') 11 | 12 | module.exports = function publish () { 13 | const conf = getConf() 14 | const buildConfig = getAppBuildConfig(conf.appPath) 15 | const { publish } = buildConfig 16 | createConnect(publish) 17 | } 18 | 19 | function createConnect (opts) { 20 | const ftps = new FTPS(opts) 21 | ftps.exec(function (err, res) { 22 | console.log(err) 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /src/util/format_webpack_message.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk') 2 | 3 | const syntaxErrorLabel = 'Syntax error:' 4 | 5 | function isLikelyASyntaxError (message) { 6 | return message.indexOf(syntaxErrorLabel) >= 0 7 | } 8 | 9 | function formatMessage (message) { 10 | let lines = message.split('\n') 11 | if (lines.length > 2 && lines[1] === '') { 12 | lines.splice(1, 1) 13 | } 14 | if (lines[0].lastIndexOf('!') >= 0) { 15 | lines[0] = lines[0].substr(lines[0].lastIndexOf('!') + 1) 16 | } 17 | lines = lines.filter(line => line.indexOf(' @ ') !== 0) 18 | if (!lines[0] || !lines[1]) { 19 | return lines.join('\n') 20 | } 21 | if (lines[1].indexOf('Module not found: ') === 0) { 22 | lines = [ 23 | lines[0], 24 | lines[1] 25 | .replace("Cannot resolve 'file' or 'directory' ", '') 26 | .replace('Cannot resolve module ', '') 27 | .replace('Error: ', '') 28 | .replace('[CaseSensitivePathsPlugin] ', '') 29 | ] 30 | } else if (lines[1].indexOf('Module build failed: ') === 0) { 31 | lines[1] = lines[1].replace( 32 | 'Module build failed: SyntaxError:', 33 | syntaxErrorLabel 34 | ) 35 | } 36 | 37 | const exportError = /\s*(.+?)\s*(")?export '(.+?)' was not found in '(.+?)'/ 38 | if (lines[1].match(exportError)) { 39 | lines[1] = lines[1].replace( 40 | exportError, 41 | "$1 '$4' does not contain an export named '$3'." 42 | ) 43 | } 44 | lines[0] = chalk.inverse(lines[0]) 45 | message = lines.join('\n') 46 | 47 | message = message.replace(/^\s*at\s((?!webpack:).)*:\d+:\d+[\s)]*(\n|$)/gm, '') 48 | return message.trim() 49 | } 50 | 51 | module.exports = function formatWebpackMessage (message) { 52 | const errors = message.errors.map(item => formatMessage(item)) 53 | const warnings = message.warnings.map(item => formatMessage(item)) 54 | 55 | const result = { 56 | errors, 57 | warnings 58 | } 59 | if (result.errors.some(isLikelyASyntaxError)) { 60 | result.errors = result.errors.filter(isLikelyASyntaxError) 61 | } 62 | return result 63 | } 64 | -------------------------------------------------------------------------------- /src/util/index.js: -------------------------------------------------------------------------------- 1 | const os = require('os') 2 | const path = require('path') 3 | const fs = require('fs-extra') 4 | const execSync = require('child_process').execSync 5 | 6 | /** 7 | * get user dir 8 | */ 9 | exports.homedir = (function () { 10 | let homedir = null 11 | const env = process.env 12 | const home = env.HOME 13 | const user = env.LOGNAME || env.USER || env.LNAME || env.USERNAME 14 | if (process.platform === 'win32') { 15 | homedir = env.USERPROFILE || env.HOMEDRIVE + env.HOMEPATH || home || null 16 | } else if (process.platform === 'darwin') { 17 | homedir = home || (user ? `/Users/${user}` : null) 18 | } else if (process.platform === 'linux') { 19 | homedir = home || (process.getuid() === 0 ? '/root' : (user ? `/home/${user}` : null)) 20 | } 21 | return typeof os.homedir === 'function' ? os.homedir : function () { 22 | return homedir 23 | } 24 | })() 25 | 26 | exports.getRootPath = function () { 27 | return path.resolve(__dirname, '../../') 28 | } 29 | 30 | /** 31 | * get athena cache base root 32 | */ 33 | exports.getAthenaPath = function () { 34 | const athenaPath = path.join(exports.homedir(), '.athena2') 35 | if (!fs.existsSync(athenaPath)) { 36 | fs.mkdirSync(athenaPath) 37 | } 38 | return athenaPath 39 | } 40 | 41 | /** 42 | * set athena config 43 | */ 44 | exports.setConfig = function (config) { 45 | const athenaPath = exports.getAthenaPath() 46 | if (typeof config === 'object') { 47 | const oldConfig = exports.getConfig() 48 | config = Object.assign({}, oldConfig, config) 49 | fs.writeFileSync(path.join(athenaPath, 'config.json'), JSON.stringify(config, null, 2)) 50 | } 51 | } 52 | 53 | /** 54 | * get athena config 55 | */ 56 | exports.getConfig = function () { 57 | const configPath = path.join(exports.getAthenaPath(), 'config.json') 58 | if (fs.existsSync(configPath)) { 59 | return require(configPath) 60 | } 61 | return {} 62 | } 63 | 64 | exports.getSystemUsername = function () { 65 | const userHome = exports.homedir() 66 | const systemUsername = process.env.USER || path.basename(userHome) 67 | return systemUsername 68 | } 69 | 70 | exports.getAthenaVersion = function () { 71 | return require(path.join(exports.getRootPath(), 'package.json')).version 72 | } 73 | 74 | exports.printAthenaVersion = function () { 75 | const athenaVersion = exports.getAthenaVersion() 76 | console.log(`👩 Athena v${athenaVersion}`) 77 | console.log() 78 | } 79 | 80 | exports.shouldUseYarn = function () { 81 | try { 82 | execSync('yarn --version', { stdio: 'ignore' }) 83 | return true 84 | } catch (e) { 85 | return false 86 | } 87 | } 88 | 89 | exports.shouldUseCnpm = function () { 90 | try { 91 | execSync('cnpm --version', { stdio: 'ignore' }) 92 | return true 93 | } catch (e) { 94 | return false 95 | } 96 | } 97 | 98 | function _normalizeFamily (family) { 99 | return family ? family.toLowerCase() : 'ipv4' 100 | } 101 | 102 | exports.getLocalIp = function (name, family) { 103 | const interfaces = os.networkInterfaces() 104 | // 105 | // Default to `ipv4` 106 | // 107 | family = _normalizeFamily(family) 108 | 109 | // 110 | // If a specific network interface has been named, 111 | // return the address. 112 | // 113 | if (name && name !== 'private' && name !== 'public') { 114 | const res = interfaces[name].filter(details => { 115 | const itemFamily = details.family.toLowerCase() 116 | return itemFamily === family 117 | }) 118 | if (res.length === 0) { 119 | return undefined 120 | } 121 | return res[0].address 122 | } 123 | 124 | const all = Object.keys(interfaces).map(nic => { 125 | // 126 | // Note: name will only be `public` or `private` 127 | // when this is called. 128 | // 129 | const addresses = interfaces[nic].filter(details => { 130 | details.family = details.family.toLowerCase() 131 | if (details.family !== family || exports.isLoopback(details.address)) { 132 | return false 133 | } else if (!name) { 134 | return true 135 | } 136 | 137 | return name === 'public' ? !exports.isPrivate(details.address) 138 | : exports.isPrivate(details.address) 139 | }) 140 | return addresses.length ? addresses[0].address : undefined 141 | }).filter(Boolean) 142 | 143 | return !all.length ? exports.loopback(family) : all[0] 144 | } 145 | 146 | exports.loopback = function loopback (family) { 147 | // 148 | // Default to `ipv4` 149 | // 150 | family = _normalizeFamily(family) 151 | 152 | if (family !== 'ipv4' && family !== 'ipv6') { 153 | throw new Error('family must be ipv4 or ipv6') 154 | } 155 | 156 | return family === 'ipv4' ? '127.0.0.1' : 'fe80::1' 157 | } 158 | 159 | exports.isLoopback = function isLoopback (addr) { 160 | return /^(::f{4}:)?127\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/ 161 | .test(addr) || 162 | /^fe80::1$/.test(addr) || 163 | /^::1$/.test(addr) || 164 | /^::$/.test(addr) 165 | } 166 | 167 | exports.isPrivate = function isPrivate (addr) { 168 | return /^(::f{4}:)?10\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/ 169 | .test(addr) || 170 | /^(::f{4}:)?192\.168\.([0-9]{1,3})\.([0-9]{1,3})$/.test(addr) || 171 | /^(::f{4}:)?172\.(1[6-9]|2\d|30|31)\.([0-9]{1,3})\.([0-9]{1,3})$/ 172 | .test(addr) || 173 | /^(::f{4}:)?127\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/.test(addr) || 174 | /^(::f{4}:)?169\.254\.([0-9]{1,3})\.([0-9]{1,3})$/.test(addr) || 175 | /^fc00:/i.test(addr) || 176 | /^fe80:/i.test(addr) || 177 | /^::1$/.test(addr) || 178 | /^::$/.test(addr) 179 | } 180 | 181 | exports.isPublic = function isPublic (addr) { 182 | return !exports.isPrivate(addr) 183 | } 184 | 185 | exports.zeroPad = function (num, places) { 186 | const zero = places - num.toString().length + 1 187 | return Array(+(zero > 0 && zero)).join('0') + num 188 | } 189 | 190 | exports.formatTime = function (date) { 191 | if (!date) { 192 | date = new Date() 193 | } else if (!(date instanceof Date)) { 194 | date = new Date(date) 195 | } 196 | const year = date.getFullYear() 197 | const month = date.getMonth() + 1 198 | const day = date.getDate() 199 | const hour = date.getHours() 200 | const minute = date.getMinutes() 201 | return `${year}-${exports.zeroPad(month, 2)}-${exports.zeroPad(day, 2)} ${exports.zeroPad(hour, 2)}:${exports.zeroPad(minute, 2)}` 202 | } 203 | 204 | exports.isEmptyObject = function (obj) { 205 | if (obj == null) { 206 | return true 207 | } 208 | for (const key in obj) { 209 | if (obj.hasOwnProperty(key)) { 210 | return false 211 | } 212 | } 213 | return true 214 | } 215 | 216 | exports.urlJoin = function () { 217 | function normalize (str) { 218 | return str 219 | .replace(/([/]+)/g, '/') 220 | .replace(/\/\?(?!\?)/g, '?') 221 | .replace(/\/#/g, '#') 222 | .replace(/:\//g, '://') 223 | } 224 | 225 | const joined = [].slice.call(arguments, 0).join('/') 226 | return normalize(joined) 227 | } 228 | -------------------------------------------------------------------------------- /src/util/open.js: -------------------------------------------------------------------------------- 1 | const exec = require('child_process').exec 2 | 3 | module.exports = function open (target, appName, callback) { 4 | let opener 5 | 6 | if (typeof (appName) === 'function') { 7 | callback = appName 8 | appName = null 9 | } 10 | 11 | switch (process.platform) { 12 | case 'darwin': 13 | if (appName) { 14 | opener = 'open -a "' + escape(appName) + '"' 15 | } else { 16 | opener = 'open' 17 | } 18 | break 19 | case 'win32': 20 | if (appName) { 21 | opener = 'start "" "' + escape(appName) + '"' 22 | } else { 23 | opener = 'start ""' 24 | } 25 | break 26 | default: 27 | if (appName) { 28 | opener = 'xdg-open "" "' + escape(appName) + '"' 29 | } else { 30 | opener = 'xdg-open ""' 31 | } 32 | break 33 | } 34 | 35 | if (process.env.SUDO_USER) { 36 | opener = 'sudo -u ' + process.env.SUDO_USER + ' ' + opener 37 | } 38 | return exec(opener + ' "' + escape(target) + '"', callback) 39 | } 40 | 41 | function escape (s) { 42 | return s.replace(/"/g, '\\"') 43 | } 44 | -------------------------------------------------------------------------------- /templates/complete/app/app-conf: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | app: '<%= appName %>', 3 | appId: '<%= appId %>', 4 | description: '<%= description %>', 5 | createTime: '<%= date %>', 6 | template: '<%= template %>', 7 | platform: '<%= platform %>', 8 | framework: '<%= framework %>', 9 | common: 'common', 10 | moduleList: ['common'], 11 | sass: '<%= sass %>' 12 | } 13 | -------------------------------------------------------------------------------- /templates/complete/app/config/dev: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // environment variables 3 | env: { 4 | NODE_ENV: '"development"' 5 | }, 6 | // define global constants for application see https://webpack.js.org/plugins/define-plugin/ 7 | defineConstants: { 8 | }, 9 | // dev server port 10 | port: 3000, 11 | // dev server host 12 | host: '0.0.0.0', 13 | // dev server protocol, support https 14 | protocol: 'http' 15 | } 16 | -------------------------------------------------------------------------------- /templates/complete/app/config/index: -------------------------------------------------------------------------------- 1 | const config = { 2 | // source files root directory 3 | sourceRoot: 'src', 4 | // output files root directory 5 | outputRoot: 'dist', 6 | // The publicPath specifies the public URL address of the output files when referenced in a browser 7 | // see https://webpack.js.org/guides/public-path/ 8 | publicPath: '/', 9 | // the directory contains css/js/images/fonts/media etc. files 10 | staticDirectory: 'static', 11 | // define global constants for application see https://webpack.js.org/plugins/define-plugin/ 12 | defineConstants: { 13 | }, 14 | // support functions 15 | module: { 16 | postcss: { 17 | // autoprefixer plugin config 18 | autoprefixer: { 19 | enable: true 20 | } 21 | } 22 | } 23 | } 24 | 25 | module.exports = function (merge) { 26 | if (process.env.NODE_ENV === 'development') { 27 | return merge({}, config, require('./dev')) 28 | } 29 | return merge({}, config, require('./prod')) 30 | } 31 | -------------------------------------------------------------------------------- /templates/complete/app/config/prod: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // environment variables 3 | env: { 4 | NODE_ENV: '"production"' 5 | }, 6 | // define global constants for application see https://webpack.js.org/plugins/define-plugin/ 7 | defineConstants: { 8 | }, 9 | pubish: { 10 | mode: 'sftp', 11 | host: 'labs.qiang.it', 12 | user: 'labs', 13 | pass: 'labslabslabs', 14 | port: 22, 15 | localPath: '/dist/', 16 | remotePath: '/usr/share/nginx/html/public/labs/' // required 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /templates/complete/app/editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /templates/complete/app/eslintconfig: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: ['standard'<%- (framework === "nerv" || framework === "react") ? ", 'standard-jsx'" : "" %>], 4 | parser: 'babel-eslint', 5 | env: { 6 | browser: true 7 | }, 8 | parserOptions: { 9 | sourceType: 'module'<% if (framework === "nerv" || framework === "react") { %>, 10 | ecmaFeatures: { 11 | 'jsx': true 12 | }<% } %> 13 | }, 14 | rules: { 15 | // allow paren-less arrow functions 16 | 'arrow-parens': 0, 17 | // allow async-await 18 | 'generator-star-spacing': 0, 19 | // allow debugger during development 20 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /templates/complete/app/gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .git/ 3 | .cache/ 4 | -------------------------------------------------------------------------------- /templates/complete/app/index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra') 2 | const path = require('path') 3 | const chalk = require('chalk') 4 | const shelljs = require('shelljs') 5 | const ora = require('ora') 6 | const uuid = require('uuid') 7 | 8 | module.exports = function create (creater, params, helper, cb) { 9 | const { appName, appId, description, framework, template, date, platform, sass } = params 10 | const commonDir = 'common' 11 | const sourceRootDir = 'src' 12 | const configDir = 'config' 13 | const configDirPath = path.join(appName, configDir) 14 | const sourceRootPath = path.join(appName, sourceRootDir) 15 | // create app dir 16 | fs.mkdirpSync(appName) 17 | fs.mkdirpSync(sourceRootPath) 18 | fs.mkdirpSync(configDirPath) 19 | fs.mkdirpSync(path.join(sourceRootPath, commonDir)) 20 | fs.mkdirpSync(path.join(sourceRootPath, commonDir, 'page')) 21 | fs.mkdirpSync(path.join(sourceRootPath, commonDir, 'component')) 22 | 23 | // copy files 24 | creater.template(template, 'app', path.join(configDir, 'index'), path.join(configDirPath, 'index.js')) 25 | creater.template(template, 'app', path.join(configDir, 'dev'), path.join(configDirPath, 'dev.js')) 26 | creater.template(template, 'app', path.join(configDir, 'prod'), path.join(configDirPath, 'prod.js')) 27 | creater.template(template, 'module', 'mod-conf', path.join(sourceRootPath, commonDir, 'mod.conf.js'), { 28 | moduleName: commonDir, 29 | moduleId: uuid.v1(), 30 | date, 31 | appName, 32 | description: 'common module', 33 | common: commonDir 34 | }) 35 | creater.template(template, 'app', 'editorconfig', path.join(appName, '.editorconfig')) 36 | creater.template(template, 'app', 'gitignore', path.join(appName, '.gitignore')) 37 | creater.template(template, 'app', 'jsconfigjson', path.join(appName, 'jsconfig.json')) 38 | creater.template(template, 'app', 'eslintconfig', path.join(appName, '.eslintrc.js'), { 39 | appName, 40 | framework, 41 | date 42 | }) 43 | creater.template(template, 'app', 'packagejson', path.join(appName, 'package.json'), { 44 | appName, 45 | framework, 46 | date 47 | }) 48 | creater.template(template, 'app', 'app-conf', path.join(appName, 'app.conf.js'), { 49 | appName, 50 | appId, 51 | platform, 52 | description, 53 | framework, 54 | template, 55 | date, 56 | sass 57 | }) 58 | 59 | creater.fs.commit(() => { 60 | console.log() 61 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created app: ${chalk.grey.bold(appName)}`)}`) 62 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created directory: ${appName}/${configDir}`)}`) 63 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created directory: ${appName}/${commonDir}`)}`) 64 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created directory: ${appName}/${commonDir}/page`)}`) 65 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created directory: ${appName}/${commonDir}/component`)}`) 66 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${appName}/${configDir}/index.js`)}`) 67 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${appName}/${configDir}/dev.js`)}`) 68 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${appName}/${configDir}/prod.js`)}`) 69 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${appName}/${commonDir}/mod.conf.js`)}`) 70 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${appName}/.editorconfig`)}`) 71 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${appName}/.gitignore`)}`) 72 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${appName}/.eslintrc.js`)}`) 73 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${appName}/package.json`)}`) 74 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${appName}/jsconfig.json`)}`) 75 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${appName}/app.conf.js`)}`) 76 | console.log() 77 | const gitInitSpinner = ora(`cd ${chalk.cyan.bold(appName)}, executing ${chalk.cyan.bold('git init')}`).start() 78 | process.chdir(appName) 79 | const gitInit = shelljs.exec('git init', { silent: true }) 80 | if (gitInit.code === 0) { 81 | gitInitSpinner.color = 'green' 82 | gitInitSpinner.succeed(gitInit.stdout) 83 | } else { 84 | gitInitSpinner.color = 'red' 85 | gitInitSpinner.fail(gitInit.stderr) 86 | } 87 | // install 88 | let command 89 | if (helper.shouldUseYarn()) { 90 | command = 'yarn install' 91 | } else if (helper.shouldUseCnpm()) { 92 | command = 'cnpm install' 93 | } else { 94 | command = 'npm install' 95 | } 96 | const installSpinner = ora(`Executing ${chalk.cyan.bold(command)}, it will take some time...`).start() 97 | const install = shelljs.exec(command, { silent: true }) 98 | if (install.code === 0) { 99 | installSpinner.color = 'green' 100 | installSpinner.succeed('Install success') 101 | console.log(`${install.stderr}${install.stdout}`) 102 | } else { 103 | installSpinner.color = 'red' 104 | installSpinner.fail(chalk.red('Install dependencies failed! Please cd in the app directory install yourself!')) 105 | console.log(`${install.stderr}${install.stdout}`) 106 | } 107 | console.log(chalk.green(`Create app ${chalk.green.bold(appName)} Successfully!`)) 108 | console.log(chalk.green(`Please cd ${chalk.green.bold(appName)} and start to work!😝`)) 109 | if (typeof cb === 'function') { 110 | cb() 111 | } 112 | }) 113 | } 114 | -------------------------------------------------------------------------------- /templates/complete/app/jsconfigjson: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "experimentalDecorators": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /templates/complete/app/packagejson: -------------------------------------------------------------------------------- 1 | { 2 | "name": "<%= appName %>", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "test": "", 7 | "lint": "eslint ./src/", 8 | "lint:fix": "eslint ./src/ --fix", 9 | "precommit": "lint-staged" 10 | }, 11 | "lint-staged": { 12 | "*.js": [ 13 | "eslint --fix", 14 | "git add" 15 | ] 16 | }, 17 | "dependencies": {<% if (framework === 'nerv') { %> 18 | "nervjs": "^1.2.8"<% } else if (framework === 'react') { %> 19 | "react": "^16.2.0", 20 | "react-dom": "^16.2.0"<% } else if (framework === 'vue') { %> 21 | "vue": "^2.5.13", 22 | "vue-router": "^3.0.1"<% } %> 23 | }, 24 | "devDependencies": { 25 | "babel-eslint": "^8.2.1", 26 | "eslint": "^4.16.0", 27 | "eslint-config-standard": "^10.2.1",<% if (framework === "nerv" || framework === "react") { %> 28 | "eslint-config-standard-jsx": "^4.0.2",<%}%> 29 | "eslint-plugin-import": "^2.7.0", 30 | "eslint-plugin-node": "^5.1.1", 31 | "eslint-plugin-promise": "^3.5.0",<% if (framework === "nerv" || framework === "react") { %> 32 | "eslint-plugin-react": "^7.3.0",<%}%> 33 | "eslint-plugin-standard": "^3.0.1", 34 | "husky": "^0.14.3", 35 | "lint-staged": "^6.0.0" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /templates/complete/component/component.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | -------------------------------------------------------------------------------- /templates/complete/component/index.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash') 2 | const fs = require('fs-extra') 3 | const path = require('path') 4 | const chalk = require('chalk') 5 | const shelljs = require('shelljs') 6 | const ora = require('ora') 7 | const uuid = require('uuid') 8 | 9 | module.exports = function create (creater, params, helper, cb) { 10 | const { appName, template, framework, componentName, description, date, sass } = params 11 | // create component dir 12 | const componentDir = 'component' 13 | const componentCss = sass ? `${componentName}.scss` : `${componentName}.css` 14 | 15 | fs.mkdirpSync(path.join(componentDir, componentName)) 16 | 17 | // copy files 18 | if (framework !== 'vue') { 19 | creater.template(template, 'component', 'component.css', path.join(componentDir, componentName, componentCss)) 20 | creater.template(template, `component/${framework}`, 'component.js', path.join(componentDir, componentName, `${componentName}.js`), { 21 | date, 22 | description, 23 | componentName, 24 | sass 25 | }) 26 | } else { 27 | creater.template(template, 'component/vue', 'component.vue', path.join(componentDir, componentName, `${componentName}.vue`), { 28 | componentName, 29 | sass 30 | }) 31 | } 32 | 33 | creater.fs.commit(() => { 34 | console.log() 35 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created component: ${chalk.grey.bold(componentName)}`)}`) 36 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created directory: ${componentDir}/${componentName}`)}`) 37 | if (framework !== 'vue') { 38 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${componentDir}/${componentName}/${componentCss}`)}`) 39 | } else { 40 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${componentDir}/${componentName}/${componentName}.vue`)}`) 41 | } 42 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${componentDir}/${componentName}/${componentName}.js`)}`) 43 | console.log() 44 | console.log(chalk.green(`Create component ${chalk.green.bold(componentName)} Successfully!`)) 45 | if (typeof cb === 'function') { 46 | cb() 47 | } 48 | }) 49 | } 50 | -------------------------------------------------------------------------------- /templates/complete/component/nerv/component.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author <%= username %> 3 | * @date <%= date %> 4 | * @desc <%= description %> 5 | */ 6 | 7 | import Nerv from 'nervjs' 8 | 9 | import '<% if (sass) { %>./<%= componentName %>.scss<% } else { %><%= componentName %>.css<%}%>' 10 | 11 | class <%= _.upperFirst(_.camelCase(componentName)) %> extends Nerv.Component { 12 | constructor () { 13 | super(...arguments) 14 | this.state = {} 15 | } 16 | 17 | render () { 18 | return ( 19 |
20 |
21 | ) 22 | } 23 | } 24 | 25 | export default <%= _.upperFirst(_.camelCase(conf.componentName)) %> 26 | -------------------------------------------------------------------------------- /templates/complete/component/react/component.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author <%= username %> 3 | * @date <%= date %> 4 | * @desc <%= description %> 5 | */ 6 | 7 | import React from 'react' 8 | import ReactDom from 'react-dom' 9 | 10 | import '<% if (sass) { %>./<%= componentName %>.scss<% } else { %><%= componentName %>.css<%}%>' 11 | 12 | class <%= _.upperFirst(_.camelCase(componentName)) %> extends React.Component { 13 | constructor () { 14 | super(...arguments) 15 | this.state = {} 16 | } 17 | 18 | render () { 19 | return ( 20 |
21 |
22 | ) 23 | } 24 | } 25 | 26 | export default <%= _.upperFirst(_.camelCase(conf.componentName)) %> 27 | -------------------------------------------------------------------------------- /templates/complete/component/vue/component.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 17 | 18 | 20 | -------------------------------------------------------------------------------- /templates/complete/index.js: -------------------------------------------------------------------------------- 1 | const app = require('./app') 2 | const appModule = require('./module') 3 | const page = require('./page') 4 | const component = require('./component') 5 | 6 | module.exports = { 7 | app: app, 8 | module: appModule, 9 | page: page, 10 | component: component 11 | } 12 | -------------------------------------------------------------------------------- /templates/complete/module/index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra') 2 | const path = require('path') 3 | const chalk = require('chalk') 4 | const shelljs = require('shelljs') 5 | const ora = require('ora') 6 | const uuid = require('uuid') 7 | 8 | module.exports = function create (creater, params, helper, cb) { 9 | const { appName, template, moduleName, description, date } = params 10 | // create module dir 11 | const sourceRootDir = 'src' 12 | const pageDir = 'page' 13 | const componentDir = 'component' 14 | const commonDir = 'common' 15 | const staticDir = 'static' 16 | const imgDir = 'images' 17 | const appConf = require(creater.appConfPath) 18 | const appConfFile = fs.readFileSync(creater.appConfPath) 19 | const appConfStr = String(appConfFile) 20 | let appConfStrLines = appConfStr.split('\n') 21 | let moduleList = appConf.moduleList 22 | 23 | if (moduleList.indexOf(moduleName) < 0) { 24 | for (let i = 0; i < appConfStrLines.length; i++) { 25 | let line = appConfStrLines[i]; 26 | if (line.indexOf('moduleList') >= 0) { 27 | appConfStrLines[i] = line.split(']')[0]; 28 | if (moduleList.length > 0) { 29 | appConfStrLines[i] += `,'${moduleName}'],` 30 | } else { 31 | appConfStrLines[i] += `'${moduleName}'],` 32 | } 33 | } 34 | } 35 | fs.writeFileSync(creater.appConfPath, appConfStrLines.join('\n')) 36 | } 37 | 38 | fs.mkdirpSync(path.join(sourceRootDir, moduleName)) 39 | fs.mkdirpSync(path.join(sourceRootDir, moduleName, pageDir)) 40 | fs.mkdirpSync(path.join(sourceRootDir, moduleName, staticDir)) 41 | fs.mkdirpSync(path.join(sourceRootDir, moduleName, componentDir)) 42 | fs.mkdirpSync(path.join(sourceRootDir, moduleName, staticDir, imgDir)) 43 | 44 | // copy files 45 | creater.template(template, 'module', 'mod-conf', path.join(sourceRootDir, moduleName, 'mod.conf.js'), { 46 | moduleName: moduleName, 47 | moduleId: uuid.v1(), 48 | date, 49 | appName, 50 | description, 51 | common: commonDir 52 | }) 53 | 54 | creater.fs.commit(() => { 55 | console.log() 56 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created module: ${chalk.grey.bold(moduleName)}`)}`) 57 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created directory: ${appName}/${sourceRootDir}/${moduleName}/${pageDir}`)}`) 58 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created directory: ${appName}/${sourceRootDir}/${moduleName}/${componentDir}`)}`) 59 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created directory: ${appName}/${sourceRootDir}/${moduleName}/${staticDir}`)}`) 60 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created directory: ${appName}/${sourceRootDir}/${moduleName}/${staticDir}/${imgDir}`)}`) 61 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${appName}/${sourceRootDir}/${moduleName}/mod.conf.js`)}`) 62 | console.log() 63 | console.log(chalk.green(`Create module ${chalk.green.bold(moduleName)} Successfully!`)) 64 | if (typeof cb === 'function') { 65 | cb() 66 | } 67 | }) 68 | } 69 | -------------------------------------------------------------------------------- /templates/complete/module/mod-conf: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | module: '<%= moduleName %>', 3 | moduleId: '<%= moduleId %>', 4 | createTime: '<%= date %>', 5 | app: '<%= appName %>', 6 | common: '<%= common %>', 7 | description: '<%= description %>' 8 | } 9 | -------------------------------------------------------------------------------- /templates/complete/page/index.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash') 2 | const fs = require('fs-extra') 3 | const path = require('path') 4 | const chalk = require('chalk') 5 | const shelljs = require('shelljs') 6 | const ora = require('ora') 7 | const uuid = require('uuid') 8 | 9 | module.exports = function create (creater, params, helper, cb) { 10 | const { appName, template, framework, pageName, description, date, sass } = params 11 | // create page dir 12 | const pageDir = 'page' 13 | const pageCss = sass ? `${pageName}.scss` : `${pageName}.css` 14 | 15 | fs.mkdirpSync(path.join(pageDir, pageName)) 16 | 17 | // copy files 18 | creater.template(template, `page/${framework}`, 'page.js', path.join(pageDir, pageName, `${pageName}.js`), { 19 | date, 20 | description, 21 | pageName, 22 | sass 23 | }) 24 | creater.template(template, 'page', 'page.html', path.join(pageDir, pageName, `${pageName}.html`), { 25 | pageName 26 | }) 27 | if (framework !== 'vue') { 28 | creater.template(template, 'page', 'page.css', path.join(pageDir, pageName, pageCss)) 29 | } else { 30 | creater.template(template, 'page/vue', 'page.vue', path.join(pageDir, pageName, `${pageName}.vue`), { 31 | pageName, 32 | sass 33 | }) 34 | } 35 | 36 | creater.fs.commit(() => { 37 | console.log() 38 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created page: ${chalk.grey.bold(pageName)}`)}`) 39 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created directory: ${pageDir}/${pageName}`)}`) 40 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${pageDir}/${pageName}/${pageName}.html`)}`) 41 | if (framework !== 'vue') { 42 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${pageDir}/${pageName}/${pageCss}`)}`) 43 | } else { 44 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${pageDir}/${pageName}/${pageName}.vue`)}`) 45 | } 46 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${pageDir}/${pageName}/${pageName}.js`)}`) 47 | console.log() 48 | console.log(chalk.green(`Create page ${chalk.green.bold(pageName)} Successfully!`)) 49 | if (typeof cb === 'function') { 50 | cb() 51 | } 52 | }) 53 | } 54 | -------------------------------------------------------------------------------- /templates/complete/page/nerv/page.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author <%= username %> 3 | * @date <%= date %> 4 | * @desc <%= description %> 5 | */ 6 | import Nerv from 'nervjs' 7 | 8 | import '<% if (sass) { %>./<%= pageName %>.scss<% } else { %><%= pageName %>.css<%}%>' 9 | 10 | class <%= _.upperFirst(_.camelCase(pageName)) %> extends Nerv.Component { 11 | constructor () { 12 | super(...arguments) 13 | this.state = {} 14 | } 15 | 16 | render () { 17 | return ( 18 |
19 |
20 | ) 21 | } 22 | } 23 | 24 | Nerv.render(<<%= _.upperFirst(_.camelCase(pageName)) %> />, document.getElementById('J_container')) 25 | -------------------------------------------------------------------------------- /templates/complete/page/page.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | -------------------------------------------------------------------------------- /templates/complete/page/page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= pageName %> 7 | 8 | 9 |
10 | 11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /templates/complete/page/react/page.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author <%= username %> 3 | * @date <%= date %> 4 | * @desc <%= description %> 5 | */ 6 | import React from 'react' 7 | import ReactDom from 'react-dom' 8 | 9 | import '<% if (sass) { %>./<%= pageName %>.scss<% } else { %><%= pageName %>.css<%}%>' 10 | 11 | class <%= _.upperFirst(_.camelCase(pageName)) %> extends React.Component { 12 | constructor () { 13 | super(...arguments) 14 | this.state = {} 15 | } 16 | 17 | render () { 18 | return ( 19 |
20 |
21 | ) 22 | } 23 | } 24 | 25 | ReactDom.render(<<%= _.upperFirst(_.camelCase(pageName)) %> />, document.getElementById('J_container')) 26 | -------------------------------------------------------------------------------- /templates/complete/page/vue/page.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author <%= username %> 3 | * @date <%= date %> 4 | * @desc <%= description %> 5 | */ 6 | 7 | import Vue from 'vue' 8 | import View from './<%= pageName %>.vue' 9 | 10 | new Vue({ 11 | render: h => h(View) 12 | }).$mount('#J_container') 13 | -------------------------------------------------------------------------------- /templates/complete/page/vue/page.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 22 | 23 | 26 | -------------------------------------------------------------------------------- /templates/h5/app/app-conf: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | app: '<%= appName %>', 3 | appId: '<%= appId %>', 4 | description: '<%= description %>', 5 | createTime: '<%= date %>', 6 | template: '<%= template %>', 7 | platform: '<%= platform %>', 8 | h5Template: '<%= h5 %>' 9 | } 10 | -------------------------------------------------------------------------------- /templates/h5/app/editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /templates/h5/app/gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .git/ 3 | .cache/ 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /templates/h5/app/index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra') 2 | const path = require('path') 3 | const chalk = require('chalk') 4 | const shelljs = require('shelljs') 5 | const ora = require('ora') 6 | const download = require('git-clone') 7 | 8 | module.exports = function create (creater, params, helper, cb) { 9 | // download template 10 | params.h5 = params.h5 ? params.h5 : 'base' 11 | const dirIsExist = fs.existsSync(path.join(creater.rootPath, 'templates/h5/app', params.h5)) 12 | if (dirIsExist) { 13 | copyFiles(creater, params, helper, cb) 14 | } else { 15 | downloadTemplate(creater, params, helper, cb) 16 | } 17 | } 18 | 19 | function downloadTemplate (creater, params, helper, cb) { 20 | const { h5 } = params 21 | const template = h5 22 | const downloadSpinner = ora(`Downloading h5 template ${template}`).start() 23 | const from = 'git@git.jd.com:o2h5/h5-templates.git' 24 | const to = path.join(creater.rootPath, 'templates/h5/app', template) 25 | download(from, to, {checkout: template}, function (err) { 26 | downloadSpinner.stop() 27 | if (err) { 28 | console.log('') 29 | console.log(' Failed to download repo ' + chalk.red(from) + ': ' + err.message.trim()) 30 | console.log('') 31 | } else { 32 | console.log('') 33 | console.log(' Base on ' + chalk.green(template) + ' init project success') 34 | console.log('') 35 | 36 | // copyFiles 37 | copyFiles(creater, params, helper, cb) 38 | } 39 | }) 40 | } 41 | 42 | function copyFiles (creater, params, helper, cb) { 43 | const { appName, appId, description, template, date, platform, h5 } = params 44 | const sourceRootDir = 'src' 45 | const sourceRootPath = path.join(appName, sourceRootDir) 46 | // create app dir 47 | fs.mkdirpSync(appName) 48 | fs.mkdirpSync(sourceRootPath) 49 | 50 | allFilesPath(path.join(creater.rootPath, `templates/h5/app/${h5}`), (err, results) => { 51 | if (err) throw err 52 | results.forEach(item => { 53 | const itemPath = item.split(path.sep) 54 | const fileRootPath = itemPath[itemPath.length - 2] 55 | const fileName = path.basename(item) 56 | if (fileRootPath === h5) { 57 | creater.template(template, `app/${h5}/`, fileName, path.join(sourceRootPath, fileName), { appName }) 58 | } else { 59 | if (fileRootPath === 'config') { 60 | if (!fs.existsSync(path.join(appName, fileRootPath))) { 61 | fs.mkdirpSync(path.join(appName, fileRootPath)) 62 | } 63 | creater.template(template, `app/${h5}/${fileRootPath}`, fileName, path.join(appName, fileRootPath, fileName)) 64 | } else { 65 | if (!fs.existsSync(path.join(sourceRootPath, fileRootPath))) { 66 | fs.mkdirpSync(path.join(sourceRootPath, fileRootPath)) 67 | } 68 | creater.template(template, `app/${h5}/${fileRootPath}`, fileName, path.join(sourceRootPath, fileRootPath, fileName)) 69 | } 70 | } 71 | }) 72 | 73 | creater.template(template, 'app', 'editorconfig', path.join(appName, '.editorconfig')) 74 | creater.template(template, 'app', 'gitignore', path.join(appName, '.gitignore')) 75 | 76 | creater.template(template, 'app', 'packagejson', path.join(appName, 'package.json'), { 77 | appName, 78 | h5, 79 | date, 80 | description 81 | }) 82 | creater.template(template, 'app', 'jsconfigjson', path.join(appName, 'jsconfig.json')) 83 | creater.template(template, 'app', 'app-conf', path.join(appName, 'app.conf.js'), { 84 | appName, 85 | appId, 86 | platform, 87 | description, 88 | template, 89 | h5, 90 | date 91 | }) 92 | 93 | creater.fs.commit(() => { 94 | console.log() 95 | const gitInitSpinner = ora(`cd ${chalk.cyan.bold(appName)}, executing ${chalk.cyan.bold('git init')}`).start() 96 | process.chdir(appName) 97 | const gitInit = shelljs.exec('git init', { silent: true }) 98 | if (gitInit.code === 0) { 99 | gitInitSpinner.color = 'green' 100 | gitInitSpinner.succeed(gitInit.stdout) 101 | } else { 102 | gitInitSpinner.color = 'red' 103 | gitInitSpinner.fail(gitInit.stderr) 104 | } 105 | // install 106 | let command 107 | if (helper.shouldUseYarn()) { 108 | command = 'yarn install' 109 | } else if (helper.shouldUseCnpm()) { 110 | command = 'cnpm install' 111 | } else { 112 | command = 'npm install' 113 | } 114 | const installSpinner = ora(`Executing ${chalk.cyan.bold(command)}, it will take some time...`).start() 115 | const install = shelljs.exec(command, { silent: true }) 116 | if (install.code === 0) { 117 | installSpinner.color = 'green' 118 | installSpinner.succeed('Install success') 119 | console.log(`${install.stderr}${install.stdout}`) 120 | } else { 121 | installSpinner.color = 'red' 122 | installSpinner.fail(chalk.red('Install dependencies failed! Please cd in the app directory install yourself!')) 123 | console.log(`${install.stderr}${install.stdout}`) 124 | } 125 | console.log(chalk.green(`Create app ${chalk.green.bold(appName)} Successfully!`)) 126 | console.log(chalk.green(`Please cd ${chalk.green.bold(appName)} and start to work!😝`)) 127 | if (typeof cb === 'function') { 128 | cb() 129 | } 130 | }) 131 | }) 132 | } 133 | 134 | function allFilesPath (dir, done) { 135 | let results = [] 136 | fs.readdir(dir, (err, list) => { 137 | if (err) return done(err) 138 | var pending = list.length 139 | if (!pending) return done(null, results) 140 | list.forEach((file) => { 141 | file = path.resolve(dir, file) 142 | fs.stat(file, (err, stat) => { 143 | if (err) { 144 | return 145 | } 146 | if (stat && stat.isDirectory() && !/.git|cache/i.test(file)) { 147 | allFilesPath(file, (err, res) => { 148 | if (err) { 149 | return 150 | } 151 | results = results.concat(res) 152 | if (!--pending) done(null, results) 153 | }) 154 | } else { 155 | if (!/.DS_Store|.git|template.conf.js/i.test(file)) results.push(file) 156 | if (!--pending) done(null, results) 157 | } 158 | }) 159 | }) 160 | }) 161 | } 162 | -------------------------------------------------------------------------------- /templates/h5/app/jsconfigjson: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "experimentalDecorators": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /templates/h5/app/packagejson: -------------------------------------------------------------------------------- 1 | { 2 | "name": "<%= appName %>", 3 | "version": "1.0.0", 4 | "description": "<%= description %>", 5 | "scripts": { 6 | "start": "ath2 s", 7 | "build": "ath2 build", 8 | "test": "", 9 | "eslint": "eslint */component */page" 10 | }, 11 | "dependencies": { 12 | "preloader.js": "^1.0.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /templates/h5/component/component.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author <%= username %> 3 | * @date <%= date %> 4 | * @desc <%= description %> 5 | */ 6 | 7 | class <%= _.upperFirst(_.camelCase(componentName)) %> { 8 | constructor() { 9 | 10 | } 11 | } 12 | 13 | export default <%= _.upperFirst(_.camelCase(componentName)) %> 14 | -------------------------------------------------------------------------------- /templates/h5/component/index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra') 2 | const path = require('path') 3 | const chalk = require('chalk') 4 | const shelljs = require('shelljs') 5 | const ora = require('ora') 6 | const uuid = require('uuid') 7 | 8 | module.exports = function create (creater, params, helper, cb) { 9 | const { appName, template, componentName, description, date } = params 10 | // create module dir 11 | const sourceRootDir = 'src' 12 | const jsDir = 'js' 13 | 14 | const appConf = require(creater.appConfPath) 15 | const appConfFile = fs.readFileSync(creater.appConfPath) 16 | 17 | if (!fs.existsSync(path.join(sourceRootDir, jsDir))) { 18 | fs.mkdirpSync(path.join(sourceRootDir, jsDir)) 19 | } 20 | 21 | // copy files 22 | creater.template(template, 'component', 'component.js', path.join(sourceRootDir, jsDir, `${componentName}.js`), { 23 | componentName: componentName, 24 | date, 25 | description, 26 | }) 27 | 28 | creater.fs.commit(() => { 29 | console.log() 30 | console.log(chalk.green(`Create component ${chalk.green.bold(componentName)} Successfully!`)) 31 | if (typeof cb === 'function') { 32 | cb() 33 | } 34 | }) 35 | } 36 | -------------------------------------------------------------------------------- /templates/h5/index.js: -------------------------------------------------------------------------------- 1 | const app = require('./app') 2 | const component = require('./component') 3 | 4 | module.exports = { 5 | app: app, 6 | component: component 7 | } 8 | -------------------------------------------------------------------------------- /templates/simple/app/app-conf: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | app: '<%= appName %>', 3 | appId: '<%= appId %>', 4 | description: '<%= description %>', 5 | createTime: '<%= date %>', 6 | template: '<%= template %>', 7 | platform: '<%= platform %>', 8 | framework: '<%= framework %>', 9 | sass: '<%= sass %>' 10 | } 11 | -------------------------------------------------------------------------------- /templates/simple/app/config/dev: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // environment variables 3 | env: { 4 | NODE_ENV: '"development"' 5 | }, 6 | // define global constants for application see https://webpack.js.org/plugins/define-plugin/ 7 | defineConstants: { 8 | }, 9 | // dev server port 10 | port: 3000, 11 | // dev server host 12 | host: '0.0.0.0', 13 | // dev server protocol, support https 14 | protocol: 'http' 15 | } 16 | -------------------------------------------------------------------------------- /templates/simple/app/config/index: -------------------------------------------------------------------------------- 1 | const config = { 2 | // source files root directory 3 | sourceRoot: 'src', 4 | // output files root directory 5 | outputRoot: 'dist', 6 | // The publicPath specifies the public URL address of the output files when referenced in a browser 7 | // see https://webpack.js.org/guides/public-path/ 8 | publicPath: '/', 9 | // the directory contains css/js/images/fonts/media etc. files 10 | staticDirectory: 'static', 11 | // define global constants for application see https://webpack.js.org/plugins/define-plugin/ 12 | defineConstants: { 13 | }, 14 | // support functions 15 | module: { 16 | postcss: { 17 | // autoprefixer plugin config 18 | autoprefixer: { 19 | enable: true 20 | } 21 | } 22 | } 23 | } 24 | 25 | module.exports = function (merge) { 26 | if (process.env.NODE_ENV === 'development') { 27 | return merge({}, config, require('./dev')) 28 | } 29 | return merge({}, config, require('./prod')) 30 | } 31 | -------------------------------------------------------------------------------- /templates/simple/app/config/prod: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // environment variables 3 | env: { 4 | NODE_ENV: '"production"' 5 | }, 6 | // define global constants for application see https://webpack.js.org/plugins/define-plugin/ 7 | defineConstants: { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /templates/simple/app/editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /templates/simple/app/eslintconfig: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: ['standard'<%- (framework === "nerv" || framework === "react") ? ", 'standard-jsx'" : "" %>], 4 | parser: 'babel-eslint', 5 | env: { 6 | browser: true 7 | }, 8 | parserOptions: { 9 | sourceType: 'module'<% if (framework === "nerv" || framework === "react") { %>, 10 | ecmaFeatures: { 11 | 'jsx': true 12 | }<% } %> 13 | }, 14 | rules: { 15 | // allow paren-less arrow functions 16 | 'arrow-parens': 0, 17 | // allow async-await 18 | 'generator-star-spacing': 0, 19 | // allow debugger during development 20 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /templates/simple/app/gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .git/ 3 | .cache/ 4 | -------------------------------------------------------------------------------- /templates/simple/app/globalcss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | -------------------------------------------------------------------------------- /templates/simple/app/index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra') 2 | const path = require('path') 3 | const chalk = require('chalk') 4 | const shelljs = require('shelljs') 5 | const ora = require('ora') 6 | 7 | module.exports = function create (creater, params, helper, cb) { 8 | const { appName, appId, description, framework, template, date, platform, sass } = params 9 | const sourceRootDir = 'src' 10 | const configDir = 'config' 11 | const configDirPath = path.join(appName, configDir) 12 | const sourceRootPath = path.join(appName, sourceRootDir) 13 | const globalCss = sass ? 'global.scss' : 'global.css' 14 | // create app dir 15 | fs.mkdirpSync(appName) 16 | fs.mkdirpSync(sourceRootPath) 17 | fs.mkdirpSync(configDirPath) 18 | 19 | // copy files 20 | creater.template(template, 'app', 'indexhtml', path.join(sourceRootPath, 'index.html'), { appName }) 21 | creater.template(template, `app/${framework}`, 'index', path.join(sourceRootPath, 'index.js'), { 22 | description, 23 | date 24 | }) 25 | if (framework === 'vue') { 26 | creater.template(template, 'app/vue', 'app', path.join(sourceRootPath, 'app.vue'), { sass }) 27 | } else { 28 | creater.template(template, 'app', 'globalcss', path.join(sourceRootPath, globalCss)) 29 | } 30 | creater.template(template, 'app', path.join(configDir, 'index'), path.join(configDirPath, 'index.js')) 31 | creater.template(template, 'app', path.join(configDir, 'dev'), path.join(configDirPath, 'dev.js')) 32 | creater.template(template, 'app', path.join(configDir, 'prod'), path.join(configDirPath, 'prod.js')) 33 | creater.template(template, 'app', 'editorconfig', path.join(appName, '.editorconfig')) 34 | creater.template(template, 'app', 'gitignore', path.join(appName, '.gitignore')) 35 | creater.template(template, 'app', 'eslintconfig', path.join(appName, '.eslintrc.js'), { 36 | appName, 37 | framework, 38 | date 39 | }) 40 | creater.template(template, 'app', 'packagejson', path.join(appName, 'package.json'), { 41 | appName, 42 | framework, 43 | date 44 | }) 45 | creater.template(template, 'app', 'jsconfigjson', path.join(appName, 'jsconfig.json')) 46 | creater.template(template, 'app', 'app-conf', path.join(appName, 'app.conf.js'), { 47 | appName, 48 | appId, 49 | platform, 50 | description, 51 | framework, 52 | template, 53 | date, 54 | sass 55 | }) 56 | 57 | creater.fs.commit(() => { 58 | console.log() 59 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created app: ${chalk.grey.bold(appName)}`)}`) 60 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created directory: ${appName}/${configDir}`)}`) 61 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${appName}/${configDir}/index.js`)}`) 62 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${appName}/${configDir}/dev.js`)}`) 63 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${appName}/${configDir}/prod.js`)}`) 64 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${appName}/.editorconfig`)}`) 65 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${appName}/.gitignore`)}`) 66 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${appName}/.eslintrc.js`)}`) 67 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${appName}/package.json`)}`) 68 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${appName}/jsconfig.json`)}`) 69 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${appName}/app.conf.js`)}`) 70 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${appName}/index.html`)}`) 71 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${appName}/index.js`)}`) 72 | if (framework === 'vue') { 73 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${appName}/app.vue`)}`) 74 | } else { 75 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${appName}/${globalCss}`)}`) 76 | } 77 | console.log() 78 | const gitInitSpinner = ora(`cd ${chalk.cyan.bold(appName)}, executing ${chalk.cyan.bold('git init')}`).start() 79 | process.chdir(appName) 80 | const gitInit = shelljs.exec('git init', { silent: true }) 81 | if (gitInit.code === 0) { 82 | gitInitSpinner.color = 'green' 83 | gitInitSpinner.succeed(gitInit.stdout) 84 | } else { 85 | gitInitSpinner.color = 'red' 86 | gitInitSpinner.fail(gitInit.stderr) 87 | } 88 | // install 89 | let command 90 | if (helper.shouldUseYarn()) { 91 | command = 'yarn install' 92 | } else if (helper.shouldUseCnpm()) { 93 | command = 'cnpm install' 94 | } else { 95 | command = 'npm install' 96 | } 97 | const installSpinner = ora(`Executing ${chalk.cyan.bold(command)}, it will take some time...`).start() 98 | const install = shelljs.exec(command, { silent: true }) 99 | if (install.code === 0) { 100 | installSpinner.color = 'green' 101 | installSpinner.succeed('Install success') 102 | console.log(`${install.stderr}${install.stdout}`) 103 | } else { 104 | installSpinner.color = 'red' 105 | installSpinner.fail(chalk.red('Install dependencies failed! Please cd in the app directory install yourself!')) 106 | console.log(`${install.stderr}${install.stdout}`) 107 | } 108 | console.log(chalk.green(`Create app ${chalk.green.bold(appName)} Successfully!`)) 109 | console.log(chalk.green(`Please cd ${chalk.green.bold(appName)} and start to work!😝`)) 110 | if (typeof cb === 'function') { 111 | cb() 112 | } 113 | }) 114 | } 115 | -------------------------------------------------------------------------------- /templates/simple/app/indexhtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= appName %> 7 | 8 | 9 |
10 | 11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /templates/simple/app/jsconfigjson: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "experimentalDecorators": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /templates/simple/app/nerv/index: -------------------------------------------------------------------------------- 1 | /** 2 | * @author <%= username %> 3 | * @date <%= date %> 4 | * @desc <%= description %> 5 | */ 6 | 7 | import Nerv from 'nervjs' 8 | 9 | class App extends Nerv.Component { 10 | constructor () { 11 | super(...arguments) 12 | this.state = {} 13 | } 14 | 15 | render () { 16 | return ( 17 |
18 |
19 | ) 20 | } 21 | } 22 | 23 | Nerv.render(, document.getElementById('J_container')) 24 | -------------------------------------------------------------------------------- /templates/simple/app/packagejson: -------------------------------------------------------------------------------- 1 | { 2 | "name": "<%= appName %>", 3 | "version": "0.0.1", 4 | "private": true, 5 | "dependencies": {<% if (framework === 'nerv') { %> 6 | "nervjs": "^1.2.8"<% } else if (framework === 'react') { %> 7 | "react": "^16.2.0", 8 | "react-dom": "^16.2.0"<% } else if (framework === 'vue') { %> 9 | "vue": "^2.5.13", 10 | "vue-router": "^3.0.1"<% } %> 11 | }, 12 | "scripts": { 13 | "test": "", 14 | "eslint": "eslint */component */page" 15 | }, 16 | "devDependencies": { 17 | "babel-eslint": "^8.2.1", 18 | "eslint": "^4.16.0", 19 | "eslint-config-standard": "^10.2.1",<% if (framework === "nerv" || framework === "react") { %> 20 | "eslint-config-standard-jsx": "^4.0.2",<%}%> 21 | "eslint-plugin-import": "^2.7.0", 22 | "eslint-plugin-node": "^5.1.1", 23 | "eslint-plugin-promise": "^3.5.0",<% if (framework === "nerv" || framework === "react") { %> 24 | "eslint-plugin-react": "^7.3.0",<%}%> 25 | "eslint-plugin-standard": "^3.0.1" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /templates/simple/app/react/index: -------------------------------------------------------------------------------- 1 | /** 2 | * @author <%= username %> 3 | * @date <%= date %> 4 | * @desc <%= description %> 5 | */ 6 | 7 | import React from 'react' 8 | import ReactDOM from 'react-dom' 9 | 10 | class App extends React.Component { 11 | constructor () { 12 | super(...arguments) 13 | this.state = {} 14 | } 15 | 16 | render () { 17 | return ( 18 |
19 |
20 | ) 21 | } 22 | } 23 | 24 | ReactDOM.render(, document.getElementById('J_container')) 25 | -------------------------------------------------------------------------------- /templates/simple/app/vue/app: -------------------------------------------------------------------------------- 1 | 6 | 7 | 22 | 23 | 26 | -------------------------------------------------------------------------------- /templates/simple/app/vue/index: -------------------------------------------------------------------------------- 1 | /** 2 | * @author <%= username %> 3 | * @date <%= date %> 4 | * @desc <%= description %> 5 | */ 6 | 7 | import Vue from 'vue' 8 | import App from './app.vue' 9 | 10 | new Vue({ 11 | render: h => h(App) 12 | }).$mount('#J_container') 13 | -------------------------------------------------------------------------------- /templates/simple/component/component.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | -------------------------------------------------------------------------------- /templates/simple/component/index.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash') 2 | const fs = require('fs-extra') 3 | const path = require('path') 4 | const chalk = require('chalk') 5 | const shelljs = require('shelljs') 6 | const ora = require('ora') 7 | const uuid = require('uuid') 8 | 9 | module.exports = function create (creater, params, helper, cb) { 10 | const { appName, template, framework, componentName, description, date, sass } = params 11 | // create component dir 12 | const componentDir = 'src/component' 13 | const componentCss = sass ? `${componentName}.scss` : `${componentName}.css` 14 | 15 | fs.mkdirpSync(path.join(componentDir, componentName)) 16 | 17 | // copy files 18 | if (framework !== 'vue') { 19 | creater.template(template, `component/${framework}`, 'component.js', path.join(componentDir, componentName, `${componentName}.js`), { 20 | date, 21 | description, 22 | componentName, 23 | sass 24 | }) 25 | creater.template(template, `component`, 'component.css', path.join(componentDir, componentName, componentCss)) 26 | } else { 27 | creater.template(template, 'component/vue', 'component.vue', path.join(componentDir, componentName, `${componentName}.vue`), { 28 | componentName, 29 | sass 30 | }) 31 | } 32 | 33 | creater.fs.commit(() => { 34 | console.log() 35 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created component: ${chalk.grey.bold(componentName)}`)}`) 36 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created directory: ${componentDir}/${componentName}`)}`) 37 | if (framework !== 'vue') { 38 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${componentDir}/${componentName}/${componentName}.js`)}`) 39 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${componentDir}/${componentName}/${componentCss}`)}`) 40 | } else { 41 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${componentDir}/${componentName}/${componentName}.vue`)}`) 42 | } 43 | console.log() 44 | console.log(chalk.green(`Create component ${chalk.green.bold(componentName)} Successfully!`)) 45 | if (typeof cb === 'function') { 46 | cb() 47 | } 48 | }) 49 | } 50 | -------------------------------------------------------------------------------- /templates/simple/component/nerv/component.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author <%= username %> 3 | * @date <%= date %> 4 | * @desc <%= description %> 5 | */ 6 | import Nerv from 'nervjs' 7 | import '<% if (sass) { %>./<%= componentName %>.scss<% } else { %><%= componentName %>.css<%}%>' 8 | 9 | class <%= _.upperFirst(_.camelCase(componentName)) %> extends Nerv.Component { 10 | constructor () { 11 | super(...arguments) 12 | this.state = {} 13 | } 14 | 15 | render () { 16 | return ( 17 |
18 |
19 | ) 20 | } 21 | } 22 | 23 | export default <%= _.upperFirst(_.camelCase(conf.componentName)) %> 24 | -------------------------------------------------------------------------------- /templates/simple/component/react/component.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author <%= username %> 3 | * @date <%= date %> 4 | * @desc <%= description %> 5 | */ 6 | import React from 'react' 7 | import '<% if (sass) { %>./<%= componentName %>.scss<% } else { %><%= componentName %>.css<%}%>' 8 | 9 | class <%= _.upperFirst(_.camelCase(componentName)) %> extends React.Component { 10 | constructor () { 11 | super(...arguments) 12 | this.state = {} 13 | } 14 | 15 | render () { 16 | return ( 17 |
18 |
19 | ) 20 | } 21 | } 22 | 23 | export default <%= _.upperFirst(_.camelCase(conf.componentName)) %> 24 | -------------------------------------------------------------------------------- /templates/simple/component/vue/component.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 17 | 18 | 20 | -------------------------------------------------------------------------------- /templates/simple/index.js: -------------------------------------------------------------------------------- 1 | const app = require('./app') 2 | const page = require('./view') 3 | const component = require('./component') 4 | 5 | module.exports = { 6 | app: app, 7 | page: page, 8 | component: component 9 | } 10 | -------------------------------------------------------------------------------- /templates/simple/view/index.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash') 2 | const fs = require('fs-extra') 3 | const path = require('path') 4 | const chalk = require('chalk') 5 | const shelljs = require('shelljs') 6 | const ora = require('ora') 7 | const uuid = require('uuid') 8 | 9 | module.exports = function create (creater, params, helper, cb) { 10 | const { appName, template, framework, pageName, description, date, sass } = params 11 | // create page dir 12 | const pageDir = 'src/view' 13 | const pageCss = sass ? `${pageName}.scss` : `${pageName}.css` 14 | 15 | fs.mkdirpSync(path.join(pageDir, pageName)) 16 | 17 | // copy files 18 | if (framework !== 'vue') { 19 | creater.template(template, `view/${framework}`, 'view.js', path.join(pageDir, pageName, `${pageName}.js`), { 20 | date, 21 | description, 22 | pageName, 23 | sass 24 | }) 25 | creater.template(template, `view`, 'view.css', path.join(pageDir, pageName, pageCss)) 26 | } else { 27 | creater.template(template, 'view/vue', 'view.vue', path.join(pageDir, pageName, `${pageName}.vue`), { 28 | pageName, 29 | sass 30 | }) 31 | } 32 | 33 | creater.fs.commit(() => { 34 | console.log() 35 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created page: ${chalk.grey.bold(pageName)}`)}`) 36 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created directory: ${pageDir}/${pageName}`)}`) 37 | if (framework !== 'vue') { 38 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${pageDir}/${pageName}/${pageName}.js`)}`) 39 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${pageDir}/${pageName}/${pageCss}`)}`) 40 | } else { 41 | console.log(`${chalk.green('✔ ')}${chalk.grey(`Created file: ${pageDir}/${pageName}/${pageName}.vue`)}`) 42 | } 43 | console.log() 44 | console.log(chalk.green(`Create page ${chalk.green.bold(pageName)} Successfully!`)) 45 | if (typeof cb === 'function') { 46 | cb() 47 | } 48 | }) 49 | } 50 | -------------------------------------------------------------------------------- /templates/simple/view/nerv/view.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author <%= username %> 3 | * @date <%= date %> 4 | * @desc <%= description %> 5 | */ 6 | import Nerv from 'nervjs' 7 | import '<% if (sass) { %>./<%= pageName %>.scss<% } else { %><%= pageName %>.css<%}%>' 8 | 9 | class <%= _.upperFirst(_.camelCase(pageName)) %> extends Nerv.Component { 10 | constructor () { 11 | super(...arguments) 12 | this.state = {} 13 | } 14 | 15 | render () { 16 | return ( 17 |
18 |
19 | ) 20 | } 21 | } 22 | 23 | export default <%= _.upperFirst(_.camelCase(conf.pageName)) %> 24 | -------------------------------------------------------------------------------- /templates/simple/view/react/view.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author <%= username %> 3 | * @date <%= date %> 4 | * @desc <%= description %> 5 | */ 6 | import React from 'react' 7 | import '<% if (sass) { %>./<%= pageName %>.scss<% } else { %><%= pageName %>.css<%}%>' 8 | 9 | class <%= _.upperFirst(_.camelCase(pageName)) %> extends React.Component { 10 | constructor () { 11 | super(...arguments) 12 | this.state = {} 13 | } 14 | 15 | render () { 16 | return ( 17 |
18 |
19 | ) 20 | } 21 | } 22 | 23 | export default <%= _.upperFirst(_.camelCase(conf.pageName)) %> 24 | -------------------------------------------------------------------------------- /templates/simple/view/view.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | -------------------------------------------------------------------------------- /templates/simple/view/vue/view.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 22 | 23 | 26 | -------------------------------------------------------------------------------- /test.txt: -------------------------------------------------------------------------------- 1 | add something 2 | --------------------------------------------------------------------------------