├── .gitignore ├── generators ├── component │ ├── templates │ │ ├── style.css │ │ ├── index.js │ │ └── component.js │ └── index.js ├── container │ ├── templates │ │ ├── style.css │ │ ├── index.js │ │ ├── actions.js │ │ ├── reducer.js │ │ ├── route.js │ │ └── container.js │ └── index.js ├── app │ ├── templates │ │ ├── .eslintignore │ │ ├── src │ │ │ ├── utils │ │ │ │ └── README.md │ │ │ ├── server │ │ │ │ ├── README.md │ │ │ │ ├── handlers │ │ │ │ │ ├── staticFiles.js │ │ │ │ │ ├── main.js │ │ │ │ │ └── name.js │ │ │ │ └── index.js │ │ │ ├── components │ │ │ │ └── Name │ │ │ │ │ ├── index.js │ │ │ │ │ ├── style.css │ │ │ │ │ └── Name.js │ │ │ ├── containers │ │ │ │ └── Main │ │ │ │ │ ├── index.js │ │ │ │ │ ├── components │ │ │ │ │ ├── Header │ │ │ │ │ │ ├── index.js │ │ │ │ │ │ ├── style.css │ │ │ │ │ │ └── Header.js │ │ │ │ │ └── Message │ │ │ │ │ │ ├── index.js │ │ │ │ │ │ ├── style.css │ │ │ │ │ │ └── Message.js │ │ │ │ │ ├── style.css │ │ │ │ │ ├── reducer.js │ │ │ │ │ ├── actions.js │ │ │ │ │ └── Main.js │ │ │ ├── constants │ │ │ │ └── actionTypes.js │ │ │ ├── images │ │ │ │ ├── cat.jpg │ │ │ │ └── header.png │ │ │ ├── routes.js │ │ │ ├── reducers.js │ │ │ ├── styles │ │ │ │ └── App.css │ │ │ ├── app.js │ │ │ └── store.js │ │ ├── test │ │ │ ├── macha.opts │ │ │ ├── .eslintrc │ │ │ ├── css-null-compiler.js │ │ │ ├── containers │ │ │ │ └── Main │ │ │ │ │ ├── reducer.test.js │ │ │ │ │ └── components │ │ │ │ │ └── Message │ │ │ │ │ └── Message.test.js │ │ │ └── dom.js │ │ ├── README.md │ │ ├── .babelrc │ │ ├── index.html │ │ ├── .gitignore │ │ ├── devServer.js │ │ ├── .eslintrc │ │ ├── webpack │ │ │ ├── server.js │ │ │ ├── base.js │ │ │ └── client.js │ │ ├── package.json │ │ ├── .stylelintrc │ │ ├── validate-commit-msg.js │ │ └── docs │ │ │ └── user_guide_zh.md │ └── index.js └── unittest │ ├── templates │ └── example.test.js │ └── index.js ├── docs ├── user_guide.md ├── images │ ├── eslint_error.png │ └── stylelint_error.png ├── todo.md └── user_guide_zh.md ├── package.json ├── README_zh.md ├── LICENSE ├── README.md └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | -------------------------------------------------------------------------------- /generators/component/templates/style.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /generators/container/templates/style.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /generators/app/templates/.eslintignore: -------------------------------------------------------------------------------- 1 | test/dom.js 2 | -------------------------------------------------------------------------------- /generators/app/templates/src/utils/README.md: -------------------------------------------------------------------------------- 1 | ## utils directory 2 | -------------------------------------------------------------------------------- /generators/app/templates/test/macha.opts: -------------------------------------------------------------------------------- 1 | --require test/dom.js 2 | -------------------------------------------------------------------------------- /generators/app/templates/src/server/README.md: -------------------------------------------------------------------------------- 1 | ## 服务端目录,可能是 Python 或 NodeJs 2 | -------------------------------------------------------------------------------- /docs/user_guide.md: -------------------------------------------------------------------------------- 1 | ## Welcome to translate [user guide](./user_guide_zh.md) to English. 2 | -------------------------------------------------------------------------------- /generators/app/templates/test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /generators/app/templates/README.md: -------------------------------------------------------------------------------- 1 | ## Welcome to translate [user guide](docs/user_guide_zh.md) to English. 2 | -------------------------------------------------------------------------------- /generators/app/templates/src/components/Name/index.js: -------------------------------------------------------------------------------- 1 | import Name from './Name'; 2 | 3 | export default Name; 4 | -------------------------------------------------------------------------------- /generators/app/templates/src/containers/Main/index.js: -------------------------------------------------------------------------------- 1 | import Main from './Main'; 2 | 3 | export default Main; 4 | -------------------------------------------------------------------------------- /docs/images/eslint_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knownsec/generator-modation/HEAD/docs/images/eslint_error.png -------------------------------------------------------------------------------- /docs/images/stylelint_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knownsec/generator-modation/HEAD/docs/images/stylelint_error.png -------------------------------------------------------------------------------- /generators/app/templates/src/containers/Main/components/Header/index.js: -------------------------------------------------------------------------------- 1 | import Header from './Header'; 2 | 3 | export default Header; 4 | -------------------------------------------------------------------------------- /generators/app/templates/src/containers/Main/components/Message/index.js: -------------------------------------------------------------------------------- 1 | import Message from './Message'; 2 | 3 | export default Message; 4 | -------------------------------------------------------------------------------- /generators/component/templates/index.js: -------------------------------------------------------------------------------- 1 | import <%= componentName %> from './<%= componentName %>'; 2 | 3 | export default <%= componentName %>; 4 | -------------------------------------------------------------------------------- /generators/container/templates/index.js: -------------------------------------------------------------------------------- 1 | import <%= containerName %> from './<%= containerName %>'; 2 | 3 | export default <%= containerName %>; 4 | -------------------------------------------------------------------------------- /generators/app/templates/src/constants/actionTypes.js: -------------------------------------------------------------------------------- 1 | export const CHANGE_NAME = 'change_name'; 2 | export const CHANGE_MESSAGE = 'change_message'; 3 | -------------------------------------------------------------------------------- /generators/app/templates/src/images/cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knownsec/generator-modation/HEAD/generators/app/templates/src/images/cat.jpg -------------------------------------------------------------------------------- /generators/app/templates/src/images/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knownsec/generator-modation/HEAD/generators/app/templates/src/images/header.png -------------------------------------------------------------------------------- /generators/app/templates/src/components/Name/style.css: -------------------------------------------------------------------------------- 1 | .name { 2 | font-size: 14px; 3 | } 4 | 5 | .name .label { 6 | margin-left: 70px; 7 | margin-right: 20px; 8 | width: 20%; 9 | } 10 | -------------------------------------------------------------------------------- /generators/container/templates/actions.js: -------------------------------------------------------------------------------- 1 | import * as at from 'constants/actionTypes'; 2 | 3 | 4 | export function someAction(msg) { 5 | return { 6 | msg, 7 | type: at.SOME_ACTION, 8 | }; 9 | } 10 | -------------------------------------------------------------------------------- /generators/app/templates/src/containers/Main/components/Message/style.css: -------------------------------------------------------------------------------- 1 | .message { 2 | font-size: 14px; 3 | margin-top: 10px; 4 | } 5 | 6 | .message .label { 7 | margin-left: 20px; 8 | margin-right: 20px; 9 | width: 20%; 10 | } 11 | -------------------------------------------------------------------------------- /generators/app/templates/test/css-null-compiler.js: -------------------------------------------------------------------------------- 1 | function noop() { 2 | return null; 3 | } 4 | 5 | require.extensions['.styl'] = noop; 6 | require.extensions['.scss'] = noop; 7 | require.extensions['.less'] = noop; 8 | require.extensions['.css'] = noop; 9 | 10 | -------------------------------------------------------------------------------- /generators/app/templates/src/containers/Main/style.css: -------------------------------------------------------------------------------- 1 | .content { 2 | display: flex; 3 | flex-direction: column; 4 | } 5 | 6 | .content .main { 7 | margin-top: 10px; 8 | text-align: center; 9 | } 10 | 11 | .content .main .cat { 12 | margin-top: 20px; 13 | } 14 | -------------------------------------------------------------------------------- /generators/app/templates/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "latest", 4 | "react", 5 | "stage-0" 6 | ], 7 | "plugins": [ 8 | "transform-runtime", 9 | "babel-plugin-transform-decorators-legacy", 10 | ["import", {"libraryName": "antd"}] 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /generators/app/templates/src/containers/Main/components/Header/style.css: -------------------------------------------------------------------------------- 1 | .header { 2 | height: 80px; 3 | background-image: url(images/header.png); 4 | background-size: contain; 5 | } 6 | 7 | .header .title { 8 | font-size: 40px; 9 | color: silver; 10 | margin-left: 20px; 11 | line-height: 80px; 12 | } 13 | -------------------------------------------------------------------------------- /generators/app/templates/src/routes.js: -------------------------------------------------------------------------------- 1 | // polyfill webpack require.ensure 2 | if (typeof require.ensure !== 'function') require.ensure = (d, c) => c(require); 3 | 4 | import Main from 'containers/Main'; 5 | 6 | export function createRoutes() { 7 | return { 8 | path: '/', 9 | component: Main, 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /generators/container/templates/reducer.js: -------------------------------------------------------------------------------- 1 | import * as at from 'constants/actionTypes'; 2 | 3 | 4 | const initialState = {}; 5 | 6 | export default function someReducer(state = initialState, action) { 7 | switch (action.type) { 8 | case at.SOME_ACTION: 9 | return state; 10 | default: 11 | return state; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /generators/app/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%= projectName %> 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /generators/app/templates/src/reducers.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import { routerReducer } from 'react-router-redux'; 3 | import main from 'containers/Main/reducer'; 4 | 5 | 6 | export default function createReducer(asyncReducers) { 7 | return combineReducers({ 8 | main, 9 | routing: routerReducer, 10 | ...asyncReducers, 11 | }); 12 | } 13 | 14 | -------------------------------------------------------------------------------- /generators/app/templates/src/styles/App.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif; 3 | line-height: 1.5; 4 | color: #666; 5 | font-size: 14px; 6 | background-color: #f7f7f7; 7 | transition: background 1s cubic-bezier(0.075, 0.82, 0.165, 1); 8 | overflow-x: hidden; 9 | 10 | height: 100%; 11 | width: 100%; 12 | } 13 | -------------------------------------------------------------------------------- /generators/app/templates/src/server/handlers/staticFiles.js: -------------------------------------------------------------------------------- 1 | export default { 2 | method: 'GET', 3 | path: '/assets/{filename*}', 4 | config: { 5 | handler: { 6 | file: (request) => { 7 | let assetsPath = `dist${request.path}`; 8 | if (process.env.NODE_ENV === 'production') { 9 | assetsPath = `.${request.path}`; 10 | } 11 | return assetsPath; 12 | }, 13 | }, 14 | }, 15 | }; 16 | -------------------------------------------------------------------------------- /generators/app/templates/test/containers/Main/reducer.test.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import main from 'containers/Main/reducer'; 3 | import * as at from 'constants/actionTypes'; 4 | import immutable from 'immutable'; 5 | 6 | describe('main reducer', () => { 7 | it('should change name correctly', () => { 8 | const result = main(immutable.fromJS({}), { 9 | type: at.CHANGE_NAME, 10 | name: 'bar', 11 | }); 12 | expect(result.get('name')).to.be.equal('bar'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /generators/unittest/templates/example.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { shallow } from 'enzyme'; 3 | import { expect } from 'chai'; 4 | import sinon from 'sinon'; 5 | 6 | const props = { 7 | someProperty: 'foo', 8 | }; 9 | 10 | const context = { 11 | fooActions: { 12 | someMethod: sinon.spy(), 13 | }, 14 | }; 15 | 16 | describe('<%= component %> component', () => { 17 | it('should render correctly', () => { 18 | const wrap = shallow(<<%= component %> {...props} />); 19 | expect(wrap.find('span').length).to.be.equal(1); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /generators/app/templates/src/containers/Main/reducer.js: -------------------------------------------------------------------------------- 1 | import * as at from 'constants/actionTypes'; 2 | import immutable from 'immutable'; 3 | 4 | const INITIAL_STATE = immutable.fromJS({ 5 | name: 'foo', 6 | message: 'hello world', 7 | }); 8 | 9 | export default function main(state = INITIAL_STATE, action) { 10 | switch (action.type) { 11 | case at.CHANGE_NAME: 12 | return state.update('name', () => action.name); 13 | case at.CHANGE_MESSAGE: 14 | return state.update('message', () => action.message); 15 | default: 16 | return state; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /generators/container/templates/route.js: -------------------------------------------------------------------------------- 1 | if (typeof require.ensure !== 'function') require.ensure = (d, c) => c(require); 2 | 3 | import { injectAsyncReducer } from 'store'; 4 | 5 | 6 | export default function createRoutes(store) { 7 | return { 8 | path: '<%= containerName.toLowerCase() %>', 9 | getComponent(location, cb) { 10 | require.ensure([], (require) => { 11 | injectAsyncReducer(store, '<%= containerName.toLowerCase() %>', require('./reducer').default); 12 | 13 | cb(null, require('./<%= containerName %>').default); 14 | }); 15 | }, 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /docs/todo.md: -------------------------------------------------------------------------------- 1 | ## Todo List 2 | 3 | - [ ] add http proxy 4 | - [ ] add generator unit test 5 | - [ ] change mocha to jest 6 | - [x] add user guide 7 | - [x] change `npm start` to use `webpack devserver` 8 | - [x] make server side can choose to config by user 9 | - [x] sub-generator container should put in src/containers 10 | - [x] sub-generator component change `.jsx` file to `.js` file 11 | - [x] add fetch data example in server side 12 | - [x] change sub generator's package.json to index.js 13 | - [x] add css check 14 | - [x] when client side, remove randomName function 15 | - [x] unittest generetor 16 | - [x] use css flex in template 17 | -------------------------------------------------------------------------------- /generators/app/templates/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | *.DS_Store 10 | 11 | # node-waf configuration 12 | .lock-wscript 13 | 14 | # Compiled binary addons (http://nodejs.org/api/addons.html) 15 | build/Release 16 | 17 | # Dependency directory 18 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 19 | node_modules 20 | 21 | # Bower 22 | bower_components/ 23 | dist 24 | 25 | # WebStorm文件 26 | *.idea/ 27 | 28 | # Emacs 29 | # tern(JS解析器, emacs里补全用的) 30 | .tern-port 31 | .#* 32 | *# 33 | *~ 34 | 35 | # Mac 临时文件 36 | .DS_Store 37 | 38 | # 其他 39 | dump.rdb 40 | -------------------------------------------------------------------------------- /generators/app/templates/src/containers/Main/actions.js: -------------------------------------------------------------------------------- 1 | import * as at from 'constants/actionTypes'; 2 | 3 | export function changeName(name) { 4 | return { 5 | type: at.CHANGE_NAME, 6 | name, 7 | }; 8 | } 9 | 10 | export function changeMessage(message) { 11 | return { 12 | type: at.CHANGE_MESSAGE, 13 | message, 14 | }; 15 | } 16 | 17 | <% if (serverSide) { -%> 18 | export function randomName(num) { 19 | return async (dispatch) => { 20 | const response = await fetch('/api/name/random', { 21 | method: 'post', 22 | body: JSON.stringify({ 23 | num, 24 | }), 25 | }); 26 | const result = await response.json(); 27 | return dispatch(changeName(result.name)); 28 | }; 29 | } 30 | <% } -%> 31 | -------------------------------------------------------------------------------- /generators/unittest/index.js: -------------------------------------------------------------------------------- 1 | const generators = require('yeoman-generator'); 2 | 3 | class Unittest extends generators.Base { 4 | constructor(args, opts) { 5 | super(args, opts); 6 | 7 | this.argument('component', { 8 | type: String, 9 | required: true, 10 | description: 'component name' 11 | }); 12 | } 13 | 14 | prompting() { 15 | return this.prompt([]).then((props) => { 16 | this.props = props; 17 | }); 18 | } 19 | 20 | writing() { 21 | this.fs.copyTpl( 22 | this.templatePath('example.test.js'), 23 | this.destinationPath(`${this.component}/${this.component}.test.js`), 24 | { 25 | component: this.component, 26 | } 27 | ); 28 | } 29 | } 30 | 31 | module.exports = Unittest; 32 | -------------------------------------------------------------------------------- /generators/app/templates/devServer.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console:0 */ 2 | const webpack = require('webpack'); 3 | const WebpackDevServer = require('webpack-dev-server'); 4 | 5 | const config = require('./webpack/client'); 6 | const runServer = require('./dist/server').runServer; 7 | 8 | const compiler = webpack(config); 9 | 10 | if (process.env.NODE_ENV === 'development') { 11 | const DashboardPlugin = require('webpack-dashboard/plugin'); 12 | compiler.apply(new DashboardPlugin()); 13 | } 14 | 15 | new WebpackDevServer(compiler, config.devServer) 16 | .listen(config.devServer.port, 'localhost', (err) => { 17 | if (err) { 18 | console.log(err); 19 | } 20 | console.log('==> ✅ Static file@localhost:' + config.devServer.port); 21 | runServer(); 22 | }); 23 | 24 | -------------------------------------------------------------------------------- /generators/app/templates/src/containers/Main/components/Header/Header.js: -------------------------------------------------------------------------------- 1 | import style from './style.css'; 2 | 3 | import React, { Component, PropTypes } from 'react'; 4 | import classnames from 'classnames'; 5 | 6 | class Header extends Component { 7 | static propTypes = { 8 | style: PropTypes.string, 9 | className: PropTypes.string, 10 | }; 11 | 12 | static defaultProps = { 13 | className: '', 14 | }; 15 | 16 | render() { 17 | const { className } = this.props; 18 | 19 | return ( 20 |
24 | Hello World 25 |
26 | ); 27 | } 28 | } 29 | 30 | export default Header; 31 | -------------------------------------------------------------------------------- /generators/app/templates/src/server/handlers/main.js: -------------------------------------------------------------------------------- 1 | export default { 2 | method: ['GET'], 3 | path: '/', 4 | 5 | config: { 6 | handler(request, reply) { 7 | const host = process.env.HOSTNAME || 'localhost'; 8 | const webserver = process.env.NODE_ENV === 'production' ? '' : `//${host}:8080`; 9 | 10 | return reply( 11 | ` 12 | 13 | 14 | 15 | <%= projectName %> 16 | 17 | 18 | 19 |
20 | 21 | 22 | ` 23 | ); 24 | }, 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "generator-modation", 3 | "version": "0.5.17", 4 | "description": "a frontend project generator with React, Redux and Webpack etc.", 5 | "license": "MIT", 6 | "main": "generators/app/index.js", 7 | "repository": "knownsec/generator-modation", 8 | "bugs": { 9 | "url": "https://github.com/knownsec/generator-modation/issues" 10 | }, 11 | "author": { 12 | "name": "zhaozhiming", 13 | "email": "zhaozhiming003@gmail.com", 14 | "url": "https://github.com/zhaozhiming" 15 | }, 16 | "engines": { 17 | "node": ">=6.0.0" 18 | }, 19 | "scripts": { 20 | "test": "echo \"Error: no test specified\" && exit 1" 21 | }, 22 | "keywords": [ 23 | "yeoman-generator" 24 | ], 25 | "files": [ 26 | "generators" 27 | ], 28 | "dependencies": { 29 | "yeoman-generator": "^0.23.3" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /generators/app/templates/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | "parser": "babel-eslint", 4 | "rules": { 5 | "indent": [2, 2, {"SwitchCase": 1}], 6 | "eol-last": 0, 7 | "no-debugger": 1, 8 | "no-restricted-syntax": 0, 9 | "react/prefer-stateless-function": 0, 10 | "react/forbid-prop-types": 0, 11 | "react/jsx-filename-extension": 0, 12 | "no-plusplus": 0, 13 | "import/imports-first": 0, 14 | "import/no-extraneous-dependencies": 0, 15 | "import/prefer-default-export": 0, 16 | "no-eval": 0 17 | }, 18 | settings: { 19 | import/resolver: { 20 | webpack: {config: 'webpack/base.js'} 21 | } 22 | }, 23 | "env": { 24 | "browser": true, 25 | "commonjs": true, 26 | "es6": true, 27 | "node": true 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /generators/app/templates/src/server/handlers/name.js: -------------------------------------------------------------------------------- 1 | const ALL_NAMES = [ 2 | 'Latlux', 3 | 'Opela', 4 | 'Tres-Zap', 5 | 'Bitwolf', 6 | 'Domainer', 7 | 'Temp', 8 | 'Konklux', 9 | 'Bitwolf', 10 | 'Zathin', 11 | 'Ventosanzap', 12 | 'Tres', 13 | ]; 14 | 15 | function getRandomName() { 16 | const randomIndex = Math.floor(Math.random() * (ALL_NAMES.length - 0)) + 0; 17 | return ALL_NAMES[randomIndex]; 18 | } 19 | 20 | export default { 21 | method: ['POST'], 22 | path: '/api/name/random', 23 | 24 | config: { 25 | handler(request, reply) { 26 | const { num } = JSON.parse(request.payload); 27 | const randomNames = []; 28 | for (let i = 0; i < num; i++) { 29 | randomNames.push(getRandomName()); 30 | } 31 | return reply({ 32 | name: randomNames.join(' '), 33 | }); 34 | }, 35 | }, 36 | }; 37 | -------------------------------------------------------------------------------- /generators/component/templates/component.js: -------------------------------------------------------------------------------- 1 | <% if (enableCssModules) { %> 2 | import style from './style.css'; 3 | <% } %> 4 | import classnames from 'classnames'; 5 | import React, { Component, PropTypes } from 'react'; 6 | 7 | 8 | class <%= componentName %> extends Component { 9 | static propTypes = { 10 | style: PropTypes.string, 11 | className: PropTypes.string, 12 | }; 13 | 14 | static defaultProps = { 15 | className: '', 16 | }; 17 | 18 | constructor(props, context) { 19 | super(props, context); 20 | this.state = {}; 21 | } 22 | 23 | render() { 24 | const { className } = this.props; 25 | 26 | return ( 27 |
)} 30 | > 31 | <%= componentName %> 32 |
33 | ); 34 | } 35 | } 36 | 37 | export default <%= componentName %>; 38 | -------------------------------------------------------------------------------- /generators/app/templates/src/server/index.js: -------------------------------------------------------------------------------- 1 | import Hapi from 'hapi'; 2 | import h2o2 from 'h2o2'; 3 | import inert from 'inert'; 4 | import main from './handlers/main'; 5 | import staticFiles from './handlers/staticFiles'; 6 | import name from './handlers/name'; 7 | 8 | 9 | /** 10 | * Start Hapi server on port 8000. 11 | */ 12 | const host = process.env.HOSTNAME || 'localhost'; 13 | const port = process.env.PORT || 8000; 14 | const server = new Hapi.Server(); 15 | 16 | server.connection({ host, port }); 17 | server.register( 18 | [h2o2, inert], (err) => { 19 | if (err) throw err; 20 | } 21 | ); 22 | 23 | server.route([main, staticFiles, name]); 24 | 25 | export function runServer() { 26 | server.start(() => { 27 | /* eslint no-console:0 */ 28 | console.info(`==> 🌎 Go to ${server.info.uri.toLowerCase()}`); 29 | }); 30 | } 31 | 32 | if (process.env.NODE_ENV === 'production') { 33 | runServer(); 34 | } 35 | 36 | export default { 37 | server, 38 | runServer, 39 | }; 40 | 41 | -------------------------------------------------------------------------------- /generators/app/templates/src/app.js: -------------------------------------------------------------------------------- 1 | import 'normalize.css'; 2 | import 'styles/App.css'; 3 | import 'antd/dist/antd.css'; 4 | import 'whatwg-fetch'; 5 | 6 | import React from 'react'; 7 | import ReactDOM from 'react-dom'; 8 | 9 | import configureStore from 'store'; 10 | import { createRoutes } from 'routes'; 11 | import { Router, hashHistory as historyProvider, match } from 'react-router'; 12 | import { Provider } from 'react-redux'; 13 | import { syncHistoryWithStore } from 'react-router-redux'; 14 | 15 | 16 | const { pathname, search, hash } = window.location; 17 | const location = `${pathname}${search}${hash}`; 18 | 19 | export const store = configureStore(historyProvider, {}); 20 | const routes = createRoutes(store); 21 | export const history = syncHistoryWithStore(historyProvider, store); 22 | 23 | match({ routes, location }, () => { 24 | ReactDOM.render( 25 | 26 | 27 | , 28 | window.document.getElementById('app') 29 | ); 30 | }); 31 | 32 | -------------------------------------------------------------------------------- /README_zh.md: -------------------------------------------------------------------------------- 1 | # 前端项目生成器 2 | 3 | * [使用指南](./docs/user_guide_zh.md) —— 如何使用项目框架开发应用 4 | 5 | ## 技术栈 6 | 7 | * React 8 | * Redux 9 | * Webpack 10 | * React-router 11 | * Immutable 12 | * Antd 13 | 14 | ## 用法 15 | 16 | * yarn global add yo 17 | * yarn global add generator-modation 18 | * mkdir myapp && cd myapp 19 | * yo modation 20 | * yarn install 21 | * yarn start 22 | * view `localhost:8000` on your browser 23 | * Enjoy! 24 | 25 | ## 代码检查 26 | 27 | * Eslint 检查基于[airbnb JS 规范](https://github.com/airbnb/javascript/tree/master/packages/eslint-config-airbnb) 28 | * Stylelint 检查 CSS 29 | * Git 提交信息检查 30 | 31 | ## 子生成器 32 | 33 | ### 添加新组件 34 | 35 | * 进到你要添加组件的目录,比如:`cd src/components` 36 | * 然后执行以下命令: 37 | ``` 38 | yo modation:component 39 | ``` 40 | 41 | ### 添加新 container 42 | 43 | * 进到`src/containers`目录,执行以下命令: 44 | ``` 45 | yo modation:container 46 | ``` 47 | 48 | ### 添加 React 组件测试用例 49 | 50 | * 进到要创建测试用例的目录,执行以下命令: 51 | ``` 52 | yo modation:unittest 53 | ``` 54 | 55 | ## [待完成功能](./docs/todo.md) 56 | -------------------------------------------------------------------------------- /generators/container/templates/container.js: -------------------------------------------------------------------------------- 1 | <% if (enableCssModules) { %> 2 | import style from './style.css'; 3 | <% } %> 4 | 5 | import React, { Component, PropTypes } from 'react'; 6 | import { connect } from 'react-redux'; 7 | import { bindActionCreators } from 'redux'; 8 | 9 | import * as actions from './actions'; 10 | 11 | 12 | function mapStateToProps(state) { 13 | return { 14 | state: state.<%= containerName.toLowerCase() %>, 15 | }; 16 | } 17 | 18 | function mapDispatchToProps(dispatch) { 19 | return { 20 | actions: bindActionCreators(actions, dispatch), 21 | }; 22 | } 23 | 24 | @connect(mapStateToProps, mapDispatchToProps) 25 | class <%= containerName %> extends Component { 26 | static propTypes = { 27 | state: PropTypes.object.isRequired, 28 | actions: PropTypes.object.isRequired, 29 | }; 30 | 31 | static defaultProps = { 32 | }; 33 | 34 | constructor(props, context) { 35 | super(props, context); 36 | this.state = {}; 37 | } 38 | 39 | render() { 40 | return
<%= containerName %>
; 41 | } 42 | } 43 | 44 | export default <%= containerName %>; 45 | -------------------------------------------------------------------------------- /generators/app/templates/test/dom.js: -------------------------------------------------------------------------------- 1 | import jsdom from 'jsdom'; 2 | 3 | // setup the simplest document possible 4 | const doc = jsdom.jsdom(''); 5 | 6 | // get the window object out of the document 7 | const win = doc.defaultView; 8 | 9 | // set globals for mocha that make access to document and window feel 10 | // natural in the test environment 11 | global.document = doc; 12 | global.window = win; 13 | global.Promise = require('bluebird'); 14 | 15 | // from mocha-jsdom https://github.com/rstacruz/mocha-jsdom/blob/master/index.js#L80 16 | function propagateToGlobal(window) { 17 | for (const key in window) { 18 | if (!window.hasOwnProperty(key)) continue; 19 | if (key in global) continue; 20 | 21 | global[key] = window[key]; 22 | } 23 | 24 | window.matchMedia = window.matchMedia || function matchMedia() { 25 | return { 26 | matches: false, 27 | addListener: () => {}, 28 | removeListener: () => {}, 29 | }; 30 | }; 31 | } 32 | 33 | // take all properties of the window object and also attach it to the 34 | // mocha global object 35 | propagateToGlobal(win); 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 zhaozhiming 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 | -------------------------------------------------------------------------------- /generators/app/templates/src/store.js: -------------------------------------------------------------------------------- 1 | import thunk from 'redux-thunk'; 2 | import { createStore, applyMiddleware, compose } from 'redux'; 3 | import { routerMiddleware } from 'react-router-redux'; 4 | 5 | import createReducer from 'reducers'; 6 | 7 | 8 | export default function configureStore(history, initialState = {}) { 9 | const store = createStore(createReducer(), initialState, compose( 10 | applyMiddleware( 11 | thunk, 12 | routerMiddleware(history), 13 | ), 14 | 15 | (process.env.NODE_ENV === 'development') && 16 | typeof window === 'object' && 17 | typeof window.devToolsExtension !== 'undefined' ? window.devToolsExtension() : f => f 18 | )); 19 | 20 | store.asyncReducers = {}; 21 | 22 | if (process.env.NODE_ENV === 'development') { 23 | if (module.hot) { 24 | module.hot.accept('reducers', () => store.replaceReducer(createReducer())); 25 | } 26 | } 27 | 28 | return store; 29 | } 30 | 31 | export function injectAsyncReducer(store, name, asyncReducer) { 32 | /* eslint no-param-reassign:0 */ 33 | store.asyncReducers[name] = asyncReducer; 34 | store.replaceReducer(createReducer(store.asyncReducers)); 35 | } 36 | 37 | -------------------------------------------------------------------------------- /generators/app/templates/src/containers/Main/components/Message/Message.js: -------------------------------------------------------------------------------- 1 | import style from './style.css'; 2 | import { Button, Input } from 'antd'; 3 | import React, { Component, PropTypes } from 'react'; 4 | 5 | 6 | class Message extends Component { 7 | static propTypes = { 8 | message: PropTypes.string.isRequired, 9 | }; 10 | 11 | static contextTypes = { 12 | mainActions: PropTypes.object, 13 | }; 14 | 15 | constructor(props, context) { 16 | super(props, context); 17 | this.state = { 18 | message: this.props.message, 19 | }; 20 | this.handleChange = this.handleChange.bind(this); 21 | this.handleClick = this.handleClick.bind(this); 22 | } 23 | 24 | handleChange(event) { 25 | this.setState({ message: event.target.value }); 26 | } 27 | 28 | handleClick() { 29 | this.context.mainActions.changeMessage(this.state.message); 30 | } 31 | 32 | render() { 33 | const { message } = this.state; 34 | return ( 35 |
36 | Message: {this.props.message} 37 | 38 | 39 |
40 | ); 41 | } 42 | } 43 | 44 | export default Message; 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Frontend Project Generator ([中文版](./README_zh.md)) 2 | 3 | * [User Guide](./docs/user_guide.md) - How to develop apps bootstrapped with generator-modation 4 | 5 | ## Technology Stack 6 | 7 | * React 8 | * Redux 9 | * Webpack 10 | * React-router 11 | * Immutable 12 | * Antd 13 | 14 | ## Usage 15 | 16 | * yarn global add yo 17 | * yarn global add generator-modation 18 | * mkdir myapp && cd myapp 19 | * yo modation 20 | * yarn install 21 | * yarn start 22 | * view `localhost:8000` on your browser 23 | * Enjoy! 24 | 25 | ## Code Check 26 | 27 | * Eslint check JS with [airbnb JS style](https://github.com/airbnb/javascript/tree/master/packages/eslint-config-airbnb) 28 | * Stylelint check CSS 29 | * Git commit message check 30 | 31 | ## Sub Generator 32 | 33 | ### Add New Component 34 | 35 | * Go to the folder where you will to add component, like: `cd src/components` 36 | * And run command as follow: 37 | ``` 38 | yo modation:component 39 | ``` 40 | 41 | ### Add New Container 42 | 43 | * Go to `src/containers` folder, run command as follow: 44 | ``` 45 | yo modation:container 46 | ``` 47 | 48 | ### Add Testcase of React Component 49 | 50 | * Go to the folder where you will to add testcase, and run command as follow: 51 | ``` 52 | yo modation:unittest 53 | ``` 54 | 55 | ## [Todo List](./docs/todo.md) 56 | -------------------------------------------------------------------------------- /generators/app/templates/test/containers/Main/components/Message/Message.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Button, Input } from 'antd'; 3 | import { shallow } from 'enzyme'; 4 | import { expect } from 'chai'; 5 | import sinon from 'sinon'; 6 | import Message from 'containers/Main/components/Message'; 7 | 8 | const props = { 9 | message: 'foo', 10 | }; 11 | 12 | const context = { 13 | mainActions: { 14 | changeMessage: sinon.spy(), 15 | }, 16 | }; 17 | 18 | describe('Message component', () => { 19 | it('should render correctly', () => { 20 | const wrap = shallow(); 21 | expect(wrap.find('span').length).to.be.equal(1); 22 | expect(wrap.find('span').text()).to.be.equal('Message: foo'); 23 | expect(wrap.find(Input).length).to.be.equal(1); 24 | expect(wrap.find(Button).length).to.be.equal(1); 25 | }); 26 | 27 | it('should button click correctly', () => { 28 | const wrap = shallow(, { context }); 29 | wrap.setState({ message: 'bar' }); 30 | wrap.find(Button).simulate('click'); 31 | expect(context.mainActions.changeMessage.callCount).to.be.equal(1); 32 | expect(wrap.state('message')).to.be.equal('bar'); 33 | }); 34 | 35 | it('should input change correctly', () => { 36 | const wrap = shallow(); 37 | wrap.find(Input).simulate('change', { target: { value: 'bar' } }); 38 | expect(wrap.state('message')).to.be.equal('bar'); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /generators/app/templates/src/components/Name/Name.js: -------------------------------------------------------------------------------- 1 | import { Button, Input } from 'antd'; 2 | import React, { Component, PropTypes } from 'react'; 3 | import style from './style.css'; 4 | 5 | class Name extends Component { 6 | static propTypes = { 7 | name: PropTypes.string.isRequired, 8 | mainActions: PropTypes.object.isRequired, 9 | }; 10 | 11 | constructor(props, context) { 12 | super(props, context); 13 | this.state = { 14 | name: this.props.name, 15 | }; 16 | this.handleChange = this.handleChange.bind(this); 17 | this.handleNormalClick = this.handleNormalClick.bind(this); 18 | <% if (serverSide) { -%> 19 | this.handleRandomClick = this.handleRandomClick.bind(this); 20 | <% } -%> 21 | } 22 | 23 | handleChange(event) { 24 | this.setState({ name: event.target.value }); 25 | } 26 | 27 | handleNormalClick() { 28 | this.props.mainActions.changeName(this.state.name); 29 | } 30 | 31 | <% if (serverSide) { -%> 32 | handleRandomClick() { 33 | this.props.mainActions.randomName(1); 34 | } 35 | <% } -%> 36 | 37 | render() { 38 | const { name } = this.state; 39 | return ( 40 |
41 | Name: {this.props.name} 42 | 43 | 44 | <% if (serverSide) { -%> 45 | 46 | <% } -%> 47 |
48 | ); 49 | } 50 | } 51 | 52 | 53 | export default Name; 54 | -------------------------------------------------------------------------------- /generators/component/index.js: -------------------------------------------------------------------------------- 1 | const generators = require('yeoman-generator'); 2 | 3 | 4 | class Component extends generators.Base { 5 | constructor(args, opts) { 6 | super(args, opts); 7 | 8 | this.argument('componentName', { 9 | type: String, 10 | required: true, 11 | description: 'component name' 12 | }); 13 | } 14 | 15 | prompting() { 16 | const prompts = [ 17 | { 18 | type: 'checkbox', 19 | name: 'features', 20 | message: 'What more would you like?', 21 | choices: [ 22 | { 23 | name: 'CSS Modules', 24 | value: 'cssModules', 25 | checked: true, 26 | }, 27 | ], 28 | }, 29 | ]; 30 | 31 | return this.prompt(prompts).then((props) => { 32 | this.props = props; 33 | }); 34 | } 35 | 36 | writing() { 37 | const componentName = this.componentName; 38 | 39 | this.fs.copyTpl( 40 | this.templatePath('component.js'), 41 | this.destinationPath(`${componentName}/${componentName}.js`), 42 | { 43 | componentName, 44 | enableCssModules: this.props.features.includes('cssModules'), 45 | } 46 | ); 47 | 48 | this.fs.copyTpl( 49 | this.templatePath('index.js'), 50 | this.destinationPath(`${componentName}/index.js`), 51 | { 52 | componentName, 53 | } 54 | ); 55 | 56 | this.fs.copy( 57 | this.templatePath('style.css'), 58 | this.destinationPath(`${componentName}/style.css`) 59 | ); 60 | } 61 | } 62 | 63 | module.exports = Component; 64 | -------------------------------------------------------------------------------- /generators/app/templates/src/containers/Main/Main.js: -------------------------------------------------------------------------------- 1 | import style from './style.css'; 2 | 3 | import React, { Component, PropTypes } from 'react'; 4 | import { bindActionCreators } from 'redux'; 5 | import { connect } from 'react-redux'; 6 | import * as MainActions from './actions'; 7 | import Name from 'components/Name'; 8 | import Header from './components/Header'; 9 | import Message from './components/Message'; 10 | import cat from 'images/cat.jpg'; 11 | 12 | 13 | function mapStateToProps(state) { 14 | const { main } = state; 15 | return { main }; 16 | } 17 | 18 | function mapDispatchToProps(dispatch) { 19 | return { 20 | mainActions: bindActionCreators(MainActions, dispatch), 21 | }; 22 | } 23 | 24 | @connect(mapStateToProps, mapDispatchToProps) 25 | class Main extends Component { 26 | static propTypes = { 27 | main: PropTypes.object.isRequired, 28 | mainActions: PropTypes.object.isRequired, 29 | }; 30 | 31 | static childContextTypes = { 32 | main: PropTypes.object, 33 | mainActions: PropTypes.object, 34 | }; 35 | 36 | getChildContext() { 37 | const { main, mainActions } = this.props; 38 | return { main, mainActions }; 39 | } 40 | 41 | render() { 42 | const { name, message } = this.props.main.toJS(); 43 | return ( 44 |
45 |
46 |
47 | 48 | 49 | demo 50 |
51 |
52 | ); 53 | } 54 | } 55 | 56 | export default Main; 57 | -------------------------------------------------------------------------------- /generators/container/index.js: -------------------------------------------------------------------------------- 1 | const generators = require('yeoman-generator'); 2 | 3 | 4 | class Container extends generators.Base { 5 | constructor(args, opts) { 6 | super(args, opts); 7 | 8 | this.argument('containerName', { 9 | type: String, 10 | required: true, 11 | description: 'coontainer name' 12 | }); 13 | } 14 | 15 | prompting() { 16 | const prompts = [ 17 | { 18 | type: 'checkbox', 19 | name: 'features', 20 | message: 'What more would you like?', 21 | choices: [ 22 | { 23 | name: 'CSS Modules', 24 | value: 'cssModules', 25 | checked: true, 26 | }, 27 | ], 28 | }, 29 | ]; 30 | 31 | return this.prompt(prompts).then((props) => { 32 | this.props = props; 33 | }); 34 | } 35 | 36 | writing() { 37 | const containerName = this.containerName; 38 | 39 | this.fs.copyTpl( 40 | this.templatePath('container.js'), 41 | this.destinationPath(`${containerName}/${this.containerName}.js`), 42 | { 43 | containerName, 44 | enableCssModules: this.props.features.includes('cssModules'), 45 | } 46 | ); 47 | 48 | this.fs.copyTpl( 49 | this.templatePath('index.js'), 50 | this.destinationPath(`${containerName}/index.js`), 51 | { containerName } 52 | ); 53 | 54 | this.fs.copyTpl( 55 | this.templatePath('route.js'), 56 | this.destinationPath(`${containerName}/route.js`), 57 | { containerName } 58 | ); 59 | 60 | this.fs.copy( 61 | this.templatePath('actions.js'), 62 | this.destinationPath(`${containerName}/actions.js`) 63 | ); 64 | 65 | this.fs.copy( 66 | this.templatePath('reducer.js'), 67 | this.destinationPath(`${containerName}/reducer.js`) 68 | ); 69 | 70 | this.fs.copy( 71 | this.templatePath('style.css'), 72 | this.destinationPath(`${containerName}/style.css`) 73 | ); 74 | 75 | } 76 | } 77 | 78 | module.exports = Container; 79 | -------------------------------------------------------------------------------- /generators/app/templates/webpack/server.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const path = require('path'); 3 | 4 | const baseConfig = require('./base'); 5 | 6 | const config = Object.assign({}, baseConfig, { 7 | target: 'node', 8 | devtool: 'cheap-module-eval-source-map', 9 | entry: ['babel-polyfill', path.join(__dirname, '/../src/server/index')], 10 | output: { 11 | path: path.join(__dirname, '../dist'), 12 | filename: 'server.js', 13 | libraryTarget: 'commonjs2', 14 | }, 15 | externals: [ 16 | function filter(context, request, cb) { 17 | const isExternal = request.match(/^[@a-z][a-z\/\.\-0-9]*$/i) 18 | && !request.match(/^(actions|components|constants|containers|images|reducers|server|sources|store|routes|utils)/) 19 | && !request.match(/^babel-polyfill/) 20 | && !context.match(/[\\/]babel-polyfill/) 21 | && !request.match(/handsontable\.full/) 22 | cb(null, Boolean(isExternal)); 23 | }, 24 | ], 25 | }); 26 | 27 | const srcPath = path.join(__dirname, '/../src'); 28 | config.module.loaders.push({ 29 | test: /\.(js|jsx)$/, 30 | loader: 'babel', 31 | include: srcPath, 32 | }); 33 | 34 | if (process.env.NODE_ENV === 'development') { 35 | config.cache = true; 36 | config.debug = true; 37 | 38 | config.entry.unshift( 39 | 'webpack/hot/poll?1000' 40 | ); 41 | 42 | config.plugins.push(...[ 43 | new webpack.DefinePlugin({ 44 | 'process.env.NODE_ENV': '"development"', 45 | __CLIENT__: false, 46 | __SERVER__: true, 47 | }), 48 | new webpack.HotModuleReplacementPlugin(), 49 | ]); 50 | } else { 51 | config.plugins.push(...[ 52 | new webpack.optimize.DedupePlugin(), 53 | new webpack.DefinePlugin({ 54 | 'process.env.NODE_ENV': '"production"', 55 | __CLIENT__: false, 56 | __SERVER__: true, 57 | }), 58 | new webpack.optimize.UglifyJsPlugin(), 59 | new webpack.optimize.OccurenceOrderPlugin(), 60 | new webpack.optimize.AggressiveMergingPlugin(), 61 | ]); 62 | } 63 | 64 | module.exports = config; 65 | -------------------------------------------------------------------------------- /generators/app/templates/webpack/base.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const path = require('path'); 3 | const ExtractTextPlugin = require('extract-text-webpack-plugin'); 4 | const styleLintPlugin = require('stylelint-webpack-plugin'); 5 | 6 | const srcPath = path.join(__dirname, '/../src'); 7 | const modulesPath = path.join(__dirname, '/../node_modules'); 8 | 9 | const plugins = [ 10 | new webpack.NoErrorsPlugin(), 11 | new webpack.ProvidePlugin({ 12 | 'Promise': 'bluebird', 13 | }), 14 | new ExtractTextPlugin('[name].css', { allChunks: true }), 15 | new styleLintPlugin({ 16 | configFile: path.join(__dirname, '../.stylelintrc'), 17 | files: '../src/**/*.css', 18 | }), 19 | ]; 20 | 21 | <% if (!serverSide) { -%> 22 | if (process.env.NODE_ENV === 'development') { 23 | const DashboardPlugin = require('webpack-dashboard/plugin'); 24 | plugins.push(new DashboardPlugin()); 25 | } 26 | <% } -%> 27 | 28 | module.exports = { 29 | cache: false, 30 | context: __dirname, 31 | debug: false, 32 | quiet: false, 33 | noInfo: false, 34 | stats: { 35 | colors: true, 36 | reasons: true, 37 | hash: true, 38 | version: true, 39 | timings: true, 40 | chunks: true, 41 | chunkModules: true, 42 | cached: true, 43 | cachedAssets: true, 44 | }, 45 | resolve: { 46 | extensions: ['', '.js', '.jsx'], 47 | modulesDirectories: [ 48 | 'src', 49 | 'node_modules', 50 | ], 51 | }, 52 | module: { 53 | preLoaders: [{ 54 | test: /\.(js|jsx)$/, 55 | include: srcPath, 56 | loader: 'eslint-loader', 57 | }], 58 | loaders: [ 59 | { 60 | test: /\.css$/, 61 | loader: ExtractTextPlugin.extract( 62 | 'isomorphic-style-loader', 63 | 'css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!postcss-loader', 64 | { publicPath: '../assets/' } 65 | ), 66 | include: srcPath, 67 | exclude: modulesPath, 68 | }, 69 | { 70 | test: /\.css$/, 71 | loader: ExtractTextPlugin.extract('isomorphic-style-loader', 'css-loader!postcss-loader'), 72 | include: modulesPath, 73 | exclude: srcPath, 74 | }, 75 | { 76 | test: /\.(png|jpg|jpeg|gif|woff|woff2)$/, 77 | loader: 'url-loader?limit=8192', 78 | }, 79 | ], 80 | }, 81 | plugins, 82 | postcss: () => { 83 | return [ 84 | require('autoprefixer')({ 85 | browsers: ['last 2 versions', 'ie >= 8'], 86 | }), 87 | ]; 88 | }, 89 | node: { 90 | process: true, 91 | __dirname: true, 92 | }, 93 | }; 94 | 95 | -------------------------------------------------------------------------------- /generators/app/templates/webpack/client.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const path = require('path'); 3 | 4 | const baseConfig = require('./base'); 5 | 6 | const srcPath = path.join(__dirname, '/../src'); 7 | const config = Object.assign({}, baseConfig, { 8 | target: 'web', 9 | entry: { 10 | app: ['babel-polyfill', path.join(srcPath, '/app')], 11 | }, 12 | output: { 13 | path: path.join(__dirname, '/../dist/assets'), 14 | filename: '[name].js', 15 | chunkFilename: '[name].[id].js', 16 | publicPath: 'assets/', 17 | }, 18 | }); 19 | 20 | 21 | if (process.env.NODE_ENV === 'development') { 22 | const hostname = process.env.HOST || 'localhost'; 23 | const port = process.env.CLIENT_PORT || 8080; 24 | 25 | config.cache = true; 26 | config.debug = true; 27 | config.devtool = 'cheap-module-eval-source-map'; 28 | 29 | Object.keys(config.entry).forEach(k => { 30 | config.entry[k].unshift( 31 | `webpack-dev-server/client?http://${hostname}:${port}`, 32 | 'webpack/hot/only-dev-server' 33 | ); 34 | }); 35 | 36 | config.output.publicPath = `http://${hostname}:${port}/assets/`; 37 | config.output.hotUpdateMainFilename = 'update/[hash]/update.json'; 38 | config.output.hotUpdateChunkFilename = 'update/[hash]/[id].update.js'; 39 | 40 | config.plugins.push(...[ 41 | new webpack.DefinePlugin({ 42 | 'process.env.NODE_ENV': '"development"', 43 | __CLIENT__: true, 44 | __SERVER__: false, 45 | }), 46 | new webpack.HotModuleReplacementPlugin(), 47 | ]); 48 | 49 | config.historyApiFallback = false; 50 | config.devServer = { 51 | port, 52 | host: hostname, 53 | publicPath: config.output.publicPath, 54 | historyApiFallback: true, 55 | hot: true, 56 | inline: false, 57 | lazy: false, 58 | noInfo: false, 59 | headers: { 'Access-Control-Allow-Origin': '*' }, 60 | stats: { colors: true }, 61 | quiet: true, 62 | }; 63 | 64 | config.module.loaders.push({ 65 | test: /\.(js|jsx)$/, 66 | loader: 'react-hot!babel', 67 | include: srcPath, 68 | }); 69 | } else { 70 | config.plugins.push(...[ 71 | new webpack.optimize.DedupePlugin(), 72 | new webpack.DefinePlugin({ 73 | 'process.env.NODE_ENV': '"production"', 74 | __CLIENT__: true, 75 | __SERVER__: false, 76 | }), 77 | new webpack.optimize.UglifyJsPlugin(), 78 | new webpack.optimize.OccurenceOrderPlugin(), 79 | new webpack.optimize.AggressiveMergingPlugin(), 80 | ]); 81 | config.module.loaders.push({ 82 | test: /\.(js|jsx)$/, 83 | loader: 'babel', 84 | include: srcPath, 85 | }); 86 | } 87 | 88 | module.exports = config; 89 | -------------------------------------------------------------------------------- /generators/app/index.js: -------------------------------------------------------------------------------- 1 | const generators = require('yeoman-generator'); 2 | 3 | module.exports = generators.Base.extend({ 4 | constructor: function() { 5 | generators.Base.apply(this, arguments); 6 | }, 7 | 8 | prompting: function() { 9 | var prompts = [ 10 | { 11 | type: 'input', 12 | name: 'projectName', 13 | message: 'Your project name', 14 | default: this.appname, 15 | }, 16 | { 17 | type: 'input', 18 | name: 'projectDesc', 19 | message: 'Your project description', 20 | }, 21 | { 22 | type: 'confirm', 23 | name: 'serverSide', 24 | message: 'Would you like to use the server side?', 25 | }, 26 | ]; 27 | 28 | return this.prompt(prompts).then(function (props) { 29 | this.props = props; 30 | }.bind(this)); 31 | }, 32 | 33 | writing: { 34 | projectTree: function() { 35 | const templateFiles = [this.templatePath() + '/**']; 36 | // files filter 37 | if (this.props.serverSide) { 38 | templateFiles.push('!**/index.html'); 39 | } else { 40 | templateFiles.push('!**/server/**'); 41 | templateFiles.push('!**/server.js'); 42 | templateFiles.push('!**/devServer.js'); 43 | } 44 | 45 | // copy all files 46 | this.fs.copy( 47 | templateFiles, 48 | this.destinationRoot(), 49 | { globOptions: { dot: true } } 50 | ); 51 | 52 | if (!this.props.serverSide) { 53 | this.fs.copyTpl( 54 | this.templatePath('index.html'), 55 | this.destinationPath('index.html'), 56 | { projectName: this.props.projectName } 57 | ); 58 | } 59 | 60 | this.fs.copyTpl( 61 | this.templatePath('src/containers/Main/actions.js'), 62 | this.destinationPath('src/containers/Main/actions.js'), 63 | { 64 | serverSide: this.props.serverSide, 65 | } 66 | ); 67 | 68 | this.fs.copyTpl( 69 | this.templatePath('src/components/Name/Name.js'), 70 | this.destinationPath('src/components/Name/Name.js'), 71 | { 72 | serverSide: this.props.serverSide, 73 | } 74 | ); 75 | 76 | this.fs.copyTpl( 77 | this.templatePath('webpack/base.js'), 78 | this.destinationPath('webpack/base.js'), 79 | { 80 | serverSide: this.props.serverSide, 81 | } 82 | ); 83 | }, 84 | 85 | packageJSON: function() { 86 | this.fs.copyTpl( 87 | this.templatePath('package.json'), 88 | this.destinationPath('package.json'), 89 | { 90 | projectName: this.props.projectName, 91 | projectDesc: this.props.projectDesc, 92 | serverSide: this.props.serverSide, 93 | } 94 | ); 95 | }, 96 | 97 | serverNode: function() { 98 | if (this.props.serverSide) { 99 | this.fs.copyTpl( 100 | this.templatePath('src/server/handlers/main.js'), 101 | this.destinationPath('src/server/handlers/main.js'), 102 | { projectName: this.props.projectName } 103 | ); 104 | } 105 | }, 106 | }, 107 | }); 108 | -------------------------------------------------------------------------------- /generators/app/templates/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "<%= projectName %>", 3 | "version": "1.0.0", 4 | "description": "<%= projectDesc %>", 5 | "main": "", 6 | "scripts": { 7 | "clean": "rm -rf dist/*", 8 | "check": "eslint src test && stylelint 'src/**/*.css'", 9 | "test": "NODE_PATH=$NODE_PATH:$PWD/src mocha --compilers js:babel-core/register,js:babel-polyfill,css:test/css-null-compiler.js --recursive", 10 | "webpack:client-prod": "NODE_ENV=production webpack --config webpack/client.js --progress ", 11 | <% if (serverSide) { -%> 12 | "webpack:server-prod": "NODE_ENV=production webpack --config webpack/server.js --progress && cp -r node_modules dist/", 13 | "webpack:server-dev": "NODE_ENV=development webpack --config webpack/server.js --progress", 14 | "dist": "npm run clean && npm run webpack:client-prod && npm run webpack:server-prod", 15 | "start": "NODE_ENV=development webpack-dashboard -- nodemon --watch src/server --exec 'npm run clean && npm run webpack:server-dev && node devServer.js'", 16 | "start:dist": "npm run dist && cd dist && node server.js", 17 | <% } else { -%> 18 | "dist": "npm run clean && npm run webpack:client-prod", 19 | "start": "CLIENT_PORT=8000 NODE_ENV=development webpack-dashboard -- webpack-dev-server --inline --config webpack/client.js", 20 | "start:dist": "npm run dist && cp index.html dist/ && cd dist && open index.html", 21 | <% } -%> 22 | "precommit": "npm run check && npm test", 23 | "commitmsg": "node validate-commit-msg.js" 24 | }, 25 | "dependencies": { 26 | "antd": "2.0.1", 27 | "bluebird": "3.4.6", 28 | "classnames": "2.2.5", 29 | <% if (serverSide) { -%> 30 | "h2o2": "5.4.0", 31 | "hapi": "15.1.1", 32 | "inert": "4.0.2", 33 | <% } -%> 34 | "immutable": "3.8.1", 35 | "isomorphic-style-loader": "1.0.0", 36 | "normalize.css": "5.0.0", 37 | "react": "15.3.2", 38 | "react-dom": "15.3.2", 39 | "react-redux": "4.4.5", 40 | "react-router": "2.8.1", 41 | "react-router-redux": "4.0.6", 42 | "redux": "3.6.0", 43 | "redux-thunk": "2.1.0" 44 | }, 45 | "devDependencies": { 46 | "autoprefixer": "6.5.1", 47 | "babel-core": "6.17.0", 48 | "babel-eslint": "7.0.0", 49 | "babel-loader": "6.2.5", 50 | "babel-plugin-import": "1.0.1", 51 | "babel-plugin-transform-decorators-legacy": "1.3.4", 52 | "babel-plugin-transform-runtime": "6.15.0", 53 | "babel-polyfill": "6.16.0", 54 | "babel-preset-latest": "6.16.0", 55 | "babel-preset-react": "6.16.0", 56 | "babel-preset-stage-0": "6.16.0", 57 | "babel-runtime": "6.11.6", 58 | "chai": "3.5.0", 59 | "css-loader": "0.25.0", 60 | "enzyme": "2.4.1", 61 | "eslint": "3.8.0", 62 | "eslint-config-airbnb": "12.0.0", 63 | "eslint-import-resolver-webpack": "0.6.0", 64 | "eslint-loader": "1.5.0", 65 | "eslint-plugin-import": "2.0.1", 66 | "eslint-plugin-jsx-a11y": "2.2.3", 67 | "eslint-plugin-react": "6.4.1", 68 | "extract-text-webpack-plugin": "1.0.1", 69 | "file-loader": "0.9.0", 70 | "findup": "0.1.5", 71 | "husky": "0.11.9", 72 | "jsdom": "9.7.1", 73 | "mocha": "2.5.3", 74 | "nodemon": "1.11.0", 75 | "postcss": "5.2.4", 76 | "postcss-loader": "0.13.0", 77 | "react-addons-test-utils": "15.3.2", 78 | "react-hot-loader": "1.3.0", 79 | "semver-regex": "1.0.0", 80 | "sinon": "1.17.6", 81 | "style-loader": "0.13.1", 82 | "stylelint": "7.4.2", 83 | "stylelint-webpack-plugin": "0.4.0", 84 | "url-loader": "0.5.7", 85 | "webpack": "1.13.2", 86 | "webpack-dashboard": "0.2.0", 87 | "webpack-dev-server": "1.16.2", 88 | "whatwg-fetch": "1.0.0" 89 | }, 90 | "engines": { 91 | "node": "6.6.0", 92 | "npm": "3.10.3" 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /generators/app/templates/.stylelintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "at-rule-empty-line-before": [ 4 | "always", 5 | { 6 | "except": [ 7 | "blockless-group", 8 | "first-nested" 9 | ], 10 | "ignore": [ 11 | "after-comment" 12 | ] 13 | } 14 | ], 15 | "at-rule-name-case": "lower", 16 | "at-rule-name-space-after": "always-single-line", 17 | "at-rule-semicolon-newline-after": "always", 18 | "block-closing-brace-newline-after": "always", 19 | "block-closing-brace-newline-before": "always-multi-line", 20 | "block-closing-brace-space-before": "always-single-line", 21 | "block-no-empty": true, 22 | "block-opening-brace-newline-after": "always-multi-line", 23 | "block-opening-brace-space-after": "always-single-line", 24 | "block-opening-brace-space-before": "always", 25 | "color-hex-case": "lower", 26 | "color-hex-length": "short", 27 | "color-no-invalid-hex": true, 28 | "comment-empty-line-before": [ 29 | "always", 30 | { 31 | "except": [ 32 | "first-nested" 33 | ], 34 | "ignore": [ 35 | "stylelint-commands" 36 | ] 37 | } 38 | ], 39 | "comment-whitespace-inside": "always", 40 | "declaration-bang-space-after": "never", 41 | "declaration-bang-space-before": "always", 42 | "declaration-block-no-ignored-properties": true, 43 | "declaration-block-no-shorthand-property-overrides": true, 44 | "declaration-block-semicolon-newline-after": "always-multi-line", 45 | "declaration-block-semicolon-space-after": "always-single-line", 46 | "declaration-block-semicolon-space-before": "never", 47 | "declaration-block-single-line-max-declarations": 1, 48 | "declaration-block-trailing-semicolon": "always", 49 | "declaration-colon-newline-after": "always-multi-line", 50 | "declaration-colon-space-after": "always-single-line", 51 | "declaration-colon-space-before": "never", 52 | "function-calc-no-unspaced-operator": true, 53 | "function-comma-newline-after": "always-multi-line", 54 | "function-comma-space-after": "always-single-line", 55 | "function-comma-space-before": "never", 56 | "function-linear-gradient-no-nonstandard-direction": true, 57 | "function-max-empty-lines": 0, 58 | "function-name-case": "lower", 59 | "function-parentheses-newline-inside": "always-multi-line", 60 | "function-parentheses-space-inside": "never-single-line", 61 | "function-whitespace-after": "always", 62 | "indentation": 2, 63 | "keyframe-declaration-no-important": true, 64 | "length-zero-no-unit": true, 65 | "max-empty-lines": 1, 66 | "media-feature-colon-space-after": "always", 67 | "media-feature-colon-space-before": "never", 68 | "media-feature-no-missing-punctuation": true, 69 | "media-feature-range-operator-space-after": "always", 70 | "media-feature-range-operator-space-before": "always", 71 | "media-query-list-comma-newline-after": "always-multi-line", 72 | "media-query-list-comma-space-after": "always-single-line", 73 | "media-query-list-comma-space-before": "never", 74 | "media-feature-parentheses-space-inside": "never", 75 | "no-eol-whitespace": true, 76 | "no-extra-semicolons": true, 77 | "no-invalid-double-slash-comments": true, 78 | "no-missing-end-of-source-newline": true, 79 | "number-leading-zero": "always", 80 | "number-no-trailing-zeros": true, 81 | "property-case": "lower", 82 | "rule-non-nested-empty-line-before": [ 83 | "always-multi-line", 84 | { 85 | "ignore": [ 86 | "after-comment" 87 | ] 88 | } 89 | ], 90 | "selector-attribute-brackets-space-inside": "never", 91 | "selector-attribute-operator-space-after": "never", 92 | "selector-attribute-operator-space-before": "never", 93 | "selector-combinator-space-after": "always", 94 | "selector-combinator-space-before": "always", 95 | "selector-list-comma-newline-after": "always", 96 | "selector-list-comma-space-before": "never", 97 | "selector-max-empty-lines": 0, 98 | "selector-pseudo-class-case": "lower", 99 | "selector-pseudo-class-parentheses-space-inside": "never", 100 | "selector-pseudo-element-case": "lower", 101 | "selector-pseudo-element-colon-notation": "single", 102 | "selector-pseudo-element-no-unknown": true, 103 | "selector-type-case": "lower", 104 | "selector-type-no-unknown": true, 105 | "shorthand-property-no-redundant-values": true, 106 | "string-no-newline": true, 107 | "unit-case": "lower", 108 | "unit-no-unknown": true, 109 | "value-list-comma-newline-after": "always-multi-line", 110 | "value-list-comma-space-after": "always-single-line", 111 | "value-list-comma-space-before": "never" 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /generators/app/templates/validate-commit-msg.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Git COMMIT-MSG hook for validating commit message 5 | * See https://docs.google.com/document/d/1rk04jEuGfk9kYzfqCuOlPTSJw3hEDZJTBN5E5f1SALo/edit 6 | * 7 | * Installation: 8 | * >> cd 9 | * >> ln -s ../../validate-commit-msg.js .git/hooks/commit-msg 10 | */ 11 | 12 | 'use strict'; 13 | 14 | var fs = require('fs'); 15 | var util = require('util'); 16 | var resolve = require('path').resolve; 17 | var findup = require('findup'); 18 | var semverRegex = require('semver-regex') 19 | 20 | var config = getConfig(); 21 | var MAX_LENGTH = config.maxSubjectLength || 100; 22 | var IGNORED = new RegExp(util.format('(^WIP)|(^%s$)', semverRegex().source)); 23 | var TYPES = config.types || ['feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'chore', 'revert']; 24 | 25 | // fixup! and squash! are part of Git, commits tagged with them are not intended to be merged, cf. https://git-scm.com/docs/git-commit 26 | var PATTERN = /^((fixup! |squash! )?(\w+)(?:\(([^\)\s]+)\))?: (.+))(?:\n|$)/; 27 | var MERGE_COMMIT_PATTERN = /^Merge /; 28 | var error = function() { 29 | // gitx does not display it 30 | // http://gitx.lighthouseapp.com/projects/17830/tickets/294-feature-display-hook-error-message-when-hook-fails 31 | // https://groups.google.com/group/gitx/browse_thread/thread/a03bcab60844b812 32 | console[config.warnOnFail ? 'warn' : 'error']('INVALID COMMIT MSG: ' + util.format.apply(null, arguments)); 33 | }; 34 | 35 | 36 | var validateMessage = function(raw) { 37 | var messageWithBody = (raw || '').split('\n').filter(function (str) { 38 | return str.indexOf('#') !== 0; 39 | }).join('\n'); 40 | 41 | var message = messageWithBody.split('\n').shift(); 42 | 43 | if (message === '') { 44 | console.log('Aborting commit due to empty commit message.'); 45 | return false; 46 | } 47 | 48 | var isValid = true; 49 | 50 | if(MERGE_COMMIT_PATTERN.test(message)){ 51 | console.log('Merge commit detected.'); 52 | return true 53 | } 54 | 55 | if (IGNORED.test(message)) { 56 | console.log('Commit message validation ignored.'); 57 | return true; 58 | } 59 | 60 | var match = PATTERN.exec(message); 61 | 62 | if (!match) { 63 | error('does not match "(): " !'); 64 | isValid = false; 65 | } else { 66 | var firstLine = match[1]; 67 | var squashing = !!match[2]; 68 | var type = match[3]; 69 | var scope = match[4]; 70 | var subject = match[5]; 71 | 72 | var SUBJECT_PATTERN = new RegExp(config.subjectPattern || '.+'); 73 | var SUBJECT_PATTERN_ERROR_MSG = config.subjectPatternErrorMsg || 'subject does not match subject pattern!'; 74 | 75 | if (firstLine.length > MAX_LENGTH && !squashing) { 76 | error('is longer than %d characters !', MAX_LENGTH); 77 | isValid = false; 78 | } 79 | 80 | if (TYPES !== '*' && TYPES.indexOf(type) === -1) { 81 | error('"%s" is not allowed type !', type); 82 | isValid = false; 83 | } 84 | 85 | if (!SUBJECT_PATTERN.exec(subject)) { 86 | error(SUBJECT_PATTERN_ERROR_MSG); 87 | isValid = false; 88 | } 89 | } 90 | 91 | // Some more ideas, do want anything like this ? 92 | // - Validate the rest of the message (body, footer, BREAKING CHANGE annotations) 93 | // - allow only specific scopes (eg. fix(docs) should not be allowed ? 94 | // - auto correct the type to lower case ? 95 | // - auto correct first letter of the subject to lower case ? 96 | // - auto add empty line after subject ? 97 | // - auto remove empty () ? 98 | // - auto correct typos in type ? 99 | // - store incorrect messages, so that we can learn 100 | 101 | isValid = isValid || config.warnOnFail; 102 | 103 | if (isValid) { // exit early and skip messaging logics 104 | return true; 105 | } 106 | 107 | var argInHelp = config.helpMessage && config.helpMessage.indexOf('%s') !== -1; 108 | 109 | if (argInHelp) { 110 | console.log(config.helpMessage, messageWithBody); 111 | } else if (message) { 112 | console.log(message); 113 | } 114 | 115 | if (!argInHelp && config.helpMessage) { 116 | console.log(config.helpMessage); 117 | } 118 | 119 | return false; 120 | }; 121 | 122 | 123 | // publish for testing 124 | exports.validateMessage = validateMessage; 125 | exports.getGitFolder = getGitFolder; 126 | exports.config = config; 127 | 128 | // hacky start if not run by mocha :-D 129 | // istanbul ignore next 130 | if (process.argv.join('').indexOf('mocha') === -1) { 131 | 132 | var commitMsgFile = process.argv[2] || getGitFolder() + '/COMMIT_EDITMSG'; 133 | var incorrectLogFile = commitMsgFile.replace('COMMIT_EDITMSG', 'logs/incorrect-commit-msgs'); 134 | 135 | var hasToString = function hasToString(x) { 136 | return x && typeof x.toString === 'function'; 137 | }; 138 | 139 | fs.readFile(commitMsgFile, function(err, buffer) { 140 | var msg = getCommitMessage(buffer); 141 | 142 | if (!validateMessage(msg)) { 143 | fs.appendFile(incorrectLogFile, msg + '\n', function() { 144 | process.exit(1); 145 | }); 146 | } else { 147 | process.exit(0); 148 | } 149 | 150 | function getCommitMessage(buffer) { 151 | return hasToString(buffer) && buffer.toString(); 152 | } 153 | }); 154 | } 155 | 156 | function getConfig() { 157 | var pkgFile = findup.sync(process.cwd(), 'package.json'); 158 | var pkg = JSON.parse(fs.readFileSync(resolve(pkgFile, 'package.json'))); 159 | return pkg && pkg.config && pkg.config['validate-commit-msg'] || {}; 160 | } 161 | 162 | function getGitFolder() 163 | { 164 | var gitDirLocation = './.git'; 165 | if (!fs.existsSync(gitDirLocation)) { 166 | throw new Error('Cannot find file ' + gitDirLocation); 167 | } 168 | 169 | if(!fs.lstatSync(gitDirLocation).isDirectory()) { 170 | var unparsedText = '' + fs.readFileSync(gitDirLocation); 171 | gitDirLocation = unparsedText.substring('gitdir: '.length).trim(); 172 | } 173 | 174 | if (!fs.existsSync(gitDirLocation)) { 175 | throw new Error('Cannot find file ' + gitDirLocation); 176 | } 177 | 178 | return gitDirLocation; 179 | } 180 | 181 | -------------------------------------------------------------------------------- /docs/user_guide_zh.md: -------------------------------------------------------------------------------- 1 | 当你使用generator创建项目之后,就可以开始开发你的Web应用了,下面是本项目框架的一个使用指南,可以指导你如何使用框架开发新功能。 2 | 3 | ## 目录 4 | 5 | * [项目结构](#项目结构) 6 | * [操作命令](#操作命令) 7 | * [npm start](#npm-start) 8 | * [npm run start:dist](#npm-run-startdist) 9 | * [npm test](#npm-test) 10 | * [npm run check](#npm-run-check) 11 | * [npm run dist](#npm-run-dist) 12 | * [静态代码检查](#静态代码检查) 13 | * [JS代码检查](#JS代码检查) 14 | * [CSS代码检查](#CSS代码检查) 15 | * [提交信息校验](#提交信息校验) 16 | * [开发一个小功能](#开发一个小功能) 17 | * [编写组件](#编写组件) 18 | * [编写action](#编写action) 19 | * [编写reducer](#编写reducer) 20 | * [编写路由](#编写路由) 21 | * [安装依赖](#安装依赖) 22 | * [导入组件](#导入组件) 23 | * [添加样式](#添加样式) 24 | * [CSS预处理](#CSS预处理) 25 | * [添加图片和字体](#添加图片和字体) 26 | * [使用Ant Design](#使用ant-design) 27 | * [发送请求](#发送请求) 28 | * [集成服务端](#集成服务端) 29 | * [编写测试](#编写测试) 30 | 31 | 32 | ## 项目结构 33 | 34 | ``` 35 | ├── .babelrc 36 | ├── .eslintrc 37 | ├── package.json 38 | ├── devServer.js 39 | ├── docs 40 | │   └── README.md 41 | ├── src 42 | │   ├── app.js 43 | │   ├── components 44 | │   │   └── Name 45 | │   │   ├── Name.js 46 | │   │   ├── index.js 47 | │   │   └── style.css 48 | │   ├── constants 49 | │   │   └── actionTypes.js 50 | │   ├── containers 51 | │   │   └── Main 52 | │   │   ├── Main.js 53 | │   │   ├── actions.js 54 | │   │   ├── components 55 | │   │   │   └── Message 56 | │   │   │   ├── Message.js 57 | │   │   │   ├── index.js 58 | │   │   │   └── style.css 59 | │   │   ├── index.js 60 | │   │   ├── reducer.js 61 | │   │   └── style.css 62 | │   ├── reducers.js 63 | │   ├── routes.js 64 | │   ├── server 65 | │   │   ├── README.md 66 | │   │   └── index.js 67 | │   ├── store.js 68 | │   ├── styles 69 | │   │   └── App.css 70 | │   └── utils 71 | │   └── README.md 72 | ├── test 73 | │   ├── containers 74 | │   │   └── Main 75 | │   │   ├── components 76 | │   │   │   └── Message 77 | │   │   │   └── Message.test.js 78 | │   │   └── reducer.test.js 79 | │   ├── css-null-compiler.js 80 | │   ├── dom.js 81 | │   └── macha.opts 82 | ├── validate-commit-msg.js 83 | └── webpack 84 | ├── base.js 85 | ├── client.js 86 | └── server.js 87 | ``` 88 | 89 | * docs: 放置项目文档 90 | * src: 源文件目录 91 | * components: 放置公共组件 92 | * constants: 放置常量文件 93 | * containers: 这是放置容器型组件的地方,容器型组件包含了自己的action,reducer和router,里面还可以有自己的子组件 94 | * test: 测试文件目录,放置测试用例 95 | * webpack: Webpack配置文件的目录,客户端和服务端的配置是分不同文件配置的 96 | 97 | ## 操作命令 98 | 99 | 在项目中,你可以运行如下命令: 100 | 101 | ### `npm start` 102 | 103 | 在开发环境运行项目,启动成功后,在浏览器打开`http://localhost:8000`可以访问。 104 | 105 | 当你修改项目中的文件并保存后,应用进程会重新加载,如果有错误会在终端显示。 106 | 107 | ### `npm run start:dist` 108 | 109 | 在生产环境运行打包后的项目,整体性能比开发环境更快。 110 | 111 | ### `npm test` 112 | 113 | 运行项目中的测试,你可以查看[编写测试](#编写测试)章节了解如何编写测试用例。 114 | 115 | ### `npm run check` 116 | 117 | 运行代码检查,发现你的代码是否有错误,检查内容包括JS和CSS文件,你可以查看[静态代码检查](#静态代码检查)章节了解更多内容。 118 | 119 | ### `npm run dist` 120 | 121 | 运行打包程序,将项目打包为静态资源文件。 122 | 123 | ## 静态代码检查 124 | 125 | 项目中集成了一些JS和CSS编码规范,这些规范是业界推崇比较好的规范,这样可以统一开发团队的编码规范,避免杂乱无章的代码。 126 | 127 | 如果你的代码不符合这些规范,在终端会提示你哪些文件有哪些错误,如下图所示: 128 | 129 | ![](images/eslint_error.png) 130 | 131 | 有两种方式可以帮助你查看到代码规范错误: 132 | 133 | * 你可以运行`npm run check`来检查你的代码是否包含错误 134 | * 项目通过`npm start`启动后,如果代码有错误,会在终端显示错误信息 135 | 136 | ### JS代码检查 137 | 138 | 项目中使用的JS检查工具是[eslint](http://eslint.org/),JS的编码规范引用了[airbnb公司的JS代码规范](https://github.com/airbnb/javascript),这些规则是基于ES6的,如果你不了解ES6,请先了解一下[ES6的语法](http://es6.ruanyifeng.com/),这样可以避免一些常见的错误。 139 | 140 | 如果对提示的错误信息不了解,可以通过Google搜索`eslint 规则名称`来查询eslint规则的详细信息,以上面的图为例,错误信息的每一行后面都有规则名称,所以搜索的关键字为`eslint no-useless-escape`,在对应的页面中通常都会教你如何修改这个错误。 141 | 142 | ### CSS代码检查 143 | 144 | 项目中使用[stylelint](https://github.com/stylelint/stylelint)来做css代码的规范检查,stylelint的规则文件是项目根目录下的`.stylelintrc`文件,你可以修改里面的规则来让其更适合你的开发团队。 145 | 146 | 同样的,如果你对提示的错误信息不了解,也可以通过Google搜索`stylelint 规则名`来了解规则的详细信息,和eslint一样,每一行错误信息的最后会显示规则名称: 147 | 148 | ![](images/stylelint_error.png) 149 | 150 | ## 提交信息校验 151 | 152 | 项目中集成git提交信息的校验,我们希望能从代码的提交记录中知道修改的主要内容,比如是修改哪个模块的代码,是新功能的添加,还是修复缺陷,或者是添加测试等。 153 | 154 | 提交信息的格式为: `(): `,其中type和subject是必须的,scope是可选的。 155 | 156 | type主要有以下几种类型: 157 | 158 | * feat:新功能(feature) 159 | * fix:修补bug 160 | * docs:文档(documentation) 161 | * style: 格式(不影响代码运行的变动) 162 | * refactor:重构(即不是新增功能,也不是修改bug的代码变动) 163 | * test:增加测试 164 | * chore:构建过程或辅助工具的变动 165 | 166 | scope用于说明 commit 影响的范围,比如数据层、控制层、视图层等等,视项目不同而不同。 167 | 168 | subject是 commit 目的的简短描述,不超过50个字符。 169 | 170 | 更详细的参考资料可以看[这里](http://www.ruanyifeng.com/blog/2016/01/commit_message_change_log.html)。 171 | 172 | ## 开发一个小功能 173 | 174 | 假设我们需要开发小功能——添加待办事项,下面分别以开发React组件,Redux action方法和reducer方法为例,介绍一下如何开发这个功能。 175 | 176 | ## 编写组件 177 | 178 | 项目使用[React](https://facebook.github.io/react/)来做前端页面渲染,React是最近比较流行的一个页面渲染框架,通过React可以很方便进行页面组件化开发,并最大化的复用页面组件,避免重复代码,提高代码可维护性。 179 | 180 | 这是一个添加待办事项的的React组件: 181 | 182 | ```js 183 | import React, { Component, PropTypes } from 'react'; 184 | 185 | class TodoInput extends Component { 186 | static propTypes = { 187 | actions: PropTypes.object.isRequired, 188 | }; 189 | 190 | constructor(props, context) { 191 | super(props, context); 192 | this.state = { 193 | value: '', 194 | }; 195 | this.handleChange = this.handleChange.bind(this); 196 | } 197 | 198 | handleChange(event) { 199 | const text = event.target.value; 200 | this.setState({ value: text}); 201 | this.props.actions.addTodo(text); 202 | } 203 | 204 | render() { 205 | return ( 206 |
207 | 208 |
209 | ); 210 | } 211 | } 212 | 213 | export default TodoInput; 214 | ``` 215 | 216 | 每个组件使用ES6的class来封装,继承react的`Component`对象,`render`方法是每个组件必须存在的方法,渲染的页面写在这里面。 217 | 218 | 在组件的开头部分是props对象的校验,`actions: PropTypes.object.isRequired`是指actions这个属性是对象(`object`)类型,而且是必须的(`isRequired`),如果在引用这个组件时没有传递actions属性,编译将会报错,如果传给actions的值不是object类型的,编译会给一个警告。 219 | 220 | `constructor`是类的构造器,里面定义了组件的state这个对象各个属性的初始值,并将`handleChange`方法绑定到了这个组件对象里面来。**请注意,并不是所有方法都需要`bind(this)`,如果方法里面用到了`this`这个关键字才需要绑定。** 221 | 222 | `handleChange`方法可以改变组件的内部数据,通过调用`setState`方法来改变,传入的参数是一个包含了state属性的对象。请注意,不能使用`this.state.value = 'foo'`这种方式给state属性赋值,一般是通过`setState`方法来改变state数据。 223 | 224 | 最后是`render`方法,一般是返回页面元素,可以通过JS语法根据state或props对象的值来生成动态的页面。请注意,`render`方法返回的对象必须只有一个根元素,比如是`
`,多于一个都会编译报错,比如这种是错误的:`
`。 225 | 226 | 更多React的详细信息可以参考以下资料: 227 | 228 | * [React官网](https://facebook.github.io/react/) 229 | * [React Pattern](http://reactpatterns.com/) 230 | 231 | ## 编写action 232 | 233 | 项目使用了[Redux](https://github.com/reactjs/redux)来做组件的数据状态管理,action是应用发送数据到Redux仓库的一个信息载体,它们是仓库的原始信息。 234 | 235 | 前面的`TodoInput`组件调用了`actions.addTodo()`方法,传入了输入框中的值,下面是这个action示例: 236 | 237 | ### actionTypes.js 238 | 239 | ```js 240 | export const ADD_TODO = 'ADD_TODO'; 241 | ``` 242 | 243 | ### action.js 244 | 245 | ```js 246 | import { ADD_TODO } from '../actionTypes'; 247 | 248 | function addTodo(text) { 249 | return { 250 | type: ADD_TODO, 251 | text, 252 | }; 253 | } 254 | ``` 255 | 256 | 首先我们定义一个常量来表示action的类型,一般这些常量会有单独一个文件(比如`actionTypes.js`)来存放。 257 | 258 | 然后定义一个方法返回一个纯JS对象,这个对象必须包含`type`属性,其它属性都是可选的。type属性的类型是字符串,这个type会和reducers里面的type对应起来,从而建立两者关联。 259 | 260 | 通常建议action返回的对象只包含一些必要的数据,并且能小尽量的小,不要包含太多属性,也不要在某个属性里面赋值一个很大的对象。 261 | 262 | ## 编写reducer 263 | 264 | 如果action表示某些事情已经发生了,那么reducer就表示如何响应这些事情,如何根据action返回的数据更新Redux仓库的内容。 265 | 266 | 在Redux中,应用的所有数据都存放在一个单独的对象里面,在写Redux代码前最好把这个概念深印在你的脑海里。 267 | 268 | reducer是一个纯函数,接收前状态和action为参数,返回一个新的状态。 269 | 270 | ```js 271 | (previousState, action) => newState 272 | ``` 273 | 274 | 在reducer方法里面建议不要做以下事情: 275 | 276 | * 修改方法参数 277 | * 执行性能消耗大的操作,比如调用后端API或者转换路由 278 | * 调用一些不纯的函数,比如`Date.now()`或`Math.random()` 279 | 280 | 下面是一个reducer的例子: 281 | 282 | ```js 283 | import { ADD_TODO } from '../actionTypes'; 284 | import immutable from 'immutable'; 285 | 286 | const initialState = immutable.fromJS({ 287 | todos: [], 288 | }); 289 | 290 | function todoApp(state = initialState, action) { 291 | switch (action.type) { 292 | case ADD_TODO: 293 | return state.get('todos', (todos) => todos.push({ 294 | text: action.text, 295 | completed: false, 296 | })); 297 | default: 298 | return state; 299 | } 300 | } 301 | ``` 302 | 303 | 首先我们引用了[`immutable`](http://facebook.github.io/immutable-js/docs)这个第三方工具库,它的主要作用是不改变原来的对象,而是在每次操作后返回一个新的对象。为什么要使用`immutable`呢?这是因为Redux仓库里面的数据都是不可变的,如果需要修改里面的数据,需要返回一个全新的对象来覆盖原来的对象,所以`immutable`非常合适在Redux项目中使用。更多`immutable`的用法可以参考其官网的文档。 304 | 305 | 然后我们构造了一个状态对象的初始值,这个是reducer方法第一次被调用时会使用的初始值,在reducer方法参数里面被这样用到`state = initialState`,如果传入的state参数为空,则使用`initialState`来为参数`state`赋值。 306 | 307 | 在reducer主体方法里面,我们根据action的类型来判断该走哪种处理逻辑,比如类型如果是`ADD_TODO`的话就在原来的`todos`集合里面添加一个新的todo对象,如果所有action类型都不匹配,就原封不动地返回原来的state对象。 308 | 309 | 更多Redux的详细信息可以参考以下资料: 310 | 311 | * [Redux 文档](http://redux.js.org/) 312 | * [Redux 视频](https://egghead.io/series/getting-started-with-redux) 313 | 314 | ## 编写路由 315 | 316 | 项目使用了[`react-router`](https://github.com/ReactTraining/react-router)来控制前端页面路由。 317 | 318 | 现在项目中的示例代码只配置一个根路由`/`,这个路由指向了`Main`组件,代码示例如下: 319 | 320 | ### routes.js(before) 321 | 322 | ```js 323 | import Main from 'containers/Main'; 324 | 325 | export function createRoutes() { 326 | return { 327 | path: '/', 328 | component: Main, 329 | }; 330 | } 331 | ``` 332 | 333 | 如果要配置更多的路由,首先需要修改`Main`组件,将在其render方法中添加`this.props.children`,表示组件会展示其子路由组件的内容。然后在`routes.js`里面添加子路由,代码示例如下: 334 | 335 | ### Main.js 336 | 337 | ```js 338 | class Main extends Component { 339 | render() { 340 | return ( 341 |
342 | {this.props.children} 343 |
344 | ); 345 | } 346 | } 347 | 348 | ``` 349 | 350 | ### routes.js(after) 351 | 352 | ```js 353 | export function createRoutes() { 354 | return { 355 | path: '/', 356 | component: Main, 357 | childRoutes: [ 358 | { path: 'about', component: About }, // 访问about可以看到About组件 359 | { path: 'inbox', component: Inbox }, // 访问inbox可以看到Inbox组件 360 | ], 361 | }; 362 | } 363 | ``` 364 | 365 | 更多react-router的详细信息可以参考以下资料: 366 | 367 | * [react-router](https://github.com/ReactTraining/react-router) 368 | * [react-router中文文档](https://react-guide.github.io/react-router-cn) 369 | 370 | ## 安装依赖 371 | 372 | 项目中已经包含了React,ReactDOM和Redux等基础依赖库,如果你想安装其他的依赖库,请通过yarn(这是最新的JS包管理工具,不了解的可以在[yarn官网](https://yarnpkg.com)查看更多详细信息)进行安装,命令如下: 373 | 374 | ``` 375 | yarn add [--dev] 376 | ``` 377 | 378 | 如果你想安装在devDependencies下就加上`--dev`参数。 379 | 380 | ## 导入组件 381 | 382 | 项目使用[Babel](https://babeljs.io/)编译ES6语法,当然你还是可以使用`require()`和`module.exports`来导入和导出你的组件,但我们还是推荐你使用`import`和`export`。 383 | 384 | 举个例子: 385 | 386 | ### `Button.js` 387 | 388 | ```js 389 | import React, { Component } from 'react'; 390 | 391 | class Button extends Component { 392 | render() { 393 | // ... 394 | } 395 | } 396 | 397 | export default Button; 398 | ``` 399 | 400 | ### `DangerButton.js` 401 | 402 | 403 | ```js 404 | import React, { Component } from 'react'; 405 | import Button from './Button'; // 导入另外Button.js文件里面的组件 406 | 407 | class DangerButton extends Component { 408 | render() { 409 | return ; 538 | } 539 | } 540 | 541 | export default YourButton; 542 | ``` 543 | 544 | 这只是一个简单的示例,在antd中有更多复杂的组件,如果要使用请仔细阅读该组件的说明文档,确定每个属性或者方法是使用正确的,如果在使用的过程中发现antd组件有什么问题,可以在其[issues区](https://github.com/ant-design/ant-design/issues)提问题,一般都能很快得到答复。 545 | 546 | 项目默认使用的antd版本是v2.0.1,查阅相关文档时请注意antd的版本是否正确。 547 | 548 | ## 发送请求 549 | 550 | 项目中使用`fetch`方法来发送http请求从服务器获取数据,一般我们建议在action方法里面来做发送请求的操作。 551 | 552 | 在项目中可以使用ES7的一些实验性语法`async\await`,使用它们可以避免写回调方法,使得我们的代码更加直观易懂。 553 | 554 | 举个例子: 555 | 556 | ```js 557 | export async function randomName(num) { 558 | const response = await fetch('/api/name/random', { 559 | method: 'post', 560 | body: JSON.stringify({ 561 | num, 562 | }), 563 | }); 564 | const result = await response.json(); 565 | return { 566 | type: at.CHANGE_NAME, 567 | name: result.name , 568 | }; 569 | } 570 | ``` 571 | 572 | 我们通过`fetch`请求后台服务器的api获取到json数据,这里分成2个部分,首先获取response,然后再从response中获取json数据,最后将结果封装在action对象中返回。 573 | 574 | 更多参考资料: 575 | 576 | * [这个API很迷人](http://www.w3ctech.com/topic/854) 577 | * [async 函数的含义和用法](http://www.ruanyifeng.com/blog/2015/05/async.html) 578 | 579 | ## 集成服务端 580 | 581 | 使用generator创建项目可以选择是否需要服务端,如果选择需要,项目框架中会添加服务端的代码。 582 | 583 | 项目服务端使用[`hapi.js`](http://hapijs.com/)作为后端服务器的框架,使用该框架可以很方便地编写后端API。 584 | 585 | 举个例子: 586 | 587 | ### index.js 588 | 589 | ```js 590 | import name from './handlers/name'; 591 | 592 | const server = new Hapi.Server(); 593 | server.route([name]); 594 | ``` 595 | 596 | ### name.js 597 | 598 | ```js 599 | export default { 600 | method: ['POST'], 601 | path: '/api/name/random', 602 | 603 | config: { 604 | handler(request, reply) { 605 | const { num } = JSON.parse(request.payload); 606 | const randomNames = []; 607 | for (let i = 0; i < num; i++) { 608 | randomNames.push(getRandomName()); 609 | } 610 | return reply({ 611 | name: randomNames.join(' '), 612 | }); 613 | }, 614 | }, 615 | }; 616 | ``` 617 | 618 | 首先在hapi的服务`server`中通过`route`方法添加`name`这个路由,这个路由其实是一个JS对象,里面有以下基本属性: 619 | 620 | * method: 定义请求的方法,比如`GET`,`PUST`等 621 | * path: 定义路由的url路径 622 | * config: 具体处理逻辑,通过`request`参数可以获取请求的参数,然后通过`reply`参数可以返回response结果 623 | 624 | ## 编写测试 625 | 626 | 项目中使用[`Mocha`](https://mochajs.org/)来做单元测试,不管是前端代码还是后端的,都可以使用该测试框架写测试用例。 627 | 628 | 在项目根目录下执行`yarn test`命令会运行所有测试用例,在git提交时也会执行该操作,如果测试用例有失败的,则不能提交成功。 629 | 630 | 项目中的`test`目录是放置测试文件的地方,我们建议每个测试文件需和功能文件一一对应,包括文件的路径。比如有个功能文件,文件路径是`src/components/Header/Header.js`,那么测试文件的路径就应该是`test/components/Header/Header.test.js`,每个测试文件需要加上`.test`后缀,这样测试框架才能找到并执行它们。 631 | 632 | 测试React组件我们需要使用[`Enzyme`](https://github.com/airbnb/enzyme)这个工具包,它是一个方便你测试React组件的工具,它提供了一系列方便的API,让你可以在测试代码中渲染组件,并通过选择器找到你要验证的页面元素进行结果校验,还可以方便地获取组件中的`props`和`state`数据来测试结果。 633 | 634 | 以测试React组件为例: 635 | 636 | ### Message.test.js 637 | 638 | ```js 639 | import React from 'react'; 640 | import { shallow } from 'enzyme'; 641 | import { expect } from 'chai'; 642 | import Message from 'containers/Main/components/Message'; 643 | 644 | describe('Message component', () => { 645 | it('should render correctly', () => { 646 | const wrap = shallow(); 647 | expect(wrap.find('span').length).to.be.equal(1); 648 | expect(wrap.find('span').text()).to.be.equal('Message: foo'); 649 | }); 650 | }); 651 | ``` 652 | 653 | 通过调用`enzyem`的`shallow`方法可以浅渲染出React组件,并通过`find`方法找到其中的html元素,验证其个数是否正确,渲染出的内容是否正确等。 654 | 655 | 通过`yo modation:unittest `命令可以让你快速创建一个测试文件,首先要进入到你要创建测试文件的文件夹,然后执行该命令。 656 | 657 | -------------------------------------------------------------------------------- /generators/app/templates/docs/user_guide_zh.md: -------------------------------------------------------------------------------- 1 | 当你使用generator创建项目之后,就可以开始开发你的Web应用了,下面是本项目框架的一个使用指南,可以指导你如何使用框架开发新功能。 2 | 3 | ## 目录 4 | 5 | * [项目结构](#项目结构) 6 | * [操作命令](#操作命令) 7 | * [npm start](#npm-start) 8 | * [npm run start:dist](#npm-run-startdist) 9 | * [npm test](#npm-test) 10 | * [npm run check](#npm-run-check) 11 | * [npm run dist](#npm-run-dist) 12 | * [静态代码检查](#静态代码检查) 13 | * [JS代码检查](#JS代码检查) 14 | * [CSS代码检查](#CSS代码检查) 15 | * [提交信息校验](#提交信息校验) 16 | * [开发一个小功能](#开发一个小功能) 17 | * [编写组件](#编写组件) 18 | * [编写action](#编写action) 19 | * [编写reducer](#编写reducer) 20 | * [编写路由](#编写路由) 21 | * [安装依赖](#安装依赖) 22 | * [导入组件](#导入组件) 23 | * [添加样式](#添加样式) 24 | * [CSS预处理](#CSS预处理) 25 | * [添加图片和字体](#添加图片和字体) 26 | * [使用Ant Design](#使用ant-design) 27 | * [发送请求](#发送请求) 28 | * [集成服务端](#集成服务端) 29 | * [编写测试](#编写测试) 30 | 31 | 32 | ## 项目结构 33 | 34 | ``` 35 | ├── .babelrc 36 | ├── .eslintrc 37 | ├── package.json 38 | ├── devServer.js 39 | ├── docs 40 | │   └── README.md 41 | ├── src 42 | │   ├── app.js 43 | │   ├── components 44 | │   │   └── Name 45 | │   │   ├── Name.js 46 | │   │   ├── index.js 47 | │   │   └── style.css 48 | │   ├── constants 49 | │   │   └── actionTypes.js 50 | │   ├── containers 51 | │   │   └── Main 52 | │   │   ├── Main.js 53 | │   │   ├── actions.js 54 | │   │   ├── components 55 | │   │   │   └── Message 56 | │   │   │   ├── Message.js 57 | │   │   │   ├── index.js 58 | │   │   │   └── style.css 59 | │   │   ├── index.js 60 | │   │   ├── reducer.js 61 | │   │   └── style.css 62 | │   ├── reducers.js 63 | │   ├── routes.js 64 | │   ├── server 65 | │   │   ├── README.md 66 | │   │   └── index.js 67 | │   ├── store.js 68 | │   ├── styles 69 | │   │   └── App.css 70 | │   └── utils 71 | │   └── README.md 72 | ├── test 73 | │   ├── containers 74 | │   │   └── Main 75 | │   │   ├── components 76 | │   │   │   └── Message 77 | │   │   │   └── Message.test.js 78 | │   │   └── reducer.test.js 79 | │   ├── css-null-compiler.js 80 | │   ├── dom.js 81 | │   └── macha.opts 82 | ├── validate-commit-msg.js 83 | └── webpack 84 | ├── base.js 85 | ├── client.js 86 | └── server.js 87 | ``` 88 | 89 | * docs: 放置项目文档 90 | * src: 源文件目录 91 | * components: 放置公共组件 92 | * constants: 放置常量文件 93 | * containers: 这是放置容器型组件的地方,容器型组件包含了自己的action,reducer和router,里面还可以有自己的子组件 94 | * test: 测试文件目录,放置测试用例 95 | * webpack: Webpack配置文件的目录,客户端和服务端的配置是分不同文件配置的 96 | 97 | ## 操作命令 98 | 99 | 在项目中,你可以运行如下命令: 100 | 101 | ### `npm start` 102 | 103 | 在开发环境运行项目,启动成功后,在浏览器打开`http://localhost:8000`可以访问。 104 | 105 | 当你修改项目中的文件并保存后,应用进程会重新加载,如果有错误会在终端显示。 106 | 107 | ### `npm run start:dist` 108 | 109 | 在生产环境运行打包后的项目,整体性能比开发环境更快。 110 | 111 | ### `npm test` 112 | 113 | 运行项目中的测试,你可以查看[编写测试](#编写测试)章节了解如何编写测试用例。 114 | 115 | ### `npm run check` 116 | 117 | 运行代码检查,发现你的代码是否有错误,检查内容包括JS和CSS文件,你可以查看[静态代码检查](#静态代码检查)章节了解更多内容。 118 | 119 | ### `npm run dist` 120 | 121 | 运行打包程序,将项目打包为静态资源文件。 122 | 123 | ## 静态代码检查 124 | 125 | 项目中集成了一些JS和CSS编码规范,这些规范是业界推崇比较好的规范,这样可以统一开发团队的编码规范,避免杂乱无章的代码。 126 | 127 | 如果你的代码不符合这些规范,在终端会提示你哪些文件有哪些错误,如下图所示: 128 | 129 | ![](images/eslint_error.png) 130 | 131 | 有两种方式可以帮助你查看到代码规范错误: 132 | 133 | * 你可以运行`npm run check`来检查你的代码是否包含错误 134 | * 项目通过`npm start`启动后,如果代码有错误,会在终端显示错误信息 135 | 136 | ### JS代码检查 137 | 138 | 项目中使用的JS检查工具是[eslint](http://eslint.org/),JS的编码规范引用了[airbnb公司的JS代码规范](https://github.com/airbnb/javascript),这些规则是基于ES6的,如果你不了解ES6,请先了解一下[ES6的语法](http://es6.ruanyifeng.com/),这样可以避免一些常见的错误。 139 | 140 | 如果对提示的错误信息不了解,可以通过Google搜索`eslint 规则名称`来查询eslint规则的详细信息,以上面的图为例,错误信息的每一行后面都有规则名称,所以搜索的关键字为`eslint no-useless-escape`,在对应的页面中通常都会教你如何修改这个错误。 141 | 142 | ### CSS代码检查 143 | 144 | 项目中使用[stylelint](https://github.com/stylelint/stylelint)来做css代码的规范检查,stylelint的规则文件是项目根目录下的`.stylelintrc`文件,你可以修改里面的规则来让其更适合你的开发团队。 145 | 146 | 同样的,如果你对提示的错误信息不了解,也可以通过Google搜索`stylelint 规则名`来了解规则的详细信息,和eslint一样,每一行错误信息的最后会显示规则名称: 147 | 148 | ![](images/stylelint_error.png) 149 | 150 | ## 提交信息校验 151 | 152 | 项目中集成git提交信息的校验,我们希望能从代码的提交记录中知道修改的主要内容,比如是修改哪个模块的代码,是新功能的添加,还是修复缺陷,或者是添加测试等。 153 | 154 | 提交信息的格式为: `(): `,其中type和subject是必须的,scope是可选的。 155 | 156 | type主要有以下几种类型: 157 | 158 | * feat:新功能(feature) 159 | * fix:修补bug 160 | * docs:文档(documentation) 161 | * style: 格式(不影响代码运行的变动) 162 | * refactor:重构(即不是新增功能,也不是修改bug的代码变动) 163 | * test:增加测试 164 | * chore:构建过程或辅助工具的变动 165 | 166 | scope用于说明 commit 影响的范围,比如数据层、控制层、视图层等等,视项目不同而不同。 167 | 168 | subject是 commit 目的的简短描述,不超过50个字符。 169 | 170 | 更详细的参考资料可以看[这里](http://www.ruanyifeng.com/blog/2016/01/commit_message_change_log.html)。 171 | 172 | ## 开发一个小功能 173 | 174 | 假设我们需要开发小功能——添加待办事项,下面分别以开发React组件,Redux action方法和reducer方法为例,介绍一下如何开发这个功能。 175 | 176 | ## 编写组件 177 | 178 | 项目使用[React](https://facebook.github.io/react/)来做前端页面渲染,React是最近比较流行的一个页面渲染框架,通过React可以很方便进行页面组件化开发,并最大化的复用页面组件,避免重复代码,提高代码可维护性。 179 | 180 | 这是一个添加待办事项的的React组件: 181 | 182 | ```js 183 | import React, { Component, PropTypes } from 'react'; 184 | 185 | class TodoInput extends Component { 186 | static propTypes = { 187 | actions: PropTypes.object.isRequired, 188 | }; 189 | 190 | constructor(props, context) { 191 | super(props, context); 192 | this.state = { 193 | value: '', 194 | }; 195 | this.handleChange = this.handleChange.bind(this); 196 | } 197 | 198 | handleChange(event) { 199 | const text = event.target.value; 200 | this.setState({ value: text}); 201 | this.props.actions.addTodo(text); 202 | } 203 | 204 | render() { 205 | return ( 206 |
207 | 208 |
209 | ); 210 | } 211 | } 212 | 213 | export default TodoInput; 214 | ``` 215 | 216 | 每个组件使用ES6的class来封装,继承react的`Component`对象,`render`方法是每个组件必须存在的方法,渲染的页面写在这里面。 217 | 218 | 在组件的开头部分是props对象的校验,`actions: PropTypes.object.isRequired`是指actions这个属性是对象(`object`)类型,而且是必须的(`isRequired`),如果在引用这个组件时没有传递actions属性,编译将会报错,如果传给actions的值不是object类型的,编译会给一个警告。 219 | 220 | `constructor`是类的构造器,里面定义了组件的state这个对象各个属性的初始值,并将`handleChange`方法绑定到了这个组件对象里面来。**请注意,并不是所有方法都需要`bind(this)`,如果方法里面用到了`this`这个关键字才需要绑定。** 221 | 222 | `handleChange`方法可以改变组件的内部数据,通过调用`setState`方法来改变,传入的参数是一个包含了state属性的对象。请注意,不能使用`this.state.value = 'foo'`这种方式给state属性赋值,一般是通过`setState`方法来改变state数据。 223 | 224 | 最后是`render`方法,一般是返回页面元素,可以通过JS语法根据state或props对象的值来生成动态的页面。请注意,`render`方法返回的对象必须只有一个根元素,比如是`
`,多于一个都会编译报错,比如这种是错误的:`
`。 225 | 226 | 更多React的详细信息可以参考以下资料: 227 | 228 | * [React官网](https://facebook.github.io/react/) 229 | * [React Pattern](http://reactpatterns.com/) 230 | 231 | ## 编写action 232 | 233 | 项目使用了[Redux](https://github.com/reactjs/redux)来做组件的数据状态管理,action是应用发送数据到Redux仓库的一个信息载体,它们是仓库的原始信息。 234 | 235 | 前面的`TodoInput`组件调用了`actions.addTodo()`方法,传入了输入框中的值,下面是这个action示例: 236 | 237 | ### actionTypes.js 238 | 239 | ```js 240 | export const ADD_TODO = 'ADD_TODO'; 241 | ``` 242 | 243 | ### action.js 244 | 245 | ```js 246 | import { ADD_TODO } from '../actionTypes'; 247 | 248 | function addTodo(text) { 249 | return { 250 | type: ADD_TODO, 251 | text, 252 | }; 253 | } 254 | ``` 255 | 256 | 首先我们定义一个常量来表示action的类型,一般这些常量会有单独一个文件(比如`actionTypes.js`)来存放。 257 | 258 | 然后定义一个方法返回一个纯JS对象,这个对象必须包含`type`属性,其它属性都是可选的。type属性的类型是字符串,这个type会和reducers里面的type对应起来,从而建立两者关联。 259 | 260 | 通常建议action返回的对象只包含一些必要的数据,并且能小尽量的小,不要包含太多属性,也不要在某个属性里面赋值一个很大的对象。 261 | 262 | ## 编写reducer 263 | 264 | 如果action表示某些事情已经发生了,那么reducer就表示如何响应这些事情,如何根据action返回的数据更新Redux仓库的内容。 265 | 266 | 在Redux中,应用的所有数据都存放在一个单独的对象里面,在写Redux代码前最好把这个概念深印在你的脑海里。 267 | 268 | reducer是一个纯函数,接收前状态和action为参数,返回一个新的状态。 269 | 270 | ```js 271 | (previousState, action) => newState 272 | ``` 273 | 274 | 在reducer方法里面建议不要做以下事情: 275 | 276 | * 修改方法参数 277 | * 执行性能消耗大的操作,比如调用后端API或者转换路由 278 | * 调用一些不纯的函数,比如`Date.now()`或`Math.random()` 279 | 280 | 下面是一个reducer的例子: 281 | 282 | ```js 283 | import { ADD_TODO } from '../actionTypes'; 284 | import immutable from 'immutable'; 285 | 286 | const initialState = immutable.fromJS({ 287 | todos: [], 288 | }); 289 | 290 | function todoApp(state = initialState, action) { 291 | switch (action.type) { 292 | case ADD_TODO: 293 | return state.get('todos', (todos) => todos.push({ 294 | text: action.text, 295 | completed: false, 296 | })); 297 | default: 298 | return state; 299 | } 300 | } 301 | ``` 302 | 303 | 首先我们引用了[`immutable`](http://facebook.github.io/immutable-js/docs)这个第三方工具库,它的主要作用是不改变原来的对象,而是在每次操作后返回一个新的对象。为什么要使用`immutable`呢?这是因为Redux仓库里面的数据都是不可变的,如果需要修改里面的数据,需要返回一个全新的对象来覆盖原来的对象,所以`immutable`非常合适在Redux项目中使用。更多`immutable`的用法可以参考其官网的文档。 304 | 305 | 然后我们构造了一个状态对象的初始值,这个是reducer方法第一次被调用时会使用的初始值,在reducer方法参数里面被这样用到`state = initialState`,如果传入的state参数为空,则使用`initialState`来为参数`state`赋值。 306 | 307 | 在reducer主体方法里面,我们根据action的类型来判断该走哪种处理逻辑,比如类型如果是`ADD_TODO`的话就在原来的`todos`集合里面添加一个新的todo对象,如果所有action类型都不匹配,就原封不动地返回原来的state对象。 308 | 309 | 更多Redux的详细信息可以参考以下资料: 310 | 311 | * [Redux 文档](http://redux.js.org/) 312 | * [Redux 视频](https://egghead.io/series/getting-started-with-redux) 313 | 314 | ## 编写路由 315 | 316 | 项目使用了[`react-router`](https://github.com/ReactTraining/react-router)来控制前端页面路由。 317 | 318 | 现在项目中的示例代码只配置一个根路由`/`,这个路由指向了`Main`组件,代码示例如下: 319 | 320 | ### routes.js(before) 321 | 322 | ```js 323 | import Main from 'containers/Main'; 324 | 325 | export function createRoutes() { 326 | return { 327 | path: '/', 328 | component: Main, 329 | }; 330 | } 331 | ``` 332 | 333 | 如果要配置更多的路由,首先需要修改`Main`组件,将在其render方法中添加`this.props.children`,表示组件会展示其子路由组件的内容。然后在`routes.js`里面添加子路由,代码示例如下: 334 | 335 | ### Main.js 336 | 337 | ```js 338 | class Main extends Component { 339 | render() { 340 | return ( 341 |
342 | {this.props.children} 343 |
344 | ); 345 | } 346 | } 347 | 348 | ``` 349 | 350 | ### routes.js(after) 351 | 352 | ```js 353 | export function createRoutes() { 354 | return { 355 | path: '/', 356 | component: Main, 357 | childRoutes: [ 358 | { path: 'about', component: About }, // 访问about可以看到About组件 359 | { path: 'inbox', component: Inbox }, // 访问inbox可以看到Inbox组件 360 | ], 361 | }; 362 | } 363 | ``` 364 | 365 | 更多react-router的详细信息可以参考以下资料: 366 | 367 | * [react-router](https://github.com/ReactTraining/react-router) 368 | * [react-router中文文档](https://react-guide.github.io/react-router-cn) 369 | 370 | ## 安装依赖 371 | 372 | 项目中已经包含了React,ReactDOM和Redux等基础依赖库,如果你想安装其他的依赖库,请通过yarn(这是最新的JS包管理工具,不了解的可以在[yarn官网](https://yarnpkg.com)查看更多详细信息)进行安装,命令如下: 373 | 374 | ``` 375 | yarn add [--dev] 376 | ``` 377 | 378 | 如果你想安装在devDependencies下就加上`--dev`参数。 379 | 380 | ## 导入组件 381 | 382 | 项目使用[Babel](https://babeljs.io/)编译ES6语法,当然你还是可以使用`require()`和`module.exports`来导入和导出你的组件,但我们还是推荐你使用`import`和`export`。 383 | 384 | 举个例子: 385 | 386 | ### `Button.js` 387 | 388 | ```js 389 | import React, { Component } from 'react'; 390 | 391 | class Button extends Component { 392 | render() { 393 | // ... 394 | } 395 | } 396 | 397 | export default Button; 398 | ``` 399 | 400 | ### `DangerButton.js` 401 | 402 | 403 | ```js 404 | import React, { Component } from 'react'; 405 | import Button from './Button'; // 导入另外Button.js文件里面的组件 406 | 407 | class DangerButton extends Component { 408 | render() { 409 | return ; 538 | } 539 | } 540 | 541 | export default YourButton; 542 | ``` 543 | 544 | 这只是一个简单的示例,在antd中有更多复杂的组件,如果要使用请仔细阅读该组件的说明文档,确定每个属性或者方法是使用正确的,如果在使用的过程中发现antd组件有什么问题,可以在其[issues区](https://github.com/ant-design/ant-design/issues)提问题,一般都能很快得到答复。 545 | 546 | 项目默认使用的antd版本是v2.0.1,查阅相关文档时请注意antd的版本是否正确。 547 | 548 | ## 发送请求 549 | 550 | 项目中使用`fetch`方法来发送http请求从服务器获取数据,一般我们建议在action方法里面来做发送请求的操作。 551 | 552 | 在项目中可以使用ES7的一些实验性语法`async\await`,使用它们可以避免写回调方法,使得我们的代码更加直观易懂。 553 | 554 | 举个例子: 555 | 556 | ```js 557 | export async function randomName(num) { 558 | const response = await fetch('/api/name/random', { 559 | method: 'post', 560 | body: JSON.stringify({ 561 | num, 562 | }), 563 | }); 564 | const result = await response.json(); 565 | return { 566 | type: at.CHANGE_NAME, 567 | name: result.name , 568 | }; 569 | } 570 | ``` 571 | 572 | 我们通过`fetch`请求后台服务器的api获取到json数据,这里分成2个部分,首先获取response,然后再从response中获取json数据,最后将结果封装在action对象中返回。 573 | 574 | 更多参考资料: 575 | 576 | * [这个API很迷人](http://www.w3ctech.com/topic/854) 577 | * [async 函数的含义和用法](http://www.ruanyifeng.com/blog/2015/05/async.html) 578 | 579 | ## 集成服务端 580 | 581 | 使用generator创建项目可以选择是否需要服务端,如果选择需要,项目框架中会添加服务端的代码。 582 | 583 | 项目服务端使用[`hapi.js`](http://hapijs.com/)作为后端服务器的框架,使用该框架可以很方便地编写后端API。 584 | 585 | 举个例子: 586 | 587 | ### index.js 588 | 589 | ```js 590 | import name from './handlers/name'; 591 | 592 | const server = new Hapi.Server(); 593 | server.route([name]); 594 | ``` 595 | 596 | ### name.js 597 | 598 | ```js 599 | export default { 600 | method: ['POST'], 601 | path: '/api/name/random', 602 | 603 | config: { 604 | handler(request, reply) { 605 | const { num } = JSON.parse(request.payload); 606 | const randomNames = []; 607 | for (let i = 0; i < num; i++) { 608 | randomNames.push(getRandomName()); 609 | } 610 | return reply({ 611 | name: randomNames.join(' '), 612 | }); 613 | }, 614 | }, 615 | }; 616 | ``` 617 | 618 | 首先在hapi的服务`server`中通过`route`方法添加`name`这个路由,这个路由其实是一个JS对象,里面有以下基本属性: 619 | 620 | * method: 定义请求的方法,比如`GET`,`PUST`等 621 | * path: 定义路由的url路径 622 | * config: 具体处理逻辑,通过`request`参数可以获取请求的参数,然后通过`reply`参数可以返回response结果 623 | 624 | ## 编写测试 625 | 626 | 项目中使用[`Mocha`](https://mochajs.org/)来做单元测试,不管是前端代码还是后端的,都可以使用该测试框架写测试用例。 627 | 628 | 在项目根目录下执行`yarn test`命令会运行所有测试用例,在git提交时也会执行该操作,如果测试用例有失败的,则不能提交成功。 629 | 630 | 项目中的`test`目录是放置测试文件的地方,我们建议每个测试文件需和功能文件一一对应,包括文件的路径。比如有个功能文件,文件路径是`src/components/Header/Header.js`,那么测试文件的路径就应该是`test/components/Header/Header.test.js`,每个测试文件需要加上`.test`后缀,这样测试框架才能找到并执行它们。 631 | 632 | 测试React组件我们需要使用[`Enzyme`](https://github.com/airbnb/enzyme)这个工具包,它是一个方便你测试React组件的工具,它提供了一系列方便的API,让你可以在测试代码中渲染组件,并通过选择器找到你要验证的页面元素进行结果校验,还可以方便地获取组件中的`props`和`state`数据来测试结果。 633 | 634 | 以测试React组件为例: 635 | 636 | ### Message.test.js 637 | 638 | ```js 639 | import React from 'react'; 640 | import { shallow } from 'enzyme'; 641 | import { expect } from 'chai'; 642 | import Message from 'containers/Main/components/Message'; 643 | 644 | describe('Message component', () => { 645 | it('should render correctly', () => { 646 | const wrap = shallow(); 647 | expect(wrap.find('span').length).to.be.equal(1); 648 | expect(wrap.find('span').text()).to.be.equal('Message: foo'); 649 | }); 650 | }); 651 | ``` 652 | 653 | 通过调用`enzyem`的`shallow`方法可以浅渲染出React组件,并通过`find`方法找到其中的html元素,验证其个数是否正确,渲染出的内容是否正确等。 654 | 655 | 通过`yo modation:unittest `命令可以让你快速创建一个测试文件,首先要进入到你要创建测试文件的文件夹,然后执行该命令。 656 | 657 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | abbrev@1: 4 | version "1.0.9" 5 | resolved "http://registry.npm.taobao.org/abbrev/download/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" 6 | 7 | amdefine@>=0.0.4: 8 | version "1.0.0" 9 | resolved "http://registry.npm.taobao.org/amdefine/download/amdefine-1.0.0.tgz#fd17474700cb5cc9c2b709f0be9d23ce3c198c33" 10 | 11 | ansi-escapes@^1.1.0: 12 | version "1.4.0" 13 | resolved "http://registry.npm.taobao.org/ansi-escapes/download/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" 14 | 15 | ansi-regex@^2.0.0: 16 | version "2.0.0" 17 | resolved "http://registry.npm.taobao.org/ansi-regex/download/ansi-regex-2.0.0.tgz#c5061b6e0ef8a81775e50f5d66151bf6bf371107" 18 | 19 | ansi-styles@^2.2.1: 20 | version "2.2.1" 21 | resolved "http://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" 22 | 23 | archive-type@^3.0.0: 24 | version "3.2.0" 25 | resolved "http://registry.npm.taobao.org/archive-type/download/archive-type-3.2.0.tgz#9cd9c006957ebe95fadad5bd6098942a813737f6" 26 | dependencies: 27 | file-type "^3.1.0" 28 | 29 | arr-diff@^2.0.0: 30 | version "2.0.0" 31 | resolved "http://registry.npm.taobao.org/arr-diff/download/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" 32 | dependencies: 33 | arr-flatten "^1.0.1" 34 | 35 | arr-flatten@^1.0.1: 36 | version "1.0.1" 37 | resolved "http://registry.npm.taobao.org/arr-flatten/download/arr-flatten-1.0.1.tgz#e5ffe54d45e19f32f216e91eb99c8ce892bb604b" 38 | 39 | array-differ@^1.0.0: 40 | version "1.0.0" 41 | resolved "http://registry.npm.taobao.org/array-differ/download/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031" 42 | 43 | array-find-index@^1.0.1: 44 | version "1.0.2" 45 | resolved "http://registry.npm.taobao.org/array-find-index/download/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" 46 | 47 | array-union@^1.0.1: 48 | version "1.0.2" 49 | resolved "http://registry.npm.taobao.org/array-union/download/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" 50 | dependencies: 51 | array-uniq "^1.0.1" 52 | 53 | array-uniq@^1.0.1, array-uniq@^1.0.2: 54 | version "1.0.3" 55 | resolved "http://registry.npm.taobao.org/array-uniq/download/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" 56 | 57 | array-unique@^0.2.1: 58 | version "0.2.1" 59 | resolved "http://registry.npm.taobao.org/array-unique/download/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" 60 | 61 | arrify@^1.0.0: 62 | version "1.0.1" 63 | resolved "http://registry.npm.taobao.org/arrify/download/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" 64 | 65 | ast-query@^1.0.1: 66 | version "1.2.0" 67 | resolved "http://registry.npm.taobao.org/ast-query/download/ast-query-1.2.0.tgz#4b725e9f3922a5edc41dc669c87bbfbfd20171a2" 68 | dependencies: 69 | class-extend "^0.1.1" 70 | escodegen "^1.6.0" 71 | esprima "^2.0.0" 72 | lodash "^4.6.1" 73 | traverse "^0.6.6" 74 | 75 | async@^1.4.2: 76 | version "1.5.2" 77 | resolved "http://registry.npm.taobao.org/async/download/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" 78 | 79 | balanced-match@^0.4.1: 80 | version "0.4.2" 81 | resolved "http://registry.npm.taobao.org/balanced-match/download/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" 82 | 83 | beeper@^1.0.0: 84 | version "1.1.0" 85 | resolved "http://registry.npm.taobao.org/beeper/download/beeper-1.1.0.tgz#9ee6fc1ce7f54feaace7ce73588b056037866a2c" 86 | 87 | binaryextensions@~1.0.0: 88 | version "1.0.1" 89 | resolved "http://registry.npm.taobao.org/binaryextensions/download/binaryextensions-1.0.1.tgz#1e637488b35b58bda5f4774bf96a5212a8c90755" 90 | 91 | bl@^1.0.0: 92 | version "1.1.2" 93 | resolved "http://registry.npm.taobao.org/bl/download/bl-1.1.2.tgz#fdca871a99713aa00d19e3bbba41c44787a65398" 94 | dependencies: 95 | readable-stream "~2.0.5" 96 | 97 | boolbase@~1.0.0: 98 | version "1.0.0" 99 | resolved "http://registry.npm.taobao.org/boolbase/download/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" 100 | 101 | brace-expansion@^1.0.0: 102 | version "1.1.6" 103 | resolved "http://registry.npm.taobao.org/brace-expansion/download/brace-expansion-1.1.6.tgz#7197d7eaa9b87e648390ea61fc66c84427420df9" 104 | dependencies: 105 | balanced-match "^0.4.1" 106 | concat-map "0.0.1" 107 | 108 | braces@^1.8.2: 109 | version "1.8.5" 110 | resolved "http://registry.npm.taobao.org/braces/download/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" 111 | dependencies: 112 | expand-range "^1.8.1" 113 | preserve "^0.2.0" 114 | repeat-element "^1.1.2" 115 | 116 | buffer-crc32@~0.2.3: 117 | version "0.2.5" 118 | resolved "http://registry.npm.taobao.org/buffer-crc32/download/buffer-crc32-0.2.5.tgz#db003ac2671e62ebd6ece78ea2c2e1b405736e91" 119 | 120 | buffer-shims@^1.0.0: 121 | version "1.0.0" 122 | resolved "http://registry.npm.taobao.org/buffer-shims/download/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" 123 | 124 | buffer-to-vinyl@^1.0.0: 125 | version "1.1.0" 126 | resolved "http://registry.npm.taobao.org/buffer-to-vinyl/download/buffer-to-vinyl-1.1.0.tgz#00f15faee3ab7a1dda2cde6d9121bffdd07b2262" 127 | dependencies: 128 | file-type "^3.1.0" 129 | readable-stream "^2.0.2" 130 | uuid "^2.0.1" 131 | vinyl "^1.0.0" 132 | 133 | builtin-modules@^1.0.0: 134 | version "1.1.1" 135 | resolved "http://registry.npm.taobao.org/builtin-modules/download/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" 136 | 137 | camelcase-keys@^2.0.0: 138 | version "2.1.0" 139 | resolved "http://registry.npm.taobao.org/camelcase-keys/download/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" 140 | dependencies: 141 | camelcase "^2.0.0" 142 | map-obj "^1.0.0" 143 | 144 | camelcase@^2.0.0: 145 | version "2.1.1" 146 | resolved "http://registry.npm.taobao.org/camelcase/download/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" 147 | 148 | capture-stack-trace@^1.0.0: 149 | version "1.0.0" 150 | resolved "http://registry.npm.taobao.org/capture-stack-trace/download/capture-stack-trace-1.0.0.tgz#4a6fa07399c26bba47f0b2496b4d0fb408c5550d" 151 | 152 | caw@^1.0.1: 153 | version "1.2.0" 154 | resolved "http://registry.npm.taobao.org/caw/download/caw-1.2.0.tgz#ffb226fe7efc547288dc62ee3e97073c212d1034" 155 | dependencies: 156 | get-proxy "^1.0.1" 157 | is-obj "^1.0.0" 158 | object-assign "^3.0.0" 159 | tunnel-agent "^0.4.0" 160 | 161 | chalk@^1.0.0, chalk@^1.1.1: 162 | version "1.1.3" 163 | resolved "http://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" 164 | dependencies: 165 | ansi-styles "^2.2.1" 166 | escape-string-regexp "^1.0.2" 167 | has-ansi "^2.0.0" 168 | strip-ansi "^3.0.0" 169 | supports-color "^2.0.0" 170 | 171 | cheerio@^0.19.0: 172 | version "0.19.0" 173 | resolved "http://registry.npm.taobao.org/cheerio/download/cheerio-0.19.0.tgz#772e7015f2ee29965096d71ea4175b75ab354925" 174 | dependencies: 175 | css-select "~1.0.0" 176 | dom-serializer "~0.1.0" 177 | entities "~1.1.1" 178 | htmlparser2 "~3.8.1" 179 | lodash "^3.2.0" 180 | 181 | class-extend@^0.1.0, class-extend@^0.1.1: 182 | version "0.1.2" 183 | resolved "http://registry.npm.taobao.org/class-extend/download/class-extend-0.1.2.tgz#8057a82b00f53f82a5d62c50ef8cffdec6fabc34" 184 | dependencies: 185 | object-assign "^2.0.0" 186 | 187 | cli-cursor@^1.0.1: 188 | version "1.0.2" 189 | resolved "http://registry.npm.taobao.org/cli-cursor/download/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" 190 | dependencies: 191 | restore-cursor "^1.0.1" 192 | 193 | cli-table@^0.3.1: 194 | version "0.3.1" 195 | resolved "http://registry.npm.taobao.org/cli-table/download/cli-table-0.3.1.tgz#f53b05266a8b1a0b934b3d0821e6e2dc5914ae23" 196 | dependencies: 197 | colors "1.0.3" 198 | 199 | cli-width@^2.0.0: 200 | version "2.1.0" 201 | resolved "http://registry.npm.taobao.org/cli-width/download/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a" 202 | 203 | clone-stats@^0.0.1: 204 | version "0.0.1" 205 | resolved "http://registry.npm.taobao.org/clone-stats/download/clone-stats-0.0.1.tgz#b88f94a82cf38b8791d58046ea4029ad88ca99d1" 206 | 207 | clone@^0.2.0: 208 | version "0.2.0" 209 | resolved "http://registry.npm.taobao.org/clone/download/clone-0.2.0.tgz#c6126a90ad4f72dbf5acdb243cc37724fe93fc1f" 210 | 211 | clone@^1.0.0: 212 | version "1.0.2" 213 | resolved "http://registry.npm.taobao.org/clone/download/clone-1.0.2.tgz#260b7a99ebb1edfe247538175f783243cb19d149" 214 | 215 | co@3.1.0: 216 | version "3.1.0" 217 | resolved "http://registry.npm.taobao.org/co/download/co-3.1.0.tgz#4ea54ea5a08938153185e15210c68d9092bc1b78" 218 | 219 | code-point-at@^1.0.0: 220 | version "1.0.1" 221 | resolved "http://registry.npm.taobao.org/code-point-at/download/code-point-at-1.0.1.tgz#1104cd34f9b5b45d3eba88f1babc1924e1ce35fb" 222 | dependencies: 223 | number-is-nan "^1.0.0" 224 | 225 | colors@1.0.3: 226 | version "1.0.3" 227 | resolved "http://registry.npm.taobao.org/colors/download/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" 228 | 229 | commander@~2.8.1: 230 | version "2.8.1" 231 | resolved "http://registry.npm.taobao.org/commander/download/commander-2.8.1.tgz#06be367febfda0c330aa1e2a072d3dc9762425d4" 232 | dependencies: 233 | graceful-readlink ">= 1.0.0" 234 | 235 | commondir@^1.0.1: 236 | version "1.0.1" 237 | resolved "http://registry.npm.taobao.org/commondir/download/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" 238 | 239 | concat-map@0.0.1: 240 | version "0.0.1" 241 | resolved "http://registry.npm.taobao.org/concat-map/download/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 242 | 243 | concat-stream@^1.4.6, concat-stream@^1.4.7: 244 | version "1.5.2" 245 | resolved "http://registry.npm.taobao.org/concat-stream/download/concat-stream-1.5.2.tgz#708978624d856af41a5a741defdd261da752c266" 246 | dependencies: 247 | inherits "~2.0.1" 248 | readable-stream "~2.0.0" 249 | typedarray "~0.0.5" 250 | 251 | convert-source-map@^1.1.1: 252 | version "1.3.0" 253 | resolved "http://registry.npm.taobao.org/convert-source-map/download/convert-source-map-1.3.0.tgz#e9f3e9c6e2728efc2676696a70eb382f73106a67" 254 | 255 | core-util-is@~1.0.0: 256 | version "1.0.2" 257 | resolved "http://registry.npm.taobao.org/core-util-is/download/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" 258 | 259 | create-error-class@^3.0.1: 260 | version "3.0.2" 261 | resolved "http://registry.npm.taobao.org/create-error-class/download/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6" 262 | dependencies: 263 | capture-stack-trace "^1.0.0" 264 | 265 | cross-spawn@^3.0.0: 266 | version "3.0.1" 267 | resolved "http://registry.npm.taobao.org/cross-spawn/download/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982" 268 | dependencies: 269 | lru-cache "^4.0.1" 270 | which "^1.2.9" 271 | 272 | css-select@~1.0.0: 273 | version "1.0.0" 274 | resolved "http://registry.npm.taobao.org/css-select/download/css-select-1.0.0.tgz#b1121ca51848dd264e2244d058cee254deeb44b0" 275 | dependencies: 276 | boolbase "~1.0.0" 277 | css-what "1.0" 278 | domutils "1.4" 279 | nth-check "~1.0.0" 280 | 281 | css-what@1.0: 282 | version "1.0.0" 283 | resolved "http://registry.npm.taobao.org/css-what/download/css-what-1.0.0.tgz#d7cc2df45180666f99d2b14462639469e00f736c" 284 | 285 | currently-unhandled@^0.4.1: 286 | version "0.4.1" 287 | resolved "http://registry.npm.taobao.org/currently-unhandled/download/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" 288 | dependencies: 289 | array-find-index "^1.0.1" 290 | 291 | dargs@^4.0.0: 292 | version "4.1.0" 293 | resolved "http://registry.npm.taobao.org/dargs/download/dargs-4.1.0.tgz#03a9dbb4b5c2f139bf14ae53f0b8a2a6a86f4e17" 294 | dependencies: 295 | number-is-nan "^1.0.0" 296 | 297 | dateformat@^1.0.11: 298 | version "1.0.12" 299 | resolved "http://registry.npm.taobao.org/dateformat/download/dateformat-1.0.12.tgz#9f124b67594c937ff706932e4a642cca8dbbfee9" 300 | dependencies: 301 | get-stdin "^4.0.1" 302 | meow "^3.3.0" 303 | 304 | debug@^2.0.0, debug@^2.1.0: 305 | version "2.2.0" 306 | resolved "http://registry.npm.taobao.org/debug/download/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" 307 | dependencies: 308 | ms "0.7.1" 309 | 310 | decamelize@^1.1.2: 311 | version "1.2.0" 312 | resolved "http://registry.npm.taobao.org/decamelize/download/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" 313 | 314 | decompress-tar@^3.0.0: 315 | version "3.1.0" 316 | resolved "http://registry.npm.taobao.org/decompress-tar/download/decompress-tar-3.1.0.tgz#217c789f9b94450efaadc5c5e537978fc333c466" 317 | dependencies: 318 | is-tar "^1.0.0" 319 | object-assign "^2.0.0" 320 | strip-dirs "^1.0.0" 321 | tar-stream "^1.1.1" 322 | through2 "^0.6.1" 323 | vinyl "^0.4.3" 324 | 325 | decompress-tarbz2@^3.0.0: 326 | version "3.1.0" 327 | resolved "http://registry.npm.taobao.org/decompress-tarbz2/download/decompress-tarbz2-3.1.0.tgz#8b23935681355f9f189d87256a0f8bdd96d9666d" 328 | dependencies: 329 | is-bzip2 "^1.0.0" 330 | object-assign "^2.0.0" 331 | seek-bzip "^1.0.3" 332 | strip-dirs "^1.0.0" 333 | tar-stream "^1.1.1" 334 | through2 "^0.6.1" 335 | vinyl "^0.4.3" 336 | 337 | decompress-targz@^3.0.0: 338 | version "3.1.0" 339 | resolved "http://registry.npm.taobao.org/decompress-targz/download/decompress-targz-3.1.0.tgz#b2c13df98166268991b715d6447f642e9696f5a0" 340 | dependencies: 341 | is-gzip "^1.0.0" 342 | object-assign "^2.0.0" 343 | strip-dirs "^1.0.0" 344 | tar-stream "^1.1.1" 345 | through2 "^0.6.1" 346 | vinyl "^0.4.3" 347 | 348 | decompress-unzip@^3.0.0: 349 | version "3.4.0" 350 | resolved "http://registry.npm.taobao.org/decompress-unzip/download/decompress-unzip-3.4.0.tgz#61475b4152066bbe3fee12f9d629d15fe6478eeb" 351 | dependencies: 352 | is-zip "^1.0.0" 353 | read-all-stream "^3.0.0" 354 | stat-mode "^0.2.0" 355 | strip-dirs "^1.0.0" 356 | through2 "^2.0.0" 357 | vinyl "^1.0.0" 358 | yauzl "^2.2.1" 359 | 360 | decompress@^3.0.0: 361 | version "3.0.0" 362 | resolved "http://registry.npm.taobao.org/decompress/download/decompress-3.0.0.tgz#af1dd50d06e3bfc432461d37de11b38c0d991bed" 363 | dependencies: 364 | buffer-to-vinyl "^1.0.0" 365 | concat-stream "^1.4.6" 366 | decompress-tar "^3.0.0" 367 | decompress-tarbz2 "^3.0.0" 368 | decompress-targz "^3.0.0" 369 | decompress-unzip "^3.0.0" 370 | stream-combiner2 "^1.1.1" 371 | vinyl-assign "^1.0.1" 372 | vinyl-fs "^2.2.0" 373 | 374 | deep-extend@^0.4.0, deep-extend@~0.4.0: 375 | version "0.4.1" 376 | resolved "http://registry.npm.taobao.org/deep-extend/download/deep-extend-0.4.1.tgz#efe4113d08085f4e6f9687759810f807469e2253" 377 | 378 | deep-is@~0.1.3: 379 | version "0.1.3" 380 | resolved "http://registry.npm.taobao.org/deep-is/download/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" 381 | 382 | detect-conflict@^1.0.0: 383 | version "1.0.1" 384 | resolved "http://registry.npm.taobao.org/detect-conflict/download/detect-conflict-1.0.1.tgz#088657a66a961c05019db7c4230883b1c6b4176e" 385 | 386 | detect-newline@^1.0.3: 387 | version "1.0.3" 388 | resolved "http://registry.npm.taobao.org/detect-newline/download/detect-newline-1.0.3.tgz#e97b1003877d70c09af1af35bfadff168de4920d" 389 | dependencies: 390 | get-stdin "^4.0.1" 391 | minimist "^1.1.0" 392 | 393 | diff@^2.1.2: 394 | version "2.2.3" 395 | resolved "http://registry.npm.taobao.org/diff/download/diff-2.2.3.tgz#60eafd0d28ee906e4e8ff0a52c1229521033bf99" 396 | 397 | dom-serializer@~0.1.0, dom-serializer@0: 398 | version "0.1.0" 399 | resolved "http://registry.npm.taobao.org/dom-serializer/download/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" 400 | dependencies: 401 | domelementtype "~1.1.1" 402 | entities "~1.1.1" 403 | 404 | domelementtype@~1.1.1: 405 | version "1.1.3" 406 | resolved "http://registry.npm.taobao.org/domelementtype/download/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" 407 | 408 | domelementtype@1: 409 | version "1.3.0" 410 | resolved "http://registry.npm.taobao.org/domelementtype/download/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" 411 | 412 | domhandler@2.3: 413 | version "2.3.0" 414 | resolved "http://registry.npm.taobao.org/domhandler/download/domhandler-2.3.0.tgz#2de59a0822d5027fabff6f032c2b25a2a8abe738" 415 | dependencies: 416 | domelementtype "1" 417 | 418 | domutils@1.4: 419 | version "1.4.3" 420 | resolved "http://registry.npm.taobao.org/domutils/download/domutils-1.4.3.tgz#0865513796c6b306031850e175516baf80b72a6f" 421 | dependencies: 422 | domelementtype "1" 423 | 424 | domutils@1.5: 425 | version "1.5.1" 426 | resolved "http://registry.npm.taobao.org/domutils/download/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" 427 | dependencies: 428 | dom-serializer "0" 429 | domelementtype "1" 430 | 431 | download@^4.1.2: 432 | version "4.4.3" 433 | resolved "http://registry.npm.taobao.org/download/download/download-4.4.3.tgz#aa55fdad392d95d4b68e8c2be03e0c2aa21ba9ac" 434 | dependencies: 435 | caw "^1.0.1" 436 | concat-stream "^1.4.7" 437 | each-async "^1.0.0" 438 | filenamify "^1.0.1" 439 | got "^5.0.0" 440 | gulp-decompress "^1.2.0" 441 | gulp-rename "^1.2.0" 442 | is-url "^1.2.0" 443 | object-assign "^4.0.1" 444 | read-all-stream "^3.0.0" 445 | readable-stream "^2.0.2" 446 | stream-combiner2 "^1.1.1" 447 | vinyl "^1.0.0" 448 | vinyl-fs "^2.2.0" 449 | ware "^1.2.0" 450 | 451 | duplexer2@^0.1.4, duplexer2@~0.1.0: 452 | version "0.1.4" 453 | resolved "http://registry.npm.taobao.org/duplexer2/download/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" 454 | dependencies: 455 | readable-stream "^2.0.2" 456 | 457 | duplexer2@0.0.2: 458 | version "0.0.2" 459 | resolved "http://registry.npm.taobao.org/duplexer2/download/duplexer2-0.0.2.tgz#c614dcf67e2fb14995a91711e5a617e8a60a31db" 460 | dependencies: 461 | readable-stream "~1.1.9" 462 | 463 | duplexify@^3.2.0: 464 | version "3.4.5" 465 | resolved "http://registry.npm.taobao.org/duplexify/download/duplexify-3.4.5.tgz#0e7e287a775af753bf57e6e7b7f21f183f6c3a53" 466 | dependencies: 467 | end-of-stream "1.0.0" 468 | inherits "^2.0.1" 469 | readable-stream "^2.0.0" 470 | stream-shift "^1.0.0" 471 | 472 | each-async@^1.0.0: 473 | version "1.1.1" 474 | resolved "http://registry.npm.taobao.org/each-async/download/each-async-1.1.1.tgz#dee5229bdf0ab6ba2012a395e1b869abf8813473" 475 | dependencies: 476 | onetime "^1.0.0" 477 | set-immediate-shim "^1.0.0" 478 | 479 | ejs@^2.3.1: 480 | version "2.5.2" 481 | resolved "http://registry.npm.taobao.org/ejs/download/ejs-2.5.2.tgz#21444ba09386f0c65b6eafb96a3d51bcb3be80d1" 482 | 483 | end-of-stream@^1.0.0: 484 | version "1.1.0" 485 | resolved "http://registry.npm.taobao.org/end-of-stream/download/end-of-stream-1.1.0.tgz#e9353258baa9108965efc41cb0ef8ade2f3cfb07" 486 | dependencies: 487 | once "~1.3.0" 488 | 489 | end-of-stream@1.0.0: 490 | version "1.0.0" 491 | resolved "http://registry.npm.taobao.org/end-of-stream/download/end-of-stream-1.0.0.tgz#d4596e702734a93e40e9af864319eabd99ff2f0e" 492 | dependencies: 493 | once "~1.3.0" 494 | 495 | entities@~1.1.1: 496 | version "1.1.1" 497 | resolved "http://registry.npm.taobao.org/entities/download/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" 498 | 499 | entities@1.0: 500 | version "1.0.0" 501 | resolved "http://registry.npm.taobao.org/entities/download/entities-1.0.0.tgz#b2987aa3821347fcde642b24fdfc9e4fb712bf26" 502 | 503 | error-ex@^1.2.0: 504 | version "1.3.0" 505 | resolved "http://registry.npm.taobao.org/error-ex/download/error-ex-1.3.0.tgz#e67b43f3e82c96ea3a584ffee0b9fc3325d802d9" 506 | dependencies: 507 | is-arrayish "^0.2.1" 508 | 509 | error@^7.0.2: 510 | version "7.0.2" 511 | resolved "http://registry.npm.taobao.org/error/download/error-7.0.2.tgz#a5f75fff4d9926126ddac0ea5dc38e689153cb02" 512 | dependencies: 513 | string-template "~0.2.1" 514 | xtend "~4.0.0" 515 | 516 | escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: 517 | version "1.0.5" 518 | resolved "http://registry.npm.taobao.org/escape-string-regexp/download/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 519 | 520 | escodegen@^1.6.0: 521 | version "1.8.1" 522 | resolved "http://registry.npm.taobao.org/escodegen/download/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018" 523 | dependencies: 524 | esprima "^2.7.1" 525 | estraverse "^1.9.1" 526 | esutils "^2.0.2" 527 | optionator "^0.8.1" 528 | optionalDependencies: 529 | source-map "~0.2.0" 530 | 531 | esprima@^2.0.0, esprima@^2.7.1: 532 | version "2.7.3" 533 | resolved "http://registry.npm.taobao.org/esprima/download/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" 534 | 535 | estraverse@^1.9.1: 536 | version "1.9.3" 537 | resolved "http://registry.npm.taobao.org/estraverse/download/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" 538 | 539 | esutils@^2.0.2: 540 | version "2.0.2" 541 | resolved "http://registry.npm.taobao.org/esutils/download/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" 542 | 543 | exit-hook@^1.0.0: 544 | version "1.1.1" 545 | resolved "http://registry.npm.taobao.org/exit-hook/download/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" 546 | 547 | expand-brackets@^0.1.4: 548 | version "0.1.5" 549 | resolved "http://registry.npm.taobao.org/expand-brackets/download/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" 550 | dependencies: 551 | is-posix-bracket "^0.1.0" 552 | 553 | expand-range@^1.8.1: 554 | version "1.8.2" 555 | resolved "http://registry.npm.taobao.org/expand-range/download/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" 556 | dependencies: 557 | fill-range "^2.1.0" 558 | 559 | extend-shallow@^2.0.1: 560 | version "2.0.1" 561 | resolved "http://registry.npm.taobao.org/extend-shallow/download/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" 562 | dependencies: 563 | is-extendable "^0.1.0" 564 | 565 | extend@^3.0.0: 566 | version "3.0.0" 567 | resolved "http://registry.npm.taobao.org/extend/download/extend-3.0.0.tgz#5a474353b9f3353ddd8176dfd37b91c83a46f1d4" 568 | 569 | external-editor@^1.1.0: 570 | version "1.1.0" 571 | resolved "http://registry.npm.taobao.org/external-editor/download/external-editor-1.1.0.tgz#c7fe15954b09af852b89aaec82a2707a0dc5597a" 572 | dependencies: 573 | extend "^3.0.0" 574 | spawn-sync "^1.0.15" 575 | temp "^0.8.3" 576 | 577 | extglob@^0.3.1: 578 | version "0.3.2" 579 | resolved "http://registry.npm.taobao.org/extglob/download/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" 580 | dependencies: 581 | is-extglob "^1.0.0" 582 | 583 | fancy-log@^1.1.0: 584 | version "1.2.0" 585 | resolved "http://registry.npm.taobao.org/fancy-log/download/fancy-log-1.2.0.tgz#d5a51b53e9ab22ca07d558f2b67ae55fdb5fcbd8" 586 | dependencies: 587 | chalk "^1.1.1" 588 | time-stamp "^1.0.0" 589 | 590 | fast-levenshtein@~2.0.4: 591 | version "2.0.5" 592 | resolved "http://registry.npm.taobao.org/fast-levenshtein/download/fast-levenshtein-2.0.5.tgz#bd33145744519ab1c36c3ee9f31f08e9079b67f2" 593 | 594 | fd-slicer@~1.0.1: 595 | version "1.0.1" 596 | resolved "http://registry.npm.taobao.org/fd-slicer/download/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65" 597 | dependencies: 598 | pend "~1.2.0" 599 | 600 | figures@^1.3.5: 601 | version "1.7.0" 602 | resolved "http://registry.npm.taobao.org/figures/download/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" 603 | dependencies: 604 | escape-string-regexp "^1.0.5" 605 | object-assign "^4.1.0" 606 | 607 | file-type@^3.1.0: 608 | version "3.8.0" 609 | resolved "http://registry.npm.taobao.org/file-type/download/file-type-3.8.0.tgz#bcadf6a8f624ebe4a10e5ad26727b6b93f16d78d" 610 | 611 | filename-regex@^2.0.0: 612 | version "2.0.0" 613 | resolved "http://registry.npm.taobao.org/filename-regex/download/filename-regex-2.0.0.tgz#996e3e80479b98b9897f15a8a58b3d084e926775" 614 | 615 | filename-reserved-regex@^1.0.0: 616 | version "1.0.0" 617 | resolved "http://registry.npm.taobao.org/filename-reserved-regex/download/filename-reserved-regex-1.0.0.tgz#e61cf805f0de1c984567d0386dc5df50ee5af7e4" 618 | 619 | filenamify@^1.0.1: 620 | version "1.2.1" 621 | resolved "http://registry.npm.taobao.org/filenamify/download/filenamify-1.2.1.tgz#a9f2ffd11c503bed300015029272378f1f1365a5" 622 | dependencies: 623 | filename-reserved-regex "^1.0.0" 624 | strip-outer "^1.0.0" 625 | trim-repeated "^1.0.0" 626 | 627 | fill-range@^2.1.0: 628 | version "2.2.3" 629 | resolved "http://registry.npm.taobao.org/fill-range/download/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723" 630 | dependencies: 631 | is-number "^2.1.0" 632 | isobject "^2.0.0" 633 | randomatic "^1.1.3" 634 | repeat-element "^1.1.2" 635 | repeat-string "^1.5.2" 636 | 637 | find-up@^1.0.0: 638 | version "1.1.2" 639 | resolved "http://registry.npm.taobao.org/find-up/download/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" 640 | dependencies: 641 | path-exists "^2.0.0" 642 | pinkie-promise "^2.0.0" 643 | 644 | first-chunk-stream@^1.0.0: 645 | version "1.0.0" 646 | resolved "http://registry.npm.taobao.org/first-chunk-stream/download/first-chunk-stream-1.0.0.tgz#59bfb50cd905f60d7c394cd3d9acaab4e6ad934e" 647 | 648 | first-chunk-stream@^2.0.0: 649 | version "2.0.0" 650 | resolved "http://registry.npm.taobao.org/first-chunk-stream/download/first-chunk-stream-2.0.0.tgz#1bdecdb8e083c0664b91945581577a43a9f31d70" 651 | dependencies: 652 | readable-stream "^2.0.2" 653 | 654 | for-in@^0.1.5: 655 | version "0.1.6" 656 | resolved "http://registry.npm.taobao.org/for-in/download/for-in-0.1.6.tgz#c9f96e89bfad18a545af5ec3ed352a1d9e5b4dc8" 657 | 658 | for-own@^0.1.3: 659 | version "0.1.4" 660 | resolved "http://registry.npm.taobao.org/for-own/download/for-own-0.1.4.tgz#0149b41a39088c7515f51ebe1c1386d45f935072" 661 | dependencies: 662 | for-in "^0.1.5" 663 | 664 | formatio@1.1.1: 665 | version "1.1.1" 666 | resolved "http://registry.npm.taobao.org/formatio/download/formatio-1.1.1.tgz#5ed3ccd636551097383465d996199100e86161e9" 667 | dependencies: 668 | samsam "~1.1" 669 | 670 | fs.realpath@^1.0.0: 671 | version "1.0.0" 672 | resolved "http://registry.npm.taobao.org/fs.realpath/download/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 673 | 674 | get-proxy@^1.0.1: 675 | version "1.1.0" 676 | resolved "http://registry.npm.taobao.org/get-proxy/download/get-proxy-1.1.0.tgz#894854491bc591b0f147d7ae570f5c678b7256eb" 677 | dependencies: 678 | rc "^1.1.2" 679 | 680 | get-stdin@^4.0.1: 681 | version "4.0.1" 682 | resolved "http://registry.npm.taobao.org/get-stdin/download/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" 683 | 684 | gh-got@^2.2.0: 685 | version "2.4.0" 686 | resolved "http://registry.npm.taobao.org/gh-got/download/gh-got-2.4.0.tgz#aa51418911ca5e4f92437114cd1209383a4aa019" 687 | dependencies: 688 | got "^5.2.0" 689 | object-assign "^4.0.1" 690 | pinkie-promise "^2.0.0" 691 | 692 | github-username@^2.0.0: 693 | version "2.1.0" 694 | resolved "http://registry.npm.taobao.org/github-username/download/github-username-2.1.0.tgz#200e5a104af42ba08a54096c708d4b6ec2fa256b" 695 | dependencies: 696 | gh-got "^2.2.0" 697 | meow "^3.5.0" 698 | 699 | glob-base@^0.3.0: 700 | version "0.3.0" 701 | resolved "http://registry.npm.taobao.org/glob-base/download/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" 702 | dependencies: 703 | glob-parent "^2.0.0" 704 | is-glob "^2.0.0" 705 | 706 | glob-parent@^2.0.0: 707 | version "2.0.0" 708 | resolved "http://registry.npm.taobao.org/glob-parent/download/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" 709 | dependencies: 710 | is-glob "^2.0.0" 711 | 712 | glob-parent@^3.0.0: 713 | version "3.0.0" 714 | resolved "http://registry.npm.taobao.org/glob-parent/download/glob-parent-3.0.0.tgz#c7bdeb5260732196c740de9274c08814056014bb" 715 | dependencies: 716 | is-glob "^3.0.0" 717 | 718 | glob-stream@^5.3.2: 719 | version "5.3.5" 720 | resolved "http://registry.npm.taobao.org/glob-stream/download/glob-stream-5.3.5.tgz#a55665a9a8ccdc41915a87c701e32d4e016fad22" 721 | dependencies: 722 | extend "^3.0.0" 723 | glob "^5.0.3" 724 | glob-parent "^3.0.0" 725 | micromatch "^2.3.7" 726 | ordered-read-streams "^0.3.0" 727 | through2 "^0.6.0" 728 | to-absolute-glob "^0.1.1" 729 | unique-stream "^2.0.2" 730 | 731 | glob@^5.0.3: 732 | version "5.0.15" 733 | resolved "http://registry.npm.taobao.org/glob/download/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" 734 | dependencies: 735 | inflight "^1.0.4" 736 | inherits "2" 737 | minimatch "2 || 3" 738 | once "^1.3.0" 739 | path-is-absolute "^1.0.0" 740 | 741 | glob@^6.0.1: 742 | version "6.0.4" 743 | resolved "http://registry.npm.taobao.org/glob/download/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" 744 | dependencies: 745 | inflight "^1.0.4" 746 | inherits "2" 747 | minimatch "2 || 3" 748 | once "^1.3.0" 749 | path-is-absolute "^1.0.0" 750 | 751 | glob@^7.0.0, glob@^7.0.3, glob@^7.0.5: 752 | version "7.1.1" 753 | resolved "http://registry.npm.taobao.org/glob/download/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" 754 | dependencies: 755 | fs.realpath "^1.0.0" 756 | inflight "^1.0.4" 757 | inherits "2" 758 | minimatch "^3.0.2" 759 | once "^1.3.0" 760 | path-is-absolute "^1.0.0" 761 | 762 | globby@^4.0.0: 763 | version "4.1.0" 764 | resolved "http://registry.npm.taobao.org/globby/download/globby-4.1.0.tgz#080f54549ec1b82a6c60e631fc82e1211dbe95f8" 765 | dependencies: 766 | array-union "^1.0.1" 767 | arrify "^1.0.0" 768 | glob "^6.0.1" 769 | object-assign "^4.0.1" 770 | pify "^2.0.0" 771 | pinkie-promise "^2.0.0" 772 | 773 | glogg@^1.0.0: 774 | version "1.0.0" 775 | resolved "http://registry.npm.taobao.org/glogg/download/glogg-1.0.0.tgz#7fe0f199f57ac906cf512feead8f90ee4a284fc5" 776 | dependencies: 777 | sparkles "^1.0.0" 778 | 779 | got@^5.0.0, got@^5.2.0: 780 | version "5.6.0" 781 | resolved "http://registry.npm.taobao.org/got/download/got-5.6.0.tgz#bb1d7ee163b78082bbc8eb836f3f395004ea6fbf" 782 | dependencies: 783 | create-error-class "^3.0.1" 784 | duplexer2 "^0.1.4" 785 | is-plain-obj "^1.0.0" 786 | is-redirect "^1.0.0" 787 | is-retry-allowed "^1.0.0" 788 | is-stream "^1.0.0" 789 | lowercase-keys "^1.0.0" 790 | node-status-codes "^1.0.0" 791 | object-assign "^4.0.1" 792 | parse-json "^2.1.0" 793 | pinkie-promise "^2.0.0" 794 | read-all-stream "^3.0.0" 795 | readable-stream "^2.0.5" 796 | timed-out "^2.0.0" 797 | unzip-response "^1.0.0" 798 | url-parse-lax "^1.0.0" 799 | 800 | graceful-fs@^4.0.0, graceful-fs@^4.1.2: 801 | version "4.1.9" 802 | resolved "http://registry.npm.taobao.org/graceful-fs/download/graceful-fs-4.1.9.tgz#baacba37d19d11f9d146d3578bc99958c3787e29" 803 | 804 | "graceful-readlink@>= 1.0.0": 805 | version "1.0.1" 806 | resolved "http://registry.npm.taobao.org/graceful-readlink/download/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" 807 | 808 | grouped-queue@^0.3.0: 809 | version "0.3.2" 810 | resolved "http://registry.npm.taobao.org/grouped-queue/download/grouped-queue-0.3.2.tgz#1005f70ece919eccbb37a318f84af99fd6c4eb5c" 811 | dependencies: 812 | lodash "^3.10.1" 813 | 814 | gruntfile-editor@^1.0.0: 815 | version "1.2.0" 816 | resolved "http://registry.npm.taobao.org/gruntfile-editor/download/gruntfile-editor-1.2.0.tgz#169cc7ff532f0b2eb900eec351f7a2bf668302d0" 817 | dependencies: 818 | ast-query "^1.0.1" 819 | lodash "^4.6.1" 820 | 821 | gulp-decompress@^1.2.0: 822 | version "1.2.0" 823 | resolved "http://registry.npm.taobao.org/gulp-decompress/download/gulp-decompress-1.2.0.tgz#8eeb65a5e015f8ed8532cafe28454960626f0dc7" 824 | dependencies: 825 | archive-type "^3.0.0" 826 | decompress "^3.0.0" 827 | gulp-util "^3.0.1" 828 | readable-stream "^2.0.2" 829 | 830 | gulp-rename@^1.2.0: 831 | version "1.2.2" 832 | resolved "http://registry.npm.taobao.org/gulp-rename/download/gulp-rename-1.2.2.tgz#3ad4428763f05e2764dec1c67d868db275687817" 833 | 834 | gulp-sourcemaps@1.6.0: 835 | version "1.6.0" 836 | resolved "http://registry.npm.taobao.org/gulp-sourcemaps/download/gulp-sourcemaps-1.6.0.tgz#b86ff349d801ceb56e1d9e7dc7bbcb4b7dee600c" 837 | dependencies: 838 | convert-source-map "^1.1.1" 839 | graceful-fs "^4.1.2" 840 | strip-bom "^2.0.0" 841 | through2 "^2.0.0" 842 | vinyl "^1.0.0" 843 | 844 | gulp-util@^3.0.1: 845 | version "3.0.7" 846 | resolved "http://registry.npm.taobao.org/gulp-util/download/gulp-util-3.0.7.tgz#78925c4b8f8b49005ac01a011c557e6218941cbb" 847 | dependencies: 848 | array-differ "^1.0.0" 849 | array-uniq "^1.0.2" 850 | beeper "^1.0.0" 851 | chalk "^1.0.0" 852 | dateformat "^1.0.11" 853 | fancy-log "^1.1.0" 854 | gulplog "^1.0.0" 855 | has-gulplog "^0.1.0" 856 | lodash._reescape "^3.0.0" 857 | lodash._reevaluate "^3.0.0" 858 | lodash._reinterpolate "^3.0.0" 859 | lodash.template "^3.0.0" 860 | minimist "^1.1.0" 861 | multipipe "^0.1.2" 862 | object-assign "^3.0.0" 863 | replace-ext "0.0.1" 864 | through2 "^2.0.0" 865 | vinyl "^0.5.0" 866 | 867 | gulplog@^1.0.0: 868 | version "1.0.0" 869 | resolved "http://registry.npm.taobao.org/gulplog/download/gulplog-1.0.0.tgz#e28c4d45d05ecbbed818363ce8f9c5926229ffe5" 870 | dependencies: 871 | glogg "^1.0.0" 872 | 873 | has-ansi@^2.0.0: 874 | version "2.0.0" 875 | resolved "http://registry.npm.taobao.org/has-ansi/download/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" 876 | dependencies: 877 | ansi-regex "^2.0.0" 878 | 879 | has-gulplog@^0.1.0: 880 | version "0.1.0" 881 | resolved "http://registry.npm.taobao.org/has-gulplog/download/has-gulplog-0.1.0.tgz#6414c82913697da51590397dafb12f22967811ce" 882 | dependencies: 883 | sparkles "^1.0.0" 884 | 885 | hosted-git-info@^2.1.4: 886 | version "2.1.5" 887 | resolved "http://registry.npm.taobao.org/hosted-git-info/download/hosted-git-info-2.1.5.tgz#0ba81d90da2e25ab34a332e6ec77936e1598118b" 888 | 889 | html-wiring@^1.0.0: 890 | version "1.2.0" 891 | resolved "http://registry.npm.taobao.org/html-wiring/download/html-wiring-1.2.0.tgz#c5f90a776e0a27241dc6df9022c37186d0270f9e" 892 | dependencies: 893 | cheerio "^0.19.0" 894 | detect-newline "^1.0.3" 895 | 896 | htmlparser2@~3.8.1: 897 | version "3.8.3" 898 | resolved "http://registry.npm.taobao.org/htmlparser2/download/htmlparser2-3.8.3.tgz#996c28b191516a8be86501a7d79757e5c70c1068" 899 | dependencies: 900 | domelementtype "1" 901 | domhandler "2.3" 902 | domutils "1.5" 903 | entities "1.0" 904 | readable-stream "1.1" 905 | 906 | indent-string@^2.1.0: 907 | version "2.1.0" 908 | resolved "http://registry.npm.taobao.org/indent-string/download/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" 909 | dependencies: 910 | repeating "^2.0.0" 911 | 912 | inflight@^1.0.4: 913 | version "1.0.6" 914 | resolved "http://registry.npm.taobao.org/inflight/download/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 915 | dependencies: 916 | once "^1.3.0" 917 | wrappy "1" 918 | 919 | inherits@^2.0.1, inherits@~2.0.1, inherits@2: 920 | version "2.0.3" 921 | resolved "http://registry.npm.taobao.org/inherits/download/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 922 | 923 | inherits@2.0.1: 924 | version "2.0.1" 925 | resolved "http://registry.npm.taobao.org/inherits/download/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" 926 | 927 | ini@~1.3.0: 928 | version "1.3.4" 929 | resolved "http://registry.npm.taobao.org/ini/download/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" 930 | 931 | inquirer@^1.0.2: 932 | version "1.2.2" 933 | resolved "http://registry.npm.taobao.org/inquirer/download/inquirer-1.2.2.tgz#f725c1316f0020e7f3d538c8c5ad0c2732c1c451" 934 | dependencies: 935 | ansi-escapes "^1.1.0" 936 | chalk "^1.0.0" 937 | cli-cursor "^1.0.1" 938 | cli-width "^2.0.0" 939 | external-editor "^1.1.0" 940 | figures "^1.3.5" 941 | lodash "^4.3.0" 942 | mute-stream "0.0.6" 943 | pinkie-promise "^2.0.0" 944 | run-async "^2.2.0" 945 | rx "^4.1.0" 946 | string-width "^1.0.1" 947 | strip-ansi "^3.0.0" 948 | through "^2.3.6" 949 | 950 | interpret@^1.0.0: 951 | version "1.0.1" 952 | resolved "http://registry.npm.taobao.org/interpret/download/interpret-1.0.1.tgz#d579fb7f693b858004947af39fa0db49f795602c" 953 | 954 | is-absolute@^0.1.5: 955 | version "0.1.7" 956 | resolved "http://registry.npm.taobao.org/is-absolute/download/is-absolute-0.1.7.tgz#847491119fccb5fb436217cc737f7faad50f603f" 957 | dependencies: 958 | is-relative "^0.1.0" 959 | 960 | is-arrayish@^0.2.1: 961 | version "0.2.1" 962 | resolved "http://registry.npm.taobao.org/is-arrayish/download/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" 963 | 964 | is-buffer@^1.0.2: 965 | version "1.1.4" 966 | resolved "http://registry.npm.taobao.org/is-buffer/download/is-buffer-1.1.4.tgz#cfc86ccd5dc5a52fa80489111c6920c457e2d98b" 967 | 968 | is-builtin-module@^1.0.0: 969 | version "1.0.0" 970 | resolved "http://registry.npm.taobao.org/is-builtin-module/download/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" 971 | dependencies: 972 | builtin-modules "^1.0.0" 973 | 974 | is-bzip2@^1.0.0: 975 | version "1.0.0" 976 | resolved "http://registry.npm.taobao.org/is-bzip2/download/is-bzip2-1.0.0.tgz#5ee58eaa5a2e9c80e21407bedf23ae5ac091b3fc" 977 | 978 | is-dotfile@^1.0.0: 979 | version "1.0.2" 980 | resolved "http://registry.npm.taobao.org/is-dotfile/download/is-dotfile-1.0.2.tgz#2c132383f39199f8edc268ca01b9b007d205cc4d" 981 | 982 | is-equal-shallow@^0.1.3: 983 | version "0.1.3" 984 | resolved "http://registry.npm.taobao.org/is-equal-shallow/download/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" 985 | dependencies: 986 | is-primitive "^2.0.0" 987 | 988 | is-extendable@^0.1.0, is-extendable@^0.1.1: 989 | version "0.1.1" 990 | resolved "http://registry.npm.taobao.org/is-extendable/download/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" 991 | 992 | is-extglob@^1.0.0: 993 | version "1.0.0" 994 | resolved "http://registry.npm.taobao.org/is-extglob/download/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" 995 | 996 | is-extglob@^2.1.0: 997 | version "2.1.0" 998 | resolved "http://registry.npm.taobao.org/is-extglob/download/is-extglob-2.1.0.tgz#33411a482b046bf95e6b0cb27ee2711af4cf15ad" 999 | 1000 | is-finite@^1.0.0: 1001 | version "1.0.2" 1002 | resolved "http://registry.npm.taobao.org/is-finite/download/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" 1003 | dependencies: 1004 | number-is-nan "^1.0.0" 1005 | 1006 | is-fullwidth-code-point@^1.0.0: 1007 | version "1.0.0" 1008 | resolved "http://registry.npm.taobao.org/is-fullwidth-code-point/download/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" 1009 | dependencies: 1010 | number-is-nan "^1.0.0" 1011 | 1012 | is-glob@^2.0.0, is-glob@^2.0.1: 1013 | version "2.0.1" 1014 | resolved "http://registry.npm.taobao.org/is-glob/download/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" 1015 | dependencies: 1016 | is-extglob "^1.0.0" 1017 | 1018 | is-glob@^3.0.0: 1019 | version "3.1.0" 1020 | resolved "http://registry.npm.taobao.org/is-glob/download/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" 1021 | dependencies: 1022 | is-extglob "^2.1.0" 1023 | 1024 | is-gzip@^1.0.0: 1025 | version "1.0.0" 1026 | resolved "http://registry.npm.taobao.org/is-gzip/download/is-gzip-1.0.0.tgz#6ca8b07b99c77998025900e555ced8ed80879a83" 1027 | 1028 | is-natural-number@^2.0.0: 1029 | version "2.1.1" 1030 | resolved "http://registry.npm.taobao.org/is-natural-number/download/is-natural-number-2.1.1.tgz#7d4c5728377ef386c3e194a9911bf57c6dc335e7" 1031 | 1032 | is-number@^2.0.2, is-number@^2.1.0: 1033 | version "2.1.0" 1034 | resolved "http://registry.npm.taobao.org/is-number/download/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" 1035 | dependencies: 1036 | kind-of "^3.0.2" 1037 | 1038 | is-obj@^1.0.0: 1039 | version "1.0.1" 1040 | resolved "http://registry.npm.taobao.org/is-obj/download/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" 1041 | 1042 | is-plain-obj@^1.0.0: 1043 | version "1.1.0" 1044 | resolved "http://registry.npm.taobao.org/is-plain-obj/download/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" 1045 | 1046 | is-posix-bracket@^0.1.0: 1047 | version "0.1.1" 1048 | resolved "http://registry.npm.taobao.org/is-posix-bracket/download/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" 1049 | 1050 | is-primitive@^2.0.0: 1051 | version "2.0.0" 1052 | resolved "http://registry.npm.taobao.org/is-primitive/download/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" 1053 | 1054 | is-promise@^2.1.0: 1055 | version "2.1.0" 1056 | resolved "http://registry.npm.taobao.org/is-promise/download/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" 1057 | 1058 | is-redirect@^1.0.0: 1059 | version "1.0.0" 1060 | resolved "http://registry.npm.taobao.org/is-redirect/download/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24" 1061 | 1062 | is-relative@^0.1.0: 1063 | version "0.1.3" 1064 | resolved "http://registry.npm.taobao.org/is-relative/download/is-relative-0.1.3.tgz#905fee8ae86f45b3ec614bc3c15c869df0876e82" 1065 | 1066 | is-retry-allowed@^1.0.0: 1067 | version "1.1.0" 1068 | resolved "http://registry.npm.taobao.org/is-retry-allowed/download/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" 1069 | 1070 | is-stream@^1.0.0, is-stream@^1.0.1: 1071 | version "1.1.0" 1072 | resolved "http://registry.npm.taobao.org/is-stream/download/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" 1073 | 1074 | is-tar@^1.0.0: 1075 | version "1.0.0" 1076 | resolved "http://registry.npm.taobao.org/is-tar/download/is-tar-1.0.0.tgz#2f6b2e1792c1f5bb36519acaa9d65c0d26fe853d" 1077 | 1078 | is-url@^1.2.0: 1079 | version "1.2.2" 1080 | resolved "http://registry.npm.taobao.org/is-url/download/is-url-1.2.2.tgz#498905a593bf47cc2d9e7f738372bbf7696c7f26" 1081 | 1082 | is-utf8@^0.2.0: 1083 | version "0.2.1" 1084 | resolved "http://registry.npm.taobao.org/is-utf8/download/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" 1085 | 1086 | is-valid-glob@^0.3.0: 1087 | version "0.3.0" 1088 | resolved "http://registry.npm.taobao.org/is-valid-glob/download/is-valid-glob-0.3.0.tgz#d4b55c69f51886f9b65c70d6c2622d37e29f48fe" 1089 | 1090 | is-zip@^1.0.0: 1091 | version "1.0.0" 1092 | resolved "http://registry.npm.taobao.org/is-zip/download/is-zip-1.0.0.tgz#47b0a8ff4d38a76431ccfd99a8e15a4c86ba2325" 1093 | 1094 | isarray@~1.0.0, isarray@1.0.0: 1095 | version "1.0.0" 1096 | resolved "http://registry.npm.taobao.org/isarray/download/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 1097 | 1098 | isarray@0.0.1: 1099 | version "0.0.1" 1100 | resolved "http://registry.npm.taobao.org/isarray/download/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" 1101 | 1102 | isexe@^1.1.1: 1103 | version "1.1.2" 1104 | resolved "http://registry.npm.taobao.org/isexe/download/isexe-1.1.2.tgz#36f3e22e60750920f5e7241a476a8c6a42275ad0" 1105 | 1106 | isobject@^2.0.0: 1107 | version "2.1.0" 1108 | resolved "http://registry.npm.taobao.org/isobject/download/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" 1109 | dependencies: 1110 | isarray "1.0.0" 1111 | 1112 | istextorbinary@^1.0.2: 1113 | version "1.0.2" 1114 | resolved "http://registry.npm.taobao.org/istextorbinary/download/istextorbinary-1.0.2.tgz#ace19354d1a9a0173efeb1084ce0f87b0ad7decf" 1115 | dependencies: 1116 | binaryextensions "~1.0.0" 1117 | textextensions "~1.0.0" 1118 | 1119 | json-stable-stringify@^1.0.0: 1120 | version "1.0.1" 1121 | resolved "http://registry.npm.taobao.org/json-stable-stringify/download/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" 1122 | dependencies: 1123 | jsonify "~0.0.0" 1124 | 1125 | jsonify@~0.0.0: 1126 | version "0.0.0" 1127 | resolved "http://registry.npm.taobao.org/jsonify/download/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" 1128 | 1129 | kind-of@^3.0.2: 1130 | version "3.0.4" 1131 | resolved "http://registry.npm.taobao.org/kind-of/download/kind-of-3.0.4.tgz#7b8ecf18a4e17f8269d73b501c9f232c96887a74" 1132 | dependencies: 1133 | is-buffer "^1.0.2" 1134 | 1135 | lazystream@^1.0.0: 1136 | version "1.0.0" 1137 | resolved "http://registry.npm.taobao.org/lazystream/download/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4" 1138 | dependencies: 1139 | readable-stream "^2.0.5" 1140 | 1141 | levn@~0.3.0: 1142 | version "0.3.0" 1143 | resolved "http://registry.npm.taobao.org/levn/download/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" 1144 | dependencies: 1145 | prelude-ls "~1.1.2" 1146 | type-check "~0.3.2" 1147 | 1148 | load-json-file@^1.0.0: 1149 | version "1.1.0" 1150 | resolved "http://registry.npm.taobao.org/load-json-file/download/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" 1151 | dependencies: 1152 | graceful-fs "^4.1.2" 1153 | parse-json "^2.2.0" 1154 | pify "^2.0.0" 1155 | pinkie-promise "^2.0.0" 1156 | strip-bom "^2.0.0" 1157 | 1158 | lodash._basecopy@^3.0.0: 1159 | version "3.0.1" 1160 | resolved "http://registry.npm.taobao.org/lodash._basecopy/download/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" 1161 | 1162 | lodash._basetostring@^3.0.0: 1163 | version "3.0.1" 1164 | resolved "http://registry.npm.taobao.org/lodash._basetostring/download/lodash._basetostring-3.0.1.tgz#d1861d877f824a52f669832dcaf3ee15566a07d5" 1165 | 1166 | lodash._basevalues@^3.0.0: 1167 | version "3.0.0" 1168 | resolved "http://registry.npm.taobao.org/lodash._basevalues/download/lodash._basevalues-3.0.0.tgz#5b775762802bde3d3297503e26300820fdf661b7" 1169 | 1170 | lodash._getnative@^3.0.0: 1171 | version "3.9.1" 1172 | resolved "http://registry.npm.taobao.org/lodash._getnative/download/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" 1173 | 1174 | lodash._isiterateecall@^3.0.0: 1175 | version "3.0.9" 1176 | resolved "http://registry.npm.taobao.org/lodash._isiterateecall/download/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" 1177 | 1178 | lodash._reescape@^3.0.0: 1179 | version "3.0.0" 1180 | resolved "http://registry.npm.taobao.org/lodash._reescape/download/lodash._reescape-3.0.0.tgz#2b1d6f5dfe07c8a355753e5f27fac7f1cde1616a" 1181 | 1182 | lodash._reevaluate@^3.0.0: 1183 | version "3.0.0" 1184 | resolved "http://registry.npm.taobao.org/lodash._reevaluate/download/lodash._reevaluate-3.0.0.tgz#58bc74c40664953ae0b124d806996daca431e2ed" 1185 | 1186 | lodash._reinterpolate@^3.0.0: 1187 | version "3.0.0" 1188 | resolved "http://registry.npm.taobao.org/lodash._reinterpolate/download/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" 1189 | 1190 | lodash._root@^3.0.0: 1191 | version "3.0.1" 1192 | resolved "http://registry.npm.taobao.org/lodash._root/download/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692" 1193 | 1194 | lodash.escape@^3.0.0: 1195 | version "3.2.0" 1196 | resolved "http://registry.npm.taobao.org/lodash.escape/download/lodash.escape-3.2.0.tgz#995ee0dc18c1b48cc92effae71a10aab5b487698" 1197 | dependencies: 1198 | lodash._root "^3.0.0" 1199 | 1200 | lodash.isarguments@^3.0.0: 1201 | version "3.1.0" 1202 | resolved "http://registry.npm.taobao.org/lodash.isarguments/download/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" 1203 | 1204 | lodash.isarray@^3.0.0: 1205 | version "3.0.4" 1206 | resolved "http://registry.npm.taobao.org/lodash.isarray/download/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" 1207 | 1208 | lodash.isequal@^4.0.0: 1209 | version "4.4.0" 1210 | resolved "http://registry.npm.taobao.org/lodash.isequal/download/lodash.isequal-4.4.0.tgz#6295768e98e14dc15ce8d362ef6340db82852031" 1211 | 1212 | lodash.keys@^3.0.0: 1213 | version "3.1.2" 1214 | resolved "http://registry.npm.taobao.org/lodash.keys/download/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" 1215 | dependencies: 1216 | lodash._getnative "^3.0.0" 1217 | lodash.isarguments "^3.0.0" 1218 | lodash.isarray "^3.0.0" 1219 | 1220 | lodash.restparam@^3.0.0: 1221 | version "3.6.1" 1222 | resolved "http://registry.npm.taobao.org/lodash.restparam/download/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" 1223 | 1224 | lodash.template@^3.0.0: 1225 | version "3.6.2" 1226 | resolved "http://registry.npm.taobao.org/lodash.template/download/lodash.template-3.6.2.tgz#f8cdecc6169a255be9098ae8b0c53d378931d14f" 1227 | dependencies: 1228 | lodash._basecopy "^3.0.0" 1229 | lodash._basetostring "^3.0.0" 1230 | lodash._basevalues "^3.0.0" 1231 | lodash._isiterateecall "^3.0.0" 1232 | lodash._reinterpolate "^3.0.0" 1233 | lodash.escape "^3.0.0" 1234 | lodash.keys "^3.0.0" 1235 | lodash.restparam "^3.0.0" 1236 | lodash.templatesettings "^3.0.0" 1237 | 1238 | lodash.templatesettings@^3.0.0: 1239 | version "3.1.1" 1240 | resolved "http://registry.npm.taobao.org/lodash.templatesettings/download/lodash.templatesettings-3.1.1.tgz#fb307844753b66b9f1afa54e262c745307dba8e5" 1241 | dependencies: 1242 | lodash._reinterpolate "^3.0.0" 1243 | lodash.escape "^3.0.0" 1244 | 1245 | lodash@^3.10.1, lodash@^3.2.0, lodash@^3.6.0: 1246 | version "3.10.1" 1247 | resolved "http://registry.npm.taobao.org/lodash/download/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" 1248 | 1249 | lodash@^4.11.1, lodash@^4.3.0, lodash@^4.6.1: 1250 | version "4.16.4" 1251 | resolved "http://registry.npm.taobao.org/lodash/download/lodash-4.16.4.tgz#01ce306b9bad1319f2a5528674f88297aeb70127" 1252 | 1253 | log-symbols@^1.0.1: 1254 | version "1.0.2" 1255 | resolved "http://registry.npm.taobao.org/log-symbols/download/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" 1256 | dependencies: 1257 | chalk "^1.0.0" 1258 | 1259 | lolex@1.3.2: 1260 | version "1.3.2" 1261 | resolved "http://registry.npm.taobao.org/lolex/download/lolex-1.3.2.tgz#7c3da62ffcb30f0f5a80a2566ca24e45d8a01f31" 1262 | 1263 | loud-rejection@^1.0.0: 1264 | version "1.6.0" 1265 | resolved "http://registry.npm.taobao.org/loud-rejection/download/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" 1266 | dependencies: 1267 | currently-unhandled "^0.4.1" 1268 | signal-exit "^3.0.0" 1269 | 1270 | lowercase-keys@^1.0.0: 1271 | version "1.0.0" 1272 | resolved "http://registry.npm.taobao.org/lowercase-keys/download/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" 1273 | 1274 | lru-cache@^4.0.1: 1275 | version "4.0.1" 1276 | resolved "http://registry.npm.taobao.org/lru-cache/download/lru-cache-4.0.1.tgz#1343955edaf2e37d9b9e7ee7241e27c4b9fb72be" 1277 | dependencies: 1278 | pseudomap "^1.0.1" 1279 | yallist "^2.0.0" 1280 | 1281 | map-obj@^1.0.0, map-obj@^1.0.1: 1282 | version "1.0.1" 1283 | resolved "http://registry.npm.taobao.org/map-obj/download/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" 1284 | 1285 | mem-fs-editor@^2.0.0: 1286 | version "2.3.0" 1287 | resolved "http://registry.npm.taobao.org/mem-fs-editor/download/mem-fs-editor-2.3.0.tgz#42a0ae1f55e76fd03f09e7c7b15b6307bdf5cb13" 1288 | dependencies: 1289 | commondir "^1.0.1" 1290 | deep-extend "^0.4.0" 1291 | ejs "^2.3.1" 1292 | glob "^7.0.3" 1293 | globby "^4.0.0" 1294 | mkdirp "^0.5.0" 1295 | multimatch "^2.0.0" 1296 | rimraf "^2.2.8" 1297 | through2 "^2.0.0" 1298 | vinyl "^1.1.0" 1299 | 1300 | mem-fs@^1.1.0: 1301 | version "1.1.3" 1302 | resolved "http://registry.npm.taobao.org/mem-fs/download/mem-fs-1.1.3.tgz#b8ae8d2e3fcb6f5d3f9165c12d4551a065d989cc" 1303 | dependencies: 1304 | through2 "^2.0.0" 1305 | vinyl "^1.1.0" 1306 | vinyl-file "^2.0.0" 1307 | 1308 | meow@^3.3.0, meow@^3.5.0: 1309 | version "3.7.0" 1310 | resolved "http://registry.npm.taobao.org/meow/download/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" 1311 | dependencies: 1312 | camelcase-keys "^2.0.0" 1313 | decamelize "^1.1.2" 1314 | loud-rejection "^1.0.0" 1315 | map-obj "^1.0.1" 1316 | minimist "^1.1.3" 1317 | normalize-package-data "^2.3.4" 1318 | object-assign "^4.0.1" 1319 | read-pkg-up "^1.0.1" 1320 | redent "^1.0.0" 1321 | trim-newlines "^1.0.0" 1322 | 1323 | merge-stream@^1.0.0: 1324 | version "1.0.0" 1325 | resolved "http://registry.npm.taobao.org/merge-stream/download/merge-stream-1.0.0.tgz#9cfd156fef35421e2b5403ce11dc6eb1962b026e" 1326 | dependencies: 1327 | readable-stream "^2.0.1" 1328 | 1329 | micromatch@^2.3.7: 1330 | version "2.3.11" 1331 | resolved "http://registry.npm.taobao.org/micromatch/download/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" 1332 | dependencies: 1333 | arr-diff "^2.0.0" 1334 | array-unique "^0.2.1" 1335 | braces "^1.8.2" 1336 | expand-brackets "^0.1.4" 1337 | extglob "^0.3.1" 1338 | filename-regex "^2.0.0" 1339 | is-extglob "^1.0.0" 1340 | is-glob "^2.0.1" 1341 | kind-of "^3.0.2" 1342 | normalize-path "^2.0.1" 1343 | object.omit "^2.0.0" 1344 | parse-glob "^3.0.4" 1345 | regex-cache "^0.4.2" 1346 | 1347 | minimatch@^3.0.0, minimatch@^3.0.2, "minimatch@2 || 3": 1348 | version "3.0.3" 1349 | resolved "http://registry.npm.taobao.org/minimatch/download/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774" 1350 | dependencies: 1351 | brace-expansion "^1.0.0" 1352 | 1353 | minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0: 1354 | version "1.2.0" 1355 | resolved "http://registry.npm.taobao.org/minimist/download/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" 1356 | 1357 | minimist@0.0.8: 1358 | version "0.0.8" 1359 | resolved "http://registry.npm.taobao.org/minimist/download/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 1360 | 1361 | mkdirp@^0.5.0, mkdirp@^0.5.1: 1362 | version "0.5.1" 1363 | resolved "http://registry.npm.taobao.org/mkdirp/download/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 1364 | dependencies: 1365 | minimist "0.0.8" 1366 | 1367 | ms@0.7.1: 1368 | version "0.7.1" 1369 | resolved "http://registry.npm.taobao.org/ms/download/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" 1370 | 1371 | multimatch@^2.0.0: 1372 | version "2.1.0" 1373 | resolved "http://registry.npm.taobao.org/multimatch/download/multimatch-2.1.0.tgz#9c7906a22fb4c02919e2f5f75161b4cdbd4b2a2b" 1374 | dependencies: 1375 | array-differ "^1.0.0" 1376 | array-union "^1.0.1" 1377 | arrify "^1.0.0" 1378 | minimatch "^3.0.0" 1379 | 1380 | multipipe@^0.1.2: 1381 | version "0.1.2" 1382 | resolved "http://registry.npm.taobao.org/multipipe/download/multipipe-0.1.2.tgz#2a8f2ddf70eed564dff2d57f1e1a137d9f05078b" 1383 | dependencies: 1384 | duplexer2 "0.0.2" 1385 | 1386 | mute-stream@0.0.6: 1387 | version "0.0.6" 1388 | resolved "http://registry.npm.taobao.org/mute-stream/download/mute-stream-0.0.6.tgz#48962b19e169fd1dfc240b3f1e7317627bbc47db" 1389 | 1390 | node-status-codes@^1.0.0: 1391 | version "1.0.0" 1392 | resolved "http://registry.npm.taobao.org/node-status-codes/download/node-status-codes-1.0.0.tgz#5ae5541d024645d32a58fcddc9ceecea7ae3ac2f" 1393 | 1394 | nopt@^3.0.0: 1395 | version "3.0.6" 1396 | resolved "http://registry.npm.taobao.org/nopt/download/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" 1397 | dependencies: 1398 | abbrev "1" 1399 | 1400 | normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: 1401 | version "2.3.5" 1402 | resolved "http://registry.npm.taobao.org/normalize-package-data/download/normalize-package-data-2.3.5.tgz#8d924f142960e1777e7ffe170543631cc7cb02df" 1403 | dependencies: 1404 | hosted-git-info "^2.1.4" 1405 | is-builtin-module "^1.0.0" 1406 | semver "2 || 3 || 4 || 5" 1407 | validate-npm-package-license "^3.0.1" 1408 | 1409 | normalize-path@^2.0.1: 1410 | version "2.0.1" 1411 | resolved "http://registry.npm.taobao.org/normalize-path/download/normalize-path-2.0.1.tgz#47886ac1662760d4261b7d979d241709d3ce3f7a" 1412 | 1413 | nth-check@~1.0.0: 1414 | version "1.0.1" 1415 | resolved "http://registry.npm.taobao.org/nth-check/download/nth-check-1.0.1.tgz#9929acdf628fc2c41098deab82ac580cf149aae4" 1416 | dependencies: 1417 | boolbase "~1.0.0" 1418 | 1419 | number-is-nan@^1.0.0: 1420 | version "1.0.1" 1421 | resolved "http://registry.npm.taobao.org/number-is-nan/download/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" 1422 | 1423 | object-assign@^2.0.0: 1424 | version "2.1.1" 1425 | resolved "http://registry.npm.taobao.org/object-assign/download/object-assign-2.1.1.tgz#43c36e5d569ff8e4816c4efa8be02d26967c18aa" 1426 | 1427 | object-assign@^3.0.0: 1428 | version "3.0.0" 1429 | resolved "http://registry.npm.taobao.org/object-assign/download/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" 1430 | 1431 | object-assign@^4.0.0, object-assign@^4.0.1, object-assign@^4.1.0: 1432 | version "4.1.0" 1433 | resolved "http://registry.npm.taobao.org/object-assign/download/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" 1434 | 1435 | object.omit@^2.0.0: 1436 | version "2.0.0" 1437 | resolved "http://registry.npm.taobao.org/object.omit/download/object.omit-2.0.0.tgz#868597333d54e60662940bb458605dd6ae12fe94" 1438 | dependencies: 1439 | for-own "^0.1.3" 1440 | is-extendable "^0.1.1" 1441 | 1442 | once@^1.3.0: 1443 | version "1.4.0" 1444 | resolved "http://registry.npm.taobao.org/once/download/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 1445 | dependencies: 1446 | wrappy "1" 1447 | 1448 | once@~1.3.0: 1449 | version "1.3.3" 1450 | resolved "http://registry.npm.taobao.org/once/download/once-1.3.3.tgz#b2e261557ce4c314ec8304f3fa82663e4297ca20" 1451 | dependencies: 1452 | wrappy "1" 1453 | 1454 | onetime@^1.0.0: 1455 | version "1.1.0" 1456 | resolved "http://registry.npm.taobao.org/onetime/download/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" 1457 | 1458 | optionator@^0.8.1: 1459 | version "0.8.2" 1460 | resolved "http://registry.npm.taobao.org/optionator/download/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" 1461 | dependencies: 1462 | deep-is "~0.1.3" 1463 | fast-levenshtein "~2.0.4" 1464 | levn "~0.3.0" 1465 | prelude-ls "~1.1.2" 1466 | type-check "~0.3.2" 1467 | wordwrap "~1.0.0" 1468 | 1469 | ordered-read-streams@^0.3.0: 1470 | version "0.3.0" 1471 | resolved "http://registry.npm.taobao.org/ordered-read-streams/download/ordered-read-streams-0.3.0.tgz#7137e69b3298bb342247a1bbee3881c80e2fd78b" 1472 | dependencies: 1473 | is-stream "^1.0.1" 1474 | readable-stream "^2.0.1" 1475 | 1476 | os-homedir@^1.0.0: 1477 | version "1.0.2" 1478 | resolved "http://registry.npm.taobao.org/os-homedir/download/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" 1479 | 1480 | os-shim@^0.1.2: 1481 | version "0.1.3" 1482 | resolved "http://registry.npm.taobao.org/os-shim/download/os-shim-0.1.3.tgz#6b62c3791cf7909ea35ed46e17658bb417cb3917" 1483 | 1484 | os-tmpdir@^1.0.0: 1485 | version "1.0.2" 1486 | resolved "http://registry.npm.taobao.org/os-tmpdir/download/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" 1487 | 1488 | parse-glob@^3.0.4: 1489 | version "3.0.4" 1490 | resolved "http://registry.npm.taobao.org/parse-glob/download/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" 1491 | dependencies: 1492 | glob-base "^0.3.0" 1493 | is-dotfile "^1.0.0" 1494 | is-extglob "^1.0.0" 1495 | is-glob "^2.0.0" 1496 | 1497 | parse-json@^2.1.0, parse-json@^2.2.0: 1498 | version "2.2.0" 1499 | resolved "http://registry.npm.taobao.org/parse-json/download/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" 1500 | dependencies: 1501 | error-ex "^1.2.0" 1502 | 1503 | path-exists@^2.0.0, path-exists@^2.1.0: 1504 | version "2.1.0" 1505 | resolved "http://registry.npm.taobao.org/path-exists/download/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" 1506 | dependencies: 1507 | pinkie-promise "^2.0.0" 1508 | 1509 | path-is-absolute@^1.0.0: 1510 | version "1.0.1" 1511 | resolved "http://registry.npm.taobao.org/path-is-absolute/download/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 1512 | 1513 | path-type@^1.0.0: 1514 | version "1.1.0" 1515 | resolved "http://registry.npm.taobao.org/path-type/download/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" 1516 | dependencies: 1517 | graceful-fs "^4.1.2" 1518 | pify "^2.0.0" 1519 | pinkie-promise "^2.0.0" 1520 | 1521 | pend@~1.2.0: 1522 | version "1.2.0" 1523 | resolved "http://registry.npm.taobao.org/pend/download/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" 1524 | 1525 | pify@^2.0.0, pify@^2.3.0: 1526 | version "2.3.0" 1527 | resolved "http://registry.npm.taobao.org/pify/download/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" 1528 | 1529 | pinkie-promise@^2.0.0, pinkie-promise@^2.0.1: 1530 | version "2.0.1" 1531 | resolved "http://registry.npm.taobao.org/pinkie-promise/download/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" 1532 | dependencies: 1533 | pinkie "^2.0.0" 1534 | 1535 | pinkie@^2.0.0: 1536 | version "2.0.4" 1537 | resolved "http://registry.npm.taobao.org/pinkie/download/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" 1538 | 1539 | prelude-ls@~1.1.2: 1540 | version "1.1.2" 1541 | resolved "http://registry.npm.taobao.org/prelude-ls/download/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" 1542 | 1543 | prepend-http@^1.0.1: 1544 | version "1.0.4" 1545 | resolved "http://registry.npm.taobao.org/prepend-http/download/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" 1546 | 1547 | preserve@^0.2.0: 1548 | version "0.2.0" 1549 | resolved "http://registry.npm.taobao.org/preserve/download/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" 1550 | 1551 | pretty-bytes@^3.0.1: 1552 | version "3.0.1" 1553 | resolved "http://registry.npm.taobao.org/pretty-bytes/download/pretty-bytes-3.0.1.tgz#27d0008d778063a0b4811bb35c79f1bd5d5fbccf" 1554 | dependencies: 1555 | number-is-nan "^1.0.0" 1556 | 1557 | process-nextick-args@~1.0.6: 1558 | version "1.0.7" 1559 | resolved "http://registry.npm.taobao.org/process-nextick-args/download/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" 1560 | 1561 | pseudomap@^1.0.1: 1562 | version "1.0.2" 1563 | resolved "http://registry.npm.taobao.org/pseudomap/download/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" 1564 | 1565 | randomatic@^1.1.3: 1566 | version "1.1.5" 1567 | resolved "http://registry.npm.taobao.org/randomatic/download/randomatic-1.1.5.tgz#5e9ef5f2d573c67bd2b8124ae90b5156e457840b" 1568 | dependencies: 1569 | is-number "^2.0.2" 1570 | kind-of "^3.0.2" 1571 | 1572 | rc@^1.1.2: 1573 | version "1.1.6" 1574 | resolved "http://registry.npm.taobao.org/rc/download/rc-1.1.6.tgz#43651b76b6ae53b5c802f1151fa3fc3b059969c9" 1575 | dependencies: 1576 | deep-extend "~0.4.0" 1577 | ini "~1.3.0" 1578 | minimist "^1.2.0" 1579 | strip-json-comments "~1.0.4" 1580 | 1581 | read-all-stream@^3.0.0: 1582 | version "3.1.0" 1583 | resolved "http://registry.npm.taobao.org/read-all-stream/download/read-all-stream-3.1.0.tgz#35c3e177f2078ef789ee4bfafa4373074eaef4fa" 1584 | dependencies: 1585 | pinkie-promise "^2.0.0" 1586 | readable-stream "^2.0.0" 1587 | 1588 | read-chunk@^1.0.1: 1589 | version "1.0.1" 1590 | resolved "http://registry.npm.taobao.org/read-chunk/download/read-chunk-1.0.1.tgz#5f68cab307e663f19993527d9b589cace4661194" 1591 | 1592 | read-pkg-up@^1.0.1: 1593 | version "1.0.1" 1594 | resolved "http://registry.npm.taobao.org/read-pkg-up/download/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" 1595 | dependencies: 1596 | find-up "^1.0.0" 1597 | read-pkg "^1.0.0" 1598 | 1599 | read-pkg@^1.0.0: 1600 | version "1.1.0" 1601 | resolved "http://registry.npm.taobao.org/read-pkg/download/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" 1602 | dependencies: 1603 | load-json-file "^1.0.0" 1604 | normalize-package-data "^2.3.2" 1605 | path-type "^1.0.0" 1606 | 1607 | readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.5: 1608 | version "2.1.5" 1609 | resolved "http://registry.npm.taobao.org/readable-stream/download/readable-stream-2.1.5.tgz#66fa8b720e1438b364681f2ad1a63c618448c9d0" 1610 | dependencies: 1611 | buffer-shims "^1.0.0" 1612 | core-util-is "~1.0.0" 1613 | inherits "~2.0.1" 1614 | isarray "~1.0.0" 1615 | process-nextick-args "~1.0.6" 1616 | string_decoder "~0.10.x" 1617 | util-deprecate "~1.0.1" 1618 | 1619 | "readable-stream@>=1.0.33-1 <1.1.0-0": 1620 | version "1.0.34" 1621 | resolved "http://registry.npm.taobao.org/readable-stream/download/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" 1622 | dependencies: 1623 | core-util-is "~1.0.0" 1624 | inherits "~2.0.1" 1625 | isarray "0.0.1" 1626 | string_decoder "~0.10.x" 1627 | 1628 | readable-stream@~1.1.9: 1629 | version "1.1.14" 1630 | resolved "http://registry.npm.taobao.org/readable-stream/download/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" 1631 | dependencies: 1632 | core-util-is "~1.0.0" 1633 | inherits "~2.0.1" 1634 | isarray "0.0.1" 1635 | string_decoder "~0.10.x" 1636 | 1637 | readable-stream@~2.0.0, readable-stream@~2.0.5: 1638 | version "2.0.6" 1639 | resolved "http://registry.npm.taobao.org/readable-stream/download/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" 1640 | dependencies: 1641 | core-util-is "~1.0.0" 1642 | inherits "~2.0.1" 1643 | isarray "~1.0.0" 1644 | process-nextick-args "~1.0.6" 1645 | string_decoder "~0.10.x" 1646 | util-deprecate "~1.0.1" 1647 | 1648 | readable-stream@1.1: 1649 | version "1.1.13" 1650 | resolved "http://registry.npm.taobao.org/readable-stream/download/readable-stream-1.1.13.tgz#f6eef764f514c89e2b9e23146a75ba106756d23e" 1651 | dependencies: 1652 | core-util-is "~1.0.0" 1653 | inherits "~2.0.1" 1654 | isarray "0.0.1" 1655 | string_decoder "~0.10.x" 1656 | 1657 | rechoir@^0.6.2: 1658 | version "0.6.2" 1659 | resolved "http://registry.npm.taobao.org/rechoir/download/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" 1660 | dependencies: 1661 | resolve "^1.1.6" 1662 | 1663 | redent@^1.0.0: 1664 | version "1.0.0" 1665 | resolved "http://registry.npm.taobao.org/redent/download/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" 1666 | dependencies: 1667 | indent-string "^2.1.0" 1668 | strip-indent "^1.0.1" 1669 | 1670 | regex-cache@^0.4.2: 1671 | version "0.4.3" 1672 | resolved "http://registry.npm.taobao.org/regex-cache/download/regex-cache-0.4.3.tgz#9b1a6c35d4d0dfcef5711ae651e8e9d3d7114145" 1673 | dependencies: 1674 | is-equal-shallow "^0.1.3" 1675 | is-primitive "^2.0.0" 1676 | 1677 | repeat-element@^1.1.2: 1678 | version "1.1.2" 1679 | resolved "http://registry.npm.taobao.org/repeat-element/download/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" 1680 | 1681 | repeat-string@^1.5.2: 1682 | version "1.5.4" 1683 | resolved "http://registry.npm.taobao.org/repeat-string/download/repeat-string-1.5.4.tgz#64ec0c91e0f4b475f90d5b643651e3e6e5b6c2d5" 1684 | 1685 | repeating@^2.0.0: 1686 | version "2.0.1" 1687 | resolved "http://registry.npm.taobao.org/repeating/download/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" 1688 | dependencies: 1689 | is-finite "^1.0.0" 1690 | 1691 | replace-ext@0.0.1: 1692 | version "0.0.1" 1693 | resolved "http://registry.npm.taobao.org/replace-ext/download/replace-ext-0.0.1.tgz#29bbd92078a739f0bcce2b4ee41e837953522924" 1694 | 1695 | resolve@^1.1.6: 1696 | version "1.1.7" 1697 | resolved "http://registry.npm.taobao.org/resolve/download/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" 1698 | 1699 | restore-cursor@^1.0.1: 1700 | version "1.0.1" 1701 | resolved "http://registry.npm.taobao.org/restore-cursor/download/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" 1702 | dependencies: 1703 | exit-hook "^1.0.0" 1704 | onetime "^1.0.0" 1705 | 1706 | rimraf@^2.2.0, rimraf@^2.2.8, rimraf@^2.4.4: 1707 | version "2.5.4" 1708 | resolved "http://registry.npm.taobao.org/rimraf/download/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04" 1709 | dependencies: 1710 | glob "^7.0.5" 1711 | 1712 | rimraf@~2.2.6: 1713 | version "2.2.8" 1714 | resolved "http://registry.npm.taobao.org/rimraf/download/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" 1715 | 1716 | run-async@^2.0.0, run-async@^2.2.0: 1717 | version "2.2.0" 1718 | resolved "http://registry.npm.taobao.org/run-async/download/run-async-2.2.0.tgz#8783abd83c7bb86f41ee0602fc82404b3bd6e8b9" 1719 | dependencies: 1720 | is-promise "^2.1.0" 1721 | pinkie-promise "^2.0.0" 1722 | 1723 | rx@^4.1.0: 1724 | version "4.1.0" 1725 | resolved "http://registry.npm.taobao.org/rx/download/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782" 1726 | 1727 | samsam@~1.1: 1728 | version "1.1.3" 1729 | resolved "http://registry.npm.taobao.org/samsam/download/samsam-1.1.3.tgz#9f5087419b4d091f232571e7fa52e90b0f552621" 1730 | 1731 | samsam@1.1.2: 1732 | version "1.1.2" 1733 | resolved "http://registry.npm.taobao.org/samsam/download/samsam-1.1.2.tgz#bec11fdc83a9fda063401210e40176c3024d1567" 1734 | 1735 | seek-bzip@^1.0.3: 1736 | version "1.0.5" 1737 | resolved "http://registry.npm.taobao.org/seek-bzip/download/seek-bzip-1.0.5.tgz#cfe917cb3d274bcffac792758af53173eb1fabdc" 1738 | dependencies: 1739 | commander "~2.8.1" 1740 | 1741 | "semver@2 || 3 || 4 || 5": 1742 | version "5.3.0" 1743 | resolved "http://registry.npm.taobao.org/semver/download/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" 1744 | 1745 | set-immediate-shim@^1.0.0: 1746 | version "1.0.1" 1747 | resolved "http://registry.npm.taobao.org/set-immediate-shim/download/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" 1748 | 1749 | shelljs@^0.7.0: 1750 | version "0.7.4" 1751 | resolved "http://registry.npm.taobao.org/shelljs/download/shelljs-0.7.4.tgz#b8f04b3a74ddfafea22acf98e0be45ded53d59c8" 1752 | dependencies: 1753 | glob "^7.0.0" 1754 | interpret "^1.0.0" 1755 | rechoir "^0.6.2" 1756 | 1757 | signal-exit@^3.0.0: 1758 | version "3.0.1" 1759 | resolved "http://registry.npm.taobao.org/signal-exit/download/signal-exit-3.0.1.tgz#5a4c884992b63a7acd9badb7894c3ee9cfccad81" 1760 | 1761 | sinon@^1.17.2: 1762 | version "1.17.6" 1763 | resolved "http://registry.npm.taobao.org/sinon/download/sinon-1.17.6.tgz#a43116db59577c8296356afee13fafc2332e58e1" 1764 | dependencies: 1765 | formatio "1.1.1" 1766 | lolex "1.3.2" 1767 | samsam "1.1.2" 1768 | util ">=0.10.3 <1" 1769 | 1770 | source-map@~0.2.0: 1771 | version "0.2.0" 1772 | resolved "http://registry.npm.taobao.org/source-map/download/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d" 1773 | dependencies: 1774 | amdefine ">=0.0.4" 1775 | 1776 | sparkles@^1.0.0: 1777 | version "1.0.0" 1778 | resolved "http://registry.npm.taobao.org/sparkles/download/sparkles-1.0.0.tgz#1acbbfb592436d10bbe8f785b7cc6f82815012c3" 1779 | 1780 | spawn-sync@^1.0.15: 1781 | version "1.0.15" 1782 | resolved "http://registry.npm.taobao.org/spawn-sync/download/spawn-sync-1.0.15.tgz#b00799557eb7fb0c8376c29d44e8a1ea67e57476" 1783 | dependencies: 1784 | concat-stream "^1.4.7" 1785 | os-shim "^0.1.2" 1786 | 1787 | spdx-correct@~1.0.0: 1788 | version "1.0.2" 1789 | resolved "http://registry.npm.taobao.org/spdx-correct/download/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40" 1790 | dependencies: 1791 | spdx-license-ids "^1.0.2" 1792 | 1793 | spdx-expression-parse@~1.0.0: 1794 | version "1.0.4" 1795 | resolved "http://registry.npm.taobao.org/spdx-expression-parse/download/spdx-expression-parse-1.0.4.tgz#9bdf2f20e1f40ed447fbe273266191fced51626c" 1796 | 1797 | spdx-license-ids@^1.0.2: 1798 | version "1.2.2" 1799 | resolved "http://registry.npm.taobao.org/spdx-license-ids/download/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57" 1800 | 1801 | sprintf-js@^1.0.3: 1802 | version "1.0.3" 1803 | resolved "http://registry.npm.taobao.org/sprintf-js/download/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" 1804 | 1805 | stat-mode@^0.2.0: 1806 | version "0.2.2" 1807 | resolved "http://registry.npm.taobao.org/stat-mode/download/stat-mode-0.2.2.tgz#e6c80b623123d7d80cf132ce538f346289072502" 1808 | 1809 | stream-combiner2@^1.1.1: 1810 | version "1.1.1" 1811 | resolved "http://registry.npm.taobao.org/stream-combiner2/download/stream-combiner2-1.1.1.tgz#fb4d8a1420ea362764e21ad4780397bebcb41cbe" 1812 | dependencies: 1813 | duplexer2 "~0.1.0" 1814 | readable-stream "^2.0.2" 1815 | 1816 | stream-shift@^1.0.0: 1817 | version "1.0.0" 1818 | resolved "http://registry.npm.taobao.org/stream-shift/download/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" 1819 | 1820 | string_decoder@~0.10.x: 1821 | version "0.10.31" 1822 | resolved "http://registry.npm.taobao.org/string_decoder/download/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" 1823 | 1824 | string-template@~0.2.1: 1825 | version "0.2.1" 1826 | resolved "http://registry.npm.taobao.org/string-template/download/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add" 1827 | 1828 | string-width@^1.0.1: 1829 | version "1.0.2" 1830 | resolved "http://registry.npm.taobao.org/string-width/download/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" 1831 | dependencies: 1832 | code-point-at "^1.0.0" 1833 | is-fullwidth-code-point "^1.0.0" 1834 | strip-ansi "^3.0.0" 1835 | 1836 | strip-ansi@^3.0.0: 1837 | version "3.0.1" 1838 | resolved "http://registry.npm.taobao.org/strip-ansi/download/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" 1839 | dependencies: 1840 | ansi-regex "^2.0.0" 1841 | 1842 | strip-bom-stream@^1.0.0: 1843 | version "1.0.0" 1844 | resolved "http://registry.npm.taobao.org/strip-bom-stream/download/strip-bom-stream-1.0.0.tgz#e7144398577d51a6bed0fa1994fa05f43fd988ee" 1845 | dependencies: 1846 | first-chunk-stream "^1.0.0" 1847 | strip-bom "^2.0.0" 1848 | 1849 | strip-bom-stream@^2.0.0: 1850 | version "2.0.0" 1851 | resolved "http://registry.npm.taobao.org/strip-bom-stream/download/strip-bom-stream-2.0.0.tgz#f87db5ef2613f6968aa545abfe1ec728b6a829ca" 1852 | dependencies: 1853 | first-chunk-stream "^2.0.0" 1854 | strip-bom "^2.0.0" 1855 | 1856 | strip-bom@^2.0.0: 1857 | version "2.0.0" 1858 | resolved "http://registry.npm.taobao.org/strip-bom/download/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" 1859 | dependencies: 1860 | is-utf8 "^0.2.0" 1861 | 1862 | strip-dirs@^1.0.0: 1863 | version "1.1.1" 1864 | resolved "http://registry.npm.taobao.org/strip-dirs/download/strip-dirs-1.1.1.tgz#960bbd1287844f3975a4558aa103a8255e2456a0" 1865 | dependencies: 1866 | chalk "^1.0.0" 1867 | get-stdin "^4.0.1" 1868 | is-absolute "^0.1.5" 1869 | is-natural-number "^2.0.0" 1870 | minimist "^1.1.0" 1871 | sum-up "^1.0.1" 1872 | 1873 | strip-indent@^1.0.1: 1874 | version "1.0.1" 1875 | resolved "http://registry.npm.taobao.org/strip-indent/download/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" 1876 | dependencies: 1877 | get-stdin "^4.0.1" 1878 | 1879 | strip-json-comments@~1.0.4: 1880 | version "1.0.4" 1881 | resolved "http://registry.npm.taobao.org/strip-json-comments/download/strip-json-comments-1.0.4.tgz#1e15fbcac97d3ee99bf2d73b4c656b082bbafb91" 1882 | 1883 | strip-outer@^1.0.0: 1884 | version "1.0.0" 1885 | resolved "http://registry.npm.taobao.org/strip-outer/download/strip-outer-1.0.0.tgz#aac0ba60d2e90c5d4f275fd8869fd9a2d310ffb8" 1886 | dependencies: 1887 | escape-string-regexp "^1.0.2" 1888 | 1889 | sum-up@^1.0.1: 1890 | version "1.0.3" 1891 | resolved "http://registry.npm.taobao.org/sum-up/download/sum-up-1.0.3.tgz#1c661f667057f63bcb7875aa1438bc162525156e" 1892 | dependencies: 1893 | chalk "^1.0.0" 1894 | 1895 | supports-color@^2.0.0: 1896 | version "2.0.0" 1897 | resolved "http://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" 1898 | 1899 | tar-stream@^1.1.1: 1900 | version "1.5.2" 1901 | resolved "http://registry.npm.taobao.org/tar-stream/download/tar-stream-1.5.2.tgz#fbc6c6e83c1a19d4cb48c7d96171fc248effc7bf" 1902 | dependencies: 1903 | bl "^1.0.0" 1904 | end-of-stream "^1.0.0" 1905 | readable-stream "^2.0.0" 1906 | xtend "^4.0.0" 1907 | 1908 | temp@^0.8.3: 1909 | version "0.8.3" 1910 | resolved "http://registry.npm.taobao.org/temp/download/temp-0.8.3.tgz#e0c6bc4d26b903124410e4fed81103014dfc1f59" 1911 | dependencies: 1912 | os-tmpdir "^1.0.0" 1913 | rimraf "~2.2.6" 1914 | 1915 | text-table@^0.2.0: 1916 | version "0.2.0" 1917 | resolved "http://registry.npm.taobao.org/text-table/download/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" 1918 | 1919 | textextensions@~1.0.0: 1920 | version "1.0.2" 1921 | resolved "http://registry.npm.taobao.org/textextensions/download/textextensions-1.0.2.tgz#65486393ee1f2bb039a60cbba05b0b68bd9501d2" 1922 | 1923 | through@^2.3.6: 1924 | version "2.3.8" 1925 | resolved "http://registry.npm.taobao.org/through/download/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" 1926 | 1927 | through2-filter@^2.0.0: 1928 | version "2.0.0" 1929 | resolved "http://registry.npm.taobao.org/through2-filter/download/through2-filter-2.0.0.tgz#60bc55a0dacb76085db1f9dae99ab43f83d622ec" 1930 | dependencies: 1931 | through2 "~2.0.0" 1932 | xtend "~4.0.0" 1933 | 1934 | through2@^0.6.0, through2@^0.6.1: 1935 | version "0.6.5" 1936 | resolved "http://registry.npm.taobao.org/through2/download/through2-0.6.5.tgz#41ab9c67b29d57209071410e1d7a7a968cd3ad48" 1937 | dependencies: 1938 | readable-stream ">=1.0.33-1 <1.1.0-0" 1939 | xtend ">=4.0.0 <4.1.0-0" 1940 | 1941 | through2@^2.0.0, through2@~2.0.0: 1942 | version "2.0.1" 1943 | resolved "http://registry.npm.taobao.org/through2/download/through2-2.0.1.tgz#384e75314d49f32de12eebb8136b8eb6b5d59da9" 1944 | dependencies: 1945 | readable-stream "~2.0.0" 1946 | xtend "~4.0.0" 1947 | 1948 | time-stamp@^1.0.0: 1949 | version "1.0.1" 1950 | resolved "http://registry.npm.taobao.org/time-stamp/download/time-stamp-1.0.1.tgz#9f4bd23559c9365966f3302dbba2b07c6b99b151" 1951 | 1952 | timed-out@^2.0.0: 1953 | version "2.0.0" 1954 | resolved "http://registry.npm.taobao.org/timed-out/download/timed-out-2.0.0.tgz#f38b0ae81d3747d628001f41dafc652ace671c0a" 1955 | 1956 | to-absolute-glob@^0.1.1: 1957 | version "0.1.1" 1958 | resolved "http://registry.npm.taobao.org/to-absolute-glob/download/to-absolute-glob-0.1.1.tgz#1cdfa472a9ef50c239ee66999b662ca0eb39937f" 1959 | dependencies: 1960 | extend-shallow "^2.0.1" 1961 | 1962 | traverse@^0.6.6: 1963 | version "0.6.6" 1964 | resolved "http://registry.npm.taobao.org/traverse/download/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137" 1965 | 1966 | trim-newlines@^1.0.0: 1967 | version "1.0.0" 1968 | resolved "http://registry.npm.taobao.org/trim-newlines/download/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" 1969 | 1970 | trim-repeated@^1.0.0: 1971 | version "1.0.0" 1972 | resolved "http://registry.npm.taobao.org/trim-repeated/download/trim-repeated-1.0.0.tgz#e3646a2ea4e891312bf7eace6cfb05380bc01c21" 1973 | dependencies: 1974 | escape-string-regexp "^1.0.2" 1975 | 1976 | tunnel-agent@^0.4.0: 1977 | version "0.4.3" 1978 | resolved "http://registry.npm.taobao.org/tunnel-agent/download/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb" 1979 | 1980 | type-check@~0.3.2: 1981 | version "0.3.2" 1982 | resolved "http://registry.npm.taobao.org/type-check/download/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" 1983 | dependencies: 1984 | prelude-ls "~1.1.2" 1985 | 1986 | typedarray@~0.0.5: 1987 | version "0.0.6" 1988 | resolved "http://registry.npm.taobao.org/typedarray/download/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" 1989 | 1990 | underscore.string@^3.0.3: 1991 | version "3.3.4" 1992 | resolved "http://registry.npm.taobao.org/underscore.string/download/underscore.string-3.3.4.tgz#2c2a3f9f83e64762fdc45e6ceac65142864213db" 1993 | dependencies: 1994 | sprintf-js "^1.0.3" 1995 | util-deprecate "^1.0.2" 1996 | 1997 | unique-stream@^2.0.2: 1998 | version "2.2.1" 1999 | resolved "http://registry.npm.taobao.org/unique-stream/download/unique-stream-2.2.1.tgz#5aa003cfbe94c5ff866c4e7d668bb1c4dbadb369" 2000 | dependencies: 2001 | json-stable-stringify "^1.0.0" 2002 | through2-filter "^2.0.0" 2003 | 2004 | untildify@^2.0.0: 2005 | version "2.1.0" 2006 | resolved "http://registry.npm.taobao.org/untildify/download/untildify-2.1.0.tgz#17eb2807987f76952e9c0485fc311d06a826a2e0" 2007 | dependencies: 2008 | os-homedir "^1.0.0" 2009 | 2010 | unzip-response@^1.0.0: 2011 | version "1.0.1" 2012 | resolved "http://registry.npm.taobao.org/unzip-response/download/unzip-response-1.0.1.tgz#4a73959f2989470fa503791cefb54e1dbbc68412" 2013 | 2014 | url-parse-lax@^1.0.0: 2015 | version "1.0.0" 2016 | resolved "http://registry.npm.taobao.org/url-parse-lax/download/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" 2017 | dependencies: 2018 | prepend-http "^1.0.1" 2019 | 2020 | user-home@^2.0.0: 2021 | version "2.0.0" 2022 | resolved "http://registry.npm.taobao.org/user-home/download/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f" 2023 | dependencies: 2024 | os-homedir "^1.0.0" 2025 | 2026 | util-deprecate@^1.0.2, util-deprecate@~1.0.1: 2027 | version "1.0.2" 2028 | resolved "http://registry.npm.taobao.org/util-deprecate/download/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 2029 | 2030 | "util@>=0.10.3 <1": 2031 | version "0.10.3" 2032 | resolved "http://registry.npm.taobao.org/util/download/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" 2033 | dependencies: 2034 | inherits "2.0.1" 2035 | 2036 | uuid@^2.0.1: 2037 | version "2.0.3" 2038 | resolved "http://registry.npm.taobao.org/uuid/download/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a" 2039 | 2040 | vali-date@^1.0.0: 2041 | version "1.0.0" 2042 | resolved "http://registry.npm.taobao.org/vali-date/download/vali-date-1.0.0.tgz#1b904a59609fb328ef078138420934f6b86709a6" 2043 | 2044 | validate-npm-package-license@^3.0.1: 2045 | version "3.0.1" 2046 | resolved "http://registry.npm.taobao.org/validate-npm-package-license/download/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc" 2047 | dependencies: 2048 | spdx-correct "~1.0.0" 2049 | spdx-expression-parse "~1.0.0" 2050 | 2051 | vinyl-assign@^1.0.1: 2052 | version "1.2.1" 2053 | resolved "http://registry.npm.taobao.org/vinyl-assign/download/vinyl-assign-1.2.1.tgz#4d198891b5515911d771a8cd9c5480a46a074a45" 2054 | dependencies: 2055 | object-assign "^4.0.1" 2056 | readable-stream "^2.0.0" 2057 | 2058 | vinyl-file@^2.0.0: 2059 | version "2.0.0" 2060 | resolved "http://registry.npm.taobao.org/vinyl-file/download/vinyl-file-2.0.0.tgz#a7ebf5ffbefda1b7d18d140fcb07b223efb6751a" 2061 | dependencies: 2062 | graceful-fs "^4.1.2" 2063 | pify "^2.3.0" 2064 | pinkie-promise "^2.0.0" 2065 | strip-bom "^2.0.0" 2066 | strip-bom-stream "^2.0.0" 2067 | vinyl "^1.1.0" 2068 | 2069 | vinyl-fs@^2.2.0: 2070 | version "2.4.4" 2071 | resolved "http://registry.npm.taobao.org/vinyl-fs/download/vinyl-fs-2.4.4.tgz#be6ff3270cb55dfd7d3063640de81f25d7532239" 2072 | dependencies: 2073 | duplexify "^3.2.0" 2074 | glob-stream "^5.3.2" 2075 | graceful-fs "^4.0.0" 2076 | gulp-sourcemaps "1.6.0" 2077 | is-valid-glob "^0.3.0" 2078 | lazystream "^1.0.0" 2079 | lodash.isequal "^4.0.0" 2080 | merge-stream "^1.0.0" 2081 | mkdirp "^0.5.0" 2082 | object-assign "^4.0.0" 2083 | readable-stream "^2.0.4" 2084 | strip-bom "^2.0.0" 2085 | strip-bom-stream "^1.0.0" 2086 | through2 "^2.0.0" 2087 | through2-filter "^2.0.0" 2088 | vali-date "^1.0.0" 2089 | vinyl "^1.0.0" 2090 | 2091 | vinyl@^0.4.3: 2092 | version "0.4.6" 2093 | resolved "http://registry.npm.taobao.org/vinyl/download/vinyl-0.4.6.tgz#2f356c87a550a255461f36bbeb2a5ba8bf784847" 2094 | dependencies: 2095 | clone "^0.2.0" 2096 | clone-stats "^0.0.1" 2097 | 2098 | vinyl@^0.5.0: 2099 | version "0.5.3" 2100 | resolved "http://registry.npm.taobao.org/vinyl/download/vinyl-0.5.3.tgz#b0455b38fc5e0cf30d4325132e461970c2091cde" 2101 | dependencies: 2102 | clone "^1.0.0" 2103 | clone-stats "^0.0.1" 2104 | replace-ext "0.0.1" 2105 | 2106 | vinyl@^1.0.0, vinyl@^1.1.0: 2107 | version "1.2.0" 2108 | resolved "http://registry.npm.taobao.org/vinyl/download/vinyl-1.2.0.tgz#5c88036cf565e5df05558bfc911f8656df218884" 2109 | dependencies: 2110 | clone "^1.0.0" 2111 | clone-stats "^0.0.1" 2112 | replace-ext "0.0.1" 2113 | 2114 | ware@^1.2.0: 2115 | version "1.3.0" 2116 | resolved "http://registry.npm.taobao.org/ware/download/ware-1.3.0.tgz#d1b14f39d2e2cb4ab8c4098f756fe4b164e473d4" 2117 | dependencies: 2118 | wrap-fn "^0.1.0" 2119 | 2120 | which@^1.2.9: 2121 | version "1.2.11" 2122 | resolved "http://registry.npm.taobao.org/which/download/which-1.2.11.tgz#c8b2eeea6b8c1659fa7c1dd4fdaabe9533dc5e8b" 2123 | dependencies: 2124 | isexe "^1.1.1" 2125 | 2126 | wordwrap@~1.0.0: 2127 | version "1.0.0" 2128 | resolved "http://registry.npm.taobao.org/wordwrap/download/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" 2129 | 2130 | wrap-fn@^0.1.0: 2131 | version "0.1.5" 2132 | resolved "http://registry.npm.taobao.org/wrap-fn/download/wrap-fn-0.1.5.tgz#f21b6e41016ff4a7e31720dbc63a09016bdf9845" 2133 | dependencies: 2134 | co "3.1.0" 2135 | 2136 | wrappy@1: 2137 | version "1.0.2" 2138 | resolved "http://registry.npm.taobao.org/wrappy/download/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 2139 | 2140 | xdg-basedir@^2.0.0: 2141 | version "2.0.0" 2142 | resolved "http://registry.npm.taobao.org/xdg-basedir/download/xdg-basedir-2.0.0.tgz#edbc903cc385fc04523d966a335504b5504d1bd2" 2143 | dependencies: 2144 | os-homedir "^1.0.0" 2145 | 2146 | xtend@^4.0.0, "xtend@>=4.0.0 <4.1.0-0", xtend@~4.0.0: 2147 | version "4.0.1" 2148 | resolved "http://registry.npm.taobao.org/xtend/download/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" 2149 | 2150 | yallist@^2.0.0: 2151 | version "2.0.0" 2152 | resolved "http://registry.npm.taobao.org/yallist/download/yallist-2.0.0.tgz#306c543835f09ee1a4cb23b7bce9ab341c91cdd4" 2153 | 2154 | yauzl@^2.2.1: 2155 | version "2.6.0" 2156 | resolved "http://registry.npm.taobao.org/yauzl/download/yauzl-2.6.0.tgz#40894d4587bb125500d05df45471cd5e114a76f9" 2157 | dependencies: 2158 | buffer-crc32 "~0.2.3" 2159 | fd-slicer "~1.0.1" 2160 | 2161 | yeoman-assert@^2.0.0: 2162 | version "2.2.1" 2163 | resolved "http://registry.npm.taobao.org/yeoman-assert/download/yeoman-assert-2.2.1.tgz#524bff6b2a83d344a7a24ea825c5eb16504396f5" 2164 | dependencies: 2165 | lodash "^3.6.0" 2166 | path-exists "^2.1.0" 2167 | 2168 | yeoman-environment@^1.1.0, yeoman-environment@^1.5.2: 2169 | version "1.6.5" 2170 | resolved "http://registry.npm.taobao.org/yeoman-environment/download/yeoman-environment-1.6.5.tgz#0db07ab315adecb6e5e3b55ad9ab2063b71a08e1" 2171 | dependencies: 2172 | chalk "^1.0.0" 2173 | debug "^2.0.0" 2174 | diff "^2.1.2" 2175 | escape-string-regexp "^1.0.2" 2176 | globby "^4.0.0" 2177 | grouped-queue "^0.3.0" 2178 | inquirer "^1.0.2" 2179 | lodash "^4.11.1" 2180 | log-symbols "^1.0.1" 2181 | mem-fs "^1.1.0" 2182 | text-table "^0.2.0" 2183 | untildify "^2.0.0" 2184 | 2185 | yeoman-generator@^0.23.0, yeoman-generator@^0.23.3: 2186 | version "0.23.4" 2187 | resolved "http://registry.npm.taobao.org/yeoman-generator/download/yeoman-generator-0.23.4.tgz#c87af73d5b0e5a94d8331c114c759901f67dfbc1" 2188 | dependencies: 2189 | async "^1.4.2" 2190 | chalk "^1.0.0" 2191 | class-extend "^0.1.0" 2192 | cli-table "^0.3.1" 2193 | cross-spawn "^3.0.0" 2194 | dargs "^4.0.0" 2195 | dateformat "^1.0.11" 2196 | debug "^2.1.0" 2197 | detect-conflict "^1.0.0" 2198 | download "^4.1.2" 2199 | error "^7.0.2" 2200 | find-up "^1.0.0" 2201 | github-username "^2.0.0" 2202 | glob "^7.0.3" 2203 | gruntfile-editor "^1.0.0" 2204 | html-wiring "^1.0.0" 2205 | istextorbinary "^1.0.2" 2206 | lodash "^4.11.1" 2207 | mem-fs-editor "^2.0.0" 2208 | mkdirp "^0.5.0" 2209 | nopt "^3.0.0" 2210 | path-exists "^2.0.0" 2211 | path-is-absolute "^1.0.0" 2212 | pretty-bytes "^3.0.1" 2213 | read-chunk "^1.0.1" 2214 | read-pkg-up "^1.0.1" 2215 | rimraf "^2.2.0" 2216 | run-async "^2.0.0" 2217 | shelljs "^0.7.0" 2218 | text-table "^0.2.0" 2219 | through2 "^2.0.0" 2220 | underscore.string "^3.0.3" 2221 | user-home "^2.0.0" 2222 | xdg-basedir "^2.0.0" 2223 | yeoman-assert "^2.0.0" 2224 | yeoman-environment "^1.1.0" 2225 | yeoman-test "^1.0.0" 2226 | yeoman-welcome "^1.0.0" 2227 | 2228 | yeoman-test@^1.0.0: 2229 | version "1.5.1" 2230 | resolved "http://registry.npm.taobao.org/yeoman-test/download/yeoman-test-1.5.1.tgz#94578f77bd8b9a0cc1419d2776d1e829333cdc10" 2231 | dependencies: 2232 | inquirer "^1.0.2" 2233 | lodash "^4.3.0" 2234 | mkdirp "^0.5.1" 2235 | pinkie-promise "^2.0.1" 2236 | rimraf "^2.4.4" 2237 | sinon "^1.17.2" 2238 | yeoman-environment "^1.5.2" 2239 | yeoman-generator "^0.23.0" 2240 | 2241 | yeoman-welcome@^1.0.0: 2242 | version "1.0.1" 2243 | resolved "http://registry.npm.taobao.org/yeoman-welcome/download/yeoman-welcome-1.0.1.tgz#f6cf198fd4fba8a771672c26cdfb8a64795c84ec" 2244 | dependencies: 2245 | chalk "^1.0.0" 2246 | 2247 | --------------------------------------------------------------------------------