chaijs / chai-http

HTTP Response assertions for the Chai Assertion Library.
http://chaijs.com/plugins/chai-http
633 stars 113 forks source link

Error: No "exports" main defined in node_modules/chai-http/package.json #345

Closed ScriptPup closed 2 weeks ago

ScriptPup commented 3 weeks ago

I'm unable to use chai-http in a new project. Every time I try to run my tests with mocha test/**/*.spec.ts, it fails to build with the following message:

 Exception during run: Error: No "exports" main defined in <project_root>/node_modules/chai-http/package.json
    at exportsNotFound (node:internal/modules/esm/resolve:294:10)
    at packageExportsResolve (node:internal/modules/esm/resolve:584:13)
    at resolveExports (node:internal/modules/cjs/loader:591:36)
    at Function.Module._findPath (node:internal/modules/cjs/loader:668:31)
    at Function.Module._resolveFilename (node:internal/modules/cjs/loader:1130:27)
    at Function.Module._resolveFilename.sharedData.moduleResolveFilenameHook.installedValue [as _resolveFilename] (<project_root>/node_modules/@cspotcode/source-map-support/source-map-support.js:811:30)
    at Function.Module._load (node:internal/modules/cjs/loader:985:27)
    at Module.require (node:internal/modules/cjs/loader:1235:19)
    at require (node:internal/modules/helpers:176:18)
    at Object.<anonymous> (<project_root>/test/rest_api/common-api.spec.ts:9:1)
    at Module._compile (node:internal/modules/cjs/loader:1376:14)
    at Module.m._compile (<project_root>/node_modules/ts-node/src/index.ts:1618:23)
    at Module._extensions..js (node:internal/modules/cjs/loader:1435:10)
    at Object.require.extensions.<computed> [as .ts] (<project_root>/node_modules/ts-node/src/index.ts:1621:12)
    at Module.load (node:internal/modules/cjs/loader:1207:32)
    at Function.Module._load (node:internal/modules/cjs/loader:1023:12)
    at Module.require (node:internal/modules/cjs/loader:1235:19)
    at require (node:internal/modules/helpers:176:18)
    at Object.exports.requireOrImport (<project_root>/node_modules/mocha/lib/nodejs/esm-utils.js:53:16)
    at async Object.exports.loadFilesAsync (<project_root>/node_modules/mocha/lib/nodejs/esm-utils.js:100:20)
    at async singleRun (<project_root>/node_modules/mocha/lib/cli/run-helpers.js:162:3)
    at async Object.exports.handler (<project_root>/node_modules/mocha/lib/cli/run.js:375:5) {
  code: 'ERR_PACKAGE_PATH_NOT_EXPORTED'
}

My package.json looks like this

{
  "name": "ProjectName",
  "version": "1.0.0",
  "description": "Description",
  "main": "./dist/server.js",
  "scripts": {
    "test": "mocha test/**/*.spec.ts --exit",
    "predev": "tsc",
    "pretest": "export MONGOMS_VERSION=6.0.9; ts-node test/start-mongdb-memsrv.ts",
    "dev": "concurrently \"nodemon dist/server.js\" \"tsc -w\"",
    "server-logs": "cat ./logs/Server.log | pino-pretty |  code -",
    "start": "nodemon dist/server.js"
  },
  "dependencies": {
    "bcrypt": "^5.1.1",
    "bcryptjs": "^2.4.3",
    "chai-http": "^5.1.0",
    "dotenv": "^16.4.5",
    "jsonwebtoken": "^9.0.2",
    "mongodb": "^6.10.0",
    "mongoose": "^8.7.3",
    "multer-gridfs-storage": "^5.0.2",
    "pino": "^9.5.0",
    "pino-pretty": "^11.3.0",
    "restify": "^11.1.0",
    "restify-errors": "^8.0.2",
    "restify-pino-logger": "^3.0.0"
  },
  "devDependencies": {
    "@types/chai": "^5.0.1",
    "@types/mocha": "^10.0.9",
    "@types/superagent": "^8.1.9",
    "chai": "^4.5.0",
    "concurrently": "^9.0.1",
    "cross-env": "^7.0.3",
    "esm": "^3.2.25",
    "mocha": "^10.8.2",
    "mongodb-memory-server": "^10.1.2",
    "npm-run-all": "^4.1.5",
    "run-script-os": "^1.1.6",
    "superagent": "^10.1.1",
    "ts-auto-mock": "^3.7.4",
    "ts-node": "^10.9.1",
    "typescript": "^5.6.3"
  }
}

And my tsconfig.json looks like this:

{
  "compilerOptions": {
    /* Visit https://aka.ms/tsconfig to read more about this file */
    "target": "es2016",                                  /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
    "module": "commonjs",                                /* Specify what module code is generated. */
    "outDir": "./dist/",                                 /* Specify an output folder for all emitted files. */
    "esModuleInterop": true,                             /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
    "forceConsistentCasingInFileNames": true,            /* Ensure that casing is correct in imports. */
    "strict": true,                                      /* Enable all strict type-checking options. */   
    "skipLibCheck": true                                 /* Skip type checking all .d.ts files. */
  },
  "include": ["src/**/*.ts", "*.ts"]
}

I've googled around and there are plenty of people with this error, but the solution always seems to be "add main: <myfile.js> to your package.json!" index.js is already defined in the 'chai-http' module, my own project already has a main defined... I have no idea why it's failing.

If I remove chai-http, my tests that don't really on the request component work fine.

Any help or ideas are greatly appreciated, been slamming my head into my desk for a few hours now.

ScriptPup commented 3 weeks ago

I had to downgrade a bunch of stuff to get this working. My new package.json looks like this.

