reactjstypescriptwebpackbabeljsweb-component

Webpack/React fails in runtime on dependencies typescript errors


I have a React + Webpack project that is meant to take react modules from a different repo and then bundle them up in a specific way to become web components. I have my modules imported in my package json: "react-modules": "git+ssh://[email protected]:my-repo#tag, and use them in my web components project like normal import { SomeModule } from "react-modules" However, even when importing a small module, my entire project goes berserk. It analyzes all of react-modules and fails on every single typescript error inside, including those commented out. I have added the suggested tsconfigs:

  "include": ["./src/**/*"],
  "exclude": ["node_modules/*", "dist"],
  "compilerOptions": {
    "baseUrl": ".",
    "outDir": "./dist/",
    "noImplicitAny": true,
    "module": "es6",
    "target": "es6",
    "jsx": "react",
    "allowJs": true,
    "allowSyntheticDefaultImports": true,
    "moduleResolution": "node",
    "sourceMap": true,
    "lib": ["ESNext", "dom"],
    "noEmit": true,
    "skipLibCheck": true,
    "isolatedModules": true,
    "esModuleInterop": true,
    "noEmitHelpers": true,
    "importHelpers": true,
    "downlevelIteration": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true
  }
}

But to not avail.

I expect to be able to import modules without breaking on every single typescript error of the imported module

The package.json of react-modules:

