@voxgig/apidef 1.9.0 → 2.0.2

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.
Files changed (73) hide show
  1. package/dist/apidef.d.ts +3 -29
  2. package/dist/apidef.js +65 -186
  3. package/dist/apidef.js.map +1 -1
  4. package/dist/builder/entity/apiEntity.d.ts +3 -0
  5. package/dist/builder/entity/apiEntity.js +51 -0
  6. package/dist/builder/entity/apiEntity.js.map +1 -0
  7. package/dist/builder/entity/def.d.ts +3 -0
  8. package/dist/builder/entity/def.js +19 -0
  9. package/dist/builder/entity/def.js.map +1 -0
  10. package/dist/builder/entity.d.ts +2 -0
  11. package/dist/builder/entity.js +30 -0
  12. package/dist/builder/entity.js.map +1 -0
  13. package/dist/builder/flow/flowHeuristic01.d.ts +2 -0
  14. package/dist/builder/flow/flowHeuristic01.js +153 -0
  15. package/dist/builder/flow/flowHeuristic01.js.map +1 -0
  16. package/dist/builder/flow.d.ts +2 -0
  17. package/dist/builder/flow.js +41 -0
  18. package/dist/builder/flow.js.map +1 -0
  19. package/dist/guide/heuristic01.d.ts +2 -0
  20. package/dist/guide/heuristic01.js +119 -0
  21. package/dist/guide/heuristic01.js.map +1 -0
  22. package/dist/guide.d.ts +2 -0
  23. package/dist/guide.js +60 -0
  24. package/dist/guide.js.map +1 -0
  25. package/dist/parse.d.ts +1 -1
  26. package/dist/parse.js +5 -4
  27. package/dist/parse.js.map +1 -1
  28. package/dist/resolver.d.ts +2 -0
  29. package/dist/resolver.js +62 -0
  30. package/dist/resolver.js.map +1 -0
  31. package/dist/transform/entity.js +25 -4
  32. package/dist/transform/entity.js.map +1 -1
  33. package/dist/transform/field.js +4 -84
  34. package/dist/transform/field.js.map +1 -1
  35. package/dist/transform/operation.d.ts +2 -2
  36. package/dist/transform/operation.js +60 -30
  37. package/dist/transform/operation.js.map +1 -1
  38. package/dist/transform/top.d.ts +2 -2
  39. package/dist/transform/top.js +5 -4
  40. package/dist/transform/top.js.map +1 -1
  41. package/dist/transform.d.ts +1 -1
  42. package/dist/transform.js +20 -10
  43. package/dist/transform.js.map +1 -1
  44. package/dist/tsconfig.tsbuildinfo +1 -1
  45. package/dist/types.d.ts +691 -0
  46. package/dist/types.js +39 -0
  47. package/dist/types.js.map +1 -0
  48. package/dist/utility.d.ts +5 -0
  49. package/dist/utility.js +84 -0
  50. package/dist/utility.js.map +1 -0
  51. package/model/apidef.jsonic +28 -24
  52. package/package.json +9 -7
  53. package/src/apidef.ts +94 -271
  54. package/src/builder/entity/apiEntity.ts +88 -0
  55. package/src/builder/entity/def.ts +44 -0
  56. package/src/builder/entity.ts +54 -0
  57. package/src/builder/flow/flowHeuristic01.ts +200 -0
  58. package/src/builder/flow.ts +61 -0
  59. package/src/guide/heuristic01.ts +178 -0
  60. package/src/guide.ts +87 -0
  61. package/src/parse.ts +6 -4
  62. package/src/resolver.ts +91 -0
  63. package/src/transform/entity.ts +36 -10
  64. package/src/transform/field.ts +9 -92
  65. package/src/transform/operation.ts +112 -46
  66. package/src/transform/top.ts +11 -9
  67. package/src/transform.ts +22 -11
  68. package/src/types.ts +89 -0
  69. package/src/utility.ts +161 -0
  70. package/dist/transform/manual.d.ts +0 -3
  71. package/dist/transform/manual.js +0 -12
  72. package/dist/transform/manual.js.map +0 -1
  73. package/src/transform/manual.ts +0 -29
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA,4CAA4C;;;AAK5C,+BAAsC;AAmBtC,MAAM,UAAU,GAAG,IAAA,WAAI,EAAC;IACtB,IAAI,EAAE,MAAM;IACZ,GAAG,EAAE,MAAM;IACX,IAAI,EAAE;QACJ,GAAG,EAAE,EAAE;QACP,GAAG,EAAE,EAAE;QACP,GAAG,EAAE;YACH,KAAK,EAAE,EAAE;YACT,MAAM,EAAE,EAAE;SACX;KACF;CACF,CAAC,CAAA;AACF,MAAM,cAAc,GAAG,IAAA,WAAI,EAAC,IAAA,WAAI,EAAC,UAAU,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAA;AAuC9D,wCAAc;AAlChB,MAAM,UAAU,GAAG,IAAA,WAAI,EAAC;IACtB,IAAI,EAAE;QACJ,IAAI,EAAE,EAAE;QACR,IAAI,EAAE,EAAE;QACR,KAAK,EAAE,EAAE;QACT,GAAG,EAAE,EAAE;QACP,GAAG,EAAE,EAAE;QACP,OAAO,EAAE,EAAE;QACX,GAAG,EAAE,EAAE;QACP,EAAE,EAAE,IAAA,UAAG,GAAE;QACT,KAAK,EAAE;YACL,GAAG,EAAE,IAAI;YACT,GAAG,EAAE,IAAI;YACT,GAAG,EAAE,IAAI;SACV;KACF;CACF,CAAC,CAAA;AACF,MAAM,cAAc,GAAG,IAAA,WAAI,EAAC,IAAA,WAAI,EAAC,UAAU,CAAC,CAAC,CAAA;AAkB3C,wCAAc"}
@@ -0,0 +1,5 @@
1
+ import type { FsUtil, Log } from './types';
2
+ declare function loadFile(path: string, what: string, fs: FsUtil, log: Log): string;
3
+ declare function formatJsonSrc(jsonsrc: string): string;
4
+ declare function depluralize(word: string): string;
5
+ export { loadFile, formatJsonSrc, depluralize, };
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.loadFile = loadFile;
4
+ exports.formatJsonSrc = formatJsonSrc;
5
+ exports.depluralize = depluralize;
6
+ function loadFile(path, what, fs, log) {
7
+ try {
8
+ const source = fs.readFileSync(path, 'utf8');
9
+ return source;
10
+ }
11
+ catch (err) {
12
+ log.error({ load: 'fail', what, path, err });
13
+ throw err;
14
+ }
15
+ }
16
+ function formatJsonSrc(jsonsrc) {
17
+ return jsonsrc
18
+ .replace(/"([a-zA-Z_][a-zA-Z_0-9]*)": /g, '$1: ')
19
+ .replace(/},/g, '}\n')
20
+ // .replace(/([a-zA-Z_][a-zA-Z_0-9]*)_COMMENT:/g, '# $1')
21
+ .replace(/\n(\s*)([a-zA-Z_][a-zA-Z_0-9]*)_COMMENT:\s*"(.*)",/g, '\n\n$1# $2 $3');
22
+ }
23
+ function depluralize(word) {
24
+ if (!word || word.length === 0) {
25
+ return word;
26
+ }
27
+ // Common irregular plurals
28
+ const irregulars = {
29
+ 'children': 'child',
30
+ 'men': 'man',
31
+ 'women': 'woman',
32
+ 'teeth': 'tooth',
33
+ 'feet': 'foot',
34
+ 'geese': 'goose',
35
+ 'mice': 'mouse',
36
+ 'people': 'person',
37
+ 'data': 'datum',
38
+ 'criteria': 'criterion',
39
+ 'phenomena': 'phenomenon',
40
+ 'indices': 'index',
41
+ 'matrices': 'matrix',
42
+ 'vertices': 'vertex',
43
+ 'analyses': 'analysis',
44
+ 'axes': 'axis',
45
+ 'crises': 'crisis',
46
+ 'diagnoses': 'diagnosis',
47
+ 'oases': 'oasis',
48
+ 'theses': 'thesis',
49
+ 'appendices': 'appendix'
50
+ };
51
+ if (irregulars[word]) {
52
+ return irregulars[word];
53
+ }
54
+ // Rules for regular plurals (applied in order)
55
+ // -ies -> -y (cities -> city)
56
+ if (word.endsWith('ies') && word.length > 3) {
57
+ return word.slice(0, -3) + 'y';
58
+ }
59
+ // -ves -> -f or -fe (wolves -> wolf, knives -> knife)
60
+ if (word.endsWith('ves')) {
61
+ const stem = word.slice(0, -3);
62
+ // Check if it should be -fe (like knife, wife, life)
63
+ if (['kni', 'wi', 'li'].includes(stem)) {
64
+ return stem + 'fe';
65
+ }
66
+ return stem + 'f';
67
+ }
68
+ // -oes -> -o (potatoes -> potato)
69
+ if (word.endsWith('oes')) {
70
+ return word.slice(0, -2);
71
+ }
72
+ // -ses, -xes, -zes, -shes, -ches -> remove -es (boxes -> box)
73
+ if (word.endsWith('ses') || word.endsWith('xes') || word.endsWith('zes') ||
74
+ word.endsWith('shes') || word.endsWith('ches')) {
75
+ return word.slice(0, -2);
76
+ }
77
+ // -s -> remove -s (cats -> cat)
78
+ if (word.endsWith('s') && !word.endsWith('ss')) {
79
+ return word.slice(0, -1);
80
+ }
81
+ // If none of the rules apply, return as is
82
+ return word;
83
+ }
84
+ //# sourceMappingURL=utility.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utility.js","sourceRoot":"","sources":["../src/utility.ts"],"names":[],"mappings":"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"}
@@ -1,33 +1,36 @@
1
1
 