{
  "name": "ProjectName",
  "version": "1.0.0",
  "description": "Description",
  "main": "./dist/server.js",
  "scripts": {
    "test": "mocha test/**/*.spec.ts --exit",
    "predev": "tsc",
    "pretest": "export MONGOMS_VERSION=6.0.9; ts-node test/start-mongdb-memsrv.ts",
    "dev": "concurrently \"nodemon dist/server.js\" \"tsc -w\"",
    "server-logs": "cat ./logs/Server.log | pino-pretty |  code -",
    "start": "nodemon dist/server.js"
  },
    "dependencies": {
    "bcrypt": "^5.1.1",
    "bcryptjs": "^2.4.3",
    "dotenv": "^16.4.5",
    "jsonwebtoken": "^9.0.2",
    "mongodb": "^6.10.0",
    "mongoose": "^8.8.0",
    "multer-gridfs-storage": "^5.0.2",
    "pino": "^9.5.0",
    "pino-pretty": "^11.3.0",
    "restify": "^11.1.0",
    "restify-errors": "^8.0.2",
    "restify-pino-logger": "^3.0.0"
  },
  "devDependencies": {
    "@types/chai": "^4.3.20",
    "@types/chai-http": "^4.2.4",
    "@types/jsonwebtoken": "^9.0.7",
    "@types/mocha": "^10.0.9",
    "@types/node": "^20.17.6",
    "@types/restify": "^8.5.12",
    "@types/restify-errors": "^4.3.9",
    "chai": "^4.5.0",
    "chai-http": "^4.4.0",
    "chai-json-schema": "^1.5.1",
    "chai-like": "^1.1.3",
    "chai-things": "^0.2.0",
    "concurrently": "^9.0.1",
    "cross-env": "^7.0.3",
    "esm": "^3.2.25",
    "mocha": "^10.8.2",
    "mongodb-memory-server": "^10.1.2",
    "nodemon": "^3.1.7",
    "npm-run-all": "^4.1.5",
    "run-script-os": "^1.1.6",
    "sinon": "^19.0.2",
    "ts-auto-mock": "^3.7.4",
    "ts-node": "^10.9.2",
    "typescript": "^5.6.3"
  }
}

I had to fully remove all packages (rm -f package-lock.json; rm -rf node_modules; npm cache clean --force) and re-install with this configuration.

I guess there was some kind of breaking change between the last time I've used chai-http and now? I really don't know. It'd be great to be able to use the latest version with whatever enhancements have been made... But seems like the latest versions of things are broken. Don't know why, don't know where the problem lies.

I tried to mess with the packages locally to get the export error to go away, but nothing I did fixed it. So, I have no clue.

43081j commented 3 weeks ago

chai-http 5.x and chai 5.x are both esm only

So if you're using require(...), you will need to stick with 4.x or use latest node which can require esm (23 I think?)

ScriptPup commented 3 weeks ago

I do not use require(...) syntax in any of my projects. Here's the test file I was trying to run in full:

process.env.LOG_LEVEL_CONSOLE = 'silent';
process.env.LOG_LEVEL = 'trace';
process.env.PORT = '8081';
process.env.PROTOCOL = "http";

import chai, { expect } from "chai";
import chaiHttp from "chai-http";
import { describe } from "mocha";
import { MongoMemoryServer } from "mongodb-memory-server";
import { setup, tearDown, server } from "../test.common";

chai.use(chaiHttp);

describe("Basic API Testing", function () {
    const srv: { mongodb: null | MongoMemoryServer } = { mongodb: null };
    this.beforeAll(async function () {
        this.timeout(4000);
        [srv.mongodb] = await setup();
        // console.log("Setup complete");
        return;
    });
    this.afterAll(async()=>{
        await tearDown(srv);
    });
    // this.afterAll(async ()=> { await tearDown(srv); return; });
    describe("Incorrect requests properly indicate their failure", () => {
        it("GET - Will return 404 for non-existent resources", async () => {
            const res = await chai.request(server).get("/_api/users_").send();
            expect(res).to.have.status(404);
            expect(res).to.be.json;   
            expect(res.body).to.include.keys("code", "message");
            expect(res.body["code"]).to.eq("ResourceNotFound");
            expect(res.body["message"]).to.eq("/_api/users_ does not exist");
        });
        it("POST - Will return 404 for non-existent resources", async () => {
            const res = await chai.request(server).post("/_api/users_").send({});
            expect(res).to.have.status(404);
            expect(res).to.be.json;
            expect(res.body).to.include.keys("code", "message");
            expect(res.body["code"]).to.eq("ResourceNotFound");
            expect(res.body["message"]).to.eq("/_api/users_ does not exist");
        });
        it("DELETE - Will return 404 for non-existent resources", async () => {
            const res = await chai.request(server).delete("/_api/users_").send();
            expect(res).to.have.status(404);
            expect(res).to.be.json;
            expect(res.body).to.include.keys("code", "message");
            expect(res.body["code"]).to.eq("ResourceNotFound");
            expect(res.body["message"]).to.eq("/_api/users_ does not exist");
        });
    });
});

Note: This is after getting it working again. Previously I had made the changes to the syntax to use directly imported { request } from chai-http rather than going through the chai.use(...) method.

43081j commented 3 weeks ago

The stack suggests it is being required. Does your tsconfig specify module: "commonjs"? Does your package.json have type: "module"?

Your source might be written as esm but the stack trace and error suggest your build output is commonjs

ScriptPup commented 2 weeks ago

Sorry, since I downgraded to a working (for me) version I haven't been hopping on to check this feed very often. The module is commonjs, I guess that might be the issue then. I thought I tried to use es-next as well and still had the problem, but can't remember for sure... Maybe eventually I'll try upgrading again to mess with it. It's probably fair to say whatever is up is a me problem vs. a package problem though. :shrug:

I don't have type: "module" in my package.json.