Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Only URLs with a scheme in: file, data, and node are supported by the default ESM loader #672

Open
mmakhalaf opened this issue May 15, 2024 · 0 comments

Comments

@mmakhalaf
Copy link

I'm getting this error after switching to my package to a node module and Typescript module resolution to NodeNext,

  • node 18.20.3
  • typescript 5.4.5
  • umzug 3.8.0
  • sequelize 6.37.3

My tsconfig module settings are as follows amongst other things, and uses project references but I don't think that's the issue,

    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",

I don't have the script that's running in the same directory as the migration directory, so I have to do something like this for glob to work.

migrations: {
    glob: [
      `${__dirname}/migrations/*.*ts`,
    ]
}

or

  migrations: {
    glob: [
      `migrations/*.*ts`,
      {
        cwd: __dirname,
      },
    ],
  },

The example says that I should define __dirname as const __dirname = new URL(".", import.meta.url).pathname.replace(/\/$/, ""). This puts a / at the start of the path, which breaks glob. I can remove it (i.e. const __dirname = new URL(".", import.meta.url).pathname.replace(/\/$/, "").replace(/^\//, "");) which allows glob to find the migration files, but then the subsequent dynamic import fails with this error,

Error: Migration 2023.08.16T14.29.36.init.ts (up) failed: Original error: Only URLs with a scheme in: file, data, and node are supported by the default ESM loader. On Windows, absolute paths must be valid file:// URLs. Received protocol 'd:'.

I can work around the issue by overriding the resolver and doing the dynamic import like this, I simplified it also for our case,

const defaultResolver: Resolver<unknown> = ({ name, path: filepath }) => {
  if (!filepath) {
    throw new Error(`Can't use default resolver for non-filesystem migrations`);
  }

  const loadModule: () => Promise<RunnableMigration<unknown>> = async () =>
    import(`file://${filepath}`) as Promise<RunnableMigration<unknown>>;

  const getModule = async () => {
    return await loadModule();
  };

  return {
    name,
    path: filepath,
    up: async ({ context }) =>
      (await getModule()).up({ path: filepath, name, context }),
    down: async ({ context }) =>
      (await getModule()).down?.({ path: filepath, name, context }),
  };
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant