├── .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 |

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 | 
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 | 
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 ;
410 | }
411 | }
412 |
413 | export default DangerButton;
414 | ```
415 |
416 | 请注意[`export`和`export default`的区别](http://stackoverflow.com/questions/36795819/react-native-es-6-when-should-i-use-curly-braces-for-import/36796281#36796281),这是一个容易搞错的地方。
417 |
418 | 当模块里面只需要导出一个东西的时候我们建议你使用默认导出比如`export default Button`,这样在导入的时候就可以直接这样导入`import Button from './Button`。
419 |
420 | 像这样`export Button`的名字导出在工具类中比较有用,这样可以导出多个方法。一个模块最多只能有一个默认的导出,但可以有多个名字导出。
421 |
422 | 下面的资料可以让你对ES6模块有更多的了解:
423 |
424 | * [When to use the curly braces?](http://stackoverflow.com/questions/36795819/react-native-es-6-when-should-i-use-curly-braces-for-import/36796281#36796281)
425 | * [Exploring ES6: Modules](http://exploringjs.com/es6/ch_modules.html)
426 | * [Understanding ES6: Modules](https://leanpub.com/understandinges6/read#leanpub-auto-encapsulating-code-with-modules)
427 |
428 | ## 添加样式
429 |
430 | 工程使用[Webpack](https://webpack.github.io/)来处理所有资源文件,包括JS,CSS和图片等。Webpack提供了一种自定义的方式来扩展JS的`import`功能。为了表示一个JS文件依赖一个CSS文件,你需要**在JS文件中`import` CSS文件**。
431 |
432 | ### Button.css
433 |
434 | ```css
435 | .button {
436 | padding: 20px;
437 | }
438 | ```
439 |
440 | ### Button.js
441 |
442 | ```js
443 | import React, { Component } from 'react';
444 | import style from './Button.css'; // 告诉Webpack Button.js 使用了这些样式
445 |
446 | class Button extends Component {
447 | render() {
448 | // 你可以将CSS文件当成一个对象来使用,.button这个样式看成是对象的一个属性
449 | return ;
450 | }
451 | }
452 | ```
453 |
454 | 这一部分跟React没有任何关系,但大部分人使用这种方式来开发React应用,因为这样会感觉比较方便,你可以在[这篇文章](https://medium.com/seek-ui-engineering/block-element-modifying-your-javascript-components-d7f99fcab52b)中了解这种方式的好处。
455 |
456 | 在开发环境,如果你的CSS文件修改了,应用进程会重新加载。在生产环境,所有的CSS文件最后会被打包成一个CSS文件。
457 |
458 | ## CSS预处理
459 |
460 | 项目会自动压缩你的CSS文件,并通过[Autoprefixer](https://github.com/postcss/autoprefixer)自动为你的CSS属性添加各种浏览器前缀,所以你在写CSS只需要写原始的CSS属性就可以了。
461 |
462 | 举个例子:
463 |
464 | ```css
465 | .App {
466 | display: flex;
467 | flex-direction: row;
468 | align-items: center;
469 | }
470 | ```
471 |
472 | 会变成:
473 |
474 | ```css
475 | .App {
476 | display: -webkit-box;
477 | display: -ms-flexbox;
478 | display: flex;
479 | -webkit-box-orient: horizontal;
480 | -webkit-box-direction: normal;
481 | -ms-flex-direction: row;
482 | flex-direction: row;
483 | -webkit-box-align: center;
484 | -ms-flex-align: center;
485 | align-items: center;
486 | }
487 | ```
488 |
489 | 项目目前不支持预处理器文件比如Less等,只支持CSS文件。
490 |
491 | ## 添加图片和字体
492 |
493 | 通过Webpack,使用图片和字体的方法跟使用CSS文件类似。
494 |
495 | 你可以在JS文件中导入一个图片文件,这样会告诉Webpack在集成文件中要包含这个图片。和导入CSS文件不一样的地方是,导入一个图片或字体文件是返回一个字符串,这个字符串是这个文件的地址。
496 |
497 | 举个例子:
498 |
499 | ```js
500 | import React from 'react';
501 | import logo from './logo.png'; // 告诉Webpack这个JS文件使用这个图片
502 |
503 | console.log(logo); // /logo.84287d09.png
504 |
505 | function Header() {
506 | // 在这里使用这个图片的路径
507 | return
;
508 | }
509 |
510 | export default function Header;
511 | ```
512 |
513 | 这样可以确保Webpack在打包的时候将图片等引入的静态文件打包到构建文件,并提供一个正确的引用路径。
514 |
515 | 在CSS文件中可以这样引入图片:
516 |
517 | ```css
518 | .Logo {
519 | background-image: url(./logo.png);
520 | }
521 | ```
522 |
523 | Webpack会找到CSS中所有相关的引用,并将它们在打包文件中体现,如果你的图片路径有误或者路径上没有图片,你会看到一个编译错误。在生产环境Webpack会将图片名替换成为一个随机的名字,如果文件修改了,会变成另外一个随机名字,所以你不用担心缓存引起的问题。
524 |
525 | ## 使用Ant Design
526 |
527 | 项目中默认使用[Ant Design](https://ant.design/)(antd)组件框架,它是使用React编写的,可以让你很方便地使用一些通用的组件,比如Button, Modal等,省去了你编写一些通用组件的工作量。
528 |
529 | 举个例子:
530 |
531 | ```js
532 | import React from 'react';
533 | import { Button } from 'antd'; // 导入antd中的Button组件
534 |
535 | class YourButton extends Component {
536 | render() {
537 | 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 | 
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 | 
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 ;
410 | }
411 | }
412 |
413 | export default DangerButton;
414 | ```
415 |
416 | 请注意[`export`和`export default`的区别](http://stackoverflow.com/questions/36795819/react-native-es-6-when-should-i-use-curly-braces-for-import/36796281#36796281),这是一个容易搞错的地方。
417 |
418 | 当模块里面只需要导出一个东西的时候我们建议你使用默认导出比如`export default Button`,这样在导入的时候就可以直接这样导入`import Button from './Button`。
419 |
420 | 像这样`export Button`的名字导出在工具类中比较有用,这样可以导出多个方法。一个模块最多只能有一个默认的导出,但可以有多个名字导出。
421 |
422 | 下面的资料可以让你对ES6模块有更多的了解:
423 |
424 | * [When to use the curly braces?](http://stackoverflow.com/questions/36795819/react-native-es-6-when-should-i-use-curly-braces-for-import/36796281#36796281)
425 | * [Exploring ES6: Modules](http://exploringjs.com/es6/ch_modules.html)
426 | * [Understanding ES6: Modules](https://leanpub.com/understandinges6/read#leanpub-auto-encapsulating-code-with-modules)
427 |
428 | ## 添加样式
429 |
430 | 工程使用[Webpack](https://webpack.github.io/)来处理所有资源文件,包括JS,CSS和图片等。Webpack提供了一种自定义的方式来扩展JS的`import`功能。为了表示一个JS文件依赖一个CSS文件,你需要**在JS文件中`import` CSS文件**。
431 |
432 | ### Button.css
433 |
434 | ```css
435 | .button {
436 | padding: 20px;
437 | }
438 | ```
439 |
440 | ### Button.js
441 |
442 | ```js
443 | import React, { Component } from 'react';
444 | import style from './Button.css'; // 告诉Webpack Button.js 使用了这些样式
445 |
446 | class Button extends Component {
447 | render() {
448 | // 你可以将CSS文件当成一个对象来使用,.button这个样式看成是对象的一个属性
449 | return ;
450 | }
451 | }
452 | ```
453 |
454 | 这一部分跟React没有任何关系,但大部分人使用这种方式来开发React应用,因为这样会感觉比较方便,你可以在[这篇文章](https://medium.com/seek-ui-engineering/block-element-modifying-your-javascript-components-d7f99fcab52b)中了解这种方式的好处。
455 |
456 | 在开发环境,如果你的CSS文件修改了,应用进程会重新加载。在生产环境,所有的CSS文件最后会被打包成一个CSS文件。
457 |
458 | ## CSS预处理
459 |
460 | 项目会自动压缩你的CSS文件,并通过[Autoprefixer](https://github.com/postcss/autoprefixer)自动为你的CSS属性添加各种浏览器前缀,所以你在写CSS只需要写原始的CSS属性就可以了。
461 |
462 | 举个例子:
463 |
464 | ```css
465 | .App {
466 | display: flex;
467 | flex-direction: row;
468 | align-items: center;
469 | }
470 | ```
471 |
472 | 会变成:
473 |
474 | ```css
475 | .App {
476 | display: -webkit-box;
477 | display: -ms-flexbox;
478 | display: flex;
479 | -webkit-box-orient: horizontal;
480 | -webkit-box-direction: normal;
481 | -ms-flex-direction: row;
482 | flex-direction: row;
483 | -webkit-box-align: center;
484 | -ms-flex-align: center;
485 | align-items: center;
486 | }
487 | ```
488 |
489 | 项目目前不支持预处理器文件比如Less等,只支持CSS文件。
490 |
491 | ## 添加图片和字体
492 |
493 | 通过Webpack,使用图片和字体的方法跟使用CSS文件类似。
494 |
495 | 你可以在JS文件中导入一个图片文件,这样会告诉Webpack在集成文件中要包含这个图片。和导入CSS文件不一样的地方是,导入一个图片或字体文件是返回一个字符串,这个字符串是这个文件的地址。
496 |
497 | 举个例子:
498 |
499 | ```js
500 | import React from 'react';
501 | import logo from './logo.png'; // 告诉Webpack这个JS文件使用这个图片
502 |
503 | console.log(logo); // /logo.84287d09.png
504 |
505 | function Header() {
506 | // 在这里使用这个图片的路径
507 | return
;
508 | }
509 |
510 | export default function Header;
511 | ```
512 |
513 | 这样可以确保Webpack在打包的时候将图片等引入的静态文件打包到构建文件,并提供一个正确的引用路径。
514 |
515 | 在CSS文件中可以这样引入图片:
516 |
517 | ```css
518 | .Logo {
519 | background-image: url(./logo.png);
520 | }
521 | ```
522 |
523 | Webpack会找到CSS中所有相关的引用,并将它们在打包文件中体现,如果你的图片路径有误或者路径上没有图片,你会看到一个编译错误。在生产环境Webpack会将图片名替换成为一个随机的名字,如果文件修改了,会变成另外一个随机名字,所以你不用担心缓存引起的问题。
524 |
525 | ## 使用Ant Design
526 |
527 | 项目中默认使用[Ant Design](https://ant.design/)(antd)组件框架,它是使用React编写的,可以让你很方便地使用一些通用的组件,比如Button, Modal等,省去了你编写一些通用组件的工作量。
528 |
529 | 举个例子:
530 |
531 | ```js
532 | import React from 'react';
533 | import { Button } from 'antd'; // 导入antd中的Button组件
534 |
535 | class YourButton extends Component {
536 | render() {
537 | 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 |
--------------------------------------------------------------------------------