Skip to content

Commit 0dfb73c

Browse files
committed
feat(ama-openapi): create design project extension
1 parent efe2dc7 commit 0dfb73c

24 files changed

Lines changed: 1477 additions & 18 deletions
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/index.*
2+
3+
/common
4+
/component
5+
/configuration
6+
/dist*
7+
/errors
8+
/store
9+
/types
10+
/validation
11+
/utils

packages/@ama-openapi/create-extension/.npmignore

Whitespace-only changes.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Ama OpenAPI Create extension
2+
3+
> [!WARNING]
4+
> [Experimental](https://github.com/AmadeusITGroup/otter/blob/main/README.md#experimental): This package is available in early access, it will be part of the v14 release.
5+
6+
[![Stable Version](https://img.shields.io/npm/v/@ama-openapi/create?style=for-the-badge)](https://www.npmjs.com/package/@ama-openapi/create)
7+
[![Bundle Size](https://img.shields.io/bundlephobia/min/@ama-openapi/create?color=green&style=for-the-badge)](https://www.npmjs.com/package/@ama-openapi/create)
8+
9+
**@ama-openapi/create-extension** is a template generator package that provides scaffolding and templates for creating OpenAPI-based extension projects.
10+
It helps developers quickly set up new OpenAPI extension projects with best practices and a standardized structure.
11+
12+
## Usage
13+
14+
```shell
15+
npm create @ama-openapi/extension <command> -- [options]
16+
```
17+
18+
The following global options can be used with any command:
19+
20+
| Options | Description |
21+
| --- | --- |
22+
| `--version`, `-v` | Display the current version of the generator. |
23+
| `--help`, `-h` | Display usage for the CLI commands. |
24+
25+
## Available commands
26+
27+
### Design Extension Project
28+
29+
Create a new [OpenAPI](https://www.openapis.org/) design project extension via the following command:
30+
31+
```shell
32+
npm create @ama-openapi extension <project-name> -- [options]
33+
```
34+
35+
The following options are available:
36+
37+
| Options | Default Value | Description |
38+
| --- | --- | --- |
39+
| `--target`, `-t` | `.` | Target directory where files will be generated. |
40+
| `--dependency-base-spec`, `-b` | - | Name of the NPM artifact to use as the dependency base specification (e.g. @my-org/specification). |
41+
42+
The command will generate a directory with the same structure of the `design` command but will setup the repository to prepare the generation of 3 specification:
43+
44+
- `base` specification which is extended by the current specification
45+
- `extension` specification bundling only the specification defined in the current project
46+
- `merge` specification which is the merge of the `base` and `extension` specifications
47+
48+
## Integration
49+
50+
This generator sets up the repository to work with:
51+
52+
- [@ama-openapi/core](https://www.npmjs.com/package/@ama-openapi/core): Core dependency management functionality
53+
- [@ama-openapi/redocly-plugin](https://www.npmjs.com/package/@ama-openapi/redocly-plugin): Redocly CLI integration
54+
- [@ama-openapi/cli](https://www.npmjs.com/package/@ama-openapi/cli): Command to manually retrieve the dependencies
55+
- [@ama-openapi/create](https://www.npmjs.com/package/@ama-openapi/create) - Project scaffolding tool
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import shared from '../../../eslint.shared.config.mjs';
2+
import local from './eslint.local.config.mjs';
3+
4+
export default [
5+
...shared,
6+
...local
7+
];
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import {
2+
dirname,
3+
} from 'node:path';
4+
import {
5+
fileURLToPath,
6+
} from 'node:url';
7+
import globals from 'globals';
8+
9+
const __filename = fileURLToPath(import.meta.url);
10+
// __dirname is not defined in ES module scope
11+
const __dirname = dirname(__filename);
12+
13+
export default [
14+
{
15+
name: '@ama-sdk/create/projects',
16+
languageOptions: {
17+
sourceType: 'module',
18+
parserOptions: {
19+
tsconfigRootDir: __dirname,
20+
projectService: true
21+
},
22+
globals: {
23+
...globals.node
24+
}
25+
}
26+
}
27+
];
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
{
2+
"name": "@ama-openapi/create-extension",
3+
"version": "0.0.0-placeholder",
4+
"preferUnplugged": true,
5+
"publishConfig": {
6+
"access": "public"
7+
},
8+
"description": "Create a new OpenApi Specification extension project",
9+
"keywords": [
10+
"create",
11+
"openapi",
12+
"extension"
13+
],
14+
"bin": "./src/index.js",
15+
"scripts": {
16+
"nx": "nx",
17+
"ng": "yarn nx",
18+
"compile": "tsc -b tsconfig.build.json",
19+
"build": "yarn nx build ama-openapi-create",
20+
"copy:templates": "cpy 'templates/**' dist/templates/",
21+
"postbuild": "cpy 'package.json' 'dist' && patch-package-json-main"
22+
},
23+
"peerDependencies": {
24+
"@o3r/telemetry": "workspace:~",
25+
"type-fest": "^4.30.1"
26+
},
27+
"peerDependenciesMeta": {
28+
"@o3r/telemetry": {
29+
"optional": true
30+
},
31+
"type-fest": {
32+
"optional": true
33+
}
34+
},
35+
"dependencies": {
36+
"@ama-openapi/core": "workspace:~",
37+
"@ama-openapi/create": "workspace:~",
38+
"ejs": "~3.1.9",
39+
"globby": "^11.1.0",
40+
"tslib": "^2.6.2",
41+
"yargs": "~18.0.0"
42+
},
43+
"devDependencies": {
44+
"@angular-devkit/schematics": "~20.3.13",
45+
"@eslint-community/eslint-plugin-eslint-comments": "^4.4.0",
46+
"@nx/eslint": "~21.6.0",
47+
"@nx/eslint-plugin": "~21.6.0",
48+
"@nx/jest": "~21.6.0",
49+
"@nx/js": "~21.6.0",
50+
"@o3r/build-helpers": "workspace:~",
51+
"@o3r/eslint-config": "workspace:~",
52+
"@o3r/eslint-plugin": "workspace:~",
53+
"@o3r/telemetry": "workspace:~",
54+
"@o3r/test-helpers": "workspace:~",
55+
"@schematics/angular": "~20.3.13",
56+
"@stylistic/eslint-plugin": "~5.6.0",
57+
"@types/ejs": "^3.1.2",
58+
"@types/jest": "~30.0.0",
59+
"@types/minimist": "^1.2.2",
60+
"@types/node": "~24.10.0",
61+
"@types/semver": "^7.3.13",
62+
"@types/yargs": "~17.0.33",
63+
"@typescript-eslint/parser": "~8.50.0",
64+
"angular-eslint": "~20.6.0",
65+
"cpy-cli": "^6.0.0",
66+
"eslint": "~9.39.0",
67+
"eslint-import-resolver-node": "~0.3.9",
68+
"eslint-import-resolver-typescript": "~4.4.0",
69+
"eslint-plugin-import": "~2.32.0",
70+
"eslint-plugin-import-newlines": "~1.4.0",
71+
"eslint-plugin-jest": "~29.10.0",
72+
"eslint-plugin-jsdoc": "~54.7.0",
73+
"eslint-plugin-prefer-arrow": "~1.2.3",
74+
"eslint-plugin-unicorn": "~60.0.0",
75+
"eslint-plugin-unused-imports": "~4.3.0",
76+
"globals": "^16.0.0",
77+
"jest": "~30.2.0",
78+
"jest-junit": "~16.0.0",
79+
"jest-util": "~30.2.0",
80+
"jsonc-eslint-parser": "~2.4.0",
81+
"nx": "~21.6.0",
82+
"rimraf": "^6.0.1",
83+
"rxjs": "^7.8.1",
84+
"semver": "^7.5.2",
85+
"ts-jest": "~29.4.0",
86+
"type-fest": "^4.30.1",
87+
"typescript": "~5.9.2",
88+
"typescript-eslint": "~8.50.0"
89+
},
90+
"generatorDependencies": {
91+
"@redocly/openapi-core": "~2.11.0"
92+
},
93+
"engines": {
94+
"node": "^20.19.0 || ^22.17.0 || ^24.0.0",
95+
"yarn": ">=2.0.0 <5.0.0",
96+
"npm": ">=4"
97+
}
98+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
{
2+
"name": "ama-openapi-create-extension",
3+
"$schema": "https://raw.githubusercontent.com/nrwl/nx/master/packages/nx/schemas/project-schema.json",
4+
"projectType": "library",
5+
"sourceRoot": "packages/@ama-openapi/create-extension/src",
6+
"prefix": "o3r",
7+
"targets": {
8+
"build": {
9+
"executor": "nx:run-script",
10+
"outputs": [
11+
"{projectRoot}/dist/package.json"
12+
],
13+
"options": {
14+
"script": "postbuild"
15+
},
16+
"dependsOn": [
17+
"compile",
18+
"copy-templates"
19+
]
20+
},
21+
"copy-templates": {
22+
"executor": "nx:run-script",
23+
"outputs": [
24+
"{projectRoot}/dist/templates/**"
25+
],
26+
"inputs": [
27+
"templates"
28+
],
29+
"options": {
30+
"script": "copy:templates"
31+
},
32+
"dependsOn": [
33+
"compile"
34+
]
35+
},
36+
"compile": {
37+
"executor": "nx:run-script",
38+
"outputs": ["{projectRoot}/dist/index.*"],
39+
"options": {
40+
"script": "compile"
41+
},
42+
"dependsOn": [
43+
"^build"
44+
]
45+
},
46+
"lint": {},
47+
"set-version": {},
48+
"prepare-publish": {},
49+
"publish": {}
50+
},
51+
"tags": []
52+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import {
2+
readFile,
3+
writeFile,
4+
} from 'node:fs/promises';
5+
import {
6+
resolve,
7+
} from 'node:path';
8+
import {
9+
type CreateOptions,
10+
generateTemplate,
11+
} from '@ama-openapi/create';
12+
13+
/** Options interface to create extension */
14+
export interface CreateExtensionOptions extends CreateOptions {
15+
/** Name of the NPM artifact to use as the dependency base specification (e.g. @my-org/specification) */
16+
dependencyBaseSpec?: string;
17+
}
18+
19+
/**
20+
* Edit package.json file after generation
21+
* @param options
22+
*/
23+
const editPackageJson = async (options: CreateExtensionOptions) => {
24+
const { target } = options;
25+
const scripts = {
26+
build: 'redocly bundle && npm run build:merge',
27+
'build:base': 'redocly bundle base',
28+
'build:extension': 'redocly bundle server',
29+
'build:merge': 'redocly bundle filter-base && redocly join filtered-base server -o ./bundle/specification.yaml',
30+
'retrieve-externals': 'ama-openapi install',
31+
'watch:retrieve-externals': 'ama-openapi watch',
32+
postinstall: 'ama-openapi install'
33+
};
34+
35+
const packageJsonPath = resolve(target, 'package.json');
36+
return writeFile(
37+
packageJsonPath,
38+
JSON.stringify(
39+
{
40+
...JSON.parse(await readFile(packageJsonPath, { encoding: 'utf8' })),
41+
...scripts
42+
},
43+
null,
44+
2
45+
)
46+
);
47+
};
48+
49+
const editGitIgnore = async (options: CreateExtensionOptions) => {
50+
const { target } = options;
51+
return writeFile(
52+
resolve(target, '.gitignore'),
53+
await readFile(resolve(__dirname, '..', '.gitignore'), { encoding: 'utf8' })
54+
+ `\n# temporary files`
55+
+ `\n/tmp`
56+
);
57+
};
58+
59+
/**
60+
* Generate extension project
61+
* @param options
62+
*/
63+
export const generateExtension = async (options: CreateExtensionOptions) => {
64+
await generateTemplate({ ...options });
65+
await generateTemplate({ ...options, templatesDirectory: resolve(__dirname, '..', 'templates') });
66+
await editPackageJson(options);
67+
await editGitIgnore(options);
68+
};
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#!/usr/bin/env node
2+
3+
import {
4+
promises as fs,
5+
} from 'node:fs';
6+
import {
7+
resolve,
8+
} from 'node:path';
9+
import {
10+
OUTPUT_DIRECTORY,
11+
// eslint-disable-next-line import/no-unresolved -- Cannot resolve mjs file in current setup (see #3738)
12+
} from '@ama-openapi/core';
13+
import type {
14+
PackageJson,
15+
} from 'type-fest';
16+
import * as yargs from 'yargs';
17+
import {
18+
hideBin,
19+
} from 'yargs/helpers';
20+
import {
21+
type CreateExtensionOptions,
22+
generateExtension,
23+
} from './generate-template';
24+
25+
void (async () => {
26+
const version = (JSON.parse(await fs.readFile(resolve(__dirname, '..', 'package.json'), { encoding: 'utf8' })) as PackageJson).version!;
27+
const { name, target, dependencyBaseSpec } = await yargs(hideBin(process.argv))
28+
.option('target', {
29+
alias: 't',
30+
type: 'string',
31+
description: 'Target directory to generate the files into',
32+
default: process.cwd()
33+
})
34+
.option('dependencyBaseSpec', {
35+
type: 'string',
36+
alias: 'b',
37+
demandOption: false,
38+
describe: 'Name of the NPM artifact to use as the dependency base specification (e.g. @my-org/specification)'
39+
})
40+
.command('* <name>', 'Create a new OpenAPI extension project.')
41+
.positional('name', {
42+
type: 'string',
43+
demandOption: true,
44+
description: 'Name of the artifact / package to generate'
45+
})
46+
.usage('Usage: npm create @ama-openapi/extension <name> -- [options]')
47+
.version(version)
48+
.alias('h', 'help')
49+
.alias('v', 'version')
50+
.parse();
51+
52+
const options: CreateExtensionOptions = {
53+
target,
54+
externalModelPath: OUTPUT_DIRECTORY,
55+
version,
56+
packageName: name,
57+
dependencyBaseSpec,
58+
logger: console
59+
};
60+
await generateExtension(options);
61+
})();
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './generate-template';

0 commit comments

Comments
 (0)