@voxgig/create-sdkgen 0.28.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/package.json
CHANGED
|
@@ -38,7 +38,9 @@ test, and the only symptom is a tree that disagrees with the model:
|
|
|
38
38
|
anything whose output might be committed.
|
|
39
39
|
|
|
40
40
|
The script deletes every target tree, regenerates, and reports what differs —
|
|
41
|
-
which is the only way to see output the generator no longer emits.
|
|
41
|
+
which is the only way to see output the generator no longer emits. A target
|
|
42
|
+
generated at the project root (`output: root: true`) owns every tracked file
|
|
43
|
+
outside `.sdk/`, so those are what it deletes for that target. It refuses
|
|
42
44
|
to run on a dirty tree, restores the target's own build artifacts and
|
|
43
45
|
lockfiles (which the generator never writes), and leaves the regenerated tree
|
|
44
46
|
in place so the diff can be read.
|
|
@@ -52,17 +52,34 @@ if [[ -n "$(git status --porcelain)" ]]; then
|
|
|
52
52
|
exit 2
|
|
53
53
|
fi
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
# Each target generated in a folder of its own, and the one, if any, that
|
|
56
|
+
# declares `output: root: true`.
|
|
57
|
+
list_targets() {
|
|
58
|
+
node -e '
|
|
59
|
+
const m = require("./.sdk/model/sdk.json")
|
|
60
|
+
const t = (m.main && m.main.kit && m.main.kit.target) || {}
|
|
61
|
+
const root = "root" === process.argv[1]
|
|
62
|
+
console.log(Object.keys(t).sort().filter((n) =>
|
|
63
|
+
root === (true === (t[n] && t[n].output && t[n].output.root))).join("\n"))
|
|
64
|
+
' "$1"
|
|
65
|
+
}
|
|
66
|
+
targets="$(list_targets folder)"
|
|
67
|
+
atroot="$(list_targets root)"
|
|
60
68
|
|
|
61
69
|
echo 'check-drift: deleting and regenerating every target ...'
|
|
62
70
|
while IFS= read -r t; do
|
|
63
71
|
[[ -n "$t" && -d "$t" ]] && rm -rf -- "$t"
|
|
64
72
|
done <<< "$targets"
|
|
65
73
|
|
|
74
|
+
# A target generated at the root owns the repository outside .sdk/, as the
|
|
75
|
+
# others own their folders. Only tracked files go: what git ignores there,
|
|
76
|
+
# such as installed dependencies, is not output.
|
|
77
|
+
if [[ -n "$atroot" ]]; then
|
|
78
|
+
git ls-files -z -- . ':(exclude).sdk' | while IFS= read -r -d '' f; do
|
|
79
|
+
rm -f -- "$f"
|
|
80
|
+
done
|
|
81
|
+
fi
|
|
82
|
+
|
|
66
83
|
log="$(mktemp)"
|
|
67
84
|
trap 'rm -f "$log"' EXIT
|
|
68
85
|
if ! ( cd "$sdk_dir" && npm run generate ) >"$log" 2>&1; then
|
|
@@ -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
|
+
}
|