@obsidiane/meridiane 1.0.1 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -5
- package/cli.js +7 -11
- package/package.json +1 -1
- package/templates/_lib_template/README.md +194 -0
- package/templates/_lib_template/ng-package.json +3 -0
- package/tools/build.js +6 -5
- package/tools/core/generate.js +40 -24
- package/tools/core/paths.js +0 -7
- package/tools/dev.js +6 -5
- package/tools/generator/models/oas-contract.js +159 -0
- package/tools/generator/models/oas-schema-name.js +71 -0
- package/tools/generator/models/openapi-to-models.js +171 -378
- package/tools/generator/models/schema-utils.js +3 -100
- package/tools/generator/models/templates/model.hbs +3 -1
- package/tools/generator/models/type-resolver.js +6 -3
- package/tools/generator/models/naming.js +0 -26
package/README.md
CHANGED
|
@@ -21,18 +21,21 @@ npm i -g @obsidiane/meridiane
|
|
|
21
21
|
```bash
|
|
22
22
|
# Dev: génère le bridge (+ models par défaut)
|
|
23
23
|
# (build standalone + install local dans node_modules)
|
|
24
|
-
npx meridiane dev <packageName> --spec <url|file> [--
|
|
24
|
+
npx meridiane dev <packageName> --spec <url|file> [--formats <mimeTypes>]... [--include <substr>]... [--exclude <substr>]... [--no-models] [--debug]
|
|
25
25
|
|
|
26
26
|
# CI/CD: génère + build Angular + npm pack (artefact prêt à publier)
|
|
27
|
-
npx meridiane build <packageName> --version <semver> --spec <url|file> [--
|
|
27
|
+
npx meridiane build <packageName> --version <semver> --spec <url|file> [--formats <mimeTypes>]... [--include <substr>]... [--exclude <substr>]... [--no-models] [--debug]
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
Options :
|
|
31
31
|
- `--debug` : active des logs supplémentaires (CLI).
|
|
32
|
+
- `--formats` : peut être répété ou fourni en liste séparée par virgules :
|
|
33
|
+
- `--formats application/ld+json`
|
|
34
|
+
- `--formats application/ld+json,application/json`
|
|
32
35
|
|
|
33
36
|
Bonnes pratiques :
|
|
34
|
-
- utiliser `--
|
|
35
|
-
- modèles générés uniquement s’ils sont utilisés par les endpoints
|
|
37
|
+
- utiliser `--formats application/ld+json` (ou plusieurs formats) pour un mode “contract-driven” :
|
|
38
|
+
- modèles générés uniquement s’ils sont utilisés par les endpoints (pour ces formats)
|
|
36
39
|
- groups conservés
|
|
37
40
|
- pas de modèles `*.jsonMergePatch` (PATCH = `Partial<...>`)
|
|
38
41
|
- noms normalisés (pas de suffixe `.jsonld`)
|
|
@@ -45,7 +48,7 @@ Note (repo Meridiane) :
|
|
|
45
48
|
## Exemple CI (générer + build + publish)
|
|
46
49
|
|
|
47
50
|
```bash
|
|
48
|
-
npx -y @obsidiane/meridiane@0.1.0 build @acme/backend-bridge --version 0.1.0 --spec https://staging.example/api/docs.json --
|
|
51
|
+
npx -y @obsidiane/meridiane@0.1.0 build @acme/backend-bridge --version 0.1.0 --spec https://staging.example/api/docs.json --formats application/ld+json
|
|
49
52
|
npm publish dist/backend-bridge
|
|
50
53
|
```
|
|
51
54
|
|
package/cli.js
CHANGED
|
@@ -8,16 +8,7 @@ import { createLogger } from './tools/core/logger.js';
|
|
|
8
8
|
|
|
9
9
|
const program = new Command();
|
|
10
10
|
|
|
11
|
-
program
|
|
12
|
-
.name('meridiane')
|
|
13
|
-
.description('Generate an Angular bridge + models from an API Platform OpenAPI spec.')
|
|
14
|
-
.showHelpAfterError()
|
|
15
|
-
.showSuggestionAfterError();
|
|
16
|
-
|
|
17
|
-
function presetParser(value, previous) {
|
|
18
|
-
if (value === undefined && previous === undefined) return true; // `--preset` (no value) => native
|
|
19
|
-
return value ?? previous;
|
|
20
|
-
}
|
|
11
|
+
program.name('meridiane').description('Generate an Angular bridge + models from an API Platform OpenAPI spec.').showHelpAfterError().showSuggestionAfterError();
|
|
21
12
|
|
|
22
13
|
function collect(value, previous = []) {
|
|
23
14
|
return [...previous, value];
|
|
@@ -26,7 +17,12 @@ function collect(value, previous = []) {
|
|
|
26
17
|
function commonOptions(cmd) {
|
|
27
18
|
return cmd
|
|
28
19
|
.option('--spec <urlOrFile>', 'OpenAPI spec source (URL or local JSON file)')
|
|
29
|
-
.option(
|
|
20
|
+
.option(
|
|
21
|
+
'--formats <mimeTypes>',
|
|
22
|
+
'Generate only selected media types (repeatable, supports commas). Enables contract-driven mode from paths.',
|
|
23
|
+
collect,
|
|
24
|
+
[]
|
|
25
|
+
)
|
|
30
26
|
.option('--include <substr>', 'Include schema names containing substring (repeatable, supports commas)', collect, [])
|
|
31
27
|
.option('--exclude <substr>', 'Exclude schema names containing substring (repeatable, supports commas)', collect, [])
|
|
32
28
|
.option('--no-models', 'Skip models generation (then --spec is not required)')
|
package/package.json
CHANGED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# __PACKAGE_NAME__
|
|
2
|
+
|
|
3
|
+
Bridge Angular (runtime + models TypeScript) pour une API Platform (Hydra/JSON-LD), avec support Mercure/SSE optionnel.
|
|
4
|
+
|
|
5
|
+
Le package expose une API Angular minimaliste :
|
|
6
|
+
- `provideBridge()` pour configurer le bridge (HTTP + tokens + Mercure) ;
|
|
7
|
+
- `FacadeFactory` / `ResourceFacade<T>` pour une API orientée ressource ;
|
|
8
|
+
- `BridgeFacade` pour des appels ad-hoc.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm i __PACKAGE_NAME__
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Le bridge est conçu pour être installé dans une application Angular. Les dépendances Angular et RxJS sont des `peerDependencies`.
|
|
17
|
+
|
|
18
|
+
## Compatibilité
|
|
19
|
+
|
|
20
|
+
- Angular `@angular/*` `^20.1.0`
|
|
21
|
+
- RxJS `^7.8.0`
|
|
22
|
+
|
|
23
|
+
## Démarrage rapide
|
|
24
|
+
|
|
25
|
+
Configurez le bridge au démarrage de l’application avec `provideBridge()`.
|
|
26
|
+
|
|
27
|
+
### Application standalone (recommandé)
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
// app.config.ts (ou main.ts)
|
|
31
|
+
import {ApplicationConfig} from '@angular/core';
|
|
32
|
+
import {provideBridge} from '__PACKAGE_NAME__';
|
|
33
|
+
|
|
34
|
+
export const appConfig: ApplicationConfig = {
|
|
35
|
+
providers: [
|
|
36
|
+
provideBridge({
|
|
37
|
+
baseUrl: 'https://api.example.com',
|
|
38
|
+
mercure: {hubUrl: 'https://api.example.com/.well-known/mercure'},
|
|
39
|
+
}),
|
|
40
|
+
],
|
|
41
|
+
};
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Application NgModule
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import {NgModule} from '@angular/core';
|
|
48
|
+
import {provideBridge} from '__PACKAGE_NAME__';
|
|
49
|
+
|
|
50
|
+
@NgModule({
|
|
51
|
+
providers: [
|
|
52
|
+
provideBridge({baseUrl: 'https://api.example.com'}),
|
|
53
|
+
],
|
|
54
|
+
})
|
|
55
|
+
export class AppModule {}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Configuration
|
|
59
|
+
|
|
60
|
+
### Cookies / `withCredentials`
|
|
61
|
+
|
|
62
|
+
Le bridge peut envoyer des cookies (sessions) côté HTTP et SSE.
|
|
63
|
+
|
|
64
|
+
Le comportement `withCredentials` par défaut est déduit de `mercure.init` :
|
|
65
|
+
- `credentials: 'include'` (défaut) → cookies envoyés
|
|
66
|
+
- `credentials: 'omit'` → cookies non envoyés
|
|
67
|
+
|
|
68
|
+
Vous pouvez aussi surcharger au cas par cas via `opts.withCredentials` sur les appels HTTP.
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
import {provideBridge} from '__PACKAGE_NAME__';
|
|
72
|
+
|
|
73
|
+
provideBridge({
|
|
74
|
+
baseUrl: 'https://api.example.com',
|
|
75
|
+
mercure: {
|
|
76
|
+
hubUrl: 'https://api.example.com/.well-known/mercure',
|
|
77
|
+
init: {credentials: 'omit'},
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Auth (Bearer)
|
|
83
|
+
|
|
84
|
+
`auth` accepte :
|
|
85
|
+
- une string (token Bearer),
|
|
86
|
+
- `{ type: 'bearer', token }`,
|
|
87
|
+
- `{ type: 'bearer', getToken }` (sync ou async),
|
|
88
|
+
- ou un `HttpInterceptorFn` custom.
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
import {provideBridge} from '__PACKAGE_NAME__';
|
|
92
|
+
|
|
93
|
+
provideBridge({
|
|
94
|
+
baseUrl: 'https://api.example.com',
|
|
95
|
+
auth: {type: 'bearer', getToken: () => localStorage.getItem('token') ?? undefined},
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Defaults (headers / timeout / retries)
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
import {provideBridge} from '__PACKAGE_NAME__';
|
|
103
|
+
|
|
104
|
+
provideBridge({
|
|
105
|
+
baseUrl: 'https://api.example.com',
|
|
106
|
+
defaults: {
|
|
107
|
+
headers: {'X-Requested-With': 'fetch'},
|
|
108
|
+
timeoutMs: 15_000,
|
|
109
|
+
retries: {count: 2, delayMs: 250, methods: ['GET']},
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Debug
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
import {provideBridge} from '__PACKAGE_NAME__';
|
|
118
|
+
|
|
119
|
+
provideBridge({baseUrl: 'https://api.example.com', debug: true});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Appeler l’API
|
|
123
|
+
|
|
124
|
+
### Style orienté ressource (`FacadeFactory` + `ResourceFacade<T>`)
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
import {inject} from '@angular/core';
|
|
128
|
+
import {FacadeFactory, ResourceFacade, Item} from '__PACKAGE_NAME__';
|
|
129
|
+
|
|
130
|
+
type Book = Item & {title?: string};
|
|
131
|
+
|
|
132
|
+
export class BooksService {
|
|
133
|
+
private readonly factory = inject(FacadeFactory);
|
|
134
|
+
readonly books: ResourceFacade<Book> = this.factory.create<Book>({url: '/api/books'});
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Exemples :
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
// collection Hydra
|
|
142
|
+
this.books.getCollection$({page: 1, itemsPerPage: 20, filters: {title: 'Dune'}});
|
|
143
|
+
|
|
144
|
+
// item (souvent via @id)
|
|
145
|
+
this.books.get$(book['@id']!);
|
|
146
|
+
|
|
147
|
+
// write
|
|
148
|
+
this.books.post$({title: 'Neuromancer'});
|
|
149
|
+
this.books.patch$(book['@id']!, {title: 'Count Zero'});
|
|
150
|
+
this.books.delete$(book['@id']!);
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Style ad-hoc (`BridgeFacade`)
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
import {inject} from '@angular/core';
|
|
157
|
+
import {BridgeFacade} from '__PACKAGE_NAME__';
|
|
158
|
+
|
|
159
|
+
export class HealthService {
|
|
160
|
+
private readonly bridge = inject(BridgeFacade);
|
|
161
|
+
getHealth$() {
|
|
162
|
+
return this.bridge.get$<{status: string}>('/health');
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Realtime (Mercure/SSE)
|
|
168
|
+
|
|
169
|
+
Le realtime est inactif tant que `mercure.hubUrl` n’est pas fourni à `provideBridge()`.
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
provideBridge({
|
|
173
|
+
baseUrl: 'https://api.example.com',
|
|
174
|
+
mercure: {hubUrl: 'https://api.example.com/.well-known/mercure', topicMode: 'url'},
|
|
175
|
+
});
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
API :
|
|
179
|
+
- `ResourceFacade<T>.watch$(iri | iri[])` / `unwatch(iri | iri[])`
|
|
180
|
+
- `ResourceFacade<T>.watchSubResource$(iri | iri[], 'field.path')`
|
|
181
|
+
- `BridgeFacade.watch$(iri | iri[])` / `unwatch(iri | iri[])`
|
|
182
|
+
|
|
183
|
+
Note SSR : la connexion SSE ne s’ouvre que dans le navigateur.
|
|
184
|
+
|
|
185
|
+
## Models TypeScript
|
|
186
|
+
|
|
187
|
+
Les models générés (si présents) sont exportés au même niveau que le runtime :
|
|
188
|
+
|
|
189
|
+
```ts
|
|
190
|
+
import type {Item} from '__PACKAGE_NAME__';
|
|
191
|
+
// import type {Book} from '__PACKAGE_NAME__';
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
En JSON-LD (`application/ld+json`), l’IRI est typiquement dans `model['@id']`.
|
package/tools/build.js
CHANGED
|
@@ -4,7 +4,7 @@ import { spawn } from 'node:child_process';
|
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
|
|
6
6
|
import { deriveLibName } from './core/lib-name.js';
|
|
7
|
-
import {
|
|
7
|
+
import { splitListArgs } from './core/paths.js';
|
|
8
8
|
import { readOpenApiSpec } from './core/spec.js';
|
|
9
9
|
import { createLogger } from './core/logger.js';
|
|
10
10
|
import { generateBridgeWorkspace } from './core/generate.js';
|
|
@@ -30,20 +30,21 @@ export async function runBuild(packageName, opts) {
|
|
|
30
30
|
if (!version) throw new Error('Missing --version');
|
|
31
31
|
|
|
32
32
|
const noModels = opts.models === false;
|
|
33
|
-
const
|
|
33
|
+
const formats = splitListArgs(opts.formats);
|
|
34
34
|
const include = splitListArgs(opts.include);
|
|
35
35
|
const exclude = splitListArgs(opts.exclude);
|
|
36
36
|
|
|
37
37
|
const libName = deriveLibName(packageName);
|
|
38
38
|
const requiredMode = 'spec';
|
|
39
|
+
const effectiveFormats = formats.length > 0 ? formats : ['application/ld+json'];
|
|
39
40
|
|
|
40
|
-
// Standalone mode
|
|
41
|
+
// Standalone mode: works from any repo (backend pipeline or otherwise).
|
|
41
42
|
log.title('build — standalone');
|
|
42
43
|
log.info(`repo: ${cwd}`);
|
|
43
44
|
log.info(`package: ${packageName}@${version}`);
|
|
44
45
|
log.info(`lib: ${libName}`);
|
|
45
46
|
if (noModels) log.info('models: désactivés (--no-models)');
|
|
46
|
-
else log.info(`spec: ${opts.spec} |
|
|
47
|
+
else log.info(`spec: ${opts.spec} | formats: ${effectiveFormats.join(', ')} | nullableMode: ${requiredMode}`);
|
|
47
48
|
|
|
48
49
|
const { workspaceRoot, distRoot, npmCacheDir, requireFromWs } = await ensureStandaloneWorkspace({ repoRoot: cwd, log });
|
|
49
50
|
log.info(`workspace: ${workspaceRoot}`);
|
|
@@ -65,7 +66,7 @@ export async function runBuild(packageName, opts) {
|
|
|
65
66
|
noModels,
|
|
66
67
|
spec,
|
|
67
68
|
requiredMode,
|
|
68
|
-
|
|
69
|
+
formats: effectiveFormats,
|
|
69
70
|
include,
|
|
70
71
|
exclude,
|
|
71
72
|
debug,
|
package/tools/core/generate.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
|
-
import {
|
|
2
|
+
import {existsSync} from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
import {
|
|
4
|
+
import {fileURLToPath} from 'node:url';
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
6
|
+
import {readJson, writeJsonIfChanged} from './json.js';
|
|
7
|
+
import {renderTemplateToFile} from '../generator/models/handlebars.js';
|
|
8
|
+
import {ensureCleanDir} from '../generator/models/utils.js';
|
|
9
|
+
import {buildModelsFromOpenAPI} from '../generator/models/openapi-to-models.js';
|
|
10
10
|
|
|
11
11
|
const __filename = fileURLToPath(import.meta.url);
|
|
12
12
|
const __dirname = path.dirname(__filename);
|
|
@@ -14,7 +14,7 @@ const __dirname = path.dirname(__filename);
|
|
|
14
14
|
const pkgRoot = path.resolve(__dirname, '../..'); // packages/meridiane
|
|
15
15
|
|
|
16
16
|
async function replacePlaceholdersInDir(dir, placeholders) {
|
|
17
|
-
const entries = await fs.readdir(dir, {
|
|
17
|
+
const entries = await fs.readdir(dir, {withFileTypes: true});
|
|
18
18
|
for (const entry of entries) {
|
|
19
19
|
const fullPath = path.join(dir, entry.name);
|
|
20
20
|
if (entry.isDirectory()) {
|
|
@@ -29,17 +29,25 @@ async function replacePlaceholdersInDir(dir, placeholders) {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
async function generateLibrary({
|
|
32
|
+
async function generateLibrary({cwd, libName, packageName, version, debug}) {
|
|
33
33
|
const tplDir = path.resolve(pkgRoot, 'templates/_lib_template');
|
|
34
34
|
const targetDir = path.resolve(cwd, 'projects', libName);
|
|
35
35
|
|
|
36
|
-
if (debug) console.log('[meridiane] generate lib', {
|
|
36
|
+
if (debug) console.log('[meridiane] generate lib', {
|
|
37
|
+
libName,
|
|
38
|
+
packageName,
|
|
39
|
+
version,
|
|
40
|
+
targetDir: path.relative(cwd, targetDir)
|
|
41
|
+
});
|
|
37
42
|
|
|
38
|
-
await fs.rm(targetDir, {
|
|
39
|
-
await fs.mkdir(targetDir, {
|
|
40
|
-
await fs.cp(tplDir, targetDir, {
|
|
43
|
+
await fs.rm(targetDir, {recursive: true, force: true});
|
|
44
|
+
await fs.mkdir(targetDir, {recursive: true});
|
|
45
|
+
await fs.cp(tplDir, targetDir, {recursive: true});
|
|
41
46
|
|
|
42
|
-
await replacePlaceholdersInDir(targetDir, {
|
|
47
|
+
await replacePlaceholdersInDir(targetDir, {
|
|
48
|
+
'__LIB_NAME__': libName,
|
|
49
|
+
'__PACKAGE_NAME__': packageName,
|
|
50
|
+
});
|
|
43
51
|
|
|
44
52
|
const libPackageJsonPath = path.join(targetDir, 'package.json');
|
|
45
53
|
if (existsSync(libPackageJsonPath)) {
|
|
@@ -52,18 +60,25 @@ async function generateLibrary({ cwd, libName, packageName, version, debug }) {
|
|
|
52
60
|
// angular.json / workspace patching is intentionally not done here (Meridiane is standalone).
|
|
53
61
|
}
|
|
54
62
|
|
|
55
|
-
async function generateModels({
|
|
63
|
+
async function generateModels({cwd, libName, spec, requiredMode, formats, include, exclude, debug}) {
|
|
56
64
|
const outDir = path.resolve(cwd, 'projects', libName, 'src', 'models');
|
|
57
65
|
const templatesDir = path.resolve(pkgRoot, 'tools', 'generator', 'models', 'templates');
|
|
58
66
|
|
|
59
|
-
if (debug)
|
|
67
|
+
if (debug)
|
|
68
|
+
console.log('[meridiane] generate models', {
|
|
69
|
+
outDir: path.relative(cwd, outDir),
|
|
70
|
+
requiredMode,
|
|
71
|
+
formats: Array.isArray(formats) ? formats : [],
|
|
72
|
+
includeCount: include.length,
|
|
73
|
+
excludeCount: exclude.length,
|
|
74
|
+
});
|
|
60
75
|
|
|
61
76
|
await ensureCleanDir(outDir);
|
|
62
77
|
|
|
63
78
|
// Write models + index.ts.
|
|
64
|
-
const {
|
|
79
|
+
const {models} = buildModelsFromOpenAPI(spec, {
|
|
65
80
|
requiredMode,
|
|
66
|
-
|
|
81
|
+
formats: Array.isArray(formats) ? formats : [],
|
|
67
82
|
includeSchemaNames: include,
|
|
68
83
|
excludeSchemaNames: exclude,
|
|
69
84
|
});
|
|
@@ -77,6 +92,7 @@ async function generateModels({ cwd, libName, spec, requiredMode, preset, includ
|
|
|
77
92
|
props: m.props,
|
|
78
93
|
imports: m.imports,
|
|
79
94
|
extendsTypes: m.extendsTypes || [],
|
|
95
|
+
extendsItem: m.extendsItem === true,
|
|
80
96
|
},
|
|
81
97
|
});
|
|
82
98
|
}
|
|
@@ -84,7 +100,7 @@ async function generateModels({ cwd, libName, spec, requiredMode, preset, includ
|
|
|
84
100
|
await renderTemplateToFile({
|
|
85
101
|
templatePath: path.join(templatesDir, 'index.hbs'),
|
|
86
102
|
outPath: path.join(outDir, 'index.ts'),
|
|
87
|
-
ctx: {
|
|
103
|
+
ctx: {models: models.map((m) => ({name: m.name}))},
|
|
88
104
|
});
|
|
89
105
|
|
|
90
106
|
if (debug) console.log(`[meridiane] ${models.length} model(s) generated`);
|
|
@@ -98,8 +114,8 @@ async function generateModels({ cwd, libName, spec, requiredMode, preset, includ
|
|
|
98
114
|
* version: string,
|
|
99
115
|
* noModels: boolean,
|
|
100
116
|
* spec?: any,
|
|
101
|
-
* requiredMode: 'all
|
|
102
|
-
*
|
|
117
|
+
* requiredMode: 'all'|'spec',
|
|
118
|
+
* formats?: string[],
|
|
103
119
|
* include: string[],
|
|
104
120
|
* exclude: string[],
|
|
105
121
|
* debug: boolean,
|
|
@@ -117,7 +133,7 @@ export async function generateBridgeWorkspace(params) {
|
|
|
117
133
|
noModels,
|
|
118
134
|
spec,
|
|
119
135
|
requiredMode,
|
|
120
|
-
|
|
136
|
+
formats,
|
|
121
137
|
include,
|
|
122
138
|
exclude,
|
|
123
139
|
debug,
|
|
@@ -126,7 +142,7 @@ export async function generateBridgeWorkspace(params) {
|
|
|
126
142
|
} = params;
|
|
127
143
|
|
|
128
144
|
log?.step?.(`génération de la librairie (projects/${libName})`);
|
|
129
|
-
await generateLibrary({
|
|
145
|
+
await generateLibrary({cwd, libName, packageName, version, debug});
|
|
130
146
|
|
|
131
147
|
// If a dist root is provided, force ng-packagr output to that directory.
|
|
132
148
|
if (distRoot) {
|
|
@@ -136,7 +152,7 @@ export async function generateBridgeWorkspace(params) {
|
|
|
136
152
|
const projectDir = path.dirname(ngPackagePath);
|
|
137
153
|
ngPkg.dest = path.relative(projectDir, desiredDestAbs).split(path.sep).join('/');
|
|
138
154
|
await writeJsonIfChanged(ngPackagePath, ngPkg);
|
|
139
|
-
log?.debug?.('ng-package.json dest overridden', {
|
|
155
|
+
log?.debug?.('ng-package.json dest overridden', {dest: ngPkg.dest});
|
|
140
156
|
}
|
|
141
157
|
|
|
142
158
|
if (noModels) {
|
|
@@ -147,6 +163,6 @@ export async function generateBridgeWorkspace(params) {
|
|
|
147
163
|
|
|
148
164
|
if (!spec) throw new Error('Internal error: spec is required when noModels=false');
|
|
149
165
|
log?.step?.('génération des models (OpenAPI)');
|
|
150
|
-
await generateModels({
|
|
166
|
+
await generateModels({cwd, libName, spec, requiredMode, formats, include, exclude, debug});
|
|
151
167
|
log?.success?.('génération terminée');
|
|
152
168
|
}
|
package/tools/core/paths.js
CHANGED
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
export function normalizePreset(presetOption) {
|
|
2
|
-
if (presetOption === undefined) return 'all';
|
|
3
|
-
if (presetOption === true) return 'native';
|
|
4
|
-
if (presetOption === 'native' || presetOption === 'all') return presetOption;
|
|
5
|
-
throw new Error(`Invalid --preset value: ${presetOption} (expected: native|all)`);
|
|
6
|
-
}
|
|
7
|
-
|
|
8
1
|
export function splitListArgs(values) {
|
|
9
2
|
const out = [];
|
|
10
3
|
for (const v of Array.isArray(values) ? values : []) {
|
package/tools/dev.js
CHANGED
|
@@ -5,7 +5,7 @@ import path from 'node:path';
|
|
|
5
5
|
import { spawn } from 'node:child_process';
|
|
6
6
|
|
|
7
7
|
import { deriveLibName } from './core/lib-name.js';
|
|
8
|
-
import {
|
|
8
|
+
import { splitListArgs } from './core/paths.js';
|
|
9
9
|
import { readOpenApiSpec } from './core/spec.js';
|
|
10
10
|
import { createLogger } from './core/logger.js';
|
|
11
11
|
import { generateBridgeWorkspace } from './core/generate.js';
|
|
@@ -139,12 +139,13 @@ export async function runDev(packageName, opts) {
|
|
|
139
139
|
const libName = deriveLibName(effectivePackageName);
|
|
140
140
|
|
|
141
141
|
const noModels = opts.models === false;
|
|
142
|
-
const
|
|
142
|
+
const formats = splitListArgs(opts.formats);
|
|
143
143
|
const include = splitListArgs(opts.include);
|
|
144
144
|
const exclude = splitListArgs(opts.exclude);
|
|
145
145
|
|
|
146
146
|
const version = '0.0.0-dev';
|
|
147
|
-
const requiredMode = 'all
|
|
147
|
+
const requiredMode = 'all';
|
|
148
|
+
const effectiveFormats = formats.length > 0 ? formats : ['application/ld+json'];
|
|
148
149
|
const specSource = useSandboxDefaults ? (opts.spec ?? 'http://localhost:8000/api/docs.json') : opts.spec;
|
|
149
150
|
|
|
150
151
|
log.title('dev — build standalone + install local');
|
|
@@ -153,7 +154,7 @@ export async function runDev(packageName, opts) {
|
|
|
153
154
|
log.info(`lib: ${libName}`);
|
|
154
155
|
if (useSandboxDefaults) log.info('defaults sandbox appliqués');
|
|
155
156
|
if (noModels) log.info('models: désactivés (--no-models)');
|
|
156
|
-
else log.info(`spec: ${specSource} |
|
|
157
|
+
else log.info(`spec: ${specSource} | formats: ${effectiveFormats.join(', ')} | nullableMode: ${requiredMode}`);
|
|
157
158
|
|
|
158
159
|
const { workspaceRoot, distRoot, npmCacheDir, requireFromWs } = await ensureStandaloneWorkspace({ repoRoot: appRoot, log });
|
|
159
160
|
log.info(`workspace: ${workspaceRoot}`);
|
|
@@ -175,7 +176,7 @@ export async function runDev(packageName, opts) {
|
|
|
175
176
|
noModels,
|
|
176
177
|
spec,
|
|
177
178
|
requiredMode,
|
|
178
|
-
|
|
179
|
+
formats: effectiveFormats,
|
|
179
180
|
include,
|
|
180
181
|
exclude,
|
|
181
182
|
debug,
|