From cbd5c1e5ce136f019ce146849d41b2001cd8d214 Mon Sep 17 00:00:00 2001 From: Ade Viankakrisna Fadlil Date: Wed, 8 Mar 2017 03:49:30 +0700 Subject: [PATCH 1/7] add watch script to react-scripts --- package.json | 5 +- packages/react-dev-utils/cleanBuildFolder.js | 29 ++ packages/react-dev-utils/isProduction.js | 14 + .../config/webpack.config.watch.js | 303 ++++++++++++++++++ packages/react-scripts/scripts/init.js | 1 + packages/react-scripts/scripts/watch.js | 191 +++++++++++ 6 files changed, 541 insertions(+), 2 deletions(-) create mode 100644 packages/react-dev-utils/cleanBuildFolder.js create mode 100644 packages/react-dev-utils/isProduction.js create mode 100644 packages/react-scripts/config/webpack.config.watch.js create mode 100644 packages/react-scripts/scripts/watch.js diff --git a/package.json b/package.json index 9cf2f50f14f..8afdb7c904d 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ "screencast": "svg-term --cast hItN7sl5yfCPTHxvFg5glhhfp --out screencast.svg --window", "test": "node packages/react-scripts/scripts/test.js --env=jsdom", "format": "prettier --trailing-comma es5 --single-quote --write 'packages/*/*.js' 'packages/*/!(node_modules)/**/*.js'", - "precommit": "lint-staged" + "precommit": "lint-staged", + "watch": "node packages/react-scripts/scripts/watch.js" }, "devDependencies": { "eslint": "^4.4.1", @@ -32,4 +33,4 @@ "git rm --cached" ] } -} +} \ No newline at end of file diff --git a/packages/react-dev-utils/cleanBuildFolder.js b/packages/react-dev-utils/cleanBuildFolder.js new file mode 100644 index 00000000000..3943dfe0cae --- /dev/null +++ b/packages/react-dev-utils/cleanBuildFolder.js @@ -0,0 +1,29 @@ +"use strict"; +var recursive = require("recursive-readdir"); +var path = require("path"); +var fs = require("fs"); +var difference = require("lodash/difference"); + +module.exports = function cleanBuildFolder(appBuild, stats) { + return new Promise(resolve => { + // Read the current contents of build folder + recursive(appBuild, (err, fileNames) => { + var assets = stats.toJson().assets; + var assetFileNames = assets + .map(asset => path.join(appBuild, asset.name)) // Get the full path from stats + .filter(Boolean); + + // Check the differences in hash + var differences = difference(fileNames,assetFileNames) + .filter((names) => names.includes(path.join(appBuild, 'static'))) + // Only delete in the static folder + + differences.forEach(file => { + if (fs.existsSync(file)) { + fs.unlinkSync(file); + } + }); + resolve(differences); + }); + }); +}; \ No newline at end of file diff --git a/packages/react-dev-utils/isProduction.js b/packages/react-dev-utils/isProduction.js new file mode 100644 index 00000000000..335cde44ccc --- /dev/null +++ b/packages/react-dev-utils/isProduction.js @@ -0,0 +1,14 @@ +// @remove-on-eject-begin +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +// @remove-on-eject-end +"use strict" +module.exports = function isProduction(){ + return process.env.NODE_ENV === 'production' +} \ No newline at end of file diff --git a/packages/react-scripts/config/webpack.config.watch.js b/packages/react-scripts/config/webpack.config.watch.js new file mode 100644 index 00000000000..38236be090a --- /dev/null +++ b/packages/react-scripts/config/webpack.config.watch.js @@ -0,0 +1,303 @@ +// @remove-on-eject-begin +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +// @remove-on-eject-end +"use strict" +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 paths = require('./paths'); +var getClientEnvironment = require('./env'); +var isProduction = require('react-dev-utils/isProduction'); + +// @remove-on-eject-begin +// `path` is not used after eject - see https://github.com/facebookincubator/create-react-app/issues/1174 +var path = require('path'); +// @remove-on-eject-end + +// 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 = paths.servedPath; +// Some apps do not use client-side routing with pushState. +// For these, "homepage" can be set to "." to enable relative asset paths. +var shouldUseRelativeAssetPaths = 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_URL%/xyz looks better than %PUBLIC_URL%xyz. +var publicUrl = publicPath.slice(0, -1); +// Get environment variables to inject into our app. +var env = getClientEnvironment(publicUrl); + +// Fall back to development if not production +if (env.stringified['process.env'].NODE_ENV !== '"production"') { + process.env.NODE_ENV = 'development'; +} + +// Note: defined here because it will be used more than once. +const cssFilename = 'static/css/' + + process.env.NODE_ENV + + '.[name].[contenthash:8].css'; + +// ExtractTextPlugin expects the build output to be flat. +// (See https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/27) +// However, our output is structured with css, js and media folders. +// To have this structure working with relative paths, we have to use custom options. +const extractTextPluginOptions = shouldUseRelativeAssetPaths + ? // Making sure that the publicPath goes back to to build folder. + { publicPath: Array(cssFilename.split('/').length).join('../') } + : {}; + +// This is the watching configuration. +// It defaults to development mode for quick iteration +module.exports = { + watch: true, + context: paths.appSrc, + devtool: isProduction() ? 'source-map' : 'cheap-module-source-map', + // There are no support for live reloading changes in this mode + 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. + // There's prefix in the filename to identify which NODE_ENV generated this build + filename: 'static/js/' + process.env.NODE_ENV + '.[name].[chunkhash:8].js', + chunkFilename: ( + 'static/js/' + process.env.NODE_ENV + '.[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 placed these paths second because we want `node_modules` to "win" + // if there are any conflicts. This matches Node resolution mechanism. + // https://github.com/facebookincubator/create-react-app/issues/253 + modules: ['node_modules'].concat(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' + } + }, + // @remove-on-eject-begin + // Resolve loaders (webpack plugins for CSS, images, transpilation) from the + // directory of `react-scripts` itself rather than the project directory. + resolveLoader: { + modules: [ + paths.ownNodeModules, + // Lerna hoists everything, so we need to look in our app directory + paths.appNodeModules + ] + }, + // @remove-on-eject-end + // This part is essentially the same as production configuration, but it remove + // the minification on development + module: { + rules: [ + // Disable require.ensure as it's not a standard language feature. + { parser: { requireEnsure: false } }, + // First, run the linter. + // It's important to do this before Babel processes the JS. + { + test: /\.(js|jsx)$/, + enforce: 'pre', + use: [ + { + // @remove-on-eject-begin + // Point ESLint to our predefined config. + options: { + // TODO: consider separate config for production, + // e.g. to enable no-console and no-debugger only in production. + configFile: path.join(__dirname, '../.eslintrc'), + useEslintrc: false + }, + // @remove-on-eject-end + loader: 'eslint-loader' + } + ], + include: paths.appSrc + }, + // ** ADDING/UPDATING LOADERS ** + // The "url" loader handles all assets unless explicitly excluded. + // The `exclude` list *must* be updated with every change to loader extensions. + // When adding a new loader, you must add its `test` + // as a new entry in the `exclude` list in the "url" loader. + + // "url" loader embeds assets smaller than specified size as data URLs to avoid requests. + // Otherwise, it acts like the "file" loader. + { + exclude: [/\.html$/, /\.(js|jsx)$/, /\.css$/, /\.json$/, /\.svg$/], + loader: 'url-loader', + options: { + limit: 10000, + name: ( + 'static/media/' + process.env.NODE_ENV + '.[name].[hash:8].[ext]' + ) + } + }, + // Process JS with Babel. + { + test: /\.(js|jsx)$/, + include: paths.appSrc, + loader: 'babel-loader', + // @remove-on-eject-begin + options: { + babelrc: false, + presets: [require.resolve('babel-preset-react-app')] + } + + // @remove-on-eject-end + }, + // 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