Search code examples
webpackcreate-react-appreact-scripts

Only the first error is shown when CRA project is built


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?


Solution

  • 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: {}
            }
        ]
    };