Estus Flask
Estus Flask

Reputation: 222548

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?

Upvotes: 6

Views: 2196

Answers (2)

Zawiszor
Zawiszor

Reputation: 538

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

Upvotes: 1

zhirzh
zhirzh

Reputation: 3449

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.

Upvotes: 4

Related Questions