Skip to content

Commit

Permalink
feat(entity-generator): add import extension for referenced entities (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
JSin committed Aug 31, 2022
1 parent 50347ef commit f80809a
Show file tree
Hide file tree
Showing 8 changed files with 530 additions and 5 deletions.
1 change: 1 addition & 0 deletions docs/docs/entity-generator.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ By default, the `EntityGenerator` generates only owning sides of relations (e.g.
- `bidirectionalRelations` to generate also the inverse sides for them
- `identifiedReferences` to generate M:1 and 1:1 relations as wrapped references
- `entitySchema` to generate the entities using `EntitySchema` instead of decorators
- `esmImport` to use esm style import for imported entities e.x. when `esmImport=true`, generated entities include `import Author from './Author.js'`

## Current limitations

Expand Down
1 change: 1 addition & 0 deletions packages/core/src/utils/Configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,7 @@ export interface MikroORMOptions<D extends IDatabaseDriver = IDatabaseDriver> ex
bidirectionalRelations?: boolean;
identifiedReferences?: boolean;
entitySchema?: boolean;
esmImport?: boolean;
};
cache: {
enabled?: boolean;
Expand Down
10 changes: 9 additions & 1 deletion packages/core/src/utils/ConfigurationLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export class ConfigurationLoader {
this.registerDotenv(options);
const paths = await this.getConfigPaths();
const env = this.loadEnvironmentVars();
const isESM = (await this.getModuleFormatFromPackage()) === 'module';

for (let path of paths) {
path = Utils.absolutePath(path);
Expand All @@ -35,7 +36,9 @@ export class ConfigurationLoader {
tmp = await tmp;
}

return new Configuration({ ...tmp, ...options, ...env }, validate);
const esmConfigOptions = isESM ? { entityGenerator: { esmImport: true } } : {};

return new Configuration({ ...esmConfigOptions, ...tmp, ...options, ...env }, validate);
}
}

Expand Down Expand Up @@ -100,6 +103,11 @@ export class ConfigurationLoader {
return paths.filter(p => p.endsWith('.js') || tsNode);
}

static async getModuleFormatFromPackage(): Promise<string> {
const config = await ConfigurationLoader.getPackageConfig();
return config?.type ?? '';
}

static async registerTsNode(configPath = 'tsconfig.json'): Promise<boolean> {
const tsConfigPath = isAbsolute(configPath) ? configPath : join(process.cwd(), configPath);

Expand Down
6 changes: 4 additions & 2 deletions packages/entity-generator/src/EntityGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,14 @@ export class EntityGenerator {
this.generateIdentifiedReferences(metadata);
}

const esmImport = this.config.get('entityGenerator').esmImport ?? false;

for (const meta of metadata) {
if (!meta.pivotTable) {
if (this.config.get('entityGenerator').entitySchema) {
this.sources.push(new EntitySchemaSourceFile(meta, this.namingStrategy, this.platform));
this.sources.push(new EntitySchemaSourceFile(meta, this.namingStrategy, this.platform, esmImport));
} else {
this.sources.push(new SourceFile(meta, this.namingStrategy, this.platform));
this.sources.push(new SourceFile(meta, this.namingStrategy, this.platform, esmImport));
}
}
}
Expand Down
6 changes: 4 additions & 2 deletions packages/entity-generator/src/SourceFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ export class SourceFile {

constructor(protected readonly meta: EntityMetadata,
protected readonly namingStrategy: NamingStrategy,
protected readonly platform: Platform) { }
protected readonly platform: Platform,
protected readonly esmImport: boolean) { }

generate(): string {
this.coreImports.add('Entity');
Expand Down Expand Up @@ -48,9 +49,10 @@ export class SourceFile {
ret += '}\n';

const imports = [`import { ${([...this.coreImports].sort().join(', '))} } from '@mikro-orm/core';`];
const entityImportExtension = this.esmImport ? '.js' : '';
const entityImports = [...this.entityImports].filter(e => e !== this.meta.className);
entityImports.sort().forEach(entity => {
imports.push(`import { ${entity} } from './${entity}';`);
imports.push(`import { ${entity} } from './${entity}${entityImportExtension}';`);
});

ret = `${imports.join('\n')}\n\n${ret}`;
Expand Down
17 changes: 17 additions & 0 deletions tests/features/cli/CLIHelper.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,23 @@ Maybe you want to check, or regenerate your yarn.lock or package-lock.json file?
pathExistsMock.mockRestore();
});

test('getModuleFormatFromPackage gets the type from package.json', async () => {
const mikroPackage = await ConfigurationLoader.getModuleFormatFromPackage();
expect(mikroPackage).toEqual('');

const packageSpy = jest.spyOn(ConfigurationLoader, 'getPackageConfig');
packageSpy.mockResolvedValue({ type: 'module' });
const esmModulePackage = await ConfigurationLoader.getModuleFormatFromPackage();
expect(esmModulePackage).toEqual('module');
const pathExistsMock = jest.spyOn(require('fs-extra'), 'pathExists');
pathExistsMock.mockResolvedValue(true);
const conf = await CLIHelper.getConfiguration();
expect(conf).toBeInstanceOf(Configuration);
expect(conf.get('entityGenerator')?.esmImport).toEqual(true);
pathExistsMock.mockRestore();
packageSpy.mockRestore();
});

test('dumpTable', async () => {
const dumpSpy = jest.spyOn(CLIHelper, 'dump');
dumpSpy.mockImplementation(() => void 0);
Expand Down
16 changes: 16 additions & 0 deletions tests/features/entity-generator/EntityGenerator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,22 @@ describe('EntityGenerator', () => {
await remove('./temp/entities');
});

test('generate entities with reference wrappers and named import [mysql]', async () => {
const orm = await initORMMySql('mysql', {
entityGenerator: {
identifiedReferences: true,
esmImport: true,
},
}, true);
const generator = orm.getEntityGenerator();
const dump = await generator.generate({ save: true, baseDir: './temp/entities' });
expect(dump).toMatchSnapshot('mysql-entity-named-dump');
await expect(pathExists('./temp/entities/Author2.ts')).resolves.toBe(true);
await remove('./temp/entities');

await orm.close(true);
});

test('generate entities from schema [sqlite]', async () => {
const orm = await initORMSqlite();
const dump = await orm.entityGenerator.generate({ save: true });
Expand Down
Loading

0 comments on commit f80809a

Please sign in to comment.