@voxgig/create-sdkgen 0.27.0 → 0.29.0
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 +23 -0
- package/dist/project/standard/CreateRoot.js +40 -43
- package/dist/project/standard/CreateRoot.js.map +1 -1
- package/dist/project/standard/ModelSdk.js +2 -2
- package/dist/project/standard/ModelSdk.js.map +1 -1
- package/dist/project/standard/migrate.d.ts +9 -0
- package/dist/project/standard/migrate.js +137 -0
- package/dist/project/standard/migrate.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/project/standard/.sdk/admin/README.md +3 -1
- package/project/standard/.sdk/admin/check-drift.sh +22 -5
- package/project/standard/.sdk/model/.model-config/{model-config.aon → model-config.aontu} +1 -1
- package/project/standard/.sdk/model/{sdk.fragment.aon → sdk.fragment.aontu} +7 -7
- package/project/standard/.sdk/package.json +12 -12
- package/project/standard/.sdk/src/Root.ts +63 -39
- package/project/standard/.sdk/src/RootPlan.ts +64 -0
- package/project/standard/.sdk/test/.model-config/model-config.aontu +4 -0
- package/project/standard/.sdk/test/feature/{feature-test-index.aon → feature-test-index.aontu} +1 -1
- package/project/standard/.sdk/test/primary/primary-test-index.aontu +30 -0
- package/project/standard/.sdk/test/struct/test.aontu +26 -0
- package/project/standard/.sdk/test/test.aontu +4 -0
- package/src/project/standard/CreateRoot.ts +45 -54
- package/src/project/standard/ModelSdk.ts +2 -2
- package/src/project/standard/migrate.ts +172 -0
- package/project/standard/.sdk/test/.model-config/model-config.aon +0 -4
- package/project/standard/.sdk/test/primary/primary-test-index.aon +0 -30
- package/project/standard/.sdk/test/struct/test.aon +0 -26
- package/project/standard/.sdk/test/test.aon +0 -4
- /package/project/standard/.sdk/model/{config.aon → config.aontu} +0 -0
- /package/project/standard/.sdk/model/edition/{edition-index.aon → edition-index.aontu} +0 -0
- /package/project/standard/.sdk/model/feature/{feature-index.aon → feature-index.aontu} +0 -0
- /package/project/standard/.sdk/model/target/{target-index.aon → target-index.aontu} +0 -0
- /package/project/standard/.sdk/test/feature/{cost.aon → cost.aontu} +0 -0
- /package/project/standard/.sdk/test/primary/{clean.aon → clean.aontu} +0 -0
- /package/project/standard/.sdk/test/primary/{done.aon → done.aontu} +0 -0
- /package/project/standard/.sdk/test/primary/{featureAdd.aon → featureAdd.aontu} +0 -0
- /package/project/standard/.sdk/test/primary/{featureHook.aon → featureHook.aontu} +0 -0
- /package/project/standard/.sdk/test/primary/{featureInit.aon → featureInit.aontu} +0 -0
- /package/project/standard/.sdk/test/primary/{fetcher.aon → fetcher.aontu} +0 -0
- /package/project/standard/.sdk/test/primary/{makeContext.aon → makeContext.aontu} +0 -0
- /package/project/standard/.sdk/test/primary/{makeError.aon → makeError.aontu} +0 -0
- /package/project/standard/.sdk/test/primary/{makeFetchDef.aon → makeFetchDef.aontu} +0 -0
- /package/project/standard/.sdk/test/primary/{makeOptions.aon → makeOptions.aontu} +0 -0
- /package/project/standard/.sdk/test/primary/{makePoint.aon → makePoint.aontu} +0 -0
- /package/project/standard/.sdk/test/primary/{makeRequest.aon → makeRequest.aontu} +0 -0
- /package/project/standard/.sdk/test/primary/{makeResponse.aon → makeResponse.aontu} +0 -0
- /package/project/standard/.sdk/test/primary/{makeResult.aon → makeResult.aontu} +0 -0
- /package/project/standard/.sdk/test/primary/{makeSpec.aon → makeSpec.aontu} +0 -0
- /package/project/standard/.sdk/test/primary/{makeUrl.aon → makeUrl.aontu} +0 -0
- /package/project/standard/.sdk/test/primary/{operator.aon → operator.aontu} +0 -0
- /package/project/standard/.sdk/test/primary/{param.aon → param.aontu} +0 -0
- /package/project/standard/.sdk/test/primary/{prepareAuth.aon → prepareAuth.aontu} +0 -0
- /package/project/standard/.sdk/test/primary/{prepareBody.aon → prepareBody.aontu} +0 -0
- /package/project/standard/.sdk/test/primary/{prepareHeaders.aon → prepareHeaders.aontu} +0 -0
- /package/project/standard/.sdk/test/primary/{prepareMethod.aon → prepareMethod.aontu} +0 -0
- /package/project/standard/.sdk/test/primary/{prepareParams.aon → prepareParams.aontu} +0 -0
- /package/project/standard/.sdk/test/primary/{preparePath.aon → preparePath.aontu} +0 -0
- /package/project/standard/.sdk/test/primary/{prepareQuery.aon → prepareQuery.aontu} +0 -0
- /package/project/standard/.sdk/test/primary/{resultBasic.aon → resultBasic.aontu} +0 -0
- /package/project/standard/.sdk/test/primary/{resultBody.aon → resultBody.aontu} +0 -0
- /package/project/standard/.sdk/test/primary/{resultHeaders.aon → resultHeaders.aontu} +0 -0
- /package/project/standard/.sdk/test/primary/{transformRequest.aon → transformRequest.aontu} +0 -0
- /package/project/standard/.sdk/test/primary/{transformResponse.aon → transformResponse.aontu} +0 -0
- /package/project/standard/.sdk/test/struct/{getpath.aon → getpath.aontu} +0 -0
- /package/project/standard/.sdk/test/struct/{inject.aon → inject.aontu} +0 -0
- /package/project/standard/.sdk/test/struct/{merge.aon → merge.aontu} +0 -0
- /package/project/standard/.sdk/test/struct/{minor.aon → minor.aontu} +0 -0
- /package/project/standard/.sdk/test/struct/{nullsem.aon → nullsem.aontu} +0 -0
- /package/project/standard/.sdk/test/struct/{select.aon → select.aontu} +0 -0
- /package/project/standard/.sdk/test/struct/{transform.aon → transform.aontu} +0 -0
- /package/project/standard/.sdk/test/struct/{validate.aon → validate.aontu} +0 -0
- /package/project/standard/.sdk/test/struct/{walk.aon → walk.aontu} +0 -0
|
@@ -15,6 +15,8 @@ import {
|
|
|
15
15
|
AgentGuide,
|
|
16
16
|
Test,
|
|
17
17
|
|
|
18
|
+
SdkGenError,
|
|
19
|
+
|
|
18
20
|
} from '@voxgig/sdkgen'
|
|
19
21
|
|
|
20
22
|
import {
|
|
@@ -29,6 +31,7 @@ import { PointUtil, Content } from 'jostraca'
|
|
|
29
31
|
|
|
30
32
|
import { Top } from './Top'
|
|
31
33
|
import { BuildSDK } from './BuildSDK'
|
|
34
|
+
import { rootPlan } from './RootPlan'
|
|
32
35
|
|
|
33
36
|
|
|
34
37
|
const {
|
|
@@ -69,58 +72,79 @@ const Root = cmp(function Root(props: any) {
|
|
|
69
72
|
ctx$.stdrep = {}
|
|
70
73
|
names(ctx$.stdrep, model.Name, 'Project' + 'Name')
|
|
71
74
|
|
|
75
|
+
const plan = rootPlan(model.main[KIT], SdkGenError)
|
|
76
|
+
|
|
72
77
|
Project({}, () => {
|
|
73
78
|
|
|
74
|
-
|
|
79
|
+
if (plan.top) {
|
|
80
|
+
Top({})
|
|
81
|
+
}
|
|
75
82
|
|
|
76
|
-
|
|
83
|
+
if (plan.build) {
|
|
84
|
+
BuildSDK({})
|
|
85
|
+
}
|
|
77
86
|
|
|
78
87
|
each(target, (target: any) => {
|
|
88
|
+
const place = plan.place[target.name]
|
|
89
|
+
|
|
90
|
+
if (null == place) {
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
|
|
79
94
|
names(target, target.name)
|
|
80
95
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
names(entity, entity.name)
|
|
90
|
-
Entity({ target, entity })
|
|
91
|
-
})
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (phaseActive('feature')) {
|
|
95
|
-
each(feature).filter((feature: any) => feature.active).map((feature: any) => {
|
|
96
|
-
names(feature, feature.name)
|
|
97
|
-
Feature({ target, feature })
|
|
98
|
-
})
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
Main({ target })
|
|
102
|
-
|
|
103
|
-
if (phaseActive('readme')) {
|
|
104
|
-
Readme({ target })
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Per-target agent guides: <lang>/AGENTS.md + CLAUDE.md, and (driven
|
|
108
|
-
// internally by AgentGuide) a guide per active feature under
|
|
109
|
-
// <lang>/src/feature/<name>/. Placement mirrors Readme.
|
|
110
|
-
if (phaseActive('agentguide')) {
|
|
111
|
-
AgentGuide({ target })
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (phaseActive('test')) {
|
|
115
|
-
Test({ target })
|
|
116
|
-
}
|
|
117
|
-
})
|
|
96
|
+
if ('root' === place) {
|
|
97
|
+
targetPhases(target, entity, feature)
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
Folder({ name: target.name }, () => {
|
|
101
|
+
targetPhases(target, entity, feature)
|
|
102
|
+
})
|
|
103
|
+
}
|
|
118
104
|
})
|
|
119
105
|
|
|
120
106
|
})
|
|
121
107
|
})
|
|
122
108
|
|
|
123
109
|
|
|
110
|
+
function targetPhases(target: any, entity: any, feature: any) {
|
|
111
|
+
const phase = target.phase || {}
|
|
112
|
+
const phaseActive = (name: string): boolean =>
|
|
113
|
+
false !== (phase[name] && phase[name].active)
|
|
114
|
+
|
|
115
|
+
if (phaseActive('entity')) {
|
|
116
|
+
each(entity).filter((entity: any) => entity.active).map((entity: any) => {
|
|
117
|
+
names(entity, entity.name)
|
|
118
|
+
Entity({ target, entity })
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (phaseActive('feature')) {
|
|
123
|
+
each(feature).filter((feature: any) => feature.active).map((feature: any) => {
|
|
124
|
+
names(feature, feature.name)
|
|
125
|
+
Feature({ target, feature })
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
Main({ target })
|
|
130
|
+
|
|
131
|
+
if (phaseActive('readme')) {
|
|
132
|
+
Readme({ target })
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Per-target agent guides: <lang>/AGENTS.md + CLAUDE.md, and (driven
|
|
136
|
+
// internally by AgentGuide) a guide per active feature under
|
|
137
|
+
// <lang>/src/feature/<name>/. Placement mirrors Readme.
|
|
138
|
+
if (phaseActive('agentguide')) {
|
|
139
|
+
AgentGuide({ target })
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (phaseActive('test')) {
|
|
143
|
+
Test({ target })
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
|
|
124
148
|
function makeFlow(def: any, data: any, stepMakers: Record<string, any>) {
|
|
125
149
|
|
|
126
150
|
const steps: any = {}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
|
|
2
|
+
// Import-free, so the scaffold's own tests can load it without the toolchain.
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
type Place = 'folder' | 'root'
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
type RootPlan = {
|
|
9
|
+
top: boolean
|
|
10
|
+
build: boolean
|
|
11
|
+
|
|
12
|
+
// Generated targets only: absent means not generated at all.
|
|
13
|
+
place: Record<string, Place>
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
type Fail = new (message: string) => Error
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
function rootPlan(kit: any, Fail: Fail = Error): RootPlan {
|
|
21
|
+
const phase = kit?.phase || {}
|
|
22
|
+
const phaseActive = (name: string): boolean =>
|
|
23
|
+
false !== (phase[name] && phase[name].active)
|
|
24
|
+
|
|
25
|
+
const top = phaseActive('top')
|
|
26
|
+
const build = phaseActive('build')
|
|
27
|
+
|
|
28
|
+
const target = kit?.target || {}
|
|
29
|
+
const place: Record<string, Place> = {}
|
|
30
|
+
|
|
31
|
+
for (const name of Object.keys(target).sort()) {
|
|
32
|
+
if (null != target[name] && false !== target[name].active) {
|
|
33
|
+
place[name] = true === target[name].output?.root ? 'root' : 'folder'
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const atRoot = Object.keys(place).filter((name: string) => 'root' === place[name])
|
|
38
|
+
|
|
39
|
+
if (1 < atRoot.length) {
|
|
40
|
+
throw new Fail(
|
|
41
|
+
'Only one target can be generated at the project root, and ' +
|
|
42
|
+
atRoot.length + ' declare `output: root: true`: ' + atRoot.join(', ') + '.')
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (1 === atRoot.length && top) {
|
|
46
|
+
throw new Fail(
|
|
47
|
+
'Target "' + atRoot[0] + '" is generated at the project root, where the ' +
|
|
48
|
+
'SDK repository files (README, LICENSE, Makefile, workflows) would ' +
|
|
49
|
+
'overwrite its own. Declare `main: kit: phase: top: active: false` in ' +
|
|
50
|
+
'model/project.aontu.')
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return { top, build, place }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
export type {
|
|
58
|
+
Place,
|
|
59
|
+
RootPlan,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export {
|
|
63
|
+
rootPlan,
|
|
64
|
+
}
|
package/project/standard/.sdk/test/feature/{feature-test-index.aon → feature-test-index.aontu}
RENAMED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
# Feature behaviour cases, one section per feature.
|
|
2
|
-
feature: cost: @"./cost.
|
|
2
|
+
feature: cost: @"./cost.aontu"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
primary: clean: @"./clean.aontu"
|
|
2
|
+
primary: done: @"./done.aontu"
|
|
3
|
+
primary: featureAdd: @"./featureAdd.aontu"
|
|
4
|
+
primary: featureHook: @"./featureHook.aontu"
|
|
5
|
+
primary: featureInit: @"./featureInit.aontu"
|
|
6
|
+
primary: fetcher: @"./fetcher.aontu"
|
|
7
|
+
primary: makeContext: @"./makeContext.aontu"
|
|
8
|
+
primary: makeError: @"./makeError.aontu"
|
|
9
|
+
primary: makeFetchDef: @"./makeFetchDef.aontu"
|
|
10
|
+
primary: makeOptions: @"./makeOptions.aontu"
|
|
11
|
+
primary: makeRequest: @"./makeRequest.aontu"
|
|
12
|
+
primary: makeResponse: @"./makeResponse.aontu"
|
|
13
|
+
primary: makeResult: @"./makeResult.aontu"
|
|
14
|
+
primary: makeSpec: @"./makeSpec.aontu"
|
|
15
|
+
primary: makePoint: @"./makePoint.aontu"
|
|
16
|
+
primary: makeUrl: @"./makeUrl.aontu"
|
|
17
|
+
primary: operator: @"./operator.aontu"
|
|
18
|
+
primary: param: @"./param.aontu"
|
|
19
|
+
primary: prepareAuth: @"./prepareAuth.aontu"
|
|
20
|
+
primary: prepareBody: @"./prepareBody.aontu"
|
|
21
|
+
primary: prepareHeaders: @"./prepareHeaders.aontu"
|
|
22
|
+
primary: prepareMethod: @"./prepareMethod.aontu"
|
|
23
|
+
primary: prepareParams: @"./prepareParams.aontu"
|
|
24
|
+
primary: preparePath: @"./preparePath.aontu"
|
|
25
|
+
primary: prepareQuery: @"./prepareQuery.aontu"
|
|
26
|
+
primary: resultBasic: @"./resultBasic.aontu"
|
|
27
|
+
primary: resultBody: @"./resultBody.aontu"
|
|
28
|
+
primary: resultHeaders: @"./resultHeaders.aontu"
|
|
29
|
+
primary: transformRequest: @"./transformRequest.aontu"
|
|
30
|
+
primary: transformResponse: @"./transformResponse.aontu"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
|
|
2
|
+
struct: &: {
|
|
3
|
+
name: key()
|
|
4
|
+
set: []
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
struct: minor: @"./minor.aontu"
|
|
8
|
+
struct: getpath: @"./getpath.aontu"
|
|
9
|
+
struct: inject: @"./inject.aontu"
|
|
10
|
+
struct: merge: @"./merge.aontu"
|
|
11
|
+
struct: transform: @"./transform.aontu"
|
|
12
|
+
struct: walk: @"./walk.aontu"
|
|
13
|
+
struct: validate: @"./validate.aontu"
|
|
14
|
+
struct: select: @"./select.aontu"
|
|
15
|
+
struct: nullsem: @"./nullsem.aontu"
|
|
16
|
+
|
|
17
|
+
primary: check: {
|
|
18
|
+
DEF: {
|
|
19
|
+
client:a:test:options:foo:1
|
|
20
|
+
}
|
|
21
|
+
basic: set: [
|
|
22
|
+
{ ctx:meta:bar:BAR0, out:zed:ZED_BAR0 }
|
|
23
|
+
{ ctx:meta:bar:BAR1, client:a, out:zed:ZED1_BAR1 }
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
import { ModelSdk } from './ModelSdk'
|
|
19
|
+
import { migrateToAontu, scaffoldFiles } from './migrate'
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
const GITIGNORE_TOP = `# Local config / secrets
|
|
@@ -61,15 +62,16 @@ log/
|
|
|
61
62
|
`
|
|
62
63
|
|
|
63
64
|
|
|
64
|
-
const PROJECT_FILE = 'project.
|
|
65
|
+
const PROJECT_FILE = 'project.aontu'
|
|
65
66
|
const PROJECT_STUB = `# Project overlay — YOURS. The scaffold creates this file once and never
|
|
66
|
-
# overwrites it
|
|
67
|
+
# overwrites it.
|
|
67
68
|
#
|
|
68
|
-
#
|
|
69
|
-
#
|
|
70
|
-
#
|
|
69
|
+
# Most of model/ is toolchain-derived and is deliberately regenerated so that
|
|
70
|
+
# toolchain fixes propagate; this file, the guide, and the target, feature and
|
|
71
|
+
# edition indexes are kept. Put anything here that is a decision about THIS
|
|
72
|
+
# project rather than a fact about the API.
|
|
71
73
|
#
|
|
72
|
-
# Included LAST by sdk.
|
|
74
|
+
# Included LAST by sdk.aontu, after target/target-index.aontu, because a key
|
|
73
75
|
# under main.kit.target.<t> can only refine a target that has already been
|
|
74
76
|
# defined. Declared earlier, the model build fails with "Cannot unify value:
|
|
75
77
|
# nil with value: string / key ext value was: nil", which names nothing that
|
|
@@ -92,37 +94,15 @@ const PROJECT_STUB = `# Project overlay — YOURS. The scaffold creates this fil
|
|
|
92
94
|
const GUIDE_FILE = 'guide.aontu'
|
|
93
95
|
const GUIDE_REL = ['model', 'guide', GUIDE_FILE]
|
|
94
96
|
|
|
97
|
+
const INDEX_KINDS = ['target', 'feature', 'edition']
|
|
98
|
+
const indexFile = (kind: string) => kind + '-index.aontu'
|
|
95
99
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
function migrateGuideIncludes(src: string): string {
|
|
103
|
-
return src.replace(
|
|
104
|
-
/(@(['"])(?:@voxgig\/apidef\/model\/[^'"]+|(?:[^'"]*\/)?[^'"/]*base-guide))\.aon\2/g,
|
|
105
|
-
'$1.aontu$2')
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
function migrateOverlay(
|
|
110
|
-
fs: any, dir: string, name: string, from: string, to: string,
|
|
111
|
-
rewrite: (src: string) => string = (src) => renameIncludes(src, from, to)): void {
|
|
112
|
-
const next = Path.join(dir, name + '.' + to)
|
|
113
|
-
const prev = Path.join(dir, name + '.' + from)
|
|
114
|
-
if (fs.existsSync(next) || !fs.existsSync(prev)) {
|
|
115
|
-
return
|
|
116
|
-
}
|
|
117
|
-
try {
|
|
118
|
-
fs.writeFileSync(next, rewrite(String(fs.readFileSync(prev))))
|
|
119
|
-
fs.unlinkSync(prev)
|
|
120
|
-
}
|
|
121
|
-
catch (_err: any) {
|
|
122
|
-
// A failed migration must not fail the scaffold: the worst case is the
|
|
123
|
-
// template being written fresh, which is what would have happened anyway.
|
|
124
|
-
}
|
|
125
|
-
}
|
|
100
|
+
// Written back from the project rather than from the template.
|
|
101
|
+
const KEPT = [
|
|
102
|
+
['model', PROJECT_FILE],
|
|
103
|
+
GUIDE_REL,
|
|
104
|
+
...INDEX_KINDS.map((kind: string) => ['model', kind, indexFile(kind)]),
|
|
105
|
+
].map((rel: string[]) => rel.join('/'))
|
|
126
106
|
|
|
127
107
|
|
|
128
108
|
function mergeGuide(existing: string | null, template: string): string {
|
|
@@ -181,10 +161,12 @@ const CreateRoot = cmp(function CreateRoot(props: any) {
|
|
|
181
161
|
|
|
182
162
|
|
|
183
163
|
const guideExclude = [spec.sdk_folder, ...GUIDE_REL].join('/')
|
|
164
|
+
const indexExclude = INDEX_KINDS.map((kind: string) =>
|
|
165
|
+
[spec.sdk_folder, 'model', kind, indexFile(kind)].join('/'))
|
|
184
166
|
|
|
185
167
|
Copy({
|
|
186
168
|
from,
|
|
187
|
-
exclude: [/\.fragment\./, guideExclude, /^\.sdk\/admin\/.*\.sh$/]
|
|
169
|
+
exclude: [/\.fragment\./, guideExclude, ...indexExclude, /^\.sdk\/admin\/.*\.sh$/]
|
|
188
170
|
})
|
|
189
171
|
|
|
190
172
|
File({ name: '.gitignore' }, () => {
|
|
@@ -196,6 +178,16 @@ const CreateRoot = cmp(function CreateRoot(props: any) {
|
|
|
196
178
|
spec.def = projdef
|
|
197
179
|
|
|
198
180
|
Folder({ name: spec.sdk_folder }, () => {
|
|
181
|
+
// Before anything reads the project's own files, so a project from the
|
|
182
|
+
// .aon era is read under the names it now has.
|
|
183
|
+
if (!spec.dryrun) {
|
|
184
|
+
const sdk = Path.join(folder, spec.sdk_folder)
|
|
185
|
+
migrateToAontu(fs, sdk, scaffoldFiles(fs, Path.join(from, spec.sdk_folder), KEPT),
|
|
186
|
+
(file: string, note: string) => ctx$.log.warn({
|
|
187
|
+
point: 'migrate-aontu', file: Path.join(sdk, file), note: file + ': ' + note,
|
|
188
|
+
}))
|
|
189
|
+
}
|
|
190
|
+
|
|
199
191
|
Folder({ name: 'admin' }, () => {
|
|
200
192
|
const admin = Path.join(from, spec.sdk_folder, 'admin')
|
|
201
193
|
for (const name of fs.readdirSync(admin).filter((name: string) => name.endsWith('.sh')).sort()) {
|
|
@@ -227,12 +219,6 @@ const CreateRoot = cmp(function CreateRoot(props: any) {
|
|
|
227
219
|
const projectPath =
|
|
228
220
|
Path.join(folder, spec.sdk_folder, 'model', PROJECT_FILE)
|
|
229
221
|
|
|
230
|
-
// Same hazard, worse symptom: this file carries the release version,
|
|
231
|
-
// so an ignored project.aontu silently resets every generated manifest
|
|
232
|
-
// to the sdkgen default 0.0.1 — the exact bug fixed earlier this week.
|
|
233
|
-
if (!spec.dryrun) {
|
|
234
|
-
migrateOverlay(fs, Path.dirname(projectPath), 'project', 'aontu', 'aon')
|
|
235
|
-
}
|
|
236
222
|
const existingProject =
|
|
237
223
|
fs.existsSync(projectPath) ? fs.readFileSync(projectPath, 'utf8') : null
|
|
238
224
|
|
|
@@ -240,6 +226,20 @@ const CreateRoot = cmp(function CreateRoot(props: any) {
|
|
|
240
226
|
Content(null == existingProject ? PROJECT_STUB : existingProject)
|
|
241
227
|
})
|
|
242
228
|
|
|
229
|
+
// `target add`, `feature add` and docgen register their items here, and
|
|
230
|
+
// docgen bootstraps only once, so a reset index would lose them for good.
|
|
231
|
+
for (const kind of INDEX_KINDS) {
|
|
232
|
+
Folder({ name: kind }, () => {
|
|
233
|
+
const name = indexFile(kind)
|
|
234
|
+
const indexPath = Path.join(folder, spec.sdk_folder, 'model', kind, name)
|
|
235
|
+
File({ name }, () => {
|
|
236
|
+
Content(fs.existsSync(indexPath) ?
|
|
237
|
+
fs.readFileSync(indexPath, 'utf8') :
|
|
238
|
+
fs.readFileSync(Path.join(from, spec.sdk_folder, 'model', kind, name), 'utf8'))
|
|
239
|
+
})
|
|
240
|
+
})
|
|
241
|
+
}
|
|
242
|
+
|
|
243
243
|
// Re-emit the guide the Copy skipped, merged over whatever is already
|
|
244
244
|
// there. On a fresh scaffold there is no existing file and this writes
|
|
245
245
|
// the template unchanged; on a re-scaffold the user's overlay survives.
|
|
@@ -247,17 +247,8 @@ const CreateRoot = cmp(function CreateRoot(props: any) {
|
|
|
247
247
|
const guideTemplate =
|
|
248
248
|
fs.readFileSync(Path.join(from, spec.sdk_folder, ...GUIDE_REL), 'utf8')
|
|
249
249
|
const guidePath = Path.join(folder, spec.sdk_folder, ...GUIDE_REL)
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
// still named .aon reads as having no overlay at all.
|
|
253
|
-
if (!spec.dryrun) {
|
|
254
|
-
migrateOverlay(fs, Path.dirname(guidePath), 'guide', 'aon', 'aontu',
|
|
255
|
-
migrateGuideIncludes)
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// An entry already named .aontu can still include a retired .aon file.
|
|
259
|
-
const existingGuide = fs.existsSync(guidePath) ?
|
|
260
|
-
migrateGuideIncludes(fs.readFileSync(guidePath, 'utf8')) : null
|
|
250
|
+
const existingGuide =
|
|
251
|
+
fs.existsSync(guidePath) ? fs.readFileSync(guidePath, 'utf8') : null
|
|
261
252
|
|
|
262
253
|
File({ name: GUIDE_FILE }, () => {
|
|
263
254
|
Content(mergeGuide(existingGuide, guideTemplate))
|
|
@@ -17,9 +17,9 @@ const ModelSdk = cmp(function ModelSdk(props: any) {
|
|
|
17
17
|
const from =
|
|
18
18
|
Path.resolve(Path.join(__dirname, '..', '..', '..', 'project', 'standard'))
|
|
19
19
|
|
|
20
|
-
File({ name: 'sdk.
|
|
20
|
+
File({ name: 'sdk.aontu' }, () => {
|
|
21
21
|
Fragment({
|
|
22
|
-
from: Path.join(from, spec.sdk_folder, 'model', 'sdk.fragment.
|
|
22
|
+
from: Path.join(from, spec.sdk_folder, 'model', 'sdk.fragment.aontu'),
|
|
23
23
|
replace: {
|
|
24
24
|
ProjectName: spec.name,
|
|
25
25
|
NAME: spec.name,
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import Path from 'node:path'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
const TREES = ['model', 'test']
|
|
5
|
+
|
|
6
|
+
const LEGACY = '.aon'
|
|
7
|
+
const CURRENT = '.aontu'
|
|
8
|
+
|
|
9
|
+
const TOOLCHAIN_RE = /^@voxgig\//
|
|
10
|
+
const APIDEF_WRITES_RE = /^model\/guide\/[^/]*base-guide\.aontu$/
|
|
11
|
+
|
|
12
|
+
// Comments and strings are matched whole, so an include is only ever an `@`
|
|
13
|
+
// outside both: a `.aon` named in a comment or held as data is not rewritten.
|
|
14
|
+
const TOKEN_RE =
|
|
15
|
+
/#[^\n]*|@([ \t]*)(["'`])([^"'`\n]*)\2|"(?:\\.|[^"\\\n])*"|'(?:\\.|[^'\\\n])*'|`(?:\\.|[^`\\])*`/g
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
type Warn = (file: string, note: string) => void
|
|
19
|
+
|
|
20
|
+
type Scaffold = {
|
|
21
|
+
kept: Set<string>
|
|
22
|
+
replaced: Set<string>
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
function rewriteIncludes(src: string, rename: (path: string) => boolean): string {
|
|
27
|
+
return src.replace(TOKEN_RE, (token: string, space: string, quote: string, path: string) =>
|
|
28
|
+
null != quote && path.endsWith(LEGACY) && rename(path) ?
|
|
29
|
+
'@' + space + quote + path.slice(0, -LEGACY.length) + CURRENT + quote : token)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
function legacyIncludes(src: string): string[] {
|
|
34
|
+
return [...src.matchAll(TOKEN_RE)]
|
|
35
|
+
.filter((m) => null != m[2] && m[3].endsWith(LEGACY))
|
|
36
|
+
.map((m) => m[3])
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
function modelFiles(fs: any, sdk: string, ext: string): string[] {
|
|
41
|
+
const found: string[] = []
|
|
42
|
+
const walk = (rel: string) => {
|
|
43
|
+
let entries: any[]
|
|
44
|
+
try {
|
|
45
|
+
entries = fs.readdirSync(Path.join(sdk, rel), { withFileTypes: true })
|
|
46
|
+
}
|
|
47
|
+
catch (_err: any) {
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
for (const entry of entries) {
|
|
51
|
+
const child = rel + '/' + entry.name
|
|
52
|
+
if (entry.isDirectory()) {
|
|
53
|
+
walk(child)
|
|
54
|
+
}
|
|
55
|
+
else if (entry.isFile() && entry.name.endsWith(ext)) {
|
|
56
|
+
found.push(child)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
TREES.forEach(walk)
|
|
61
|
+
return found.sort()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
function scaffoldFiles(fs: any, templateSdk: string, kept: string[]): Scaffold {
|
|
66
|
+
const keep = new Set(kept)
|
|
67
|
+
const written = modelFiles(fs, templateSdk, CURRENT)
|
|
68
|
+
.map((rel: string) => rel.replace('.fragment.', '.'))
|
|
69
|
+
return { kept: keep, replaced: new Set(written.filter((rel) => !keep.has(rel))) }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
// A failed step must not fail the scaffold: the file keeps the name it had.
|
|
74
|
+
function attempt(step: () => void): void {
|
|
75
|
+
try {
|
|
76
|
+
step()
|
|
77
|
+
}
|
|
78
|
+
catch (_err: any) {
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
// Written beside the target and renamed over it, so a write that fails part
|
|
84
|
+
// way leaves the file it would have replaced whole.
|
|
85
|
+
function writeWhole(fs: any, path: string, data: any): void {
|
|
86
|
+
const tmp = path + '.migrating'
|
|
87
|
+
try {
|
|
88
|
+
fs.writeFileSync(tmp, data)
|
|
89
|
+
fs.renameSync(tmp, path)
|
|
90
|
+
}
|
|
91
|
+
catch (err: any) {
|
|
92
|
+
attempt(() => fs.unlinkSync(tmp))
|
|
93
|
+
throw err
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
function migrateFile(fs: any, prev: string, next: string): void {
|
|
99
|
+
if (fs.existsSync(next) || !fs.existsSync(prev)) {
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
attempt(() => {
|
|
103
|
+
writeWhole(fs, next, fs.readFileSync(prev))
|
|
104
|
+
fs.unlinkSync(prev)
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
function includeRenamer(fs: any, sdk: string, scaffold: Scaffold, from: string) {
|
|
110
|
+
const dir = Path.dirname(Path.join(sdk, from))
|
|
111
|
+
return (path: string): boolean => {
|
|
112
|
+
if (TOOLCHAIN_RE.test(path)) {
|
|
113
|
+
return true
|
|
114
|
+
}
|
|
115
|
+
if (path.startsWith('@')) {
|
|
116
|
+
return false
|
|
117
|
+
}
|
|
118
|
+
const twin = Path.resolve(dir, path.slice(0, -LEGACY.length) + CURRENT)
|
|
119
|
+
const rel = Path.relative(sdk, twin).split(Path.sep).join('/')
|
|
120
|
+
return fs.existsSync(twin) || scaffold.kept.has(rel) || scaffold.replaced.has(rel) ||
|
|
121
|
+
APIDEF_WRITES_RE.test(rel)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
// Renames first and rewrites after, so an include changes only once the file
|
|
127
|
+
// it names is on disk as .aontu.
|
|
128
|
+
function migrateToAontu(fs: any, sdk: string, scaffold: Scaffold, warn: Warn): void {
|
|
129
|
+
const legacy = modelFiles(fs, sdk, LEGACY)
|
|
130
|
+
const twin = (rel: string) => rel.slice(0, -LEGACY.length) + CURRENT
|
|
131
|
+
const abs = (rel: string) => Path.join(sdk, rel)
|
|
132
|
+
|
|
133
|
+
for (const rel of legacy.filter((rel) => !scaffold.replaced.has(twin(rel)))) {
|
|
134
|
+
migrateFile(fs, abs(rel), abs(twin(rel)))
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const sources = modelFiles(fs, sdk, CURRENT).filter((rel) => !scaffold.replaced.has(rel))
|
|
138
|
+
for (const rel of sources) {
|
|
139
|
+
attempt(() => {
|
|
140
|
+
const src = String(fs.readFileSync(abs(rel)))
|
|
141
|
+
const out = rewriteIncludes(src, includeRenamer(fs, sdk, scaffold, rel))
|
|
142
|
+
if (out !== src) {
|
|
143
|
+
writeWhole(fs, abs(rel), out)
|
|
144
|
+
}
|
|
145
|
+
})
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
for (const rel of legacy.filter((rel) => scaffold.replaced.has(twin(rel)))) {
|
|
149
|
+
attempt(() => fs.unlinkSync(abs(rel)))
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
for (const rel of modelFiles(fs, sdk, LEGACY)) {
|
|
153
|
+
warn(rel, fs.existsSync(abs(twin(rel))) ?
|
|
154
|
+
'left in place: ' + twin(rel) + ' exists, and is the file aontu reads' :
|
|
155
|
+
'could not be migrated to ' + twin(rel))
|
|
156
|
+
}
|
|
157
|
+
for (const rel of sources) {
|
|
158
|
+
attempt(() => {
|
|
159
|
+
for (const path of legacyIncludes(String(fs.readFileSync(abs(rel))))) {
|
|
160
|
+
warn(rel, 'includes ' + path + ', which aontu refuses: rename that file ' +
|
|
161
|
+
'to .aontu and update the include')
|
|
162
|
+
}
|
|
163
|
+
})
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
export {
|
|
169
|
+
migrateToAontu,
|
|
170
|
+
rewriteIncludes,
|
|
171
|
+
scaffoldFiles,
|
|
172
|
+
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
primary: clean: @"./clean.aon"
|
|
2
|
-
primary: done: @"./done.aon"
|
|
3
|
-
primary: featureAdd: @"./featureAdd.aon"
|
|
4
|
-
primary: featureHook: @"./featureHook.aon"
|
|
5
|
-
primary: featureInit: @"./featureInit.aon"
|
|
6
|
-
primary: fetcher: @"./fetcher.aon"
|
|
7
|
-
primary: makeContext: @"./makeContext.aon"
|
|
8
|
-
primary: makeError: @"./makeError.aon"
|
|
9
|
-
primary: makeFetchDef: @"./makeFetchDef.aon"
|
|
10
|
-
primary: makeOptions: @"./makeOptions.aon"
|
|
11
|
-
primary: makeRequest: @"./makeRequest.aon"
|
|
12
|
-
primary: makeResponse: @"./makeResponse.aon"
|
|
13
|
-
primary: makeResult: @"./makeResult.aon"
|
|
14
|
-
primary: makeSpec: @"./makeSpec.aon"
|
|
15
|
-
primary: makePoint: @"./makePoint.aon"
|
|
16
|
-
primary: makeUrl: @"./makeUrl.aon"
|
|
17
|
-
primary: operator: @"./operator.aon"
|
|
18
|
-
primary: param: @"./param.aon"
|
|
19
|
-
primary: prepareAuth: @"./prepareAuth.aon"
|
|
20
|
-
primary: prepareBody: @"./prepareBody.aon"
|
|
21
|
-
primary: prepareHeaders: @"./prepareHeaders.aon"
|
|
22
|
-
primary: prepareMethod: @"./prepareMethod.aon"
|
|
23
|
-
primary: prepareParams: @"./prepareParams.aon"
|
|
24
|
-
primary: preparePath: @"./preparePath.aon"
|
|
25
|
-
primary: prepareQuery: @"./prepareQuery.aon"
|
|
26
|
-
primary: resultBasic: @"./resultBasic.aon"
|
|
27
|
-
primary: resultBody: @"./resultBody.aon"
|
|
28
|
-
primary: resultHeaders: @"./resultHeaders.aon"
|
|
29
|
-
primary: transformRequest: @"./transformRequest.aon"
|
|
30
|
-
primary: transformResponse: @"./transformResponse.aon"
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
struct: &: {
|
|
3
|
-
name: key()
|
|
4
|
-
set: []
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
struct: minor: @"./minor.aon"
|
|
8
|
-
struct: getpath: @"./getpath.aon"
|
|
9
|
-
struct: inject: @"./inject.aon"
|
|
10
|
-
struct: merge: @"./merge.aon"
|
|
11
|
-
struct: transform: @"./transform.aon"
|
|
12
|
-
struct: walk: @"./walk.aon"
|
|
13
|
-
struct: validate: @"./validate.aon"
|
|
14
|
-
struct: select: @"./select.aon"
|
|
15
|
-
struct: nullsem: @"./nullsem.aon"
|
|
16
|
-
|
|
17
|
-
primary: check: {
|
|
18
|
-
DEF: {
|
|
19
|
-
client:a:test:options:foo:1
|
|
20
|
-
}
|
|
21
|
-
basic: set: [
|
|
22
|
-
{ ctx:meta:bar:BAR0, out:zed:ZED_BAR0 }
|
|
23
|
-
{ ctx:meta:bar:BAR1, client:a, out:zed:ZED1_BAR1 }
|
|
24
|
-
]
|
|
25
|
-
}
|
|
26
|
-
|