@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.
Files changed (139) hide show
  1. package/CHANGELOG.md +931 -0
  2. package/LICENSE +202 -0
  3. package/README.md +109 -0
  4. package/package.json +49 -0
  5. package/rescript.json +32 -0
  6. package/run-generator.mjs +2 -0
  7. package/run-platform-generator.mjs +2 -0
  8. package/scripts/generate-currency.mjs +215 -0
  9. package/scripts/iso-4217-list-one.xml +1956 -0
  10. package/src/AnsiStyle.res +40 -0
  11. package/src/AnsiStyle.res.mjs +54 -0
  12. package/src/LogPrefix.res +192 -0
  13. package/src/LogPrefix.res.mjs +159 -0
  14. package/src/PackageVersion.res +67 -0
  15. package/src/PackageVersion.res.mjs +81 -0
  16. package/src/components/Aggregate.res +64 -0
  17. package/src/components/Aggregate.res.mjs +2 -0
  18. package/src/components/AutomationSlice.res +279 -0
  19. package/src/components/AutomationSlice.res.mjs +30 -0
  20. package/src/components/CapabilityManifest.res +74 -0
  21. package/src/components/CapabilityManifest.res.mjs +61 -0
  22. package/src/components/ComponentKind.res +99 -0
  23. package/src/components/ComponentKind.res.mjs +125 -0
  24. package/src/components/Counter.res +24 -0
  25. package/src/components/Counter.res.mjs +2 -0
  26. package/src/components/DcbDecode.res +118 -0
  27. package/src/components/DcbDecode.res.mjs +100 -0
  28. package/src/components/DcbScopeInference.res +244 -0
  29. package/src/components/DcbScopeInference.res.mjs +177 -0
  30. package/src/components/DcbTag.res +1335 -0
  31. package/src/components/DcbTag.res.mjs +898 -0
  32. package/src/components/DcbValidation.res +427 -0
  33. package/src/components/DcbValidation.res.mjs +423 -0
  34. package/src/components/DisplayName.res +40 -0
  35. package/src/components/DisplayName.res.mjs +26 -0
  36. package/src/components/ExtensionPoint.res +27 -0
  37. package/src/components/ExtensionPoint.res.mjs +2 -0
  38. package/src/components/InboundTranslationSlice.res +85 -0
  39. package/src/components/InboundTranslationSlice.res.mjs +2 -0
  40. package/src/components/OutboundTranslationSlice.res +153 -0
  41. package/src/components/OutboundTranslationSlice.res.mjs +2 -0
  42. package/src/components/Plugin.res +538 -0
  43. package/src/components/Plugin.res.mjs +264 -0
  44. package/src/components/PluginName.res +39 -0
  45. package/src/components/PluginName.res.mjs +45 -0
  46. package/src/components/ReadModel.res +199 -0
  47. package/src/components/ReadModel.res.mjs +18 -0
  48. package/src/components/Reference.res +55 -0
  49. package/src/components/Reference.res.mjs +50 -0
  50. package/src/components/Snapshot.res +26 -0
  51. package/src/components/Snapshot.res.mjs +2 -0
  52. package/src/components/StateAnnotations.res +97 -0
  53. package/src/components/StateAnnotations.res.mjs +15 -0
  54. package/src/components/StateChangeSlice.res +131 -0
  55. package/src/components/StateChangeSlice.res.mjs +2 -0
  56. package/src/components/StateViewSlice.res +123 -0
  57. package/src/components/StateViewSlice.res.mjs +2 -0
  58. package/src/components/Task.res +62 -0
  59. package/src/components/Task.res.mjs +2 -0
  60. package/src/generator/Codegen.res +842 -0
  61. package/src/generator/Codegen.res.mjs +565 -0
  62. package/src/generator/Config.res +106 -0
  63. package/src/generator/Config.res.mjs +69 -0
  64. package/src/generator/Discovery.res +230 -0
  65. package/src/generator/Discovery.res.mjs +198 -0
  66. package/src/generator/Generator_Node.res +14 -0
  67. package/src/generator/Generator_Node.res.mjs +18 -0
  68. package/src/generator/Pairing.res +460 -0
  69. package/src/generator/Pairing.res.mjs +415 -0
  70. package/src/generator/PlatformCodegen.res +207 -0
  71. package/src/generator/PlatformCodegen.res.mjs +154 -0
  72. package/src/generator/PlatformGenerator.res +126 -0
  73. package/src/generator/PlatformGenerator.res.mjs +114 -0
  74. package/src/generator/PlatformManifests.res +203 -0
  75. package/src/generator/PlatformManifests.res.mjs +212 -0
  76. package/src/generator/PluginGenerator.res +57 -0
  77. package/src/generator/PluginGenerator.res.mjs +73 -0
  78. package/src/semantic/Bytes.res +54 -0
  79. package/src/semantic/Bytes.res.mjs +38 -0
  80. package/src/semantic/Capabilities.res +43 -0
  81. package/src/semantic/Capabilities.res.mjs +17 -0
  82. package/src/semantic/Color.res +51 -0
  83. package/src/semantic/Color.res.mjs +29 -0
  84. package/src/semantic/Currency.res +598 -0
  85. package/src/semantic/Currency.res.mjs +743 -0
  86. package/src/semantic/DateRange.res +148 -0
  87. package/src/semantic/DateRange.res.mjs +74 -0
  88. package/src/semantic/Duration.res +53 -0
  89. package/src/semantic/Duration.res.mjs +26 -0
  90. package/src/semantic/Email.res +51 -0
  91. package/src/semantic/Email.res.mjs +31 -0
  92. package/src/semantic/GeoPoint.res +226 -0
  93. package/src/semantic/GeoPoint.res.mjs +190 -0
  94. package/src/semantic/Geocoding.res +127 -0
  95. package/src/semantic/Geocoding.res.mjs +36 -0
  96. package/src/semantic/Money.res +196 -0
  97. package/src/semantic/Money.res.mjs +138 -0
  98. package/src/semantic/Offload.res +294 -0
  99. package/src/semantic/Offload.res.mjs +191 -0
  100. package/src/semantic/Percent.res +53 -0
  101. package/src/semantic/Percent.res.mjs +33 -0
  102. package/src/semantic/Phone.res +55 -0
  103. package/src/semantic/Phone.res.mjs +29 -0
  104. package/src/semantic/Semantic.res +162 -0
  105. package/src/semantic/Semantic.res.mjs +95 -0
  106. package/src/semantic/StorageRef.res +164 -0
  107. package/src/semantic/StorageRef.res.mjs +111 -0
  108. package/src/semantic/Url.res +66 -0
  109. package/src/semantic/Url.res.mjs +48 -0
  110. package/src/types/Authorization.res +23 -0
  111. package/src/types/Authorization.res.mjs +33 -0
  112. package/src/types/Behavior.res +86 -0
  113. package/src/types/Behavior.res.mjs +2 -0
  114. package/src/types/DateTime.res +29 -0
  115. package/src/types/DateTime.res.mjs +16 -0
  116. package/src/types/EventMapping.res +100 -0
  117. package/src/types/EventMapping.res.mjs +15 -0
  118. package/src/types/Handler.res +30 -0
  119. package/src/types/Handler.res.mjs +2 -0
  120. package/src/types/Id.res +75 -0
  121. package/src/types/Id.res.mjs +37 -0
  122. package/src/types/Identity.res +46 -0
  123. package/src/types/Identity.res.mjs +51 -0
  124. package/src/types/Message.res +326 -0
  125. package/src/types/Message.res.mjs +186 -0
  126. package/src/types/Projection.res +220 -0
  127. package/src/types/Projection.res.mjs +44 -0
  128. package/src/types/QueryEngine.res +123 -0
  129. package/src/types/QueryEngine.res.mjs +12 -0
  130. package/src/types/ReadConsistency.res +38 -0
  131. package/src/types/ReadConsistency.res.mjs +29 -0
  132. package/src/types/Schedule.res +65 -0
  133. package/src/types/Schedule.res.mjs +68 -0
  134. package/src/types/SideEffect.res +45 -0
  135. package/src/types/SideEffect.res.mjs +2 -0
  136. package/src/types/StoredEvent.res +46 -0
  137. package/src/types/StoredEvent.res.mjs +32 -0
  138. package/src/types/Visibility.res +24 -0
  139. 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 */