{
  "name": "react-modules",
  "version": "0.0.2",
  "description": "React modules",
  "main": "src/index.ts",
  "license": "UNLICENSED",
  "peerDependencies": {},
  "dependencies": {
    "@apollo/client": "^3.7.15",
    "@material-ui/core": "^4.11.4",
    "@pdftron/webviewer": "8.1.0",
    "@rematch/select": "^2.0.5",
    "@sentry/browser": "^7.54.0",
    "@sentry/react": "^7.54.0",
    "@types/material-ui": "^0.21.8",
    "apollo-client": "^2.4.5",
    "element-closest": "^3.0.0",
    "flow-typed": "^3.3.1",
    "global": "^4.4.0",
    "graphql": "14.0.2",
    "graphql-tag": "^2.12.6",
    "highcharts": "6.1.1",
    "highcharts-react-official": "1.4.0",
    "html-to-image": "^1.9.0",
    "i18next": "11.9.0",
    "i18next-xhr-backend": "3.0.0",
    "lodash": "4.17.10",
    "moment": "2.22.2",
    "prop-types": "15.6.1",
    "rc-slider": "^9.7.1",
    "react": "16.14.0",
    "react-apollo": "2.1.11",
    "react-circular-progressbar": "2.0.3",
    "react-content-loader": "^4.3.4",
    "react-contenteditable": "^3.3.5",
    "react-dom": "16.14.0",
    "react-i18next": "8.0.6",
    "react-intercom": "^1.0.15",
    "react-pdf": "^4.1.0",
    "react-redux": "^5.1.1",
    "react-responsive": "^6.1.1",
    "react-select": "^5.7.3",
    "react-swipeable": "4.2.0",
    "react-tippy": "https://github.com/HiredScore/react-tippy/tarball/39b3b7d625bc8e07b4cb2352149111aace8f007c",
    "react-toastify": "^8.1.0",
    "react-tooltip": "^4.2.6",
    "react-virtualized": "^9.22.3",
    "react-widgets": "^4.4.10",
    "recompose": "^0.26.0",
    "scroll-into-view": "^1.9.3",
    "string-pixel-width": "^1.10.0",
    "styled-components": "^5.3.3",
    "styled-system": "5.1.5",
    "typesync": "^0.8.0"
  },
  "scripts": {
    "start": "NODE_ENV=development webpack --mode=development --watch",
    "build": "NODE_ENV=production ./node_modules/.bin/webpack --mode=production",
    "storybook": "start-storybook -p 9001 -c .storybook -s ./.storybook/static",
    "storybook:build": "yarn build-storybook -s ./.storybook/static",
    "storybook:ci": "STORYBOOK_CI=true yarn storybook:build",
    "design-system": "yarn storybook:build -o designsystem-static",
    "test": "node scripts/test.js --env=jsdom --silent",
    "test:debug": "yarn test -u --runInBand --no-cache --ci --silent=False",
    "test:visual:build": "yarn storybook:ci > /dev/null 2>&1",
    "test:visual:run": "yarn loki test --chromeDockerImage yukinying/chrome-headless-browser:93.0.4535.3 --reactUri file:./storybook-static",
    "loki-test-report": "reg-cli ./.loki/current ./.loki/reference ./.loki/difference --extendedErrors --enableAntialias --report ./.loki/report.html --json ./.loki/report.json",
    "test:visual:update": "yarn storybook:ci && yarn loki update --chromeDockerImage yukinying/chrome-headless-browser:93.0.4535.3 --reactUri file:./storybook-static",
    "test:all": "CI=1 yarn test && yarn test:visual:run",
    "test:all:update": "CI=1 yarn test -u && yarn test:visual:update",
    "build-all": "yarn build",
    "typecheck": "tsc --noEmit",
    "lint": "eslint src/ --ext .js,.ts,.tsx",
    "wtf": "./node_modules/.bin/rimraf node_modules && rm -f package-lock.json && rm -f yarn.lock && yarn cache clean && yarn",
    "test:coverage": "yarn test --watchAll=false --coverage",
    "generate": "plop"
  },
  "devDependencies": {
    "@babel/cli": "^7.5.0",
    "@babel/core": "^7.5.4",
    "@babel/helper-builder-react-jsx-experimental": "^7.12.11",
    "@babel/helper-define-map": "^7.15.4",
    "@babel/plugin-proposal-class-properties": "^7.5.0",
    "@babel/plugin-transform-runtime": "^7.5.0",
    "@babel/polyfill": "^7.4.4",
    "@babel/preset-env": "^7.5.4",
    "@babel/preset-flow": "^7.0.0",
    "@babel/preset-react": "^7.0.0",
    "@babel/preset-typescript": "^7.16.0",
    "@babel/register": "^7.4.4",
    "@babel/runtime": "^7.5.4",
    "@loki/create-async-callback": "^0.28.0",
    "@purtuga/esm-webpack-plugin": "^1.5.0",
    "@rematch/core": "^1.1.0",
    "@storybook/addon-actions": "^6.3.12",
    "@storybook/addon-console": "^1.2.3",
    "@storybook/addon-controls": "^6.3.12",
    "@storybook/addon-essentials": "^6.3.12",
    "@storybook/addon-links": "^6.3.12",
    "@storybook/addon-storyshots": "^6.3.12",
    "@storybook/addon-storyshots-puppeteer": "^6.3.12",
    "@storybook/addon-viewport": "^6.3.12",
    "@storybook/addons": "^6.3.12",
    "@storybook/cli": "^6.3.12",
    "@storybook/react": "^6.3.12",
    "@svgr/webpack": "^5.4.0",
    "@testing-library/jest-dom": "^5.16.4",
    "@testing-library/react": "12.0.0",
    "@types/babel__core": "^7.1.18",
    "@types/babel__plugin-transform-runtime": "^7.9.2",
    "@types/babel__preset-env": "^7.9.2",
    "@types/case-sensitive-paths-webpack-plugin": "2.1.2",
    "@types/element-closest": "^3.0.0",
    "@types/enzyme": "3.10.11",
    "@types/enzyme-adapter-react-16": "1.0.6",
    "@types/eslint": "^8.4.1",
    "@types/eslint-plugin-prettier": "^3.1.0",
    "@types/file-loader": "5.0.1",
    "@types/html-webpack-plugin": "3.2.6",
    "@types/jest-specific-snapshot": "^0.5.5",
    "@types/lodash": "4.14.178",
    "@types/mini-css-extract-plugin": "^2.5.1",
    "@types/mockdate": "3.0.0",
    "@types/optimize-css-assets-webpack-plugin": "5.0.1",
    "@types/prettier": "^2.4.3",
    "@types/prop-types": "15.7.4",
    "@types/react": "^18.0.12",
    "@types/react-dev-utils": "^9.0.11",
    "@types/react-dom": "^18.0.5",
    "@types/react-pdf": "^5.7.2",
    "@types/react-redux": "^7.1.24",
    "@types/react-responsive": "^8.0.5",
    "@types/react-test-renderer": "17.0.1",
    "@types/react-virtualized": "^9.21.16",
    "@types/react-widgets": "^4.4.7",
    "@types/recompose": "^0.26.5",
    "@types/rimraf": "^3.0.2",
    "@types/scroll-into-view": "^1.16.0",
    "@types/string-pixel-width": "^1.7.2",
    "@types/styled-components": "^5.1.25",
    "@types/styled-system": "^5.1.15",
    "@types/webpack": "5.28.0",
    "@types/webpack-dev-server": "3.11.0",
    "@types/webpack-manifest-plugin": "3.0.5",
    "@types/workbox-webpack-plugin": "3.6.3",
    "@typescript-eslint/eslint-plugin": "^5.6.0",
    "@typescript-eslint/parser": "^5.6.0",
    "add": "^2.0.6",
    "add-npm-scripts": "^1.0.0",
    "addon-storyshots-selenium": "0.1.0",
    "babel-eslint": "^10.1.0",
    "babel-jest": "^24.8.0",
    "babel-loader": "8.0.4",
    "babel-plugin-import-graphql": "^2.6.2",
    "babel-plugin-named-asset-import": "0.3.1",
    "babel-plugin-react-generate-property": "1.0.5",
    "babel-plugin-require-context-hook": "1.0.0",
    "babel-plugin-styled-components": "^1.10.0",
    "babel-plugin-transform-dynamic-import": "^2.1.0",
    "babel-preset-react-app": "^9.0.0",
    "case-sensitive-paths-webpack-plugin": "2.1.2",
    "css-loader": "1.0.0",
    "enzyme": "3.11.0",
    "enzyme-adapter-react-16": "1.15.6",
    "eslint": "^7.32.0",
    "eslint-config-airbnb": "17.1.0",
    "eslint-config-prettier": "^8.3.0",
    "eslint-import-resolver-typescript": "^2.5.0",
    "eslint-loader": "2.1.1",
    "eslint-plugin-flowtype": "3.0.0",
    "eslint-plugin-import": "2.14.0",
    "eslint-plugin-jest": "21.25.1",
    "eslint-plugin-jsx-a11y": "6.1.2",
    "eslint-plugin-node": "5.1.1",
    "eslint-plugin-prettier": "^4.0.0",
    "eslint-plugin-promise": "3.5.0",
    "eslint-plugin-react": "7.11.1",
    "eslint-webpack-plugin": "^2.5.2",
    "expect": "23.1.0",
    "file-loader": "2.0.0",
    "html-webpack-plugin": "4.0.0-beta.5",
    "jest": "^26.0",
    "jest-junit": "6.4.0",
    "jest-specific-snapshot": "^1.0.0",
    "jest-styled-components": "^7.0.8",
    "loki": "^0.28.1",
    "mini-css-extract-plugin": "^0.4.2",
    "mockdate": "2.0.2",
    "optimize-css-assets-webpack-plugin": "5.0.1",
    "plop": "^3.1.1",
    "pnp-webpack-plugin": "1.1.0",
    "postcss-flexbugs-fixes": "4.1.0",
    "postcss-loader": "3.0.0",
    "postcss-preset-env": "^6.6.0",
    "postcss-safe-parser": "4.0.2",
    "prettier": "^2.4.0",
    "react-dev-utils": "5.0.2",
    "react-svg-loader": "3.0.3",
    "react-test-renderer": "16.4.0",
    "reg-cli": "^0.16.1",
    "rimraf": "^2.6.3",
    "sass-loader": "7.1.0",
    "storybook-addon-locale": "^0.3.6",
    "style-loader": "0.23.1",
    "ts-migrate": "^0.1.26",
    "typescript": "^4.5.2",
    "url-loader": "1.1.2",
    "webpack": "4.44.2",
    "webpack-cli": "3.1.2",
    "webpack-dev-server": "3.11.0",
    "webpack-manifest-plugin": "2.0.4",
    "webpack-md5-hash": "0.0.6",
    "workbox-webpack-plugin": "3.6.3"
  },
  "resolutions": {
    "enzyme/cheerio": "=1.0.0-rc.3"
  },
  "loki": {
    "requireReference": true,
    "chromeDockerWithoutSeccomp": true,
    "chromeFlags": "--headless --disable-gpu --hide-scrollbars --no-sandbox",
    "chromeSelector": ".wrapper > *, #root > *",
    "configurations": {
      "chrome.laptop": {
        "target": "chrome.docker",
        "width": 1366,
        "height": 768,
        "mobile": false,
        "fetchFailIgnore": "/sample-videos/"
      }
    }
  }
}

The structure is basically: src/components/component/component.tsx, it's redux so there's also: src/blocks/block/index.ts with it's container, view, component and model.


Solution

  • TLDR; You need to specify main and types field in the libraries package.json file.

    You need to understand how TypeScript changes the workflow for developing Node.js/NPM based applications.

    To start with, you are authoring your library react-modules using TypeScript. When you install it in some other project as a dependency, either via npm registry or using the git+ssh:// specified in package.json of your consuming application, it gets installed inside the node_modules folder. Anything inside the node_modules should be JavaScript and not the raw TypeScript source files (unless, you are using Deno or similar TS runtime.)

    When you introduce TypeScript in you tech stack, your workflow is roughly this:

    1. Write your library using TypeScript.
    2. In general, when, you compile using TypeScript's CLI - tsc, it compiles your source code into a JavaScript code. You can put this generated code anywhere. Generally something like a <ROOT>/dist or <ROOT>/build folder is used as a convention.
    3. If you have some complex setup (like importing the CSS from TS file), then plain tsc is not enough. You will need a bundler like Webpack to exact CSS into a standalone CSS file. For libraries, the Rollup.js is a better choice due to various reasons.
    4. The compiled code using tsc generates two files for each source .ts file - one plain old .js file that contains no TS code and other is *.d.ts file which TypeScript compiler uses to understand the shape and types exposed by your library.
    5. Note that these *.d.ts are pure definitions files. Their absence has no impact on Node.js or even the bundler like Webpack. It only impact when you run type check using the tsc.
    6. If you are not publishing the library to NPM (applicable in your case since you are using the git+ssh:// protocol), you need to generate the dist folder and make it part of your tag (effectively committing the generated dist folder).
    7. Finally, if you used the dist folder to generate the compiled code and your library's barrel file is src/index.ts, then you need to update your package.json of the library with following fields:
    {
      "name": "react-modules",
      "main": "./dist/index.js",
      "types": "./dist/index.d.ts"
    }
    

    You may also end up using newer exports and type field depending on the requirements and target.

    So to summarize, you cannot directly using .ts file as a package's main entry file. You need to compile the library's ts code and use the generated code and publish the NPM module or git tag with that generated code.