Skip to content

Commit 577a5ff

Browse files
committed
First modifications: ESM support for OData
1 parent 2ef38c6 commit 577a5ff

File tree

4 files changed

+159
-67
lines changed

4 files changed

+159
-67
lines changed

packages/generator-common/src/file-writer/package-json.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ export interface PackageJsonOptions {
1414
* @internal
1515
*/
1616
description: string;
17+
/**
18+
* @internal
19+
*/
20+
moduleType?: 'commonjs' | 'esm';
1721
}
1822

1923
/* eslint-disable valid-jsdoc */
@@ -23,7 +27,7 @@ export interface PackageJsonOptions {
2327
export function packageJsonBase(
2428
options: PackageJsonOptions
2529
): Record<string, any> {
26-
return {
30+
const basePackageJson: Record<string, any> = {
2731
name: options.npmPackageName,
2832
version: '1.0.0',
2933
description: options.description,
@@ -39,4 +43,17 @@ export function packageJsonBase(
3943
url: ''
4044
}
4145
};
46+
47+
if (options.moduleType === 'esm') {
48+
basePackageJson.type = 'module';
49+
basePackageJson.main = './index.mjs';
50+
basePackageJson.exports = {
51+
'.': {
52+
import: './index.mjs',
53+
require: './index.js'
54+
}
55+
};
56+
}
57+
58+
return basePackageJson;
4259
}

packages/generator/src/generator.ts

Lines changed: 110 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import { complexTypeSourceFile } from './complex-type';
3535
import { entitySourceFile } from './entity';
3636
import { enumTypeSourceFile } from './enum-type';
3737
import { sourceFile } from './file-generator';
38-
import { cliOptions } from './options';
38+
import { cliOptions, tsconfigJson } from './options';
3939
import { hasEntities } from './generator-utils';
4040
import {
4141
entityApiFile,
@@ -45,9 +45,9 @@ import {
4545
import { operationsSourceFile } from './operations';
4646
import { sdkMetadata } from './sdk-metadata';
4747
import { parseAllServices } from './service-generator';
48-
import { indexFile, packageJson, readme } from './service';
48+
import { indexFile, packageJson, PackageJsonOptions, readme } from './service';
4949
import type { GeneratorOptions, ParsedGeneratorOptions } from './options';
50-
import type { ProjectOptions } from 'ts-morph';
50+
import type { Directory, ProjectOptions } from 'ts-morph';
5151
import type {
5252
CreateFileOptions,
5353
OptionsPerService
@@ -181,10 +181,11 @@ export async function generateProject(
181181
emptyDirSync(options.outputDir.toString());
182182
}
183183

184-
const project = new Project(projectOptions());
184+
const tsConfig = await tsconfigJson(options);
185+
const project = new Project(projectOptions(options.generateESM ? 'esm' : 'commonjs'));
185186

186187
const promises = services.map(service =>
187-
generateSourcesForService(service, project, options)
188+
generateSourcesForService(service, project, options, tsConfig)
188189
);
189190

190191
if (options.optionsPerService) {
@@ -241,7 +242,8 @@ async function getFileCreationOptions(
241242
prettierOptions: await readPrettierConfig(
242243
options.prettierConfig?.toString()
243244
),
244-
overwrite: options.overwrite
245+
overwrite: options.overwrite,
246+
generateESM: options.generateESM
245247
};
246248
}
247249

@@ -298,7 +300,8 @@ async function generateEntityApis(
298300
export async function generateSourcesForService(
299301
service: VdmServiceMetadata,
300302
project: Project,
301-
options: ParsedGeneratorOptions
303+
options: ParsedGeneratorOptions,
304+
tsConfig: string | undefined
302305
): Promise<void> {
303306
const serviceDirPath = join(
304307
options.outputDir,
@@ -310,37 +313,49 @@ export async function generateSourcesForService(
310313
if (!existsSync(serviceDirPath)) {
311314
await mkdir(serviceDirPath, { recursive: true });
312315
}
313-
const filePromises: Promise<any>[] = [];
314-
logger.verbose(`[${service.originalFileName}] Generating entities ...`);
316+
317+
await generateMandatorySources(serviceDir, service, options, createFileOptions);
318+
319+
if (options.metadata) {
320+
await generateMetadata(service, options);
321+
}
315322

316323
if (options.packageJson) {
317-
filePromises.push(
318-
createFile(
319-
serviceDirPath,
320-
'package.json',
321-
await packageJson({
322-
npmPackageName: service.serviceOptions.packageName,
323-
sdkVersion: await getSdkVersion(),
324-
description: packageDescription(service.speakingModuleName),
325-
oDataVersion: service.oDataVersion
326-
}),
327-
createFileOptions
328-
)
329-
);
324+
await generatePackageJson(serviceDirPath, service, options);
330325
}
331326

332-
if (options.transpile || options.tsconfig) {
333-
filePromises.push(
334-
createFile(
335-
serviceDirPath,
336-
'tsconfig.json',
337-
options.tsconfig
338-
? await readCustomTsConfig(options.tsconfig)
339-
: formatTsConfig(),
340-
createFileOptions
341-
)
327+
if (options.include) {
328+
await copyFiles(options.include, serviceDirPath, options.overwrite);
329+
}
330+
331+
if (tsConfig) {
332+
await createFile(
333+
serviceDirPath,
334+
'tsconfig.json',
335+
tsConfig,
336+
createFileOptions
342337
);
338+
const transpileOptions = {
339+
compilerOptions: await readCompilerOptions(serviceDirPath),
340+
createFileOptions
341+
};
342+
await transpileDirectory(serviceDirPath, transpileOptions);
343+
}
344+
345+
if (options.readme) {
346+
await generateReadme(serviceDirPath, service, options);
343347
}
348+
}
349+
350+
async function generateMandatorySources(
351+
serviceDir: Directory,
352+
service: VdmServiceMetadata,
353+
options: ParsedGeneratorOptions,
354+
createFileOptions: CreateFileOptions
355+
): Promise<void> {
356+
const filePromises: Promise<any>[] = [];
357+
358+
logger.verbose(`[${service.originalFileName}] Generating entities ...`);
344359

345360
if (hasEntities(service)) {
346361
logger.verbose(
@@ -404,7 +419,6 @@ export async function generateSourcesForService(
404419
);
405420
});
406421

407-
// Merge generated function-imports.ts and action-imports.ts into one operations.ts.
408422
if (service.operations.length) {
409423
logger.verbose(`[${service.originalFileName}] Generating operations ...`);
410424
filePromises.push(
@@ -421,42 +435,73 @@ export async function generateSourcesForService(
421435
sourceFile(serviceDir, 'index', indexFile(service), createFileOptions)
422436
);
423437

424-
if (options.readme) {
425-
logger.verbose(`[${service.originalFileName}] Generating readme ...`);
426-
filePromises.push(
427-
createFile(
428-
serviceDirPath,
429-
'README.md',
430-
readme(service),
431-
createFileOptions
432-
)
433-
);
438+
await Promise.all(filePromises);
439+
}
440+
441+
async function generateMetadata(
442+
service: VdmServiceMetadata,
443+
options: ParsedGeneratorOptions
444+
): Promise<void> {
445+
const { clientFileName } = getSdkMetadataFileNames(
446+
service.originalFileName
447+
);
448+
logger.verbose(`Generating sdk client metadata ${clientFileName}...`);
449+
450+
const path = resolve(dirname(service.edmxPath.toString()), 'sdk-metadata');
451+
if (!existsSync(path)) {
452+
await mkdir(path);
434453
}
435454

436-
if (options.metadata) {
437-
const { clientFileName } = getSdkMetadataFileNames(
438-
service.originalFileName
439-
);
440-
logger.verbose(`Generating sdk client metadata ${clientFileName}...`);
455+
const createFileOptions = await getFileCreationOptions(options);
456+
await createFile(
457+
path,
458+
clientFileName,
459+
JSON.stringify(await sdkMetadata(service), null, 2),
460+
createFileOptions
461+
);
462+
}
441463

442-
const path = resolve(dirname(service.edmxPath.toString()), 'sdk-metadata');
443-
if (!existsSync(path)) {
444-
await mkdir(path);
445-
}
464+
async function generatePackageJson(
465+
serviceDirPath: string,
466+
service: VdmServiceMetadata,
467+
options: ParsedGeneratorOptions
468+
): Promise<void> {
469+
logger.verbose(`Generating package.json in ${serviceDirPath}.`);
470+
const createFileOptions = await getFileCreationOptions(options);
446471

447-
filePromises.push(
448-
createFile(
449-
path,
450-
clientFileName,
451-
JSON.stringify(await sdkMetadata(service), null, 2),
452-
createFileOptions
453-
)
454-
);
455-
}
456-
await Promise.all(filePromises);
472+
const packageJsonOptions: PackageJsonOptions = {
473+
npmPackageName: service.serviceOptions.packageName,
474+
sdkVersion: await getSdkVersion(),
475+
description: packageDescription(service.speakingModuleName),
476+
oDataVersion: service.oDataVersion,
477+
moduleType: options.generateESM ? 'esm' : 'commonjs'
478+
};
479+
480+
await createFile(
481+
serviceDirPath,
482+
'package.json',
483+
await packageJson(packageJsonOptions),
484+
createFileOptions
485+
);
486+
}
487+
488+
async function generateReadme(
489+
serviceDirPath: string,
490+
service: VdmServiceMetadata,
491+
options: ParsedGeneratorOptions
492+
): Promise<void> {
493+
logger.verbose(`Generating readme in ${serviceDirPath}.`);
494+
const createFileOptions = await getFileCreationOptions(options);
495+
496+
await createFile(
497+
serviceDirPath,
498+
'README.md',
499+
readme(service),
500+
createFileOptions
501+
);
457502
}
458503

459-
function projectOptions(): ProjectOptions {
504+
function projectOptions(moduleType: 'commonjs' | 'esm' = 'commonjs'): ProjectOptions {
460505
return {
461506
skipAddingFilesFromTsConfig: true,
462507
manipulationSettings: {
@@ -466,12 +511,12 @@ function projectOptions(): ProjectOptions {
466511
},
467512
compilerOptions: {
468513
target: ScriptTarget.ES2021,
469-
module: ModuleKind.CommonJS,
514+
module: moduleType === 'esm' ? ModuleKind.ESNext : ModuleKind.CommonJS,
470515
declaration: true,
471516
declarationMap: true,
472517
sourceMap: true,
473518
diagnostics: true,
474-
moduleResolution: ModuleResolutionKind.NodeJs,
519+
moduleResolution: moduleType === 'esm' ? ModuleResolutionKind.NodeNext : ModuleResolutionKind.NodeJs,
475520
esModuleInterop: true,
476521
inlineSources: false,
477522
noImplicitAny: true

packages/generator/src/options/options.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { getCommonCliOptions } from '@sap-cloud-sdk/generator-common/internal';
1+
import { getCommonCliOptions, formatTsConfig, readCustomTsConfig } from '@sap-cloud-sdk/generator-common/internal';
22
import type {
33
Options,
44
ParsedOptions,
@@ -17,6 +17,10 @@ export interface GeneratorOptions extends CommonGeneratorOptions {
1717
* Number of node processes used for transpilation of JavaScript files.
1818
*/
1919
transpilationProcesses?: number;
20+
/**
21+
* Whether to generate ECMAScript modules instead of CommonJS modules.
22+
*/
23+
generateESM?: boolean;
2024
}
2125

2226
/**
@@ -44,5 +48,27 @@ export const cliOptions = {
4448
hidden: true,
4549
replacedBy: 'processesJsGeneration'
4650
},
51+
generateESM: {
52+
describe:
53+
'When enabled, all generated files follow the ECMAScript module syntax.',
54+
type: 'boolean',
55+
default: false
56+
},
4757
...getCommonCliOptions('OData')
4858
} as const satisfies Options<GeneratorOptions>;
59+
60+
/**
61+
* Build a tsconfig.json file as string.
62+
* If the given options include a tsConfig setting, this config is read and returned.
63+
* @param options - Options passed to the generator.
64+
* @returns The serialized tsconfig.json contents.
65+
* @internal
66+
*/
67+
export async function tsconfigJson({
68+
transpile,
69+
tsconfig: tsConfig
70+
}: ParsedGeneratorOptions): Promise<string | undefined> {
71+
if (transpile || tsConfig) {
72+
return tsConfig ? readCustomTsConfig(tsConfig) : formatTsConfig();
73+
}
74+
}

packages/generator/src/service/package-json.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ export interface PackageJsonOptions extends PackageJsonOptionsBase {
1111
* @internal
1212
*/
1313
oDataVersion: ODataVersion;
14+
/**
15+
* @internal
16+
*/
17+
moduleType: 'commonjs' | 'esm';
1418
}
1519

1620
/**

0 commit comments

Comments
 (0)