1

I'm rather new to the world of NPM package publishing, but I have a small reusable React component that I published a while back, and recently I updated that package to a new major version by implementing Typescript support. It seems to work pretty well; however, a user of the package has reported that when trying to run their Jest tests after importing the component into a fresh create-react-app application, Jest claims that it can't find the component's existence.

Example: User installs my-package from NPM into their CRA app, and changes their App.js thus:

import logo from "./logo.svg";
import "./App.css";
import MyComponent from "my-component";

function App() {
    return (
        <div className="App">
            Hello World
            <MyComponent someProp="somePropValue" />
        </div>
    );
}

export default App;

They also create a test thus:

import { render, screen } from "@testing-library/react";
import App from "./App";

test("renders App component", () => {
    render(<App />);
    const element = screen.getByText(/Hello World/i);
    expect(element).toBeInTheDocument();
});

Running this test (with npm run test) blows up with the following error:

 ● Test suite failed to run

    Cannot find module 'my-component' from 'src/App.js'

    Require stack:
      src/App.js
      src/App.test.js

Googling this error message leads to a lot of people talking about setting up ts-config.json or ts-jest in the consuming project, but I don't recall ever having to do this when installing other people's packages in my own projects. So I feel like this is possibly a problem with my package's configuration instead. I would appreciate any insight on what I might have done wrong in setting it up.

Here is the package.json file for my package:

{
    "name": "react-multivalue-text-input",
    "version": "2.2.0",
    "description": " A text input component for React which maintains and displays a collection of entered values as an array of strings.",
    "module": "dist/index.js",
    "types": "dist/index.d.ts",
    "scripts": {
        "prepare": "husky install",
        "dev": "node src/dev/serve.js",
        "clean": "rimraf dist",
        "build": "npm run clean && node src/dev/build.js && tsc",
        "test": "jest",
        "lint": "eslint src/",
        "validate": "run-s test lint",
        "docs:serve": "npx styleguidist server",
        "docs:build": "npx styleguidist build",
        "prepublishOnly": "npm run build"
    },
    "author": "",
    "license": "ISC",
    "devDependencies": {
        "@testing-library/dom": "^8.13.0",
        "@testing-library/jest-dom": "^5.16.4",
        "@testing-library/react": "^13.2.0",
        "@testing-library/user-event": "^14.1.1",
        "@types/jest": "^27.5.0",
        "@types/testing-library__react": "^10.2.0",
        "@typescript-eslint/eslint-plugin": "^5.21.0",
        "@typescript-eslint/parser": "^5.21.0",
        "chokidar": "^3.5.3",
        "commitizen": "^4.2.4",
        "css-loader": "^6.7.1",
        "css-tree": "^2.3.1",
        "cz-conventional-changelog": "^3.3.0",
        "esbuild": "^0.14.38",
        "esbuild-css-modules-plugin": "^2.6.3",
        "eslint": "^8.14.0",
        "eslint-config-airbnb": "^19.0.4",
        "eslint-config-prettier": "^8.5.0",
        "husky": "^8.0.1",
        "jest": "^28.0.3",
        "jest-environment-jsdom": "^28.0.2",
        "live-server": "^1.2.2",
        "npm-run-all": "^4.1.5",
        "react-docgen-typescript": "^2.2.2",
        "react-styleguidist": "^11.2.0",
        "rimraf": "^3.0.2",
        "style-loader": "^3.3.1",
        "ts-jest": "^28.0.1",
        "ts-loader": "^9.3.0",
        "typescript": "^4.6.4",
        "typescript-plugin-css-modules": "^4.1.1",
        "webpack": "^5.72.0"
    },
    "dependencies": {
        "@types/react": "^18.0.8",
        "@types/react-dom": "^18.0.3",
        "prop-types": "^15.8.1",
        "react": "^18.1.0",
        "react-dom": "^18.1.0"
    },
    "peerDependencies": {
        "react": "^18.1.0",
        "react-dom": "^18.1.0"
    },
    "files": [
        "dist"
    ],
    "config": {
        "commitizen": {
            "path": "./node_modules/cz-conventional-changelog"
        }
    }
}

And here is my build script:

const { build } = require('esbuild');
const cssModulesPlugin = require('esbuild-css-modules-plugin');
const { dependencies } = require('../../package.json');

const entryFile = 'src/index.tsx';
const shared = {
    bundle: true,
    entryPoints: [entryFile],
    // Treat all dependencies in package.json as externals to keep bundle size to a minimum
    external: Object.keys(dependencies),
    logLevel: 'info',
    minify: true,
    sourcemap: true,
    plugins: [cssModulesPlugin()]
};

build({
    ...shared,
    format: 'esm',
    outfile: './dist/index.js',
    target: ['esnext', 'node12.22.0']
});

ANd here is my tsconfig.json:

{
    "compilerOptions": {
        "target": "es5",
        "module": "commonjs",
        "declaration": true,
        "outDir": "./dist",
        "strict": true,
        "jsx": "react",
        "esModuleInterop": true,
        "lib": ["dom", "dom.iterable", "esnext"],
        "moduleResolution": "node",
        "skipLibCheck": true
    },
    "include": ["src"],
    "exclude": [
        "node_modules",
        "**/*.test.ts",
        "**/*.test.tsx",
        "**/*.spec.tsx",
        "src/dev/dev.tsx",
        "src/build/build.tsx"
    ]
}

Is there any obvious reason why this sort of configuration would lead to this un-smooth user experience on the package consumer's side? I would appreciate any guidance to point me in the right direction. (Alternatively, is this somehow a normal experience when consuming Typescript packages, and not a problem on my side?)

rosalindwills
  • 1,382
  • 13
  • 23

1 Answers1

1

Your package.json file misses the "main" field from v2.0.0, but was correctly present before.

If main is not set it defaults to index.js in the package's root folder.

You now have a "module" field instead, that links to your package entry point, but not all tools support it. See What is the "module" package.json field for?

ghybs
  • 47,565
  • 6
  • 74
  • 99