Initial commit

This commit is contained in:
Tristan Yang 2019-08-27 17:27:26 +08:00
commit cf0e4a7ad2
54 changed files with 13347 additions and 0 deletions

16
.babelrc Normal file
View File

@ -0,0 +1,16 @@
{
"presets": [
[
"@babel/preset-env",
{
"modules": false,
"corejs": 3,
"targets": {
"chrome": 60
}
}
],
"@babel/preset-react"
],
"plugins": ["@babel/plugin-proposal-class-properties", "@babel/plugin-syntax-dynamic-import"]
}

19
.editorconfig Normal file
View File

@ -0,0 +1,19 @@
# EditorConfig is awesome: http://EditorConfig.org
# top-most EditorConfig file
root = true
[*.md]
trim_trailing_whitespace = false
[*.js]
trim_trailing_whitespace = true
# Unix-style newlines with a newline ending every file
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
insert_final_newline = true
max_line_length = 100

5
.eslintignore Normal file
View File

@ -0,0 +1,5 @@
/scripts
/node_modules
/build
/.vscode
/public

32
.eslintrc Normal file
View File

@ -0,0 +1,32 @@
{
"extends": ["eslint:recommended", "plugin:react/recommended", "prettier"],
"plugins": ["react-hooks"],
"rules": {
"semi": 2,
"no-console": "off",
"react/prop-types": 0,
"no-unused-vars": ["error", { "ignoreRestSiblings": true }],
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
"max-lines": ["warn", 400]
},
"parser": "babel-eslint",
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
},
"env": {
"browser": true,
"node": true,
"es6": true,
"serviceworker": true
},
"settings": {
"react": {
"version": "16.8"
}
}
}

23
.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
# dependencies
/node_modules
*/node_modules
# testing
/coverage
# production
/build
/dist
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.idea

6
.huskyrc Normal file
View File

@ -0,0 +1,6 @@
{
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
"pre-commit": "lint-staged"
}
}

3
.lintstagedrc Normal file
View File

@ -0,0 +1,3 @@
{
"src/**/*.{js,jsx}": ["eslint --fix", "git add"]
}

2
.npmrc Normal file
View File

@ -0,0 +1,2 @@
registry=https://registry.npm.taobao.org
engine-strict=true

8
.prettierrc Normal file
View File

@ -0,0 +1,8 @@
{
"printWidth": 100,
"singleQuote": true,
"bracketSpacing": true,
"jsxBracketSameLine": false,
"tabWidth": 2,
"semi": true
}

21
CHANGELOG.md Normal file
View File

