Skip to content

Commit dbfdd6f

Browse files
committed
feat(ama-openapi): create design project extension
1 parent 057863d commit dbfdd6f

25 files changed

Lines changed: 622 additions & 19 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, the final version will be released in v15.
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: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
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": "^5.3.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": "~21.1.0",
45+
"@angular/cli": "~21.1.0",
46+
"@eslint-community/eslint-plugin-eslint-comments": "^4.4.0",
47+
"@nx/eslint": "~22.5.0",
48+
"@nx/eslint-plugin": "~22.5.0",
49+
"@nx/jest": "~22.5.0",
50+
"@nx/js": "~22.5.0",
51+
"@o3r/build-helpers": "workspace:~",
52+
"@o3r/eslint-config": "workspace:~",
53+
"@o3r/eslint-plugin": "workspace:~",
54+
"@o3r/telemetry": "workspace:~",
55+
"@o3r/test-helpers": "workspace:~",
56+
"@schematics/angular": "~21.1.0",
57+
"@stylistic/eslint-plugin": "~5.7.0",
58+
"@types/ejs": "^3.1.2",
59+
"@types/jest": "~30.0.0",
60+
"@types/minimist": "^1.2.2",
61+
"@types/node": "~24.10.0",
62+
"@types/semver": "^7.3.13",
63+
"@types/yargs": "~17.0.33",
64+
"@typescript-eslint/parser": "~8.54.0",
65+
"angular-eslint": "~21.2.0",
66+
"cpy-cli": "^6.0.0",
67+
"eslint": "~9.39.0",
68+
"eslint-import-resolver-node": "~0.3.9",
69+
"eslint-import-resolver-typescript": "~4.4.0",
70+
"eslint-plugin-import": "~2.32.0",
71+
"eslint-plugin-import-newlines": "~1.4.0",
72+
"eslint-plugin-jest": "~29.12.0",
73+
"eslint-plugin-jsdoc": "~61.7.0",
74+
"eslint-plugin-prefer-arrow": "~1.2.3",
75+
"eslint-plugin-unicorn": "~62.0.0",
76+
"eslint-plugin-unused-imports": "~4.3.0",
77+
"globals": "^16.0.0",
78+
"jest": "~30.2.0",
79+
"jest-junit": "~16.0.0",
80+
"jest-util": "~30.2.0",
81+
"jsonc-eslint-parser": "~2.4.0",
82+
"nx": "~22.5.0",
83+
"rimraf": "^6.0.1",
84+
"rxjs": "^7.8.1",
85+
"semver": "^7.5.2",
86+
"ts-jest": "~29.4.0",
87+
"type-fest": "^5.3.1",
88+
"typescript": "~5.9.2",
89+
"typescript-eslint": "~8.54.0"
90+
},
91+
"generatorDependencies": {
92+
"@redocly/openapi-core": "~2.18.0"
93+
},
94+
"engines": {
95+
"node": "^20.19.0 || ^22.17.0 || ^24.0.0",
96+
"yarn": ">=2.0.0 <5.0.0",
97+
"npm": ">=4"
98+
}
99+
}
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: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
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+
/**
16+
* Name of the NPM artifact to use as the dependency base specification (e.g. `@my-org/specification`)
17+
*/
18+
dependencyBaseSpec?: string;
19+
}
20+
21+
/**
22+
* Edit package.json file after generation
23+
* @param options
24+
*/
25+
const editPackageJson = async (options: CreateExtensionOptions) => {
26+
const { target } = options;
27+
const scripts = {
28+
build: 'redocly bundle && npm run build:merge',
29+
'build:base': 'redocly bundle base',
30+
'build:extension': 'redocly bundle server',
31+
'build:merge': 'redocly bundle filter-base && redocly join filtered-base server -o ./bundle/specification.yaml',
32+
'retrieve-externals': 'ama-openapi install',
33+
'watch:retrieve-externals': 'ama-openapi watch',
34+
postinstall: 'ama-openapi install'
35+
};
36+
37+
const packageJsonPath = resolve(target, 'package.json');
38+
return writeFile(
39+
packageJsonPath,
40+
JSON.stringify(
41+
{
42+
...JSON.parse(await readFile(packageJsonPath, { encoding: 'utf8' })),
43+
...scripts
44+
},
45+
null,
46+
2
47+
)
48+
);
49+
};
50+
51+
const editGitIgnore = async (options: CreateExtensionOptions) => {
52+
const { target } = options;
53+
return writeFile(
54+
resolve(target, '.gitignore'),
55+
await readFile(resolve(__dirname, '..', '.gitignore'), { encoding: 'utf8' })
56+
+ `\n# temporary files`
57+
+ `\n/tmp`
58+
);
59+
};
60+
61+
/**
62+
* Generate extension project
63+
* @param options
64+
*/
65+
export const generateExtension = async (options: CreateExtensionOptions) => {
66+
await generateTemplate({ ...options });
67+
await generateTemplate({ ...options, templatesDirectory: resolve(__dirname, '..', 'templates') });
68+
await editPackageJson(options);
69+
await editGitIgnore(options);
70+
};
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)