@reventlessdev/reventless-spec 3.0.0-alpha.142 → 3.0.0-alpha.144

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.
@@ -83,106 +83,109 @@ let readJson = (path: string) =>
83
83
 
84
84
  // ── Entry point ──────────────────────────────────────────────────────────────
85
85
 
86
- let main = async () => {
87
- let argv = NodeProcess.argv->Array.slice(~start=2, ~end=NodeProcess.argv->Array.length)
88
- let flag = key =>
89
- switch argv->Array.indexOf("--" ++ key) {
90
- | -1 => None
91
- | i => argv->Array.get(i + 1)
86
+ type args = {traitPackage: string, host: string, report: string, out: string}
87
+
88
+ let parseArgs = (argv: array<string>): result<args, string> =>
89
+ CliArgs.parse(~strings=["host", "report", "out"], argv)
90
+ ->Result.flatMap(a => a->CliArgs.atMost(1))
91
+ ->Result.flatMap(a =>
92
+ switch (
93
+ a->CliArgs.positionals->Array.get(0),
94
+ a->CliArgs.string("host"),
95
+ a->CliArgs.string("report"),
96
+ a->CliArgs.string("out"),
97
+ ) {
98
+ | (None | Some(""), _, _, _) => Error("<trait-package> is required.")
99
+ | (Some(traitPackage), Some(host), Some(report), Some(out)) =>
100
+ Ok({traitPackage, host, report, out})
101
+ | (Some(_), _, _, _) => Error("--host, --report and --out are all required.")
92
102
  }
103
+ )
93
104
 
94
- switch argv->Array.get(0) {
95
- | None | Some("") | Some("--help") | Some("-h") => {
96
- Console.log(usage)
97
- NodeProcess.exit(argv->Array.length == 0 ? 1 : 0)
105
+ let cli: CliArgs.cli<args> = {bin: "certify-trait", usage, parse: parseArgs}
106
+
107
+ let main = () =>
108
+ CliArgs.run(cli, async ({traitPackage, host, report: reportPath, out}) => {
109
+ // `@scope/trait-attachments` → `Attachments_Conformance`, the same
110
+ // derivation `graft-trait` does for `_Scaffold`.
111
+ let conformanceModule =
112
+ traitPackage
113
+ ->String.split("/")
114
+ ->Array.last
115
+ ->Option.getOr("")
116
+ ->String.replace("trait-", "")
117
+ ->String.split("-")
118
+ ->Array.map(part =>
119
+ part->String.charAt(0)->String.toUpperCase ++
120
+ part->String.slice(~start=1, ~end=part->String.length)
121
+ )
122
+ ->Array.join("") ++ "_Conformance"
123
+ let specifier = `${traitPackage}/src/${conformanceModule}.res.mjs`
124
+ // Resolved from the **caller's** directory: the trait is a dependency of
125
+ // the plugin being certified and deliberately not one of this package.
126
+ let modulePath = try NodeModule.createRequire(
127
+ NodeProcess.cwd() ++ "/index.js",
128
+ )->NodeModule.requireResolve(specifier) catch {
129
+ | _ => specifier
130
+ }
131
+ let conformance: conformanceExports = try await dynImport(
132
+ NodeUrl.pathToFileURL(modulePath)["href"],
133
+ ) catch {
134
+ | _ =>
135
+ fail(
136
+ `${traitPackage} ships no conformance suite (looked for ${specifier}).\n` ++ ` A trait without one cannot be certified — there is nothing to prove.`,
137
+ )
138
+ %raw(`undefined`)
98
139
  }
99
- | Some(traitPackage) =>
100
- switch (flag("host"), flag("report"), flag("out")) {
101
- | (None, _, _) | (_, None, _) | (_, _, None) =>
102
- fail("--host, --report and --out are all required.\n\n" ++ usage)
103
- | (Some(host), Some(reportPath), Some(out)) => {
104
- // `@scope/trait-attachments` → `Attachments_Conformance`, the same
105
- // derivation `graft-trait` does for `_Scaffold`.
106
- let conformanceModule =
107
- traitPackage
108
- ->String.split("/")
109
- ->Array.last
110
- ->Option.getOr("")
111
- ->String.replace("trait-", "")
112
- ->String.split("-")
113
- ->Array.map(part =>
114
- part->String.charAt(0)->String.toUpperCase ++
115
- part->String.slice(~start=1, ~end=part->String.length)
116
- )
117
- ->Array.join("") ++ "_Conformance"
118
- let specifier = `${traitPackage}/src/${conformanceModule}.res.mjs`
119
- // Resolved from the **caller's** directory: the trait is a dependency of
120
- // the plugin being certified and deliberately not one of this package.
121
- let modulePath = try NodeModule.createRequire(
122
- NodeProcess.cwd() ++ "/index.js",
123
- )->NodeModule.requireResolve(specifier) catch {
124
- | _ => specifier
125
- }
126
- let conformance: conformanceExports = try await dynImport(
127
- NodeUrl.pathToFileURL(modulePath)["href"],
128
- ) catch {
129
- | _ =>
130
- fail(
131
- `${traitPackage} ships no conformance suite (looked for ${specifier}).\n` ++ ` A trait without one cannot be certified — there is nothing to prove.`,
132
- )
133
- %raw(`undefined`)
134
- }
135
140
 
136
- let suite = conformance.suiteName(host)
137
- let parsed = switch readJson(reportPath)->Util_Sury.fromJson(reportSchema) {
138
- | value => value
139
- | exception _ =>
140
- fail(`${reportPath} is not a Jest JSON report (expected \`testResults\`).`)
141
- %raw(`undefined`)
142
- }
141
+ let suite = conformance.suiteName(host)
142
+ let parsed = switch readJson(reportPath)->Util_Sury.fromJson(reportSchema) {
143
+ | value => value
144
+ | exception _ =>
145
+ fail(`${reportPath} is not a Jest JSON report (expected \`testResults\`).`)
146
+ %raw(`undefined`)
147
+ }
143
148
 
144
- switch parsed->assertionsFor(~suite) {
145
- // The failure worth catching. A green build that never ran the suite
146
- // looks exactly like one that ran it and passed, so silence is refused
147
- // rather than certified as zero-of-zero.
148
- | [] =>
149
- fail(
150
- `the report contains no suite titled "${suite}".\n` ++
151
- ` Either the conformance binding was never registered, or ${host} is not the ` ++ `name its Spec declares.`,
152
- )
153
- | results => {
154
- let resolveVersion = specifier =>
155
- try {
156
- let entry =
157
- NodeModule.createRequire(
158
- NodeProcess.cwd() ++ "/index.js",
159
- )->NodeModule.requireResolve(specifier)
160
- PackageVersion.fromModuleUrl(NodeUrl.pathToFileURL(entry)["href"])
161
- } catch {
162
- | _ => "0.0.0"
163
- }
164
-
165
- let certificate = TraitCertificate.fromReport(
166
- ~trait=traitPackage,
167
- ~traitVersion=resolveVersion(specifier),
168
- // The framework the suite ran against, read the same way — a trait
169
- // is certified against a version, never in the abstract.
170
- ~framework=resolveVersion("@reventlessdev/reventless-spec/package.json"),
171
- ~host,
172
- ~suite,
173
- ~results,
174
- )
175
- NodeFs.writeFileSync(out, certificate->TraitCertificate.render)
176
- Console.log(`certify-trait: ${certificate->TraitCertificate.summarize}`)
177
- Console.log(`Wrote: ${out}`)
178
- if !(certificate->TraitCertificate.verified) {
179
- NodeProcess.exit(1)
180
- }
149
+ switch parsed->assertionsFor(~suite) {
150
+ // The failure worth catching. A green build that never ran the suite
151
+ // looks exactly like one that ran it and passed, so silence is refused
152
+ // rather than certified as zero-of-zero.
153
+ | [] =>
154
+ fail(
155
+ `the report contains no suite titled "${suite}".\n` ++
156
+ ` Either the conformance binding was never registered, or ${host} is not the ` ++ `name its Spec declares.`,
157
+ )
158
+ | results => {
159
+ let resolveVersion = specifier =>
160
+ try {
161
+ let entry =
162
+ NodeModule.createRequire(NodeProcess.cwd() ++ "/index.js")->NodeModule.requireResolve(
163
+ specifier,
164
+ )
165
+ PackageVersion.fromModuleUrl(NodeUrl.pathToFileURL(entry)["href"])
166
+ } catch {
167
+ | _ => "0.0.0"
181
168
  }
169
+
170
+ let certificate = TraitCertificate.fromReport(
171
+ ~trait=traitPackage,
172
+ ~traitVersion=resolveVersion(specifier),
173
+ // The framework the suite ran against, read the same way — a trait
174
+ // is certified against a version, never in the abstract.
175
+ ~framework=resolveVersion("@reventlessdev/reventless-spec/package.json"),
176
+ ~host,
177
+ ~suite,
178
+ ~results,
179
+ )
180
+ NodeFs.writeFileSync(out, certificate->TraitCertificate.render)
181
+ Console.log(`certify-trait: ${certificate->TraitCertificate.summarize}`)
182
+ Console.log(`Wrote: ${out}`)
183
+ if !(certificate->TraitCertificate.verified) {
184
+ NodeProcess.exit(1)
182
185
  }
183
186
  }
184
187
  }
185
- }
186
- }
188
+ })
187
189
 
188
- main()->Promise.ignore
190
+ // No top-level call: `../../run-certify-trait.mjs` invokes [main], so a test can
191
+ // import this module.
@@ -6,6 +6,8 @@ import * as Nodeurl from "node:url";
6
6
  import * as Nodemodule from "node:module";
7
7
  import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
8
8
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
9
+ import * as Stdlib_Result from "@rescript/runtime/lib/es6/Stdlib_Result.js";
10
+ import * as CliArgs$Reventless from "../CliArgs.res.mjs";
9
11
  import * as Util_Sury$Reventless from "../util/Util_Sury.res.mjs";
10
12
  import * as PackageVersion$Reventless from "../PackageVersion.res.mjs";
11
13
  import * as TraitCertificate$Reventless from "../components/TraitCertificate.res.mjs";
@@ -60,86 +62,99 @@ function readJson(path) {
60
62
  }
61
63
  }
62
64
 
63
- async function main() {
64
- let argv = process.argv.slice(2, process.argv.length);
65
- let flag = key => {
66
- let i = argv.indexOf("--" + key);
67
- if (i !== -1) {
68
- return argv[i + 1 | 0];
69
- }
70
- };
71
- let traitPackage = argv[0];
72
- if (traitPackage !== undefined) {
73
- switch (traitPackage) {
74
- case "" :
75
- case "--help" :
76
- case "-h" :
77
- break;
78
- default:
79
- let match = flag("host");
80
- let match$1 = flag("report");
81
- let match$2 = flag("out");
82
- if (match === undefined) {
83
- return fail("--host, --report and --out are all required.\n\n" + usage);
84
- }
85
- if (match$1 === undefined) {
86
- return fail("--host, --report and --out are all required.\n\n" + usage);
87
- }
88
- if (match$2 === undefined) {
89
- return fail("--host, --report and --out are all required.\n\n" + usage);
90
- }
91
- let conformanceModule = Stdlib_Option.getOr(Stdlib_Array.last(traitPackage.split("/")), "").replace("trait-", "").split("-").map(part => part.charAt(0).toUpperCase() + part.slice(1, part.length)).join("") + "_Conformance";
92
- let specifier = traitPackage + `/src/` + conformanceModule + `.res.mjs`;
93
- let modulePath;
94
- try {
95
- modulePath = Nodemodule.createRequire(process.cwd() + "/index.js").resolve(specifier);
96
- } catch (exn) {
97
- modulePath = specifier;
98
- }
99
- let conformance;
100
- try {
101
- conformance = await import(Nodeurl.pathToFileURL(modulePath).href);
102
- } catch (exn$1) {
103
- fail(traitPackage + ` ships no conformance suite (looked for ` + specifier + `).\n A trait without one cannot be certified — there is nothing to prove.`);
104
- conformance = undefined;
105
- }
106
- let suite = conformance.suiteName(match);
107
- let parsed;
108
- try {
109
- parsed = Util_Sury$Reventless.fromJson(readJson(match$1), reportSchema);
110
- } catch (exn$2) {
111
- fail(match$1 + ` is not a Jest JSON report (expected \`testResults\`).`);
112
- parsed = undefined;
113
- }
114
- let results = assertionsFor(parsed, suite);
115
- if (results.length === 0) {
116
- return fail(`the report contains no suite titled "` + suite + `".\n` + (` Either the conformance binding was never registered, or ` + match + ` is not the `) + `name its Spec declares.`);
117
- }
118
- let resolveVersion = specifier => {
119
- try {
120
- let entry = Nodemodule.createRequire(process.cwd() + "/index.js").resolve(specifier);
121
- return PackageVersion$Reventless.fromModuleUrl(Nodeurl.pathToFileURL(entry).href);
122
- } catch (exn) {
123
- return "0.0.0";
65
+ function parseArgs(argv) {
66
+ return Stdlib_Result.flatMap(Stdlib_Result.flatMap(CliArgs$Reventless.parse([
67
+ "host",
68
+ "report",
69
+ "out"
70
+ ], undefined, undefined, undefined, argv), a => CliArgs$Reventless.atMost(a, 1)), a => {
71
+ let match = CliArgs$Reventless.positionals(a)[0];
72
+ let match$1 = CliArgs$Reventless.string(a, "host");
73
+ let match$2 = CliArgs$Reventless.string(a, "report");
74
+ let match$3 = CliArgs$Reventless.string(a, "out");
75
+ if (match !== undefined && match !== "") {
76
+ if (match$1 !== undefined && match$2 !== undefined && match$3 !== undefined) {
77
+ return {
78
+ TAG: "Ok",
79
+ _0: {
80
+ traitPackage: match,
81
+ host: match$1,
82
+ report: match$2,
83
+ out: match$3
124
84
  }
125
85
  };
126
- let certificate = TraitCertificate$Reventless.fromReport(traitPackage, resolveVersion(specifier), resolveVersion("@reventlessdev/reventless-spec/package.json"), match, suite, results);
127
- Nodefs.writeFileSync(match$2, TraitCertificate$Reventless.render(certificate), "utf8");
128
- console.log(`certify-trait: ` + TraitCertificate$Reventless.summarize(certificate));
129
- console.log(`Wrote: ` + match$2);
130
- if (!TraitCertificate$Reventless.verified(certificate)) {
131
- process.exit(1);
132
- return;
133
- } else {
134
- return;
135
- }
86
+ } else {
87
+ return {
88
+ TAG: "Error",
89
+ _0: "--host, --report and --out are all required."
90
+ };
91
+ }
92
+ } else {
93
+ return {
94
+ TAG: "Error",
95
+ _0: "<trait-package> is required."
96
+ };
136
97
  }
137
- }
138
- console.log(usage);
139
- process.exit(argv.length === 0 ? 1 : 0);
98
+ });
140
99
  }
141
100
 
142
- main();
101
+ let cli = {
102
+ bin: "certify-trait",
103
+ usage: usage,
104
+ parse: parseArgs
105
+ };
106
+
107
+ function main() {
108
+ return CliArgs$Reventless.run(cli, undefined, undefined, async param => {
109
+ let out = param.out;
110
+ let reportPath = param.report;
111
+ let host = param.host;
112
+ let traitPackage = param.traitPackage;
113
+ let conformanceModule = Stdlib_Option.getOr(Stdlib_Array.last(traitPackage.split("/")), "").replace("trait-", "").split("-").map(part => part.charAt(0).toUpperCase() + part.slice(1, part.length)).join("") + "_Conformance";
114
+ let specifier = traitPackage + `/src/` + conformanceModule + `.res.mjs`;
115
+ let modulePath;
116
+ try {
117
+ modulePath = Nodemodule.createRequire(process.cwd() + "/index.js").resolve(specifier);
118
+ } catch (exn) {
119
+ modulePath = specifier;
120
+ }
121
+ let conformance;
122
+ try {
123
+ conformance = await import(Nodeurl.pathToFileURL(modulePath).href);
124
+ } catch (exn$1) {
125
+ fail(traitPackage + ` ships no conformance suite (looked for ` + specifier + `).\n A trait without one cannot be certified — there is nothing to prove.`);
126
+ conformance = undefined;
127
+ }
128
+ let suite = conformance.suiteName(host);
129
+ let parsed;
130
+ try {
131
+ parsed = Util_Sury$Reventless.fromJson(readJson(reportPath), reportSchema);
132
+ } catch (exn$2) {
133
+ fail(reportPath + ` is not a Jest JSON report (expected \`testResults\`).`);
134
+ parsed = undefined;
135
+ }
136
+ let results = assertionsFor(parsed, suite);
137
+ if (results.length === 0) {
138
+ return fail(`the report contains no suite titled "` + suite + `".\n` + (` Either the conformance binding was never registered, or ` + host + ` is not the `) + `name its Spec declares.`);
139
+ }
140
+ let resolveVersion = specifier => {
141
+ try {
142
+ let entry = Nodemodule.createRequire(process.cwd() + "/index.js").resolve(specifier);
143
+ return PackageVersion$Reventless.fromModuleUrl(Nodeurl.pathToFileURL(entry).href);
144
+ } catch (exn) {
145
+ return "0.0.0";
146
+ }
147
+ };
148
+ let certificate = TraitCertificate$Reventless.fromReport(traitPackage, resolveVersion(specifier), resolveVersion("@reventlessdev/reventless-spec/package.json"), host, suite, results);
149
+ Nodefs.writeFileSync(out, TraitCertificate$Reventless.render(certificate), "utf8");
150
+ console.log(`certify-trait: ` + TraitCertificate$Reventless.summarize(certificate));
151
+ console.log(`Wrote: ` + out);
152
+ if (!TraitCertificate$Reventless.verified(certificate)) {
153
+ process.exit(1);
154
+ return;
155
+ }
156
+ });
157
+ }
143
158
 
144
159
  export {
145
160
  fail,
@@ -149,6 +164,8 @@ export {
149
164
  reportSchema,
150
165
  assertionsFor,
151
166
  readJson,
167
+ parseArgs,
168
+ cli,
152
169
  main,
153
170
  }
154
171
  /* reportAssertionSchema Not a pure module */