@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
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// The Root for a DOCS item that generates outside the SDK repo
|
|
2
|
+
// (`main: kit: docs: <n>: output: path`).
|
|
3
|
+
//
|
|
4
|
+
// Out of tree is the NORMAL case for a docs item, not the exception it is for
|
|
5
|
+
// a target: a documentation site usually has its own repo, and in-tree it
|
|
6
|
+
// would collide with the SDK repo's own README and CI workflows.
|
|
7
|
+
//
|
|
8
|
+
// The two facts that make this a separate `generate()` pass rather than a
|
|
9
|
+
// folder are the ones `ExternalTarget` documents at length: jostraca's
|
|
10
|
+
// `FileHandler.validName` refuses a `..` segment in a folder name (the guard
|
|
11
|
+
// that keeps generation inside the tree it was pointed at, not an oversight
|
|
12
|
+
// to route around), and the output root is `jopts.folder` PER CALL — so
|
|
13
|
+
// writing somewhere else means another call.
|
|
14
|
+
//
|
|
15
|
+
// No Folder wraps the item: the destination IS the site, so its files go at
|
|
16
|
+
// the root of the output path.
|
|
17
|
+
|
|
18
|
+
import { cmp, names, Project } from 'jostraca'
|
|
19
|
+
|
|
20
|
+
import { KIT } from '../types'
|
|
21
|
+
|
|
22
|
+
import { DocsItem, prepareModel } from './Docs'
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
const ExternalDocs = cmp(function ExternalDocs(props: any) {
|
|
26
|
+
const { model, item, cmpfolder, sdkrelpath } = props
|
|
27
|
+
const ctx$ = props.ctx$
|
|
28
|
+
|
|
29
|
+
// Components live in the PROJECT, not in the repo being written to. This
|
|
30
|
+
// pass has retargeted jostraca's output folder, which is what requirePath
|
|
31
|
+
// otherwise resolves against — see utility.resolvePath. Losing this line
|
|
32
|
+
// makes the pass look for components under the DESTINATION.
|
|
33
|
+
ctx$.cmpfolder = cmpfolder
|
|
34
|
+
|
|
35
|
+
// The path from the destination back to the SDK project, for a site that
|
|
36
|
+
// sits beside the SDK in a known layout and needs to name it.
|
|
37
|
+
ctx$.sdkrelpath = sdkrelpath
|
|
38
|
+
|
|
39
|
+
// The same preamble the in-tree pass performs — one definition, both
|
|
40
|
+
// callers, because a project whose ONLY docs item is out of tree must
|
|
41
|
+
// generate exactly as one with both.
|
|
42
|
+
prepareModel(model, ctx$)
|
|
43
|
+
|
|
44
|
+
Project({}, () => {
|
|
45
|
+
names(item, item.name ?? item.key$)
|
|
46
|
+
|
|
47
|
+
DocsItem({ item })
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
export {
|
|
53
|
+
ExternalDocs
|
|
54
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// WHERE A KIND'S DEFINITION FILES LIVE.
|
|
2
|
+
//
|
|
3
|
+
// `model/<kind>/<name>.aontu`, with `model/<kind>/<kind>-index.aontu` as the
|
|
4
|
+
// include list beside them. One line of path-building — and it was written out
|
|
5
|
+
// longhand in three places (the resolver, the feature catalogue, doctor), each
|
|
6
|
+
// with its own idea of which files in that directory count.
|
|
7
|
+
//
|
|
8
|
+
// That is the same shape of defect this workstream has now fixed four times:
|
|
9
|
+
// the same rule expressed twice, drifting. The manifest makes it four callers,
|
|
10
|
+
// so it becomes one function first.
|
|
11
|
+
//
|
|
12
|
+
// The EXCLUSIONS matter as much as the path. A `model/<kind>/` directory holds
|
|
13
|
+
// the index file (a list of includes, not a definition) and, in the shipped
|
|
14
|
+
// scaffold, a `README.md`. Neither is an item, and a caller that forgets one
|
|
15
|
+
// invents a `feature-index` feature.
|
|
16
|
+
|
|
17
|
+
import Path from 'node:path'
|
|
18
|
+
|
|
19
|
+
import { isJunk } from './junk'
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
// The definition file for one item.
|
|
23
|
+
function definitionPath(sdkfolder: string, kind: string, name: string): string {
|
|
24
|
+
return Path.join(sdkfolder, 'model', kind, name + '.aontu')
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
// The directory holding a kind's definitions.
|
|
29
|
+
function definitionFolder(sdkfolder: string, kind: string): string {
|
|
30
|
+
return Path.join(sdkfolder, 'model', kind)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
// The include list beside them.
|
|
35
|
+
function indexName(kind: string): string {
|
|
36
|
+
return kind + '-index.aontu'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
// Every item of `kind` a `.sdk` folder DEFINES, sorted. Sorted because it
|
|
41
|
+
// feeds an exact-set comparison against a manifest and, through
|
|
42
|
+
// `availableFeatures`, the feature-trim catalogue — both of which are compared
|
|
43
|
+
// as text, so a readdir order that varies by filesystem would vary the result.
|
|
44
|
+
//
|
|
45
|
+
// An absent directory is not an error: a package providing only features has
|
|
46
|
+
// no `model/target/`, and that is the normal shape of a feature package.
|
|
47
|
+
function definitionNames(fs: any, sdkfolder: string, kind: string): string[] {
|
|
48
|
+
const dir = definitionFolder(sdkfolder, kind)
|
|
49
|
+
const index = indexName(kind)
|
|
50
|
+
|
|
51
|
+
// `existsSync` is true for a REGULAR FILE and for a directory this process
|
|
52
|
+
// cannot read, so guarding on it and then calling `readdirSync` let a bare
|
|
53
|
+
// `ENOTDIR`/`EACCES` escape to callers that have no catch — including
|
|
54
|
+
// `featureCatalogue`, which runs on every ordinary `target add`, so the
|
|
55
|
+
// whole add aborted with an errno instead of a diagnostic. Not-a-readable-
|
|
56
|
+
// directory is the same answer as not-there: this kind defines nothing
|
|
57
|
+
// here.
|
|
58
|
+
let entries: string[]
|
|
59
|
+
try {
|
|
60
|
+
entries = fs.readdirSync(dir)
|
|
61
|
+
}
|
|
62
|
+
catch (err: any) {
|
|
63
|
+
return []
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// The `.aontu` suffix is not enough on its own: an emacs lock link is named
|
|
67
|
+
// `.#target.aontu` and a merge leaves `target.aontu.orig`, so an editor open
|
|
68
|
+
// in the wrong window invents an item called `.#target`, which then fails to
|
|
69
|
+
// resolve everywhere it is named. See helpers/junk.
|
|
70
|
+
return entries
|
|
71
|
+
.filter((n: string) => n.endsWith('.aontu') && index !== n && !isJunk(n))
|
|
72
|
+
.map((n: string) => n.replace(/\.aontu$/, ''))
|
|
73
|
+
.sort()
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
export {
|
|
78
|
+
definitionPath,
|
|
79
|
+
definitionFolder,
|
|
80
|
+
definitionNames,
|
|
81
|
+
indexName,
|
|
82
|
+
}
|
|
@@ -37,6 +37,10 @@ import Path from 'node:path'
|
|
|
37
37
|
|
|
38
38
|
import { KIT, getModelPath } from '@voxgig/apidef'
|
|
39
39
|
|
|
40
|
+
import { definitionNames } from './definition'
|
|
41
|
+
|
|
42
|
+
import { isJunk } from './junk'
|
|
43
|
+
|
|
40
44
|
|
|
41
45
|
// One feature's source within a target template tree.
|
|
42
46
|
type FeatureSource = {
|
|
@@ -52,12 +56,25 @@ type FeatureSource = {
|
|
|
52
56
|
}
|
|
53
57
|
|
|
54
58
|
|
|
59
|
+
// One entry inside a feature container, before the catalogue has had its say.
|
|
60
|
+
type FeatureEntry = FeatureSource & {
|
|
61
|
+
// Does it follow a per-feature NAMING convention? See `featureShaped`.
|
|
62
|
+
shaped: boolean
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
|
|
55
66
|
// Directory name that marks a feature container. Kept exact (not a substring
|
|
56
67
|
// match) so `utility/feature_add.go` and `test/feature_test.go` — which are
|
|
57
68
|
// shared machinery, not per-feature source — are never treated as features.
|
|
58
69
|
const FEATURE_DIR = 'feature'
|
|
59
70
|
|
|
60
71
|
|
|
72
|
+
// The always-present foundation every other feature builds on. It has no
|
|
73
|
+
// model file, so it is in no catalogue, and it must never be trimmed or
|
|
74
|
+
// reported as an unrecognised stray — every target ships one.
|
|
75
|
+
const BASE_FEATURE = 'base'
|
|
76
|
+
|
|
77
|
+
|
|
61
78
|
// Derive the feature name an entry inside a feature directory belongs to.
|
|
62
79
|
// Returns the lowercased name, which the caller then checks against the
|
|
63
80
|
// available set.
|
|
@@ -88,25 +105,51 @@ function featureOf(entry: string, folder: boolean): string {
|
|
|
88
105
|
// Feature names this generator can supply, read from the scaffold's
|
|
89
106
|
// `model/feature/*.aontu`. This is the authoritative catalogue: a name not in
|
|
90
107
|
// it is not a feature, so nothing outside it is ever excluded.
|
|
108
|
+
//
|
|
109
|
+
// LOWERCASED, unlike `definitionNames`, because these are matched against
|
|
110
|
+
// names DERIVED FROM FILENAMES (`RetryFeature.swift` -> `retry`), and the
|
|
111
|
+
// languages disagree about case. The manifest compares definition names as
|
|
112
|
+
// written, so the two callers cannot share the lowercasing.
|
|
91
113
|
function availableFeatures(fs: any, sdkfolder: string): string[] {
|
|
92
|
-
|
|
114
|
+
return definitionNames(fs, sdkfolder, 'feature')
|
|
115
|
+
.map((n: string) => n.toLowerCase())
|
|
116
|
+
.sort()
|
|
117
|
+
}
|
|
118
|
+
|
|
93
119
|
|
|
94
|
-
|
|
95
|
-
|
|
120
|
+
// Does the entry LOOK like per-feature source, whatever it is called?
|
|
121
|
+
//
|
|
122
|
+
// The naming conventions carry the intent: `<name>_feature.<ext>`,
|
|
123
|
+
// `<Name>Feature.<ext>`, and a directory named for the feature. A bare
|
|
124
|
+
// `<name>.<ext>` does NOT — `retry.rs` and `support.rs` are written the same
|
|
125
|
+
// way, so shape cannot separate rust's feature source from rust's shared
|
|
126
|
+
// machinery, and only the catalogue can. That is the deliberate blind spot of
|
|
127
|
+
// `package check`'s unrecognised-source finding: it reports what it can prove
|
|
128
|
+
// looks like a feature, and stays quiet where it would have to guess.
|
|
129
|
+
function featureShaped(entry: string, folder: boolean): boolean {
|
|
130
|
+
if (folder) {
|
|
131
|
+
return true
|
|
96
132
|
}
|
|
97
133
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
.sort()
|
|
134
|
+
const stem = entry.replace(/\.[^.]+$/, '')
|
|
135
|
+
|
|
136
|
+
return /_feature$/i.test(stem) || /Feature$/.test(stem)
|
|
102
137
|
}
|
|
103
138
|
|
|
104
139
|
|
|
105
|
-
//
|
|
106
|
-
//
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
140
|
+
// Every entry inside a feature container of a target's template tree —
|
|
141
|
+
// whether or not its derived name is a feature this generator knows.
|
|
142
|
+
//
|
|
143
|
+
// `known` decides DESCENT, not membership: a directory that names a known
|
|
144
|
+
// feature is the whole feature, so the walk stops there, and one that does
|
|
145
|
+
// not is walked through (`src/feature/base/` holds `BaseFeature.ts`, and the
|
|
146
|
+
// container rule below then ignores it because its parent is not `feature`).
|
|
147
|
+
// The unknown entries are what `package check` reports and what
|
|
148
|
+
// `findFeatureSources` drops; both need the same walk, so there is one.
|
|
149
|
+
function findFeatureEntries(
|
|
150
|
+
fs: any, tmfolder: string, known: Set<string>,
|
|
151
|
+
): FeatureEntry[] {
|
|
152
|
+
const found: FeatureEntry[] = []
|
|
110
153
|
|
|
111
154
|
if (!fs.existsSync(tmfolder)) {
|
|
112
155
|
return found
|
|
@@ -118,6 +161,14 @@ function findFeatureSources(fs: any, tmfolder: string, available: string[]): Fea
|
|
|
118
161
|
const entries = fs.readdirSync(abs).sort()
|
|
119
162
|
|
|
120
163
|
for (const entry of entries) {
|
|
164
|
+
// A `__pycache__` inside `src/feature/` is not a feature — but every
|
|
165
|
+
// rule here derives a feature NAME from an entry name, so without this
|
|
166
|
+
// it becomes one: reported by `package check` as an unknown feature, and
|
|
167
|
+
// trimmed (or not) as if it were source. See helpers/junk.
|
|
168
|
+
if (isJunk(entry)) {
|
|
169
|
+
continue
|
|
170
|
+
}
|
|
171
|
+
|
|
121
172
|
const entryrel = '' === rel ? entry : rel + '/' + entry
|
|
122
173
|
const folder = fs.statSync(Path.join(tmfolder, entryrel)).isDirectory()
|
|
123
174
|
|
|
@@ -126,8 +177,12 @@ function findFeatureSources(fs: any, tmfolder: string, available: string[]): Fea
|
|
|
126
177
|
// looking for more.
|
|
127
178
|
if (FEATURE_DIR === Path.basename(rel)) {
|
|
128
179
|
const name = featureOf(entry, folder)
|
|
180
|
+
|
|
181
|
+
found.push({
|
|
182
|
+
name, path: entryrel, folder, shaped: featureShaped(entry, folder),
|
|
183
|
+
})
|
|
184
|
+
|
|
129
185
|
if (known.has(name)) {
|
|
130
|
-
found.push({ name, path: entryrel, folder })
|
|
131
186
|
continue
|
|
132
187
|
}
|
|
133
188
|
}
|
|
@@ -144,6 +199,17 @@ function findFeatureSources(fs: any, tmfolder: string, available: string[]): Fea
|
|
|
144
199
|
}
|
|
145
200
|
|
|
146
201
|
|
|
202
|
+
// Walk a target's template tree and return every per-feature source entry.
|
|
203
|
+
// `tmfolder` is the target template root (`<sdk>/tm/<lang>`).
|
|
204
|
+
function findFeatureSources(fs: any, tmfolder: string, available: string[]): FeatureSource[] {
|
|
205
|
+
const known = new Set(available)
|
|
206
|
+
|
|
207
|
+
return findFeatureEntries(fs, tmfolder, known)
|
|
208
|
+
.filter((e: FeatureEntry) => known.has(e.name))
|
|
209
|
+
.map(({ name, path, folder }: FeatureEntry) => ({ name, path, folder }))
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
|
|
147
213
|
// Turn discovered sources into path patterns for a jostraca `Copy` exclude.
|
|
148
214
|
//
|
|
149
215
|
// jostraca tests each candidate against the path built up during its walk,
|
|
@@ -196,11 +262,15 @@ function srcFeatureExcludes(model: any): RegExp[] {
|
|
|
196
262
|
|
|
197
263
|
export type {
|
|
198
264
|
FeatureSource,
|
|
265
|
+
FeatureEntry,
|
|
199
266
|
}
|
|
200
267
|
|
|
201
268
|
export {
|
|
269
|
+
BASE_FEATURE,
|
|
202
270
|
featureOf,
|
|
271
|
+
featureShaped,
|
|
203
272
|
availableFeatures,
|
|
273
|
+
findFeatureEntries,
|
|
204
274
|
findFeatureSources,
|
|
205
275
|
featureExcludes,
|
|
206
276
|
fullsetExcludes,
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
// WHAT NEVER COUNTS AS CONTENT.
|
|
2
|
+
//
|
|
3
|
+
// Every tree sdkgen reads — the scaffold's `tm/<lang>` and `src/cmp/<lang>`, an
|
|
4
|
+
// external package's `.sdk`, a project's own copies of both — is a DIRECTORY ON
|
|
5
|
+
// A DEVELOPER'S MACHINE, and a developer's machine drops things in directories:
|
|
6
|
+
// `.DS_Store` from the Finder, `__pycache__` from running a python module,
|
|
7
|
+
// `node_modules` from an `npm install` in the wrong shell, a `~` backup from an
|
|
8
|
+
// editor. None of it is authored, none of it is tracked (it is all gitignored),
|
|
9
|
+
// and all of it is invisible in `git status` — so the first thing that notices
|
|
10
|
+
// is whatever compares two trees.
|
|
11
|
+
//
|
|
12
|
+
// It bit as: a stray `tm/py/utility/__pycache__/make_options.cpython-314.pyc`,
|
|
13
|
+
// created in August by running that module in place, copied verbatim by
|
|
14
|
+
// `target add py` into the add output, and reported by the golden-manifest test
|
|
15
|
+
// as toolchain drift. The copy is the real problem — the test only saw it. Left
|
|
16
|
+
// alone, that `.pyc` ships into every consumer's SDK repo, compiled against
|
|
17
|
+
// whatever python the maintainer happened to have.
|
|
18
|
+
//
|
|
19
|
+
// The rule is one list, in one place, applied by everything that walks a tree:
|
|
20
|
+
// the WRITER (jostraca's Copy, through `cmp.Copy.ignore`, plus the two walks
|
|
21
|
+
// target-add does itself) and every READER that compares against what the
|
|
22
|
+
// writer produced (doctor's drift walk, the feature-source scan, the definition
|
|
23
|
+
// listing). A writer and a reader that disagree about what counts report the
|
|
24
|
+
// whole tree as a fork — the same discipline `templateReplacements` and
|
|
25
|
+
// `aliasCmpName` are held to, and for the same reason.
|
|
26
|
+
//
|
|
27
|
+
// MEMBERSHIP IS A PROMISE that no template will ever legitimately carry that
|
|
28
|
+
// name, so this list holds only what a TOOL creates and a person does not: no
|
|
29
|
+
// `build`, `dist`, `bin` or `out` (a template may well ship one), and
|
|
30
|
+
// emphatically no `target` — sdkgen's own model directory is called that, and
|
|
31
|
+
// it is also where cargo and maven put their output. `test/junk.test.ts` checks
|
|
32
|
+
// the list against every name in the shipped scaffold, so an over-broad pattern
|
|
33
|
+
// fails there rather than by silently dropping a file from someone's SDK.
|
|
34
|
+
|
|
35
|
+
// All of these are matched against the BARE NAME of a directory entry, not its
|
|
36
|
+
// path — the same way jostraca's `cmp.Copy.ignore` is applied, so that naming a
|
|
37
|
+
// directory prunes its whole subtree without every caller having to spell out
|
|
38
|
+
// what is inside it.
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
// Droppings that are not output of anything: an editor, the Finder, or a
|
|
42
|
+
// version control system left them, and they occupy no space in the meaning of
|
|
43
|
+
// the tree. Kept separate from BUILD because "would we copy this" and "is this
|
|
44
|
+
// directory empty" are different questions, and only this half answers both —
|
|
45
|
+
// a destination holding `node_modules` is not empty by any reading.
|
|
46
|
+
const NOISE: RegExp[] = [
|
|
47
|
+
// Editors and merges. `~` duplicates jostraca's built-in `IGNORED_RE`,
|
|
48
|
+
// deliberately: that one governs its Copy walk alone, and the walks sdkgen
|
|
49
|
+
// does itself (the prune, the feature scan) have to agree with it.
|
|
50
|
+
/~$/, // editor backup
|
|
51
|
+
/^\.#/, // emacs lock link
|
|
52
|
+
/^#.*#$/, // emacs autosave
|
|
53
|
+
/\.sw[a-p]$/, // vim swap
|
|
54
|
+
/\.(bak|orig|rej)$/, // backups, and what a conflicted merge leaves behind
|
|
55
|
+
|
|
56
|
+
// Operating systems.
|
|
57
|
+
/^\.DS_Store$/,
|
|
58
|
+
/^__MACOSX$/,
|
|
59
|
+
/^Thumbs\.db$/i,
|
|
60
|
+
/^desktop\.ini$/i,
|
|
61
|
+
|
|
62
|
+
// Version control. A nested checkout inside a template tree is never the
|
|
63
|
+
// payload, and copying one produces a project git cannot make sense of.
|
|
64
|
+
/^\.git$/,
|
|
65
|
+
/^\.svn$/,
|
|
66
|
+
/^\.hg$/,
|
|
67
|
+
/^\.idea$/, // not `.vscode` — an SDK may ship editor settings
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
// Toolchain output, per language. Each of these appears exactly when someone
|
|
72
|
+
// runs that language's tools inside a template tree, which is the normal way to
|
|
73
|
+
// check that a template actually works — and none of them is ever the payload.
|
|
74
|
+
const BUILD: RegExp[] = [
|
|
75
|
+
/^node_modules$/,
|
|
76
|
+
/^__pycache__$/,
|
|
77
|
+
/\.py[co]$/,
|
|
78
|
+
/^\.(pytest|mypy|ruff)_cache$/,
|
|
79
|
+
/^\.tox$/,
|
|
80
|
+
/^\.venv$/,
|
|
81
|
+
/^\.gradle$/,
|
|
82
|
+
/\.class$/,
|
|
83
|
+
/^\.dart_tool$/,
|
|
84
|
+
/^\.?zig-cache$/,
|
|
85
|
+
/^zig-out$/,
|
|
86
|
+
/^_build$/, // mix, dune
|
|
87
|
+
/^\.elixir_ls$/,
|
|
88
|
+
/^\.cpcache$/, // clojure
|
|
89
|
+
/^\.stack-work$/,
|
|
90
|
+
/^dist-newstyle$/, // cabal
|
|
91
|
+
/^\.cargo$/,
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
const JUNK: RegExp[] = [...NOISE, ...BUILD]
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
// Is this directory entry something no tree operation should see?
|
|
99
|
+
function isJunk(name: string): boolean {
|
|
100
|
+
return matches(name, JUNK)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
// Weaker: does this entry leave a directory as good as empty? Toolchain output
|
|
105
|
+
// does NOT — it is junk to copy and content to find.
|
|
106
|
+
function isNoise(name: string): boolean {
|
|
107
|
+
return matches(name, NOISE)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
function matches(name: string, patterns: RegExp[]): boolean {
|
|
112
|
+
for (const re of patterns) {
|
|
113
|
+
// These are module-level regexes reused across every entry of every tree.
|
|
114
|
+
// None carries `g` or `y` today, but a `test` on a stateful regex resumes
|
|
115
|
+
// from the previous match and would skip every other file — the exact bug
|
|
116
|
+
// jostraca's own `excluded()` carries a comment about.
|
|
117
|
+
re.lastIndex = 0
|
|
118
|
+
|
|
119
|
+
if (re.test(name)) {
|
|
120
|
+
return true
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return false
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
// The jostraca `cmp` option block, which is how the WRITER side of the rule is
|
|
129
|
+
// applied: jostraca tests `cmp.Copy.ignore` against the bare name of every
|
|
130
|
+
// entry it walks, directories included, and prunes what matches.
|
|
131
|
+
//
|
|
132
|
+
// Passed PER CALL by each action, not left to the Jostraca instance — the same
|
|
133
|
+
// discipline `control.dryrun` is held to, and for a related reason: the actions
|
|
134
|
+
// take whatever instance their caller hands them (`actx.jostraca`), and a
|
|
135
|
+
// consumer that builds its own would otherwise silently lose the filter. A
|
|
136
|
+
// per-call value is merged last and wins.
|
|
137
|
+
//
|
|
138
|
+
// `/~$/` is first, and must stay first while any supported jostraca is at or
|
|
139
|
+
// below 0.31.0. That merge takes this list over its own default of `[/~$/]`
|
|
140
|
+
// with `deep()`, which recursed INTO index 0 — two RegExps are both objects, so
|
|
141
|
+
// it iterated the incoming one's enumerable properties, of which a RegExp has
|
|
142
|
+
// none, and kept the default. Whatever sat at index 0 was silently discarded.
|
|
143
|
+
// Leading with the value already there makes that a no-op instead of a hole,
|
|
144
|
+
// and jostraca's built-in `IGNORED_RE` covers `~` anyway.
|
|
145
|
+
//
|
|
146
|
+
// FIXED UPSTREAM (jostraca `deep` now replaces a custom-constructor value
|
|
147
|
+
// rather than walking into it), so this is compatibility, not a live
|
|
148
|
+
// workaround: harmless on a fixed jostraca, load-bearing on an older one.
|
|
149
|
+
//
|
|
150
|
+
// A FRESH object each call: `deep` mutates its base, and jostraca's merge order
|
|
151
|
+
// leaves these values reachable from options it keeps.
|
|
152
|
+
function copyOpts(): { Copy: { ignore: RegExp[] } } {
|
|
153
|
+
return {
|
|
154
|
+
Copy: {
|
|
155
|
+
ignore: [/~$/, ...JUNK]
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
export {
|
|
162
|
+
JUNK,
|
|
163
|
+
NOISE,
|
|
164
|
+
BUILD,
|
|
165
|
+
isJunk,
|
|
166
|
+
isNoise,
|
|
167
|
+
copyOpts,
|
|
168
|
+
}
|