typespec-hono 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/app.js +29 -7
- package/package.json +2 -2
package/dist/src/app.js
CHANGED
|
@@ -600,7 +600,7 @@ securityFor) {
|
|
|
600
600
|
"\t\t\t},",
|
|
601
601
|
"\t\t)",
|
|
602
602
|
].join("\n");
|
|
603
|
-
return { target, text };
|
|
603
|
+
return { target, text, headOnly };
|
|
604
604
|
});
|
|
605
605
|
/**
|
|
606
606
|
* Every identifier this file names, imported from the module that declares it.
|
|
@@ -608,8 +608,21 @@ securityFor) {
|
|
|
608
608
|
* **Derived from what the rendered text actually references, not from what was available.** An
|
|
609
609
|
* unused import fails the lint a generated file has to pass like any other, and a missing one is a
|
|
610
610
|
* file that does not compile. Both have happened.
|
|
611
|
+
*
|
|
612
|
+
* **The text is TOKENISED and compared by equality, never matched name by name.** It used to build
|
|
613
|
+
* `new RegExp("\\b" + identifier + "\\b")` per name, and that was wrong twice over for any
|
|
614
|
+
* identifier containing a `$`, which TypeSpec permits in a model or operation name: `$` is an
|
|
615
|
+
* anchor in a regular expression, and escaping it alone does not help because `\b` is a WORD
|
|
616
|
+
* boundary and `$` is not a word character, so `\b\$select\b` cannot match either. Measured on
|
|
617
|
+
* `op list$Items(...): Item$Ref[]`, every identifier reported ABSENT, the whole
|
|
618
|
+
* `./schemas.gen.js` import was dropped, and the emitted file failed with four
|
|
619
|
+
* `TS2304: Cannot find name`. No diagnostic anywhere.
|
|
620
|
+
*
|
|
621
|
+
* Splitting on the language's own identifier rule and testing membership has neither failure: no
|
|
622
|
+
* metacharacter can be misread, and `Foo` cannot match `FooExtra`.
|
|
611
623
|
*/
|
|
612
624
|
const rendered = [...registrations.map((r) => r.text), ...methods, ...aliases].join("\n");
|
|
625
|
+
const mentioned = new Set(rendered.match(/[A-Za-z_$][A-Za-z0-9_$]*/g) ?? []);
|
|
613
626
|
const referenced = [
|
|
614
627
|
...new Set(entries.flatMap((entry) => [
|
|
615
628
|
entry.names.path,
|
|
@@ -620,7 +633,7 @@ securityFor) {
|
|
|
620
633
|
entry.names.responses,
|
|
621
634
|
].filter((name) => name !== undefined))),
|
|
622
635
|
]
|
|
623
|
-
.filter((identifier) =>
|
|
636
|
+
.filter((identifier) => mentioned.has(identifier))
|
|
624
637
|
.toSorted();
|
|
625
638
|
const imports = referenced.length === 0
|
|
626
639
|
? ""
|
|
@@ -670,15 +683,24 @@ securityFor) {
|
|
|
670
683
|
// Imported only where a route actually negotiates: an unused import fails the repo's own lint.
|
|
671
684
|
const negotiates = [...grouped.values()].some((group) => group.length > 1);
|
|
672
685
|
// Same rule: imported only where a HEAD operation stands alone on its path.
|
|
673
|
-
const guardsHead = registrations.some((registration) => registration.
|
|
686
|
+
const guardsHead = registrations.some((registration) => registration.headOnly);
|
|
674
687
|
/**
|
|
675
|
-
*
|
|
688
|
+
* **Which runtime imports to write is read from the DATA, never from the rendered text.**
|
|
676
689
|
*
|
|
677
|
-
*
|
|
678
|
-
*
|
|
679
|
-
*
|
|
690
|
+
* Matching a substring of generated output is how one of these went missing already: it tested for
|
|
691
|
+
* `byContentType([`, the emitted call gained arguments before its bracket, the substring stopped
|
|
692
|
+
* matching, the import stopped being written, and the emitted module threw
|
|
680
693
|
* `ReferenceError: byContentType is not defined` at registration. Nothing in the emitter objected,
|
|
681
694
|
* because the check was a string about a string.
|
|
695
|
+
*
|
|
696
|
+
* `guardsHead` above had the same shape, and the sibling library had it in a worse place, deciding
|
|
697
|
+
* whether a parameter gets a wire decoder by `declared.startsWith("z.number()")`. A literal union
|
|
698
|
+
* (`@query size: 10 | 25 | 50`) begins with neither prefix, so every conformant caller of that
|
|
699
|
+
* parameter got a 400 that no document comparison could see.
|
|
700
|
+
*
|
|
701
|
+
* The rendered text is still read to decide which VALIDATOR identifiers to import, and that one is
|
|
702
|
+
* safe in a way these are not: an identifier absent from the text is genuinely not needed, so the
|
|
703
|
+
* check cannot be wrong in the direction that breaks a build.
|
|
682
704
|
*/
|
|
683
705
|
const dispatchesBody = entries.some((entry) => entry.dispatched !== undefined);
|
|
684
706
|
const runtimeModule = JSON.stringify(emitted.options.runtimeModule);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "typespec-hono",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "TypeSpec emitter: generate a Hono server, and the Zod validators it enforces, from an HTTP service definition, agreeing with the OpenAPI document @typespec/openapi3 publishes from the same source.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudflare-workers",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"provenance": true
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"typespec-http-zod": "^0.
|
|
47
|
+
"typespec-http-zod": "^0.6.0"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@hono/zod-openapi": "^1.4.0",
|