From 714c699108ede31750f0716b568250d7f092c4c8 Mon Sep 17 00:00:00 2001 From: Salley Liu Date: Tue, 26 Apr 2022 14:23:32 +0800 Subject: [PATCH 01/10] Add all files in backpack-addons --- .../react-scripts/backpack-addons/README.md | 34 ++ .../backpack-addons/amdExcludes.js | 16 + .../backpack-addons/babelIncludePrefixes.js | 25 ++ .../backpack-addons/crossOriginLoading.js | 11 + .../backpack-addons/cssModules.js | 65 ++++ .../backpack-addons/externals.js | 27 ++ .../backpack-addons/ignoreCssWarnings.js | 10 + .../backpack-addons/runtimeChunk.js | 22 ++ .../backpack-addons/sassFunctions.js | 14 + .../backpack-addons/splitChunks.js | 26 ++ .../backpack-addons/sriEnabled.js | 21 ++ .../backpack-addons/ssr/MultiCompilerUi.js | 307 ++++++++++++++++++ .../backpack-addons/ssr/customWebpackUtils.js | 201 ++++++++++++ .../backpack-addons/ssr/forkSsr.js | 67 ++++ .../backpack-addons/ssr/isSsr.js | 31 ++ .../backpack-addons/ssr/statusFile.js | 31 ++ 16 files changed, 908 insertions(+) create mode 100644 packages/react-scripts/backpack-addons/README.md create mode 100644 packages/react-scripts/backpack-addons/amdExcludes.js create mode 100644 packages/react-scripts/backpack-addons/babelIncludePrefixes.js create mode 100644 packages/react-scripts/backpack-addons/crossOriginLoading.js create mode 100644 packages/react-scripts/backpack-addons/cssModules.js create mode 100644 packages/react-scripts/backpack-addons/externals.js create mode 100644 packages/react-scripts/backpack-addons/ignoreCssWarnings.js create mode 100644 packages/react-scripts/backpack-addons/runtimeChunk.js create mode 100644 packages/react-scripts/backpack-addons/sassFunctions.js create mode 100644 packages/react-scripts/backpack-addons/splitChunks.js create mode 100644 packages/react-scripts/backpack-addons/sriEnabled.js create mode 100644 packages/react-scripts/backpack-addons/ssr/MultiCompilerUi.js create mode 100644 packages/react-scripts/backpack-addons/ssr/customWebpackUtils.js create mode 100644 packages/react-scripts/backpack-addons/ssr/forkSsr.js create mode 100644 packages/react-scripts/backpack-addons/ssr/isSsr.js create mode 100644 packages/react-scripts/backpack-addons/ssr/statusFile.js diff --git a/packages/react-scripts/backpack-addons/README.md b/packages/react-scripts/backpack-addons/README.md new file mode 100644 index 0000000000..154d4cf278 --- /dev/null +++ b/packages/react-scripts/backpack-addons/README.md @@ -0,0 +1,34 @@ +Our react scripts fork includes a number of custom configuration items in order to support building web products at Skyscanner. The table below will describe what each of the configs do + +## Features Description + +| Feature | Description | Default Value | +|:---|:--|:---| +| **babelIncludePrefixes** | Array of module name prefixes to opt into babel compilation.
Default includes **@skyscanner/bpk-, bpk- and saddlebag-** packages by default to be compiled | **[@skyscanner/bpk-, bpk- and saddlebag-]** | +| **sriEnabled** | Determines if Subresource Intergrity is used during build to add an integrity hash for files.
The SRI is a security feature to enable browsers to verify the files they fetch are unmodified.
If enabled crossOriginLoading value is overriden with anonymous to allow output to have integrity value
See [webpack subresource integrity docs](https://github.com/waysact/webpack-subresource-integrity/blob/master/README.md) | **false** (this is currently the default in the future security may want to make it true by default but pending them still trying things about) | +| **crossOriginLoading** | Option to enable cross origin loading of chunks to modify the default webpack behaviour of being false.
Docs: https://webpack.js.org/configuration/output/#outputcrossoriginloading | **false** | +| **ignoreCssWarnings** | Provides the ablity to supress CSS ordering warnings when its safe and ordering is not of a concern on the output
See [mini css extract plugin docs](https://github.com/webpack-contrib/mini-css-extract-plugin#remove-order-warnings) | **false** - by default we should care about order as it can sometimes have an output impact | +| **cssModules** | Determines if cssModules are being used.
If enabled supports css modules and configures css-loader for use
If not enabled supports sass modules in the project and configures sass-loader for use | **true** | +| **amdExludes** | Array of module names to be excluded from AMD ([Asynchronous Module Definition](https://webpack.js.org/api/module-methods/#amd)) parsing.
lodash included by defeault. | **['lodash']** | +| **externals** | Provides a way of excluding dependencies from the bundles and instead relies on them being available at runtime on the clients environment E.g. React libraries.
See https://webpack.js.org/configuration/externals/ | **{}** | +| **ssrExternals** | The same as above `externals` except used for server side rendering only in **ssr.js** | **{}** | +| **enableAutomaticChunking** | Opts into automatic chunking of vender, common and app code.
When enabled the **splitChunks** plugin creates vender and common chunks which are split and when provided uses the `venderChunkRegex` to specify what is in each chunk.
When enabled **runtimeChunk** plugin creates a separate runtime chunk for projects to enable long term caching. | **false** | +| **vendorsChunkRegex** | Regex for picking what goes into the vendors chunk. Requires enableAutomaticChunking to be enabled.
See [cacheGroups](https://webpack.js.org/plugins/split-chunks-plugin/#splitchunkscachegroups) docs for further details. | | +| **sassFunctions** | This function encodes svg content into `base64` when there is a `bpk-icon` in the.scss file. | | + +## How to add new feature + +- Add the custom feature to backpack-addons. +- Require features in the file(for example, webpack.config.js) where they are needed. + - For each line of code that is changed, make sure to add a comment `// #backpack-addon {{featureName}}` + - Try to keep the lines of code changed outside of the backpack-addons folder to a minimum +`require(...../backpack-addons/...)` files instead of writing it inline as much as possible. +- Add a description of the new feature in the table above the current document. + +## How to upgrade from the upstream `facebook/create-react-app` repo + +- Replace all of `packages/react-scripts` with the upstream version. +- Restore `packages/react-scripts/backpack-addons` all features. +- Restore (will require manual work+checking) every line which has a `// #backpack-addons` comment. +- Compare [upgrade] document and restore the rest of the content (for example, added files, other modified files). +- Test for project. \ No newline at end of file diff --git a/packages/react-scripts/backpack-addons/amdExcludes.js b/packages/react-scripts/backpack-addons/amdExcludes.js new file mode 100644 index 0000000000..28d1bdc308 --- /dev/null +++ b/packages/react-scripts/backpack-addons/amdExcludes.js @@ -0,0 +1,16 @@ +'use strict'; + +const paths = require('../config/paths'); +const appPackageJson = require(paths.appPackageJson); +const bpkReactScriptsConfig = appPackageJson['backpack-react-scripts'] || {}; + +module.exports = { + test: new RegExp( + `(^|/)(${(bpkReactScriptsConfig.amdExcludes || []) + .concat('lodash') + .join('|')})(/|.|$)` + ), + parser: { + amd: false, + } +}; \ No newline at end of file diff --git a/packages/react-scripts/backpack-addons/babelIncludePrefixes.js b/packages/react-scripts/backpack-addons/babelIncludePrefixes.js new file mode 100644 index 0000000000..cff6b2ae46 --- /dev/null +++ b/packages/react-scripts/backpack-addons/babelIncludePrefixes.js @@ -0,0 +1,25 @@ +"use strict"; + +const paths = require("../config/paths"); +const appPackageJson = require(paths.appPackageJson); +const bpkReactScriptsConfig = appPackageJson["backpack-react-scripts"] || {}; +const customModuleRegexes = bpkReactScriptsConfig.babelIncludePrefixes + ? bpkReactScriptsConfig.babelIncludePrefixes.map( + (prefix) => new RegExp(`node_modules[\\/]${prefix}`) + ) + : []; + +// Backpack / saddlebag node module regexes +const backpackModulesRegex = /node_modules[\\/]bpk-/; +const saddlebagModulesRegex = /node_modules[\\/]saddlebag-/; +const scopedBackpackModulesRegex = /node_modules[\\/]@skyscanner[\\/]bpk-/; + +module.exports = () => { + return [ + paths.appSrc, + backpackModulesRegex, + saddlebagModulesRegex, + scopedBackpackModulesRegex, + ...customModuleRegexes, + ]; +}; \ No newline at end of file diff --git a/packages/react-scripts/backpack-addons/crossOriginLoading.js b/packages/react-scripts/backpack-addons/crossOriginLoading.js new file mode 100644 index 0000000000..db8a2d2abe --- /dev/null +++ b/packages/react-scripts/backpack-addons/crossOriginLoading.js @@ -0,0 +1,11 @@ +'use strict'; + +const paths = require("../config/paths"); +const appPackageJson = require(paths.appPackageJson); +const bpkReactScriptsConfig = appPackageJson['backpack-react-scripts'] || {}; +const crossOriginLoading = bpkReactScriptsConfig.crossOriginLoading || false; +const sriEnabled = bpkReactScriptsConfig.sriEnabled || false; + +module.exports = { + crossOriginLoading: sriEnabled ? 'anonymous' : crossOriginLoading +} \ No newline at end of file diff --git a/packages/react-scripts/backpack-addons/cssModules.js b/packages/react-scripts/backpack-addons/cssModules.js new file mode 100644 index 0000000000..7afd088f8a --- /dev/null +++ b/packages/react-scripts/backpack-addons/cssModules.js @@ -0,0 +1,65 @@ +'use strict'; + +const postcssNormalize = require('postcss-normalize'); +const paths = require('../config/paths'); +const appPackageJson = require(paths.appPackageJson); +const bpkReactScriptsConfig = appPackageJson['backpack-react-scripts'] || {}; +const cssModulesEnabled = bpkReactScriptsConfig.cssModules !== false; + +// Backpack / saddlebag node module regexes +const backpackModulesRegex = /node_modules[\\/]bpk-/; +const scopedBackpackModulesRegex = /node_modules[\\/]@skyscanner[\\/]bpk-/; + +const getStyleTestRegexes = regexType => { + // style files regexes, the regex values should keep up to date with webpack.config.js + const cssRegex = /\.css$/; + const cssModuleRegex = /\.module\.css$/; + const sassRegex = /\.(scss|sass)$/; + const sassModuleRegex = /\.module\.(scss|sass)$/; + + switch (regexType) { + case 'css': + return { + and: [cssRegex, () => !cssModulesEnabled], + not: [backpackModulesRegex, scopedBackpackModulesRegex], + }; + case 'cssModule': + return [ + cssModuleRegex, + { + and: [cssRegex, () => cssModulesEnabled], + }, + { + and: [cssRegex, backpackModulesRegex, scopedBackpackModulesRegex], + }, + ]; + case 'sass': + return { + and: [sassRegex, () => !cssModulesEnabled], + not: [backpackModulesRegex, scopedBackpackModulesRegex], + }; + case 'sassModule': + return [ + sassModuleRegex, + { + and: [sassRegex, () => cssModulesEnabled], + }, + { + and: [sassRegex, backpackModulesRegex, scopedBackpackModulesRegex], + }, + ]; + default: + throw new Error('Not implemented.'); + } +}; + +const getCSSModuleLocalIdent = () => { + return require('../utils/getCSSModuleLocalIdentWithProjectName')( + appPackageJson.name + ); +}; + +module.exports = { + getStyleTestRegexes, + getCSSModuleLocalIdent, +}; \ No newline at end of file diff --git a/packages/react-scripts/backpack-addons/externals.js b/packages/react-scripts/backpack-addons/externals.js new file mode 100644 index 0000000000..9fe93241b8 --- /dev/null +++ b/packages/react-scripts/backpack-addons/externals.js @@ -0,0 +1,27 @@ +'use strict'; + +const paths = require('../config/paths'); +const appPackageJson = require(paths.appPackageJson); +const bpkReactScriptsConfig = appPackageJson['backpack-react-scripts'] || {}; + +function externals(isEnvProduction) { + if (!isEnvProduction) { + return { + externals: {}, + } + } + return { + externals: bpkReactScriptsConfig.externals || {}, + } +} + +function ssrExternals() { + return { + externals: bpkReactScriptsConfig.ssrExternals || [], + } +} + +module.exports = { + externals, + ssrExternals +}; \ No newline at end of file diff --git a/packages/react-scripts/backpack-addons/ignoreCssWarnings.js b/packages/react-scripts/backpack-addons/ignoreCssWarnings.js new file mode 100644 index 0000000000..fe4b83fc9c --- /dev/null +++ b/packages/react-scripts/backpack-addons/ignoreCssWarnings.js @@ -0,0 +1,10 @@ +'use strict'; + +const paths = require('../config/paths'); +const appPackageJson = require(paths.appPackageJson); +const bpkReactScriptsConfig = appPackageJson['backpack-react-scripts'] || {}; +const ignoreCssWarnings = bpkReactScriptsConfig.ignoreCssWarnings || false; + +module.exports = { + ignoreOrder: ignoreCssWarnings +} \ No newline at end of file diff --git a/packages/react-scripts/backpack-addons/runtimeChunk.js b/packages/react-scripts/backpack-addons/runtimeChunk.js new file mode 100644 index 0000000000..a937380496 --- /dev/null +++ b/packages/react-scripts/backpack-addons/runtimeChunk.js @@ -0,0 +1,22 @@ +'use strict'; + +const paths = require('../config/paths'); +const appPackageJson = require(paths.appPackageJson); +const bpkReactScriptsConfig = appPackageJson['backpack-react-scripts'] || {}; + +const runtimeChunk = { + runtimeChunk: bpkReactScriptsConfig.enableAutomaticChunking + ? { + name: entrypoint => `runtime-${entrypoint.name}`, + } + : false +} + +const ssrRuntimeChunk = { + runtimeChunk: false, +} + +module.exports = { + runtimeChunk, + ssrRuntimeChunk +}; \ No newline at end of file diff --git a/packages/react-scripts/backpack-addons/sassFunctions.js b/packages/react-scripts/backpack-addons/sassFunctions.js new file mode 100644 index 0000000000..e4b45dc4a0 --- /dev/null +++ b/packages/react-scripts/backpack-addons/sassFunctions.js @@ -0,0 +1,14 @@ +'use strict'; +const sass = require('node-sass'); + +module.exports = { + sassOptions: { + functions: { + 'encodebase64($string)': str => { + const buffer = Buffer.from(str.getValue()); + + return sass.types.String(buffer.toString('base64')); + } + } + } +}; \ No newline at end of file diff --git a/packages/react-scripts/backpack-addons/splitChunks.js b/packages/react-scripts/backpack-addons/splitChunks.js new file mode 100644 index 0000000000..77392e04b4 --- /dev/null +++ b/packages/react-scripts/backpack-addons/splitChunks.js @@ -0,0 +1,26 @@ +'use strict'; + +const paths = require('../config/paths'); +const appPackageJson = require(paths.appPackageJson); +const bpkReactScriptsConfig = appPackageJson['backpack-react-scripts'] || {}; + +module.exports = (isEnvDevelopment) => { + // Automatically split vendor and commons + // https://twitter.com/wSokra/status/969633336732905474 + // https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366 + return { + splitChunks: bpkReactScriptsConfig.enableAutomaticChunking + ? { + chunks: 'all', + name: isEnvDevelopment, + cacheGroups: bpkReactScriptsConfig.vendorsChunkRegex + ? { + defaultVendors: { + test: new RegExp(bpkReactScriptsConfig.vendorsChunkRegex) + }, + } + : {}, + } + : {} + } +}; \ No newline at end of file diff --git a/packages/react-scripts/backpack-addons/sriEnabled.js b/packages/react-scripts/backpack-addons/sriEnabled.js new file mode 100644 index 0000000000..83e740605e --- /dev/null +++ b/packages/react-scripts/backpack-addons/sriEnabled.js @@ -0,0 +1,21 @@ +"use strict"; + +const SubresourceIntegrityPlugin = require("webpack-subresource-integrity"); +const paths = require("../config/paths"); +const appPackageJson = require(paths.appPackageJson); +const bpkReactScriptsConfig = appPackageJson["backpack-react-scripts"] || {}; +const sriEnabled = bpkReactScriptsConfig.sriEnabled || false; + +module.exports = () => { + return ( + // Calculate and inject Subresource Integrity (SRI) hashes + // https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity + // This is a security feature that enables browsers to verify that resources + // they fetch (for example, from a CDN) are delivered without unexpected manipulation. + sriEnabled && + new SubresourceIntegrityPlugin({ + enabled: true, + hashFuncNames: ["sha384"], + }) + ); +}; \ No newline at end of file diff --git a/packages/react-scripts/backpack-addons/ssr/MultiCompilerUi.js b/packages/react-scripts/backpack-addons/ssr/MultiCompilerUi.js new file mode 100644 index 0000000000..51e340d187 --- /dev/null +++ b/packages/react-scripts/backpack-addons/ssr/MultiCompilerUi.js @@ -0,0 +1,307 @@ +const clearConsole = require('react-dev-utils/clearConsole'); +const chalk = require('chalk'); + +const isInteractive = process.stdout.isTTY; + +/** + * This class manages the output of multiple Webpack compilers to render a + * rudimentary Terminal UI. It's designed to be used with + * utils/customWebpackUtils.js#createCustomCompiler. + * + * Each Webpack compiler is represented by a CompilerUi instance, which can be + * extended. Each CompilerUi instance has a name which is used to track its + * output and label it in the UI. + * + * Several varieties of CompilerUi are provided for different use cases. + * + * See the start-ssr.js script for an example of it in action. + * + * @example + * const ui = new MultiCompilerUi(); + * + * // The create() method returns a basic, pre-registered CompilerUi + * const simpleUi = ui.create('simple', { color: 'blue' }); + * + * // You can extend CompilerUi to control behaviour + * class MyCustomUi extends CompilerUi { + * log(message = '') { + * // Customise the log method + * } + * } + * + * // CompilerUi instances must be registered before they're used. + * const customUi = new MyCustomUi('custom', { color: 'green' }); + * ui.register(customUi); + * + * // No output is generated until start() is called. + * ui.start(); + * + * // The log() method appends output, the clear() method clears it. + * simpleUi.log('Some info'); + * simpleUi.log('More info'); + * simpleUi.clear(); + * simpleUi.log('Fresh info'); + * + * // CompilerUi output is independent + * customUi.log('woah'); + * customUi.clear(); + */ +class MultiCompilerUi { + constructor() { + this.output = {}; + this.compilers = {}; + } + + /** + * Create and register a basic CompilerUi. + * + * @param {string} name - Used to label the compiler's output in the UI. + * @param {CompilerUiOptions} options + * @returns {CompilerUi} + */ + create(name, options = {}) { + const compiler = new CompilerUi(name, options); + this.register(compiler); + return compiler; + } + + /** + * Register a new CompilerUi instance. The instance can't be used until this + * method is called. + * + * @param {CompilerUi} compiler + * @returns {*} + */ + register(compiler) { + compiler.ui = this; + this.compilers[compiler.name] = compiler; + this.output[compiler.name] = ''; + + compiler.start(); + return compiler; + } + + append(name, message = '') { + this.output[name] += message + '\n'; + this.render(); + } + + clear(name) { + this.output[name] = ''; + + // Don't render in non-interactive mode, so that we don't spam the console + // with empty messages. + if (isInteractive) { + this.render(); + } + } + + start() { + this.started = true; + this.render(); + } + + render() { + if (!this.started) { + return; + } + + if (isInteractive) { + clearConsole(); + + // We only need a top border in interactive mode - in non-interactive mode + // the bottom border demarcates builds. + printBorder('='); + } + + Object.entries(this.compilers).forEach(([name, { options }], index) => { + const color = chalk.black[options.color]; + const label = isInteractive + ? color(` ${options.label || name} `) + : `[${options.label || name}]`; + + const message = this.output[name]; + console.log( + message + .trim() + .split('\n') + .map(line => `${label} ${line}`) + .join('\n') + ); + + // Build separator + if (index < Object.keys(this.compilers).length - 1) { + printBorder('-'); + } + }); + + // Bottom border + printBorder('='); + } +} + +/** + * @typedef {Object} CompilerUiOptions + * @property {string} color - A chalk method used to style output e.g. yellow, bgBlue + */ + +/** + * The CompilerUi manages the output of a single Webpack compiler, and can pass + * messages back to a MultiCompilerUi to be rendered. + * + * @property {string} name + * @property {CompilerUiOptions} options + * @property {MultiCompilerUi} [ui] - When a CompilerUi instance is passed to {@link MultiCompilerUi#register}, + * the MultiCompilerUi instance is attached. + */ +class CompilerUi { + constructor(name, options) { + this.name = name; + this.options = options; + } + + /** + * This method is called when the instance is registered with a MultiCompilerUi instance. + * It provides an opportunity to log a message before the first Webpack compiler + * event is received. + */ + start() { + this.log('Starting...'); + // We use this in log() to clear the initial message + this.new = true; + } + + log(message = '') { + // We want to replace the initial starting message the first time we receive an event + if (this.new) { + this.clear(); + this.new = false; + } + + this.ui.append(this.name, message); + } + + clear() { + this.ui.clear(this.name); + } + + printInstructions() {} +} + +/** + * A CompilerUi extended to show CRA's instructions for using the WebpackDevServer. + */ +class WebCompilerUi extends CompilerUi { + /** + * + * @param name + * @param options + * @param {Object} compilerConfig - Config object passed to createCustomCompiler + * @param {string} compilerConfig.appName + * @param {Object} compilerConfig.urls + * @param {boolean} compilerConfig.useYarn + */ + constructor(name, options, compilerConfig) { + super(name, options); + this.compilerConfig = compilerConfig; + } + + /** + * This function is taken directly from react-dev-utils/WebpackDevServerUtils.js, + * with all `console.log` references replaced with `this.log`. + */ + printInstructions() { + const { appName, urls, useYarn } = this.compilerConfig; + + this.log(); + this.log(`You can now view ${chalk.bold(appName)} in the browser.`); + this.log(); + + if (urls.lanUrlForTerminal) { + this.log( + ` ${chalk.bold('Local:')} ${urls.localUrlForTerminal}` + ); + this.log( + ` ${chalk.bold('On Your Network:')} ${urls.lanUrlForTerminal}` + ); + } else { + this.log(` ${urls.localUrlForTerminal}`); + } + + this.log(); + this.log('Note that the development build is not optimized.'); + this.log( + `To create a production build, use ` + + `${chalk.cyan(`${useYarn ? 'yarn' : 'npm run'} build`)}.` + ); + this.log(); + } +} + +function printBorder(char) { + console.log(); + console.log(char.repeat(process.stdout.columns || 30)); + console.log(); +} + +/** + * A CompilerUi designed to be used inside a forked process. It sends messages + * to its parent process with process.send. By convention it will send a "clear" + * message to denote the clear() method. + */ +class ProcessSendCompilerUi extends CompilerUi { + constructor() { + super(); + } + + log(message = '') { + process.send(message); + } + + clear() { + process.send('clear'); + } +} + +/** + * A CompilerUi designed to receive messages from a ProcessSendCompilerUi. Use + * this instance in the parent process. + */ +class ProcessMessageCompilerUi extends CompilerUi { + constructor(childProcess, name, options) { + super(name, options); + + childProcess.on('message', this.onMessage.bind(this)); + } + + onMessage(message) { + if (message === 'clear') { + this.clear(); + } else { + this.log(message); + } + } +} + +/** + * A CompilerUi that just logs messages - useful for debugging. + */ +class DebugCompilerUi extends CompilerUi { + constructor() { + super(); + } + + log(message = '') { + console.log(message); + } + + clear() {} +} + +module.exports = { + MultiCompilerUi, + WebCompilerUi, + ProcessSendCompilerUi, + ProcessMessageCompilerUi, + DebugCompilerUi, +}; \ No newline at end of file diff --git a/packages/react-scripts/backpack-addons/ssr/customWebpackUtils.js b/packages/react-scripts/backpack-addons/ssr/customWebpackUtils.js new file mode 100644 index 0000000000..ecc9fb42aa --- /dev/null +++ b/packages/react-scripts/backpack-addons/ssr/customWebpackUtils.js @@ -0,0 +1,201 @@ +const chalk = require('chalk'); +const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages'); +const typescriptFormatter = require('react-dev-utils/typescriptFormatter'); +const forkTsCheckerWebpackPlugin = require('react-dev-utils/ForkTsCheckerWebpackPlugin'); + +/** + * We set this to true so that we always call ui.clear() - we check for a TTY + * inside {@link MultiCompilerUi}. + */ +const isInteractive = true; + +/** + * This function overrides react-dev-utils/WebpackDevServerUtils.createCompiler. + * The original function directly manages console output, which doesn't work + * when running multiple compilers at once. This function accepts a + * {@link CompilerUi} instance and sends any output messages to it. + * + * It is a copy-paste of the source with the following references replaced: + * + * - console.log() -> ui.log() + * - clearConsole() -> ui.clear() + * - printInstructions() -> ui.printInstructions() + * + * To update this function in the future when CRA changes, just copy the original + * here and make the updates above. + * + * @param {CompilerUi} ui + * @param {Object} config - The same config object passed to CRA's createCompiler + * @returns {*} - A Webpack compiler + */ +function createCustomCompiler( + ui, + { + appName, + config, + devSocket, + urls, + useYarn, + useTypeScript, + tscCompileOnError, + webpack, + } +) { + // "Compiler" is a low-level interface to webpack. + // It lets us listen to some events and provide our own custom messages. + let compiler; + try { + compiler = webpack(config); + } catch (err) { + ui.log(chalk.red('Failed to compile.')); + ui.log(); + ui.log(err.message || err); + ui.log(); + process.exit(1); + } + + // "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.hooks.invalid.tap('invalid', () => { + if (isInteractive) { + ui.clear(); + } + ui.log('Compiling...'); + }); + + let isFirstCompile = true; + let tsMessagesPromise; + let tsMessagesResolver; + + if (useTypeScript) { + compiler.hooks.beforeCompile.tap('beforeCompile', () => { + tsMessagesPromise = new Promise(resolve => { + tsMessagesResolver = msgs => resolve(msgs); + }); + }); + + forkTsCheckerWebpackPlugin + .getCompilerHooks(compiler) + .receive.tap('afterTypeScriptCheck', (diagnostics, lints) => { + const allMsgs = [...diagnostics, ...lints]; + const format = message => + `${message.file}\n${typescriptFormatter(message, true)}`; + + tsMessagesResolver({ + errors: allMsgs.filter(msg => msg.severity === 'error').map(format), + warnings: allMsgs + .filter(msg => msg.severity === 'warning') + .map(format), + }); + }); + } + + // "done" event fires when webpack has finished recompiling the bundle. + // Whether or not you have warnings or errors, you will get this event. + compiler.hooks.done.tap('done', async stats => { + if (isInteractive) { + ui.clear(); + } + + // 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. + // We only construct the warnings and errors for speed: + // https://github.com/facebook/create-react-app/issues/4492#issuecomment-421959548 + const statsData = stats.toJson({ + all: false, + warnings: true, + errors: true, + }); + + if (useTypeScript && statsData.errors.length === 0) { + const delayedMsg = setTimeout(() => { + ui.log( + chalk.yellow( + 'Files successfully emitted, waiting for typecheck results...' + ) + ); + }, 100); + + const messages = await tsMessagesPromise; + clearTimeout(delayedMsg); + if (tscCompileOnError) { + statsData.warnings.push(...messages.errors); + } else { + statsData.errors.push(...messages.errors); + } + statsData.warnings.push(...messages.warnings); + + // Push errors and warnings into compilation result + // to show them after page refresh triggered by user. + if (tscCompileOnError) { + stats.compilation.warnings.push(...messages.errors); + } else { + stats.compilation.errors.push(...messages.errors); + } + stats.compilation.warnings.push(...messages.warnings); + + if (messages.errors.length > 0) { + if (tscCompileOnError) { + devSocket.warnings(messages.errors); + } else { + devSocket.errors(messages.errors); + } + } else if (messages.warnings.length > 0) { + devSocket.warnings(messages.warnings); + } + + if (isInteractive) { + ui.clear(); + } + } + + const messages = formatWebpackMessages(statsData); + const isSuccessful = !messages.errors.length && !messages.warnings.length; + if (isSuccessful) { + ui.log(chalk.green('Compiled successfully!')); + } + if (isSuccessful && (isInteractive || isFirstCompile)) { + ui.printInstructions(appName, urls, useYarn); + } + isFirstCompile = false; + + // If errors exist, only show errors. + 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; + } + ui.log(chalk.red('Failed to compile.\n')); + ui.log(messages.errors.join('\n\n')); + return; + } + + // Show warnings if no errors were found. + if (messages.warnings.length) { + ui.log(chalk.yellow('Compiled with warnings.\n')); + ui.log(messages.warnings.join('\n\n')); + + // Teach some ESLint tricks. + ui.log( + '\nSearch for the ' + + chalk.underline(chalk.yellow('keywords')) + + ' to learn more about each warning.' + ); + ui.log( + 'To ignore, add ' + + chalk.cyan('// eslint-disable-next-line') + + ' to the line before.\n' + ); + } + }); + + return compiler; +} + +module.exports = { + createCustomCompiler, +}; \ No newline at end of file diff --git a/packages/react-scripts/backpack-addons/ssr/forkSsr.js b/packages/react-scripts/backpack-addons/ssr/forkSsr.js new file mode 100644 index 0000000000..4176f982f5 --- /dev/null +++ b/packages/react-scripts/backpack-addons/ssr/forkSsr.js @@ -0,0 +1,67 @@ +/** + * This script runs a Webpack compiler to watch the SSR build. It is designed to + * be run by child_process.fork from the start-ssr script. + */ + + const chalk = require('chalk'); + const fs = require('fs'); + const webpack = require('webpack'); + const { prepareUrls } = require('react-dev-utils/WebpackDevServerUtils'); + const paths = require('../../config/paths'); + + const ssrConfigFactory = require('../../config/webpack.config.ssr'); + const statusFile = require('./statusFile'); + const { DebugCompilerUi, ProcessSendCompilerUi } = require('./MultiCompilerUi'); + const { createCustomCompiler } = require('./customWebpackUtils'); + + const useYarn = fs.existsSync(paths.yarnLockFile); + + const config = ssrConfigFactory('development'); + const appName = require(paths.appPackageJson).name; + + const useTypeScript = fs.existsSync(paths.appTsConfig); + const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === 'true'; + + // We don't start a DevServer so we can use dummy URLs + const urls = prepareUrls( + 'http', // protocol + 'localhost', // HOST + '3000', // port + paths.publicUrlOrPath.slice(0, -1) + ); + + // We don't start a DevServer so we can drop devSocket messages + const devSocket = { + warnings: () => {}, + errors: () => {}, + }; + + const ui = process.send ? new ProcessSendCompilerUi() : new DebugCompilerUi(); + + const compiler = createCustomCompiler(ui, { + appName, + config, + devSocket, + urls, + useYarn, + useTypeScript, + tscCompileOnError, + webpack, + }); + + statusFile.init(compiler, paths.appBuildWeb); + + compiler.watch( + { + ignored: ['node_modules'], + }, + err => { + if (err) { + ui.clear(); + ui.log(chalk.red('Failed to compile:') + '\n' + (err.message || err)); + process.exit(1); + } + + statusFile.done(paths.appBuildSsr); + } + ); \ No newline at end of file diff --git a/packages/react-scripts/backpack-addons/ssr/isSsr.js b/packages/react-scripts/backpack-addons/ssr/isSsr.js new file mode 100644 index 0000000000..f8804ab8d0 --- /dev/null +++ b/packages/react-scripts/backpack-addons/ssr/isSsr.js @@ -0,0 +1,31 @@ +'use strict'; + +const fs = require('fs'); +const paths = require('../../config/paths'); + +/** + * In order to build for SSR, need to confirm 3 places + * + * 1. `ssrEnabled` is on + * 2. Use `build-ssr` command + * 3. `ssr.js` exists in `src` folder + * + */ +const isSsr = () => { + // Ensure `ssrEnabled` is on + const appPackageJson = require(paths.appPackageJson); + const bpkReactScriptsConfig = appPackageJson['backpack-react-scripts'] || {}; + const ssrEnabled = bpkReactScriptsConfig.ssrEnabled || false; + + // Ensure using `build-ssr` command + const args = process.argv.slice(2); + const scriptIndex = args.findIndex(x => x === 'build-ssr'); + const isToBuildSsr = scriptIndex === -1; + + // Ensure `ssr.js` exists in `src` folder + const isSsrJsExisted = fs.existsSync(paths.appSsrJs); + + return ssrEnabled && isToBuildSsr && isSsrJsExisted; +}; + +module.exports = isSsr; \ No newline at end of file diff --git a/packages/react-scripts/backpack-addons/ssr/statusFile.js b/packages/react-scripts/backpack-addons/ssr/statusFile.js new file mode 100644 index 0000000000..c39db8ebf4 --- /dev/null +++ b/packages/react-scripts/backpack-addons/ssr/statusFile.js @@ -0,0 +1,31 @@ +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const mkdirp = require('mkdirp'); + +function statusFilePath(buildPath) { + return path.join(buildPath, '.build-status'); +} + +function done(buildPath) { + fs.writeFileSync(statusFilePath(buildPath), 'DONE'); +} + +function inProgress(buildPath) { + fs.writeFileSync(statusFilePath(buildPath), 'IN_PROGRESS'); +} + +exports.init = function(compiler, buildPath) { + mkdirp.sync(buildPath); + inProgress(buildPath); + + compiler.hooks.invalid.tap('invalid', () => { + inProgress(buildPath); + }); + compiler.hooks.done.tap('done', () => { + done(buildPath); + }); +}; + +exports.done = done; \ No newline at end of file From 0aa33ad1a13b8aad60dbac4fbf31dfd591024ab8 Mon Sep 17 00:00:00 2001 From: Salley Liu Date: Tue, 26 Apr 2022 14:26:08 +0800 Subject: [PATCH 02/10] Add css/js html in config --- packages/react-scripts/config/css.html | 5 +++++ packages/react-scripts/config/js.html | 3 +++ 2 files changed, 8 insertions(+) create mode 100644 packages/react-scripts/config/css.html create mode 100644 packages/react-scripts/config/js.html diff --git a/packages/react-scripts/config/css.html b/packages/react-scripts/config/css.html new file mode 100644 index 0000000000..2fa4ab4eb6 --- /dev/null +++ b/packages/react-scripts/config/css.html @@ -0,0 +1,5 @@ +<% _.each(htmlWebpackPlugin.files.css, file => { %> + <% }); %> \ No newline at end of file diff --git a/packages/react-scripts/config/js.html b/packages/react-scripts/config/js.html new file mode 100644 index 0000000000..b8df9648f7 --- /dev/null +++ b/packages/react-scripts/config/js.html @@ -0,0 +1,3 @@ +<% _.each(htmlWebpackPlugin.files.js, file => { %> + + <% }); %> \ No newline at end of file From cf50cb42196ac9966d2e6a54ed8217fceb381eae Mon Sep 17 00:00:00 2001 From: Salley Liu Date: Tue, 26 Apr 2022 14:27:17 +0800 Subject: [PATCH 03/10] other add and delete file in react-scripts --- .../scripts/utils/verifyTypeScriptSetup.js | 298 ------------------ .../getCSSModuleLocalIdentWithProjectName.js | 22 ++ 2 files changed, 22 insertions(+), 298 deletions(-) delete mode 100644 packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js create mode 100644 packages/react-scripts/utils/getCSSModuleLocalIdentWithProjectName.js diff --git a/packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js b/packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js deleted file mode 100644 index cdc2d77a88..0000000000 --- a/packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js +++ /dev/null @@ -1,298 +0,0 @@ -// @remove-file-on-eject -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -'use strict'; - -const chalk = require('react-dev-utils/chalk'); -const fs = require('fs'); -const resolve = require('resolve'); -const path = require('path'); -const paths = require('../../config/paths'); -const os = require('os'); -const semver = require('semver'); -const immer = require('react-dev-utils/immer').produce; -const globby = require('react-dev-utils/globby').sync; - -const hasJsxRuntime = (() => { - if (process.env.DISABLE_NEW_JSX_TRANSFORM === 'true') { - return false; - } - - try { - require.resolve('react/jsx-runtime', { paths: [paths.appPath] }); - return true; - } catch (e) { - return false; - } -})(); - -function writeJson(fileName, object) { - fs.writeFileSync( - fileName, - JSON.stringify(object, null, 2).replace(/\n/g, os.EOL) + os.EOL - ); -} - -function verifyNoTypeScript() { - const typescriptFiles = globby( - ['**/*.(ts|tsx)', '!**/node_modules', '!**/*.d.ts'], - { cwd: paths.appSrc } - ); - if (typescriptFiles.length > 0) { - console.warn( - chalk.yellow( - `We detected TypeScript in your project (${chalk.bold( - `src${path.sep}${typescriptFiles[0]}` - )}) and created a ${chalk.bold('tsconfig.json')} file for you.` - ) - ); - console.warn(); - return false; - } - return true; -} - -function verifyTypeScriptSetup() { - let firstTimeSetup = false; - - if (!fs.existsSync(paths.appTsConfig)) { - if (verifyNoTypeScript()) { - return; - } - writeJson(paths.appTsConfig, {}); - firstTimeSetup = true; - } - - const isYarn = fs.existsSync(paths.yarnLockFile); - - // Ensure typescript is installed - let ts; - try { - // TODO: Remove this hack once `globalThis` issue is resolved - // https://github.com/jsdom/jsdom/issues/2961 - const globalThisWasDefined = !!global.globalThis; - - ts = require(resolve.sync('typescript', { - basedir: paths.appNodeModules, - })); - - if (!globalThisWasDefined && !!global.globalThis) { - delete global.globalThis; - } - } catch (_) { - console.error( - chalk.bold.red( - `It looks like you're trying to use TypeScript but do not have ${chalk.bold( - 'typescript' - )} installed.` - ) - ); - console.error( - chalk.bold( - 'Please install', - chalk.cyan.bold('typescript'), - 'by running', - chalk.cyan.bold( - isYarn ? 'yarn add typescript' : 'npm install typescript' - ) + '.' - ) - ); - console.error( - chalk.bold( - 'If you are not trying to use TypeScript, please remove the ' + - chalk.cyan('tsconfig.json') + - ' file from your package root (and any TypeScript files).' - ) - ); - console.error(); - process.exit(1); - } - - const compilerOptions = { - // These are suggested values and will be set when not present in the - // tsconfig.json - // 'parsedValue' matches the output value from ts.parseJsonConfigFileContent() - target: { - parsedValue: ts.ScriptTarget.ES5, - suggested: 'es5', - }, - lib: { suggested: ['dom', 'dom.iterable', 'esnext'] }, - allowJs: { suggested: true }, - skipLibCheck: { suggested: true }, - esModuleInterop: { suggested: true }, - allowSyntheticDefaultImports: { suggested: true }, - strict: { suggested: true }, - forceConsistentCasingInFileNames: { suggested: true }, - noFallthroughCasesInSwitch: { suggested: true }, - - // These values are required and cannot be changed by the user - // Keep this in sync with the webpack config - module: { - parsedValue: ts.ModuleKind.ESNext, - value: 'esnext', - reason: 'for import() and import/export', - }, - moduleResolution: { - parsedValue: ts.ModuleResolutionKind.NodeJs, - value: 'node', - reason: 'to match webpack resolution', - }, - resolveJsonModule: { value: true, reason: 'to match webpack loader' }, - isolatedModules: { value: true, reason: 'implementation limitation' }, - noEmit: { value: true }, - jsx: { - parsedValue: - hasJsxRuntime && semver.gte(ts.version, '4.1.0-beta') - ? ts.JsxEmit.ReactJSX - : ts.JsxEmit.React, - value: - hasJsxRuntime && semver.gte(ts.version, '4.1.0-beta') - ? 'react-jsx' - : 'react', - reason: 'to support the new JSX transform in React 17', - }, - paths: { value: undefined, reason: 'aliased imports are not supported' }, - }; - - const formatDiagnosticHost = { - getCanonicalFileName: fileName => fileName, - getCurrentDirectory: ts.sys.getCurrentDirectory, - getNewLine: () => os.EOL, - }; - - const messages = []; - let appTsConfig; - let parsedTsConfig; - let parsedCompilerOptions; - try { - const { config: readTsConfig, error } = ts.readConfigFile( - paths.appTsConfig, - ts.sys.readFile - ); - - if (error) { - throw new Error(ts.formatDiagnostic(error, formatDiagnosticHost)); - } - - appTsConfig = readTsConfig; - - // Get TS to parse and resolve any "extends" - // Calling this function also mutates the tsconfig above, - // adding in "include" and "exclude", but the compilerOptions remain untouched - let result; - parsedTsConfig = immer(readTsConfig, config => { - result = ts.parseJsonConfigFileContent( - config, - ts.sys, - path.dirname(paths.appTsConfig) - ); - }); - - if (result.errors && result.errors.length) { - throw new Error( - ts.formatDiagnostic(result.errors[0], formatDiagnosticHost) - ); - } - - parsedCompilerOptions = result.options; - } catch (e) { - if (e && e.name === 'SyntaxError') { - console.error( - chalk.red.bold( - 'Could not parse', - chalk.cyan('tsconfig.json') + '.', - 'Please make sure it contains syntactically correct JSON.' - ) - ); - } - - console.log(e && e.message ? `${e.message}` : ''); - process.exit(1); - } - - if (appTsConfig.compilerOptions == null) { - appTsConfig.compilerOptions = {}; - firstTimeSetup = true; - } - - for (const option of Object.keys(compilerOptions)) { - const { parsedValue, value, suggested, reason } = compilerOptions[option]; - - const valueToCheck = parsedValue === undefined ? value : parsedValue; - const coloredOption = chalk.cyan('compilerOptions.' + option); - - if (suggested != null) { - if (parsedCompilerOptions[option] === undefined) { - appTsConfig = immer(appTsConfig, config => { - config.compilerOptions[option] = suggested; - }); - messages.push( - `${coloredOption} to be ${chalk.bold( - 'suggested' - )} value: ${chalk.cyan.bold(suggested)} (this can be changed)` - ); - } - } else if (parsedCompilerOptions[option] !== valueToCheck) { - appTsConfig = immer(appTsConfig, config => { - config.compilerOptions[option] = value; - }); - messages.push( - `${coloredOption} ${chalk.bold( - valueToCheck == null ? 'must not' : 'must' - )} be ${valueToCheck == null ? 'set' : chalk.cyan.bold(value)}` + - (reason != null ? ` (${reason})` : '') - ); - } - } - - // tsconfig will have the merged "include" and "exclude" by this point - if (parsedTsConfig.include == null) { - appTsConfig = immer(appTsConfig, config => { - config.include = ['src']; - }); - messages.push( - `${chalk.cyan('include')} should be ${chalk.cyan.bold('src')}` - ); - } - - if (messages.length > 0) { - if (firstTimeSetup) { - console.log( - chalk.bold( - 'Your', - chalk.cyan('tsconfig.json'), - 'has been populated with default values.' - ) - ); - console.log(); - } else { - console.warn( - chalk.bold( - 'The following changes are being made to your', - chalk.cyan('tsconfig.json'), - 'file:' - ) - ); - messages.forEach(message => { - console.warn(' - ' + message); - }); - console.warn(); - } - writeJson(paths.appTsConfig, appTsConfig); - } - - // Reference `react-scripts` types - if (!fs.existsSync(paths.appTypeDeclarations)) { - fs.writeFileSync( - paths.appTypeDeclarations, - `/// ${os.EOL}` - ); - } -} - -module.exports = verifyTypeScriptSetup; diff --git a/packages/react-scripts/utils/getCSSModuleLocalIdentWithProjectName.js b/packages/react-scripts/utils/getCSSModuleLocalIdentWithProjectName.js new file mode 100644 index 0000000000..b960519bed --- /dev/null +++ b/packages/react-scripts/utils/getCSSModuleLocalIdentWithProjectName.js @@ -0,0 +1,22 @@ +'use strict'; + +const crypto = require('crypto'); + +const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent'); + +const createHash = buffer => + new Buffer(crypto.createHash('md5').update(buffer).digest('hex')) + .toString('base64') + .substr(0, 5); + +module.exports = projectName => (...args) => { + const localIdent = getCSSModuleLocalIdent(...args); + + // Create an ident based on the file location, class name and Project Name. + // Will be unique across projects + // Format [ file/folder ]_[ localName ]__[ 5 character hash of CRA ident and project name ] + return ( + localIdent.substring(0, localIdent.length - 5) + + createHash(localIdent + projectName) + ); +}; \ No newline at end of file From 1e895a196447530f25e66a4559f13e755b8aa443 Mon Sep 17 00:00:00 2001 From: Salley Liu Date: Tue, 26 Apr 2022 15:19:34 +0800 Subject: [PATCH 04/10] Standardized format --- .../react-scripts/backpack-addons/README.md | 4 +- .../backpack-addons/amdExcludes.js | 2 +- .../backpack-addons/babelIncludePrefixes.js | 2 +- .../backpack-addons/crossOriginLoading.js | 2 +- .../backpack-addons/cssModules.js | 2 +- .../backpack-addons/externals.js | 2 +- .../backpack-addons/ignoreCssWarnings.js | 2 +- .../backpack-addons/runtimeChunk.js | 2 +- .../backpack-addons/sassFunctions.js | 2 +- .../backpack-addons/splitChunks.js | 2 +- .../backpack-addons/sriEnabled.js | 2 +- .../backpack-addons/ssr/MultiCompilerUi.js | 2 +- .../backpack-addons/ssr/customWebpackUtils.js | 2 +- .../backpack-addons/ssr/forkSsr.js | 124 +++++++++--------- .../backpack-addons/ssr/isSsr.js | 2 +- .../backpack-addons/ssr/statusFile.js | 2 +- .../getCSSModuleLocalIdentWithProjectName.js | 2 +- 17 files changed, 79 insertions(+), 79 deletions(-) diff --git a/packages/react-scripts/backpack-addons/README.md b/packages/react-scripts/backpack-addons/README.md index 154d4cf278..ed3e924dd5 100644 --- a/packages/react-scripts/backpack-addons/README.md +++ b/packages/react-scripts/backpack-addons/README.md @@ -28,7 +28,7 @@ Our react scripts fork includes a number of custom configuration items in order ## How to upgrade from the upstream `facebook/create-react-app` repo - Replace all of `packages/react-scripts` with the upstream version. -- Restore `packages/react-scripts/backpack-addons` all features. +- Restore all features in `packages/react-scripts/backpack-addons` folder from the old version of BRS. - Restore (will require manual work+checking) every line which has a `// #backpack-addons` comment. - Compare [upgrade] document and restore the rest of the content (for example, added files, other modified files). -- Test for project. \ No newline at end of file +- Test for project. diff --git a/packages/react-scripts/backpack-addons/amdExcludes.js b/packages/react-scripts/backpack-addons/amdExcludes.js index 28d1bdc308..52bc824646 100644 --- a/packages/react-scripts/backpack-addons/amdExcludes.js +++ b/packages/react-scripts/backpack-addons/amdExcludes.js @@ -13,4 +13,4 @@ module.exports = { parser: { amd: false, } -}; \ No newline at end of file +}; diff --git a/packages/react-scripts/backpack-addons/babelIncludePrefixes.js b/packages/react-scripts/backpack-addons/babelIncludePrefixes.js index cff6b2ae46..708b39337b 100644 --- a/packages/react-scripts/backpack-addons/babelIncludePrefixes.js +++ b/packages/react-scripts/backpack-addons/babelIncludePrefixes.js @@ -22,4 +22,4 @@ module.exports = () => { scopedBackpackModulesRegex, ...customModuleRegexes, ]; -}; \ No newline at end of file +}; diff --git a/packages/react-scripts/backpack-addons/crossOriginLoading.js b/packages/react-scripts/backpack-addons/crossOriginLoading.js index db8a2d2abe..15c0ab296b 100644 --- a/packages/react-scripts/backpack-addons/crossOriginLoading.js +++ b/packages/react-scripts/backpack-addons/crossOriginLoading.js @@ -8,4 +8,4 @@ const sriEnabled = bpkReactScriptsConfig.sriEnabled || false; module.exports = { crossOriginLoading: sriEnabled ? 'anonymous' : crossOriginLoading -} \ No newline at end of file +}; diff --git a/packages/react-scripts/backpack-addons/cssModules.js b/packages/react-scripts/backpack-addons/cssModules.js index 7afd088f8a..004e4ecda4 100644 --- a/packages/react-scripts/backpack-addons/cssModules.js +++ b/packages/react-scripts/backpack-addons/cssModules.js @@ -62,4 +62,4 @@ const getCSSModuleLocalIdent = () => { module.exports = { getStyleTestRegexes, getCSSModuleLocalIdent, -}; \ No newline at end of file +}; diff --git a/packages/react-scripts/backpack-addons/externals.js b/packages/react-scripts/backpack-addons/externals.js index 9fe93241b8..073d235fbf 100644 --- a/packages/react-scripts/backpack-addons/externals.js +++ b/packages/react-scripts/backpack-addons/externals.js @@ -24,4 +24,4 @@ function ssrExternals() { module.exports = { externals, ssrExternals -}; \ No newline at end of file +}; diff --git a/packages/react-scripts/backpack-addons/ignoreCssWarnings.js b/packages/react-scripts/backpack-addons/ignoreCssWarnings.js index fe4b83fc9c..4318cd36d3 100644 --- a/packages/react-scripts/backpack-addons/ignoreCssWarnings.js +++ b/packages/react-scripts/backpack-addons/ignoreCssWarnings.js @@ -7,4 +7,4 @@ const ignoreCssWarnings = bpkReactScriptsConfig.ignoreCssWarnings || false; module.exports = { ignoreOrder: ignoreCssWarnings -} \ No newline at end of file +}; diff --git a/packages/react-scripts/backpack-addons/runtimeChunk.js b/packages/react-scripts/backpack-addons/runtimeChunk.js index a937380496..75ed67b49b 100644 --- a/packages/react-scripts/backpack-addons/runtimeChunk.js +++ b/packages/react-scripts/backpack-addons/runtimeChunk.js @@ -19,4 +19,4 @@ const ssrRuntimeChunk = { module.exports = { runtimeChunk, ssrRuntimeChunk -}; \ No newline at end of file +}; diff --git a/packages/react-scripts/backpack-addons/sassFunctions.js b/packages/react-scripts/backpack-addons/sassFunctions.js index e4b45dc4a0..d19782bad3 100644 --- a/packages/react-scripts/backpack-addons/sassFunctions.js +++ b/packages/react-scripts/backpack-addons/sassFunctions.js @@ -11,4 +11,4 @@ module.exports = { } } } -}; \ No newline at end of file +}; diff --git a/packages/react-scripts/backpack-addons/splitChunks.js b/packages/react-scripts/backpack-addons/splitChunks.js index 77392e04b4..4f94c989d4 100644 --- a/packages/react-scripts/backpack-addons/splitChunks.js +++ b/packages/react-scripts/backpack-addons/splitChunks.js @@ -23,4 +23,4 @@ module.exports = (isEnvDevelopment) => { } : {} } -}; \ No newline at end of file +}; diff --git a/packages/react-scripts/backpack-addons/sriEnabled.js b/packages/react-scripts/backpack-addons/sriEnabled.js index 83e740605e..8223350e8f 100644 --- a/packages/react-scripts/backpack-addons/sriEnabled.js +++ b/packages/react-scripts/backpack-addons/sriEnabled.js @@ -18,4 +18,4 @@ module.exports = () => { hashFuncNames: ["sha384"], }) ); -}; \ No newline at end of file +}; diff --git a/packages/react-scripts/backpack-addons/ssr/MultiCompilerUi.js b/packages/react-scripts/backpack-addons/ssr/MultiCompilerUi.js index 51e340d187..eb04376551 100644 --- a/packages/react-scripts/backpack-addons/ssr/MultiCompilerUi.js +++ b/packages/react-scripts/backpack-addons/ssr/MultiCompilerUi.js @@ -304,4 +304,4 @@ module.exports = { ProcessSendCompilerUi, ProcessMessageCompilerUi, DebugCompilerUi, -}; \ No newline at end of file +}; diff --git a/packages/react-scripts/backpack-addons/ssr/customWebpackUtils.js b/packages/react-scripts/backpack-addons/ssr/customWebpackUtils.js index ecc9fb42aa..fd411bb421 100644 --- a/packages/react-scripts/backpack-addons/ssr/customWebpackUtils.js +++ b/packages/react-scripts/backpack-addons/ssr/customWebpackUtils.js @@ -198,4 +198,4 @@ function createCustomCompiler( module.exports = { createCustomCompiler, -}; \ No newline at end of file +}; diff --git a/packages/react-scripts/backpack-addons/ssr/forkSsr.js b/packages/react-scripts/backpack-addons/ssr/forkSsr.js index 4176f982f5..dccf32c620 100644 --- a/packages/react-scripts/backpack-addons/ssr/forkSsr.js +++ b/packages/react-scripts/backpack-addons/ssr/forkSsr.js @@ -3,65 +3,65 @@ * be run by child_process.fork from the start-ssr script. */ - const chalk = require('chalk'); - const fs = require('fs'); - const webpack = require('webpack'); - const { prepareUrls } = require('react-dev-utils/WebpackDevServerUtils'); - const paths = require('../../config/paths'); - - const ssrConfigFactory = require('../../config/webpack.config.ssr'); - const statusFile = require('./statusFile'); - const { DebugCompilerUi, ProcessSendCompilerUi } = require('./MultiCompilerUi'); - const { createCustomCompiler } = require('./customWebpackUtils'); - - const useYarn = fs.existsSync(paths.yarnLockFile); - - const config = ssrConfigFactory('development'); - const appName = require(paths.appPackageJson).name; - - const useTypeScript = fs.existsSync(paths.appTsConfig); - const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === 'true'; - - // We don't start a DevServer so we can use dummy URLs - const urls = prepareUrls( - 'http', // protocol - 'localhost', // HOST - '3000', // port - paths.publicUrlOrPath.slice(0, -1) - ); - - // We don't start a DevServer so we can drop devSocket messages - const devSocket = { - warnings: () => {}, - errors: () => {}, - }; - - const ui = process.send ? new ProcessSendCompilerUi() : new DebugCompilerUi(); - - const compiler = createCustomCompiler(ui, { - appName, - config, - devSocket, - urls, - useYarn, - useTypeScript, - tscCompileOnError, - webpack, - }); - - statusFile.init(compiler, paths.appBuildWeb); - - compiler.watch( - { - ignored: ['node_modules'], - }, - err => { - if (err) { - ui.clear(); - ui.log(chalk.red('Failed to compile:') + '\n' + (err.message || err)); - process.exit(1); - } - - statusFile.done(paths.appBuildSsr); - } - ); \ No newline at end of file +const chalk = require('chalk'); +const fs = require('fs'); +const webpack = require('webpack'); +const { prepareUrls } = require('react-dev-utils/WebpackDevServerUtils'); +const paths = require('../../config/paths'); + +const ssrConfigFactory = require('../../config/webpack.config.ssr'); +const statusFile = require('./statusFile'); +const { DebugCompilerUi, ProcessSendCompilerUi } = require('./MultiCompilerUi'); +const { createCustomCompiler } = require('./customWebpackUtils'); + +const useYarn = fs.existsSync(paths.yarnLockFile); + +const config = ssrConfigFactory('development'); +const appName = require(paths.appPackageJson).name; + +const useTypeScript = fs.existsSync(paths.appTsConfig); +const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === 'true'; + +// We don't start a DevServer so we can use dummy URLs +const urls = prepareUrls( + 'http', // protocol + 'localhost', // HOST + '3000', // port + paths.publicUrlOrPath.slice(0, -1) +); + +// We don't start a DevServer so we can drop devSocket messages +const devSocket = { + warnings: () => {}, + errors: () => {}, +}; + +const ui = process.send ? new ProcessSendCompilerUi() : new DebugCompilerUi(); + +const compiler = createCustomCompiler(ui, { + appName, + config, + devSocket, + urls, + useYarn, + useTypeScript, + tscCompileOnError, + webpack, +}); + +statusFile.init(compiler, paths.appBuildWeb); + +compiler.watch( + { + ignored: ['node_modules'], + }, + err => { + if (err) { + ui.clear(); + ui.log(chalk.red('Failed to compile:') + '\n' + (err.message || err)); + process.exit(1); + } + + statusFile.done(paths.appBuildSsr); + } +); diff --git a/packages/react-scripts/backpack-addons/ssr/isSsr.js b/packages/react-scripts/backpack-addons/ssr/isSsr.js index f8804ab8d0..1852b8b93b 100644 --- a/packages/react-scripts/backpack-addons/ssr/isSsr.js +++ b/packages/react-scripts/backpack-addons/ssr/isSsr.js @@ -28,4 +28,4 @@ const isSsr = () => { return ssrEnabled && isToBuildSsr && isSsrJsExisted; }; -module.exports = isSsr; \ No newline at end of file +module.exports = isSsr; diff --git a/packages/react-scripts/backpack-addons/ssr/statusFile.js b/packages/react-scripts/backpack-addons/ssr/statusFile.js index c39db8ebf4..f918867a24 100644 --- a/packages/react-scripts/backpack-addons/ssr/statusFile.js +++ b/packages/react-scripts/backpack-addons/ssr/statusFile.js @@ -28,4 +28,4 @@ exports.init = function(compiler, buildPath) { }); }; -exports.done = done; \ No newline at end of file +exports.done = done; diff --git a/packages/react-scripts/utils/getCSSModuleLocalIdentWithProjectName.js b/packages/react-scripts/utils/getCSSModuleLocalIdentWithProjectName.js index b960519bed..d3faef486a 100644 --- a/packages/react-scripts/utils/getCSSModuleLocalIdentWithProjectName.js +++ b/packages/react-scripts/utils/getCSSModuleLocalIdentWithProjectName.js @@ -19,4 +19,4 @@ module.exports = projectName => (...args) => { localIdent.substring(0, localIdent.length - 5) + createHash(localIdent + projectName) ); -}; \ No newline at end of file +}; From 017a2b0daa297462805d3e58be58f9b50d1b6423 Mon Sep 17 00:00:00 2001 From: Salley Liu Date: Tue, 26 Apr 2022 15:26:58 +0800 Subject: [PATCH 05/10] Add an empty new line in the end of this file. --- packages/react-scripts/config/css.html | 3 ++- packages/react-scripts/config/js.html | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/react-scripts/config/css.html b/packages/react-scripts/config/css.html index 2fa4ab4eb6..d6253f52d7 100644 --- a/packages/react-scripts/config/css.html +++ b/packages/react-scripts/config/css.html @@ -2,4 +2,5 @@ rel="stylesheet" href="<%= file %>" /> - <% }); %> \ No newline at end of file + <% }); %> + \ No newline at end of file diff --git a/packages/react-scripts/config/js.html b/packages/react-scripts/config/js.html index b8df9648f7..6a04f63c6e 100644 --- a/packages/react-scripts/config/js.html +++ b/packages/react-scripts/config/js.html @@ -1,3 +1,4 @@ <% _.each(htmlWebpackPlugin.files.js, file => { %> - <% }); %> \ No newline at end of file + <% }); %> + \ No newline at end of file From 4334991f0d4df73ff5332c8754534d1cfe6d8b3e Mon Sep 17 00:00:00 2001 From: Salley Liu Date: Tue, 26 Apr 2022 16:05:59 +0800 Subject: [PATCH 06/10] restore verifyTypeScriptSetup.js --- .../scripts/utils/verifyTypeScriptSetup.js | 299 ++++++++++++++++++ 1 file changed, 299 insertions(+) create mode 100644 packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js diff --git a/packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js b/packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js new file mode 100644 index 0000000000..8baef81b6f --- /dev/null +++ b/packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js @@ -0,0 +1,299 @@ +// @remove-file-on-eject +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + + 'use strict'; + + const chalk = require('react-dev-utils/chalk'); + const fs = require('fs'); + const resolve = require('resolve'); + const path = require('path'); + const paths = require('../../config/paths'); + const os = require('os'); + const semver = require('semver'); + const immer = require('react-dev-utils/immer').produce; + const globby = require('react-dev-utils/globby').sync; + + const hasJsxRuntime = (() => { + if (process.env.DISABLE_NEW_JSX_TRANSFORM === 'true') { + return false; + } + + try { + require.resolve('react/jsx-runtime', { paths: [paths.appPath] }); + return true; + } catch (e) { + return false; + } + })(); + + function writeJson(fileName, object) { + fs.writeFileSync( + fileName, + JSON.stringify(object, null, 2).replace(/\n/g, os.EOL) + os.EOL + ); + } + + function verifyNoTypeScript() { + const typescriptFiles = globby( + ['**/*.(ts|tsx)', '!**/node_modules', '!**/*.d.ts'], + { cwd: paths.appSrc } + ); + if (typescriptFiles.length > 0) { + console.warn( + chalk.yellow( + `We detected TypeScript in your project (${chalk.bold( + `src${path.sep}${typescriptFiles[0]}` + )}) and created a ${chalk.bold('tsconfig.json')} file for you.` + ) + ); + console.warn(); + return false; + } + return true; + } + + function verifyTypeScriptSetup() { + let firstTimeSetup = false; + + if (!fs.existsSync(paths.appTsConfig)) { + if (verifyNoTypeScript()) { + return; + } + writeJson(paths.appTsConfig, {}); + firstTimeSetup = true; + } + + const isYarn = fs.existsSync(paths.yarnLockFile); + + // Ensure typescript is installed + let ts; + try { + // TODO: Remove this hack once `globalThis` issue is resolved + // https://github.com/jsdom/jsdom/issues/2961 + const globalThisWasDefined = !!global.globalThis; + + ts = require(resolve.sync('typescript', { + basedir: paths.appNodeModules, + })); + + if (!globalThisWasDefined && !!global.globalThis) { + delete global.globalThis; + } + } catch (_) { + console.error( + chalk.bold.red( + `It looks like you're trying to use TypeScript but do not have ${chalk.bold( + 'typescript' + )} installed.` + ) + ); + console.error( + chalk.bold( + 'Please install', + chalk.cyan.bold('typescript'), + 'by running', + chalk.cyan.bold( + isYarn ? 'yarn add typescript' : 'npm install typescript' + ) + '.' + ) + ); + console.error( + chalk.bold( + 'If you are not trying to use TypeScript, please remove the ' + + chalk.cyan('tsconfig.json') + + ' file from your package root (and any TypeScript files).' + ) + ); + console.error(); + process.exit(1); + } + + const compilerOptions = { + // These are suggested values and will be set when not present in the + // tsconfig.json + // 'parsedValue' matches the output value from ts.parseJsonConfigFileContent() + target: { + parsedValue: ts.ScriptTarget.ES5, + suggested: 'es5', + }, + lib: { suggested: ['dom', 'dom.iterable', 'esnext'] }, + allowJs: { suggested: true }, + skipLibCheck: { suggested: true }, + esModuleInterop: { suggested: true }, + allowSyntheticDefaultImports: { suggested: true }, + strict: { suggested: true }, + forceConsistentCasingInFileNames: { suggested: true }, + noFallthroughCasesInSwitch: { suggested: true }, + + // These values are required and cannot be changed by the user + // Keep this in sync with the webpack config + module: { + parsedValue: ts.ModuleKind.ESNext, + value: 'esnext', + reason: 'for import() and import/export', + }, + moduleResolution: { + parsedValue: ts.ModuleResolutionKind.NodeJs, + value: 'node', + reason: 'to match webpack resolution', + }, + resolveJsonModule: { value: true, reason: 'to match webpack loader' }, + isolatedModules: { value: true, reason: 'implementation limitation' }, + noEmit: { value: true }, + jsx: { + parsedValue: + hasJsxRuntime && semver.gte(ts.version, '4.1.0-beta') + ? ts.JsxEmit.ReactJSX + : ts.JsxEmit.React, + value: + hasJsxRuntime && semver.gte(ts.version, '4.1.0-beta') + ? 'react-jsx' + : 'react', + reason: 'to support the new JSX transform in React 17', + }, + paths: { value: undefined, reason: 'aliased imports are not supported' }, + }; + + const formatDiagnosticHost = { + getCanonicalFileName: fileName => fileName, + getCurrentDirectory: ts.sys.getCurrentDirectory, + getNewLine: () => os.EOL, + }; + + const messages = []; + let appTsConfig; + let parsedTsConfig; + let parsedCompilerOptions; + try { + const { config: readTsConfig, error } = ts.readConfigFile( + paths.appTsConfig, + ts.sys.readFile + ); + + if (error) { + throw new Error(ts.formatDiagnostic(error, formatDiagnosticHost)); + } + + appTsConfig = readTsConfig; + + // Get TS to parse and resolve any "extends" + // Calling this function also mutates the tsconfig above, + // adding in "include" and "exclude", but the compilerOptions remain untouched + let result; + parsedTsConfig = immer(readTsConfig, config => { + result = ts.parseJsonConfigFileContent( + config, + ts.sys, + path.dirname(paths.appTsConfig) + ); + }); + + if (result.errors && result.errors.length) { + throw new Error( + ts.formatDiagnostic(result.errors[0], formatDiagnosticHost) + ); + } + + parsedCompilerOptions = result.options; + } catch (e) { + if (e && e.name === 'SyntaxError') { + console.error( + chalk.red.bold( + 'Could not parse', + chalk.cyan('tsconfig.json') + '.', + 'Please make sure it contains syntactically correct JSON.' + ) + ); + } + + console.log(e && e.message ? `${e.message}` : ''); + process.exit(1); + } + + if (appTsConfig.compilerOptions == null) { + appTsConfig.compilerOptions = {}; + firstTimeSetup = true; + } + + for (const option of Object.keys(compilerOptions)) { + const { parsedValue, value, suggested, reason } = compilerOptions[option]; + + const valueToCheck = parsedValue === undefined ? value : parsedValue; + const coloredOption = chalk.cyan('compilerOptions.' + option); + + if (suggested != null) { + if (parsedCompilerOptions[option] === undefined) { + appTsConfig = immer(appTsConfig, config => { + config.compilerOptions[option] = suggested; + }); + messages.push( + `${coloredOption} to be ${chalk.bold( + 'suggested' + )} value: ${chalk.cyan.bold(suggested)} (this can be changed)` + ); + } + } else if (parsedCompilerOptions[option] !== valueToCheck) { + appTsConfig = immer(appTsConfig, config => { + config.compilerOptions[option] = value; + }); + messages.push( + `${coloredOption} ${chalk.bold( + valueToCheck == null ? 'must not' : 'must' + )} be ${valueToCheck == null ? 'set' : chalk.cyan.bold(value)}` + + (reason != null ? ` (${reason})` : '') + ); + } + } + + // tsconfig will have the merged "include" and "exclude" by this point + if (parsedTsConfig.include == null) { + appTsConfig = immer(appTsConfig, config => { + config.include = ['src']; + }); + messages.push( + `${chalk.cyan('include')} should be ${chalk.cyan.bold('src')}` + ); + } + + if (messages.length > 0) { + if (firstTimeSetup) { + console.log( + chalk.bold( + 'Your', + chalk.cyan('tsconfig.json'), + 'has been populated with default values.' + ) + ); + console.log(); + } else { + console.warn( + chalk.bold( + 'The following changes are being made to your', + chalk.cyan('tsconfig.json'), + 'file:' + ) + ); + messages.forEach(message => { + console.warn(' - ' + message); + }); + console.warn(); + } + writeJson(paths.appTsConfig, appTsConfig); + } + + // Reference `react-scripts` types + if (!fs.existsSync(paths.appTypeDeclarations)) { + fs.writeFileSync( + paths.appTypeDeclarations, + `/// ${os.EOL}` + ); + } + } + + module.exports = verifyTypeScriptSetup; + \ No newline at end of file From 220a660047c04d48ef9e475a7f887106eaba5fd9 Mon Sep 17 00:00:00 2001 From: Salley Liu Date: Tue, 26 Apr 2022 16:07:43 +0800 Subject: [PATCH 07/10] Modify format --- .../scripts/utils/verifyTypeScriptSetup.js | 579 +++++++++--------- 1 file changed, 289 insertions(+), 290 deletions(-) diff --git a/packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js b/packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js index 8baef81b6f..0017c7494c 100644 --- a/packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js +++ b/packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js @@ -6,294 +6,293 @@ * LICENSE file in the root directory of this source tree. */ - 'use strict'; +'use strict'; - const chalk = require('react-dev-utils/chalk'); - const fs = require('fs'); - const resolve = require('resolve'); - const path = require('path'); - const paths = require('../../config/paths'); - const os = require('os'); - const semver = require('semver'); - const immer = require('react-dev-utils/immer').produce; - const globby = require('react-dev-utils/globby').sync; - - const hasJsxRuntime = (() => { - if (process.env.DISABLE_NEW_JSX_TRANSFORM === 'true') { - return false; - } - - try { - require.resolve('react/jsx-runtime', { paths: [paths.appPath] }); - return true; - } catch (e) { - return false; - } - })(); - - function writeJson(fileName, object) { - fs.writeFileSync( - fileName, - JSON.stringify(object, null, 2).replace(/\n/g, os.EOL) + os.EOL - ); - } - - function verifyNoTypeScript() { - const typescriptFiles = globby( - ['**/*.(ts|tsx)', '!**/node_modules', '!**/*.d.ts'], - { cwd: paths.appSrc } - ); - if (typescriptFiles.length > 0) { - console.warn( - chalk.yellow( - `We detected TypeScript in your project (${chalk.bold( - `src${path.sep}${typescriptFiles[0]}` - )}) and created a ${chalk.bold('tsconfig.json')} file for you.` - ) - ); - console.warn(); - return false; - } - return true; - } - - function verifyTypeScriptSetup() { - let firstTimeSetup = false; - - if (!fs.existsSync(paths.appTsConfig)) { - if (verifyNoTypeScript()) { - return; - } - writeJson(paths.appTsConfig, {}); - firstTimeSetup = true; - } - - const isYarn = fs.existsSync(paths.yarnLockFile); - - // Ensure typescript is installed - let ts; - try { - // TODO: Remove this hack once `globalThis` issue is resolved - // https://github.com/jsdom/jsdom/issues/2961 - const globalThisWasDefined = !!global.globalThis; - - ts = require(resolve.sync('typescript', { - basedir: paths.appNodeModules, - })); - - if (!globalThisWasDefined && !!global.globalThis) { - delete global.globalThis; - } - } catch (_) { - console.error( - chalk.bold.red( - `It looks like you're trying to use TypeScript but do not have ${chalk.bold( - 'typescript' - )} installed.` - ) - ); - console.error( - chalk.bold( - 'Please install', - chalk.cyan.bold('typescript'), - 'by running', - chalk.cyan.bold( - isYarn ? 'yarn add typescript' : 'npm install typescript' - ) + '.' - ) - ); - console.error( - chalk.bold( - 'If you are not trying to use TypeScript, please remove the ' + - chalk.cyan('tsconfig.json') + - ' file from your package root (and any TypeScript files).' - ) - ); - console.error(); - process.exit(1); - } - - const compilerOptions = { - // These are suggested values and will be set when not present in the - // tsconfig.json - // 'parsedValue' matches the output value from ts.parseJsonConfigFileContent() - target: { - parsedValue: ts.ScriptTarget.ES5, - suggested: 'es5', - }, - lib: { suggested: ['dom', 'dom.iterable', 'esnext'] }, - allowJs: { suggested: true }, - skipLibCheck: { suggested: true }, - esModuleInterop: { suggested: true }, - allowSyntheticDefaultImports: { suggested: true }, - strict: { suggested: true }, - forceConsistentCasingInFileNames: { suggested: true }, - noFallthroughCasesInSwitch: { suggested: true }, - - // These values are required and cannot be changed by the user - // Keep this in sync with the webpack config - module: { - parsedValue: ts.ModuleKind.ESNext, - value: 'esnext', - reason: 'for import() and import/export', - }, - moduleResolution: { - parsedValue: ts.ModuleResolutionKind.NodeJs, - value: 'node', - reason: 'to match webpack resolution', - }, - resolveJsonModule: { value: true, reason: 'to match webpack loader' }, - isolatedModules: { value: true, reason: 'implementation limitation' }, - noEmit: { value: true }, - jsx: { - parsedValue: - hasJsxRuntime && semver.gte(ts.version, '4.1.0-beta') - ? ts.JsxEmit.ReactJSX - : ts.JsxEmit.React, - value: - hasJsxRuntime && semver.gte(ts.version, '4.1.0-beta') - ? 'react-jsx' - : 'react', - reason: 'to support the new JSX transform in React 17', - }, - paths: { value: undefined, reason: 'aliased imports are not supported' }, - }; - - const formatDiagnosticHost = { - getCanonicalFileName: fileName => fileName, - getCurrentDirectory: ts.sys.getCurrentDirectory, - getNewLine: () => os.EOL, - }; - - const messages = []; - let appTsConfig; - let parsedTsConfig; - let parsedCompilerOptions; - try { - const { config: readTsConfig, error } = ts.readConfigFile( - paths.appTsConfig, - ts.sys.readFile - ); - - if (error) { - throw new Error(ts.formatDiagnostic(error, formatDiagnosticHost)); - } - - appTsConfig = readTsConfig; - - // Get TS to parse and resolve any "extends" - // Calling this function also mutates the tsconfig above, - // adding in "include" and "exclude", but the compilerOptions remain untouched - let result; - parsedTsConfig = immer(readTsConfig, config => { - result = ts.parseJsonConfigFileContent( - config, - ts.sys, - path.dirname(paths.appTsConfig) - ); - }); - - if (result.errors && result.errors.length) { - throw new Error( - ts.formatDiagnostic(result.errors[0], formatDiagnosticHost) - ); - } - - parsedCompilerOptions = result.options; - } catch (e) { - if (e && e.name === 'SyntaxError') { - console.error( - chalk.red.bold( - 'Could not parse', - chalk.cyan('tsconfig.json') + '.', - 'Please make sure it contains syntactically correct JSON.' - ) - ); - } - - console.log(e && e.message ? `${e.message}` : ''); - process.exit(1); - } - - if (appTsConfig.compilerOptions == null) { - appTsConfig.compilerOptions = {}; - firstTimeSetup = true; - } - - for (const option of Object.keys(compilerOptions)) { - const { parsedValue, value, suggested, reason } = compilerOptions[option]; - - const valueToCheck = parsedValue === undefined ? value : parsedValue; - const coloredOption = chalk.cyan('compilerOptions.' + option); - - if (suggested != null) { - if (parsedCompilerOptions[option] === undefined) { - appTsConfig = immer(appTsConfig, config => { - config.compilerOptions[option] = suggested; - }); - messages.push( - `${coloredOption} to be ${chalk.bold( - 'suggested' - )} value: ${chalk.cyan.bold(suggested)} (this can be changed)` - ); - } - } else if (parsedCompilerOptions[option] !== valueToCheck) { - appTsConfig = immer(appTsConfig, config => { - config.compilerOptions[option] = value; - }); - messages.push( - `${coloredOption} ${chalk.bold( - valueToCheck == null ? 'must not' : 'must' - )} be ${valueToCheck == null ? 'set' : chalk.cyan.bold(value)}` + - (reason != null ? ` (${reason})` : '') - ); - } - } - - // tsconfig will have the merged "include" and "exclude" by this point - if (parsedTsConfig.include == null) { - appTsConfig = immer(appTsConfig, config => { - config.include = ['src']; - }); - messages.push( - `${chalk.cyan('include')} should be ${chalk.cyan.bold('src')}` - ); - } - - if (messages.length > 0) { - if (firstTimeSetup) { - console.log( - chalk.bold( - 'Your', - chalk.cyan('tsconfig.json'), - 'has been populated with default values.' - ) - ); - console.log(); - } else { - console.warn( - chalk.bold( - 'The following changes are being made to your', - chalk.cyan('tsconfig.json'), - 'file:' - ) - ); - messages.forEach(message => { - console.warn(' - ' + message); - }); - console.warn(); - } - writeJson(paths.appTsConfig, appTsConfig); - } - - // Reference `react-scripts` types - if (!fs.existsSync(paths.appTypeDeclarations)) { - fs.writeFileSync( - paths.appTypeDeclarations, - `/// ${os.EOL}` - ); - } - } - - module.exports = verifyTypeScriptSetup; - \ No newline at end of file +const chalk = require('react-dev-utils/chalk'); +const fs = require('fs'); +const resolve = require('resolve'); +const path = require('path'); +const paths = require('../../config/paths'); +const os = require('os'); +const semver = require('semver'); +const immer = require('react-dev-utils/immer').produce; +const globby = require('react-dev-utils/globby').sync; + +const hasJsxRuntime = (() => { +if (process.env.DISABLE_NEW_JSX_TRANSFORM === 'true') { + return false; +} + +try { + require.resolve('react/jsx-runtime', { paths: [paths.appPath] }); + return true; +} catch (e) { + return false; +} +})(); + +function writeJson(fileName, object) { +fs.writeFileSync( + fileName, + JSON.stringify(object, null, 2).replace(/\n/g, os.EOL) + os.EOL +); +} + +function verifyNoTypeScript() { +const typescriptFiles = globby( + ['**/*.(ts|tsx)', '!**/node_modules', '!**/*.d.ts'], + { cwd: paths.appSrc } +); +if (typescriptFiles.length > 0) { + console.warn( + chalk.yellow( + `We detected TypeScript in your project (${chalk.bold( + `src${path.sep}${typescriptFiles[0]}` + )}) and created a ${chalk.bold('tsconfig.json')} file for you.` + ) + ); + console.warn(); + return false; +} +return true; +} + +function verifyTypeScriptSetup() { +let firstTimeSetup = false; + +if (!fs.existsSync(paths.appTsConfig)) { + if (verifyNoTypeScript()) { + return; + } + writeJson(paths.appTsConfig, {}); + firstTimeSetup = true; +} + +const isYarn = fs.existsSync(paths.yarnLockFile); + +// Ensure typescript is installed +let ts; +try { + // TODO: Remove this hack once `globalThis` issue is resolved + // https://github.com/jsdom/jsdom/issues/2961 + const globalThisWasDefined = !!global.globalThis; + + ts = require(resolve.sync('typescript', { + basedir: paths.appNodeModules, + })); + + if (!globalThisWasDefined && !!global.globalThis) { + delete global.globalThis; + } +} catch (_) { + console.error( + chalk.bold.red( + `It looks like you're trying to use TypeScript but do not have ${chalk.bold( + 'typescript' + )} installed.` + ) + ); + console.error( + chalk.bold( + 'Please install', + chalk.cyan.bold('typescript'), + 'by running', + chalk.cyan.bold( + isYarn ? 'yarn add typescript' : 'npm install typescript' + ) + '.' + ) + ); + console.error( + chalk.bold( + 'If you are not trying to use TypeScript, please remove the ' + + chalk.cyan('tsconfig.json') + + ' file from your package root (and any TypeScript files).' + ) + ); + console.error(); + process.exit(1); +} + +const compilerOptions = { + // These are suggested values and will be set when not present in the + // tsconfig.json + // 'parsedValue' matches the output value from ts.parseJsonConfigFileContent() + target: { + parsedValue: ts.ScriptTarget.ES5, + suggested: 'es5', + }, + lib: { suggested: ['dom', 'dom.iterable', 'esnext'] }, + allowJs: { suggested: true }, + skipLibCheck: { suggested: true }, + esModuleInterop: { suggested: true }, + allowSyntheticDefaultImports: { suggested: true }, + strict: { suggested: true }, + forceConsistentCasingInFileNames: { suggested: true }, + noFallthroughCasesInSwitch: { suggested: true }, + + // These values are required and cannot be changed by the user + // Keep this in sync with the webpack config + module: { + parsedValue: ts.ModuleKind.ESNext, + value: 'esnext', + reason: 'for import() and import/export', + }, + moduleResolution: { + parsedValue: ts.ModuleResolutionKind.NodeJs, + value: 'node', + reason: 'to match webpack resolution', + }, + resolveJsonModule: { value: true, reason: 'to match webpack loader' }, + isolatedModules: { value: true, reason: 'implementation limitation' }, + noEmit: { value: true }, + jsx: { + parsedValue: + hasJsxRuntime && semver.gte(ts.version, '4.1.0-beta') + ? ts.JsxEmit.ReactJSX + : ts.JsxEmit.React, + value: + hasJsxRuntime && semver.gte(ts.version, '4.1.0-beta') + ? 'react-jsx' + : 'react', + reason: 'to support the new JSX transform in React 17', + }, + paths: { value: undefined, reason: 'aliased imports are not supported' }, +}; + +const formatDiagnosticHost = { + getCanonicalFileName: fileName => fileName, + getCurrentDirectory: ts.sys.getCurrentDirectory, + getNewLine: () => os.EOL, +}; + +const messages = []; +let appTsConfig; +let parsedTsConfig; +let parsedCompilerOptions; +try { + const { config: readTsConfig, error } = ts.readConfigFile( + paths.appTsConfig, + ts.sys.readFile + ); + + if (error) { + throw new Error(ts.formatDiagnostic(error, formatDiagnosticHost)); + } + + appTsConfig = readTsConfig; + + // Get TS to parse and resolve any "extends" + // Calling this function also mutates the tsconfig above, + // adding in "include" and "exclude", but the compilerOptions remain untouched + let result; + parsedTsConfig = immer(readTsConfig, config => { + result = ts.parseJsonConfigFileContent( + config, + ts.sys, + path.dirname(paths.appTsConfig) + ); + }); + + if (result.errors && result.errors.length) { + throw new Error( + ts.formatDiagnostic(result.errors[0], formatDiagnosticHost) + ); + } + + parsedCompilerOptions = result.options; +} catch (e) { + if (e && e.name === 'SyntaxError') { + console.error( + chalk.red.bold( + 'Could not parse', + chalk.cyan('tsconfig.json') + '.', + 'Please make sure it contains syntactically correct JSON.' + ) + ); + } + + console.log(e && e.message ? `${e.message}` : ''); + process.exit(1); +} + +if (appTsConfig.compilerOptions == null) { + appTsConfig.compilerOptions = {}; + firstTimeSetup = true; +} + +for (const option of Object.keys(compilerOptions)) { + const { parsedValue, value, suggested, reason } = compilerOptions[option]; + + const valueToCheck = parsedValue === undefined ? value : parsedValue; + const coloredOption = chalk.cyan('compilerOptions.' + option); + + if (suggested != null) { + if (parsedCompilerOptions[option] === undefined) { + appTsConfig = immer(appTsConfig, config => { + config.compilerOptions[option] = suggested; + }); + messages.push( + `${coloredOption} to be ${chalk.bold( + 'suggested' + )} value: ${chalk.cyan.bold(suggested)} (this can be changed)` + ); + } + } else if (parsedCompilerOptions[option] !== valueToCheck) { + appTsConfig = immer(appTsConfig, config => { + config.compilerOptions[option] = value; + }); + messages.push( + `${coloredOption} ${chalk.bold( + valueToCheck == null ? 'must not' : 'must' + )} be ${valueToCheck == null ? 'set' : chalk.cyan.bold(value)}` + + (reason != null ? ` (${reason})` : '') + ); + } +} + +// tsconfig will have the merged "include" and "exclude" by this point +if (parsedTsConfig.include == null) { + appTsConfig = immer(appTsConfig, config => { + config.include = ['src']; + }); + messages.push( + `${chalk.cyan('include')} should be ${chalk.cyan.bold('src')}` + ); +} + +if (messages.length > 0) { + if (firstTimeSetup) { + console.log( + chalk.bold( + 'Your', + chalk.cyan('tsconfig.json'), + 'has been populated with default values.' + ) + ); + console.log(); + } else { + console.warn( + chalk.bold( + 'The following changes are being made to your', + chalk.cyan('tsconfig.json'), + 'file:' + ) + ); + messages.forEach(message => { + console.warn(' - ' + message); + }); + console.warn(); + } + writeJson(paths.appTsConfig, appTsConfig); +} + +// Reference `react-scripts` types +if (!fs.existsSync(paths.appTypeDeclarations)) { + fs.writeFileSync( + paths.appTypeDeclarations, + `/// ${os.EOL}` + ); +} +} + +module.exports = verifyTypeScriptSetup; From f158bd1259d0cb617d5f3dfbcb5b95cddea14aa7 Mon Sep 17 00:00:00 2001 From: Salley Liu Date: Tue, 26 Apr 2022 16:15:31 +0800 Subject: [PATCH 08/10] Modify file format --- .../backpack-addons/crossOriginLoading.js | 2 +- .../backpack-addons/ignoreCssWarnings.js | 2 +- .../scripts/utils/verifyTypeScriptSetup.js | 232 +++++++++--------- 3 files changed, 118 insertions(+), 118 deletions(-) diff --git a/packages/react-scripts/backpack-addons/crossOriginLoading.js b/packages/react-scripts/backpack-addons/crossOriginLoading.js index 15c0ab296b..e090cc8715 100644 --- a/packages/react-scripts/backpack-addons/crossOriginLoading.js +++ b/packages/react-scripts/backpack-addons/crossOriginLoading.js @@ -7,5 +7,5 @@ const crossOriginLoading = bpkReactScriptsConfig.crossOriginLoading || false; const sriEnabled = bpkReactScriptsConfig.sriEnabled || false; module.exports = { - crossOriginLoading: sriEnabled ? 'anonymous' : crossOriginLoading + crossOriginLoading: sriEnabled ? 'anonymous' : crossOriginLoading }; diff --git a/packages/react-scripts/backpack-addons/ignoreCssWarnings.js b/packages/react-scripts/backpack-addons/ignoreCssWarnings.js index 4318cd36d3..973e4c3276 100644 --- a/packages/react-scripts/backpack-addons/ignoreCssWarnings.js +++ b/packages/react-scripts/backpack-addons/ignoreCssWarnings.js @@ -6,5 +6,5 @@ const bpkReactScriptsConfig = appPackageJson['backpack-react-scripts'] || {}; const ignoreCssWarnings = bpkReactScriptsConfig.ignoreCssWarnings || false; module.exports = { - ignoreOrder: ignoreCssWarnings + ignoreOrder: ignoreCssWarnings }; diff --git a/packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js b/packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js index 0017c7494c..cdc2d77a88 100644 --- a/packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js +++ b/packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js @@ -19,107 +19,107 @@ const immer = require('react-dev-utils/immer').produce; const globby = require('react-dev-utils/globby').sync; const hasJsxRuntime = (() => { -if (process.env.DISABLE_NEW_JSX_TRANSFORM === 'true') { + if (process.env.DISABLE_NEW_JSX_TRANSFORM === 'true') { return false; -} + } -try { + try { require.resolve('react/jsx-runtime', { paths: [paths.appPath] }); return true; -} catch (e) { + } catch (e) { return false; -} + } })(); function writeJson(fileName, object) { -fs.writeFileSync( + fs.writeFileSync( fileName, JSON.stringify(object, null, 2).replace(/\n/g, os.EOL) + os.EOL -); + ); } function verifyNoTypeScript() { -const typescriptFiles = globby( + const typescriptFiles = globby( ['**/*.(ts|tsx)', '!**/node_modules', '!**/*.d.ts'], { cwd: paths.appSrc } -); -if (typescriptFiles.length > 0) { + ); + if (typescriptFiles.length > 0) { console.warn( - chalk.yellow( + chalk.yellow( `We detected TypeScript in your project (${chalk.bold( - `src${path.sep}${typescriptFiles[0]}` + `src${path.sep}${typescriptFiles[0]}` )}) and created a ${chalk.bold('tsconfig.json')} file for you.` - ) + ) ); console.warn(); return false; -} -return true; + } + return true; } function verifyTypeScriptSetup() { -let firstTimeSetup = false; + let firstTimeSetup = false; -if (!fs.existsSync(paths.appTsConfig)) { + if (!fs.existsSync(paths.appTsConfig)) { if (verifyNoTypeScript()) { - return; + return; } writeJson(paths.appTsConfig, {}); firstTimeSetup = true; -} + } -const isYarn = fs.existsSync(paths.yarnLockFile); + const isYarn = fs.existsSync(paths.yarnLockFile); -// Ensure typescript is installed -let ts; -try { + // Ensure typescript is installed + let ts; + try { // TODO: Remove this hack once `globalThis` issue is resolved // https://github.com/jsdom/jsdom/issues/2961 const globalThisWasDefined = !!global.globalThis; ts = require(resolve.sync('typescript', { - basedir: paths.appNodeModules, + basedir: paths.appNodeModules, })); if (!globalThisWasDefined && !!global.globalThis) { - delete global.globalThis; + delete global.globalThis; } -} catch (_) { + } catch (_) { console.error( - chalk.bold.red( + chalk.bold.red( `It looks like you're trying to use TypeScript but do not have ${chalk.bold( - 'typescript' + 'typescript' )} installed.` - ) + ) ); console.error( - chalk.bold( + chalk.bold( 'Please install', chalk.cyan.bold('typescript'), 'by running', chalk.cyan.bold( - isYarn ? 'yarn add typescript' : 'npm install typescript' + isYarn ? 'yarn add typescript' : 'npm install typescript' ) + '.' - ) + ) ); console.error( - chalk.bold( + chalk.bold( 'If you are not trying to use TypeScript, please remove the ' + - chalk.cyan('tsconfig.json') + - ' file from your package root (and any TypeScript files).' - ) + chalk.cyan('tsconfig.json') + + ' file from your package root (and any TypeScript files).' + ) ); console.error(); process.exit(1); -} + } -const compilerOptions = { + const compilerOptions = { // These are suggested values and will be set when not present in the // tsconfig.json // 'parsedValue' matches the output value from ts.parseJsonConfigFileContent() target: { - parsedValue: ts.ScriptTarget.ES5, - suggested: 'es5', + parsedValue: ts.ScriptTarget.ES5, + suggested: 'es5', }, lib: { suggested: ['dom', 'dom.iterable', 'esnext'] }, allowJs: { suggested: true }, @@ -133,50 +133,50 @@ const compilerOptions = { // These values are required and cannot be changed by the user // Keep this in sync with the webpack config module: { - parsedValue: ts.ModuleKind.ESNext, - value: 'esnext', - reason: 'for import() and import/export', + parsedValue: ts.ModuleKind.ESNext, + value: 'esnext', + reason: 'for import() and import/export', }, moduleResolution: { - parsedValue: ts.ModuleResolutionKind.NodeJs, - value: 'node', - reason: 'to match webpack resolution', + parsedValue: ts.ModuleResolutionKind.NodeJs, + value: 'node', + reason: 'to match webpack resolution', }, resolveJsonModule: { value: true, reason: 'to match webpack loader' }, isolatedModules: { value: true, reason: 'implementation limitation' }, noEmit: { value: true }, jsx: { - parsedValue: + parsedValue: hasJsxRuntime && semver.gte(ts.version, '4.1.0-beta') - ? ts.JsxEmit.ReactJSX - : ts.JsxEmit.React, - value: + ? ts.JsxEmit.ReactJSX + : ts.JsxEmit.React, + value: hasJsxRuntime && semver.gte(ts.version, '4.1.0-beta') - ? 'react-jsx' - : 'react', - reason: 'to support the new JSX transform in React 17', + ? 'react-jsx' + : 'react', + reason: 'to support the new JSX transform in React 17', }, paths: { value: undefined, reason: 'aliased imports are not supported' }, -}; + }; -const formatDiagnosticHost = { + const formatDiagnosticHost = { getCanonicalFileName: fileName => fileName, getCurrentDirectory: ts.sys.getCurrentDirectory, getNewLine: () => os.EOL, -}; + }; -const messages = []; -let appTsConfig; -let parsedTsConfig; -let parsedCompilerOptions; -try { + const messages = []; + let appTsConfig; + let parsedTsConfig; + let parsedCompilerOptions; + try { const { config: readTsConfig, error } = ts.readConfigFile( - paths.appTsConfig, - ts.sys.readFile + paths.appTsConfig, + ts.sys.readFile ); if (error) { - throw new Error(ts.formatDiagnostic(error, formatDiagnosticHost)); + throw new Error(ts.formatDiagnostic(error, formatDiagnosticHost)); } appTsConfig = readTsConfig; @@ -186,113 +186,113 @@ try { // adding in "include" and "exclude", but the compilerOptions remain untouched let result; parsedTsConfig = immer(readTsConfig, config => { - result = ts.parseJsonConfigFileContent( + result = ts.parseJsonConfigFileContent( config, ts.sys, path.dirname(paths.appTsConfig) - ); + ); }); if (result.errors && result.errors.length) { - throw new Error( + throw new Error( ts.formatDiagnostic(result.errors[0], formatDiagnosticHost) - ); + ); } parsedCompilerOptions = result.options; -} catch (e) { + } catch (e) { if (e && e.name === 'SyntaxError') { - console.error( + console.error( chalk.red.bold( - 'Could not parse', - chalk.cyan('tsconfig.json') + '.', - 'Please make sure it contains syntactically correct JSON.' + 'Could not parse', + chalk.cyan('tsconfig.json') + '.', + 'Please make sure it contains syntactically correct JSON.' ) - ); + ); } console.log(e && e.message ? `${e.message}` : ''); process.exit(1); -} + } -if (appTsConfig.compilerOptions == null) { + if (appTsConfig.compilerOptions == null) { appTsConfig.compilerOptions = {}; firstTimeSetup = true; -} + } -for (const option of Object.keys(compilerOptions)) { + for (const option of Object.keys(compilerOptions)) { const { parsedValue, value, suggested, reason } = compilerOptions[option]; const valueToCheck = parsedValue === undefined ? value : parsedValue; const coloredOption = chalk.cyan('compilerOptions.' + option); if (suggested != null) { - if (parsedCompilerOptions[option] === undefined) { + if (parsedCompilerOptions[option] === undefined) { appTsConfig = immer(appTsConfig, config => { - config.compilerOptions[option] = suggested; + config.compilerOptions[option] = suggested; }); messages.push( - `${coloredOption} to be ${chalk.bold( + `${coloredOption} to be ${chalk.bold( 'suggested' - )} value: ${chalk.cyan.bold(suggested)} (this can be changed)` + )} value: ${chalk.cyan.bold(suggested)} (this can be changed)` ); - } + } } else if (parsedCompilerOptions[option] !== valueToCheck) { - appTsConfig = immer(appTsConfig, config => { + appTsConfig = immer(appTsConfig, config => { config.compilerOptions[option] = value; - }); - messages.push( + }); + messages.push( `${coloredOption} ${chalk.bold( - valueToCheck == null ? 'must not' : 'must' + valueToCheck == null ? 'must not' : 'must' )} be ${valueToCheck == null ? 'set' : chalk.cyan.bold(value)}` + - (reason != null ? ` (${reason})` : '') - ); + (reason != null ? ` (${reason})` : '') + ); } -} + } -// tsconfig will have the merged "include" and "exclude" by this point -if (parsedTsConfig.include == null) { + // tsconfig will have the merged "include" and "exclude" by this point + if (parsedTsConfig.include == null) { appTsConfig = immer(appTsConfig, config => { - config.include = ['src']; + config.include = ['src']; }); messages.push( - `${chalk.cyan('include')} should be ${chalk.cyan.bold('src')}` + `${chalk.cyan('include')} should be ${chalk.cyan.bold('src')}` ); -} + } -if (messages.length > 0) { + if (messages.length > 0) { if (firstTimeSetup) { - console.log( + console.log( chalk.bold( - 'Your', - chalk.cyan('tsconfig.json'), - 'has been populated with default values.' + 'Your', + chalk.cyan('tsconfig.json'), + 'has been populated with default values.' ) - ); - console.log(); + ); + console.log(); } else { - console.warn( + console.warn( chalk.bold( - 'The following changes are being made to your', - chalk.cyan('tsconfig.json'), - 'file:' + 'The following changes are being made to your', + chalk.cyan('tsconfig.json'), + 'file:' ) - ); - messages.forEach(message => { + ); + messages.forEach(message => { console.warn(' - ' + message); - }); - console.warn(); + }); + console.warn(); } writeJson(paths.appTsConfig, appTsConfig); -} + } -// Reference `react-scripts` types -if (!fs.existsSync(paths.appTypeDeclarations)) { + // Reference `react-scripts` types + if (!fs.existsSync(paths.appTypeDeclarations)) { fs.writeFileSync( - paths.appTypeDeclarations, - `/// ${os.EOL}` + paths.appTypeDeclarations, + `/// ${os.EOL}` ); -} + } } module.exports = verifyTypeScriptSetup; From d98838de9bd51d94a045f73995c1852e11326785 Mon Sep 17 00:00:00 2001 From: Jayson Wu Date: Tue, 26 Apr 2022 16:17:15 +0800 Subject: [PATCH 09/10] Remove extra empty line. --- packages/react-scripts/config/css.html | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/react-scripts/config/css.html b/packages/react-scripts/config/css.html index d6253f52d7..2fa4ab4eb6 100644 --- a/packages/react-scripts/config/css.html +++ b/packages/react-scripts/config/css.html @@ -2,5 +2,4 @@ rel="stylesheet" href="<%= file %>" /> - <% }); %> - \ No newline at end of file + <% }); %> \ No newline at end of file From bea8365bf07d3344f3f4feafdd9177776d2e85ff Mon Sep 17 00:00:00 2001 From: Jayson Wu Date: Tue, 26 Apr 2022 16:18:44 +0800 Subject: [PATCH 10/10] Restore empty line in the end of file. --- packages/react-scripts/config/css.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/react-scripts/config/css.html b/packages/react-scripts/config/css.html index 2fa4ab4eb6..d6253f52d7 100644 --- a/packages/react-scripts/config/css.html +++ b/packages/react-scripts/config/css.html @@ -2,4 +2,5 @@ rel="stylesheet" href="<%= file %>" /> - <% }); %> \ No newline at end of file + <% }); %> + \ No newline at end of file