6

In the default TypeScript based create-react-app project, only the first TS error is shown when the project is built with react-scripts but all errors are shown when running tsc.

The project was initialized with create-react-app foo --typescript and only src/index.tsx was modified after initialization:

src/index.tsx src/index.tsx

console.log(typeof nonExistentVar);
    console.log(typeof nonExistentVar);
console.log(typeof nonExistentVar2);
    console.log(typeof nonExistentVar2);
export {};

package.json

export {};
{


  "name": "foo",

  "version": "0.1.0",

  "private": true,

  "dependencies": {

    "@types/jest": "24.0.15",

    "@types/node": "12.6.8",

    "@types/react": "16.8.23",

    "@types/react-dom": "16.8.5",

    "react": "^16.8.6",

    "react-dom": "^16.8.6",

    "react-scripts": "3.0.1",

    "typescript": "3.5.3"

  },

  "scripts": {

    "start": "react-scripts start",

    "build": "react-scripts build",

    "test": "react-scripts test",

    "eject": "react-scripts eject"

  },

  "browserslist": {

    "production": [

      ">0.2%",

      "not dead",

      "not op_mini all"

    ],

    "development": [

      "last 1 chrome version",

      "last 1 firefox version",

      "last 1 safari version"

    ]

  }

}

tsconfig.json

{

  "compilerOptions": {

    "target": "es5",

    "lib": [

      "dom",

      "dom.iterable",

      "esnext"

    ],

    "allowJs": true,

    "skipLibCheck": true,

    "esModuleInterop": true,

    "allowSyntheticDefaultImports": true,

    "strict": true,

    "forceConsistentCasingInFileNames": true,

    "module": "esnext",

    "moduleResolution": "node",

    "resolveJsonModule": true,

    "isolatedModules": true,

    "noEmit": true,

    "jsx": "preserve"

  },

  "include": [

    "src"

  ]

}

npm start shows only the first error:

Failed to compile.

C:/foo/src/index.tsx
TypeScript error in C:/foo/src/index.tsx(1,20):
Cannot find name 'nonExistentVar'.  TS2304

  > 1 | console.log(typeof nonExistentVar);
      |                    ^
    2 | console.log(typeof nonExistentVar2);
    3 | export {};

And tsc shows all errors at once:

src/index.tsx:1:20 - error TS2304: Cannot find name 'nonExistentVar'.

1 console.log(typeof nonExistentVar);
                     ~~~~~~~~~~~~~~

src/index.tsx:2:20 - error TS2304: Cannot find name 'nonExistentVar2'.

2 console.log(typeof nonExistentVar2);
                     ~~~~~~~~~~~~~~~


Found 2 errors.

