@reventlessdev/reventless-spec 3.0.0-alpha.100
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/CHANGELOG.md +931 -0
- package/LICENSE +202 -0
- package/README.md +109 -0
- package/package.json +49 -0
- package/rescript.json +32 -0
- package/run-generator.mjs +2 -0
- package/run-platform-generator.mjs +2 -0
- package/scripts/generate-currency.mjs +215 -0
- package/scripts/iso-4217-list-one.xml +1956 -0
- package/src/AnsiStyle.res +40 -0
- package/src/AnsiStyle.res.mjs +54 -0
- package/src/LogPrefix.res +192 -0
- package/src/LogPrefix.res.mjs +159 -0
- package/src/PackageVersion.res +67 -0
- package/src/PackageVersion.res.mjs +81 -0
- package/src/components/Aggregate.res +64 -0
- package/src/components/Aggregate.res.mjs +2 -0
- package/src/components/AutomationSlice.res +279 -0
- package/src/components/AutomationSlice.res.mjs +30 -0
- package/src/components/CapabilityManifest.res +74 -0
- package/src/components/CapabilityManifest.res.mjs +61 -0
- package/src/components/ComponentKind.res +99 -0
- package/src/components/ComponentKind.res.mjs +125 -0
- package/src/components/Counter.res +24 -0
- package/src/components/Counter.res.mjs +2 -0
- package/src/components/DcbDecode.res +118 -0
- package/src/components/DcbDecode.res.mjs +100 -0
- package/src/components/DcbScopeInference.res +244 -0
- package/src/components/DcbScopeInference.res.mjs +177 -0
- package/src/components/DcbTag.res +1335 -0
- package/src/components/DcbTag.res.mjs +898 -0
- package/src/components/DcbValidation.res +427 -0
- package/src/components/DcbValidation.res.mjs +423 -0
- package/src/components/DisplayName.res +40 -0
- package/src/components/DisplayName.res.mjs +26 -0
- package/src/components/ExtensionPoint.res +27 -0
- package/src/components/ExtensionPoint.res.mjs +2 -0
- package/src/components/InboundTranslationSlice.res +85 -0
- package/src/components/InboundTranslationSlice.res.mjs +2 -0
- package/src/components/OutboundTranslationSlice.res +153 -0
- package/src/components/OutboundTranslationSlice.res.mjs +2 -0
- package/src/components/Plugin.res +538 -0
- package/src/components/Plugin.res.mjs +264 -0
- package/src/components/PluginName.res +39 -0
- package/src/components/PluginName.res.mjs +45 -0
- package/src/components/ReadModel.res +199 -0
- package/src/components/ReadModel.res.mjs +18 -0
- package/src/components/Reference.res +55 -0
- package/src/components/Reference.res.mjs +50 -0
- package/src/components/Snapshot.res +26 -0
- package/src/components/Snapshot.res.mjs +2 -0
- package/src/components/StateAnnotations.res +97 -0
- package/src/components/StateAnnotations.res.mjs +15 -0
- package/src/components/StateChangeSlice.res +131 -0
- package/src/components/StateChangeSlice.res.mjs +2 -0
- package/src/components/StateViewSlice.res +123 -0
- package/src/components/StateViewSlice.res.mjs +2 -0
- package/src/components/Task.res +62 -0
- package/src/components/Task.res.mjs +2 -0
- package/src/generator/Codegen.res +842 -0
- package/src/generator/Codegen.res.mjs +565 -0
- package/src/generator/Config.res +106 -0
- package/src/generator/Config.res.mjs +69 -0
- package/src/generator/Discovery.res +230 -0
- package/src/generator/Discovery.res.mjs +198 -0
- package/src/generator/Generator_Node.res +14 -0
- package/src/generator/Generator_Node.res.mjs +18 -0
- package/src/generator/Pairing.res +460 -0
- package/src/generator/Pairing.res.mjs +415 -0
- package/src/generator/PlatformCodegen.res +207 -0
- package/src/generator/PlatformCodegen.res.mjs +154 -0
- package/src/generator/PlatformGenerator.res +126 -0
- package/src/generator/PlatformGenerator.res.mjs +114 -0
- package/src/generator/PlatformManifests.res +203 -0
- package/src/generator/PlatformManifests.res.mjs +212 -0
- package/src/generator/PluginGenerator.res +57 -0
- package/src/generator/PluginGenerator.res.mjs +73 -0
- package/src/semantic/Bytes.res +54 -0
- package/src/semantic/Bytes.res.mjs +38 -0
- package/src/semantic/Capabilities.res +43 -0
- package/src/semantic/Capabilities.res.mjs +17 -0
- package/src/semantic/Color.res +51 -0
- package/src/semantic/Color.res.mjs +29 -0
- package/src/semantic/Currency.res +598 -0
- package/src/semantic/Currency.res.mjs +743 -0
- package/src/semantic/DateRange.res +148 -0
- package/src/semantic/DateRange.res.mjs +74 -0
- package/src/semantic/Duration.res +53 -0
- package/src/semantic/Duration.res.mjs +26 -0
- package/src/semantic/Email.res +51 -0
- package/src/semantic/Email.res.mjs +31 -0
- package/src/semantic/GeoPoint.res +226 -0
- package/src/semantic/GeoPoint.res.mjs +190 -0
- package/src/semantic/Geocoding.res +127 -0
- package/src/semantic/Geocoding.res.mjs +36 -0
- package/src/semantic/Money.res +196 -0
- package/src/semantic/Money.res.mjs +138 -0
- package/src/semantic/Offload.res +294 -0
- package/src/semantic/Offload.res.mjs +191 -0
- package/src/semantic/Percent.res +53 -0
- package/src/semantic/Percent.res.mjs +33 -0
- package/src/semantic/Phone.res +55 -0
- package/src/semantic/Phone.res.mjs +29 -0
- package/src/semantic/Semantic.res +162 -0
- package/src/semantic/Semantic.res.mjs +95 -0
- package/src/semantic/StorageRef.res +164 -0
- package/src/semantic/StorageRef.res.mjs +111 -0
- package/src/semantic/Url.res +66 -0
- package/src/semantic/Url.res.mjs +48 -0
- package/src/types/Authorization.res +23 -0
- package/src/types/Authorization.res.mjs +33 -0
- package/src/types/Behavior.res +86 -0
- package/src/types/Behavior.res.mjs +2 -0
- package/src/types/DateTime.res +29 -0
- package/src/types/DateTime.res.mjs +16 -0
- package/src/types/EventMapping.res +100 -0
- package/src/types/EventMapping.res.mjs +15 -0
- package/src/types/Handler.res +30 -0
- package/src/types/Handler.res.mjs +2 -0
- package/src/types/Id.res +75 -0
- package/src/types/Id.res.mjs +37 -0
- package/src/types/Identity.res +46 -0
- package/src/types/Identity.res.mjs +51 -0
- package/src/types/Message.res +326 -0
- package/src/types/Message.res.mjs +186 -0
- package/src/types/Projection.res +220 -0
- package/src/types/Projection.res.mjs +44 -0
- package/src/types/QueryEngine.res +123 -0
- package/src/types/QueryEngine.res.mjs +12 -0
- package/src/types/ReadConsistency.res +38 -0
- package/src/types/ReadConsistency.res.mjs +29 -0
- package/src/types/Schedule.res +65 -0
- package/src/types/Schedule.res.mjs +68 -0
- package/src/types/SideEffect.res +45 -0
- package/src/types/SideEffect.res.mjs +2 -0
- package/src/types/StoredEvent.res +46 -0
- package/src/types/StoredEvent.res.mjs +32 -0
- package/src/types/Visibility.res +24 -0
- package/src/types/Visibility.res.mjs +25 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Nodefs from "node:fs";
|
|
4
|
+
import * as Nodepath from "node:path";
|
|
5
|
+
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
6
|
+
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
7
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
8
|
+
import * as PluginName$Reventless from "../components/PluginName.res.mjs";
|
|
9
|
+
|
|
10
|
+
function readJson(path) {
|
|
11
|
+
try {
|
|
12
|
+
return JSON.parse(Nodefs.readFileSync(path, "utf8"));
|
|
13
|
+
} catch (exn) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function getStrField(json, key) {
|
|
19
|
+
return Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(json), d => d[key]), Stdlib_JSON.Decode.string);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function getIntField(json, key) {
|
|
23
|
+
return Stdlib_Option.map(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(json), d => d[key]), Stdlib_JSON.Decode.float), prim => prim | 0);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function getStrArrayField(json, key) {
|
|
27
|
+
return Stdlib_Option.map(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(json), d => d[key]), Stdlib_JSON.Decode.array), arr => Stdlib_Array.filterMap(arr, Stdlib_JSON.Decode.string));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function getComponentRuntime(json) {
|
|
31
|
+
let result = {};
|
|
32
|
+
Stdlib_Option.forEach(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(json), d => d["runtime"]), Stdlib_JSON.Decode.object), runtimeObj => {
|
|
33
|
+
Object.entries(runtimeObj).forEach(param => {
|
|
34
|
+
let entry = param[1];
|
|
35
|
+
result[param[0]] = {
|
|
36
|
+
memorySize: getIntField(entry, "memorySize"),
|
|
37
|
+
timeout: getIntField(entry, "timeout")
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function read(srcDir) {
|
|
45
|
+
let packageJsonPath = Nodepath.join(Nodepath.dirname(srcDir), "package.json");
|
|
46
|
+
let packageJsonName = Stdlib_Option.flatMap(readJson(packageJsonPath), j => getStrField(j, "name"));
|
|
47
|
+
let pluginJsonPath = Nodepath.join(srcDir, "plugin.json");
|
|
48
|
+
let pluginJson = Nodefs.existsSync(pluginJsonPath) ? readJson(pluginJsonPath) : undefined;
|
|
49
|
+
return {
|
|
50
|
+
name: PluginName$Reventless.resolve(Stdlib_Option.flatMap(pluginJson, j => getStrField(j, "name")), packageJsonName),
|
|
51
|
+
heartbeatInterval: Stdlib_Option.getOr(Stdlib_Option.flatMap(pluginJson, j => getIntField(j, "heartbeatInterval")), 5),
|
|
52
|
+
exclude: Stdlib_Option.getOr(Stdlib_Option.flatMap(pluginJson, j => getStrArrayField(j, "exclude")), []),
|
|
53
|
+
componentRuntime: Stdlib_Option.getOr(Stdlib_Option.map(pluginJson, getComponentRuntime), {}),
|
|
54
|
+
variant: "Composition"
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let packageNameToPluginName = PluginName$Reventless.fromPackageName;
|
|
59
|
+
|
|
60
|
+
export {
|
|
61
|
+
packageNameToPluginName,
|
|
62
|
+
readJson,
|
|
63
|
+
getStrField,
|
|
64
|
+
getIntField,
|
|
65
|
+
getStrArrayField,
|
|
66
|
+
getComponentRuntime,
|
|
67
|
+
read,
|
|
68
|
+
}
|
|
69
|
+
/* node:fs Not a pure module */
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
// Directory scanner: walks src/, classifies .res files by parent folder name.
|
|
2
|
+
|
|
3
|
+
// Re-export the single-source `ComponentKind.t` (same constructors, so the bare
|
|
4
|
+
// pattern matches in `Pairing` keep resolving via type inference) and derive the
|
|
5
|
+
// folder classifier from it — one vocabulary shared with the gwt discovery.
|
|
6
|
+
type componentType = ComponentKind.t =
|
|
7
|
+
| StateChangeSlice
|
|
8
|
+
| StateViewSlice
|
|
9
|
+
| StateViewSliceStream
|
|
10
|
+
| AutomationSlice
|
|
11
|
+
| InboundTranslationSlice
|
|
12
|
+
| OutboundTranslationSlice
|
|
13
|
+
| Aggregate
|
|
14
|
+
| ReadModel
|
|
15
|
+
| ReadModelStream
|
|
16
|
+
| Task
|
|
17
|
+
| ExtensionPoint
|
|
18
|
+
| Extension
|
|
19
|
+
|
|
20
|
+
// relPath: path relative to srcDir (e.g. "ReadModel/ProductsProjections.res")
|
|
21
|
+
type discoveredFile = {stem: string, componentType: componentType, epGroup: option<string>, relPath: string}
|
|
22
|
+
|
|
23
|
+
let folderToComponentType = ComponentKind.folderToKind
|
|
24
|
+
|
|
25
|
+
// The intra-plugin grouping band ("chapter") a file belongs to, derived from its
|
|
26
|
+
// path relative to `src/`: the first directory segment that is not a recognised
|
|
27
|
+
// kind-folder. `src/<Chapter>/<Kind>/<Component>.res` → `Some("<Chapter>")`; a file
|
|
28
|
+
// directly under a kind-folder (`src/<Kind>/<Component>.res`) or at the src root →
|
|
29
|
+
// `None`. Uses the single-source `ComponentKind.isKindFolder`, so a chapter read
|
|
30
|
+
// here (build time, disk) agrees with the authoring tool's identical heuristic and
|
|
31
|
+
// with a chapter reflected off the deployed plugin structure. See
|
|
32
|
+
// docs/plans/deployed-chapter-grouping.md.
|
|
33
|
+
let chapterOf = (relPath: string): option<string> => {
|
|
34
|
+
let segments = relPath->String.split("/")
|
|
35
|
+
// Need at least one directory segment before the filename.
|
|
36
|
+
if segments->Array.length < 2 {
|
|
37
|
+
None
|
|
38
|
+
} else {
|
|
39
|
+
switch segments->Array.get(0) {
|
|
40
|
+
| Some(first) if !ComponentKind.isKindFolder(first) => Some(first)
|
|
41
|
+
| _ => None
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Deduplicated (stem → chapter) pairs across the discovered files, sorted by stem
|
|
47
|
+
// for deterministic codegen. Only files that carry a chapter are included; stems are
|
|
48
|
+
// unique within a plugin (validated), so keying by stem cannot collide. Body files
|
|
49
|
+
// (`_Behavior`, `_Projections`, …) share their component's chapter but are never
|
|
50
|
+
// looked up by `Spec.name`, so their presence is harmless.
|
|
51
|
+
let chaptersByStem = (discovered: array<discoveredFile>): array<(string, string)> => {
|
|
52
|
+
let byStem: Dict.t<string> = Dict.make()
|
|
53
|
+
discovered->Array.forEach(d =>
|
|
54
|
+
switch chapterOf(d.relPath) {
|
|
55
|
+
| Some(ch) => byStem->Dict.set(d.stem, ch)
|
|
56
|
+
| None => ()
|
|
57
|
+
}
|
|
58
|
+
)
|
|
59
|
+
byStem
|
|
60
|
+
->Dict.toArray
|
|
61
|
+
->Array.toSorted(((a, _), (b, _)) =>
|
|
62
|
+
if a < b {
|
|
63
|
+
-1.0
|
|
64
|
+
} else if a > b {
|
|
65
|
+
1.0
|
|
66
|
+
} else {
|
|
67
|
+
0.0
|
|
68
|
+
}
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let isAlwaysExcludedDir = (name: string): bool =>
|
|
73
|
+
name === "Plugin" || name === "tests" || name === "lib"
|
|
74
|
+
|
|
75
|
+
// Simple glob matching: supports exact match, `/**` prefix, and `/*` single level.
|
|
76
|
+
let matchesGlob = (path: string, pattern: string): bool =>
|
|
77
|
+
if pattern === "**" {
|
|
78
|
+
true
|
|
79
|
+
} else if not(pattern->String.includes("*")) {
|
|
80
|
+
// Exact file match or directory prefix
|
|
81
|
+
path === pattern || path->String.startsWith(pattern ++ "/")
|
|
82
|
+
} else if pattern->String.endsWith("/**") {
|
|
83
|
+
let prefix = pattern->String.slice(~start=0, ~end=pattern->String.length - 3)
|
|
84
|
+
path === prefix || path->String.startsWith(prefix ++ "/")
|
|
85
|
+
} else if pattern->String.endsWith("/*") {
|
|
86
|
+
let prefix = pattern->String.slice(~start=0, ~end=pattern->String.length - 2)
|
|
87
|
+
path->String.startsWith(prefix ++ "/") &&
|
|
88
|
+
!(path
|
|
89
|
+
->String.slice(~start=prefix->String.length + 1, ~end=path->String.length)
|
|
90
|
+
->String.includes("/"))
|
|
91
|
+
} else {
|
|
92
|
+
path === pattern
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
let isExcluded = (relPath: string, excludePatterns: array<string>): bool =>
|
|
96
|
+
excludePatterns->Array.some(p => matchesGlob(relPath, p))
|
|
97
|
+
|
|
98
|
+
let stemOf = (filename: string): option<string> =>
|
|
99
|
+
if filename->String.endsWith(".res") {
|
|
100
|
+
Some(filename->String.slice(~start=0, ~end=filename->String.length - 4))
|
|
101
|
+
} else {
|
|
102
|
+
None
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
let isSkipped = (stem: string): bool =>
|
|
106
|
+
stem->String.endsWith("Test")
|
|
107
|
+
|| stem->String.endsWith("Fixtures")
|
|
108
|
+
// `_EventMappings` and `_Mappings` files in per-entity Aggregate/ folders
|
|
109
|
+
// are picked up by the dedicated [Pairing.findEventMappings] walker;
|
|
110
|
+
// treating them as Aggregate specs would hunt for a non-existent
|
|
111
|
+
// `_EventMappingsBehavior` / `_MappingsBehavior`.
|
|
112
|
+
|| stem->String.endsWith("_EventMappings")
|
|
113
|
+
|| stem->String.endsWith("_Mappings")
|
|
114
|
+
|
|
115
|
+
// Collect .res files directly in dir (non-recursive) into acc.
|
|
116
|
+
let collectFiles = (
|
|
117
|
+
~dir: string,
|
|
118
|
+
~relDir: string,
|
|
119
|
+
~componentType: componentType,
|
|
120
|
+
~epGroup: option<string>,
|
|
121
|
+
~exclude: array<string>,
|
|
122
|
+
~acc: array<discoveredFile>,
|
|
123
|
+
): unit =>
|
|
124
|
+
Generator_Node.readDir(dir)->Array.forEach(entry => {
|
|
125
|
+
if entry->NodeFs.isFile {
|
|
126
|
+
let filename = entry->NodeFs.direntName
|
|
127
|
+
let relPath = if relDir === "" {filename} else {relDir ++ "/" ++ filename}
|
|
128
|
+
if !isExcluded(relPath, exclude) {
|
|
129
|
+
switch stemOf(filename) {
|
|
130
|
+
| Some(stem) if !isSkipped(stem) => acc->Array.push({stem, componentType, epGroup, relPath})
|
|
131
|
+
| _ => ()
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
// Recursively walk srcDir, classifying files into acc.
|
|
138
|
+
let rec walkDir = (
|
|
139
|
+
~dir: string,
|
|
140
|
+
~relDir: string,
|
|
141
|
+
~parentComponentType: option<componentType>,
|
|
142
|
+
~epGroup: option<string>,
|
|
143
|
+
~exclude: array<string>,
|
|
144
|
+
~acc: array<discoveredFile>,
|
|
145
|
+
): unit =>
|
|
146
|
+
Generator_Node.readDir(dir)->Array.forEach(entry => {
|
|
147
|
+
let entryName = entry->NodeFs.direntName
|
|
148
|
+
let relPath = if relDir === "" {entryName} else {relDir ++ "/" ++ entryName}
|
|
149
|
+
|
|
150
|
+
if isExcluded(relPath, exclude) {
|
|
151
|
+
()
|
|
152
|
+
} else if entry->NodeFs.isDirectory {
|
|
153
|
+
if isAlwaysExcludedDir(entryName) {
|
|
154
|
+
()
|
|
155
|
+
} else {
|
|
156
|
+
switch folderToComponentType(entryName) {
|
|
157
|
+
| Some(ExtensionPoint) =>
|
|
158
|
+
let epDir = NodePath.join([dir, entryName])
|
|
159
|
+
let children = Generator_Node.readDir(epDir)
|
|
160
|
+
let hasSubDirs = children->Array.some(e => e->NodeFs.isDirectory)
|
|
161
|
+
if hasSubDirs {
|
|
162
|
+
// Each subfolder is a named EP group
|
|
163
|
+
children->Array.forEach(child => {
|
|
164
|
+
if child->NodeFs.isDirectory {
|
|
165
|
+
let groupName = child->NodeFs.direntName
|
|
166
|
+
let groupDir = NodePath.join([epDir, groupName])
|
|
167
|
+
let groupRelDir = relPath ++ "/" ++ groupName
|
|
168
|
+
collectFiles(
|
|
169
|
+
~dir=groupDir,
|
|
170
|
+
~relDir=groupRelDir,
|
|
171
|
+
~componentType=ExtensionPoint,
|
|
172
|
+
~epGroup=Some(groupName),
|
|
173
|
+
~exclude,
|
|
174
|
+
~acc,
|
|
175
|
+
)
|
|
176
|
+
}
|
|
177
|
+
})
|
|
178
|
+
} else {
|
|
179
|
+
// Flat EP folder — all .res files are for one extension point
|
|
180
|
+
collectFiles(
|
|
181
|
+
~dir=epDir,
|
|
182
|
+
~relDir=relPath,
|
|
183
|
+
~componentType=ExtensionPoint,
|
|
184
|
+
~epGroup=None,
|
|
185
|
+
~exclude,
|
|
186
|
+
~acc,
|
|
187
|
+
)
|
|
188
|
+
}
|
|
189
|
+
| Some(ct) =>
|
|
190
|
+
// Recurse into component type folder, collecting files at any depth
|
|
191
|
+
walkDir(
|
|
192
|
+
~dir=NodePath.join([dir, entryName]),
|
|
193
|
+
~relDir=relPath,
|
|
194
|
+
~parentComponentType=Some(ct),
|
|
195
|
+
~epGroup=None,
|
|
196
|
+
~exclude,
|
|
197
|
+
~acc,
|
|
198
|
+
)
|
|
199
|
+
| None =>
|
|
200
|
+
// Chapter folder or other — recurse inheriting parent context
|
|
201
|
+
walkDir(
|
|
202
|
+
~dir=NodePath.join([dir, entryName]),
|
|
203
|
+
~relDir=relPath,
|
|
204
|
+
~parentComponentType,
|
|
205
|
+
~epGroup,
|
|
206
|
+
~exclude,
|
|
207
|
+
~acc,
|
|
208
|
+
)
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
} else if entry->NodeFs.isFile {
|
|
212
|
+
switch parentComponentType {
|
|
213
|
+
| None => ()
|
|
214
|
+
| Some(ct) =>
|
|
215
|
+
let filename = entryName
|
|
216
|
+
if !isExcluded(relPath, exclude) {
|
|
217
|
+
switch stemOf(filename) {
|
|
218
|
+
| Some(stem) if !isSkipped(stem) => acc->Array.push({stem, componentType: ct, epGroup, relPath})
|
|
219
|
+
| _ => ()
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
let scan = (~srcDir: string, ~exclude: array<string>): array<discoveredFile> => {
|
|
227
|
+
let acc: array<discoveredFile> = []
|
|
228
|
+
walkDir(~dir=srcDir, ~relDir="", ~parentComponentType=None, ~epGroup=None, ~exclude, ~acc)
|
|
229
|
+
acc
|
|
230
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Nodepath from "node:path";
|
|
4
|
+
import * as ComponentKind$Reventless from "../components/ComponentKind.res.mjs";
|
|
5
|
+
import * as Generator_Node$Reventless from "./Generator_Node.res.mjs";
|
|
6
|
+
|
|
7
|
+
function chapterOf(relPath) {
|
|
8
|
+
let segments = relPath.split("/");
|
|
9
|
+
if (segments.length < 2) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
let first = segments[0];
|
|
13
|
+
if (first !== undefined && !ComponentKind$Reventless.isKindFolder(first)) {
|
|
14
|
+
return first;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function chaptersByStem(discovered) {
|
|
19
|
+
let byStem = {};
|
|
20
|
+
discovered.forEach(d => {
|
|
21
|
+
let ch = chapterOf(d.relPath);
|
|
22
|
+
if (ch !== undefined) {
|
|
23
|
+
byStem[d.stem] = ch;
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
return Object.entries(byStem).toSorted((param, param$1) => {
|
|
28
|
+
let b = param$1[0];
|
|
29
|
+
let a = param[0];
|
|
30
|
+
if (a < b) {
|
|
31
|
+
return -1.0;
|
|
32
|
+
} else if (a > b) {
|
|
33
|
+
return 1.0;
|
|
34
|
+
} else {
|
|
35
|
+
return 0.0;
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function isAlwaysExcludedDir(name) {
|
|
41
|
+
if (name === "Plugin" || name === "tests") {
|
|
42
|
+
return true;
|
|
43
|
+
} else {
|
|
44
|
+
return name === "lib";
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function matchesGlob(path, pattern) {
|
|
49
|
+
if (pattern === "**") {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
if (!pattern.includes("*")) {
|
|
53
|
+
if (path === pattern) {
|
|
54
|
+
return true;
|
|
55
|
+
} else {
|
|
56
|
+
return path.startsWith(pattern + "/");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (pattern.endsWith("/**")) {
|
|
60
|
+
let prefix = pattern.slice(0, pattern.length - 3 | 0);
|
|
61
|
+
if (path === prefix) {
|
|
62
|
+
return true;
|
|
63
|
+
} else {
|
|
64
|
+
return path.startsWith(prefix + "/");
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (!pattern.endsWith("/*")) {
|
|
68
|
+
return path === pattern;
|
|
69
|
+
}
|
|
70
|
+
let prefix$1 = pattern.slice(0, pattern.length - 2 | 0);
|
|
71
|
+
if (path.startsWith(prefix$1 + "/")) {
|
|
72
|
+
return !path.slice(prefix$1.length + 1 | 0, path.length).includes("/");
|
|
73
|
+
} else {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function isExcluded(relPath, excludePatterns) {
|
|
79
|
+
return excludePatterns.some(p => matchesGlob(relPath, p));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function stemOf(filename) {
|
|
83
|
+
if (filename.endsWith(".res")) {
|
|
84
|
+
return filename.slice(0, filename.length - 4 | 0);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function isSkipped(stem) {
|
|
89
|
+
if (stem.endsWith("Test") || stem.endsWith("Fixtures") || stem.endsWith("_EventMappings")) {
|
|
90
|
+
return true;
|
|
91
|
+
} else {
|
|
92
|
+
return stem.endsWith("_Mappings");
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function collectFiles(dir, relDir, componentType, epGroup, exclude, acc) {
|
|
97
|
+
Generator_Node$Reventless.readDir(dir).forEach(entry => {
|
|
98
|
+
if (!entry.isFile()) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
let filename = entry.name;
|
|
102
|
+
let relPath = relDir === "" ? filename : relDir + "/" + filename;
|
|
103
|
+
if (isExcluded(relPath, exclude)) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
let stem = stemOf(filename);
|
|
107
|
+
if (stem !== undefined && !isSkipped(stem)) {
|
|
108
|
+
acc.push({
|
|
109
|
+
stem: stem,
|
|
110
|
+
componentType: componentType,
|
|
111
|
+
epGroup: epGroup,
|
|
112
|
+
relPath: relPath
|
|
113
|
+
});
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function walkDir(dir, relDir, parentComponentType, epGroup, exclude, acc) {
|
|
120
|
+
Generator_Node$Reventless.readDir(dir).forEach(entry => {
|
|
121
|
+
let entryName = entry.name;
|
|
122
|
+
let relPath = relDir === "" ? entryName : relDir + "/" + entryName;
|
|
123
|
+
if (isExcluded(relPath, exclude)) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (entry.isDirectory()) {
|
|
127
|
+
if (isAlwaysExcludedDir(entryName)) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
let ct = ComponentKind$Reventless.folderToKind(entryName);
|
|
131
|
+
if (ct === undefined) {
|
|
132
|
+
return walkDir(Nodepath.join(dir, entryName), relPath, parentComponentType, epGroup, exclude, acc);
|
|
133
|
+
}
|
|
134
|
+
if (ct !== "ExtensionPoint") {
|
|
135
|
+
return walkDir(Nodepath.join(dir, entryName), relPath, ct, undefined, exclude, acc);
|
|
136
|
+
}
|
|
137
|
+
let epDir = Nodepath.join(dir, entryName);
|
|
138
|
+
let children = Generator_Node$Reventless.readDir(epDir);
|
|
139
|
+
let hasSubDirs = children.some(e => e.isDirectory());
|
|
140
|
+
if (hasSubDirs) {
|
|
141
|
+
children.forEach(child => {
|
|
142
|
+
if (!child.isDirectory()) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
let groupName = child.name;
|
|
146
|
+
let groupDir = Nodepath.join(epDir, groupName);
|
|
147
|
+
let groupRelDir = relPath + "/" + groupName;
|
|
148
|
+
collectFiles(groupDir, groupRelDir, "ExtensionPoint", groupName, exclude, acc);
|
|
149
|
+
});
|
|
150
|
+
return;
|
|
151
|
+
} else {
|
|
152
|
+
return collectFiles(epDir, relPath, "ExtensionPoint", undefined, exclude, acc);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (!entry.isFile()) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
if (parentComponentType === undefined) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (isExcluded(relPath, exclude)) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
let stem = stemOf(entryName);
|
|
165
|
+
if (stem !== undefined && !isSkipped(stem)) {
|
|
166
|
+
acc.push({
|
|
167
|
+
stem: stem,
|
|
168
|
+
componentType: parentComponentType,
|
|
169
|
+
epGroup: epGroup,
|
|
170
|
+
relPath: relPath
|
|
171
|
+
});
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function scan(srcDir, exclude) {
|
|
178
|
+
let acc = [];
|
|
179
|
+
walkDir(srcDir, "", undefined, undefined, exclude, acc);
|
|
180
|
+
return acc;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
let folderToComponentType = ComponentKind$Reventless.folderToKind;
|
|
184
|
+
|
|
185
|
+
export {
|
|
186
|
+
folderToComponentType,
|
|
187
|
+
chapterOf,
|
|
188
|
+
chaptersByStem,
|
|
189
|
+
isAlwaysExcludedDir,
|
|
190
|
+
matchesGlob,
|
|
191
|
+
isExcluded,
|
|
192
|
+
stemOf,
|
|
193
|
+
isSkipped,
|
|
194
|
+
collectFiles,
|
|
195
|
+
walkDir,
|
|
196
|
+
scan,
|
|
197
|
+
}
|
|
198
|
+
/* node:path Not a pure module */
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// What remains of the generator's own Node bindings after they moved to
|
|
2
|
+
// `@reventlessdev/rescript-node`: one helper, which is not a binding.
|
|
3
|
+
//
|
|
4
|
+
// The generator walks directories that may legitimately not exist — an
|
|
5
|
+
// `Extension/` folder is optional, and so is every other component folder — and
|
|
6
|
+
// it treats "absent" and "empty" the same way. `readdirSync` throws on absent,
|
|
7
|
+
// so every walk would otherwise carry its own try/catch. This does not belong in
|
|
8
|
+
// the bindings package: a binding that swallows an error is a policy, and the
|
|
9
|
+
// policy is the generator's.
|
|
10
|
+
|
|
11
|
+
let readDir = (dir: string): array<NodeFs.dirent> =>
|
|
12
|
+
try NodeFs.readdirSync(dir, {withFileTypes: true}) catch {
|
|
13
|
+
| _ => []
|
|
14
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Nodefs from "node:fs";
|
|
4
|
+
|
|
5
|
+
function readDir(dir) {
|
|
6
|
+
try {
|
|
7
|
+
return Nodefs.readdirSync(dir, {
|
|
8
|
+
withFileTypes: true
|
|
9
|
+
});
|
|
10
|
+
} catch (exn) {
|
|
11
|
+
return [];
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export {
|
|
16
|
+
readDir,
|
|
17
|
+
}
|
|
18
|
+
/* node:fs Not a pure module */
|