From 0c26ce7383c4f3eafbc04132a8d70469add26535 Mon Sep 17 00:00:00 2001
From: Davide Canton <davide.canton5@gmail.com>
Date: Sat, 16 Nov 2024 00:34:10 +0100
Subject: [PATCH] feat: uri encoded $refs (#617)

---
 api.md                                      |  7 +++++++
 test/programs/generic-anonymous/schema.json |  4 ++--
 test/programs/generic-multiargs/schema.json |  4 ++--
 test/programs/generic-multiple/schema.json  |  4 ++--
 test/programs/generic-recursive/schema.json |  6 +++---
 test/programs/generic-simple/schema.json    |  2 +-
 typescript-json-schema.ts                   | 20 ++++++++++++++++++--
 7 files changed, 35 insertions(+), 12 deletions(-)

diff --git a/api.md b/api.md
index 7e863391..9a73afdf 100644
--- a/api.md
+++ b/api.md
@@ -1846,6 +1846,13 @@ export type MyTuple = [string, number, boolean?];
 ```
 
 
+## [type-aliases-tuple-with-names](./test/programs/type-aliases-tuple-with-names)
+
+```ts
+export type MyTuple = [a: string, b: 123, c?: boolean, ...d: number[]];
+```
+
+
 ## [type-aliases-tuple-with-rest-element](./test/programs/type-aliases-tuple-with-rest-element)
 
 ```ts
diff --git a/test/programs/generic-anonymous/schema.json b/test/programs/generic-anonymous/schema.json
index 877856f4..578582b9 100644
--- a/test/programs/generic-anonymous/schema.json
+++ b/test/programs/generic-anonymous/schema.json
@@ -37,10 +37,10 @@
     },
     "properties": {
         "value1": {
-            "$ref": "#/definitions/MyGeneric<string,number>"
+            "$ref": "#/definitions/MyGeneric%3Cstring%2Cnumber%3E"
         },
         "value2": {
-            "$ref": "#/definitions/MyGeneric<number,string>"
+            "$ref": "#/definitions/MyGeneric%3Cnumber%2Cstring%3E"
         }
     },
     "required": [
diff --git a/test/programs/generic-multiargs/schema.json b/test/programs/generic-multiargs/schema.json
index 877856f4..578582b9 100644
--- a/test/programs/generic-multiargs/schema.json
+++ b/test/programs/generic-multiargs/schema.json
@@ -37,10 +37,10 @@
     },
     "properties": {
         "value1": {
-            "$ref": "#/definitions/MyGeneric<string,number>"
+            "$ref": "#/definitions/MyGeneric%3Cstring%2Cnumber%3E"
         },
         "value2": {
-            "$ref": "#/definitions/MyGeneric<number,string>"
+            "$ref": "#/definitions/MyGeneric%3Cnumber%2Cstring%3E"
         }
     },
     "required": [
diff --git a/test/programs/generic-multiple/schema.json b/test/programs/generic-multiple/schema.json
index b87cde25..34ac33d9 100644
--- a/test/programs/generic-multiple/schema.json
+++ b/test/programs/generic-multiple/schema.json
@@ -29,10 +29,10 @@
     },
     "properties": {
         "value1": {
-            "$ref": "#/definitions/MyGeneric<number>"
+            "$ref": "#/definitions/MyGeneric%3Cnumber%3E"
         },
         "value2": {
-            "$ref": "#/definitions/MyGeneric<string>"
+            "$ref": "#/definitions/MyGeneric%3Cstring%3E"
         }
     },
     "required": [
diff --git a/test/programs/generic-recursive/schema.json b/test/programs/generic-recursive/schema.json
index c040119b..3fc50458 100644
--- a/test/programs/generic-recursive/schema.json
+++ b/test/programs/generic-recursive/schema.json
@@ -6,7 +6,7 @@
             "additionalProperties": false,
             "properties": {
                 "field": {
-                    "$ref": "#/definitions/MyGeneric<string,number>"
+                    "$ref": "#/definitions/MyGeneric%3Cstring%2Cnumber%3E"
                 }
             },
             "required": [
@@ -18,7 +18,7 @@
             "additionalProperties": false,
             "properties": {
                 "field": {
-                    "$ref": "#/definitions/MyGeneric<number,string>"
+                    "$ref": "#/definitions/MyGeneric%3Cnumber%2Cstring%3E"
                 }
             },
             "required": [
@@ -30,7 +30,7 @@
             "additionalProperties": false,
             "properties": {
                 "value": {
-                    "$ref": "#/definitions/MyGeneric<string,number>"
+                    "$ref": "#/definitions/MyGeneric%3Cstring%2Cnumber%3E"
                 }
             },
             "required": [
diff --git a/test/programs/generic-simple/schema.json b/test/programs/generic-simple/schema.json
index 68bf6c6f..e568f81d 100644
--- a/test/programs/generic-simple/schema.json
+++ b/test/programs/generic-simple/schema.json
@@ -17,7 +17,7 @@
     },
     "properties": {
         "value": {
-            "$ref": "#/definitions/MyGeneric<number>"
+            "$ref": "#/definitions/MyGeneric%3Cnumber%3E"
         }
     },
     "required": [
diff --git a/typescript-json-schema.ts b/typescript-json-schema.ts
index f2730b86..795ffe13 100644
--- a/typescript-json-schema.ts
+++ b/typescript-json-schema.ts
@@ -1414,7 +1414,7 @@ export class JsonSchemaGenerator {
             // We don't return the full definition, but we put it into
             // reffedDefinitions below.
             returnedDefinition = {
-                $ref: `${this.args.id}#/definitions/` + fullTypeName,
+                $ref: createRefURI(this.args.id, fullTypeName),
             };
         }
 
@@ -1519,7 +1519,7 @@ export class JsonSchemaGenerator {
                 }, {});
 
                 returnedDefinition = {
-                    $ref: `${this.args.id}#/definitions/` + fullTypeName,
+                    $ref: createRefURI(this.args.id, fullTypeName),
                     ...annotations,
                 };
             }
@@ -1640,6 +1640,22 @@ export class JsonSchemaGenerator {
     }
 }
 
+/**
+ * Generates the reference URI to the definition in the schema `id`
+ * containing the definition `name`.
+ *
+ * Encodes the `name` using `encodeURIComponent`.
+ *
+ * @see https://datatracker.ietf.org/doc/html/rfc3986
+ * @param id the id of the schema containing the definition
+ * @param name the name of the definition
+ * @returns the URI pointing to the definition in the schema
+ */
+function createRefURI(id: string, name: string): string {
+    const encoded = encodeURIComponent(name);
+    return `${id}#/definitions/${encoded}`;
+}
+
 export function getProgramFromFiles(
     files: string[],
     jsonCompilerOptions: any = {},