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?)