Skip to content

Commit

Permalink
fix: make importModels support non-default CJS exports (#16844)
Browse files Browse the repository at this point in the history
Co-authored-by: Zoé <zoe@ephys.dev>
  • Loading branch information
ckvv and ephys committed Feb 2, 2024
1 parent bd7fb13 commit bf53c9f
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 12 deletions.
9 changes: 8 additions & 1 deletion packages/core/src/import-models.ts
Expand Up @@ -2,6 +2,7 @@ import { pathToFileURL } from 'node:url';
import glob from 'fast-glob';
import uniq from 'lodash/uniq';
import type { ModelStatic } from './model.js';
import { isPlainObject } from './utils/check.js';
import { isModelStatic } from './utils/model-utils.js';

type ModelMatch = (path: string, exportName: string, exportValue: ModelStatic) => boolean;
Expand Down Expand Up @@ -34,7 +35,13 @@ export async function importModels(globPaths: string | string[], modelMatch?: Mo
}

async function importModelNoGlob(url: string, modelMatch?: ModelMatch): Promise<ModelStatic[]> {
const module = await import(url);
let module = await import(url);
// When importing a CJS file, sometimes only the default export is available,
// as named exports depend on the file's exports being statically analyzable by node.
// The default export contains the contents of the file's `module.exports`
if (module.default && isPlainObject(module.default)) {
module = { ...module.default, ...module };
}

return Object.keys(module)
.filter(exportName => {
Expand Down
27 changes: 16 additions & 11 deletions packages/core/test/unit/import-models/import-models.test.ts
Expand Up @@ -3,6 +3,8 @@ import glob from 'fast-glob';
import type { ModelStatic } from '@sequelize/core';
import { importModels } from '@sequelize/core';
// @ts-expect-error -- commonjs file
import { Bundler } from './models/bundler';
// @ts-expect-error -- commonjs file
import Node from './models/node.abstract';
// @ts-expect-error -- commonjs file
import User from './models/user';
Expand All @@ -13,17 +15,19 @@ describe('importModels', () => {
it('can import models using a single glob path', async () => {
const models = await importModels(`${dirname}/models/*.{ts,js}`);

expect(models).to.have.length(2);
expect(models[0]).to.eq(Node);
expect(models[1]).to.eq(User);
expect(models).to.have.length(3);
expect(models[0]).to.eq(Bundler);
expect(models[1]).to.eq(Node);
expect(models[2]).to.eq(User);
});

it('can import models using multiple glob paths', async () => {
const models = await importModels([`${dirname}/models/node.abstract.js`, `${dirname}/models/user.js`]);
const models = await importModels([`${dirname}/models/bundler.js`, `${dirname}/models/node.abstract.js`, `${dirname}/models/user.js`]);

expect(models).to.have.length(2);
expect(models[0]).to.eq(Node);
expect(models[1]).to.eq(User);
expect(models).to.have.length(3);
expect(models[0]).to.eq(Bundler);
expect(models[1]).to.eq(Node);
expect(models[2]).to.eq(User);
});

it('can exclude results using the second parameter', async () => {
Expand All @@ -37,10 +41,11 @@ describe('importModels', () => {

expect(models.length).to.eq(0);

expect(calls[0].path.endsWith('test/unit/import-models/models/node.abstract.js'));
expect(calls[0].exportName).to.eq('default');
expect(calls[0].exportValue).to.eq(Node);
expect(calls[0].exportValue).to.eq(Bundler);
expect(calls[1].path.endsWith('test/unit/import-models/models/node.abstract.js'));
expect(calls[1].exportName).to.eq('default');
expect(calls[1].exportValue).to.eq(Node);

expect(calls[1].exportValue).to.eq(User);
expect(calls[2].exportValue).to.eq(User);
});
});
42 changes: 42 additions & 0 deletions packages/core/test/unit/import-models/models/bundler.js
@@ -0,0 +1,42 @@
/**
*
* Simulate the behavior of bundler (esbuild, rollup ...) converting `ECMAScript` to `CommonJS`.
* Source code
*
import { Model } from "@sequelize/core";
export class Bundler extends Model {};
*/
const __defProp = Object.defineProperty;
const __getOwnPropDesc = Object.getOwnPropertyDescriptor;
const __getOwnPropNames = Object.getOwnPropertyNames;
const __hasOwnProp = Object.prototype.hasOwnProperty;
const __export = (target, all) => {
for (const name in all) {
__defProp(target, name, { get: all[name], enumerable: true });
}
};

const __copyProps = (to, from, except, desc) => {
if (from && typeof from === 'object' || typeof from === 'function') {
for (const key of __getOwnPropNames(from)) {
if (!__hasOwnProp.call(to, key) && key !== except) {
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
}
}

return to;
};

const __toCommonJS = mod => __copyProps(__defProp({}, '__esModule', { value: true }), mod);

// src/bundler.ts
const bundler_exports = {};
__export(bundler_exports, {
Bundler: () => Bundler,
});
module.exports = __toCommonJS(bundler_exports);
const import_core = require('@sequelize/core');

const Bundler = class extends import_core.Model {};

0 comments on commit bf53c9f

Please sign in to comment.