Search code examples
reactjstypescriptbabeljsstyled-componentstsc

is there a way to prevent my component package from installing styled-component in it's directory in node modules


i have a custom ui library made with react and styled components, i've specified in the library's package.json to use my main apps styled-components

  "peerDependencies": {
    "react": "^18.2.0",
    "nuka-carousel": "6.0.3",
    "styled-components": "6.0.8"
  },

but when i include the library in my app and run yarn install i noticed that it's installed in the library's node_moduleenter image description here

as a result the component breaks when trying to access my apps theme

- error Error [TypeError]: Cannot read properties of undefined (reading 'h1')
    at /Users/user/Desktop/code/appclient/node_modules/ui-kit/lib/components/H1/styled/H1.js:16:36
    at ke (/Users/user/Desktop/code/appclient/node_modules/ui-kit/node_modules/styled-components/dist/styled-components.cjs.js:1:17838)
    at e.generateAndInjectStyles (/Users/user/Desktop/code/appclient/node_modules/ui-kit/node_modules/styled-components/dist/styled-components.cjs.js:1:19353)
    at /Users/user/Desktop/code/appclient/node_modules/ui-kit/node_modules/styled-components/dist/styled-components.cjs.js:1:21658
    at /Users/user/Desktop/code/appclient/node_modules/ui-kit/node_modules/styled-components/dist/styled-components.cjs.js:1:21771
    at I (/Users/user/Desktop/code/appclient/node_modules/ui-kit/node_modules/styled-components/dist/styled-components.cjs.js:1:21998)
    at renderWithHooks (/Users/user/Desktop/code/appclient/node_modules/react-dom/cjs/react-dom-server.browser.development.js:5658:16)
    at renderForwardRef (/Users/user/Desktop/code/appclient/node_modules/react-dom/cjs/react-dom-server.browser.development.js:5842:18)
    at renderElement (/Users/user/Desktop/code/appclient/node_modules/react-dom/cjs/react-dom-server.browser.development

if i delete the styled-components inside the library inside node_modules

enter image description here

the app starts working as expected as it's using the styled-components of my main app

enter image description here

Let me know if there's something i'm missing that could be causing the issue. I can provide more details if necessary as well. Thanks!

Edit: Adding package json of library and my main app as requested

my library

{
  "name": "uikit",
  "version": "1.16.0",
  "description": "A collection of components",
  "main": "./lib/index.js",
  "resolutions": {
    "@storybook/react-docgen-typescript-plugin": "1.0.6--canary.9.cd77847.0",
    "@babel/preset-env": "^7.22.10",
    "jackspeak": "2.1.1"
  },
  "scripts": {
    "compile:clean": "rimraf lib coverage",
    "compile": "tsc -m es6 --outDir lib-",
    "lint": "yarn run lint:eslint && yarn run lint:stylelint",
    "test:full": "yarn lint && yarn test:unit",
    "test:unit": "yarn jest",
    "storybook": "storybook dev -p 6006",
    "build-storybook": "storybook build",
    "release": "yarn compile:clean && yarn compile",
    "release:major": "yarn release && standard-version --release-as major",
    "release:minor": "yarn release && standard-version --release-as minor",
    "release:patch": "yarn release && standard-version --release-as patch",
    "release:alpha": "yarn release && standard-version --prerelease alpha",
    "start": "yarn storybook",
    "prepare": "husky install"
  },
  "keywords": [
    "components",
    "icons"
  ],
  "license": "MIT",
  "devDependencies": {
    "@babel/cli": "^7.22.10",
    "@babel/core": "^7.22.10",
    "@babel/eslint-parser": "^7.22.10",
    "@babel/eslint-plugin": "^7.22.10",
    "@babel/plugin-proposal-class-properties": "^7.18.6",
    "@babel/plugin-proposal-decorators": "^7.22.10",
    "@babel/plugin-proposal-export-default-from": "^7.22.5",
    "@babel/plugin-proposal-export-namespace-from": "^7.18.9",
    "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6",
    "@babel/plugin-proposal-optional-chaining": "^7.21.0",
    "@babel/plugin-proposal-private-methods": "^7.18.6",
    "@babel/plugin-transform-runtime": "^7.22.10",
    "@babel/preset-env": "^7.22.10",
    "@babel/preset-react": "^7.22.5",
    "@babel/preset-typescript": "^7.22.5",
    "@babel/runtime": "^7.22.10",
    "@mdx-js/loader": "^2.1.3",
    "@storybook/addon-a11y": "^7.4.6",
    "@storybook/addon-actions": "^7.4.6",
    "@storybook/addon-docs": "^7.4.6",
    "@storybook/addon-essentials": "^7.4.6",
    "@storybook/addon-mdx-gfm": "^7.4.6",
    "@storybook/addon-storysource": "^7.4.6",
    "@storybook/addon-viewport": "^7.4.6",
    "@storybook/addons": "^7.4.6",
    "@storybook/react": "^7.4.6",
    "@storybook/react-webpack5": "^7.4.6",
    "@storybook/source-loader": "^7.4.6",
    "@storybook/theming": "^7.4.6",
    "@stylelint/postcss-css-in-js": "^0.38.0",
    "@types/body-scroll-lock": "^3.1.0",
    "@types/leaflet": "^1.9.0",
    "@types/lodash": "^4.14.195",
    "@types/luxon": "^3.2.0",
    "@types/node": "^20.8.6",
    "@types/react-motion": "^0.0.35",
    "@types/react-test-renderer": "^18.0.0",
    "@types/react-transition-group": "^4.4.5",
    "@types/shortid": "^0.0.30",
    "@typescript-eslint/eslint-plugin": "^5.45.1",
    "@typescript-eslint/parser": "^5.45.1",
    "autoprefixer": "^10.4.13",
    "babel-loader": "^9.1.0",
    "babel-plugin-module-resolver": "^5.0.0",
    "babel-plugin-styled-components": "^2.0.7",
    "css-loader": "^6.8.1",
    "eslint": "^8.24.0",
    "eslint-plugin-import": "^2.27.5",
    "eslint-plugin-jest-async": "^1.0.3",
    "eslint-plugin-jsx-a11y": "^6.7.1",
    "eslint-plugin-react": "^7.32.2",
    "eslint-plugin-react-hooks": "^4.6.0",
    "eslint-plugin-storybook": "^0.6.14",
    "husky": "^8.0.1",
    "identity-obj-proxy": "^3.0.0",
    "jest": "^29.1.2",
    "jsdom": "^22.0.0",
    "jsdom-global": "^3.0.2",
    "postcss": "^8.4.18",
    "postcss-syntax": "^0.36.2",
    "react": "^18.2.0",
    "rimraf": "^5.0.1",
    "sass": "^1.55.0",
    "sass-loader": "^13.3.2",
    "standard-version": "^9.5.0",
    "storybook": "^7.4.6",
    "style-loader": "^3.3.3",
    "stylelint": "^14.9.1",
    "stylelint-config-recommended-scss": "^8.0.0",
    "stylelint-config-standard": "^29.0.0",
    "stylelint-config-styled-components": "^0.1.1",
    "stylelint-processor-styled-components": "^1.9.0",
    "stylelint-scss": "^5.0.0",
    "typescript": "^5.2.2",
    "webpack": "^5.74.0",
    "webpack-cli": "^5.0.0"
  },
  "dependencies": {
    "@testing-library/jest-dom": "^6.0.0",
    "@testing-library/react": "^14.0.0",
    "@testing-library/user-event": "^14.4.3",
    "body-scroll-lock": "^4.0.0-beta.0",
    "downshift": "^6.1.12",
    "file-loader": "^4.1.0",
    "fuzzysort": "^2.0.1",
    "jasmine-reporters": "^2.5.0",
    "jest-environment-jsdom": "^29.1.2",
    "jest-fetch-mock": "^3.0.3",
    "jest-jasmine2": "^29.1.2",
    "jest-styled-components": "^7.1.1",
    "leaflet": "^1.8.0",
    "lodash": "^4.17.21",
    "luxon": "^3.0.4",
    "moment": "^2.29.4",
    "nuka-carousel": "6.0.3",
    "pluralize": "^8.0.0",
    "prop-types": "^15.8.1",
    "qs": "^6.11.2",
    "react-addons-shallow-compare": "^15.6.0",
    "react-autosuggest": "^10.0.2",
    "react-dates": "^21.8.0",
    "react-device-detect": "^2.2.2",
    "react-dom": "^18.2.0",
    "react-leaflet": "^4.0.2",
    "react-moment-proptypes": "^1.8.1",
    "react-motion": "^0.5.2",
    "react-test-renderer": "^18.2.0",
    "react-transition-group": "^4.4.5",
    "react-truncate-markup": "^5.1.2",
    "shortid": "^2.2.8",
    "styled-components": "6.0.8",
    "stylis": "^4.0.0",
    "uniqid": "^5.4.0"
  },
  "peerDependencies": {
    "react": "^18.2.0",
    "nuka-carousel": "6.0.3",
    "styled-components": "6.0.8"
  },
  "jest": {
    "moduleFileExtensions": [
      "jsx",
      "js",
      "ts",
      "tsx"
    ],
    "testEnvironment": "jsdom",
    "testRunner": "jest-jasmine2",
    "moduleNameMapper": {
      "\\.(css|scss)$": "identity-obj-proxy",
      "^.+\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js"
    },
    "testPathIgnorePatterns": [
      "/node_modules/",
      "/lib/"
    ],
    "collectCoverage": true,
    "verbose": true,
    "modulePathIgnorePatterns": [
      "rpmbuild"
    ],
    "roots": [
      "<rootDir>",
      "./src"
    ],
    "modulePaths": [
      "<rootDir>",
      "./src"
    ],
    "moduleDirectories": [
      "node_modules"
    ],
    "unmockedModulePathPatterns": [
      "<rootDir>/node_modules/react/",
      "<rootDir>/node_modules/react-dom/",
      "<rootDir>/node_modules/react-addons-test-utils/",
      "<rootDir>/node_modules/fbjs",
      "<rootDir>/node_modules/core-js",
      "<rootDir>/node_modules/jasmine-reporters"
    ],
    "setupFilesAfterEnv": [
      "<rootDir>/setupTests.js"
    ]
  },
  "husky": {
    "hooks": {
      "pre-commit": "yarn test"
    }
  }
}

my main app

{
  "name": "appclient",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "clean:next": "node clean_next.js",
    "check:connection": "node check_connection.js",
    "clean:check:connection": "yarn run clean:next && yarn run check:connection",
    "dev": "yarn run clean:check:connection && NODE_ENV=development env-cmd next -p 3020",
    "dev:otel": "OTEL_ENADLED='true' yarn run dev",
    "build": "IS_BUILDING='true' NODE_ENV=production next build",
    "start": "IS_BUILDING='false' next start",
    "test": "NODE_ENV=test jest --coverage --testPathIgnorePatterns cypress/**",
    "load_test": "siege -c10 -i -t30M -f ./tests/load_test_locations.txt",
    "lint": "yarn run eslint && yarn run stylelint",
    "eslint": "$(yarn bin)/eslint --ext .js,.jsx,.ts,.tsx src/**",
    "stylelint": "$(yarn bin)/stylelint src/**",
    "prepare": "husky install"
  },
  "lint-staged": {
    "*.{js,jsx,ts,tsx}": [
      "eslint --fix",
      "stylelint"
    ]
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@apollo/client": "3.8.5",
    "@boiseitguru/cookie-cutter": "^0.2.1",
    "@cloudnative/health": "^2.1.2",
    "@cloudnative/health-connect": "^2.1.0",
    "@datadog/browser-rum": "^4.50.1",
    "@marvelapp/react-ab-test": "^3.1.0",
    "@opentelemetry/exporter-trace-otlp-http": "^0.44.0",
    "@opentelemetry/resources": "^1.17.1",
    "@opentelemetry/sdk-node": "^0.44.0",
    "@opentelemetry/sdk-trace-base": "^1.17.1",
    "@opentelemetry/semantic-conventions": "^1.17.1",
    "@optimizely/optimizely-sdk": "^4.10.0",
    "@researchgate/react-intersection-observer": "^1.3.4",
    "@stripe/react-stripe-js": "^2.3.1",
    "@stripe/stripe-js": "^2.1.7",
    "@testing-library/jest-dom": "^6.1.4",
    "@testing-library/react": "^14.0.0",
    "@testing-library/user-event": "^14.5.1",
    "@types/actioncable": "^5.2.9",
    "actioncable": "^5.2.8",
    "apollo-link": "^1.2.14",
    "apollo-link-rest": "^0.9.0",
    "array-move": "^4.0.0",
    "axios": "^0.27.2",
    "cypress-iframe": "^1.0.1",
    "cypress-wait-until": "^2.0.1",
    "deepmerge": "^4.3.1",
    "device": "^0.3.11",
    "dotenv": "^16.3.1",
    "uikit": "1.16.1",  // my custom package
    "formik": "^2.4.5",
    "graphql": "^15.8.0",
    "graphql-ruby-client": "^1.11.9",
    "html-react-parser": "^4.2.2",
    "intersection-observer": "^0.12.2",
    "isomorphic-fetch": "^3.0.0",
    "libphonenumber-js": "^1.10.47",
    "lodash": "^4.17.21",
    "lru-cache": "^10.0.1",
    "luxon": "^3.4.3",
    "moment": "^2.29.4",
    "next": "13.4.19",
    "next-api-middleware": "^2.0.1",
    "next-useragent": "^2.8.0",
    "nuka-carousel": "^5.5.1",
    "pino": "^8.16.0",
    "pluralize": "^8.0.0",
    "prop-types": "^15.8.1",
    "qs": "^6.11.2",
    "query-string": "^8.1.0",
    "react": "^18.2.0",
    "react-cropper": "^2.3.3",
    "react-dates": "^21.8.0",
    "react-device-detect": "^2.2.3",
    "react-dom": "^18.2.0",
    "react-dropzone": "^14.2.3",
    "react-google-login": "^5.2.2",
    "react-inlinesvg": "^4.0.5",
    "react-marquee-slider": "^1.1.5",
    "react-responsive-carousel": "^3.2.23",
    "react-router-dom": "^6.16.0",
    "react-scroll-to-component-ssr": "^1.0.0",
    "react-sortable-hoc": "^2.0.0",
    "react-spring": "^9.7.3",
    "react-table": "^7.8.0",
    "react-tooltip": "^5.21.5",
    "react-waypoint": "^10.3.0",
    "rxjs": "^7.8.1",
    "sharp": "^0.32.6",
    "styled-components": "^6.0.9",
    "stylis": "^4.0.0",
    "use-clipboard-copy": "^0.2.0",
    "util": "^0.12.5",
    "yup": "^1.3.2"
  },
  "devDependencies": {
    "@jest/globals": "^29.7.0",
    "@percy/cli": "^1.27.3",
    "@percy/cypress": "^3.1.2",
    "@stylelint/postcss-css-in-js": "^0.38.0",
    "@types/jest": "^29.5.5",
    "@types/lodash": "^4.14.199",
    "@types/query-string": "^6.3.0",
    "@typescript-eslint/eslint-plugin": "^5.62.0",
    "@typescript-eslint/parser": "^5.62.0",
    "axios-mock-adapter": "^1.22.0",
    "babel-jest": "^29.7.0",
    "cypress": "^13.3.1",
    "cypress-plugin-tab": "^1.0.5",
    "env-cmd": "^10.1.0",
    "eslint": "^8.51.0",
    "eslint-config-next": "13.4.19",
    "eslint-import-resolver-alias": "^1.1.2",
    "eslint-plugin-cypress": "^2.15.1",
    "eslint-plugin-es": "^4.1.0",
    "eslint-plugin-import": "^2.28.1",
    "eslint-plugin-optimize-regex": "^1.2.1",
    "husky": "^8.0.3",
    "jasmine-reporters": "^2.5.2",
    "jest": "^29.7.0",
    "jest-canvas-mock": "^2.5.2",
    "jest-cli": "^29.6.0",
    "jest-environment-jsdom": "^29.6.4",
    "jsdom": "^22.1.0",
    "lint-staged": "^14.0.1",
    "mochawesome": "^7.1.3",
    "mochawesome-merge": "^4.3.0",
    "postcss-styled": "^0.34.0",
    "postcss-styled-syntax": "^0.5.0",
    "postcss-syntax": "^0.36.2",
    "prettier-eslint": "^15.0.1",
    "react-test-renderer": "^18.2.0",
    "stylelint": "^15.10.3",
    "stylelint-config-recommended": "^13.0.0",
    "stylelint-config-standard": "^34.0.0",
    "stylelint-config-styled-components": "^0.1.1",
    "stylelint-processor-styled-components": "^1.10.0",
    "typescript": "^5.2.2",
    "webpack": "5.88.2"
  },
  "jest": {
    "testEnvironment": "jsdom",
    "moduleFileExtensions": [
      "jsx",
      "js",
      "ts",
      "tsx"
    ],
    "transform": {
      "^.+\\.(js|jsx|ts|tsx)$": [
        "babel-jest",
        {
          "presets": [
            "next/babel"
          ]
        }
      ]
    },
    "transformIgnorePatterns": [
      "node_modules/(?!(decode-uri-component|filter-obj|split-on-first|query-string)/)"
    ],
    "modulePaths": [
      "src",
      "."
    ],
    "setupFilesAfterEnv": [
      "<rootDir>/setupTests.js",
      "jest-canvas-mock"
    ]
  }
}


Solution

  • The installed version of styled-components in the main app is different from the version that exists in the custom library.

    Just remove styled-component from custom library dependencies and use a lenient version of styled-component in custom library peerDependencies like as ^6.0.0 as follows:

      "peerDependencies": {
        "styled-components": "^6.0.0"
      }
    

    If styled-component is used in your custom library, just add it to devDependencies not dependencies.

    Based on npm blog and this post:

    peer dependency requirements, unlike those for regular dependencies, should be lenient. You should not lock your peer dependencies down to specific patch versions. It would be really annoying if one Chai plugin peer-depended on Chai 1.4.1, while another depended on Chai 1.5.0, simply because the authors were lazy and didn't spend the time figuring out the actual minimum version of Chai they are compatible with.

    If you want your package to use the same styled-component as the main app, find a lower version of styled-component that is compatible with your app and use a lenient requirement in peerDependencies so that styled-component does not get placed in your custom library folder unnecessarily