awilix icon indicating copy to clipboard operation
awilix copied to clipboard

Rename `awilix.module.js` to `awilix.module.mjs` (better Vitest support)

Open pago opened this issue 3 years ago • 5 comments

According to Vitest, it seems like the current export of the ESM build is not quite correct.

When using Awilix with Vitest, I get informed of the following:

Module /Users/.../node_modules/awilix/lib/awilix.module.js:1 seems to be an ES Module but shipped in a CommonJS package. You might want to create an issue to the package "awilix" asking them to ship the file in .mjs extension or add "type": "module" in their package.json.

As a temporary workaround you can try to inline the package by updating your config:

// vitest.config.js
export default {
  test: {
    deps: {
      inline: [
        "awilix"
      ]
    }
  }
}

The proposed workaround works fine, but it would be nice if it wasn't needed.

I also tested native ESM loading in Node.js, which worked fine with the current setup. So I'm not sure if the package really is wrong or if it's a Vitest/Vite issue.

I'm happy to contribute a PR to make the adjustment if you like.

pago avatar Jul 30 '22 09:07 pago

I have yet to actually use ESM myself so I don’t know much about it; would doing this be considered a breaking change?

jeffijoe avatar Jul 30 '22 13:07 jeffijoe

I don't think so, unless somebody explicitly imported awilix/lib/awilix.module.js, in that case their code would be broken. But I'm also not entirely sure.

pago avatar Aug 01 '22 08:08 pago

I know some people use Awilix with Webpack, but if renaming that file won't break anything, then I have no problem doing this.

jeffijoe avatar Aug 01 '22 10:08 jeffijoe

I'll send a PR and test it with Webpack beforehand. Will just take a few days before I get to it. 🙂

pago avatar Aug 01 '22 11:08 pago

To consume a package as an ES module, the package.json needs to be either have "type": "module"or a .mjs extension so I think that's the key issue here.

bestickley avatar Aug 04 '22 14:08 bestickley

Not sure if it's related but whenever I try to add awilix to deps.inline, I get the following error:

Error: Cannot find module './errors'
Require stack:
- /Users/jclaessens/proj/packages/api-next/awilix
    at Module._resolveFilename (node:internal/modules/cjs/loader:985:15)
    at Module._load (node:internal/modules/cjs/loader:833:27)
    at Module.require (node:internal/modules/cjs/loader:1051:19)
    at require (node:internal/modules/cjs/helpers:103:18)
    at awilix:13:14
    at ViteNodeRunner.directRequest (file:///Users/jclaessens/proj/node_modules/.pnpm/[email protected]/node_modules/vitest/dist/chunk-vite-node-client.4dd32c96.js:250:11)
    at async ViteNodeRunner.cachedRequest (file:///Users/jclaessens/proj/node_modules/.pnpm/[email protected]/node_modules/vitest/dist/chunk-vite-node-client.4dd32c96.js:127:12)
    at async request (file:///Users/jclaessens/proj/node_modules/.pnpm/[email protected]/node_modules/vitest/dist/chunk-vite-node-client.4dd32c96.js:150:16)
    at async /Users/jclaessens/proj/packages/api-next/src/diContainer.ts:3:31
    at async ViteNodeRunner.directRequest (file:///Users/jclaessens/proj/node_modules/.pnpm/[email protected]/node_modules/vitest/dist/chunk-vite-node-client.4dd32c96.js:250:5) {
  code: 'MODULE_NOT_FOUND',
  requireStack: [ '/Users/jclaessens/proj/packages/api-next/awilix' ]
}

If I don't add awilix to deps.inline, I get the following error for every .ts file I try to load when initializing the DI container.

TypeError: Unknown file extension ".ts" for /Users/jclaessens/proj/packages/api-next/src/modules/equipment/equipment.service.ts
 ELIFECYCLE  Test failed. See above for more details.

I load my modules like this:

import { diContainer } from '@fastify/awilix';
import { InjectionMode, Lifetime, asClass, asValue } from 'awilix';
import { getManagementClient } from './modules/auth0/managementClient';
import { prisma } from './db';

export async function registerDiContainer() {
  const container = await diContainer
    .loadModules(
      [
        ['src/modules/**/*.service.ts', { register: asClass }],
        ['src/modules/**/*.repository.ts', { register: asClass }],
      ],
      {
        formatName: 'camelCase',
        resolverOptions: {
          lifetime: Lifetime.SCOPED,
          injectionMode: InjectionMode.PROXY,
        },
        esModules: true,
      },
    );

  return container.register({
    auth0ManagementClient: asValue(getManagementClient()),
    prisma: asValue(prisma),
  });
}

tsconfig:

{
  "extends": "fastify-tsconfig",
  "compilerOptions": {
    "esModuleInterop": true,
    "lib": ["ESNext"],
    "outDir": "build",
    "paths": {
      "@/*": ["./src/*"],
      "~/*": ["./*"]
    },
    "strict": true,
    "target": "ESNext"
  }
}

All of this is only happening with vitest. No issues when building (esbuild) or running in dev mode (tsx).

I'm also using @fastify/awilix. Any idea?

jclaessens97 avatar Oct 14 '22 09:10 jclaessens97

My issue is solved by not using loadModules but by just importing all services and use register instead like so:

import { diContainer } from '@fastify/awilix';
import { Lifetime, asClass, asValue } from 'awilix';

import managementClient from './modules/auth0/managementClient';
import { prisma } from './db';
import Auth0Service from './modules/auth0/auth0.service';
import OrganisationService from './modules/organisation/organisation.service';
import OrganisationRepository from './modules/organisation/organisation.repository';
import EquipmentRepository from './modules/equipment/equipment.repository';
import EquipmentService from './modules/equipment/equipment.service';

export function registerDiContainer() {
  return diContainer.register({
    auth0ManagementClient: asValue(managementClient),
    prisma: asValue(prisma),

    auth0Service: asClass(Auth0Service, { lifetime: Lifetime.SINGLETON }),

    organisationRepository: asClass(OrganisationRepository, { lifetime: Lifetime.SCOPED }),
    organisationService: asClass(OrganisationService, { lifetime: Lifetime.SCOPED }),

    equipmentRepository: asClass(EquipmentRepository, { lifetime: Lifetime.SCOPED }),
    equipmentService: asClass(EquipmentService, { lifetime: Lifetime.SCOPED }),
  });
}

Is there some way to not have to import any service individually?

So it appears not to be related to the original issue at all, I'm sorry for that 😅

jclaessens97 avatar Oct 14 '22 13:10 jclaessens97

I just released Awilix v8 which renames awilix.module.js to awilix.module.mjs.

jeffijoe avatar Oct 14 '22 14:10 jeffijoe

@jclaessens97 your issue seems to be due to trying to load .ts files in tests and your runner isn't configured to transpile them.

jeffijoe avatar Oct 14 '22 14:10 jeffijoe