@ -0,0 +1,21 @@
# Changelog
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
## 1.0.1 - 2019-07-15
### Commits
- docs: first blood [`649a8cb`](https://github.com/zerosoul/react-starter/commit/649a8cb3ffa064a8eb1d68b7b1baa4c3f07c0094)
- feat: add installedCheck and update packages [`8407a06`](https://github.com/zerosoul/react-starter/commit/8407a06048cacc90dca5699d57821c5a8e93d7cc)
- build: update packages [`da8ad8b`](https://github.com/zerosoul/react-starter/commit/da8ad8bc6e468538d300c35f5a51191cbfdb1507)
- chore: update packages [`626c164`](https://github.com/zerosoul/react-starter/commit/626c164b42184726fd4d16c782fad834ff7f98ab)
- fix: add installedCheck [`8a0ef02`](https://github.com/zerosoul/react-starter/commit/8a0ef028ecb74314fb4a8844bffab61eb23509a5)
- build: update packages [`4008cd2`](https://github.com/zerosoul/react-starter/commit/4008cd20d21c13fe1cc01ceb27c69482eea8604c)
- build: update packages [`180bd5c`](https://github.com/zerosoul/react-starter/commit/180bd5ca802265dff16a1f79e49f3df985697ded)
- feat: update packages [`33e927e`](https://github.com/zerosoul/react-starter/commit/33e927eab5e5b726413a240aa305b2b9bb589664)
- feat: disable AutoDllPlugin in dev [`eb72b1a`](https://github.com/zerosoul/react-starter/commit/eb72b1a3fd636c23c1b1d36d01e13bc3e1a6daa2)
- Initial commit [`c61a6fa`](https://github.com/zerosoul/react-starter/commit/c61a6faccb32fb7e30c9b198be4112dbcc49eee2)
- docs: readme [`086b511`](https://github.com/zerosoul/react-starter/commit/086b51158b96d7157454179585c2505469a26e5e)
- feat: remove pre push hooks [`9f4d9cc`](https://github.com/zerosoul/react-starter/commit/9f4d9ccf5d3a217b3f25594eabd3d96be7db4528)
- docs: remove image file [`e628e85`](https://github.com/zerosoul/react-starter/commit/e628e85e1b70db05cb911fe799183b245d792792)

3
README.md Normal file
View File

@ -0,0 +1,3 @@
# react-starter
react webapp development scaffold for mobile and pc, with or without redux/router.

29
README.zh.md Normal file
View File

@ -0,0 +1,29 @@
# 项目名
[English Version](README.md)
[项目名](https://xxx.com/)
## 特性
### 功能
- [x] 已完成的
- [ ] 未完成的
### 体验
- [x] 已具备的
## 技术栈
- [create-react-app](https://github.com/facebook/create-react-app): 大家都在用的 react 项目构建架子
- [react](https://reactjs.org): 最流行的前端 UI 构建语言
- redux + react-redux: 最流行的 react 状态管理解决方案
- [styled-components](https://styled-components.com): react 中 css 解决方案CSS-IN-JS 最佳实践
- eslint + prettier: 为了更好地编码
- husky + commitlint: 为了更好地 GIT 提交
## 更新日志
[更新日志](CHANGELOG.md)

36
changelog.template.hbs Normal file
View File

@ -0,0 +1,36 @@
# Changelog
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
{{#each releases}}
{{#if href}}
## [{{title}}]({{href}}){{#if tag}} - {{isoDate}}{{/if}}
{{else}}
## {{title}}{{#if tag}} - {{isoDate}}{{/if}}
{{/if}}
{{#if summary}}
{{summary}}
{{/if}}
{{#if merges}}
### Merged
{{#each merges}}
- {{{message}}} {{#if href}}[`#{{id}}`]({{href}}){{/if}}
{{/each}}
{{/if}}
{{#if fixes}}
### Fixed
{{#each fixes}}
- {{{commit.subject}}}{{#each fixes}} {{#if href}}[`#{{id}}`]({{href}}){{/if}}{{/each}}
{{/each}}
{{/if}}
{{#commit-list commits heading='### Commits'}}
- {{#if breaking}}**Breaking change:** {{/if}}{{{subject}}} {{#if href}}[`{{shorthash}}`]({{href}}){{/if}}
{{/commit-list}}
{{/each}}

1
commitlint.config.js Normal file
View File

@ -0,0 +1 @@
module.exports = { extends: ['@commitlint/config-conventional'] };

91
config/env.js Normal file
View File

@ -0,0 +1,91 @@
const fs = require("fs");
const path = require("path");
const paths = require("./paths");
// Make sure that including paths.js after env.js will read .env variables.
delete require.cache[require.resolve("./paths")];
const NODE_ENV = process.env.NODE_ENV;
if (!NODE_ENV) {
throw new Error(
"The NODE_ENV environment variable is required but was not specified."
);
}
// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
var dotenvFiles = [
`${paths.dotenv}.${NODE_ENV}.local`,
`${paths.dotenv}.${NODE_ENV}`,
// Don't include `.env.local` for `test` environment
// since normally you expect tests to produce the same
// results for everyone
NODE_ENV !== "test" && `${paths.dotenv}.local`,
paths.dotenv
].filter(Boolean);
// Load environment variables from .env* files. Suppress warnings using silent
// if this file is missing. dotenv will never modify any environment variables
// that have already been set. Variable expansion is supported in .env files.
// https://github.com/motdotla/dotenv
// https://github.com/motdotla/dotenv-expand
dotenvFiles.forEach(dotenvFile => {
if (fs.existsSync(dotenvFile)) {
require("dotenv-expand")(
require("dotenv").config({
path: dotenvFile
})
);
}
});
// We support resolving modules according to `NODE_PATH`.
// This lets you use absolute paths in imports inside large monorepos:
// https://github.com/facebookincubator/create-react-app/issues/253.
// It works similar to `NODE_PATH` in Node itself:
// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders
// Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.
// Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims.
// https://github.com/facebookincubator/create-react-app/issues/1023#issuecomment-265344421
// We also resolve them to make sure all tools using them work consistently.
const appDirectory = fs.realpathSync(process.cwd());
process.env.NODE_PATH = (process.env.NODE_PATH || "")
.split(path.delimiter)
.filter(folder => folder && !path.isAbsolute(folder))
.map(folder => path.resolve(appDirectory, folder))
.join(path.delimiter);
// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
// injected into the application via DefinePlugin in Webpack configuration.
const REACT_APP = /^REACT_APP_/i;
function getClientEnvironment(publicUrl) {
const raw = Object.keys(process.env)
.filter(key => REACT_APP.test(key))
.reduce(
(env, key) => {
env[key] = process.env[key];
return env;
},
{
// Useful for determining whether were running in production mode.
// Most importantly, it switches React into the correct mode.
NODE_ENV: process.env.NODE_ENV || "development",
// Useful for resolving the correct path to static assets in `public`.
// For example, <img src={process.env.PUBLIC_URL + '/img/logo.png'} />.
// This should only be used as an escape hatch. Normally you would put
// images into the `src` and `import` them in code to get their paths.
PUBLIC_URL: publicUrl
}
);
// Stringify all values so we can feed into Webpack DefinePlugin
const stringified = {
"process.env": Object.keys(raw).reduce((env, key) => {
env[key] = JSON.stringify(raw[key]);
return env;
}, {})
};
return { raw, stringified };
}
module.exports = getClientEnvironment;

53
config/paths.js Normal file
View File

@ -0,0 +1,53 @@
const path = require("path");
const fs = require("fs");
const url = require("url");
// Make sure any symlinks in the project folder are resolved:
// https://github.com/facebookincubator/create-react-app/issues/637
const appDirectory = fs.realpathSync(process.cwd());
const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
const envPublicUrl = process.env.PUBLIC_URL;
function ensureSlash(path, needsSlash) {
const hasSlash = path.endsWith("/");
if (hasSlash && !needsSlash) {
return path.substr(path, path.length - 1);
} else if (!hasSlash && needsSlash) {
return `${path}/`;
} else {
return path;
}
}
const getPublicUrl = appPackageJson =>
envPublicUrl || require(appPackageJson).homepage;
// We use `PUBLIC_URL` environment variable or "homepage" field to infer
// "public path" at which the app is served.
// Webpack needs to know it to put the right <script> hrefs into HTML even in
// single-page apps that may serve index.html for nested URLs like /todos/42.
// We can't use a relative path in HTML because we don't want to load something
// like /todos/42/static/js/bundle.7289d.js. We have to know the root.
function getServedPath(appPackageJson) {
const publicUrl = getPublicUrl(appPackageJson);
const servedUrl =
envPublicUrl || (publicUrl ? url.parse(publicUrl).pathname : "/");
return ensureSlash(servedUrl, true);
}
// config after eject: we're in ./config/
module.exports = {
dotenv: resolveApp(".env"),
appBuild: resolveApp("build"),
appPublic: resolveApp("public"),
appHtml: resolveApp("public/index.html"),
appIndexJs: resolveApp("src/index.js"),
appPackageJson: resolveApp("package.json"),
appSrc: resolveApp("src"),
yarnLockFile: resolveApp("yarn.lock"),
appNodeModules: resolveApp("node_modules"),
publicUrl: getPublicUrl(resolveApp("package.json")),
servedPath: getServedPath(resolveApp("package.json")),
vendorConfig: resolveApp("config/vendor.config.js")
};

View File

@ -0,0 +1,118 @@
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const InterpolateHtmlPlugin = require('interpolate-html-plugin');
const paths = require('./paths');
const getClientEnvironment = require('./env');
const HappyPack = require('happypack');
const AutoDllPlugin = require('autodll-webpack-plugin');
// publicUrl和publicPath类似
// 只是这个值会在 index.html中用 %PUBLIC_URL% 引用,
// 以及js中process.env.PUBLIC_URL引用
// 省略‘/’,是为了让 %PUBLIC_PATH%/xyz更直观些总比 %PUBLIC_PATH%xyz 好阅读些
const publicUrl = process.env.NODE_ENV === 'development' ? '' : paths.servedPath;
// 需要注入app中的环境变量
const env = getClientEnvironment(publicUrl);
module.exports = {
resolve: {
// webpack 能识别的文件扩展名
extensions: ['.js', '.jsx', '.json', '.less', '.css']
// 针对 npm 中的第三方模块优先采用 jsnext:main 中指向的 ES6 模块化语法的文件
// mainFields: ["jsnext:main", "browser", "main"]
},
module: {
rules: [
{
oneOf: [
// 图片转 base64
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: require.resolve('url-loader'),
options: {
limit: 10000
}
},
// babel loader
{
test: /\.(js|jsx|mjs)$/,
include: paths.appSrc,
exclude: /node_modules/,
use: 'happypack/loader'
},
// file-loader将所有静态文件可被WebpackDevServer伺服
// 生产环境这些静态文件会被拷贝到build目录
// 之所以不用 test ,是为了能够处理被上面的 loader 漏掉的文件
{
exclude: [
/\.(js|jsx|mjs)$/,
/\.(css|less)$/,
/\.(bmp|gif|jpe?g|png)$/,
/\.less$/,
/\.html$/,
/\.json$/
],
loader: 'file-loader',
options: {
name: 'static/assets/[name].[hash:8].[ext]'
}
}
]
}
]
},
plugins: [
new HappyPack({
loaders: [
{
loader: 'babel-loader',
options: {
cacheDirectory: true
}
}
]
}),
new HtmlWebpackPlugin({
inject: true,
template: paths.appHtml,
minify:
process.env.NODE_ENV === 'development'
? {}
: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true
}
}),
new AutoDllPlugin(
process.env.NODE_ENV === 'development'
? {}
: {
context: path.join(__dirname, '..'),
inject: true, //自动在index.html引入dll
debug: true,
filename: '[name]_[hash].dll.js',
path: './dll',
entry: {
react: ['react', 'react-dom', 'styled-components']
}
}
),
new InterpolateHtmlPlugin(env.raw)
],
// node中用到但是浏览器不用到的类库给出空对象模拟
node: {
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty'
}
};

View File

@ -0,0 +1,79 @@
const webpack = require('webpack');
const paths = require('./paths');
const merge = require('webpack-merge');
const commonConfig = require('./webpack.config.common');
// 为方便配置,以根目录为本地服务器地址
const publicPath = '/';
// 下面是development的webpack配置
// 为了更好的开发体验,会专注于更快的构建速度
module.exports = merge(commonConfig, {
mode: 'development',
// 入口
entry: [
// 热更新,并实时显示错误
'react-dev-utils/webpackHotDevClient',
// 主要代码,之所以放在最后,是为了程序有错误,修改后还能刷新
paths.appIndexJs
],
output: {
// Add /* filename */ comments to generated require()s in the output.
pathinfo: true,
// WebpackDevServer 伺服的一个虚拟主文件
// 包含所有入口的代码以及 webpack runtime
filename: 'static/js/bundle.js',
// chunk 文件(开启 code splitting 才会有)
chunkFilename: 'static/js/[name].chunk.js',
// WebpackDevServer 伺服的根目录:/
publicPath
},
module: {
rules: [
{
// oneOf 的功能类似 switch 语句,最后一个是兜底的
oneOf: [
// style-loader:把css代码注射到 style 标签里
// css-loader:解析css中的“引用路径”构建出资源依赖
// 生产环境中会把css提取到一个文件中不会在style标签里
{
test: /\.css$/,
// exclude: /node_modules/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1
}
}
]
}
]
}
]
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('development')
}
}),
// Add module names to factory functions so they appear in browser profiler.
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin()
],
// Turn off performance hints during development because we don't do any
// splitting or minification in interest of speed. These warnings become
// cumbersome.
performance: {
hints: false
},
optimization: {
splitChunks: {
chunks: 'all'
}
}
});

View File

@ -0,0 +1,77 @@
const webpack = require('webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const ManifestPlugin = require('webpack-manifest-plugin');
const merge = require('webpack-merge');
const commonConfig = require('./webpack.config.common');
const paths = require('./paths');
const TerserPlugin = require('terser-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
// 以下是生产环境的配置项,目标是为了打包出体积更小、更合理的资源文件
module.exports = merge(commonConfig, {
mode: 'production',
// 遇到错误就抛错,不再往下执行
bail: true,
output: {
// 打包的主路径
path: paths.appBuild,
// 主文件以及一个异步加载chunk的文件
filename: 'static/js/[name].[chunkhash:6].js',
chunkFilename: 'static/js/[name].[chunkhash:6].chunk.js',
publicPath: paths.servedPath
},
resolve: {
modules: [__dirname, 'node_modules']
},
module: {
rules: [
{
oneOf: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader']
}
]
}
]
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV || 'production')
}
}),
new webpack.NamedChunksPlugin(),
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: '[name].css',
chunkFilename: '[name].css'
}),
// Generate a manifest file which contains a mapping of all asset filenames
// to their corresponding output file so that tools can pick it up without
// having to parse `index.html`.
new ManifestPlugin({
fileName: 'asset-manifest.json'
}),
new BundleAnalyzerPlugin({
openAnalyzer: false,
analyzerMode: 'static'
})
],
optimization: {
minimizer: [
new TerserPlugin({
parallel: true,
cache: true,
terserOptions: {
mangle: true,
ecma: 8,
compress: {
drop_console: true
}
}
})
]
}
});

View File

@ -0,0 +1,75 @@
const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware');
const noopServiceWorkerMiddleware = require('react-dev-utils/noopServiceWorkerMiddleware');
const ignoredFiles = require('react-dev-utils/ignoredFiles');
const paths = require('./paths');
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
const host = process.env.HOST || '0.0.0.0';
module.exports = function(proxy, allowedHost) {
return {
// gzip
compress: true,
// Silence WebpackDevServer's own logs since they're generally not useful.
// It will still show compile warnings and errors with this setting.
clientLogLevel: 'none',
// By default WebpackDevServer serves physical files from current directory
// in addition to all the virtual build products that it serves from memory.
// This is confusing because those files wont automatically be available in
// production build folder unless we copy them. However, copying the whole
// project directory is dangerous because we may expose sensitive files.
// Instead, we establish a convention that only files in `public` directory
// get served. Our build script will copy `public` into the `build` folder.
// In `index.html`, you can get URL of `public` folder with %PUBLIC_URL%:
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
// In JavaScript code, you can access it with `process.env.PUBLIC_URL`.
// Note that we only recommend to use `public` folder as an escape hatch
// for files like `favicon.ico`, `manifest.json`, and libraries that are
// for some reason broken when imported through Webpack. If you just want to
// use an image, put it in `src` and `import` it from JavaScript instead.
contentBase: paths.appPublic,
// By default files from `contentBase` will not trigger a page reload.
watchContentBase: true,
inline: true,
// Enable hot reloading server. It will provide /sockjs-node/ endpoint
// for the WebpackDevServer client so it can learn when the files were
// updated. The WebpackDevServer client is included as an entry point
// in the Webpack development configuration. Note that only changes
// to CSS are currently hot reloaded. JS changes will refresh the browser.
hot: true,
// It is important to tell WebpackDevServer to use the same "root" path
// as we specified in the config. In development, we always serve from /.
publicPath: '/',
// WebpackDevServer is noisy by default so we emit custom message instead
// by listening to the compiler events with `compiler.plugin` calls above.
quiet: true,
// Reportedly, this avoids CPU overload on some systems.
// https://github.com/facebookincubator/create-react-app/issues/293
// src/node_modules is not ignored to support absolute imports
// https://github.com/facebookincubator/create-react-app/issues/1065
watchOptions: {
ignored: ignoredFiles(paths.appSrc)
},
// Enable HTTPS if the HTTPS environment variable is set to 'true'
https: protocol === 'https',
host,
overlay: true,
historyApiFallback: {
// Paths with dots should still use the history fallback.
// See https://github.com/facebookincubator/create-react-app/issues/387.
disableDotRule: true
},
public: allowedHost,
proxy,
before(app) {
// This lets us open files from the runtime error overlay.
app.use(errorOverlayMiddleware());
// This service worker file is effectively a 'no-op' that will reset any
// previous service worker registered for the same host:port combination.
// We do this in development to avoid hitting the production cache if
// it used the same host and port.
// https://github.com/facebookincubator/create-react-app/issues/2272#issuecomment-302832432
app.use(noopServiceWorkerMiddleware());
}
};
};

11922
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

80
package.json Normal file
View File

@ -0,0 +1,80 @@
{
"name": "react-starter",
"version": "1.1.0",
"description": "React webapp starter",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "NODE_ENV=development node scripts/start.js",
"build": "NODE_ENV=production node scripts/build.js",
"deploy": "npm run build && gh-pages -d build",
"changelog": "auto-changelog -p --release-summary -l false --template changelog.template.hbs && git add CHANGELOG.md"
},
"repository": {
"type": "git",
"url": "git+https://github.com/zerosoul/react-starter.git"
},
"keywords": [
"css3",
"template",
"react",
"frontend",
"tool",
"amazing"
],
"author": "tristan",
"license": "ISC",
"bugs": {
"url": "https://github.com/zerosoul/react-starter/issues"
},
"homepage": "https://zerosoul.github.io/react-starter",
"dependencies": {
"react": "^16.9.0",
"react-dom": "^16.9.0",
"styled-components": "^4.3.2",
"styled-reset": "^3.0.2"
},
"devDependencies": {
"@babel/cli": "^7.5.5",
"@babel/core": "^7.5.5",
"@babel/plugin-proposal-class-properties": "^7.5.5",
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
"@babel/preset-env": "^7.5.5",
"@babel/preset-react": "^7.0.0",
"@commitlint/cli": "^8.1.0",
"@commitlint/config-conventional": "^8.1.0",
"auto-changelog": "^1.15.0",
"autodll-webpack-plugin": "^0.4.2",
"babel-eslint": "^10.0.2",
"babel-loader": "^8.0.6",
"css-loader": "^3.2.0",
"eslint": "^6.2.1",
"eslint-config-prettier": "^6.1.0",
"eslint-plugin-react": "^7.14.3",
"eslint-plugin-react-hooks": "^2.0.1",
"file-loader": "^4.2.0",
"gh-pages": "^2.1.1",
"happypack": "^5.0.1",
"html-webpack-plugin": "^3.2.0",
"husky": "^3.0.4",
"installed-check-core": "^3.0.0",
"interpolate-html-plugin": "^3.0.0",
"lint-staged": "^9.2.3",
"mini-css-extract-plugin": "^0.8.0",
"prettier": "^1.17.1",
"react-dev-utils": "^9.0.3",
"style-loader": "^1.0.0",
"terser-webpack-plugin": "^1.4.1",
"url-loader": "^2.1.0",
"webpack": "^4.39.2",
"webpack-bundle-analyzer": "^3.4.1",
"webpack-cli": "^3.3.7",
"webpack-dev-server": "^3.8.0",
"webpack-manifest-plugin": "^2.0.4",
"webpack-merge": "^4.2.1"
},
"engines": {
"node": ">=10.0.0",
"npm": ">=6.0.0"
}
}

BIN
public/android-chrome-192x192.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
public/android-chrome-512x512.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
public/apple-touch-icon.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
public/favicon-16x16.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 759 B

BIN
public/favicon-32x32.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
public/favicon.ico Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

123
public/index.html Normal file
View File

@ -0,0 +1,123 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<!-- 优先使用 IE 最新版本和 Chrome -->
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="theme-color" content="#68cdaa" />
<meta name="referrer" content="never" />
<meta name="description" content="technology logo memory game" />
<meta name="viewport" content="width=device-width, user-scalable=no" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no"
/>
<!-- 启用360浏览器的极速模式(webkit) -->
<meta name="renderer" content="webkit" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<link rel="apple-touch-icon" href="apple-touch-icon.png" />
<!-- apple splash -->
<link
href="static/splash/iphone5_splash.png"
media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2)"
rel="apple-touch-startup-image"
/>
<link
href="static/splash/iphone6_splash.png"
media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2)"
rel="apple-touch-startup-image"
/>
<link
href="static/splash/iphoneplus_splash.png"
media="(device-width: 621px) and (device-height: 1104px) and (-webkit-device-pixel-ratio: 3)"
rel="apple-touch-startup-image"
/>
<link
href="static/splash/iphonex_splash.png"
media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3)"
rel="apple-touch-startup-image"
/>
<link
href="static/splash/iphonexr_splash.png"
media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2)"
rel="apple-touch-startup-image"
/>
<link
href="static/splash/iphonexsmax_splash.png"
media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3)"
rel="apple-touch-startup-image"
/>
<link
href="static/splash/ipad_splash.png"
media="(device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2)"
rel="apple-touch-startup-image"
/>
<link
href="static/splash/ipadpro1_splash.png"
media="(device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2)"
rel="apple-touch-startup-image"
/>
<link
href="static/splash/ipadpro3_splash.png"
media="(device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2)"
rel="apple-touch-startup-image"
/>
<link
href="static/splash/ipadpro2_splash.png"
media="(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2)"
rel="apple-touch-startup-image"
/>
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="manifest.json" />
<link rel="manifest" href="site.webmanifest" />
<link rel="icon" type="image/png" sizes="32x32" href="favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="favicon-16x16.png" />
<link rel="shortcut icon" href="favicon.ico" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>Amazing Border Radius Generator</title>
</head>
<body ontouchstart="">
<noscript>
您需要开启Javascript功能
</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
<script>
if ('serviceWorker' in navigator) {
console.log('Will the service worker register?');
navigator.serviceWorker
.register('sw.js')
.then(function(reg) {
console.log('Yes, it did.');
})
.catch(function(err) {
console.log("No it didn't. This happened:", err);
});
}
</script>
</html>

15
public/manifest.json Normal file
View File

@ -0,0 +1,15 @@
{
"short_name": "Memory Game",
"name": "Frontend Logo Memory Game",
"icons": [
{
"src": "apple-touch-icon.png",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/png"
}
],
"start_url": "./index.html",
"display": "standalone",
"theme_color": "#57647e",
"background_color": "#1fbcd2"
}

1
public/site.webmanifest Executable file
View File

@ -0,0 +1 @@
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

3
public/sw.js Normal file
View File

@ -0,0 +1,3 @@
self.addEventListener('fetch', function(event) {
event.respondWith(fetch(event.request));
});

145
scripts/build.js Normal file
View File

@ -0,0 +1,145 @@
// Do this as the first thing so that any code reading it knows the right env.
process.env.BABEL_ENV = process.env.NODE_ENV || "production";
process.env.NODE_ENV = process.env.NODE_ENV || "production";
// Makes the script crash on unhandled rejections instead of silently
// ignoring them. In the future, promise rejections that are not handled will
// terminate the Node.js process with a non-zero exit code.
process.on("unhandledRejection", err => {
throw err;
});
// Ensure environment variables are read.
require("../config/env");
const path = require("path");
const chalk = require("chalk");
const fs = require("fs-extra");
const webpack = require("webpack");
const config = require("../config/webpack.config.prod");
const paths = require("../config/paths");
const checkRequiredFiles = require("react-dev-utils/checkRequiredFiles");
const formatWebpackMessages = require("react-dev-utils/formatWebpackMessages");
const printHostingInstructions = require("react-dev-utils/printHostingInstructions");
const FileSizeReporter = require("react-dev-utils/FileSizeReporter");
const printBuildError = require("react-dev-utils/printBuildError");
const measureFileSizesBeforeBuild =
FileSizeReporter.measureFileSizesBeforeBuild;
const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild;
const useYarn = fs.existsSync(paths.yarnLockFile);
// 文件体积过大提示gzip & chunk
const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024;
const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024;
// Warn and crash if required files are missing
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
process.exit(1);
}
// 首先读取当前build目录下的文件大小以便后面作比较
measureFileSizesBeforeBuild(paths.appBuild)
.then(previousFileSizes => {
// Remove all content but keep the directory so that
// if you're in it, you don't end up in Trash
fs.emptyDirSync(paths.appBuild);
// Merge with the public folder
copyPublicFolder();
// Start the webpack build
return build(previousFileSizes);
})
.then(
({ stats, previousFileSizes, warnings }) => {
if (warnings.length) {
console.log(chalk.yellow("Compiled with warnings.\n"));
console.log(warnings.join("\n\n"));
console.log(
"\nSearch for the " +
chalk.underline(chalk.yellow("keywords")) +
" to learn more about each warning."
);
console.log(
"To ignore, add " +
chalk.cyan("// eslint-disable-next-line") +
" to the line before.\n"
);
} else {
console.log(chalk.green("Compiled successfully.\n"));
}
console.log("File sizes after gzip:\n");
printFileSizesAfterBuild(
stats,
previousFileSizes,
paths.appBuild,
WARN_AFTER_BUNDLE_GZIP_SIZE,
WARN_AFTER_CHUNK_GZIP_SIZE
);
const appPackage = require(paths.appPackageJson);
const publicUrl = paths.publicUrl;
const publicPath = config.output.publicPath;
const buildFolder = path.relative(process.cwd(), paths.appBuild);
printHostingInstructions(
appPackage,
publicUrl,
publicPath,
buildFolder,
useYarn
);
},
err => {
console.log(chalk.red("Failed to compile.\n"));
printBuildError(err);
process.exit(1);
}
);
// Create the production build and print the deployment instructions.
function build(previousFileSizes) {
console.log("Creating an optimized production build...");
let compiler = webpack(config);
return new Promise((resolve, reject) => {
compiler.run((err, stats) => {
if (err) {
return reject(err);
}
const messages = formatWebpackMessages(stats.toJson({}, true));
if (messages.errors.length) {
// Only keep the first error. Others are often indicative
// of the same problem, but confuse the reader with noise.
if (messages.errors.length > 1) {
messages.errors.length = 1;
}
return reject(new Error(messages.errors.join("\n\n")));
}
if (
process.env.CI &&
(typeof process.env.CI !== "string" ||
process.env.CI.toLowerCase() !== "false") &&
messages.warnings.length
) {
console.log(
chalk.yellow(
"\nTreating warnings as errors because process.env.CI = true.\n" +
"Most CI servers set it automatically.\n"
)
);
return reject(new Error(messages.warnings.join("\n\n")));
}
return resolve({
stats,
previousFileSizes,
warnings: messages.warnings
});
});
});
}
function copyPublicFolder() {
fs.copySync(paths.appPublic, paths.appBuild, {
dereference: true,
filter: file => file !== paths.appHtml
});
}

114
scripts/start.js Normal file
View File

@ -0,0 +1,114 @@
// Do this as the first thing so that any code reading it knows the right env.
process.env.BABEL_ENV = 'development';
process.env.NODE_ENV = 'development';
// Makes the script crash on unhandled rejections instead of silently
// ignoring them. In the future, promise rejections that are not handled will
// terminate the Node.js process with a non-zero exit code.
process.on('unhandledRejection', err => {
throw err;
});
// Ensure environment variables are read.
require('../config/env');
const fs = require('fs');
const chalk = require('chalk');
const webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
const clearConsole = require('react-dev-utils/clearConsole');
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
const {
choosePort,
createCompiler,
prepareProxy,
prepareUrls
} = require('react-dev-utils/WebpackDevServerUtils');
const openBrowser = require('react-dev-utils/openBrowser');
const paths = require('../config/paths');
const config = require('../config/webpack.config.dev');
const createDevServerConfig = require('../config/webpackDevServer.config');
const useYarn = fs.existsSync(paths.yarnLockFile);
const isInteractive = process.stdout.isTTY;
const installedCheck = require('installed-check-core');
// Warn and crash if required files are missing
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
process.exit(1);
}
// Tools like Cloud9 rely on this.
const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 8088;
const HOST = process.env.HOST || '0.0.0.0';
// 检查已安装的包和package.json中是否一致
installedCheck({ versionCheck: true }).then(result => {
if (result.errors.length) {
let logStr = chalk.bgRed.bold(
'Dependency check errors: \n\n' + result.errors.join('\n') + '\n'
);
console.log(logStr);
let solveStr = chalk.bgGreenBright.bold(
'try running: "npm install" to solve this error' + '\n'
);
console.log(solveStr);
process.exit(1);
}
});
if (process.env.HOST) {
console.log(
chalk.cyan(
`Attempting to bind to HOST environment variable: ${chalk.yellow(
chalk.bold(process.env.HOST)
)}`
)
);
console.log(`If this was unintentional, check that you haven't mistakenly set it in your shell.`);
console.log(`Learn more here: ${chalk.yellow('http://bit.ly/2mwWSwH')}`);
console.log();
}
// We attempt to use the default port but if it is busy, we offer the user to
// run on a different port. `choosePort()` Promise resolves to the next free port.
choosePort(HOST, DEFAULT_PORT)
.then(port => {
if (port == null) {
// We have not found a port.
return;
}
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
const appName = require(paths.appPackageJson).name;
const urls = prepareUrls(protocol, HOST, port);
// Create a webpack compiler that is configured with custom messages.
const compiler = createCompiler({ webpack, config, appName, urls, useYarn });
// Load proxy config
const proxySetting = require(paths.appPackageJson).proxy;
const proxyConfig = prepareProxy(proxySetting, paths.appPublic);
// Serve webpack assets generated by the compiler over a web sever.
const serverConfig = createDevServerConfig(proxyConfig, urls.lanUrlForConfig);
const devServer = new WebpackDevServer(compiler, serverConfig);
// Launch WebpackDevServer.
devServer.listen(port, HOST, err => {
if (err) {
return console.log(err);
}
if (isInteractive) {
clearConsole();
}
console.log(chalk.cyan('Starting the development server...\n'));
openBrowser(urls.localUrlForBrowser);
});
['SIGINT', 'SIGTERM'].forEach(function(sig) {
process.on(sig, function() {
devServer.close();
process.exit();
});
});
})
.catch(err => {
if (err && err.message) {
console.log(err.message);
}
process.exit(1);
});

17
src/App.js Normal file
View File

@ -0,0 +1,17 @@
import React from 'react';
import Header from './components/Header';
import Footer from './components/Footer';
import styled from 'styled-components';
const StyledBody = styled.section`
height: 60vh;
`;
const App = () => {
return (
<>
<Header />
<StyledBody>body</StyledBody>
<Footer />
</>
);
};
export default App;

53
src/Global.style.js Normal file
View File

@ -0,0 +1,53 @@
import { createGlobalStyle } from 'styled-components';
import reset from 'styled-reset';
const GlobalStyle = createGlobalStyle`
${reset}
*{
box-sizing:border-box;
user-select:none;
outline:none;
-webkit-text-size-adjust: none;
-webkit-tap-highlight-color: rgba(0,0,0,0);
color:#ffffeb;
}
html{
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-family:"Fangzheng ZY", "Hiragino Sans GB", "Heiti SC", "Microsoft YaHei", "WenQuanYi Micro Hei";
}
body{
-webkit-overflow-scrolling: touch;
overflow:scroll;
margin:0 auto;
min-height:100vh;
position: relative;
}
#root{
min-height:100vh;
background-image:linear-gradient(135deg, rgb(96, 108, 136) 0%, rgb(63, 76, 107) 100%);
}
@media screen and (min-width: 320px){
html {
font-size: 12px;
}
}
@media screen and (min-width: 375px){
html {
font-size: 14px;
}
}
@media screen and (min-width: 480px){
html {
font-size: 20px;
}
}
@media screen and (min-width: 768px){
html {
font-size: 24px;
}
}
`;
export default GlobalStyle;

BIN
src/assets/img/icon.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
src/assets/img/weibo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

11
src/components/Footer.js Normal file
View File

@ -0,0 +1,11 @@
import React from 'react';
import styled from 'styled-components';
const Wrapper = styled.footer`
height: 20vh;
background: #333;
color: #fff;
`;
export default function Footer() {
return <Wrapper>Footer</Wrapper>;
}

11
src/components/Header.js Normal file
View File

@ -0,0 +1,11 @@
import React from 'react';
import styled from 'styled-components';
const Wrapper = styled.header`
height: 20vh;
background: #333;
color: #fff;
`;
export default function Header() {
return <Wrapper>Header</Wrapper>;
}

16
src/index.js Normal file
View File

@ -0,0 +1,16 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import GlobalStyle from './Global.style';
import { unregister } from './registerServiceWorker';
ReactDOM.render(
<>
<GlobalStyle />
<App />
</>,
document.getElementById('root')
);
unregister();

View File

@ -0,0 +1,113 @@
// In production, we register a service worker to serve assets from local cache.
// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on the "N+1" visit to a page, since previously
// cached resources are updated in the background.
// To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
// This link also includes instructions on opting out of this behavior.
const isLocalhost = Boolean(
window.location.hostname === 'localhost' ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
// 127.0.0.1/8 is considered localhost for IPv4.
window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/)
);
export default function register() {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
// serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
return;
}
window.addEventListener('load', () => {
const swUrl = `service-worker.js`;
if (isLocalhost) {
// This is running on localhost. Lets check if a service worker still exists or not.
checkValidServiceWorker(swUrl);
// Add some additional logging to localhost, pointing developers to the
// service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => {
console.log(
'This web app is being served cache-first by a service ' +
'worker. To learn more, visit https://goo.gl/SC7cgQ'
);
});
} else {
// Is not local host. Just register service worker
registerValidSW(swUrl);
}
});
}
}
function registerValidSW(swUrl) {
navigator.serviceWorker
.register(swUrl)
.then(registration => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
// At this point, the old content will have been purged and
// the fresh content will have been added to the cache.
// It's the perfect time to display a "New content is
// available; please refresh." message in your web app.
console.log('New content is available; please refresh.');
} else {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console.log('Content is cached for offline use.');
}
}
};
};
})
.catch(error => {
console.error('Error during service worker registration:', error);
});
}
function checkValidServiceWorker(swUrl) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl)
.then(response => {
// Ensure service worker exists, and that we really are getting a JS file.
if (
response.status === 404 ||
response.headers.get('content-type').indexOf('javascript') === -1
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => {
registration.unregister().then(() => {
window.location.reload();
});
});
} else {
// Service worker found. Proceed as normal.
registerValidSW(swUrl);
}
})
.catch(() => {
console.log('No internet connection found. App is running in offline mode.');
});
}
export function unregister() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(registration => {
registration.unregister();
});
}
}

26
src/utils.js Normal file
View File

@ -0,0 +1,26 @@
export function shuffle(array) {
let counter = array.length;
console.log('shuffle', array);
// While there are elements in the array
while (counter > 0) {
// Pick a random index
let index = Math.floor(Math.random() * counter);
// Decrease counter by 1
counter--;
// And swap the last element with it
let temp = array[counter];
array[counter] = array[index];
array[index] = temp;
}
return array;
}
export function getTimeFormated(count, zh = false) {
return zh
? `${String(Math.floor(count / 60))}${String(count % 60)}`
: `${String(Math.floor(count / 60)).padStart(2, '0')}:${String(count % 60).padStart(2, '0')}`;
}