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

fix: default specs filename on sdk generation #1807

Merged
merged 1 commit into from
Jun 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 15 additions & 8 deletions packages/@ama-sdk/core/cli/update-spec-from-npm.cts
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this CLI should be moved to the @ama-sdk/schematics as part of the sdk generator and to include generator specific constants to a package used as prod dep.
(same remark for the other CLIs)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with this. Here is the issue opened for next major #1843.

Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,22 @@
import * as minimist from 'minimist';
import { existsSync } from 'node:fs';
import { createRequire } from 'node:module';
import { extname, join } from 'node:path';
import { extname, posix } from 'node:path';
import { copyFile, readFile } from 'node:fs/promises';
import type { PackageJson } from 'type-fest';
import type { OpenApiToolsConfiguration, OpenApiToolsGenerator } from '@ama-sdk/core';
import { LOCAL_SPEC_FILENAME, SPEC_JSON_EXTENSION, SPEC_YAML_EXTENSION } from '@ama-sdk/core';

const argv = minimist(process.argv.slice(2));
const packageName = argv._[0];
const { help, output, 'package-path': packagePath, quiet } = argv;
const openApiConfigDefaultPath = './openapitools.json';
const supportedExtensions = ['json', 'yaml', 'yml'];
const noop = () => undefined;
const logger = quiet ? {error: noop, warn: noop, log: noop, info: noop, debug: noop} : console;
const SPEC_YML_EXTENSION = 'yml';
const DEFAULT_SPEC_EXPORT_PATH_IN_NPM_MODULE = 'openapi';

const supportedExtensions = [SPEC_JSON_EXTENSION, SPEC_YAML_EXTENSION, SPEC_YML_EXTENSION];

