@voxgig/sdkgen 3.4.7 → 3.5.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/bin/voxgig-sdkgen +78 -10
- package/dist/action/action.d.ts +5 -2
- package/dist/action/action.js +113 -6
- package/dist/action/action.js.map +1 -1
- package/dist/action/check.d.ts +15 -0
- package/dist/action/check.js +347 -0
- package/dist/action/check.js.map +1 -0
- package/dist/action/dispatch.d.ts +8 -0
- package/dist/action/dispatch.js +74 -0
- package/dist/action/dispatch.js.map +1 -0
- package/dist/action/docs.d.ts +4 -0
- package/dist/action/docs.js +236 -0
- package/dist/action/docs.js.map +1 -0
- package/dist/action/doctor.d.ts +5 -2
- package/dist/action/doctor.js +526 -74
- package/dist/action/doctor.js.map +1 -1
- package/dist/action/feature.js +96 -17
- package/dist/action/feature.js.map +1 -1
- package/dist/action/kind.d.ts +30 -0
- package/dist/action/kind.js +258 -0
- package/dist/action/kind.js.map +1 -0
- package/dist/action/package.d.ts +25 -0
- package/dist/action/package.js +852 -0
- package/dist/action/package.js.map +1 -0
- package/dist/action/resolve.d.ts +18 -0
- package/dist/action/resolve.js +280 -0
- package/dist/action/resolve.js.map +1 -0
- package/dist/action/target.d.ts +7 -1
- package/dist/action/target.js +240 -97
- package/dist/action/target.js.map +1 -1
- package/dist/cmp/Docs.d.ts +4 -0
- package/dist/cmp/Docs.js +98 -0
- package/dist/cmp/Docs.js.map +1 -0
- package/dist/cmp/ExternalDocs.d.ts +2 -0
- package/dist/cmp/ExternalDocs.js +43 -0
- package/dist/cmp/ExternalDocs.js.map +1 -0
- package/dist/helpers/definition.d.ts +5 -0
- package/dist/helpers/definition.js +72 -0
- package/dist/helpers/definition.js.map +1 -0
- package/dist/helpers/featureSource.d.ts +8 -2
- package/dist/helpers/featureSource.js +61 -12
- package/dist/helpers/featureSource.js.map +1 -1
- package/dist/helpers/junk.d.ts +11 -0
- package/dist/helpers/junk.js +149 -0
- package/dist/helpers/junk.js.map +1 -0
- package/dist/helpers/manifest.d.ts +50 -0
- package/dist/helpers/manifest.js +328 -0
- package/dist/helpers/manifest.js.map +1 -0
- package/dist/helpers/modelcheck.d.ts +22 -0
- package/dist/helpers/modelcheck.js +203 -0
- package/dist/helpers/modelcheck.js.map +1 -0
- package/dist/helpers/semver.d.ts +4 -0
- package/dist/helpers/semver.js +174 -0
- package/dist/helpers/semver.js.map +1 -0
- package/dist/helpers/shipped.d.ts +4 -0
- package/dist/helpers/shipped.js +36 -0
- package/dist/helpers/shipped.js.map +1 -0
- package/dist/helpers/stdrep.d.ts +9 -1
- package/dist/helpers/stdrep.js +22 -0
- package/dist/helpers/stdrep.js.map +1 -1
- package/dist/sdkgen.d.ts +15 -3
- package/dist/sdkgen.js +233 -67
- package/dist/sdkgen.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types.d.ts +10 -2
- package/dist/utility.js +17 -0
- package/dist/utility.js.map +1 -1
- package/model/sdkgen.aontu +69 -0
- package/package.json +3 -2
- package/project/.sdk/model/feature/audit.aontu +1 -0
- package/project/.sdk/model/feature/cache.aontu +1 -0
- package/project/.sdk/model/feature/clienttrack.aontu +1 -0
- package/project/.sdk/model/feature/debug.aontu +1 -0
- package/project/.sdk/model/feature/idempotency.aontu +1 -0
- package/project/.sdk/model/feature/log.aontu +1 -0
- package/project/.sdk/model/feature/metrics.aontu +1 -0
- package/project/.sdk/model/feature/netsim.aontu +1 -0
- package/project/.sdk/model/feature/paging.aontu +1 -0
- package/project/.sdk/model/feature/proxy.aontu +1 -0
- package/project/.sdk/model/feature/ratelimit.aontu +1 -0
- package/project/.sdk/model/feature/rbac.aontu +1 -0
- package/project/.sdk/model/feature/retry.aontu +1 -0
- package/project/.sdk/model/feature/streaming.aontu +1 -0
- package/project/.sdk/model/feature/telemetry.aontu +1 -0
- package/project/.sdk/model/feature/test.aontu +1 -0
- package/project/.sdk/model/feature/timeout.aontu +1 -0
- package/project/.sdk/model/target/c.aontu +1 -0
- package/project/.sdk/model/target/clojure.aontu +1 -0
- package/project/.sdk/model/target/cpp.aontu +1 -0
- package/project/.sdk/model/target/dart.aontu +1 -0
- package/project/.sdk/model/target/elixir.aontu +1 -0
- package/project/.sdk/model/target/go-cli.aontu +1 -0
- package/project/.sdk/model/target/go-mcp.aontu +1 -0
- package/project/.sdk/model/target/go.aontu +1 -0
- package/project/.sdk/model/target/haskell.aontu +1 -0
- package/project/.sdk/model/target/java.aontu +1 -0
- package/project/.sdk/model/target/js.aontu +1 -0
- package/project/.sdk/model/target/kotlin.aontu +1 -0
- package/project/.sdk/model/target/lean.aontu +1 -0
- package/project/.sdk/model/target/lua.aontu +1 -0
- package/project/.sdk/model/target/ocaml.aontu +1 -0
- package/project/.sdk/model/target/perl.aontu +1 -0
- package/project/.sdk/model/target/php.aontu +1 -0
- package/project/.sdk/model/target/py-data.aontu +1 -0
- package/project/.sdk/model/target/py.aontu +1 -0
- package/project/.sdk/model/target/rb.aontu +1 -0
- package/project/.sdk/model/target/rust.aontu +1 -0
- package/project/.sdk/model/target/scala.aontu +1 -0
- package/project/.sdk/model/target/seneca-provider.aontu +1 -0
- package/project/.sdk/model/target/zig.aontu +1 -0
- package/project/.sdk/src/cmp/c/Config_c.ts +44 -0
- package/project/.sdk/src/cmp/c/fragment/Main.fragment.c +3 -1
- package/project/.sdk/src/cmp/clojure/Config_clojure.ts +37 -42
- package/project/.sdk/src/cmp/clojure/utility_clojure.ts +42 -0
- package/project/.sdk/src/cmp/cpp/Config_cpp.ts +18 -0
- package/project/.sdk/src/cmp/csharp/Config_csharp.ts +125 -52
- package/project/.sdk/src/cmp/csharp/fragment/Main.fragment.cs +3 -1
- package/project/.sdk/src/cmp/csharp/utility_csharp.ts +94 -6
- package/project/.sdk/src/cmp/dart/Config_dart.ts +43 -5
- package/project/.sdk/src/cmp/dart/fragment/Config.data.fragment.dart +74 -0
- package/project/.sdk/src/cmp/dart/fragment/Config.fragment.dart +6 -9
- package/project/.sdk/src/cmp/dart/fragment/Main.fragment.dart +16 -5
- package/project/.sdk/src/cmp/dart/utility_dart.ts +18 -0
- package/project/.sdk/src/cmp/elixir/Config_elixir.ts +128 -13
- package/project/.sdk/src/cmp/elixir/fragment/Main.fragment.ex +3 -1
- package/project/.sdk/src/cmp/elixir/utility_elixir.ts +6 -0
- package/project/.sdk/src/cmp/haskell/Config_haskell.ts +35 -52
- package/project/.sdk/src/cmp/haskell/Package_haskell.ts +1 -0
- package/project/.sdk/src/cmp/haskell/Test_haskell.ts +1 -1
- package/project/.sdk/src/cmp/haskell/utility_haskell.ts +15 -5
- package/project/.sdk/src/cmp/java/Config_java.ts +21 -0
- package/project/.sdk/src/cmp/kotlin/Config_kotlin.ts +15 -0
- package/project/.sdk/src/cmp/ocaml/Config_ocaml.ts +42 -39
- package/project/.sdk/src/cmp/perl/Config_perl.ts +15 -0
- package/project/.sdk/src/cmp/perl/fragment/Main.fragment.pm +3 -1
- package/project/.sdk/src/cmp/rust/Config_rust.ts +44 -0
- package/project/.sdk/src/cmp/rust/fragment/Main.fragment.rs +4 -2
- package/project/.sdk/src/cmp/scala/Config_scala.ts +15 -0
- package/project/.sdk/src/cmp/swift/Config_swift.ts +17 -0
- package/project/.sdk/src/cmp/swift/fragment/Main.fragment.swift +3 -1
- package/project/.sdk/src/cmp/zig/Config_zig.ts +44 -0
- package/project/.sdk/src/cmp/zig/TestEntity_zig.ts +9 -2
- package/project/.sdk/src/cmp/zig/fragment/Main.fragment.zig +3 -1
- package/project/.sdk/tm/c/core/sdk.h +1 -0
- package/project/.sdk/tm/c/utility/make_options.c +5 -1
- package/project/.sdk/tm/clojure/src/sdk/core.clj +51 -3
- package/project/.sdk/tm/cpp/core/types.hpp +4 -1
- package/project/.sdk/tm/cpp/utility/pipeline.hpp +5 -1
- package/project/.sdk/tm/csharp/test/PipelineTest.cs +15 -0
- package/project/.sdk/tm/csharp/utility/MakeOptions.cs +67 -1
- package/project/.sdk/tm/dart/lib/utility/MakeOptionsUtility.dart +43 -0
- package/project/.sdk/tm/elixir/lib/projectname/utility.ex +58 -2
- package/project/.sdk/tm/haskell/{test/TestJson.hs → src/SdkJson.hs} +8 -4
- package/project/.sdk/tm/haskell/src/SdkRuntime.hs +55 -2
- package/project/.sdk/tm/haskell/test/Runner.hs +1 -1
- package/project/.sdk/tm/haskell/test/StructCorpus.hs +1 -1
- package/project/.sdk/tm/java/core/SdkClient.java +3 -1
- package/project/.sdk/tm/java/utility/MakeOptions.java +5 -1
- package/project/.sdk/tm/kotlin/core/SdkClient.kt +3 -1
- package/project/.sdk/tm/kotlin/utility/MakeOptions.kt +5 -1
- package/project/.sdk/tm/ocaml/Makefile +3 -2
- package/project/.sdk/tm/ocaml/sdk_json.ml +106 -0
- package/project/.sdk/tm/ocaml/sdk_runtime.ml +56 -0
- package/project/.sdk/tm/ocaml/test/struct_corpus.ml +4 -86
- package/project/.sdk/tm/perl/utility/make_options.pm +5 -1
- package/project/.sdk/tm/rust/utility/make_options.rs +79 -1
- package/project/.sdk/tm/scala/core/SdkClient.scala +3 -1
- package/project/.sdk/tm/scala/utility/Make.scala +5 -1
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/MakeOptions.swift +5 -1
- package/project/.sdk/tm/zig/core/utility.zig +85 -1
- package/project/sdkgen-package.json +57 -0
- package/src/action/action.ts +136 -6
- package/src/action/check.ts +495 -0
- package/src/action/dispatch.ts +105 -0
- package/src/action/docs.ts +310 -0
- package/src/action/doctor.ts +692 -81
- package/src/action/feature.ts +109 -17
- package/src/action/kind.ts +375 -0
- package/src/action/package.ts +1173 -0
- package/src/action/resolve.ts +362 -0
- package/src/action/target.ts +290 -108
- package/src/cmp/Docs.ts +125 -0
- package/src/cmp/ExternalDocs.ts +54 -0
- package/src/helpers/definition.ts +82 -0
- package/src/helpers/featureSource.ts +83 -13
- package/src/helpers/junk.ts +168 -0
- package/src/helpers/manifest.ts +474 -0
- package/src/helpers/modelcheck.ts +262 -0
- package/src/helpers/semver.ts +211 -0
- package/src/helpers/shipped.ts +41 -0
- package/src/helpers/stdrep.ts +70 -0
- package/src/sdkgen.ts +294 -66
- package/src/types.ts +33 -3
- package/src/utility.ts +17 -0
package/src/action/target.ts
CHANGED
|
@@ -6,17 +6,19 @@ import {
|
|
|
6
6
|
Folder,
|
|
7
7
|
Copy,
|
|
8
8
|
File,
|
|
9
|
+
Content,
|
|
9
10
|
cmp,
|
|
10
11
|
each,
|
|
12
|
+
template,
|
|
11
13
|
} from 'jostraca'
|
|
12
14
|
|
|
13
15
|
import { showChanges } from '@voxgig/util'
|
|
14
16
|
|
|
15
17
|
import { showDryrun } from '../helpers/dryrun'
|
|
16
18
|
|
|
17
|
-
import { templateReplacements } from '../helpers/stdrep'
|
|
19
|
+
import { templateReplacements, provenanceReplace } from '../helpers/stdrep'
|
|
18
20
|
|
|
19
|
-
import {
|
|
21
|
+
import { isJunk, copyOpts } from '../helpers/junk'
|
|
20
22
|
|
|
21
23
|
import { Aontu } from 'aontu'
|
|
22
24
|
|
|
@@ -32,6 +34,7 @@ import type {
|
|
|
32
34
|
import { SdkGenError } from '../utility'
|
|
33
35
|
|
|
34
36
|
import {
|
|
37
|
+
BASE_FEATURE,
|
|
35
38
|
availableFeatures,
|
|
36
39
|
findFeatureSources,
|
|
37
40
|
featureExcludes,
|
|
@@ -48,6 +51,10 @@ import {
|
|
|
48
51
|
loadContent,
|
|
49
52
|
} from './action'
|
|
50
53
|
|
|
54
|
+
import { kindModel, resolveKind, escapeRe } from './kind'
|
|
55
|
+
|
|
56
|
+
import { BUNDLED, resolveSource, registerInstalled } from './resolve'
|
|
57
|
+
|
|
51
58
|
|
|
52
59
|
const CMD_MAP: any = {
|
|
53
60
|
add: cmd_target_add
|
|
@@ -99,6 +106,10 @@ async function target_add(targets: string[], actx: ActionContext): Promise<Actio
|
|
|
99
106
|
control: {
|
|
100
107
|
dryrun: !!actx.opts.dryrun
|
|
101
108
|
},
|
|
109
|
+
// Per-call for the same reason `control` is: this action runs on whatever
|
|
110
|
+
// Jostraca instance the caller handed it, and a template tree must not
|
|
111
|
+
// carry a maintainer's build droppings into a project. See helpers/junk.
|
|
112
|
+
cmp: copyOpts(),
|
|
102
113
|
}
|
|
103
114
|
|
|
104
115
|
opts.log.info({
|
|
@@ -131,9 +142,21 @@ async function target_add(targets: string[], actx: ActionContext): Promise<Actio
|
|
|
131
142
|
showDryrun(opts.log, 'target-result', jres, actx.folder)
|
|
132
143
|
}
|
|
133
144
|
|
|
134
|
-
//
|
|
135
|
-
//
|
|
136
|
-
//
|
|
145
|
+
// The targets just written are not in the in-memory model — nothing
|
|
146
|
+
// recompiles `model/sdk.aontu` mid-process — so put them there before the
|
|
147
|
+
// fan-out below.
|
|
148
|
+
//
|
|
149
|
+
// TargetRoot copies each new target's feature source from ITS OWN TREE,
|
|
150
|
+
// which is the whole story only while every feature ships in the same
|
|
151
|
+
// package as every target. For a feature supplied by a DIFFERENT package,
|
|
152
|
+
// the source lives in that package's overlay, and only `feature_add`'s
|
|
153
|
+
// two-tree lookup consults it — for targets in the model. So a target added
|
|
154
|
+
// while such a feature was already active got no source for it at all:
|
|
155
|
+
// TargetRoot could not find it, and the fan-out could not see the target.
|
|
156
|
+
registerInstalled('target', targets, actx)
|
|
157
|
+
|
|
158
|
+
// feature_add copies feature templates for every target in the model,
|
|
159
|
+
// which now includes the ones just added.
|
|
137
160
|
await feature_add(features, actx)
|
|
138
161
|
|
|
139
162
|
opts.log.info({
|
|
@@ -148,9 +171,15 @@ async function target_add(targets: string[], actx: ActionContext): Promise<Actio
|
|
|
148
171
|
|
|
149
172
|
|
|
150
173
|
const TargetRoot = cmp(function TargetRoot(props: any) {
|
|
151
|
-
const { ctx$, targets, features } = props
|
|
174
|
+
const { ctx$, targets, features, actx } = props
|
|
152
175
|
const { model, log } = ctx$
|
|
153
176
|
|
|
177
|
+
const fs = ctx$.fs()
|
|
178
|
+
|
|
179
|
+
// The prune below writes through `fs` directly rather than through
|
|
180
|
+
// jostraca, so it has to be told about the dry run itself.
|
|
181
|
+
const dryrun = !!actx?.opts?.dryrun
|
|
182
|
+
|
|
154
183
|
// TODO: jostraca - make from value easier to specify
|
|
155
184
|
// const tfolder = 'node_modules/@voxgig/sdkgen/project/.sdk'
|
|
156
185
|
|
|
@@ -169,7 +198,13 @@ const TargetRoot = cmp(function TargetRoot(props: any) {
|
|
|
169
198
|
note: tref
|
|
170
199
|
})
|
|
171
200
|
|
|
172
|
-
|
|
201
|
+
// Resolved through the shared kind spine, so a BARE name follows what
|
|
202
|
+
// the model records exactly as a feature's does. Without that, a target
|
|
203
|
+
// installed from an external package resolved back to the bundled
|
|
204
|
+
// scaffold on its next `target add` — the same write-only-provenance
|
|
205
|
+
// trap features had.
|
|
206
|
+
const source = resolveKind(tref, 'target', ctx$)
|
|
207
|
+
const { name: tname, folder: tfolder, origname: torigname, base } = source
|
|
173
208
|
tnames.push(tname)
|
|
174
209
|
const targetNote = tname + (tname != tref ? ' ref:' + tref : '')
|
|
175
210
|
|
|
@@ -180,26 +215,34 @@ const TargetRoot = cmp(function TargetRoot(props: any) {
|
|
|
180
215
|
note: tname + (tname != torigname ? 'original' + torigname : '') + ' from:' + tfolder
|
|
181
216
|
})
|
|
182
217
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
Copy
|
|
199
|
-
|
|
200
|
-
|
|
218
|
+
// An ALIASED add (`target add go~go2`) installs the target under a new
|
|
219
|
+
// name, and every one of the three trees has to agree about that name.
|
|
220
|
+
const aliased = tname !== torigname
|
|
221
|
+
|
|
222
|
+
// The definition file and the index entry: the same for every kind, so
|
|
223
|
+
// they are emitted once, in action/kind.
|
|
224
|
+
Folder({ name: 'model/target' }, () => kindModel({
|
|
225
|
+
ctx$, kind: 'target', source, names: tnames,
|
|
226
|
+
content: ctx$.meta.content.target_index,
|
|
227
|
+
}))
|
|
228
|
+
|
|
229
|
+
if (aliased) {
|
|
230
|
+
// Components are dispatched by CONVENTION — `cmp/<t>/Main_<t>` — so
|
|
231
|
+
// an aliased tree whose files keep the origin suffix resolves
|
|
232
|
+
// nothing: `src/cmp/go2/Main_go.ts` is invisible to a lookup for
|
|
233
|
+
// `cmp/go2/Main_go2`. jostraca's tree Copy has no per-entry rename
|
|
234
|
+
// hook, so an aliased tree is emitted file by file instead.
|
|
235
|
+
aliasCmpTree(ctx$, tfolder + '/src/cmp/' + torigname,
|
|
236
|
+
'src/cmp/' + tname, torigname, tname)
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
Folder({ name: 'src/cmp/' + tname }, () => {
|
|
240
|
+
Copy({
|
|
241
|
+
from: tfolder + '/src/cmp/' + torigname,
|
|
242
|
+
// exclude: true
|
|
243
|
+
})
|
|
201
244
|
})
|
|
202
|
-
}
|
|
245
|
+
}
|
|
203
246
|
|
|
204
247
|
// Copy the whole template tree MINUS the source of every feature the
|
|
205
248
|
// model did not ask for. Which files those are is discovered from the
|
|
@@ -221,7 +264,8 @@ const TargetRoot = cmp(function TargetRoot(props: any) {
|
|
|
221
264
|
// package failed to compile — "fhHasFeature redeclared in this block".
|
|
222
265
|
//
|
|
223
266
|
// The invariant this restores: tm/<target> == source tree MINUS trim.
|
|
224
|
-
pruneStaleTemplates(
|
|
267
|
+
pruneStaleTemplates(
|
|
268
|
+
ctx$, tfolder + '/tm/' + torigname, 'tm/' + tname, trim, dryrun)
|
|
225
269
|
|
|
226
270
|
Folder({ name: 'tm/' + tname }, () => {
|
|
227
271
|
Copy({
|
|
@@ -241,6 +285,127 @@ const TargetRoot = cmp(function TargetRoot(props: any) {
|
|
|
241
285
|
})
|
|
242
286
|
|
|
243
287
|
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
// `<Cmp>_<origname>.<ext>` -> `<Cmp>_<tname>.<ext>`, for an aliased install.
|
|
291
|
+
// Anything not carrying the suffix (tsconfig.json, the fragment sources)
|
|
292
|
+
// keeps its name — the `.<ext>` anchor is what keeps this off
|
|
293
|
+
// `Main.fragment.go`, whose `go` is a file extension.
|
|
294
|
+
//
|
|
295
|
+
// Shared with doctor for the same reason as aliasCmpText: doctor walks the
|
|
296
|
+
// ORIGIN tree to decide what should be present, so it has to land each file
|
|
297
|
+
// under the same name the writer gave it, or it reports the whole tree as
|
|
298
|
+
// missing.
|
|
299
|
+
function aliasCmpName(name: string, torigname: string, tname: string): string {
|
|
300
|
+
return name.replace(
|
|
301
|
+
new RegExp('_' + escapeRe(torigname) + '(\\.[^.]+)$'), '_' + tname + '$1')
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
// The origin name a component carries INSIDE its source, rewritten for an
|
|
306
|
+
// aliased install. Shared with doctor, which re-applies it before comparing —
|
|
307
|
+
// same discipline as templateReplacements: a writer and a reader that
|
|
308
|
+
// disagree by a character make every file read as a fork.
|
|
309
|
+
//
|
|
310
|
+
// Rewritten here rather than through jostraca's `replace` map, because that
|
|
311
|
+
// map canonicalises each key into a regex group NAME — `_go'` and `_go"` both
|
|
312
|
+
// reduce to the same name, so the later entry silently won and every
|
|
313
|
+
// single-quoted import came out as `from './Package_go2"`. One explicit regex
|
|
314
|
+
// keeps the quote it matched.
|
|
315
|
+
// `cmpbase` is the component tree's PREFIX — `src/cmp/` for a target,
|
|
316
|
+
// `src/cmp/docs/` for a docs item. Taken from the caller rather than fixed
|
|
317
|
+
// here: a docs item's components live one level deeper, and a rewrite that
|
|
318
|
+
// looked for `src/cmp/<orig>/` left an aliased docs component pointing at the
|
|
319
|
+
// ORIGIN's fragments — reading them silently when both are installed.
|
|
320
|
+
function aliasCmpText(
|
|
321
|
+
src: string, torigname: string, tname: string, cmpbase = 'src/cmp/',
|
|
322
|
+
): string {
|
|
323
|
+
const orig = escapeRe(torigname)
|
|
324
|
+
|
|
325
|
+
return src
|
|
326
|
+
// The fragment directory, read relative to __dirname. The fragments are
|
|
327
|
+
// copied into the ALIAS's folder, so leaving the origin path would miss —
|
|
328
|
+
// or, if the origin target is also installed, silently read ITS fragments.
|
|
329
|
+
.replace(new RegExp(escapeRe(cmpbase) + orig + '/', 'g'), cmpbase + tname + '/')
|
|
330
|
+
// Sibling imports: `'./Package_go'` -> `'./Package_go2'`. Anchored on the
|
|
331
|
+
// closing quote (captured, so the style is preserved) to keep it off file
|
|
332
|
+
// EXTENSIONS — `Main.fragment.go` must not become `Main.fragment.go2`.
|
|
333
|
+
.replace(new RegExp('_' + orig + '([\'"])', 'g'), '_' + tname + '$1')
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
// Emit an aliased `src/cmp` tree: every file renamed from the origin suffix
|
|
338
|
+
// to the installed one, and its CONTENT rewritten to match.
|
|
339
|
+
//
|
|
340
|
+
// Renaming alone would break the tree, because a component names its origin
|
|
341
|
+
// twice over: sibling imports (`from './Package_go'`) and the fragment
|
|
342
|
+
// directory it reads through `__dirname` (`/../../../src/cmp/go/fragment/`,
|
|
343
|
+
// in 67 of the shipped components). Both are rewritten here — the fragment
|
|
344
|
+
// path because the fragments are copied to the ALIAS's folder, so the origin
|
|
345
|
+
// path would either miss or, worse, silently read the origin target's
|
|
346
|
+
// fragments if that target is also installed.
|
|
347
|
+
//
|
|
348
|
+
// Files are emitted through jostraca (`File`/`Content`) rather than copied
|
|
349
|
+
// with `fs`, so a dry run reports them and writes nothing, exactly as the
|
|
350
|
+
// tree Copy on the unaliased path does.
|
|
351
|
+
function aliasCmpTree(
|
|
352
|
+
ctx$: any,
|
|
353
|
+
fromDir: string,
|
|
354
|
+
toRel: string,
|
|
355
|
+
torigname: string,
|
|
356
|
+
tname: string,
|
|
357
|
+
cmpbase = 'src/cmp/',
|
|
358
|
+
) {
|
|
359
|
+
const fs = ctx$.fs()
|
|
360
|
+
|
|
361
|
+
const aliasText = (src: string) =>
|
|
362
|
+
aliasCmpText(src, torigname, tname, cmpbase)
|
|
363
|
+
|
|
364
|
+
const emit = (dir: string, rel: string) => {
|
|
365
|
+
let entries: any[]
|
|
366
|
+
try {
|
|
367
|
+
entries = fs.readdirSync(dir, { withFileTypes: true })
|
|
368
|
+
}
|
|
369
|
+
catch (e: any) {
|
|
370
|
+
return
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Sorted, so an aliased tree is emitted in the same byte-stable order
|
|
374
|
+
// everything else in this toolchain is. Junk is dropped here because this
|
|
375
|
+
// walk stands in for a tree Copy, which drops it through
|
|
376
|
+
// `cmp.Copy.ignore` — an aliased install must not be the one path that
|
|
377
|
+
// ships a maintainer's `__pycache__`. See helpers/junk.
|
|
378
|
+
const names = entries
|
|
379
|
+
.map((ent: any) => ent.name)
|
|
380
|
+
.filter((name: string) => !isJunk(name))
|
|
381
|
+
.sort()
|
|
382
|
+
|
|
383
|
+
for (const name of names) {
|
|
384
|
+
const child = Path.join(dir, name)
|
|
385
|
+
const ent = entries.find((e: any) => e.name === name)
|
|
386
|
+
|
|
387
|
+
if (ent.isDirectory()) {
|
|
388
|
+
Folder({ name }, () => emit(child, rel + '/' + name))
|
|
389
|
+
continue
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const renamed = aliasCmpName(name, torigname, tname)
|
|
393
|
+
|
|
394
|
+
// `template` against the model with no replace map, matching what Copy
|
|
395
|
+
// does for the unaliased tree — jostraca's Copy always interpolates
|
|
396
|
+
// `$$ref$$` against the model, so an aliased tree must too.
|
|
397
|
+
const src = fs.readFileSync(child, 'utf8')
|
|
398
|
+
|
|
399
|
+
File({ name: renamed }, () => Content(template(aliasText(src), ctx$.model)))
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
Folder({ name: toRel }, () => emit(fromDir, toRel))
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
|
|
244
409
|
// Path patterns that keep a target's unwanted feature source out of the
|
|
245
410
|
// project: the source of every AVAILABLE feature the model did not select,
|
|
246
411
|
// plus the templates that only compile with the complete feature set.
|
|
@@ -274,6 +439,7 @@ function pruneStaleTemplates(
|
|
|
274
439
|
fromDir: string,
|
|
275
440
|
toRel: string,
|
|
276
441
|
trim: RegExp[],
|
|
442
|
+
dryrun?: boolean,
|
|
277
443
|
) {
|
|
278
444
|
const { log } = ctx$
|
|
279
445
|
const fs = ctx$.fs()
|
|
@@ -291,6 +457,16 @@ function pruneStaleTemplates(
|
|
|
291
457
|
return
|
|
292
458
|
}
|
|
293
459
|
for (const ent of entries) {
|
|
460
|
+
// Junk is invisible to this listing on BOTH sides, and it has to be
|
|
461
|
+
// both. In the source it is never copied, so counting it would want a
|
|
462
|
+
// file that can never arrive. In the destination it was never written
|
|
463
|
+
// by this toolchain, so counting it as stale would have `target add`
|
|
464
|
+
// delete a maintainer's own `__pycache__` out of their SDK repo —
|
|
465
|
+
// this prune's remit is the tree it writes, not the tree it finds.
|
|
466
|
+
if (isJunk(ent.name)) {
|
|
467
|
+
continue
|
|
468
|
+
}
|
|
469
|
+
|
|
294
470
|
const child = Path.join(dir, ent.name)
|
|
295
471
|
const childRel = '' === rel ? ent.name : rel + '/' + ent.name
|
|
296
472
|
if (ent.isDirectory()) {
|
|
@@ -324,6 +500,29 @@ function pruneStaleTemplates(
|
|
|
324
500
|
return
|
|
325
501
|
}
|
|
326
502
|
|
|
503
|
+
// A DRY RUN must not delete. This prune calls `fs.unlinkSync` directly, and
|
|
504
|
+
// jostraca enforces `control.dryrun` only inside its own write layer — so
|
|
505
|
+
// `-y target add <t>` previewed the copies and then really removed every
|
|
506
|
+
// stale template, which is the opposite of what the flag promises and
|
|
507
|
+
// exactly the blast radius a maintainer runs `-y` to inspect. Report the
|
|
508
|
+
// deletions instead, in the same shape the copies are reported.
|
|
509
|
+
if (dryrun) {
|
|
510
|
+
log.info({
|
|
511
|
+
point: 'target-template-prune', target: toRel, count: stale.length,
|
|
512
|
+
files: stale, dryrun: true,
|
|
513
|
+
note: toRel + ': would remove ' + stale.length +
|
|
514
|
+
' stale template(s) — ** DRY RUN **, nothing was written'
|
|
515
|
+
})
|
|
516
|
+
for (const rel of stale) {
|
|
517
|
+
log.info({
|
|
518
|
+
point: 'target-template-prune-file', target: toRel,
|
|
519
|
+
file: toRel + '/' + rel, dryrun: true,
|
|
520
|
+
note: 'would remove ' + toRel + '/' + rel
|
|
521
|
+
})
|
|
522
|
+
}
|
|
523
|
+
return
|
|
524
|
+
}
|
|
525
|
+
|
|
327
526
|
const removed: string[] = []
|
|
328
527
|
for (const rel of stale) {
|
|
329
528
|
try {
|
|
@@ -349,6 +548,52 @@ function pruneStaleTemplates(
|
|
|
349
548
|
}
|
|
350
549
|
|
|
351
550
|
|
|
551
|
+
// WHICH NAMES COUNT AS FEATURE SOURCE, when deciding what to trim.
|
|
552
|
+
//
|
|
553
|
+
// This used to be the SOURCE folder's own `model/feature/` listing, which is
|
|
554
|
+
// right only while every target ships in the same package as every feature.
|
|
555
|
+
// An external target package declares no feature models of its own — it has
|
|
556
|
+
// no reason to — so nothing was discovered, nothing was trimmed, and the
|
|
557
|
+
// consumer received the target's source for EVERY feature regardless of what
|
|
558
|
+
// its model selected. Measured on a target copied from the bundled `go`: the
|
|
559
|
+
// bundled one keeps 2 feature source files, the external one kept all 18.
|
|
560
|
+
//
|
|
561
|
+
// That is the failure `helpers/featureSource` was written to end (272 stray
|
|
562
|
+
// files in one repo), arriving again by the one route it did not cover.
|
|
563
|
+
//
|
|
564
|
+
// The catalogue is therefore the union of every place a feature this project
|
|
565
|
+
// could select can come from:
|
|
566
|
+
//
|
|
567
|
+
// - the bundled scaffold, which is what a bare `feature add <name>` means;
|
|
568
|
+
// - the source package's own declarations, for a package shipping both;
|
|
569
|
+
// - the consumer's OWN installed feature models, which is how an EXTERNAL
|
|
570
|
+
// feature's source becomes trimmable at all.
|
|
571
|
+
//
|
|
572
|
+
// Every term is a place feature DEFINITIONS live, deliberately. Taking the
|
|
573
|
+
// third from the model's feature KEYS instead would make any name a project
|
|
574
|
+
// happens to declare a trim candidate — and a file in a `feature/` directory
|
|
575
|
+
// is not necessarily a feature. `tm/rust/feature/support.rs` and its siblings
|
|
576
|
+
// are shared machinery that `Main_rust` emits unconditionally (`pub mod
|
|
577
|
+
// support`), so a project that declared a feature called `support` would have
|
|
578
|
+
// had that file pruned and produced a crate that cannot compile. A definition
|
|
579
|
+
// file is evidence that the name really denotes a feature; a model key is not.
|
|
580
|
+
//
|
|
581
|
+
// For a bundled add this is `bundled ∪ bundled ∪ (⊆ bundled)`, so the trim is
|
|
582
|
+
// byte-identical to what it always was — the goldens hold it to that.
|
|
583
|
+
function featureCatalogue(ctx$: any, tfolder: string): string[] {
|
|
584
|
+
const fs = ctx$.fs()
|
|
585
|
+
const root = ctx$.folder ?? '.'
|
|
586
|
+
|
|
587
|
+
const names = new Set<string>([
|
|
588
|
+
...availableFeatures(fs, Path.join(root, BUNDLED)),
|
|
589
|
+
...availableFeatures(fs, tfolder),
|
|
590
|
+
...availableFeatures(fs, root),
|
|
591
|
+
])
|
|
592
|
+
|
|
593
|
+
return Array.from(names).sort()
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
|
|
352
597
|
function trimFeatures(
|
|
353
598
|
ctx$: any,
|
|
354
599
|
tfolder: string,
|
|
@@ -371,9 +616,9 @@ function trimFeatures(
|
|
|
371
616
|
|
|
372
617
|
// `base` is not a declared feature — it is the always-present foundation
|
|
373
618
|
// every other feature builds on — so it is never a trim candidate.
|
|
374
|
-
const selected = new Set([
|
|
619
|
+
const selected = new Set([BASE_FEATURE, ...(features ?? [])])
|
|
375
620
|
|
|
376
|
-
const available =
|
|
621
|
+
const available = featureCatalogue(ctx$, tfolder)
|
|
377
622
|
const drop = findFeatureSources(fs, tfolder + '/tm/' + torigname, available)
|
|
378
623
|
.filter((s) => !selected.has(s.name))
|
|
379
624
|
|
|
@@ -440,95 +685,32 @@ function readTargetFeature(
|
|
|
440
685
|
}
|
|
441
686
|
|
|
442
687
|
|
|
443
|
-
//
|
|
444
|
-
//
|
|
445
|
-
//
|
|
446
|
-
//
|
|
447
|
-
// On POSIX Path.sep IS '/', so this is the same split it always was.
|
|
448
|
-
function lastSegment(ref: string): string {
|
|
449
|
-
return getelem(ref.split('/').flatMap((p: string) => p.split(Path.sep)), -1)
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
|
|
688
|
+
// `target add`'s view of the shared resolver: the same resolution every kind
|
|
689
|
+
// uses, with this action's historical field names. Kept as a wrapper so its
|
|
690
|
+
// callers (TargetRoot, doctor) and their tests do not have to move with the
|
|
691
|
+
// extraction.
|
|
453
692
|
function resolveTarget(tref: string, ctx$: any) {
|
|
454
|
-
|
|
455
|
-
let torigname = tref
|
|
456
|
-
let tfolder = 'node_modules/@voxgig/sdkgen/project/.sdk'
|
|
457
|
-
|
|
458
|
-
const root = ctx$.folder
|
|
459
|
-
const fs = ctx$.fs()
|
|
460
|
-
|
|
461
|
-
let fulltfolder = Path.normalize(Path.join(root, tfolder))
|
|
462
|
-
tname = lastSegment(tref)
|
|
693
|
+
const src = resolveSource(tref, 'target', ctx$)
|
|
463
694
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
torigname = lastSegment(aliasref)
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
const search: string[] = []
|
|
474
|
-
let found = false
|
|
475
|
-
// Windows: an absolute ref is `D:\a\...` or `D:/a/...`, and a Path.join'd
|
|
476
|
-
// one carries backslashes, so neither `includes('/')` nor `startsWith('/')`
|
|
477
|
-
// recognises it. Path.isAbsolute and Path.sep are platform-correct and
|
|
478
|
-
// reduce to the same answers on POSIX.
|
|
479
|
-
if (aliasref.includes('/') || aliasref.includes(Path.sep)) {
|
|
480
|
-
// NOTE: the last path element of the ref is the target name, not a folder.
|
|
481
|
-
const aliasbase = Path.dirname(aliasref)
|
|
482
|
-
|
|
483
|
-
if (!Path.isAbsolute(aliasref)) {
|
|
484
|
-
fulltfolder = Path.normalize(Path.join(root, 'node_modules', aliasbase, '.sdk'))
|
|
485
|
-
search.push(fulltfolder)
|
|
486
|
-
found = fs.existsSync(fulltfolder)
|
|
487
|
-
|
|
488
|
-
if (!found) {
|
|
489
|
-
fulltfolder = Path.normalize(Path.join(root, aliasbase, '.sdk'))
|
|
490
|
-
search.push(fulltfolder)
|
|
491
|
-
found = fs.existsSync(fulltfolder)
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
else {
|
|
495
|
-
fulltfolder = Path.normalize(Path.join(aliasbase, '.sdk'))
|
|
496
|
-
search.push(fulltfolder)
|
|
497
|
-
found = fs.existsSync(fulltfolder)
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
else {
|
|
501
|
-
search.push(fulltfolder)
|
|
502
|
-
found = fs.existsSync(fulltfolder)
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
if (!found) {
|
|
506
|
-
throw new Error('Target folder not found in:\n' + search.join('\n '))
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
// `base` is the target folder relative to the project root. Compare with the
|
|
510
|
-
// PLATFORM separator: on Windows `root + '/'` never prefixes a normalised
|
|
511
|
-
// absolute path, so the root would not be stripped and `base` would stay
|
|
512
|
-
// absolute. Normalise both sides first for the same reason.
|
|
513
|
-
const nroot = Path.normalize(root)
|
|
514
|
-
const rootslash = nroot.endsWith(Path.sep) ? nroot : nroot + Path.sep
|
|
515
|
-
const out = {
|
|
516
|
-
tname,
|
|
517
|
-
tfolder: fulltfolder,
|
|
518
|
-
torigname,
|
|
519
|
-
base: fulltfolder.startsWith(rootslash)
|
|
520
|
-
? fulltfolder.slice(rootslash.length)
|
|
521
|
-
: fulltfolder
|
|
695
|
+
return {
|
|
696
|
+
tname: src.name,
|
|
697
|
+
tfolder: src.folder,
|
|
698
|
+
torigname: src.origname,
|
|
699
|
+
base: src.base,
|
|
700
|
+
package: src.package,
|
|
522
701
|
}
|
|
523
|
-
|
|
524
|
-
return out
|
|
525
702
|
}
|
|
526
703
|
|
|
527
704
|
|
|
528
705
|
export {
|
|
529
706
|
action_target,
|
|
707
|
+
featureCatalogue,
|
|
530
708
|
target_add,
|
|
531
709
|
resolveTarget,
|
|
532
710
|
trimFeatures,
|
|
533
711
|
readTargetFeature,
|
|
712
|
+
aliasCmpText,
|
|
713
|
+
aliasCmpName,
|
|
714
|
+
aliasCmpTree,
|
|
715
|
+
pruneStaleTemplates,
|
|
534
716
|
}
|
package/src/cmp/Docs.ts
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// GENERATING A DOCS ITEM. See docs/design/sdkgen-packages.md §20.3.
|
|
2
|
+
//
|
|
3
|
+
// A docs item is dispatched by the same convention a target is —
|
|
4
|
+
// `cmp/docs/<n>/Main_<n>` — so an item's package supplies the emitter and
|
|
5
|
+
// sdkgen supplies only the call.
|
|
6
|
+
//
|
|
7
|
+
// WHY THIS IS NOT RENDERED BY THE CONSUMER'S Root.ts
|
|
8
|
+
//
|
|
9
|
+
// A project's `Root.ts` is written once, by create-sdkgen, and is never
|
|
10
|
+
// touched again (doctor has an `unwired` category precisely because of it).
|
|
11
|
+
// So a kind introduced later can reach generation in one of two ways: every
|
|
12
|
+
// existing project edits its Root, or sdkgen runs the kind's own pass. The
|
|
13
|
+
// second is what happens here — `docs add` in a project scaffolded years ago
|
|
14
|
+
// generates with no scaffold change at all, which is the whole point of
|
|
15
|
+
// putting the destinations in packages.
|
|
16
|
+
//
|
|
17
|
+
// The cost is honest and small: a second `generate()` call over the same
|
|
18
|
+
// root, so the changes report arrives in two parts.
|
|
19
|
+
|
|
20
|
+
import { cmp, each, names, Folder } from 'jostraca'
|
|
21
|
+
|
|
22
|
+
import { requirePath } from '../utility'
|
|
23
|
+
|
|
24
|
+
import { ensureStdrep } from '../helpers/stdrep'
|
|
25
|
+
|
|
26
|
+
import { KIT } from '../types'
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
// The model preamble every pass performs before rendering: the case-variant
|
|
30
|
+
// name constants (`Name`, `NAME`, …) and the ProjectName replacement map that
|
|
31
|
+
// template substitution reads.
|
|
32
|
+
//
|
|
33
|
+
// Done HERE rather than relied upon, because a docs pass does NOT go through
|
|
34
|
+
// the consumer's Root — nothing else has set `ctx$.model`, and when there are
|
|
35
|
+
// out-of-tree items the Root was handed a FILTERED COPY, so the preamble it
|
|
36
|
+
// performed did not touch this object at all.
|
|
37
|
+
function prepareModel(model: any, ctx$: any) {
|
|
38
|
+
ctx$.model = model
|
|
39
|
+
|
|
40
|
+
model.const = model.const || { name: model.name }
|
|
41
|
+
names(model.const, model.name)
|
|
42
|
+
if (null == model.const.year) {
|
|
43
|
+
model.const.year = new Date().getFullYear()
|
|
44
|
+
}
|
|
45
|
+
names(model, model.name)
|
|
46
|
+
|
|
47
|
+
ctx$.stdrep = ctx$.stdrep || {}
|
|
48
|
+
names(ctx$.stdrep, model.Name, 'Project' + 'Name')
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
// One item: its emitter, called with the item and the shared replace map.
|
|
53
|
+
//
|
|
54
|
+
// No Folder here — WHERE it lands is the caller's business, because that is
|
|
55
|
+
// exactly what differs between the two passes: in-tree the item owns
|
|
56
|
+
// `<sdk-repo>/<name>/`, out-of-tree the destination IS the item's root.
|
|
57
|
+
const DocsItem = cmp(function DocsItem(props: any) {
|
|
58
|
+
const { item, ctx$ } = props
|
|
59
|
+
const log = ctx$.log
|
|
60
|
+
const model = props.model ?? ctx$.model
|
|
61
|
+
|
|
62
|
+
const stdrep = ensureStdrep(ctx$)
|
|
63
|
+
|
|
64
|
+
// The MODEL KEY is the fallback, and it is not paranoia: `name` comes from
|
|
65
|
+
// the base schema's `name: key()`, so an item whose project has not
|
|
66
|
+
// included that schema — or whose own definition omits it — reached
|
|
67
|
+
// `require('cmp/docs/undefined/Main_undefined')`, a message naming nothing
|
|
68
|
+
// the author wrote. Seen in the first real install of a docs package.
|
|
69
|
+
const name = item.name ?? item.key$
|
|
70
|
+
|
|
71
|
+
if (null == name || '' === name) {
|
|
72
|
+
throw new Error(
|
|
73
|
+
'Docs item has no name and no model key, so its component cannot be ' +
|
|
74
|
+
'located: ' + JSON.stringify(Object.keys(item ?? {})))
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const Main_docs = requirePath(ctx$, `cmp/docs/${name}/Main_${name}`)
|
|
78
|
+
|
|
79
|
+
Main_docs['Main']({ model, docs: item, stdrep })
|
|
80
|
+
|
|
81
|
+
log.info({
|
|
82
|
+
point: 'generate-docs', docs: name, note: 'docs:' + name
|
|
83
|
+
})
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
// Every IN-TREE docs item, each in its own folder.
|
|
88
|
+
//
|
|
89
|
+
// Items with `output: path` are excluded: they get their own pass rooted at
|
|
90
|
+
// that path, and rendering them here as well would ALSO write them into
|
|
91
|
+
// `<sdk-repo>/<name>/` — the same reason `withoutExternal` exists for
|
|
92
|
+
// targets.
|
|
93
|
+
const Docs = cmp(function Docs(props: any) {
|
|
94
|
+
const { ctx$ } = props
|
|
95
|
+
const model = props.model ?? ctx$.model
|
|
96
|
+
|
|
97
|
+
prepareModel(model, ctx$)
|
|
98
|
+
|
|
99
|
+
const items = model?.main?.[KIT]?.docs ?? {}
|
|
100
|
+
|
|
101
|
+
each(items, (item: any) => {
|
|
102
|
+
if (false === item.active) {
|
|
103
|
+
return
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const path = item.output?.path
|
|
107
|
+
|
|
108
|
+
if (null != path && '' !== path) {
|
|
109
|
+
return
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const name = item.name ?? item.key$
|
|
113
|
+
|
|
114
|
+
names(item, name)
|
|
115
|
+
|
|
116
|
+
Folder({ name }, () => DocsItem({ item }))
|
|
117
|
+
})
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
export {
|
|
122
|
+
Docs,
|
|
123
|
+
DocsItem,
|
|
124
|
+
prepareModel,
|
|
125
|
+
}
|