[
  {
    "path": ".babelrc",
    "content": "{\n    \"presets\": [\n        \"env\",\n        \"stage-3\",\n        \"react\"\n    ],\n    \"plugins\": [\n        \"transform-runtime\"\n    ]\n}"
  },
  {
    "path": ".gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (https://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\njspm_packages/\n\n# TypeScript v1 declaration files\ntypings/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n\n# next.js build output\n.next\n\n# others\n.idea\n.DS_Store\ndist\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019 wechat-miniprogram\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# react-kbone\n\n使用 react 多端开发(小程序和Web)，基于 [kbone](https://github.com/Tencent/kbone) 的 element 和 render。\n\n## 特性\n\n* 一键接入，立即使用\n* 支持完整 JSX 语法，任意位置任意方式书写 JSX\n\n## 一套语法多端运行\n\n```jsx\nfunction Counter() {\n  const [count, setCount] = useState(0)\n  return (\n    <div>\n      <button onClick={() => setCount(count - 1)}>-</button>\n      <span>{count}</span>\n      <button onClick={() => setCount(count + 1)}>+</button>\n      <div onClick={clickHandle}>跳转</div>\n    </div>\n  )\n}\n\nfunction clickHandle() {\n  if ('undefined' != typeof wx && wx.getSystemInfoSync) {\n    wx.navigateTo({\n      url: '../log/index?id=1',\n    })\n  } else {\n    location.href = 'log.html'\n  }\n}\n\nexport default Counter\n```\n\n## 快速开始\n\n```\nnpx kbone-cli init my-app\ncd my-app\nnpm run mp        // 开发小程序\nnpm run build:mp  // 构建小程序\nnpm run web       // 开发 web\nnpm run build     // 构建 web\n```\n\n\n## 目录说明\n\n```\n├─ dist\n│  ├─ mp     // 微信开发者工具指向的目录，用于生产环境\n│  ├─ web    // web 编译出的文件，用于生产环境\n├─ build     // 构建相关\n├─ src\n│  ├─ assets\n│  ├─ components     // 存放所有组件\n│  ├─ log.jsx        // 入口文件，会 build 成 log.html\n│  └─ index.jsx      // 入口文件，会 build 成 index.html\n```\n\n## 注意事项\n\nreact 并没有提供根组件实例的销毁方法（如 vue.$destroy），所以在多页应用中页面关闭时不会触发该页面组件的 componentWillUnmount 钩子。开发者可自行监听 wxunload 或 beforeunload 事件来进行页面的销毁工作，比如调用 render 方法渲染一个空节点，强行触发页面组件的 componentWillUnmount 钩子。\n\n## 谁在使用 kbone？\n\n<table>\n\t<tbody>\n\t\t<tr>\n\t\t\t<td>\n        <a target=\"_blank\" href=\"https://developers.weixin.qq.com/community/develop/mixflow\">\n          <img width=\"200px\" src=\"https://raw.githubusercontent.com/wechat-miniprogram/kbone/develop/docs/images/code1.jpg\">\n        </a>\n      </td>\n\t\t\t<td>\n        <a target=\"_blank\" href=\"http://omijs.org\">\n          <img width=\"200px\" src=\"https://github.com/Tencent/omi/raw/master/assets/omi-cloud.jpg\">\n        </a>\n      </td>\n\t\t\t<td width=\"92px\">\n        <a target=\"_blank\" href=\"https://github.com/Tencent/omi/issues/new\">告诉我们</a>\n      </td>\n    </tr>\n  </tbody>\n</table>\n\n## License\n\nMIT \n"
  },
  {
    "path": "build/miniprogram.config.js",
    "content": "/**\n * 配置参考：https://wechat-miniprogram.github.io/kbone/docs/config/\n */\n\nmodule.exports = {\n    origin: 'https://test.miniprogram.com',\n    entry: '/',\n    router: {\n        home: [\n            '/(home|index)?',\n            '/index.html',\n            '/test/(home|index)',\n        ],\n        other: [\n            '/test/list/:id',\n            '/test/detail/:id',\n        ],\n    },\n    redirect: {\n        notFound: 'home',\n        accessDenied: 'home',\n    },\n    generate: {\n        autoBuildNpm: 'npm',\n    },\n    app: {\n        navigationBarTitleText: 'miniprogram-project',\n    },\n\tappExtraConfig: {\n        sitemapLocation: 'sitemap.json',\n\t},\n    global: {},\n    pages: {},\n    optimization: {\n\t\tdomSubTreeLevel: 10,\n\n\t\telementMultiplexing: true,\n\t\ttextMultiplexing: true,\n\t\tcommentMultiplexing: true,\n\t\tdomExtendMultiplexing: true,\n\n\t\tstyleValueReduce: 5000,\n\t\tattrValueReduce: 5000,\n\t},\n    projectConfig: {\n        projectname: 'kbone-template-react',\n        appid: '',\n    },\n}\n"
  },
  {
    "path": "build/webpack.base.config.js",
    "content": "const path = require('path')\n\nmodule.exports = {\n  context: path.resolve(__dirname, '../'),\n  entry: {\n    index: path.resolve(__dirname, '../src/index.jsx'),\n    log: path.resolve(__dirname, '../src/log.jsx'),\n  },\n  output: {\n    path: path.resolve(__dirname, '../dist/web'),\n    filename: '[name].js',\n    publicPath: '/',\n  },\n  module: {\n    rules: [\n      {\n        test: /\\.[t|j]sx?$/,\n        loader: 'babel-loader',\n        exclude: /node_modules/,\n      },\n      {\n        test: /\\.(png|jpg|gif|svg)$/,\n        loader: 'file-loader',\n        options: {\n          name: '[name].[ext]?[hash]',\n        },\n      },\n    ],\n  },\n  resolve: {\n    extensions: ['*', '.js', '.jsx', '.json']\n  },\n}\n"
  },
  {
    "path": "build/webpack.dev.config.js",
    "content": "const webpack = require('webpack')\nconst { merge } = require('webpack-merge')\nconst baseWebpackConfig = require('./webpack.base.config')\nconst HtmlWebpackPlugin = require('html-webpack-plugin')\nconst portfinder = require('portfinder')\n\nconst htmlPluginList = Object.keys(baseWebpackConfig.entry).map(name => {\n  return new HtmlWebpackPlugin({\n    filename: `${name}.html`,\n    template: 'index.html',\n    inject: true,\n    chunks: [name],\n  })\n})\n\nconst devWebpackConfig = merge(baseWebpackConfig, {\n  mode: 'development',\n  devServer: {\n    historyApiFallback: {\n      rewrites: [{from: /.*/, to: '/index.html'}],\n    },\n    hot: true,\n    compress: true,\n    host: process.env.HOST || 'localhost',\n    port: +process.env.PORT || 8080,\n    open: true, // 自动打开浏览器\n    client: {\n      logging: 'warn',\n      overlay: { // 展示全屏报错\n        warnings: false,\n        errors: true\n      },\n    },\n    static: {\n      publicPath: '/',\n    },\n    proxy: {},\n  },\n  watchOptions: {\n    poll: false,\n  },\n  devtool: 'cheap-module-eval-source-map',\n  module: {\n    rules: [\n      {\n        test: /\\.css$/,\n        use: ['style-loader', 'css-loader'],\n      },\n    ],\n  },\n  plugins: [\n    new webpack.DefinePlugin({\n      'process.env': {\n        NODE_ENV: '\"development\"',\n      },\n    }),\n    new webpack.HotModuleReplacementPlugin(),\n    ...htmlPluginList,\n  ],\n})\n\nmodule.exports = new Promise((resolve, reject) => {\n  portfinder.basePort = +process.env.PORT || 8080\n  portfinder.getPort((err, port) => {\n    if (err) {\n      reject(err)\n    } else {\n      devWebpackConfig.devServer.port = port\n\n      resolve(devWebpackConfig)\n    }\n  })\n})\n"
  },
  {
    "path": "build/webpack.mp.config.js",
    "content": "const path = require('path')\nconst webpack = require('webpack')\nconst MiniCssExtractPlugin = require('mini-css-extract-plugin')\nconst OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')\nconst TerserPlugin = require('terser-webpack-plugin')\nconst MpPlugin = require('mp-webpack-plugin')\n\nconst isOptimize = false // 是否压缩业务代码，开发者工具可能无法完美支持业务代码使用到的 es 特性，建议自己做代码压缩\n\nmodule.exports = {\n  mode: 'production',\n  entry: {\n    index: path.resolve(__dirname, '../src/index.jsx'),\n    log: path.resolve(__dirname, '../src/log.jsx'),\n  },\n  output: {\n    path: path.resolve(__dirname, '../dist/mp/common'), // 放到小程序代码目录中的 common 目录下\n    filename: '[name].js', // 必需字段，不能修改\n    library: 'createApp', // 必需字段，不能修改\n    libraryExport: 'default', // 必需字段，不能修改\n    libraryTarget: 'window', // 必需字段，不能修改\n  },\n  target: 'web', // 必需字段，不能修改\n  optimization: {\n    runtimeChunk: false, // 必需字段，不能修改\n    splitChunks: {\n      // 代码分隔配置，不建议修改\n      chunks: 'all',\n      minSize: 1000,\n      maxSize: 0,\n      minChunks: 1,\n      maxAsyncRequests: 100,\n      maxInitialRequests: 100,\n      automaticNameDelimiter: '~',\n      name: true,\n      cacheGroups: {\n        vendors: {\n          test: /[\\\\/]node_modules[\\\\/]/,\n          priority: -10,\n        },\n        default: {\n          minChunks: 2,\n          priority: -20,\n          reuseExistingChunk: true,\n        },\n      },\n    },\n\n    minimizer: isOptimize\n      ? [\n          // 压缩CSS\n          new OptimizeCSSAssetsPlugin({\n            assetNameRegExp: /\\.(css|wxss)$/g,\n            cssProcessor: require('cssnano'),\n            cssProcessorPluginOptions: {\n              preset: [\n                'default',\n                {\n                  discardComments: {\n                    removeAll: true,\n                  },\n                  minifySelectors: false, // 因为 wxss 编译器不支持 .some>:first-child 这样格式的代码，所以暂时禁掉这个\n                },\n              ],\n            },\n            canPrint: false,\n          }),\n          // 压缩 js\n          new TerserPlugin({\n            test: /\\.js(\\?.*)?$/i,\n            parallel: true,\n          }),\n        ]\n      : [],\n  },\n  module: {\n    rules: [\n      {\n        test: /\\.css$/,\n        use: [MiniCssExtractPlugin.loader, 'css-loader'],\n      },\n      {\n        test: /\\.[t|j]sx?$/,\n        loader: 'babel-loader',\n        exclude: /node_modules/,\n      },\n      {\n        test: /\\.(png|jpg|gif|svg)$/,\n        loader: 'file-loader',\n        options: {\n          name: '[name].[ext]?[hash]',\n        },\n      },\n    ],\n  },\n  resolve: {\n    extensions: ['*', '.js', '.jsx', '.json'],\n  },\n  plugins: [\n    new webpack.DefinePlugin({\n      'process.env.isMiniprogram': process.env.isMiniprogram, // 注入环境变量，用于业务代码判断\n    }),\n    new MiniCssExtractPlugin({\n      filename: '[name].wxss',\n    }),\n    new MpPlugin(require('./miniprogram.config')),\n  ],\n}\n"
  },
  {
    "path": "build/webpack.prod.config.js",
    "content": "const path = require('path')\nconst webpack = require('webpack')\nconst { merge } = require('webpack-merge')\nconst baseWebpackConfig = require('./webpack.base.config')\nconst HtmlWebpackPlugin = require('html-webpack-plugin')\nconst MiniCssExtractPlugin = require('mini-css-extract-plugin')\nconst OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')\nconst TerserPlugin = require('terser-webpack-plugin')\n\nconst htmlPluginList = Object.keys(baseWebpackConfig.entry).map(name => {\n  return new HtmlWebpackPlugin({\n    filename: path.resolve(__dirname, `../dist/web/${name}.html`),\n    template: 'index.html',\n    inject: true,\n    minify: {\n      removeComments: true,\n      collapseWhitespace: true,\n      removeAttributeQuotes: true,\n    },\n    chunks: [name],\n  })\n})\n\nconst webpackConfig = merge(baseWebpackConfig, {\n  mode: 'production',\n  output: {\n    path: path.resolve(__dirname, '../dist/web'),\n    filename: path.posix.join('static', 'js/[name].[chunkhash].js'),\n    chunkFilename: path.posix.join('static', 'js/[id].[chunkhash].js'),\n  },\n  optimization: {\n    splitChunks: {\n      // 代码分割配置\n      chunks: 'async',\n      minSize: 30000,\n      maxSize: 0,\n      minChunks: 1,\n      maxAsyncRequests: 5,\n      maxInitialRequests: 3,\n      automaticNameDelimiter: '~',\n      name: true,\n      cacheGroups: {\n        vendors: {\n          test: /[\\\\/]node_modules[\\\\/]/,\n          priority: -10,\n        },\n        default: {\n          minChunks: 2,\n          priority: -20,\n          reuseExistingChunk: true,\n        },\n      },\n    },\n    minimizer: [\n      // 压缩CSS\n      new OptimizeCSSAssetsPlugin({\n        assetNameRegExp: /\\.css$/g,\n        cssProcessor: require('cssnano'),\n        cssProcessorPluginOptions: {\n          preset: [\n            'default',\n            {\n              discardComments: {\n                removeAll: true,\n              },\n            },\n          ],\n        },\n        canPrint: false,\n      }),\n      // 压缩 js\n      new TerserPlugin({\n        test: /\\.js(\\?.*)?$/i,\n        parallel: true,\n      }),\n    ],\n  },\n  devtool: false,\n  module: {\n    rules: [\n      {\n        test: /\\.css$/,\n        use: [MiniCssExtractPlugin.loader, 'css-loader'],\n      },\n    ],\n  },\n  plugins: [\n    new webpack.DefinePlugin({\n      'process.env': {\n        NODE_ENV: '\"production\"',\n      },\n    }),\n    // 分离 css 文件\n    new MiniCssExtractPlugin({\n      filename: path.posix.join('static', 'css/[name].[hash].css'),\n    }),\n    ...htmlPluginList,\n    // 当 vendor 模块没有改变时，保证模块 id 不变\n    new webpack.HashedModuleIdsPlugin(),\n  ],\n})\n\nmodule.exports = webpackConfig\n"
  },
  {
    "path": "index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no,minimal-ui, viewport-fit=cover\" />\n    <meta content=\"yes\"name=\"apple-mobile-web-app-capable\"/>\n    <meta content=\"black\"name=\"apple-mobile-web-app-status-bar-style\"/>\n    <meta name=\"format-detection\"content=\"telephone=no, email=no\" />\n    <title>react</title>\n    <style type=\"text/css\">\n      #app {\n        font-family: 'Avenir', Helvetica, Arial, sans-serif;\n        -webkit-font-smoothing: antialiased;\n        -moz-osx-font-smoothing: grayscale;\n        text-align: center;\n        color: #2c3e50;\n        margin-top: 60px;\n      }\n    </style>\n  </head>\n  <body>\n    <div id=\"app\"></div>\n  </body>\n</html>"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"react-kbone\",\n  \"version\": \"0.0.1\",\n  \"description\": \"\",\n  \"author\": \"wechat-miniprogram\",\n  \"keywords\": [\n    \"react\",\n    \"kbone\",\n    \"mp\"\n  ],\n  \"scripts\": {\n    \"start\": \"npm run mp\",\n    \"web\": \"cross-env NODE_ENV=development webpack-dev-server --progress --config build/webpack.dev.config.js\",\n    \"mp\": \"rimraf dist/mp/common && cross-env NODE_ENV=development webpack --config build/webpack.mp.config.js --watch --progress\",\n    \"build\": \"rimraf dist/web && cross-env NODE_ENV=production webpack --config build/webpack.prod.config.js --progress\",\n    \"build:mp\": \"rimraf dist/mp/common && cross-env NODE_ENV=production webpack --config build/webpack.mp.config.js --progress\"\n  },\n  \"dependencies\": {\n    \"react\": \"^16.9.0\",\n    \"react-dom\": \"^16.9.0\"\n  },\n  \"browserslist\": [\n    \"> 1%\",\n    \"last 2 versions\",\n    \"not ie <= 8\"\n  ],\n  \"devDependencies\": {\n    \"@webpack-cli/serve\": \"^1.6.1\",\n    \"babel-core\": \"^6.26.0\",\n    \"babel-loader\": \"^7.1.2\",\n    \"babel-plugin-transform-runtime\": \"^6.23.0\",\n    \"babel-preset-env\": \"^1.7.0\",\n    \"babel-preset-react\": \"^6.24.1\",\n    \"babel-preset-stage-3\": \"^6.24.1\",\n    \"cross-env\": \"^5.0.5\",\n    \"css-loader\": \"^0.28.7\",\n    \"file-loader\": \"^1.1.4\",\n    \"html-webpack-plugin\": \"^4.5.2\",\n    \"mini-css-extract-plugin\": \"^0.5.0\",\n    \"mp-webpack-plugin\": \"latest\",\n    \"optimize-css-assets-webpack-plugin\": \"^5.0.8\",\n    \"portfinder\": \"^1.0.28\",\n    \"rimraf\": \"^2.7.1\",\n    \"style-loader\": \"^2.0.0\",\n    \"stylehacks\": \"^4.0.3\",\n    \"terser-webpack-plugin\": \"^4.2.3\",\n    \"webpack\": \"^4.29.6\",\n    \"webpack-cli\": \"^4.9.2\",\n    \"webpack-dev-server\": \"^4.7.4\",\n    \"webpack-merge\": \"^5.8.0\"\n  },\n  \"license\": \"MIT\"\n}\n"
  },
  {
    "path": "src/components/counter/index.css",
    "content": "span{\n  color: red;\n}"
  },
  {
    "path": "src/components/counter/index.jsx",
    "content": "import React, { useState } from 'react'\nimport './index.css'\n\nfunction Counter() {\n  const [count, setCount] = useState(0)\n  return (\n    <div>\n      <button onClick={() => setCount(count - 1)}>-</button>\n      <span>{count}</span>\n      <button onClick={() => setCount(count + 1)}>+</button>\n      <div onClick={clickHandle}>跳转</div>\n    </div>\n  )\n}\n\nfunction clickHandle() {\n  if ('undefined' != typeof wx && wx.getSystemInfoSync) {\n    wx.navigateTo({\n      url: '../log/index?id=1',\n    })\n  } else {\n    location.href = 'log.html'\n  }\n}\n\nexport default Counter\n"
  },
  {
    "path": "src/index.jsx",
    "content": "import React from 'react'\nimport ReactDOM from 'react-dom'\nimport Counter from './components/counter'\n\nexport default function createApp() {\n  const container = document.createElement('div')\n  container.id = 'app'\n  document.body.appendChild(container)\n\n  ReactDOM.render(<Counter />, container)\n}\n\n;('undefined' != typeof wx && wx.getSystemInfoSync) || createApp()\n"
  },
  {
    "path": "src/log.jsx",
    "content": "import React from 'react'\nimport ReactDOM from 'react-dom'\n\nexport default function createApp() {\n  const container = document.createElement('div')\n  container.id = 'app'\n  document.body.appendChild(container)\n\n  ReactDOM.render(<div>我是log页面</div>, container)\n}\n\n;('undefined' != typeof wx && wx.getSystemInfoSync) || createApp()\n"
  }
]