2
- main: api: guide: control: transform: &: {
3
- order: string
4
- }
5
-
6
- main: api: guide: control: transform: openapi: {
7
- order: *`
2
+ main: api: guide: control: {
3
+
4
+ transform: openapi: {
5
+ order: *`
6
+ top,
7
+ entity,
8
+ operation,
9
+ field,
10
+ ` | string,
11
+
12
+ element: {
13
+ top: {}
14
+ entity: {}
15
+ operation: {}
16
+ field: {}
17
+ }
18
+ }
8
19
 
9
- top,
10
- entity,
11
- operation,
12
- field,
13
- manual,
14
-
15
- ` | string
16
- }
20
+ builder: standard: {
21
+ order: *`
22
+ entity,
23
+ flow,
24
+ ` | string,
17
25
 
26
+ element: {
27
+ entity: {}
28
+ flow: {}
29
+ }
30
+ }
18
31
 
19
- main: api: guide: transform: &: {
20
- name: .$KEY
21
- load: string
22
32
  }
23
33
 
24
- main: api: guide: transform: {
25
- top: {}
26
- entity: {}
27
- operation: {}
28
- field: {}
29
- manual: {}
30
- }
31
34
 
32
35
 
33
36
  main: api: guide: entity: &: {
@@ -46,3 +49,4 @@ main: api: guide: entity: &: {
46
49
 
47
50
 
48
51
 
52
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voxgig/apidef",
3
- "version": "1.9.0",
3
+ "version": "2.0.2",
4
4
  "main": "dist/apidef.js",
5
5
  "type": "commonjs",
6
6
  "types": "dist/apidef.d.ts",
@@ -19,13 +19,13 @@
19
19
  "voxgig-apidef": "bin/voxgig-apidef"
20
20
  },
21
21
  "scripts": {
22
- "test": "node --enable-source-maps --test dist-test",
23
- "test22": "node --enable-source-maps --test \"dist-test/*.test.js\"",
24
- "test-some": "node --enable-source-maps --test-name-pattern=\"$npm_config_pattern\" --test dist-test",
22
+ "test": "node --enable-source-maps --test \"dist-test/**/*.test.js\"",
23
+ "test-some": "node --enable-source-maps --test-name-pattern=\"$npm_config_pattern\" --test \"dist-test/**/*.test.js\"",
25
24
  "watch": "tsc --build src test -w",
26
25
  "build": "tsc --build src test",
27
26
  "clean": "rm -rf dist dist-test node_modules yarn.lock package-lock.json",
28
27
  "reset": "npm run clean && npm i && npm run build && npm test",
28
+ "postinstall": "patch-package",
29
29
  "repo-tag": "REPO_VERSION=`node -e \"console.log(require('./package').version)\"` && echo TAG: v$REPO_VERSION && git commit -a -m v$REPO_VERSION && git push && git tag v$REPO_VERSION && git push --tags;",
30
30
  "repo-publish": "npm run clean && npm i && npm run repo-publish-quick",
31
31
  "repo-publish-quick": "npm run build && npm run test && npm run repo-tag && npm publish --registry https://registry.npmjs.org --access=public"
@@ -41,19 +41,21 @@
41
41
  "devDependencies": {
42
42
  "@hapi/code": "^9.0.3",
43
43
  "@types/js-yaml": "^4.0.9",
44
- "@types/node": "22.15.29",
44
+ "@types/node": "24.0.12",
45
45
  "aontu": "^0.28.0",
46
- "esbuild": "^0.25.5",
46
+ "esbuild": "^0.25.6",
47
47
  "json-schema-to-ts": "^3.1.1",
48
48
  "memfs": "^4.17.2",
49
+ "patch-package": "^8.0.0",
49
50
  "typescript": "^5.8.3"
50
51
  },
51
52
  "dependencies": {
52
53
  "@redocly/openapi-core": "^1.34.3",
54
+ "@voxgig/struct": "^0.0.4",
53
55
  "@voxgig/util": "^0.0.10",
54
56
  "chokidar": "^4.0.3",
55
57
  "gubu": "^9.0.0",
56
- "jostraca": "^0.19.0",
58
+ "jostraca": "^0.20.0",
57
59
  "pino": "^9.7.0",
58
60
  "pino-pretty": "^13.0.0",
59
61
  "sonic-boom": "^4.2.0"
package/src/apidef.ts CHANGED
@@ -1,85 +1,70 @@
1
- /* Copyright (c) 2024 Voxgig, MIT License */
1
+ /* Copyright (c) 2024-2025 Voxgig, MIT License */
2
2
 
3
3
  import * as Fs from 'node:fs'
4
4
  import Path from 'node:path'
5
5
  import { inspect } from 'node:util'
6
6
 
7
- import { bundleFromString, createConfig } from '@redocly/openapi-core'
8
- import { Gubu, Open, Any } from 'gubu'
9
- import { each } from 'jostraca'
10
- import { prettyPino, Pino } from '@voxgig/util'
7
+ import { Jostraca, Project, names } from 'jostraca'
8
+
9
+ import { prettyPino } from '@voxgig/util'
10
+
11
+
12
+ import type {
13
+ ApiDefOptions,
14
+ Model,
15
+ Build,
16
+ ApiModel,
17
+ } from './types'
18
+
19
+ import {
20
+ OpenModelShape,
21
+ OpenBuildShape,
22
+ } from './types'
23
+
24
+
25
+ import {
26
+ resolveGuide,
27
+ } from './guide'
28
+
29
+
30
+ import {
31
+ parse
32
+ } from './parse'
11
33
 
12
34
 
13
35
  import {
14
- resolveTransforms,
15
- processTransforms,
16
36
  fixName,
17
37
  } from './transform'
18
38
 
19
39
 
20
- type ApiDefOptions = {
21
- def?: string
22
- fs?: any
23
- pino?: ReturnType<typeof Pino>
24
- debug?: boolean | string
25
- folder?: string
26
- meta?: Record<string, any>
27
- outprefix?: string
28
- }
29
-
30
40
 
31
- type ApiModel = {
32
- main: {
33
- api: {
34
- entity: Record<string, any>
35
- }
36
- def: Record<string, any>
37
- }
38
- }
41
+ import {
42
+ resolveElements
43
+ } from './resolver'
39
44
 
45
+ import {
46
+ loadFile,
47
+ } from './utility'
40
48
 
41
- const ModelShape = Gubu({
42
- def: String,
43
- main: {
44
- sdk: {},
45
- def: {},
46
- api: {},
47
- }
48
- })
49
- const OpenModelShape = Gubu(Open(ModelShape))
50
-
51
- type Model = ReturnType<typeof ModelShape>
52
-
53
-
54
- const BuildShape = Gubu({
55
- spec: {
56
- base: '',
57
- path: '',
58
- debug: '',
59
- use: {},
60
- res: [],
61
- require: '',
62
- log: {},
63
- fs: Any(),
64
- watch: {
65
- mod: true,
66
- add: true,
67
- rem: true,
68
- }
69
- }
70
- })
71
- const OpenBuildShape = Gubu(Open(BuildShape))
72
49
 
73
- type Build = ReturnType<typeof BuildShape>
50
+ import { topTransform } from './transform/top'
51
+ import { entityTransform } from './transform/entity'
52
+ import { operationTransform } from './transform/operation'
53
+ import { fieldTransform } from './transform/field'
74
54
 
55
+ import { makeEntityBuilder } from './builder/entity'
56
+ import { makeFlowBuilder } from './builder/flow'
75
57
 
76
58
 
77
59
  function ApiDef(opts: ApiDefOptions) {
60
+
61
+ // TODO: gubu opts!
78
62
  const fs = opts.fs || Fs
79
63
  const pino = prettyPino('apidef', opts)
80
-
81
64
  const log = pino.child({ cmp: 'apidef' })
82
65
 
66
+ opts.strategy = opts.strategy || 'heuristic01'
67
+
83
68
 
84
69
  async function generate(spec: any) {
85
70
  const start = Date.now()
@@ -87,6 +72,17 @@ function ApiDef(opts: ApiDefOptions) {
87
72
  const model: Model = OpenModelShape(spec.model)
88
73
  const build: Build = OpenBuildShape(spec.build)
89
74
 
75
+ names(model, model.name)
76
+
77
+ const apimodel: ApiModel = {
78
+ main: {
79
+ api: {
80
+ entity: {}
81
+ },
82
+ def: {},
83
+ },
84
+ }
85
+
90
86
  const buildspec = build.spec
91
87
 
92
88
  let defpath = model.def
@@ -101,76 +97,63 @@ function ApiDef(opts: ApiDefOptions) {
101
97
 
102
98
  // TODO: Validate spec
103
99
  const ctx = {
100
+ fs,
104
101
  log,
105
102
  spec,
106
103
  opts,
107
104
  util: { fixName },
108
105
  defpath: Path.dirname(defpath),
109
106
  model,
107
+ apimodel,
108
+ def: undefined
110
109
  }
111
110
 
112
- const transformSpec = await resolveTransforms(ctx)
111
+ const defsrc = loadFile(defpath, 'def', fs, log)
113
112
 
114
- log.debug({
115
- point: 'transform', spec: transformSpec,
116
- note: log.levelVal <= 20 ? inspect(transformSpec) : ''
117
- })
113
+ const def = await parse('OpenAPI', defsrc, { file: defpath })
114
+ ctx.def = def
118
115
 
116
+ const guideBuilder = await resolveGuide(ctx)
119
117
 
120
- let source
121
- try {
122
- source = fs.readFileSync(defpath, 'utf8')
123
- }
124
- catch (err: any) {
125
- log.error({ read: 'fail', what: 'def', file: defpath, err })
126
- throw err
127
- }
128
-
129
-
130
- const config = await createConfig({})
131
- let bundle
132
-
133
- try {
134
- bundle = await bundleFromString({
135
- source,
136
- config,
137
- dereference: true,
138
- })
139
- }
140
- catch (err: any) {
141
- log.error({ parse: 'fail', what: 'openapi', file: defpath, err })
142
- throw err
143
- }
144
118
 
119
+ // const transformSpec = await resolveTransforms(ctx)
120
+ const transforms = await resolveElements(ctx, 'transform', 'openapi', {
121
+ top: topTransform,
122
+ entity: entityTransform,
123
+ operation: operationTransform,
124
+ field: fieldTransform,
125
+ })
145
126
 
146
- const apimodel: ApiModel = {
147
- main: {
148
- api: {
149
- entity: {}
150
- },
151
- def: {},
152
- },
153
- }
127
+ const builders = await resolveElements(ctx, 'builder', 'standard', {
128
+ entity: makeEntityBuilder,
129
+ flow: makeFlowBuilder,
130
+ })
154
131
 
155
- const def = bundle.bundle.parsed
156
- const processResult = await processTransforms(ctx, transformSpec, apimodel, def)
157
132
 
158
- if (!processResult.ok) {
159
- log.error({
160
- fail: 'process', point: 'transform-result',
161
- result: processResult, note: processResult.msg,
162
- err: processResult.results[0]?.err
163
- })
133
+ const jostraca = Jostraca({
134
+ now: spec.now,
135
+ fs: () => fs,
136
+ log,
137
+ })
164
138
 
165
- return { ok: false, name: 'apidef', processResult }
166
- }
139
+ const jmodel = {}
167
140
 
141
+ const root = () => Project({ folder: '.' }, async () => {
142
+ guideBuilder()
143
+ // entityBuilder()
144
+ // flowBuilder()
168
145
 
169
- const modelPath = Path.normalize(spec.config.model)
146
+ for (let builder of builders) {
147
+ builder()
148
+ }
149
+ })
170
150
 
171
- buildModel_api(apimodel, modelPath)
172
- buildModel_def(apimodel, modelPath)
173
- buildModel_entity(apimodel, modelPath)
151
+ const jres = await jostraca.generate({
152
+ // folder: Path.dirname(opts.folder as string),
153
+ folder: opts.folder,
154
+ model: jmodel,
155
+ existing: { txt: { merge: true } }
156
+ }, root)
174
157
 
175
158
  log.info({ point: 'generate-end', note: 'success', break: true })
176
159
 
@@ -181,154 +164,20 @@ function ApiDef(opts: ApiDefOptions) {
181
164
  }
182
165
  }
183
166
 
184
-
185
- function buildModel_api(apimodel: ApiModel, modelPath: string) {
186
- const modelapi = { main: { api: apimodel.main.api } }
187
- let modelSrc = JSON.stringify(modelapi, null, 2)
188
-
189
- modelSrc =
190
- '# GENERATED FILE - DO NOT EDIT\n\n' +
191
- modelSrc.substring(1, modelSrc.length - 1).replace(/\n /g, '\n')
192
-
193
- writeChanged('api-model', modelPath, modelSrc)
194
- return modelPath
195
- }
196
-
197
-
198
- function buildModel_def(apimodel: ApiModel, modelPath: string) {
199
- const modelBasePath = Path.dirname(modelPath)
200
- const defFilePath = Path.join(modelBasePath,
201
- (null == opts.outprefix ? '' : opts.outprefix) + 'def-generated.jsonic')
202
-
203
- const modelDef = { main: { def: apimodel.main.def } }
204
- let modelDefSrc = JSON.stringify(modelDef, null, 2)
205
-
206
- modelDefSrc =
207
- '# GENERATED FILE - DO NOT EDIT\n\n' +
208
- modelDefSrc.substring(1, modelDefSrc.length - 1).replace(/\n /g, '\n')
209
-
210
- writeChanged('def-model', defFilePath, modelDefSrc)
211
- }
212
-
213
-
214
- function buildModel_entity(apimodel: ApiModel, modelPath: string) {
215
- const modelBasePath = Path.dirname(modelPath)
216
-
217
- const entityIncludes: string[] = []
218
-
219
- each(apimodel.main.api.entity, ((entity: any) => {
220
- entityIncludes.push(entity.name)
221
-
222
- // HEURISTIC: id may be name_id or nameId
223
- const fieldAliases =
224
- each(entity.op, (op: any) =>
225
- each(op.param))
226
- .flat()
227
- .reduce((a: any, p: any) =>
228
-
229
- (entity.field[p.keys] ? null :
230
- (p.key$.toLowerCase().includes(entity.name) ?
231
- (a[p.key$] = 'id', a.id = p.key$) :
232
- null)
233
-
234
- , a), {})
235
-
236
- const fieldAliasesSrc =
237
- JSON.stringify(fieldAliases, null, 2)
238
- .replace(/\n/g, '\n ')
239
-
240
- const entityFileSrc = `
241
- # Entity ${entity.name}
242
-
243
- main: sdk: entity: ${entity.name}: {
244
- alias: field: ${fieldAliasesSrc}
245
- }
246
-
247
- `
248
- const entityFilePath = Path.join(modelBasePath, 'entity',
249
- (null == opts.outprefix ? '' : opts.outprefix) + entity.name + '.jsonic')
250
-
251
- fs.mkdirSync(Path.dirname(entityFilePath), { recursive: true })
252
-
253
- // TODO: diff merge
254
- writeChanged('entity-model', entityFilePath, entityFileSrc, { update: false })
255
- }))
256
-
257
-
258
- modifyModel(
259
- fs,
260
- Path.join(
261
- modelBasePath,
262
- (null == opts.outprefix ? '' : opts.outprefix) + 'sdk.jsonic'),
263
- entityIncludes
264
- )
265
- }
266
-
267
-
268
- function writeChanged(
269
- point: string, path: string, content: string,
270
- flags?: { update?: boolean }
271
- ) {
272
- let exists = false
273
- let changed = false
274
-
275
- flags = flags || {}
276
- flags.update = null == flags.update ? true : !!flags.update
277
-
278
- let action = ''
279
- try {
280
- let existingContent: string = ''
281
- path = Path.normalize(path)
282
-
283
- exists = fs.existsSync(path)
284
-
285
- if (exists) {
286
- action = 'read'
287
- existingContent = fs.readFileSync(path, 'utf8')
288
- }
289
-
290
- changed = existingContent !== content
291
-
292
- action = flags.update ? 'write' : 'skip'
293
-
294
- log.info({
295
- point: 'write-' + point,
296
- note: (changed ? '' : 'not-') + 'changed ' + path,
297
- write: 'file', skip: !changed, exists, changed,
298
- contentLength: content.length, file: path
299
- })
300
-
301
- if (!exists || (changed && flags.update)) {
302
- fs.writeFileSync(path, content)
303
- }
304
- }
305
- catch (err: any) {
306
- log.error({
307
- fail: action, point, file: path, exists, changed,
308
- contentLength: content.length, err
309
- })
310
- err.__logged__ = true
311
- throw err
312
- }
313
- }
314
-
315
167
  return {
316
168
  generate,
317
169
  }
318
170
  }
319
171
 
320
172
 
321
-
322
173
  ApiDef.makeBuild = async function(opts: ApiDefOptions) {
323
174
  let apidef: any = undefined
324
175
 
325
- const outprefix = null == opts.outprefix ? '' : opts.outprefix
176
+ // const outprefix = null == opts.outprefix ? '' : opts.outprefix
326
177
 
327
178
  const config = {
328
179
  def: opts.def || 'no-def',
329
180
  kind: 'openapi3',
330
- model: opts.folder ?
331
- (opts.folder + '/' + outprefix + 'api-generated.jsonic') : 'no-model',
332
181
  meta: opts.meta || {},
333
182
  }
334
183
 
@@ -351,41 +200,15 @@ ApiDef.makeBuild = async function(opts: ApiDefOptions) {
351
200
 
352
201
 
353
202
 
354
- async function modifyModel(fs: any, path: string, entityIncludes: string[]) {
355
- // TODO: This is a kludge.
356
- // Aontu should provide option for as-is AST so that can be used
357
- // to find injection point more reliably
358
-
359
-
360
- let src = fs.existsSync(path) ? fs.readFileSync(path, 'utf8') :
361
- 'main: sdk: entity: {}\n'
362
-
363
- let newsrc = '' + src
364
-
365
- // Inject target file references into model
366
- entityIncludes.sort().map((entname: string) => {
367
- const lineRE =
368
- new RegExp(`@"entity/${entname}.jsonic"`)
369
-
370
- if (!src.match(lineRE)) {
371
- newsrc = newsrc.replace(/(main:\s+sdk:\s+entity:\s+\{\s*\}\n)/, '$1' +
372
- `@"entity/${entname}.jsonic"\n`)
373
- }
374
- })
375
-
376
- if (newsrc.length !== src.length) {
377
- fs.writeFileSync(path, newsrc)
378
- }
379
- }
380
203
 
381
204
 
382
205
 
383
206
  export type {
384
207
  ApiDefOptions,
385
- // ApiDefSpec,
386
208
  }
387
209
 
388
210
 
389
211
  export {
390
212
  ApiDef,
213
+ parse,
391
214
  }