How can I force start and build scripts to show all errors?

Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • If compiler fails to find a variable named `nonExistentVar`, how is it supposed to proceed and show further errors? Because of it cannot find the variable, it failed to build and shows why it failed. It makes total sense to me to show only the first error. – Harun Yilmaz Jul 26 '19 at 11:49
  • 1
    *how is it supposed to proceed and show further errors* - raw `tsc` call allows that. I'm not sure how to do that, hence the question. My goal is to list all errors that need to be fixed on project build instead of fixing them one by one. – Estus Flask Jul 26 '19 at 11:54
  • 1
    OK, sorry, I got it. I found [this](https://github.com/wmonk/create-react-app-typescript/issues/103#issuecomment-314756009) thread and then after some search found [this](https://www.npmjs.com/package/react-dev-utils). Maybe these will give some clue. – Harun Yilmaz Jul 26 '19 at 12:16
  • 1
    I found this https://github.com/facebook/create-react-app/blob/67a2088a7b6ebbf6b78c82bb4b179d732340e2f2/packages/react-dev-utils/WebpackDevServerUtils.js#L226 However, turning off this logic didn't help out. You have to eject and tweak `scripts/build.js` file – amankkg Jul 26 '19 at 12:40
  • @HarunYilmaz Thanks for the research, I'll give it a check. – Estus Flask Jul 26 '19 at 12:41
  • gotta check `react-scripts`' `tsc` usage closely... – amankkg Jul 26 '19 at 12:42
  • @amankkg Thanks for the link, it expains the cause. At this point I'm trying to avoid ejection if possible. – Estus Flask Jul 26 '19 at 12:43

2 Answers2

4

The problem?

Here's what really happened. When fork-ts-checker-webpack-plugin finds "type errors" in your code, it adds them to webpack's compilation errors for further processing and/or logging.

When the start script from react-scripts package is executed, the same errors array is trimmed to length 1. It then shows the first error (the only error) and stops the process.

When the build script is run, it's the react-dev-utils/WebpackDevServerUtils that does the same thing internally.


The solution?

As pointed out by @amankkg, "you have to eject and tweak scripts/build.js file" and the build process will work the way you want it to.

The real problem is with starting a dev server since react-dev-utils/WebpackDevServerUtils is a part of node_modules and tweaking it locally is not a long-term fix. Your best bet is to fork the repo on github, make the required changes and use your forked version in your project(s).


Edit 1

Also, if you run the webpack config with just webpack-cli, you'd see both the errors (along with a completed build).

Just eject the code, modify webpack's config file to set webpackEnv from NODE_ENV:

module.exports = function(webpackEnv) {
  webpackEnv = webpackEnv || process.env.NODE_ENV    //// add this line
  const isEnvDevelopment = webpackEnv === 'development';
  const isEnvProduction = webpackEnv === 'production';

and run the following:

npm i -g webpack-cli
NODE_ENV=development webpack --config config/webpack.config.js

Here's the sample output:

...

Entrypoint main = static/js/bundle.js ...

ERROR in /foo/src/index.tsx
ERROR in /foo/src/index.tsx(14,20):
TS2304: Cannot find name 'nonExistentVar'.

ERROR in /foo/src/index.tsx
ERROR in /foo/src/index.tsx(15,20):
TS2304: Cannot find name 'nonExistentVar2'.

...

Edit 2

There is one more thing you could try. There's this node package patch-package that allows patching node_modules code locally and commits the said patch to your repo. I haven't used it but the docs explain the process pretty neatly. You should definitely check it out.

zhirzh
  • 3,273
  • 3
  • 25
  • 30
  • 1
    Thank you. I'm trying to avoid the ejection if possible. I will likely resort to patch-package since this seems to be unfixable with react-app-rewired. – Estus Flask Aug 02 '19 at 08:12
1

Found a better solution. No forking or ejecting necessary. You can write a very simple plugin that hooks into fork-ts-checker-webpack-plugin to get all the errors and print them. Edit craco.config.js to create the plugin class in my example I called it PrintAllWebpackErrorsPlugin. Then instantiate the class in the webpack section of the module.exports. Don't forget to reset craco start to apply the changes. The craco.config.js file should look something like the following:

const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
// This plugin uses a hook on the ForkTSCheckedWebpackPlugin to extract the errors and print them to console
class PrintAllWebpackErrorsPlugin {
    apply(compiler) {
        const hooks = ForkTsCheckerWebpackPlugin.getCompilerHooks(compiler);
        hooks.done.tap("PrintAllWebpackErrorsPlugin", function(errors) {
            errors.forEach(err => {
                console.log(err.file)
                console.log(`Typescript error in ${err.file}(${err.line},${err.character})`)
                console.log(`${err.message} TS${err.code}`)
            })
        })
    }
}

module.exports = {
    reactScriptsVersion: "react-scripts" /* (default value) */,
    style: {
        modules: {
            localIdentName: ""
        },
        css: {
            loaderOptions: { /* Any css-loader configuration options: https://github.com/webpack-contrib/css-loader. */ },
            loaderOptions: (cssLoaderOptions, { env, paths }) => { return cssLoaderOptions; }
        },
        sass: {
            loaderOptions: { /* Any sass-loader configuration options: https://github.com/webpack-contrib/sass-loader. */ },
            loaderOptions: (sassLoaderOptions, { env, paths }) => { return sassLoaderOptions; }
        },
        postcss: {
        }
    },
    eslint: {
        enable: false /* (default value) */,
        mode: "extends" /* (default value) */ || "file",
        configure: { /* Any eslint configuration options: https://eslint.org/docs/user-guide/configuring */ },
        configure: (eslintConfig, { env, paths }) => { return eslintConfig; },
        pluginOptions: { /* Any eslint plugin configuration options: https://github.com/webpack-contrib/eslint-webpack-plugin#options. */ },
        pluginOptions: (eslintOptions, { env, paths }) => { return eslintOptions; }
    },
    babel: {
        presets: [],
        plugins: [],
        loaderOptions: { /* Any babel-loader configuration options: https://github.com/babel/babel-loader. */ },
        loaderOptions: (babelLoaderOptions, { env, paths }) => { return babelLoaderOptions; }
    },
    typescript: {
        enableTypeChecking: true /* (default value)  */
    },
    webpack: {
        alias: {},
        plugins: {
            add: [
                // Notice I'm instantiating the plugin here to include it.
                new PrintAllWebpackErrorsPlugin(),
            ], /* An array of plugins */
            remove: [],  /* An array of plugin constructor's names (i.e. "StyleLintPlugin", "ESLintWebpackPlugin" ) */
        },
        configure: { /* Any webpack configuration options: https://webpack.js.org/configuration */ },
        configure: (webpackConfig, { env, paths }) => { return webpackConfig; }
    },
    jest: {
        babel: {
            addPresets: true, /* (default value) */
            addPlugins: true  /* (default value) */
        },
        configure: { /* Any Jest configuration options: https://jestjs.io/docs/en/configuration */ },
        configure: (jestConfig, { env, paths, resolve, rootDir }) => { return jestConfig; }
    },
    devServer: { /* Any devServer configuration options: https://webpack.js.org/configuration/dev-server/#devserver */ },
    devServer: (devServerConfig, { env, paths, proxy, allowedHost }) => { return devServerConfig; },
    plugins: [
        {
            plugin: {
                overrideCracoConfig: ({ cracoConfig, pluginOptions, context: { env, paths } }) => { return cracoConfig; },
                overrideWebpackConfig: ({ webpackConfig, cracoConfig, pluginOptions, context: { env, paths } }) => { return webpackConfig; },
                overrideDevServerConfig: ({ devServerConfig, cracoConfig, pluginOptions, context: { env, paths, proxy, allowedHost } }) => { return devServerConfig; },
                overrideJestConfig: ({ jestConfig, cracoConfig, pluginOptions, context: { env, paths, resolve, rootDir } }) => { return jestConfig },
            },
            options: {}
        }
    ]
};
Zawiszor
  • 528
  • 4
  • 10