@ojiepermana/angular 21.1.9 → 21.1.11
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/generator/api/README.md
CHANGED
|
@@ -18,6 +18,9 @@ bun run gen:sdk:init
|
|
|
18
18
|
|
|
19
19
|
# 3. Edit sdk.config.json, then generate
|
|
20
20
|
bun run gen:sdk
|
|
21
|
+
|
|
22
|
+
# 4. Run the split-by-domain regression checks
|
|
23
|
+
bun run test:gen:sdk
|
|
21
24
|
```
|
|
22
25
|
|
|
23
26
|
## Consumer usage after publish
|
|
@@ -174,17 +177,21 @@ client primitives land in `shared/`.
|
|
|
174
177
|
|
|
175
178
|
Every domain folder contains `services/`, `fn/`, `models/`, `permissions/`,
|
|
176
179
|
and its own `public-api.ts` (which re-exports `shared/public-api` for
|
|
177
|
-
convenience). The root
|
|
178
|
-
|
|
179
|
-
|
|
180
|
+
convenience). The root still owns the aggregate metadata barrel:
|
|
181
|
+
`metadata.ts`, `openapi-helpers.ts`, and `permissions/index.ts` stay at the SDK
|
|
182
|
+
root so `shared/` never depends on sibling domains. The root `public-api.ts`
|
|
183
|
+
aggregates `shared`, root metadata helpers, and every domain, so consumers can
|
|
184
|
+
still do `import { UserService } from './sdk'` regardless of layout.
|
|
180
185
|
|
|
181
186
|
Model ownership rule (per-domain mode):
|
|
182
187
|
|
|
183
188
|
- A model used by exactly one domain → emitted inside that domain's `models/`.
|
|
184
189
|
- A model shared across two or more domains → emitted inside `shared/models/`.
|
|
185
190
|
- Client primitives (`ApiConfiguration`, `BaseService`, `RequestBuilder`,
|
|
186
|
-
`StrictHttpResponse`, `Api`), metadata, validators, and
|
|
187
|
-
|
|
191
|
+
`StrictHttpResponse`, `Api`), shared metadata types, validators, and
|
|
192
|
+
navigation always live under `shared/`.
|
|
193
|
+
- Aggregate metadata helpers (`metadata.ts`, `openapi-helpers.ts`) and the
|
|
194
|
+
top-level `permissions/index.ts` stay at the SDK root.
|
|
188
195
|
|
|
189
196
|
Example consumption when using `mode: 'library'` with `splitByDomain: true`:
|
|
190
197
|
|
|
@@ -197,11 +204,11 @@ import { GCSService } from '@my-scope/sdk/storage/gcs'; // splitDepth: 'tag'
|
|
|
197
204
|
|
|
198
205
|
## Output modes
|
|
199
206
|
|
|
200
|
-
| Mode | What it emits
|
|
201
|
-
| ---------------------- |
|
|
202
|
-
| `standalone` | A plain folder (no `ng-package.json`).
|
|
203
|
-
| `library` | Standalone output **plus** `ng-package.json`, `package.json` (peerDeps),
|
|
204
|
-
| `secondary-entrypoint` | Standalone output **plus** a minimal `ng-package.json` pointing at `public-api.ts
|
|
207
|
+
| Mode | What it emits | Use when… |
|
|
208
|
+
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
|
|
209
|
+
| `standalone` | A plain folder (no `ng-package.json`). | You consume the SDK via path alias / `tsconfig.paths` inside the same app. |
|
|
210
|
+
| `library` | Standalone output **plus** `ng-package.json`, `package.json` (peerDeps), `README.md`, and nested `ng-package.json` files for split-by-domain secondary entrypoints. | You want to build it with ng-packagr and publish to npm. |
|
|
211
|
+
| `secondary-entrypoint` | Standalone output **plus** a minimal `ng-package.json` pointing at `public-api.ts`, plus nested `ng-package.json` files for split-by-domain secondary entrypoints. | You drop the folder inside an existing library so ng-packagr picks it up as a subpath. |
|
|
205
212
|
|
|
206
213
|
## Feature flags
|
|
207
214
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.sdk = sdk;
|
|
4
|
+
exports.writeResult = writeResult;
|
|
4
5
|
const node_path_1 = require("node:path");
|
|
5
6
|
const loader_1 = require("../../src/config/loader");
|
|
6
7
|
const engine_1 = require("../../src/engine");
|
|
@@ -39,6 +40,7 @@ function writeResult(tree, workspaceRoot, result, context) {
|
|
|
39
40
|
context.logger.info(`[sdk] ${result.target.mode} → ${relOutput} ` +
|
|
40
41
|
`(schemas=${result.stats.schemas}, operations=${result.stats.operations}, ` +
|
|
41
42
|
`tags=${result.stats.tags}, files=${result.stats.files})`);
|
|
43
|
+
removeStaleFiles(tree, workspaceRoot, result);
|
|
42
44
|
for (const file of result.files) {
|
|
43
45
|
const absolute = (0, node_path_1.resolve)(result.outputDir, file.path);
|
|
44
46
|
const treePath = normalizeTreePath(workspaceRoot, absolute);
|
|
@@ -51,6 +53,22 @@ function writeResult(tree, workspaceRoot, result, context) {
|
|
|
51
53
|
}
|
|
52
54
|
}
|
|
53
55
|
}
|
|
56
|
+
function removeStaleFiles(tree, workspaceRoot, result) {
|
|
57
|
+
const outputRoot = normalizeTreePath(workspaceRoot, result.outputDir);
|
|
58
|
+
if (outputRoot === '/') {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const nextFiles = new Set(result.files.map((file) => normalizeTreePath(workspaceRoot, (0, node_path_1.resolve)(result.outputDir, file.path))));
|
|
62
|
+
const staleFiles = [];
|
|
63
|
+
tree.getDir(outputRoot).visit((path) => {
|
|
64
|
+
if (!nextFiles.has(path)) {
|
|
65
|
+
staleFiles.push(path);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
for (const staleFile of staleFiles) {
|
|
69
|
+
tree.delete(staleFile);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
54
72
|
function normalizeTreePath(workspaceRoot, absolute) {
|
|
55
73
|
const rel = (0, node_path_1.relative)(workspaceRoot, absolute);
|
|
56
74
|
const posix = rel.split(/\\+/).join('/');
|
|
@@ -7,7 +7,10 @@ exports.relayoutPerDomain = relayoutPerDomain;
|
|
|
7
7
|
*
|
|
8
8
|
* ```
|
|
9
9
|
* <outputDir>/
|
|
10
|
-
* shared/ client primitives, shared models, metadata, validators
|
|
10
|
+
* shared/ client primitives, shared models, metadata types, validators
|
|
11
|
+
* metadata.ts aggregate metadata barrel (root only)
|
|
12
|
+
* openapi-helpers.ts metadata helpers (root only)
|
|
13
|
+
* permissions/index.ts aggregate operation rules (root only)
|
|
11
14
|
* <domain>/ one folder per OpenAPI tag (kebab-cased)
|
|
12
15
|
* models/ models only referenced by this domain
|
|
13
16
|
* fn/ tree-shakeable operation functions for this domain
|
|
@@ -191,12 +194,13 @@ function computeNewPath(oldPath, ctx) {
|
|
|
191
194
|
'strict-http-response.ts',
|
|
192
195
|
'api.ts',
|
|
193
196
|
'api.navigation.ts',
|
|
194
|
-
'metadata.ts',
|
|
195
197
|
'metadata-types.ts',
|
|
196
|
-
'openapi-helpers.ts',
|
|
197
198
|
]);
|
|
198
199
|
if (rootShared.has(oldPath))
|
|
199
200
|
return `${SHARED}/${oldPath}`;
|
|
201
|
+
if (oldPath === 'metadata.ts' || oldPath === 'openapi-helpers.ts') {
|
|
202
|
+
return oldPath;
|
|
203
|
+
}
|
|
200
204
|
if (oldPath.startsWith('models/')) {
|
|
201
205
|
const kebab = oldPath.slice('models/'.length).replace(/\.ts$/, '');
|
|
202
206
|
const name = ctx.kebabToSchema.get(kebab);
|
|
@@ -220,7 +224,7 @@ function computeNewPath(oldPath, ctx) {
|
|
|
220
224
|
return `${domain}/services/${fileName}`;
|
|
221
225
|
}
|
|
222
226
|
if (oldPath === 'permissions/index.ts')
|
|
223
|
-
return
|
|
227
|
+
return oldPath;
|
|
224
228
|
if (oldPath.startsWith('permissions/')) {
|
|
225
229
|
const fileName = oldPath.slice('permissions/'.length);
|
|
226
230
|
const tagKebab = fileName.replace(/\.ts$/, '');
|
|
@@ -269,7 +273,7 @@ function rewriteImports(content, oldPath, newPath, oldToNew) {
|
|
|
269
273
|
function emitPublicApis(ir, target, mapping) {
|
|
270
274
|
const out = [];
|
|
271
275
|
const schemaNames = ir.schemas.map((s) => s.name);
|
|
272
|
-
// shared/public-api.ts —
|
|
276
|
+
// shared/public-api.ts — shared primitives + shared models only.
|
|
273
277
|
const sharedLines = [target.banner, ''];
|
|
274
278
|
if (target.features.client) {
|
|
275
279
|
sharedLines.push(`export { ApiConfiguration, provideApiConfiguration } from './api-configuration';`);
|
|
@@ -288,8 +292,8 @@ function emitPublicApis(ir, target, mapping) {
|
|
|
288
292
|
sharedLines.push('');
|
|
289
293
|
}
|
|
290
294
|
if (target.features.metadata) {
|
|
291
|
-
sharedLines.push(`export * from './metadata';`);
|
|
292
|
-
sharedLines.push(`export * from './
|
|
295
|
+
sharedLines.push(`export * from './metadata-types';`);
|
|
296
|
+
sharedLines.push(`export * from './validators';`);
|
|
293
297
|
sharedLines.push('');
|
|
294
298
|
}
|
|
295
299
|
if (target.features.navigation) {
|
|
@@ -338,6 +342,10 @@ function emitPublicApis(ir, target, mapping) {
|
|
|
338
342
|
// Root public-api.ts aggregates everything.
|
|
339
343
|
const rootLines = [target.banner, ''];
|
|
340
344
|
rootLines.push(`export * from './${SHARED}/public-api';`);
|
|
345
|
+
if (target.features.metadata) {
|
|
346
|
+
rootLines.push(`export * from './metadata';`);
|
|
347
|
+
rootLines.push(`export * from './openapi-helpers';`);
|
|
348
|
+
}
|
|
341
349
|
for (const domain of mapping.domains) {
|
|
342
350
|
rootLines.push(`export * from './${domain}/public-api';`);
|
|
343
351
|
}
|
|
@@ -35,10 +35,8 @@ function writeLibrary(files, ir, target) {
|
|
|
35
35
|
};
|
|
36
36
|
return [
|
|
37
37
|
...files,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
content: (0, template_1.finalize)(JSON.stringify(ngPackage, null, 2)),
|
|
41
|
-
},
|
|
38
|
+
createNgPackageFile('ng-package.json', ngPackage),
|
|
39
|
+
...createNestedEntrypointNgPackages(files),
|
|
42
40
|
{
|
|
43
41
|
path: 'package.json',
|
|
44
42
|
content: (0, template_1.finalize)(JSON.stringify(pkg, null, 2)),
|
|
@@ -59,11 +57,26 @@ function writeSecondaryEntrypoint(files, _ir, _target) {
|
|
|
59
57
|
const ngPackage = {
|
|
60
58
|
lib: { entryFile: 'public-api.ts' },
|
|
61
59
|
};
|
|
62
|
-
return [
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
60
|
+
return [...files, createNgPackageFile('ng-package.json', ngPackage), ...createNestedEntrypointNgPackages(files)];
|
|
61
|
+
}
|
|
62
|
+
function createNestedEntrypointNgPackages(files) {
|
|
63
|
+
return collectSecondaryEntrypointDirs(files).map((dir) => createNgPackageFile(`${dir}/ng-package.json`, {
|
|
64
|
+
lib: { entryFile: 'public-api.ts' },
|
|
65
|
+
}));
|
|
66
|
+
}
|
|
67
|
+
function collectSecondaryEntrypointDirs(files) {
|
|
68
|
+
const dirs = new Set();
|
|
69
|
+
for (const file of files) {
|
|
70
|
+
if (!file.path.endsWith('/public-api.ts')) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
dirs.add(file.path.slice(0, -'/public-api.ts'.length));
|
|
74
|
+
}
|
|
75
|
+
return [...dirs].sort();
|
|
76
|
+
}
|
|
77
|
+
function createNgPackageFile(path, content) {
|
|
78
|
+
return {
|
|
79
|
+
path,
|
|
80
|
+
content: (0, template_1.finalize)(JSON.stringify(content, null, 2)),
|
|
81
|
+
};
|
|
69
82
|
}
|