nakarte

Source code of https://map.sikmir.ru (fork)
git clone git://git.sikmir.ru/nakarte
Log | Files | Refs | LICENSE

commit 6b2528df15d2fe50f09bfacb4ad3ee17e6b6727b
parent b1215b24c32c1a659e23386ed8c14851e5db5e3e
Author: Sergej Orlov <wladimirych@gmail.com>
Date:   Sat, 19 Nov 2016 13:22:46 +0300

build config generated with make-react-app

Diffstat:
Aconfig/env.js | 28++++++++++++++++++++++++++++
Aconfig/jest/CSSStub.js | 1+
Aconfig/jest/FileStub.js | 1+
Aconfig/paths.js | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aconfig/polyfills.js | 14++++++++++++++
Aconfig/webpack.config.dev.js | 206+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aconfig/webpack.config.prod.js | 244+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apackage.json | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ascripts/build.js | 205+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ascripts/start.js | 268+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ascripts/test.js | 19+++++++++++++++++++
11 files changed, 1133 insertions(+), 0 deletions(-)

diff --git a/config/env.js b/config/env.js @@ -0,0 +1,28 @@ +// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be +// injected into the application via DefinePlugin in Webpack configuration. + +var REACT_APP = /^REACT_APP_/i; + +function getClientEnvironment(publicUrl) { + var processEnv = Object + .keys(process.env) + .filter(key => REACT_APP.test(key)) + .reduce((env, key) => { + env[key] = JSON.stringify(process.env[key]); + return env; + }, { + // Useful for determining whether we’re running in production mode. + // Most importantly, it switches React into the correct mode. + 'NODE_ENV': JSON.stringify( + 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': JSON.stringify(publicUrl) + }); + return {'process.env': processEnv}; +} + +module.exports = getClientEnvironment; diff --git a/config/jest/CSSStub.js b/config/jest/CSSStub.js @@ -0,0 +1 @@ +module.exports = {}; diff --git a/config/jest/FileStub.js b/config/jest/FileStub.js @@ -0,0 +1 @@ +module.exports = "test-file-stub"; diff --git a/config/paths.js b/config/paths.js @@ -0,0 +1,57 @@ +var path = require('path'); +var fs = require('fs'); + +// Make sure any symlinks in the project folder are resolved: +// https://github.com/facebookincubator/create-react-app/issues/637 +var appDirectory = fs.realpathSync(process.cwd()); +function resolveApp(relativePath) { + return path.resolve(appDirectory, relativePath); +} + +// 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 + +// We will export `nodePaths` as an array of absolute paths. +// It will then be used by Webpack configs. +// Jest doesn’t need this because it already handles `NODE_PATH` out of the box. + +var nodePaths = (process.env.NODE_PATH || '') + .split(process.platform === 'win32' ? ';' : ':') + .filter(Boolean) + .map(resolveApp); + +// config after eject: we're in ./config/ +module.exports = { + appBuild: resolveApp('build'), + appPublic: resolveApp('public'), + appHtml: resolveApp('public/index.html'), + appIndexJs: resolveApp('src/index.js'), + appPackageJson: resolveApp('package.json'), + appSrc: resolveApp('src'), + testsSetup: resolveApp('src/setupTests.js'), + appNodeModules: resolveApp('node_modules'), + ownNodeModules: resolveApp('node_modules'), + nodePaths: nodePaths +}; + + + +// config before publish: we're in ./packages/react-scripts/config/ +if (__dirname.indexOf(path.join('packages', 'react-scripts', 'config')) !== -1) { + module.exports = { + appBuild: resolveOwn('../../../build'), + appPublic: resolveOwn('../template/public'), + appHtml: resolveOwn('../template/public/index.html'), + appIndexJs: resolveOwn('../template/src/index.js'), + appPackageJson: resolveOwn('../package.json'), + appSrc: resolveOwn('../template/src'), + testsSetup: resolveOwn('../template/src/setupTests.js'), + appNodeModules: resolveOwn('../node_modules'), + ownNodeModules: resolveOwn('../node_modules'), + nodePaths: nodePaths + }; +} diff --git a/config/polyfills.js b/config/polyfills.js @@ -0,0 +1,14 @@ +if (typeof Promise === 'undefined') { + // Rejection tracking prevents a common issue where React gets into an + // inconsistent state due to an error, but it gets swallowed by a Promise, + // and the user has no idea what causes React's erratic future behavior. + require('promise/lib/rejection-tracking').enable(); + window.Promise = require('promise/lib/es6-extensions.js'); +} + +// fetch() polyfill for making API calls. +require('whatwg-fetch'); + +// Object.assign() is commonly used with React. +// It will use the native implementation if it's present and isn't buggy. +Object.assign = require('object-assign'); diff --git a/config/webpack.config.dev.js b/config/webpack.config.dev.js @@ -0,0 +1,206 @@ +var path = require('path'); +var autoprefixer = require('autoprefixer'); +var webpack = require('webpack'); +var findCacheDir = require('find-cache-dir'); +var HtmlWebpackPlugin = require('html-webpack-plugin'); +var CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); +var InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin'); +var WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin'); +var getClientEnvironment = require('./env'); +var paths = require('./paths'); + +// Webpack uses `publicPath` to determine where the app is being served from. +// In development, we always serve from the root. This makes config easier. +var publicPath = '/'; +// `publicUrl` is just like `publicPath`, but we will provide it to our app +// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript. +// Omit trailing slash as %PUBLIC_PATH%/xyz looks better than %PUBLIC_PATH%xyz. +var publicUrl = ''; +// Get environment variables to inject into our app. +var env = getClientEnvironment(publicUrl); + +// This is the development configuration. +// It is focused on developer experience and fast rebuilds. +// The production configuration is different and lives in a separate file. +module.exports = { + // This makes the bundle appear split into separate modules in the devtools. + // We don't use source maps here because they can be confusing: + // https://github.com/facebookincubator/create-react-app/issues/343#issuecomment-237241875 + // You may want 'cheap-module-source-map' instead if you prefer source maps. + devtool: 'eval', + // devtool: 'cheap-module-source-map', + // These are the "entry points" to our application. + // This means they will be the "root" imports that are included in JS bundle. + // The first two entry points enable "hot" CSS and auto-refreshes for JS. + entry: [ + // Include an alternative client for WebpackDevServer. A client's job is to + // connect to WebpackDevServer by a socket and get notified about changes. + // When you save a file, the client will either apply hot updates (in case + // of CSS changes), or refresh the page (in case of JS changes). When you + // make a syntax error, this client will display a syntax error overlay. + // Note: instead of the default WebpackDevServer client, we use a custom one + // to bring better experience for Create React App users. You can replace + // the line below with these two lines if you prefer the stock client: + // require.resolve('webpack-dev-server/client') + '?/', + // require.resolve('webpack/hot/dev-server'), + require.resolve('react-dev-utils/webpackHotDevClient'), + // We ship a few polyfills by default: + require.resolve('./polyfills'), + // Finally, this is your app's code: + paths.appIndexJs + // We include the app code last so that if there is a runtime error during + // initialization, it doesn't blow up the WebpackDevServer client, and + // changing JS code would still trigger a refresh. + ], + output: { + // Next line is not used in dev but WebpackDevServer crashes without it: + path: paths.appBuild, + // Add /* filename */ comments to generated require()s in the output. + pathinfo: true, + // This does not produce a real file. It's just the virtual path that is + // served by WebpackDevServer in development. This is the JS bundle + // containing code from all our entry points, and the Webpack runtime. + filename: 'static/js/bundle.js', + // This is the URL that app is served from. We use "/" in development. + publicPath: publicPath + }, + resolve: { + // This allows you to set a fallback for where Webpack should look for modules. + // We read `NODE_PATH` environment variable in `paths.js` and pass paths here. + // We use `fallback` instead of `root` because we want `node_modules` to "win" + // if there any conflicts. This matches Node resolution mechanism. + // https://github.com/facebookincubator/create-react-app/issues/253 + root: [path.resolve('./src')], + fallback: paths.nodePaths, + // These are the reasonable defaults supported by the Node ecosystem. + // We also include JSX as a common component filename extension to support + // some tools, although we do not recommend using it, see: + // https://github.com/facebookincubator/create-react-app/issues/290 + extensions: ['.js', '.json', '.jsx', ''], + alias: { + // Support React Native Web + // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/ + 'react-native': 'react-native-web' + } + }, + + module: { + // First, run the linter. + // It's important to do this before Babel processes the JS. + preLoaders: [ + { + test: /\.(js|jsx)$/, + loader: 'eslint', + include: paths.appSrc, + } + ], + loaders: [ + // Process JS with Babel. + { + test: /\.(js|jsx)$/, + include: paths.appSrc, + loader: 'babel', + query: { + + // This is a feature of `babel-loader` for webpack (not Babel itself). + // It enables caching results in ./node_modules/.cache/react-scripts/ + // directory for faster rebuilds. We use findCacheDir() because of: + // https://github.com/facebookincubator/create-react-app/issues/483 + cacheDirectory: findCacheDir({ + name: 'react-scripts' + }) + } + }, + // "postcss" loader applies autoprefixer to our CSS. + // "css" loader resolves paths in CSS and adds assets as dependencies. + // "style" loader turns CSS into JS modules that inject <style> tags. + // In production, we use a plugin to extract that CSS to a file, but + // in development "style" loader enables hot editing of CSS. + { + test: /\.css$/, + loader: 'style!css?importLoaders=1!postcss' + }, + // JSON is not enabled by default in Webpack but both Node and Browserify + // allow it implicitly so we also enable it. + { + test: /\.json$/, + loader: 'json' + }, + // "file" loader makes sure those assets get served by WebpackDevServer. + // When you `import` an asset, you get its (virtual) filename. + // In production, they would get copied to the `build` folder. + { + test: /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2)(\?.*)?$/, + loader: 'file', + query: { + // name: 'static/media/[name].[hash:8].[ext]' + name: 'static/media/[name].[ext]' + } + }, + // "url" loader works just like "file" loader but it also embeds + // assets smaller than specified size as data URLs to avoid requests. + { + test: /\.(mp4|webm|wav|mp3|m4a|aac|oga)(\?.*)?$/, + loader: 'url', + query: { + limit: 10000, + name: 'static/media/[name].[hash:8].[ext]' + } + }, + + { + test: /\.(html)(\?.*)?$/, + loader: 'raw' + } + + ] + }, + + // We use PostCSS for autoprefixing only. + postcss: function() { + return [ + autoprefixer({ + browsers: [ + '>1%', + 'last 4 versions', + 'Firefox ESR', + 'not ie < 9', // React doesn't support IE8 anyway + ] + }), + ]; + }, + plugins: [ + // Makes the public URL available as %PUBLIC_URL% in index.html, e.g.: + // <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico"> + // In development, this will be an empty string. + new InterpolateHtmlPlugin({ + PUBLIC_URL: publicUrl + }), + // Generates an `index.html` file with the <script> injected. + new HtmlWebpackPlugin({ + inject: true, + template: paths.appHtml, + }), + // Makes some environment variables available to the JS code, for example: + // if (process.env.NODE_ENV === 'development') { ... }. See `./env.js`. + new webpack.DefinePlugin(env), + // This is necessary to emit hot updates (currently CSS only): + new webpack.HotModuleReplacementPlugin(), + // Watcher doesn't work well if you mistype casing in a path so we use + // a plugin that prints an error when you attempt to do this. + // See https://github.com/facebookincubator/create-react-app/issues/240 + new CaseSensitivePathsPlugin(), + // If you require a missing module and then `npm install` it, you still have + // to restart the development server for Webpack to discover it. This plugin + // makes the discovery automatic so you don't have to restart. + // See https://github.com/facebookincubator/create-react-app/issues/186 + new WatchMissingNodeModulesPlugin(paths.appNodeModules) + ], + // Some libraries import Node modules but don't use them in the browser. + // Tell Webpack to provide empty mocks for them so importing them works. + node: { + fs: 'empty', + net: 'empty', + tls: 'empty' + } +}; diff --git a/config/webpack.config.prod.js b/config/webpack.config.prod.js @@ -0,0 +1,244 @@ +var path = require('path'); +var autoprefixer = require('autoprefixer'); +var webpack = require('webpack'); +var HtmlWebpackPlugin = require('html-webpack-plugin'); +var ExtractTextPlugin = require('extract-text-webpack-plugin'); +var ManifestPlugin = require('webpack-manifest-plugin'); +var InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin'); +var url = require('url'); +var paths = require('./paths'); +var getClientEnvironment = require('./env'); + +function ensureSlash(path, needsSlash) { + var hasSlash = path.endsWith('/'); + if (hasSlash && !needsSlash) { + return path.substr(path, path.length - 1); + } else if (!hasSlash && needsSlash) { + return path + '/'; + } else { + return path; + } +} + +// We use "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. +var homepagePath = require(paths.appPackageJson).homepage; +var homepagePathname = homepagePath ? url.parse(homepagePath).pathname : '/'; +// Webpack uses `publicPath` to determine where the app is being served from. +// It requires a trailing slash, or the file assets will get an incorrect path. +var publicPath = ensureSlash(homepagePathname, true); +// `publicUrl` is just like `publicPath`, but we will provide it to our app +// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript. +// Omit trailing slash as %PUBLIC_PATH%/xyz looks better than %PUBLIC_PATH%xyz. +var publicUrl = ensureSlash(homepagePathname, false); +// Get environment variables to inject into our app. +var env = getClientEnvironment(publicUrl); + +// Assert this just to be safe. +// Development builds of React are slow and not intended for production. +if (env['process.env'].NODE_ENV !== '"production"') { + throw new Error('Production builds must have NODE_ENV=production.'); +} + +// This is the production configuration. +// It compiles slowly and is focused on producing a fast and minimal bundle. +// The development configuration is different and lives in a separate file. +module.exports = { + // Don't attempt to continue if there are any errors. + bail: true, + // We generate sourcemaps in production. This is slow but gives good results. + // You can exclude the *.map files from the build during deployment. + devtool: 'source-map', + // In production, we only want to load the polyfills and the app code. + entry: [ + require.resolve('./polyfills'), + paths.appIndexJs + ], + output: { + // The build folder. + path: paths.appBuild, + // Generated JS file names (with nested folders). + // There will be one main bundle, and one file per asynchronous chunk. + // We don't currently advertise code splitting but Webpack supports it. + filename: 'static/js/[name].[chunkhash:8].js', + chunkFilename: 'static/js/[name].[chunkhash:8].chunk.js', + // We inferred the "public path" (such as / or /my-project) from homepage. + publicPath: publicPath + }, + resolve: { + // This allows you to set a fallback for where Webpack should look for modules. + // We read `NODE_PATH` environment variable in `paths.js` and pass paths here. + // We use `fallback` instead of `root` because we want `node_modules` to "win" + // if there any conflicts. This matches Node resolution mechanism. + // https://github.com/facebookincubator/create-react-app/issues/253 + root: [path.resolve('./src')], + fallback: paths.nodePaths, + // These are the reasonable defaults supported by the Node ecosystem. + // We also include JSX as a common component filename extension to support + // some tools, although we do not recommend using it, see: + // https://github.com/facebookincubator/create-react-app/issues/290 + extensions: ['.js', '.json', '.jsx', ''], + alias: { + // Support React Native Web + // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/ + 'react-native': 'react-native-web' + } + }, + + module: { + // First, run the linter. + // It's important to do this before Babel processes the JS. + preLoaders: [ + { + test: /\.(js|jsx)$/, + loader: 'eslint', + include: paths.appSrc + } + ], + loaders: [ + // Process JS with Babel. + { + test: /\.(js|jsx)$/, + include: paths.appSrc, + loader: 'babel', + + }, + // The notation here is somewhat confusing. + // "postcss" loader applies autoprefixer to our CSS. + // "css" loader resolves paths in CSS and adds assets as dependencies. + // "style" loader normally turns CSS into JS modules injecting <style>, + // but unlike in development configuration, we do something different. + // `ExtractTextPlugin` first applies the "postcss" and "css" loaders + // (second argument), then grabs the result CSS and puts it into a + // separate file in our build process. This way we actually ship + // a single CSS file in production instead of JS code injecting <style> + // tags. If you use code splitting, however, any async bundles will still + // use the "style" loader inside the async code so CSS from them won't be + // in the main CSS file. + { + test: /\.css$/, + // "?-autoprefixer" disables autoprefixer in css-loader itself: + // https://github.com/webpack/css-loader/issues/281 + // We already have it thanks to postcss. We only pass this flag in + // production because "css" loader only enables autoprefixer-powered + // removal of unnecessary prefixes when Uglify plugin is enabled. + // Webpack 1.x uses Uglify plugin as a signal to minify *all* the assets + // including CSS. This is confusing and will be removed in Webpack 2: + // https://github.com/webpack/webpack/issues/283 + loader: ExtractTextPlugin.extract('style', 'css?importLoaders=1&-autoprefixer!postcss') + // Note: this won't work without `new ExtractTextPlugin()` in `plugins`. + }, + // JSON is not enabled by default in Webpack but both Node and Browserify + // allow it implicitly so we also enable it. + { + test: /\.json$/, + loader: 'json' + }, + // "file" loader makes sure those assets end up in the `build` folder. + // When you `import` an asset, you get its filename. + { + test: /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2)(\?.*)?$/, + loader: 'file', + query: { + name: 'static/media/[name].[hash:8].[ext]' + } + }, + // "url" loader works just like "file" loader but it also embeds + // assets smaller than specified size as data URLs to avoid requests. + { + test: /\.(mp4|webm|wav|mp3|m4a|aac|oga)(\?.*)?$/, + loader: 'url', + query: { + limit: 10000, + name: 'static/media/[name].[hash:8].[ext]' + } + }, + { + test: /\.(html)(\?.*)?$/, + loader: 'raw' + } + + ] + }, + + // We use PostCSS for autoprefixing only. + postcss: function() { + return [ + autoprefixer({ + browsers: [ + '>1%', + 'last 4 versions', + 'Firefox ESR', + 'not ie < 9', // React doesn't support IE8 anyway + ] + }), + ]; + }, + plugins: [ + // Makes the public URL available as %PUBLIC_URL% in index.html, e.g.: + // <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico"> + // In production, it will be an empty string unless you specify "homepage" + // in `package.json`, in which case it will be the pathname of that URL. + new InterpolateHtmlPlugin({ + PUBLIC_URL: publicUrl + }), + // Generates an `index.html` file with the <script> injected. + new HtmlWebpackPlugin({ + inject: true, + template: paths.appHtml, + minify: { + removeComments: true, + collapseWhitespace: true, + removeRedundantAttributes: true, + useShortDoctype: true, + removeEmptyAttributes: true, + removeStyleLinkTypeAttributes: true, + keepClosingSlash: true, + minifyJS: true, + minifyCSS: true, + minifyURLs: true + } + }), + // Makes some environment variables available to the JS code, for example: + // if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`. + // It is absolutely essential that NODE_ENV was set to production here. + // Otherwise React will be compiled in the very slow development mode. + new webpack.DefinePlugin(env), + // This helps ensure the builds are consistent if source hasn't changed: + new webpack.optimize.OccurrenceOrderPlugin(), + // Try to dedupe duplicated modules, if any: + new webpack.optimize.DedupePlugin(), + // Minify the code. + new webpack.optimize.UglifyJsPlugin({ + compress: { + screw_ie8: true, // React doesn't support IE8 + warnings: false + }, + mangle: { + screw_ie8: true + }, + output: { + comments: false, + screw_ie8: true + } + }), + // Note: this won't work without ExtractTextPlugin.extract(..) in `loaders`. + new ExtractTextPlugin('static/css/[name].[contenthash:8].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' + }) + ], + // Some libraries import Node modules but don't use them in the browser. + // Tell Webpack to provide empty mocks for them so importing them works. + node: { + fs: 'empty', + net: 'empty', + tls: 'empty' + } +}; diff --git a/package.json b/package.json @@ -0,0 +1,90 @@ +{ + "name": "nakarte", + "version": "0.1.0", + "private": true, + "devDependencies": { + "autoprefixer": "6.5.1", + "babel-core": "6.17.0", + "babel-eslint": "7.0.0", + "babel-jest": "16.0.0", + "babel-loader": "6.2.5", + "babel-preset-react-app": "^1.0.0", + "case-sensitive-paths-webpack-plugin": "1.1.4", + "chalk": "1.1.3", + "connect-history-api-fallback": "1.3.0", + "cross-spawn": "4.0.2", + "css-loader": "0.25.0", + "detect-port": "1.0.1", + "dotenv": "2.0.0", + "eslint": "3.8.1", + "eslint-config-react-app": "^0.3.0", + "eslint-loader": "1.6.0", + "eslint-plugin-flowtype": "2.21.0", + "eslint-plugin-import": "2.0.1", + "eslint-plugin-jsx-a11y": "2.2.3", + "eslint-plugin-react": "6.4.1", + "extract-text-webpack-plugin": "1.0.1", + "file-loader": "0.9.0", + "filesize": "3.3.0", + "find-cache-dir": "0.1.1", + "fs-extra": "0.30.0", + "gzip-size": "3.0.0", + "html-webpack-plugin": "2.24.0", + "http-proxy-middleware": "0.17.2", + "jest": "16.0.2", + "json-loader": "0.5.4", + "object-assign": "4.1.0", + "path-exists": "2.1.0", + "postcss-loader": "1.0.0", + "promise": "7.1.1", + "react-dev-utils": "^0.3.0", + "recursive-readdir": "2.1.0", + "rimraf": "2.5.4", + "strip-ansi": "3.0.1", + "style-loader": "0.13.1", + "url-loader": "0.5.7", + "webpack": "1.13.2", + "webpack-dev-server": "1.16.2", + "webpack-manifest-plugin": "1.1.0", + "whatwg-fetch": "1.0.0" + }, + "dependencies": { + "knockout": "^3.4.0", + "leaflet": "^1.0.1", + "load-script": "^1.0.0", + "raw-loader": "^0.5.1", + "react": "^15.3.2", + "react-dom": "^15.3.2" + }, + "scripts": { + "start": "node scripts/start.js", + "build": "node scripts/build.js", + "test": "node scripts/test.js --env=jsdom" + }, + "jest": { + "moduleFileExtensions": [ + "jsx", + "js", + "json" + ], + "moduleNameMapper": { + "^.+\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/config/jest/FileStub.js", + "^.+\\.css$": "<rootDir>/config/jest/CSSStub.js" + }, + "setupFiles": [ + "<rootDir>/config/polyfills.js" + ], + "testPathIgnorePatterns": [ + "<rootDir>/(build|docs|node_modules)/" + ], + "testEnvironment": "node" + }, + "babel": { + "presets": [ + "react-app" + ] + }, + "eslintConfig": { + "extends": "react-app" + } +} diff --git a/scripts/build.js b/scripts/build.js @@ -0,0 +1,205 @@ +// Do this as the first thing so that any code reading it knows the right env. +process.env.NODE_ENV = 'production'; + +// Load environment variables from .env file. Suppress warnings using silent +// if this file is missing. dotenv will never modify any environment variables +// that have already been set. +// https://github.com/motdotla/dotenv +require('dotenv').config({silent: true}); + +var chalk = require('chalk'); +var fs = require('fs-extra'); +var path = require('path'); +var filesize = require('filesize'); +var gzipSize = require('gzip-size').sync; +var rimrafSync = require('rimraf').sync; +var webpack = require('webpack'); +var config = require('../config/webpack.config.prod'); +var paths = require('../config/paths'); +var checkRequiredFiles = require('react-dev-utils/checkRequiredFiles'); +var recursive = require('recursive-readdir'); +var stripAnsi = require('strip-ansi'); + +// Warn and crash if required files are missing +if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) { + process.exit(1); +} + +// Input: /User/dan/app/build/static/js/main.82be8.js +// Output: /static/js/main.js +function removeFileNameHash(fileName) { + return fileName + .replace(paths.appBuild, '') + .replace(/\/?(.*)(\.\w+)(\.js|\.css)/, (match, p1, p2, p3) => p1 + p3); +} + +// Input: 1024, 2048 +// Output: "(+1 KB)" +function getDifferenceLabel(currentSize, previousSize) { + var FIFTY_KILOBYTES = 1024 * 50; + var difference = currentSize - previousSize; + var fileSize = !Number.isNaN(difference) ? filesize(difference) : 0; + if (difference >= FIFTY_KILOBYTES) { + return chalk.red('+' + fileSize); + } else if (difference < FIFTY_KILOBYTES && difference > 0) { + return chalk.yellow('+' + fileSize); + } else if (difference < 0) { + return chalk.green(fileSize); + } else { + return ''; + } +} + +// First, read the current file sizes in build directory. +// This lets us display how much they changed later. +recursive(paths.appBuild, (err, fileNames) => { + var previousSizeMap = (fileNames || []) + .filter(fileName => /\.(js|css)$/.test(fileName)) + .reduce((memo, fileName) => { + var contents = fs.readFileSync(fileName); + var key = removeFileNameHash(fileName); + memo[key] = gzipSize(contents); + return memo; + }, {}); + + // Remove all content but keep the directory so that + // if you're in it, you don't end up in Trash + rimrafSync(paths.appBuild + '/*'); + + // Start the webpack build + build(previousSizeMap); + + // Merge with the public folder + copyPublicFolder(); +}); + +// Print a detailed summary of build files. +function printFileSizes(stats, previousSizeMap) { + var assets = stats.toJson().assets + .filter(asset => /\.(js|css)$/.test(asset.name)) + .map(asset => { + var fileContents = fs.readFileSync(paths.appBuild + '/' + asset.name); + var size = gzipSize(fileContents); + var previousSize = previousSizeMap[removeFileNameHash(asset.name)]; + var difference = getDifferenceLabel(size, previousSize); + return { + folder: path.join('build', path.dirname(asset.name)), + name: path.basename(asset.name), + size: size, + sizeLabel: filesize(size) + (difference ? ' (' + difference + ')' : '') + }; + }); + assets.sort((a, b) => b.size - a.size); + var longestSizeLabelLength = Math.max.apply(null, + assets.map(a => stripAnsi(a.sizeLabel).length) + ); + assets.forEach(asset => { + var sizeLabel = asset.sizeLabel; + var sizeLength = stripAnsi(sizeLabel).length; + if (sizeLength < longestSizeLabelLength) { + var rightPadding = ' '.repeat(longestSizeLabelLength - sizeLength); + sizeLabel += rightPadding; + } + console.log( + ' ' + sizeLabel + + ' ' + chalk.dim(asset.folder + path.sep) + chalk.cyan(asset.name) + ); + }); +} + +// Print out errors +function printErrors(summary, errors) { + console.log(chalk.red(summary)); + console.log(); + errors.forEach(err => { + console.log(err.message || err); + console.log(); + }); +} + +// Create the production build and print the deployment instructions. +function build(previousSizeMap) { + console.log('Creating an optimized production build...'); + webpack(config).run((err, stats) => { + if (err) { + printErrors('Failed to compile.', [err]); + process.exit(1); + } + + if (stats.compilation.errors.length) { + printErrors('Failed to compile.', stats.compilation.errors); + process.exit(1); + } + + console.log(chalk.green('Compiled successfully.')); + console.log(); + + console.log('File sizes after gzip:'); + console.log(); + printFileSizes(stats, previousSizeMap); + console.log(); + + var openCommand = process.platform === 'win32' ? 'start' : 'open'; + var homepagePath = require(paths.appPackageJson).homepage; + var publicPath = config.output.publicPath; + if (homepagePath && homepagePath.indexOf('.github.io/') !== -1) { + // "homepage": "http://user.github.io/project" + console.log('The project was built assuming it is hosted at ' + chalk.green(publicPath) + '.'); + console.log('You can control this with the ' + chalk.green('homepage') + ' field in your ' + chalk.cyan('package.json') + '.'); + console.log(); + console.log('The ' + chalk.cyan('build') + ' folder is ready to be deployed.'); + console.log('To publish it at ' + chalk.green(homepagePath) + ', run:'); + console.log(); + console.log(' ' + chalk.cyan('npm') + ' install --save-dev gh-pages'); + console.log(); + console.log('Add the following script in your ' + chalk.cyan('package.json') + '.'); + console.log(); + console.log(' ' + chalk.dim('// ...')); + console.log(' ' + chalk.yellow('"scripts"') + ': {'); + console.log(' ' + chalk.dim('// ...')); + console.log(' ' + chalk.yellow('"deploy"') + ': ' + chalk.yellow('"gh-pages -d build"')); + console.log(' }'); + console.log(); + console.log('Then run:'); + console.log(); + console.log(' ' + chalk.cyan('npm') + ' run deploy'); + console.log(); + } else if (publicPath !== '/') { + // "homepage": "http://mywebsite.com/project" + console.log('The project was built assuming it is hosted at ' + chalk.green(publicPath) + '.'); + console.log('You can control this with the ' + chalk.green('homepage') + ' field in your ' + chalk.cyan('package.json') + '.'); + console.log(); + console.log('The ' + chalk.cyan('build') + ' folder is ready to be deployed.'); + console.log(); + } else { + // no homepage or "homepage": "http://mywebsite.com" + console.log('The project was built assuming it is hosted at the server root.'); + if (homepagePath) { + // "homepage": "http://mywebsite.com" + console.log('You can control this with the ' + chalk.green('homepage') + ' field in your ' + chalk.cyan('package.json') + '.'); + console.log(); + } else { + // no homepage + console.log('To override this, specify the ' + chalk.green('homepage') + ' in your ' + chalk.cyan('package.json') + '.'); + console.log('For example, add this to build it for GitHub Pages:') + console.log(); + console.log(' ' + chalk.green('"homepage"') + chalk.cyan(': ') + chalk.green('"http://myname.github.io/myapp"') + chalk.cyan(',')); + console.log(); + } + console.log('The ' + chalk.cyan('build') + ' folder is ready to be deployed.'); + console.log('You may also serve it locally with a static server:') + console.log(); + console.log(' ' + chalk.cyan('npm') + ' install -g pushstate-server'); + console.log(' ' + chalk.cyan('pushstate-server') + ' build'); + console.log(' ' + chalk.cyan(openCommand) + ' http://localhost:9000'); + console.log(); + } + }); +} + +function copyPublicFolder() { + fs.copySync(paths.appPublic, paths.appBuild, { + dereference: true, + filter: file => file !== paths.appHtml + }); +} diff --git a/scripts/start.js b/scripts/start.js @@ -0,0 +1,268 @@ +process.env.NODE_ENV = 'development'; + +// Load environment variables from .env file. Suppress warnings using silent +// if this file is missing. dotenv will never modify any environment variables +// that have already been set. +// https://github.com/motdotla/dotenv +require('dotenv').config({silent: true}); + +var chalk = require('chalk'); +var webpack = require('webpack'); +var WebpackDevServer = require('webpack-dev-server'); +var historyApiFallback = require('connect-history-api-fallback'); +var httpProxyMiddleware = require('http-proxy-middleware'); +var detect = require('detect-port'); +var clearConsole = require('react-dev-utils/clearConsole'); +var checkRequiredFiles = require('react-dev-utils/checkRequiredFiles'); +var formatWebpackMessages = require('react-dev-utils/formatWebpackMessages'); +var openBrowser = require('react-dev-utils/openBrowser'); +var prompt = require('react-dev-utils/prompt'); +var config = require('../config/webpack.config.dev'); +var paths = require('../config/paths'); + +// Warn and crash if required files are missing +if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) { + process.exit(1); +} + +// Tools like Cloud9 rely on this. +var DEFAULT_PORT = process.env.PORT || 3000; +var compiler; +var handleCompile; + +// You can safely remove this after ejecting. +// We only use this block for testing of Create React App itself: +var isSmokeTest = process.argv.some(arg => arg.indexOf('--smoke-test') > -1); +if (isSmokeTest) { + handleCompile = function (err, stats) { + if (err || stats.hasErrors() || stats.hasWarnings()) { + process.exit(1); + } else { + process.exit(0); + } + }; +} + +function setupCompiler(host, port, protocol) { + // "Compiler" is a low-level interface to Webpack. + // It lets us listen to some events and provide our own custom messages. + compiler = webpack(config, handleCompile); + + // "invalid" event fires when you have changed a file, and Webpack is + // recompiling a bundle. WebpackDevServer takes care to pause serving the + // bundle, so if you refresh, it'll wait instead of serving the old one. + // "invalid" is short for "bundle invalidated", it doesn't imply any errors. + compiler.plugin('invalid', function() { + clearConsole(); + console.log('Compiling...'); + }); + + // "done" event fires when Webpack has finished recompiling the bundle. + // Whether or not you have warnings or errors, you will get this event. + compiler.plugin('done', function(stats) { + clearConsole(); + + // We have switched off the default Webpack output in WebpackDevServer + // options so we are going to "massage" the warnings and errors and present + // them in a readable focused way. + var messages = formatWebpackMessages(stats.toJson({}, true)); + if (!messages.errors.length && !messages.warnings.length) { + console.log(chalk.green('Compiled successfully!')); + console.log(); + console.log('The app is running at:'); + console.log(); + console.log(' ' + chalk.cyan(protocol + '://' + host + ':' + port + '/')); + console.log(); + console.log('Note that the development build is not optimized.'); + console.log('To create a production build, use ' + chalk.cyan('npm run build') + '.'); + console.log(); + } + + // If errors exist, only show errors. + if (messages.errors.length) { + console.log(chalk.red('Failed to compile.')); + console.log(); + messages.errors.forEach(message => { + console.log(message); + console.log(); + }); + return; + } + + // Show warnings if no errors were found. + if (messages.warnings.length) { + console.log(chalk.yellow('Compiled with warnings.')); + console.log(); + messages.warnings.forEach(message => { + console.log(message); + console.log(); + }); + // Teach some ESLint tricks. + console.log('You may use special comments to disable some warnings.'); + console.log('Use ' + chalk.yellow('// eslint-disable-next-line') + ' to ignore the next line.'); + console.log('Use ' + chalk.yellow('/* eslint-disable */') + ' to ignore all warnings in a file.'); + } + }); +} + +// We need to provide a custom onError function for httpProxyMiddleware. +// It allows us to log custom error messages on the console. +function onProxyError(proxy) { + return function(err, req, res){ + var host = req.headers && req.headers.host; + console.log( + chalk.red('Proxy error:') + ' Could not proxy request ' + chalk.cyan(req.url) + + ' from ' + chalk.cyan(host) + ' to ' + chalk.cyan(proxy) + '.' + ); + console.log( + 'See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (' + + chalk.cyan(err.code) + ').' + ); + console.log(); + + // And immediately send the proper error response to the client. + // Otherwise, the request will eventually timeout with ERR_EMPTY_RESPONSE on the client side. + if (res.writeHead && !res.headersSent) { + res.writeHead(500); + } + res.end('Proxy error: Could not proxy request ' + req.url + ' from ' + + host + ' to ' + proxy + ' (' + err.code + ').' + ); + } +} + +function addMiddleware(devServer) { + // `proxy` lets you to specify a fallback server during development. + // Every unrecognized request will be forwarded to it. + var proxy = require(paths.appPackageJson).proxy; + devServer.use(historyApiFallback({ + // Paths with dots should still use the history fallback. + // See https://github.com/facebookincubator/create-react-app/issues/387. + disableDotRule: true, + // For single page apps, we generally want to fallback to /index.html. + // However we also want to respect `proxy` for API calls. + // So if `proxy` is specified, we need to decide which fallback to use. + // We use a heuristic: if request `accept`s text/html, we pick /index.html. + // Modern browsers include text/html into `accept` header when navigating. + // However API calls like `fetch()` won’t generally accept text/html. + // If this heuristic doesn’t work well for you, don’t use `proxy`. + htmlAcceptHeaders: proxy ? + ['text/html'] : + ['text/html', '*/*'] + })); + if (proxy) { + if (typeof proxy !== 'string') { + console.log(chalk.red('When specified, "proxy" in package.json must be a string.')); + console.log(chalk.red('Instead, the type of "proxy" was "' + typeof proxy + '".')); + console.log(chalk.red('Either remove "proxy" from package.json, or make it a string.')); + process.exit(1); + } + + // Otherwise, if proxy is specified, we will let it handle any request. + // There are a few exceptions which we won't send to the proxy: + // - /index.html (served as HTML5 history API fallback) + // - /*.hot-update.json (WebpackDevServer uses this too for hot reloading) + // - /sockjs-node/* (WebpackDevServer uses this for hot reloading) + // Tip: use https://jex.im/regulex/ to visualize the regex + var mayProxy = /^(?!\/(index\.html$|.*\.hot-update\.json$|sockjs-node\/)).*$/; + devServer.use(mayProxy, + // Pass the scope regex both to Express and to the middleware for proxying + // of both HTTP and WebSockets to work without false positives. + httpProxyMiddleware(pathname => mayProxy.test(pathname), { + target: proxy, + logLevel: 'silent', + onError: onProxyError(proxy), + secure: false, + changeOrigin: true + }) + ); + } + // Finally, by now we have certainly resolved the URL. + // It may be /index.html, so let the dev server try serving it again. + devServer.use(devServer.middleware); +} + +function runDevServer(host, port, protocol) { + var devServer = new WebpackDevServer(compiler, { + // 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 won’t 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_PATH%: + // <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, + // 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: config.output.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 + watchOptions: { + ignored: /node_modules/ + }, + // Enable HTTPS if the HTTPS environment variable is set to 'true' + https: protocol === "https", + host: host + }); + + // Our custom middleware proxies requests to /index.html or a remote API. + addMiddleware(devServer); + + // Launch WebpackDevServer. + devServer.listen(port, (err, result) => { + if (err) { + return console.log(err); + } + + clearConsole(); + console.log(chalk.cyan('Starting the development server...')); + console.log(); + openBrowser(protocol + '://' + host + ':' + port + '/'); + }); +} + +function run(port) { + var protocol = process.env.HTTPS === 'true' ? "https" : "http"; + var host = process.env.HOST || 'localhost'; + setupCompiler(host, port, protocol); + runDevServer(host, port, protocol); +} + +// We attempt to use the default port but if it is busy, we offer the user to +// run on a different port. `detect()` Promise resolves to the next free port. +detect(DEFAULT_PORT).then(port => { + if (port === DEFAULT_PORT) { + run(port); + return; + } + + clearConsole(); + var question = + chalk.yellow('Something is already running on port ' + DEFAULT_PORT + '.') + + '\n\nWould you like to run the app on another port instead?'; + + prompt(question, true).then(shouldChangePort => { + if (shouldChangePort) { + run(port); + } + }); +}); diff --git a/scripts/test.js b/scripts/test.js @@ -0,0 +1,19 @@ +process.env.NODE_ENV = 'test'; +process.env.PUBLIC_URL = ''; + +// Load environment variables from .env file. Suppress warnings using silent +// if this file is missing. dotenv will never modify any environment variables +// that have already been set. +// https://github.com/motdotla/dotenv +require('dotenv').config({silent: true}); + +const jest = require('jest'); +const argv = process.argv.slice(2); + +// Watch unless on CI +if (!process.env.CI) { + argv.push('--watch'); +} + + +jest.run(argv);