@reventlessdev/reventless-spec 3.0.0-alpha.143 → 3.0.0-alpha.145
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 +20 -0
- package/package.json +2 -2
- package/run-certify-trait.mjs +2 -1
- package/run-generator.mjs +2 -1
- package/run-graft-trait.mjs +2 -1
- package/run-platform-generator.mjs +2 -1
- package/run-trait-manifest.mjs +2 -1
- package/src/CliArgs.res +209 -0
- package/src/CliArgs.res.mjs +325 -0
- package/src/components/DcbScopeInference.res +39 -1
- package/src/components/DcbScopeInference.res.mjs +20 -0
- package/src/components/DcbTag.res +68 -3
- package/src/components/DcbTag.res.mjs +76 -12
- package/src/components/DcbValidation.res +42 -1
- package/src/components/DcbValidation.res.mjs +19 -2
- package/src/generator/CertifyTrait.res +96 -93
- package/src/generator/CertifyTrait.res.mjs +92 -75
- package/src/generator/GraftTrait.res +147 -148
- package/src/generator/GraftTrait.res.mjs +131 -128
- package/src/generator/PlatformGenerator.res +50 -31
- package/src/generator/PlatformGenerator.res.mjs +107 -76
- package/src/generator/PluginGenerator.res +29 -29
- package/src/generator/PluginGenerator.res.mjs +87 -55
- package/src/generator/TraitManifestCli.res +69 -68
- package/src/generator/TraitManifestCli.res.mjs +67 -51
- package/src/lifecycle/CheckLifecycleModel.res +203 -189
- package/src/lifecycle/CheckLifecycleModel.res.mjs +146 -141
- package/src/types/PrepareAccounts.res +38 -57
- package/src/types/PrepareAccounts.res.mjs +28 -82
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
import * as Nodefs from "node:fs";
|
|
4
4
|
import * as Nodeurl from "node:url";
|
|
5
5
|
import * as Nodepath from "node:path";
|
|
6
|
-
import * as Stdlib_Dict from "@rescript/runtime/lib/es6/Stdlib_Dict.js";
|
|
7
6
|
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
8
7
|
import * as Nodemodule from "node:module";
|
|
9
8
|
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
10
9
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
10
|
+
import * as Stdlib_Result from "@rescript/runtime/lib/es6/Stdlib_Result.js";
|
|
11
|
+
import * as CliArgs$Reventless from "../CliArgs.res.mjs";
|
|
11
12
|
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
12
13
|
import * as Util_Sury$Reventless from "../util/Util_Sury.res.mjs";
|
|
13
14
|
|
|
@@ -26,35 +27,6 @@ let usage = `Usage: graft-trait <trait-package> --into <srcDir> --tests <testsDi
|
|
|
26
27
|
Every other --key value is a field of the trait's config. The trait validates
|
|
27
28
|
them, so run it once to be told what it wants.`;
|
|
28
29
|
|
|
29
|
-
function parseFlags(args) {
|
|
30
|
-
let out = {};
|
|
31
|
-
let go = _i => {
|
|
32
|
-
while (true) {
|
|
33
|
-
let i = _i;
|
|
34
|
-
let arg = args[i];
|
|
35
|
-
if (arg === undefined) {
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
if (arg.startsWith("--")) {
|
|
39
|
-
let key = arg.slice(2, arg.length);
|
|
40
|
-
let value = args[i + 1 | 0];
|
|
41
|
-
if (value !== undefined && !value.startsWith("--")) {
|
|
42
|
-
out[key] = value;
|
|
43
|
-
_i = i + 2 | 0;
|
|
44
|
-
continue;
|
|
45
|
-
}
|
|
46
|
-
out[key] = true;
|
|
47
|
-
_i = i + 1 | 0;
|
|
48
|
-
continue;
|
|
49
|
-
}
|
|
50
|
-
_i = i + 1 | 0;
|
|
51
|
-
continue;
|
|
52
|
-
};
|
|
53
|
-
};
|
|
54
|
-
go(0);
|
|
55
|
-
return out;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
30
|
function splitList(value) {
|
|
59
31
|
return value.split(",").map(part => part.trim()).filter(part => part !== "").map(prim => prim);
|
|
60
32
|
}
|
|
@@ -82,112 +54,143 @@ function arrayFieldNames(schema) {
|
|
|
82
54
|
}
|
|
83
55
|
}
|
|
84
56
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
let tests = stringFlag("tests");
|
|
99
|
-
let dryRun = Stdlib_Option.isSome(flags["dry-run"]);
|
|
100
|
-
if (into === undefined) {
|
|
101
|
-
return fail("--into and --tests are both required.\n\n" + usage);
|
|
102
|
-
}
|
|
103
|
-
if (tests === undefined) {
|
|
104
|
-
return fail("--into and --tests are both required.\n\n" + usage);
|
|
105
|
-
}
|
|
106
|
-
let scaffoldModule = Stdlib_Option.getOr(Stdlib_Array.last(traitPackage.split("/")), "").replace("trait-", "").split("-").map(part => part.charAt(0).toUpperCase() + part.slice(1, part.length)).join("") + "_Scaffold";
|
|
107
|
-
let specifier = traitPackage + `/src/` + scaffoldModule + `.res.mjs`;
|
|
108
|
-
let modulePath;
|
|
109
|
-
try {
|
|
110
|
-
modulePath = Nodemodule.createRequire(process.cwd() + "/index.js").resolve(specifier);
|
|
111
|
-
} catch (exn) {
|
|
112
|
-
modulePath = specifier;
|
|
113
|
-
}
|
|
114
|
-
let scaffold;
|
|
115
|
-
try {
|
|
116
|
-
scaffold = await import(Nodeurl.pathToFileURL(modulePath).href);
|
|
117
|
-
} catch (exn$1) {
|
|
118
|
-
fail(traitPackage + ` ships no scaffold (looked for ` + specifier + `).\n Not every trait has one — a trait whose graft is all patches has nothing to write.\n Write the graft by hand instead; its README says what the host must declare.`);
|
|
119
|
-
scaffold = undefined;
|
|
120
|
-
}
|
|
121
|
-
let listFields = arrayFieldNames(scaffold.configSchema);
|
|
122
|
-
let raw = {};
|
|
123
|
-
Object.entries(flags).forEach(param => {
|
|
124
|
-
let value = param[1];
|
|
125
|
-
let key = param[0];
|
|
126
|
-
let match = listFields.includes(key);
|
|
127
|
-
let match$1 = Stdlib_JSON.Decode.string(value);
|
|
128
|
-
if (match && match$1 !== undefined) {
|
|
129
|
-
raw[key] = splitList(match$1);
|
|
130
|
-
} else {
|
|
131
|
-
raw[key] = value;
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
-
[
|
|
135
|
-
"into",
|
|
136
|
-
"tests",
|
|
137
|
-
"dry-run"
|
|
138
|
-
].forEach(k => Stdlib_Dict.$$delete(raw, k));
|
|
139
|
-
let config;
|
|
140
|
-
try {
|
|
141
|
-
config = Util_Sury$Reventless.fromJson(raw, scaffold.configSchema);
|
|
142
|
-
} catch (raw_exn) {
|
|
143
|
-
let exn$2 = Primitive_exceptions.internalToException(raw_exn);
|
|
144
|
-
fail(traitPackage + ` refused this config: ` + Util_Sury$Reventless.exnMessage(exn$2, undefined) + `\n Every --key is a field of the trait's own config; it decides what it needs.`);
|
|
145
|
-
config = undefined;
|
|
146
|
-
}
|
|
147
|
-
let match = scaffold.emit(config, into, tests);
|
|
148
|
-
let patches = match.patches;
|
|
149
|
-
match.files.forEach(param => {
|
|
150
|
-
let contents = param.contents;
|
|
151
|
-
let path = param.path;
|
|
152
|
-
if (dryRun) {
|
|
153
|
-
console.log(`Would write: ` + path + ` (` + contents.split("\n").length.toString() + ` lines)`);
|
|
154
|
-
} else if (Nodefs.existsSync(path)) {
|
|
155
|
-
console.log(`Skipped (exists): ` + path);
|
|
156
|
-
} else {
|
|
157
|
-
Nodefs.mkdirSync(Nodepath.dirname(path), {
|
|
158
|
-
recursive: true
|
|
159
|
-
});
|
|
160
|
-
Nodefs.writeFileSync(path, contents, "utf8");
|
|
161
|
-
console.log(`Wrote: ` + path);
|
|
162
|
-
}
|
|
163
|
-
});
|
|
164
|
-
if (patches.length !== 0) {
|
|
165
|
-
console.log("");
|
|
166
|
-
console.log("── Paste these; they go into files you already own ──────────────");
|
|
167
|
-
patches.forEach(param => {
|
|
168
|
-
console.log("");
|
|
169
|
-
console.log(`# ` + param.into + ` — ` + param.at);
|
|
170
|
-
console.log(param.contents);
|
|
171
|
-
});
|
|
172
|
-
console.log("");
|
|
173
|
-
console.log("Printed rather than written: placing an arm in an existing ordered `switch`\nis an AST operation, and a text splice into the wrong arm is a bug the\ncompiler cannot see.");
|
|
174
|
-
}
|
|
175
|
-
process.exit(0);
|
|
176
|
-
return;
|
|
57
|
+
function parseArgs(argv) {
|
|
58
|
+
return Stdlib_Result.flatMap(Stdlib_Result.flatMap(CliArgs$Reventless.parse([
|
|
59
|
+
"into",
|
|
60
|
+
"tests"
|
|
61
|
+
], ["dry-run"], undefined, true, argv), a => CliArgs$Reventless.atMost(a, 1)), a => {
|
|
62
|
+
let match = CliArgs$Reventless.positionals(a)[0];
|
|
63
|
+
let match$1 = CliArgs$Reventless.string(a, "into");
|
|
64
|
+
let match$2 = CliArgs$Reventless.string(a, "tests");
|
|
65
|
+
if (match === undefined) {
|
|
66
|
+
return {
|
|
67
|
+
TAG: "Error",
|
|
68
|
+
_0: "<trait-package> is required."
|
|
69
|
+
};
|
|
177
70
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
71
|
+
if (match === "") {
|
|
72
|
+
return {
|
|
73
|
+
TAG: "Error",
|
|
74
|
+
_0: "<trait-package> is required."
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
if (match$1 === undefined) {
|
|
78
|
+
return {
|
|
79
|
+
TAG: "Error",
|
|
80
|
+
_0: "--into and --tests are both required."
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
if (match$2 === undefined) {
|
|
84
|
+
return {
|
|
85
|
+
TAG: "Error",
|
|
86
|
+
_0: "--into and --tests are both required."
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
let fields = {};
|
|
90
|
+
CliArgs$Reventless.pairs(a).forEach(param => {
|
|
91
|
+
let value = param[1];
|
|
92
|
+
let tmp;
|
|
93
|
+
tmp = typeof value !== "object" ? true : value._0;
|
|
94
|
+
fields[param[0]] = tmp;
|
|
95
|
+
});
|
|
96
|
+
return {
|
|
97
|
+
TAG: "Ok",
|
|
98
|
+
_0: {
|
|
99
|
+
traitPackage: match,
|
|
100
|
+
into: match$1,
|
|
101
|
+
tests: match$2,
|
|
102
|
+
dryRun: CliArgs$Reventless.bool(a, "dry-run"),
|
|
103
|
+
fields: fields
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
});
|
|
181
107
|
}
|
|
182
108
|
|
|
183
|
-
|
|
109
|
+
let cli = {
|
|
110
|
+
bin: "graft-trait",
|
|
111
|
+
usage: usage,
|
|
112
|
+
parse: parseArgs
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
function main() {
|
|
116
|
+
return CliArgs$Reventless.run(cli, undefined, undefined, async param => {
|
|
117
|
+
let dryRun = param.dryRun;
|
|
118
|
+
let traitPackage = param.traitPackage;
|
|
119
|
+
let scaffoldModule = Stdlib_Option.getOr(Stdlib_Array.last(traitPackage.split("/")), "").replace("trait-", "").split("-").map(part => part.charAt(0).toUpperCase() + part.slice(1, part.length)).join("") + "_Scaffold";
|
|
120
|
+
let specifier = traitPackage + `/src/` + scaffoldModule + `.res.mjs`;
|
|
121
|
+
let modulePath;
|
|
122
|
+
try {
|
|
123
|
+
modulePath = Nodemodule.createRequire(process.cwd() + "/index.js").resolve(specifier);
|
|
124
|
+
} catch (exn) {
|
|
125
|
+
modulePath = specifier;
|
|
126
|
+
}
|
|
127
|
+
let scaffold;
|
|
128
|
+
try {
|
|
129
|
+
scaffold = await import(Nodeurl.pathToFileURL(modulePath).href);
|
|
130
|
+
} catch (exn$1) {
|
|
131
|
+
fail(traitPackage + ` ships no scaffold (looked for ` + specifier + `).\n Not every trait has one — a trait whose graft is all patches has nothing to write.\n Write the graft by hand instead; its README says what the host must declare.`);
|
|
132
|
+
scaffold = undefined;
|
|
133
|
+
}
|
|
134
|
+
let listFields = arrayFieldNames(scaffold.configSchema);
|
|
135
|
+
let raw = {};
|
|
136
|
+
Object.entries(param.fields).forEach(param => {
|
|
137
|
+
let value = param[1];
|
|
138
|
+
let key = param[0];
|
|
139
|
+
let match = listFields.includes(key);
|
|
140
|
+
let match$1 = Stdlib_JSON.Decode.string(value);
|
|
141
|
+
if (match && match$1 !== undefined) {
|
|
142
|
+
raw[key] = splitList(match$1);
|
|
143
|
+
} else {
|
|
144
|
+
raw[key] = value;
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
let config;
|
|
148
|
+
try {
|
|
149
|
+
config = Util_Sury$Reventless.fromJson(raw, scaffold.configSchema);
|
|
150
|
+
} catch (raw_exn) {
|
|
151
|
+
let exn$2 = Primitive_exceptions.internalToException(raw_exn);
|
|
152
|
+
fail(traitPackage + ` refused this config: ` + Util_Sury$Reventless.exnMessage(exn$2, undefined) + `\n Every --key is a field of the trait's own config; it decides what it needs.`);
|
|
153
|
+
config = undefined;
|
|
154
|
+
}
|
|
155
|
+
let match = scaffold.emit(config, param.into, param.tests);
|
|
156
|
+
let patches = match.patches;
|
|
157
|
+
match.files.forEach(param => {
|
|
158
|
+
let contents = param.contents;
|
|
159
|
+
let path = param.path;
|
|
160
|
+
if (dryRun) {
|
|
161
|
+
console.log(`Would write: ` + path + ` (` + contents.split("\n").length.toString() + ` lines)`);
|
|
162
|
+
} else if (Nodefs.existsSync(path)) {
|
|
163
|
+
console.log(`Skipped (exists): ` + path);
|
|
164
|
+
} else {
|
|
165
|
+
Nodefs.mkdirSync(Nodepath.dirname(path), {
|
|
166
|
+
recursive: true
|
|
167
|
+
});
|
|
168
|
+
Nodefs.writeFileSync(path, contents, "utf8");
|
|
169
|
+
console.log(`Wrote: ` + path);
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
if (patches.length !== 0) {
|
|
173
|
+
console.log("");
|
|
174
|
+
console.log("── Paste these; they go into files you already own ──────────────");
|
|
175
|
+
patches.forEach(param => {
|
|
176
|
+
console.log("");
|
|
177
|
+
console.log(`# ` + param.into + ` — ` + param.at);
|
|
178
|
+
console.log(param.contents);
|
|
179
|
+
});
|
|
180
|
+
console.log("");
|
|
181
|
+
console.log("Printed rather than written: placing an arm in an existing ordered `switch`\nis an AST operation, and a text splice into the wrong arm is a bug the\ncompiler cannot see.");
|
|
182
|
+
}
|
|
183
|
+
process.exit(0);
|
|
184
|
+
});
|
|
185
|
+
}
|
|
184
186
|
|
|
185
187
|
export {
|
|
186
188
|
fail,
|
|
187
189
|
usage,
|
|
188
|
-
parseFlags,
|
|
189
190
|
splitList,
|
|
190
191
|
arrayFieldNames,
|
|
192
|
+
parseArgs,
|
|
193
|
+
cli,
|
|
191
194
|
main,
|
|
192
195
|
}
|
|
193
|
-
/*
|
|
196
|
+
/* node:fs Not a pure module */
|
|
@@ -22,12 +22,28 @@ let asObject = (json: JSON.t): option<dict<JSON.t>> => json->JSON.Decode.object
|
|
|
22
22
|
let stringAt = (obj: dict<JSON.t>, field: string): option<string> =>
|
|
23
23
|
obj->Dict.get(field)->Option.flatMap(JSON.Decode.string)
|
|
24
24
|
|
|
25
|
-
let
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
let usage = `Usage: generate-platform <deploy-manifest.yaml>
|
|
26
|
+
|
|
27
|
+
Reads the plugins the deploy manifest lists, unions their committed
|
|
28
|
+
capabilities.json manifests, and writes the platform's
|
|
29
|
+
src/PlatformCapabilities.res.`
|
|
30
|
+
|
|
31
|
+
type args = {manifest: string}
|
|
32
|
+
|
|
33
|
+
let parseArgs = (argv: array<string>): result<args, string> =>
|
|
34
|
+
CliArgs.parse(argv)
|
|
35
|
+
->Result.flatMap(a => a->CliArgs.atMost(1))
|
|
36
|
+
->Result.flatMap(a =>
|
|
37
|
+
switch a->CliArgs.positionals->Array.get(0) {
|
|
38
|
+
| None | Some("") => Error("<deploy-manifest.yaml> is required.")
|
|
39
|
+
| Some(manifest) => Ok({manifest: manifest})
|
|
40
|
+
}
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
let cli: CliArgs.cli<args> = {bin: "generate-platform", usage, parse: parseArgs}
|
|
44
|
+
|
|
45
|
+
let main = () =>
|
|
46
|
+
CliArgs.run(cli, async ({manifest: manifestArg}) => {
|
|
31
47
|
let manifestPath = NodePath.resolve([manifestArg])
|
|
32
48
|
if !NodeFs.existsSync(manifestPath) {
|
|
33
49
|
fail(`${manifestPath} not found`)
|
|
@@ -52,16 +68,18 @@ let () = {
|
|
|
52
68
|
->Option.flatMap(m => m->Dict.get("plugins"))
|
|
53
69
|
->Option.flatMap(JSON.Decode.array)
|
|
54
70
|
->Option.map(entries =>
|
|
55
|
-
entries->Array.filterMap(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
71
|
+
entries->Array.filterMap(
|
|
72
|
+
entry => {
|
|
73
|
+
let obj = entry->asObject
|
|
74
|
+
switch (
|
|
75
|
+
obj->Option.flatMap(o => o->stringAt("name")),
|
|
76
|
+
obj->Option.flatMap(o => o->stringAt("path")),
|
|
77
|
+
) {
|
|
78
|
+
| (Some(name), Some(path)) => Some((name, path))
|
|
79
|
+
| _ => None
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
)
|
|
65
83
|
)
|
|
66
84
|
|
|
67
85
|
switch (platformPath, plugins) {
|
|
@@ -89,20 +107,22 @@ let () = {
|
|
|
89
107
|
[]
|
|
90
108
|
}
|
|
91
109
|
| Manifests(manifests) =>
|
|
92
|
-
manifests->Array.map(
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
110
|
+
manifests->Array.map(
|
|
111
|
+
({path, via}) => {
|
|
112
|
+
Console.log(
|
|
113
|
+
`Read: ${pluginName} — ${path} (via ${PlatformManifests.describeVia(via)})`,
|
|
114
|
+
)
|
|
115
|
+
let manifest = try NodeFs.readFileSync(path)
|
|
116
|
+
->JSON.parseOrThrow
|
|
117
|
+
->S.parseOrThrow(~to=CapabilityManifest.schema) catch {
|
|
118
|
+
| _ => {
|
|
119
|
+
fail(`could not parse ${path} as a capability manifest`)
|
|
120
|
+
({capabilities: []}: CapabilityManifest.t)
|
|
121
|
+
}
|
|
102
122
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
123
|
+
({pluginName, manifest}: PlatformCodegen.pluginManifest)
|
|
124
|
+
},
|
|
125
|
+
)
|
|
106
126
|
}
|
|
107
127
|
})
|
|
108
128
|
|
|
@@ -122,5 +142,4 @@ let () = {
|
|
|
122
142
|
}
|
|
123
143
|
| _ => fail(`${manifestPath} needs a \`platform.path\` and a \`plugins\` list with name + path`)
|
|
124
144
|
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
145
|
+
})
|
|
@@ -7,6 +7,8 @@ import * as Nodepath from "node:path";
|
|
|
7
7
|
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
8
8
|
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
9
9
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
10
|
+
import * as Stdlib_Result from "@rescript/runtime/lib/es6/Stdlib_Result.js";
|
|
11
|
+
import * as CliArgs$Reventless from "../CliArgs.res.mjs";
|
|
10
12
|
import * as PlatformCodegen$Reventless from "./PlatformCodegen.res.mjs";
|
|
11
13
|
import * as PlatformManifests$Reventless from "./PlatformManifests.res.mjs";
|
|
12
14
|
import * as CapabilityManifest$Reventless from "../components/CapabilityManifest.res.mjs";
|
|
@@ -24,91 +26,120 @@ function stringAt(obj, field) {
|
|
|
24
26
|
return Stdlib_Option.flatMap(obj[field], Stdlib_JSON.Decode.string);
|
|
25
27
|
}
|
|
26
28
|
|
|
27
|
-
let
|
|
29
|
+
let usage = `Usage: generate-platform <deploy-manifest.yaml>
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
} catch (exn) {
|
|
42
|
-
fail(`could not parse ` + manifestPath + ` as YAML`);
|
|
43
|
-
deployManifest = null;
|
|
44
|
-
}
|
|
45
|
-
let platformPath = Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(deployManifest), m => m["platform"]), asObject), p => stringAt(p, "path"));
|
|
46
|
-
let plugins = Stdlib_Option.map(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(deployManifest), m => m["plugins"]), Stdlib_JSON.Decode.array), entries => Stdlib_Array.filterMap(entries, entry => {
|
|
47
|
-
let obj = Stdlib_JSON.Decode.object(entry);
|
|
48
|
-
let match = Stdlib_Option.flatMap(obj, o => stringAt(o, "name"));
|
|
49
|
-
let match$1 = Stdlib_Option.flatMap(obj, o => stringAt(o, "path"));
|
|
50
|
-
if (match !== undefined && match$1 !== undefined) {
|
|
51
|
-
return [
|
|
52
|
-
match,
|
|
53
|
-
match$1
|
|
54
|
-
];
|
|
55
|
-
}
|
|
56
|
-
}));
|
|
57
|
-
if (platformPath !== undefined) {
|
|
58
|
-
if (plugins !== undefined) {
|
|
59
|
-
let pluginManifests = plugins.flatMap(param => {
|
|
60
|
-
let pluginName = param[0];
|
|
61
|
-
let pluginDir = Nodepath.resolve(manifestDir, param[1]);
|
|
62
|
-
if (!Nodefs.existsSync(pluginDir)) {
|
|
63
|
-
fail(`plugin "` + pluginName + `" points at ` + pluginDir + `, which does not exist`);
|
|
64
|
-
}
|
|
65
|
-
let manifests = PlatformManifests$Reventless.resolve(undefined, pluginDir);
|
|
66
|
-
if (typeof manifests !== "object") {
|
|
67
|
-
console.log(`Skipped: ` + pluginName + ` (` + pluginDir + `) — no plugin composition`);
|
|
68
|
-
return [];
|
|
69
|
-
}
|
|
70
|
-
if (manifests.TAG === "Manifests") {
|
|
71
|
-
return manifests._0.map(param => {
|
|
72
|
-
let path = param.path;
|
|
73
|
-
console.log(`Read: ` + pluginName + ` — ` + path + ` (via ` + PlatformManifests$Reventless.describeVia(param.via) + `)`);
|
|
74
|
-
let manifest;
|
|
75
|
-
try {
|
|
76
|
-
manifest = Sury.parseOrThrow(JSON.parse(Nodefs.readFileSync(path, "utf8")), CapabilityManifest$Reventless.schema);
|
|
77
|
-
} catch (exn) {
|
|
78
|
-
fail(`could not parse ` + path + ` as a capability manifest`);
|
|
79
|
-
manifest = {
|
|
80
|
-
capabilities: []
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
return {
|
|
84
|
-
pluginName: pluginName,
|
|
85
|
-
manifest: manifest
|
|
86
|
-
};
|
|
87
|
-
});
|
|
31
|
+
Reads the plugins the deploy manifest lists, unions their committed
|
|
32
|
+
capabilities.json manifests, and writes the platform's
|
|
33
|
+
src/PlatformCapabilities.res.`;
|
|
34
|
+
|
|
35
|
+
function parseArgs(argv) {
|
|
36
|
+
return Stdlib_Result.flatMap(Stdlib_Result.flatMap(CliArgs$Reventless.parse(undefined, undefined, undefined, undefined, argv), a => CliArgs$Reventless.atMost(a, 1)), a => {
|
|
37
|
+
let manifest = CliArgs$Reventless.positionals(a)[0];
|
|
38
|
+
if (manifest !== undefined && manifest !== "") {
|
|
39
|
+
return {
|
|
40
|
+
TAG: "Ok",
|
|
41
|
+
_0: {
|
|
42
|
+
manifest: manifest
|
|
88
43
|
}
|
|
89
|
-
|
|
90
|
-
|
|
44
|
+
};
|
|
45
|
+
} else {
|
|
46
|
+
return {
|
|
47
|
+
TAG: "Error",
|
|
48
|
+
_0: "<deploy-manifest.yaml> is required."
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
let cli = {
|
|
55
|
+
bin: "generate-platform",
|
|
56
|
+
usage: usage,
|
|
57
|
+
parse: parseArgs
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
function main() {
|
|
61
|
+
return CliArgs$Reventless.run(cli, undefined, undefined, async param => {
|
|
62
|
+
let manifestPath = Nodepath.resolve(param.manifest);
|
|
63
|
+
if (!Nodefs.existsSync(manifestPath)) {
|
|
64
|
+
fail(manifestPath + ` not found`);
|
|
65
|
+
}
|
|
66
|
+
let manifestDir = Nodepath.dirname(manifestPath);
|
|
67
|
+
let deployManifest;
|
|
68
|
+
try {
|
|
69
|
+
deployManifest = Yaml.parse(Nodefs.readFileSync(manifestPath, "utf8"));
|
|
70
|
+
} catch (exn) {
|
|
71
|
+
fail(`could not parse ` + manifestPath + ` as YAML`);
|
|
72
|
+
deployManifest = null;
|
|
73
|
+
}
|
|
74
|
+
let platformPath = Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(deployManifest), m => m["platform"]), asObject), p => stringAt(p, "path"));
|
|
75
|
+
let plugins = Stdlib_Option.map(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(deployManifest), m => m["plugins"]), Stdlib_JSON.Decode.array), entries => Stdlib_Array.filterMap(entries, entry => {
|
|
76
|
+
let obj = Stdlib_JSON.Decode.object(entry);
|
|
77
|
+
let match = Stdlib_Option.flatMap(obj, o => stringAt(o, "name"));
|
|
78
|
+
let match$1 = Stdlib_Option.flatMap(obj, o => stringAt(o, "path"));
|
|
79
|
+
if (match !== undefined && match$1 !== undefined) {
|
|
80
|
+
return [
|
|
81
|
+
match,
|
|
82
|
+
match$1
|
|
83
|
+
];
|
|
84
|
+
}
|
|
85
|
+
}));
|
|
86
|
+
if (platformPath === undefined) {
|
|
87
|
+
return fail(manifestPath + ` needs a \`platform.path\` and a \`plugins\` list with name + path`);
|
|
88
|
+
}
|
|
89
|
+
if (plugins === undefined) {
|
|
90
|
+
return fail(manifestPath + ` needs a \`platform.path\` and a \`plugins\` list with name + path`);
|
|
91
|
+
}
|
|
92
|
+
let pluginManifests = plugins.flatMap(param => {
|
|
93
|
+
let pluginName = param[0];
|
|
94
|
+
let pluginDir = Nodepath.resolve(manifestDir, param[1]);
|
|
95
|
+
if (!Nodefs.existsSync(pluginDir)) {
|
|
96
|
+
fail(`plugin "` + pluginName + `" points at ` + pluginDir + `, which does not exist`);
|
|
97
|
+
}
|
|
98
|
+
let manifests = PlatformManifests$Reventless.resolve(undefined, pluginDir);
|
|
99
|
+
if (typeof manifests !== "object") {
|
|
100
|
+
console.log(`Skipped: ` + pluginName + ` (` + pluginDir + `) — no plugin composition`);
|
|
91
101
|
return [];
|
|
92
|
-
});
|
|
93
|
-
let message = PlatformCodegen$Reventless.render(pluginManifests);
|
|
94
|
-
if (message.TAG === "Ok") {
|
|
95
|
-
let outputPath = Nodepath.resolve(manifestDir, platformPath, "src", "PlatformCapabilities.res");
|
|
96
|
-
Nodefs.writeFileSync(outputPath, message._0, "utf8");
|
|
97
|
-
console.log("Generated: " + outputPath);
|
|
98
|
-
} else {
|
|
99
|
-
fail(message._0);
|
|
100
102
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
+
if (manifests.TAG === "Manifests") {
|
|
104
|
+
return manifests._0.map(param => {
|
|
105
|
+
let path = param.path;
|
|
106
|
+
console.log(`Read: ` + pluginName + ` — ` + path + ` (via ` + PlatformManifests$Reventless.describeVia(param.via) + `)`);
|
|
107
|
+
let manifest;
|
|
108
|
+
try {
|
|
109
|
+
manifest = Sury.parseOrThrow(JSON.parse(Nodefs.readFileSync(path, "utf8")), CapabilityManifest$Reventless.schema);
|
|
110
|
+
} catch (exn) {
|
|
111
|
+
fail(`could not parse ` + path + ` as a capability manifest`);
|
|
112
|
+
manifest = {
|
|
113
|
+
capabilities: []
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
pluginName: pluginName,
|
|
118
|
+
manifest: manifest
|
|
119
|
+
};
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
let match = manifests._0;
|
|
123
|
+
fail(`no capabilities.json found for plugin "` + pluginName + `" (` + pluginDir + `) — build the plugin first; its build emits src/capabilities.json beside Plugin.res ` + (`(` + match.evidence + `; expected ` + match.expected + `)`));
|
|
124
|
+
return [];
|
|
125
|
+
});
|
|
126
|
+
let message = PlatformCodegen$Reventless.render(pluginManifests);
|
|
127
|
+
if (message.TAG !== "Ok") {
|
|
128
|
+
return fail(message._0);
|
|
103
129
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
130
|
+
let outputPath = Nodepath.resolve(manifestDir, platformPath, "src", "PlatformCapabilities.res");
|
|
131
|
+
Nodefs.writeFileSync(outputPath, message._0, "utf8");
|
|
132
|
+
console.log("Generated: " + outputPath);
|
|
133
|
+
});
|
|
107
134
|
}
|
|
108
135
|
|
|
109
136
|
export {
|
|
110
137
|
fail,
|
|
111
138
|
asObject,
|
|
112
139
|
stringAt,
|
|
140
|
+
usage,
|
|
141
|
+
parseArgs,
|
|
142
|
+
cli,
|
|
143
|
+
main,
|
|
113
144
|
}
|
|
114
|
-
/*
|
|
145
|
+
/* sury Not a pure module */
|
|
@@ -1,32 +1,33 @@
|
|
|
1
1
|
// Entry point for the plugin generator.
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
(
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if argv2 !== "--aws" {
|
|
23
|
-
Console.error("Usage: generate-plugin <srcDir>")
|
|
24
|
-
Console.error(" generate-plugin --aws <Namespace> <srcDir>")
|
|
2
|
+
|
|
3
|
+
let usage = `Usage: generate-plugin [--aws <Namespace>] <srcDir>
|
|
4
|
+
|
|
5
|
+
<srcDir> the plugin's sources; Plugin.res is written into it
|
|
6
|
+
--aws <Namespace> generate an -aws package instead: Plugin.res and Main.res
|
|
7
|
+
are written into ./src, composing the plugin whose sources
|
|
8
|
+
are <srcDir> under <Namespace>`
|
|
9
|
+
|
|
10
|
+
type args = {variant: Config.variant, srcDir: string}
|
|
11
|
+
|
|
12
|
+
let parseArgs = (argv: array<string>): result<args, string> =>
|
|
13
|
+
CliArgs.parse(~strings=["aws"], argv)
|
|
14
|
+
->Result.flatMap(a => a->CliArgs.atMost(1))
|
|
15
|
+
->Result.flatMap(a =>
|
|
16
|
+
switch (a->CliArgs.string("aws"), a->CliArgs.positionals->Array.get(0)) {
|
|
17
|
+
| (Some(""), _) => Error("--aws needs a value")
|
|
18
|
+
| (_, None | Some("")) => Error("<srcDir> is required.")
|
|
19
|
+
| (None, Some(srcDir)) => Ok({variant: Config.Composition, srcDir})
|
|
20
|
+
| (Some(compositionNamespace), Some(srcDir)) =>
|
|
21
|
+
Ok({variant: Config.Aws({compositionNamespace: compositionNamespace}), srcDir})
|
|
25
22
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
let cli: CliArgs.cli<args> = {bin: "generate-plugin", usage, parse: parseArgs}
|
|
26
|
+
|
|
27
|
+
// A usage mistake exits non-zero, so `prebuild` (and CI) fail instead of
|
|
28
|
+
// continuing green with no Plugin.res generated.
|
|
29
|
+
let main = () =>
|
|
30
|
+
CliArgs.run(cli, async ({variant, srcDir: srcDirArg}) => {
|
|
30
31
|
// Resolve to absolute path (handles relative paths and trailing slashes)
|
|
31
32
|
let srcDir = NodePath.resolve([srcDirArg])
|
|
32
33
|
|
|
@@ -55,5 +56,4 @@ let () = {
|
|
|
55
56
|
NodeFs.writeFileSync(mainPath, mainSource)
|
|
56
57
|
Console.log("Generated: " ++ mainPath)
|
|
57
58
|
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
59
|
+
})
|