if (help) {
// eslint-disable-next-line no-console
Expand All @@ -27,7 +31,7 @@ if (help) {

package-name The full identifier of the npm package (e.g. @my-scope/my-package)
--package-path The relative path inside the npm package where to find the spec file (default: './openapi.yml')
--output The path where the spec file should be copied (default: './openapi.yml')
--output The path where the spec file should be copied (default: './${LOCAL_SPEC_FILENAME}.${SPEC_YAML_EXTENSION}')
--quiet Don't log anything
`);
process.exit(0);
Expand All @@ -40,14 +44,14 @@ if (!packageName) {

void (async () => {
let specSourcePath;
const appRequire = createRequire(join(process.cwd(), 'package.json'));
const appRequire = createRequire(posix.join(process.cwd(), 'package.json'));
const packageJsonPath = appRequire.resolve(`${packageName}/package.json`);
if (!packagePath) {
const packageJson = JSON.parse(await readFile(packageJsonPath, {encoding: 'utf8'})) as PackageJson;
const exportMatcher = new RegExp(`openapi\\.(?:${supportedExtensions.join('|')})$`);
const exportMatcher = new RegExp(`^\\./${DEFAULT_SPEC_EXPORT_PATH_IN_NPM_MODULE}\\.(?:${supportedExtensions.join('|')})$`);
const matchingExport = packageJson.exports && Object.keys(packageJson.exports).find((exportPath) => exportMatcher.test(exportPath));
if (matchingExport) {
specSourcePath = appRequire.resolve(`${packageName}/${matchingExport}`);
specSourcePath = appRequire.resolve(posix.join(packageName, matchingExport));
}
} else {
specSourcePath = packageJsonPath.replace(/package.json$/, packagePath);
Expand All @@ -59,8 +63,11 @@ void (async () => {

let specDestinationPath = output;
if (!specDestinationPath) {
const specSourceExtension = extname(specSourcePath);
specDestinationPath = `./openapi${specSourceExtension}`;
let specSourceExtension = extname(specSourcePath);
if (specSourceExtension === `.${SPEC_YML_EXTENSION}`) {
specSourceExtension = `.${SPEC_YAML_EXTENSION}`;
}
specDestinationPath = `./${LOCAL_SPEC_FILENAME}${specSourceExtension}`;
if (existsSync(openApiConfigDefaultPath)) {
const openApiConfig = JSON.parse(await readFile(openApiConfigDefaultPath, {encoding: 'utf8'})) as OpenApiToolsConfiguration;
const generators: OpenApiToolsGenerator[] = Object.values(openApiConfig['generator-cli']?.generators ?? {});
Expand Down
10 changes: 10 additions & 0 deletions packages/@ama-sdk/core/src/utils/generators.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// TODO: Change to `openapi` in v11 (ref: #1745)
/** Name of the specification file copied locally (without extension) */
export const LOCAL_SPEC_FILENAME = 'swagger-spec';

/** Extension of the Specification file in YAML format */
export const SPEC_YAML_EXTENSION = 'yaml';

// TODO: Change to `json` in v11 (ref: #1745)
/** Extension of the Specification file in JSON format */
export const SPEC_JSON_EXTENSION = 'yaml';
1 change: 1 addition & 0 deletions packages/@ama-sdk/core/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from './ie11';
export * from './json-token';
export * from './mime-types';
export * from './generic-api';
export * from './generators';
2 changes: 1 addition & 1 deletion packages/@ama-sdk/create/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ npm create @ama-sdk typescript <project-name> -- --package-manager=yarn [...opti

- `--spec-path`: Path to the swagger/open-api specification used to generate the SDK
- `--spec-package-name`: The npm package name where the spec file can be fetched
- `--spec-package-path`: The path inside the package where to find the spec file
- `--spec-package-path`: The export path inside the package.json where to find the spec file. Defaults to _./openapi.[yml|yaml|json]_
- `--spec-package-version`: The version to target for the npm package where the spec file can be fetched
- `--spec-package-registry`: The npm registry where the spec file can be fetched

Expand Down
14 changes: 12 additions & 2 deletions packages/@ama-sdk/create/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
/* eslint-disable no-console */

import { execSync, spawnSync } from 'node:child_process';
import { dirname, join, parse, relative, resolve } from 'node:path';
import { dirname, extname, join, parse, relative, resolve } from 'node:path';
import * as minimist from 'minimist';
import { LOCAL_SPEC_FILENAME, SPEC_JSON_EXTENSION, SPEC_YAML_EXTENSION } from '@ama-sdk/core';

const packageManagerEnv = process.env.npm_config_user_agent?.split('/')[0];
const binPath = resolve(require.resolve('@angular-devkit/schematics-cli/package.json'), '../bin/schematics.js');
Expand Down Expand Up @@ -65,6 +66,14 @@ if (argv['spec-path'] && argv['spec-package-name']) {
process.exit(-4);
}

/* The local file (path) which will be created in case of generation using specs from an npm module */
let localFilePathToBeCreated: string | undefined;

if (argv['spec-package-name']) {
const localSpecFileComputedExtension = argv['spec-package-path'] && extname(argv['spec-package-path']) === '.json' ? `.${SPEC_JSON_EXTENSION}` : `.${SPEC_YAML_EXTENSION}`;
localFilePathToBeCreated = `./${LOCAL_SPEC_FILENAME}${localSpecFileComputedExtension}`;
}

const commonSchematicArgs = [
argv.debug !== undefined ? `--debug=${argv.debug as string}` : '--debug=false', // schematics enable debug mode per default when using schematics with relative path
...(name ? ['--name', name] : []),
Expand All @@ -88,7 +97,8 @@ const run = () => {
];
const coreSchematicArgs = [
...commonSchematicArgs,
'--spec-path', argv['spec-package-name'] ? './openapi.yml' : isSpecRelativePath ? relative(resolveTargetDirectory, resolve(process.cwd(), argv['spec-path'])) : argv['spec-path']
'--spec-path',
localFilePathToBeCreated || (isSpecRelativePath ? relative(resolveTargetDirectory, resolve(process.cwd(), argv['spec-path'])) : argv['spec-path'])
];

const runner = process.platform === 'win32' ? `${packageManager}.cmd` : packageManager;
Expand Down
1 change: 1 addition & 0 deletions packages/@ama-sdk/schematics/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ The generated package comes with the following script in the package.json:
> [!TIP]
> The `--spec-path` parameter supports YAML and JSON file formats based on the file system path or remote URL.
> The `--spec-package-name` parameter can be used as an alternative to `--spec-path` if you want to generate an SDK with specs from an npm package. It comes with the optional parameters `--spec-package-registry` and `--spec-package-path` to be able to retrieve the npm package and the specs file from it. By default, the generator expects the `package.json` file inside the npm module containing the specs, to have an export with the key `./openapi.[yaml|yml|json]` and the value will be the relative path to the spec file inside the package.
If you use `Yarn2+` with PnP, you can modify the following `scripts` in `package.json` to generate the SDK based on specifications in a dependency package:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { OpenApiToolsConfiguration } from '@ama-sdk/core';
import { Tree } from '@angular-devkit/schematics';
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
import * as path from 'node:path';
import { LOCAL_SPEC_FILENAME, SPEC_JSON_EXTENSION, SPEC_YAML_EXTENSION } from './index';
import { LOCAL_SPEC_FILENAME, SPEC_JSON_EXTENSION, SPEC_YAML_EXTENSION } from '@ama-sdk/core';
import type { JsonObject } from 'type-fest';

const collectionPath = path.join(__dirname, '..', '..', '..', 'collection.json');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { OpenApiToolsConfiguration, OpenApiToolsGenerator, PathObject } from '@ama-sdk/core';
import { LOCAL_SPEC_FILENAME, SPEC_JSON_EXTENSION, SPEC_YAML_EXTENSION } from '@ama-sdk/core';
import {
apply,
chain,
Expand Down Expand Up @@ -27,15 +28,6 @@ import { generateOperationFinderFromSingleFile } from './helpers/path-extractor'
const JAVA_OPTIONS = ['specPath', 'specConfigPath', 'globalProperty', 'outputPath'];
const OPEN_API_TOOLS_OPTIONS = ['generatorName', 'output', 'inputSpec', 'config', 'globalProperty'];

// TODO: Change to `open-api` when #1735 is done
mrednic-1A marked this conversation as resolved.
Show resolved Hide resolved
/** Name of the specification file copied locally (without extension) */
export const LOCAL_SPEC_FILENAME = 'swagger-spec';
/** Extension of the Specification file in YAML format */
export const SPEC_YAML_EXTENSION = 'yaml';
// TODO: Change to `json` when #1735 is done
/** Extension of the Specification file in JSON format */
export const SPEC_JSON_EXTENSION = 'yaml';

const getRegexpTemplate = (regexp: RegExp) => `new RegExp('${regexp.toString().replace(/\/(.*)\//, '$1').replace(/\\\//g, '/')}')`;

const getPathObjectTemplate = (pathObj: PathObject) => {
Expand Down
37 changes: 27 additions & 10 deletions packages/@ama-sdk/schematics/schematics/typescript/shell/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,15 @@ function ngGenerateTypescriptSDKFn(options: NgGenerateTypescriptSDKShellSchemati
const specScope = options.specPackageName?.startsWith('@') ? options.specPackageName.substring(1).split('/')[0] : undefined;

if (properties.packageManager === 'yarn') {
const yarnrcPath = posix.join(tree.root.path, '.yarnrc.yml');
const yarnrc = (load(tree.exists(yarnrcPath) ? tree.readText(yarnrcPath) : '') || {}) as any;
const workspaceRootYarnRcPath = posix.join(tree.root.path, '.yarnrc.yml');
const standaloneYarnRcPath = posix.join(targetPath, '.yarnrc.yml');

let yarnrc;
if (tree.exists(workspaceRootYarnRcPath)) {
yarnrc = load(tree.readText(workspaceRootYarnRcPath));
} else {
yarnrc = (load(tree.exists(standaloneYarnRcPath) ? tree.readText(standaloneYarnRcPath) : '') || {}) as any;
}
yarnrc.nodeLinker ||= 'pnp';
yarnrc.packageExtensions ||= {};
yarnrc.packageExtensions['@ama-sdk/schematics@*'] = {
Expand All @@ -103,20 +110,30 @@ function ngGenerateTypescriptSDKFn(options: NgGenerateTypescriptSDKShellSchemati
};
}

if (tree.exists(yarnrcPath)) {
tree.overwrite(yarnrcPath, dump(yarnrc, {indent: 2}));
if (tree.exists(workspaceRootYarnRcPath)) {
tree.overwrite(workspaceRootYarnRcPath, dump(yarnrc, {indent: 2}));
} else if (tree.exists(standaloneYarnRcPath)){
tree.overwrite(standaloneYarnRcPath, dump(yarnrc, {indent: 2}));
} else {
tree.create(yarnrcPath, dump(yarnrc, {indent: 2}));
tree.create(standaloneYarnRcPath, dump(yarnrc, {indent: 2}));
}
} else if (properties.packageManager === 'npm') {
if (options.specPackageRegistry && specScope) {
const npmrcPath = posix.join(tree.root.path, '.npmrc');
let npmrc = tree.exists(npmrcPath) ? tree.readText(npmrcPath) : '';
const workspaceRootNpmrcPath = posix.join(tree.root.path, '.npmrc');
const standaloneNpmrcPath = posix.join(targetPath, '.npmrc');
let npmrc;
if (tree.exists(workspaceRootNpmrcPath)) {
npmrc = tree.readText(workspaceRootNpmrcPath);
} else {
npmrc = tree.exists(standaloneNpmrcPath) ? tree.readText(standaloneNpmrcPath) : '';
}
npmrc += `\n@${specScope}:registry=${options.specPackageRegistry}\n`;
if (tree.exists(npmrcPath)) {
tree.overwrite(npmrcPath, npmrc);
if (tree.exists(workspaceRootNpmrcPath)) {
tree.overwrite(workspaceRootNpmrcPath, npmrc);
} else if (tree.exists(standaloneNpmrcPath)){
tree.overwrite(standaloneNpmrcPath, npmrc);
} else {
tree.create(npmrcPath, npmrc);
tree.create(standaloneNpmrcPath, npmrc);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,13 @@ export class JsonFormatter implements Formatter {

const content = JSON.stringify({
...generatePackageJson(artifactName, spec),
main: path.relative(this.cwd, this.filePath)
main: path.relative(this.cwd, this.filePath),
exports: {
// eslint-disable-next-line @typescript-eslint/naming-convention
'./openapi.json': {
default: path.relative(this.cwd, this.filePath)
}
}
}, null, 2);

const res = await fs.promises.writeFile(path.resolve(this.cwd, 'package.json'), content);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,13 @@ export class SplitYamlFormatter implements Formatter {

const content = JSON.stringify({
...generatePackageJson(artifactName, spec),
main: path.relative(this.cwd, this.filePath)
main: path.relative(this.cwd, this.filePath),
exports: {
// eslint-disable-next-line @typescript-eslint/naming-convention
'./openapi.yaml': {
default: path.relative(this.cwd, this.filePath)
}
}
}, null, 2);

await fs.promises.writeFile(path.resolve(this.cwd, 'package.json'), content);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,13 @@ export class YamlFormatter implements Formatter {

const content = JSON.stringify({
...generatePackageJson(artifactName, spec),
main: path.relative(this.cwd, this.filePath)
main: path.relative(this.cwd, this.filePath),
exports: {
// eslint-disable-next-line @typescript-eslint/naming-convention
'./openapi.yaml': {
default: path.relative(this.cwd, this.filePath)
}
}
}, null, 2);

await fs.promises.writeFile(path.resolve(this.cwd, 'package.json'), content);
Expand Down
8 changes: 5 additions & 3 deletions packages/@o3r/workspace/schematics/sdk/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,9 @@ function generateSdkFn(options: NgGenerateSdkSchema): Rule {
]), MergeStrategy.Overwrite);

const packageManager = getPackageManager({ workspaceConfig });
const specExtension = options.specPackagePath ? path.extname(options.specPackagePath) : '.yml';
const specPath = options.specPackageName ? `openapi${specExtension}` : options.specPath;
const specExtension = options.specPackagePath ? path.extname(options.specPackagePath) : '.yaml';
// TODO: Change `swagger-spec` to `openapi` in v11 (ref: #1745)
const specPath = options.specPackageName ? `swagger-spec${specExtension}` : options.specPath;
return chain([
externalSchematic('@ama-sdk/schematics', 'typescript-shell', {
...options,
Expand All @@ -68,7 +69,8 @@ function generateSdkFn(options: NgGenerateSdkSchema): Rule {
c.addTask(new NpmExecTask('amasdk-update-spec-from-npm', [
options.specPackageName,
...options.specPackagePath ? ['--package-path', options.specPackagePath] : [],
'--output', path.join(process.cwd(), targetPath, `openapi${specExtension}`)
// TODO: Change `swagger-spec` to `openapi` in v11 (ref: #1745)
'--output', path.join(process.cwd(), targetPath, `swagger-spec${specExtension}`)
], targetPath), [installTask])
] : [];
c.addTask(new RunSchematicTask('@ama-sdk/schematics', 'typescript-core', {
Expand Down
Loading