Search code examples
node.jsreactjstypescripttsconfig

ERR_REQUIRE_ESM after upgrading node 16=>20 and other libraries


I am trying to upgrade node from version 16 => 20
I also decided to try and upgrade some other libraries altogether
I tried starting my backend after the upgrades

% yarn run dev
[nodemon] 3.0.1
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): src/**/*
[nodemon] watching extensions: ts
[nodemon] starting `ts-node --transpileOnly --files src/index.ts`

soon followed by

/Users/.../backend/node_modules/ts-node/dist/index.js:851
            return old(m, filename);
                   ^
Error [ERR_REQUIRE_ESM]: require() of ES Module /Users/.../backend/node_modules/file-type/index.js from /Users/.../backend/src/feature-core/upload-file.ts not supported.
Instead change the require of index.js in /Users/.../backend/src/feature-core/upload-file.ts to a dynamic import() which is available in all CommonJS modules.
    at require.extensions.<computed> [as .js] (/Users/.../backend/node_modules/ts-node/dist/index.js:851:20)

I am not sure what needs to be updated or how, I tried making some changes following some github issues but could't find a solution.

Update

This is where the error takes place:

import { protectedProcedure } from '../trpc/initialize'
import { randomString } from '../utils/rand'
import { uploadFile as uploadFileToS3 } from '../lib/s3'
import { TRPCError } from '@trpc/server'
import { fileTypeFromBuffer } from 'file-type'

export const uploadFileBase64 = protectedProcedure.input(z.object({ body: z.string() })).mutation(async ({ input }) => {
  const buffer = Buffer.from(input.body, 'base64')
  const fileType = await fileTypeFromBuffer(buffer)

  if (fileType === undefined) {
    throw new TRPCError({ code: 'BAD_REQUEST', message: 'ファイルの種類を判定できませんでした' })
  }

  const key = `${randomString()}.${fileType.ext}`
  await uploadFileToS3({
    key,
    body: buffer,
    contentType: fileType.mime,
  })

  return { key: '' }
})

I am not using require, neither here nor in any other file, so according to the answers I am guessing I need to add "type": "module" to my package.json.
However after adding that I got this error instead:

TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for /Users/.../backend/src/index.ts
    at new NodeError (node:internal/errors:405:5)
    at Object.getFileProtocolModuleFormat [as file:] (node:internal/modules/esm/get_format:99:9)
    at defaultGetFormat (node:internal/modules/esm/get_format:142:36)
    at defaultLoad (node:internal/modules/esm/load:91:20)
    at DefaultModuleLoader.load (node:internal/modules/esm/loader:263:26)
    at DefaultModuleLoader.moduleProvider (node:internal/modules/esm/loader:179:22)
    at new ModuleJob (node:internal/modules/esm/module_job:63:26)
    at DefaultModuleLoader.#createModuleJob (node:internal/modules/esm/loader:203:17)
    at DefaultModuleLoader.getJobFromResolveResult (node:internal/modules/esm/loader:156:34)
    at DefaultModuleLoader.getModuleJob (node:internal/modules/esm/loader:141:17) {
  code: 'ERR_UNKNOWN_FILE_EXTENSION'
}
[nodemon] app crashed - waiting for file changes before starting...

So I am guessing there is still something I am missing.


Solution

  • Quick Answer

    const fileTypes = await eval("import('file-type')")
    

    putting this inside an async function should do the trick

    Explanation

    As explained by @monim the library file-type became ESM-only package after an upgrade, which required me to add "type": "module" and change all my imports to ESM format.

    Did I want to change to ESM?
    No, only one library had become ESM-only, and inside a pretty large directory I would rather keep the changes I make minimal. So as suggested by @Evert I tried using await import inside an async function.
    This worked perfectly, and everything ran smoothly except tslint which gave me this error:

    error TS1323: Dynamic imports are only supported when the '--module' flag is set to 'es2020', 'es2022', 'esnext', 'commonjs', 'amd', 'system', 'umd', 'node16', or 'nodenext'.
    

    And after a quick search I found out that changing the code to eval('await import()) could fix the tslint error message.
    If you are not using tslint this should work fine as well:

    const fileTypes = await import ('file-type')