Repository: pmcelhaney/counterfact
Pull request #542 (2023-08-31 16:04:40)

Fix bugs found from GitHub
Tried running Counterfact against [Github's massive spec](https://github.blog/2020-07-27-introducing-githubs-openapi-description/). Found and fixed three bugs. 


Patch:
diff --git a/.changeset/empty-eyes-boil.md b/.changeset/empty-eyes-boil.md
new file mode 100644
index 0000000..8162aa0
--- /dev/null
+++ b/.changeset/empty-eyes-boil.md
@@ -0,0 +1,5 @@
+---
+"counterfact": patch
+---
+
+fix a few edge cases found when running against Github's extensive OpenAPI description: https://github.com/github/rest-api-description
diff --git a/src/typescript-generator/context-coder.js b/src/typescript-generator/context-coder.js
index 72c9a27..608659c 100644
--- a/src/typescript-generator/context-coder.js
+++ b/src/typescript-generator/context-coder.js
@@ -41,7 +41,11 @@ class Context {
       return "new Context()";
     }
 
-    script.repository.get(script.path).exportDefault(this);
+    const parentPath = nodePath
+      .normalize(nodePath.join(script.path, "../../$.context.ts"))
+      .replaceAll("\\", "/");
+
+    script.repository.get(parentPath).exportDefault(this);
 
     return { raw: 'export { default } from "../$.context.mjs"' };
   }
diff --git a/src/typescript-generator/printers.js b/src/typescript-generator/printers.js
index 688617d..f4529f4 100644
--- a/src/typescript-generator/printers.js
+++ b/src/typescript-generator/printers.js
@@ -9,7 +9,3 @@ export function printObject(entries) {
     .map(([key, value]) => `"${key}": ${value}`)
     .join(",\n")}\n}`;
 }
-
-export function printArray(items) {
-  return `[\n${items.join(",\n")}\n]`;
-}
diff --git a/src/typescript-generator/schema-type-coder.js b/src/typescript-generator/schema-type-coder.js
index f39459f..5567cc9 100644
--- a/src/typescript-generator/schema-type-coder.js
+++ b/src/typescript-generator/schema-type-coder.js
@@ -35,7 +35,7 @@ export class SchemaTypeCoder extends Coder {
         data.required?.includes(name) || property.data.required;
       const optionalFlag = isRequired ? "" : "?";
 
-      return `${name}${optionalFlag}: ${new SchemaTypeCoder(property).write(
+      return `"${name}"${optionalFlag}: ${new SchemaTypeCoder(property).write(
         script,
       )}`;
     });
@@ -55,6 +55,18 @@ export class SchemaTypeCoder extends Coder {
     )}>`;
   }
 
+  writePrimitive(value) {
+    if (typeof value === "string") {
+      return `"${value}"`;
+    }
+
+    if (value === null) {
+      return "null";
+    }
+
+    return value;
+  }
+
   writeType(script, type) {
     if (type === "object") {
       return this.objectSchema(script);
@@ -95,7 +107,7 @@ export class SchemaTypeCoder extends Coder {
 
   writeEnum(script, requirement) {
     return requirement.data
-      .map((item) => (typeof item === "string" ? `"${item}"` : item))
+      .map((item) => this.writePrimitive(item))
       .join(" | ");
   }
 
diff --git a/src/typescript-generator/script.js b/src/typescript-generator/script.js
index 65446c5..53e96bb 100644
--- a/src/typescript-generator/script.js
+++ b/src/typescript-generator/script.js
@@ -10,7 +10,7 @@ export class Script {
     this.repository = repository;
     this.exports = new Map();
     this.imports = new Map();
-    this.externalImports = new Map();
+    this.externalImport = new Map();
     this.cache = new Map();
     this.typeCache = new Map();
     this.path = path;
@@ -125,7 +125,7 @@ export class Script {
   }
 
   importExternal(name, modulePath, isType = false) {
-    this.externalImports.set(name, { isType, modulePath });
+    this.externalImport.set(name, { isType, modulePath });
 
     return name;
   }
@@ -150,9 +150,9 @@ export class Script {
     );
   }
 
-  externalImportsStatements() {
+  externalImportStatements() {
     return Array.from(
-      this.externalImports,
+      this.externalImport,
       ([name, { isDefault, isType, modulePath }]) =>
         `import${isType ? " type" : ""} ${
           isDefault ? name : `{ ${name} }`
@@ -199,7 +199,7 @@ export class Script {
   contents() {
     return prettier.format(
       [
-        this.externalImportsStatements().join("\n"),
+        this.externalImportStatements().join("\n"),
         this.importStatements().join("\n"),
         "\n\n",
         this.exportStatements().join("\n\n"),


Test patch:
diff --git a/test/typescript-generator/__snapshots__/context-coder.test.js.snap b/test/typescript-generator/__snapshots__/context-coder.test.js.snap
deleted file mode 100644
index 60645c6..0000000
--- a/test/typescript-generator/__snapshots__/context-coder.test.js.snap
+++ /dev/null
@@ -1,6 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`a ContextCoder finds the request method in the URL 1`] = `
-"export { default } from \\"../$.context.mjs\\";
-"
-`;
diff --git a/test/typescript-generator/__snapshots__/end-to-end.test.js.snap b/test/typescript-generator/__snapshots__/end-to-end.test.js.snap
index ce82466..edcfd08 100644
--- a/test/typescript-generator/__snapshots__/end-to-end.test.js.snap
+++ b/test/typescript-generator/__snapshots__/end-to-end.test.js.snap
@@ -37,7 +37,7 @@ Map {
         "typeDeclaration": "HTTP_PUT",
       },
     },
-    "externalImports": Map {},
+    "externalImport": Map {},
     "imports": Map {
       "HTTP_POST" => Object {
         "isDefault": false,
@@ -138,7 +138,7 @@ Map {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {
+          "externalImport": Map {
             "ResponseBuilderFactory" => Object {
               "isType": true,
               "modulePath": "../response-builder-factory.js",
@@ -179,7 +179,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "paths/$.context.ts",
                 "repository": Repository {
@@ -212,7 +212,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {
                   "Category" => Object {
                     "isDefault": false,
@@ -235,7 +235,7 @@ class Context {
                           "typeDeclaration": "",
                         },
                       },
-                      "externalImports": Map {},
+                      "externalImport": Map {},
                       "imports": Map {},
                       "path": "components/Category.ts",
                       "repository": Repository {
@@ -266,7 +266,7 @@ class Context {
                           "typeDeclaration": "",
                         },
                       },
-                      "externalImports": Map {},
+                      "externalImport": Map {},
                       "imports": Map {},
                       "path": "components/Tag.ts",
                       "repository": Repository {
@@ -393,7 +393,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {
+          "externalImport": Map {
             "ResponseBuilderFactory" => Object {
               "isType": true,
               "modulePath": "../response-builder-factory.js",
@@ -434,7 +434,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "paths/$.context.ts",
                 "repository": Repository {
@@ -467,7 +467,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {
                   "Category" => Object {
                     "isDefault": false,
@@ -490,7 +490,7 @@ class Context {
                           "typeDeclaration": "",
                         },
                       },
-                      "externalImports": Map {},
+                      "externalImport": Map {},
                       "imports": Map {},
                       "path": "components/Category.ts",
                       "repository": Repository {
@@ -521,7 +521,7 @@ class Context {
                           "typeDeclaration": "",
                         },
                       },
-                      "externalImports": Map {},
+                      "externalImport": Map {},
                       "imports": Map {},
                       "path": "components/Tag.ts",
                       "repository": Repository {
@@ -652,7 +652,7 @@ class Context {
         "typeDeclaration": "",
       },
     },
-    "externalImports": Map {
+    "externalImport": Map {
       "ResponseBuilderFactory" => Object {
         "isType": true,
         "modulePath": "../response-builder-factory.js",
@@ -693,7 +693,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {},
+          "externalImport": Map {},
           "imports": Map {},
           "path": "paths/$.context.ts",
           "repository": Repository {
@@ -726,7 +726,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {},
+          "externalImport": Map {},
           "imports": Map {
             "Category" => Object {
               "isDefault": false,
@@ -749,7 +749,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "components/Category.ts",
                 "repository": Repository {
@@ -780,7 +780,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "components/Tag.ts",
                 "repository": Repository {
@@ -827,7 +827,7 @@ class Context {
         "typeDeclaration": "HTTP_GET",
       },
     },
-    "externalImports": Map {},
+    "externalImport": Map {},
     "imports": Map {
       "HTTP_GET" => Object {
         "isDefault": false,
@@ -878,7 +878,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {
+          "externalImport": Map {
             "ResponseBuilderFactory" => Object {
               "isType": true,
               "modulePath": "../../response-builder-factory.js",
@@ -921,7 +921,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "paths/pet/$.context.ts",
                 "repository": Repository {
@@ -954,7 +954,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {
                   "Category" => Object {
                     "isDefault": false,
@@ -977,7 +977,7 @@ class Context {
                           "typeDeclaration": "",
                         },
                       },
-                      "externalImports": Map {},
+                      "externalImport": Map {},
                       "imports": Map {},
                       "path": "components/Category.ts",
                       "repository": Repository {
@@ -1008,7 +1008,7 @@ class Context {
                           "typeDeclaration": "",
                         },
                       },
-                      "externalImports": Map {},
+                      "externalImport": Map {},
                       "imports": Map {},
                       "path": "components/Tag.ts",
                       "repository": Repository {
@@ -1089,7 +1089,7 @@ class Context {
         "typeDeclaration": "",
       },
     },
-    "externalImports": Map {
+    "externalImport": Map {
       "ResponseBuilderFactory" => Object {
         "isType": true,
         "modulePath": "../../response-builder-factory.js",
@@ -1132,7 +1132,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {},
+          "externalImport": Map {},
           "imports": Map {},
           "path": "paths/pet/$.context.ts",
           "repository": Repository {
@@ -1165,7 +1165,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {},
+          "externalImport": Map {},
           "imports": Map {
             "Category" => Object {
               "isDefault": false,
@@ -1188,7 +1188,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "components/Category.ts",
                 "repository": Repository {
@@ -1219,7 +1219,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "components/Tag.ts",
                 "repository": Repository {
@@ -1266,7 +1266,7 @@ class Context {
         "typeDeclaration": "HTTP_GET",
       },
     },
-    "externalImports": Map {},
+    "externalImport": Map {},
     "imports": Map {
       "HTTP_GET" => Object {
         "isDefault": false,
@@ -1317,7 +1317,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {
+          "externalImport": Map {
             "ResponseBuilderFactory" => Object {
               "isType": true,
               "modulePath": "../../response-builder-factory.js",
@@ -1360,7 +1360,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "paths/pet/$.context.ts",
                 "repository": Repository {
@@ -1393,7 +1393,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {
                   "Category" => Object {
                     "isDefault": false,
@@ -1416,7 +1416,7 @@ class Context {
                           "typeDeclaration": "",
                         },
                       },
-                      "externalImports": Map {},
+                      "externalImport": Map {},
                       "imports": Map {},
                       "path": "components/Category.ts",
                       "repository": Repository {
@@ -1447,7 +1447,7 @@ class Context {
                           "typeDeclaration": "",
                         },
                       },
-                      "externalImports": Map {},
+                      "externalImport": Map {},
                       "imports": Map {},
                       "path": "components/Tag.ts",
                       "repository": Repository {
@@ -1528,7 +1528,7 @@ class Context {
         "typeDeclaration": "",
       },
     },
-    "externalImports": Map {
+    "externalImport": Map {
       "ResponseBuilderFactory" => Object {
         "isType": true,
         "modulePath": "../../response-builder-factory.js",
@@ -1571,7 +1571,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {},
+          "externalImport": Map {},
           "imports": Map {},
           "path": "paths/pet/$.context.ts",
           "repository": Repository {
@@ -1604,7 +1604,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {},
+          "externalImport": Map {},
           "imports": Map {
             "Category" => Object {
               "isDefault": false,
@@ -1627,7 +1627,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "components/Category.ts",
                 "repository": Repository {
@@ -1658,7 +1658,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "components/Tag.ts",
                 "repository": Repository {
@@ -1731,7 +1731,7 @@ class Context {
         "typeDeclaration": "HTTP_DELETE",
       },
     },
-    "externalImports": Map {},
+    "externalImport": Map {},
     "imports": Map {
       "HTTP_GET" => Object {
         "isDefault": false,
@@ -1826,7 +1826,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {
+          "externalImport": Map {
             "ResponseBuilderFactory" => Object {
               "isType": true,
               "modulePath": "../../response-builder-factory.js",
@@ -1869,7 +1869,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "paths/pet/$.context.ts",
                 "repository": Repository {
@@ -1902,7 +1902,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {
                   "Category" => Object {
                     "isDefault": false,
@@ -1925,7 +1925,7 @@ class Context {
                           "typeDeclaration": "",
                         },
                       },
-                      "externalImports": Map {},
+                      "externalImport": Map {},
                       "imports": Map {},
                       "path": "components/Category.ts",
                       "repository": Repository {
@@ -1956,7 +1956,7 @@ class Context {
                           "typeDeclaration": "",
                         },
                       },
-                      "externalImports": Map {},
+                      "externalImport": Map {},
                       "imports": Map {},
                       "path": "components/Tag.ts",
                       "repository": Repository {
@@ -2077,7 +2077,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {
+          "externalImport": Map {
             "ResponseBuilderFactory" => Object {
               "isType": true,
               "modulePath": "../../response-builder-factory.js",
@@ -2120,7 +2120,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "paths/pet/$.context.ts",
                 "repository": Repository {
@@ -2153,7 +2153,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {
                   "Category" => Object {
                     "isDefault": false,
@@ -2176,7 +2176,7 @@ class Context {
                           "typeDeclaration": "",
                         },
                       },
-                      "externalImports": Map {},
+                      "externalImport": Map {},
                       "imports": Map {},
                       "path": "components/Category.ts",
                       "repository": Repository {
@@ -2207,7 +2207,7 @@ class Context {
                           "typeDeclaration": "",
                         },
                       },
-                      "externalImports": Map {},
+                      "externalImport": Map {},
                       "imports": Map {},
                       "path": "components/Tag.ts",
                       "repository": Repository {
@@ -2328,7 +2328,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {
+          "externalImport": Map {
             "ResponseBuilderFactory" => Object {
               "isType": true,
               "modulePath": "../../response-builder-factory.js",
@@ -2371,7 +2371,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "paths/pet/$.context.ts",
                 "repository": Repository {
@@ -2404,7 +2404,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {
                   "Category" => Object {
                     "isDefault": false,
@@ -2427,7 +2427,7 @@ class Context {
                           "typeDeclaration": "",
                         },
                       },
-                      "externalImports": Map {},
+                      "externalImport": Map {},
                       "imports": Map {},
                       "path": "components/Category.ts",
                       "repository": Repository {
@@ -2458,7 +2458,7 @@ class Context {
                           "typeDeclaration": "",
                         },
                       },
-                      "externalImports": Map {},
+                      "externalImport": Map {},
                       "imports": Map {},
                       "path": "components/Tag.ts",
                       "repository": Repository {
@@ -2583,7 +2583,7 @@ class Context {
         "typeDeclaration": "",
       },
     },
-    "externalImports": Map {
+    "externalImport": Map {
       "ResponseBuilderFactory" => Object {
         "isType": true,
         "modulePath": "../../response-builder-factory.js",
@@ -2626,7 +2626,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {},
+          "externalImport": Map {},
           "imports": Map {},
           "path": "paths/pet/$.context.ts",
           "repository": Repository {
@@ -2659,7 +2659,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {},
+          "externalImport": Map {},
           "imports": Map {
             "Category" => Object {
               "isDefault": false,
@@ -2682,7 +2682,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "components/Category.ts",
                 "repository": Repository {
@@ -2713,7 +2713,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "components/Tag.ts",
                 "repository": Repository {
@@ -2760,7 +2760,7 @@ class Context {
         "typeDeclaration": "HTTP_POST",
       },
     },
-    "externalImports": Map {},
+    "externalImport": Map {},
     "imports": Map {
       "HTTP_POST" => Object {
         "isDefault": false,
@@ -2798,7 +2798,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {
+          "externalImport": Map {
             "ResponseBuilderFactory" => Object {
               "isType": true,
               "modulePath": "../../../response-builder-factory.js",
@@ -2841,7 +2841,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "paths/pet/{petId}/$.context.ts",
                 "repository": Repository {
@@ -2872,7 +2872,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "components/ApiResponse.ts",
                 "repository": Repository {
@@ -2931,7 +2931,7 @@ class Context {
         "typeDeclaration": "",
       },
     },
-    "externalImports": Map {
+    "externalImport": Map {
       "ResponseBuilderFactory" => Object {
         "isType": true,
         "modulePath": "../../../response-builder-factory.js",
@@ -2974,7 +2974,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {},
+          "externalImport": Map {},
           "imports": Map {},
           "path": "paths/pet/{petId}/$.context.ts",
           "repository": Repository {
@@ -3005,7 +3005,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {},
+          "externalImport": Map {},
           "imports": Map {},
           "path": "components/ApiResponse.ts",
           "repository": Repository {
@@ -3043,7 +3043,7 @@ class Context {
         "typeDeclaration": "HTTP_GET",
       },
     },
-    "externalImports": Map {},
+    "externalImport": Map {},
     "imports": Map {
       "HTTP_GET" => Object {
         "isDefault": false,
@@ -3080,7 +3080,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {
+          "externalImport": Map {
             "ResponseBuilderFactory" => Object {
               "isType": true,
               "modulePath": "../../response-builder-factory.js",
@@ -3123,7 +3123,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "paths/store/$.context.ts",
                 "repository": Repository {
@@ -3181,7 +3181,7 @@ class Context {
         "typeDeclaration": "",
       },
     },
-    "externalImports": Map {
+    "externalImport": Map {
       "ResponseBuilderFactory" => Object {
         "isType": true,
         "modulePath": "../../response-builder-factory.js",
@@ -3224,7 +3224,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {},
+          "externalImport": Map {},
           "imports": Map {},
           "path": "paths/store/$.context.ts",
           "repository": Repository {
@@ -3262,7 +3262,7 @@ class Context {
         "typeDeclaration": "HTTP_POST",
       },
     },
-    "externalImports": Map {},
+    "externalImport": Map {},
     "imports": Map {
       "HTTP_POST" => Object {
         "isDefault": false,
@@ -3306,7 +3306,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {
+          "externalImport": Map {
             "ResponseBuilderFactory" => Object {
               "isType": true,
               "modulePath": "../../response-builder-factory.js",
@@ -3349,7 +3349,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "paths/store/$.context.ts",
                 "repository": Repository {
@@ -3380,7 +3380,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "components/Order.ts",
                 "repository": Repository {
@@ -3445,7 +3445,7 @@ class Context {
         "typeDeclaration": "",
       },
     },
-    "externalImports": Map {
+    "externalImport": Map {
       "ResponseBuilderFactory" => Object {
         "isType": true,
         "modulePath": "../../response-builder-factory.js",
@@ -3488,7 +3488,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {},
+          "externalImport": Map {},
           "imports": Map {},
           "path": "paths/store/$.context.ts",
           "repository": Repository {
@@ -3519,7 +3519,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {},
+          "externalImport": Map {},
           "imports": Map {},
           "path": "components/Order.ts",
           "repository": Repository {
@@ -3570,7 +3570,7 @@ class Context {
         "typeDeclaration": "HTTP_DELETE",
       },
     },
-    "externalImports": Map {},
+    "externalImport": Map {},
     "imports": Map {
       "HTTP_GET" => Object {
         "isDefault": false,
@@ -3652,7 +3652,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {
+          "externalImport": Map {
             "ResponseBuilderFactory" => Object {
               "isType": true,
               "modulePath": "../../../response-builder-factory.js",
@@ -3695,7 +3695,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "paths/store/order/$.context.ts",
                 "repository": Repository {
@@ -3726,7 +3726,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "components/Order.ts",
                 "repository": Repository {
@@ -3825,7 +3825,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {
+          "externalImport": Map {
             "ResponseBuilderFactory" => Object {
               "isType": true,
               "modulePath": "../../../response-builder-factory.js",
@@ -3868,7 +3868,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "paths/store/order/$.context.ts",
                 "repository": Repository {
@@ -3899,7 +3899,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "components/Order.ts",
                 "repository": Repository {
@@ -4002,7 +4002,7 @@ class Context {
         "typeDeclaration": "",
       },
     },
-    "externalImports": Map {
+    "externalImport": Map {
       "ResponseBuilderFactory" => Object {
         "isType": true,
         "modulePath": "../../../response-builder-factory.js",
@@ -4045,7 +4045,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {},
+          "externalImport": Map {},
           "imports": Map {},
           "path": "paths/store/order/$.context.ts",
           "repository": Repository {
@@ -4076,7 +4076,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {},
+          "externalImport": Map {},
           "imports": Map {},
           "path": "components/Order.ts",
           "repository": Repository {
@@ -4114,7 +4114,7 @@ class Context {
         "typeDeclaration": "HTTP_POST",
       },
     },
-    "externalImports": Map {},
+    "externalImport": Map {},
     "imports": Map {
       "HTTP_POST" => Object {
         "isDefault": false,
@@ -4159,7 +4159,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {
+          "externalImport": Map {
             "ResponseBuilderFactory" => Object {
               "isType": true,
               "modulePath": "../response-builder-factory.js",
@@ -4200,7 +4200,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "paths/$.context.ts",
                 "repository": Repository {
@@ -4231,7 +4231,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "components/User.ts",
                 "repository": Repository {
@@ -4297,7 +4297,7 @@ class Context {
         "typeDeclaration": "",
       },
     },
-    "externalImports": Map {
+    "externalImport": Map {
       "ResponseBuilderFactory" => Object {
         "isType": true,
         "modulePath": "../response-builder-factory.js",
@@ -4338,7 +4338,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {},
+          "externalImport": Map {},
           "imports": Map {},
           "path": "paths/$.context.ts",
           "repository": Repository {
@@ -4369,7 +4369,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {},
+          "externalImport": Map {},
           "imports": Map {},
           "path": "components/User.ts",
           "repository": Repository {
@@ -4407,7 +4407,7 @@ class Context {
         "typeDeclaration": "HTTP_POST",
       },
     },
-    "externalImports": Map {},
+    "externalImport": Map {},
     "imports": Map {
       "HTTP_POST" => Object {
         "isDefault": false,
@@ -4458,7 +4458,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {
+          "externalImport": Map {
             "ResponseBuilderFactory" => Object {
               "isType": true,
               "modulePath": "../../response-builder-factory.js",
@@ -4501,7 +4501,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "paths/user/$.context.ts",
                 "repository": Repository {
@@ -4532,7 +4532,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "components/User.ts",
                 "repository": Repository {
@@ -4604,7 +4604,7 @@ class Context {
         "typeDeclaration": "",
       },
     },
-    "externalImports": Map {
+    "externalImport": Map {
       "ResponseBuilderFactory" => Object {
         "isType": true,
         "modulePath": "../../response-builder-factory.js",
@@ -4647,7 +4647,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {},
+          "externalImport": Map {},
           "imports": Map {},
           "path": "paths/user/$.context.ts",
           "repository": Repository {
@@ -4678,7 +4678,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {},
+          "externalImport": Map {},
           "imports": Map {},
           "path": "components/User.ts",
           "repository": Repository {
@@ -4716,7 +4716,7 @@ class Context {
         "typeDeclaration": "HTTP_GET",
       },
     },
-    "externalImports": Map {},
+    "externalImport": Map {},
     "imports": Map {
       "HTTP_GET" => Object {
         "isDefault": false,
@@ -4769,7 +4769,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {
+          "externalImport": Map {
             "ResponseBuilderFactory" => Object {
               "isType": true,
               "modulePath": "../../response-builder-factory.js",
@@ -4812,7 +4812,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "paths/user/$.context.ts",
                 "repository": Repository {
@@ -4886,7 +4886,7 @@ class Context {
         "typeDeclaration": "",
       },
     },
-    "externalImports": Map {
+    "externalImport": Map {
       "ResponseBuilderFactory" => Object {
         "isType": true,
         "modulePath": "../../response-builder-factory.js",
@@ -4929,7 +4929,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {},
+          "externalImport": Map {},
           "imports": Map {},
           "path": "paths/user/$.context.ts",
           "repository": Repository {
@@ -4965,7 +4965,7 @@ class Context {
         "typeDeclaration": "HTTP_GET",
       },
     },
-    "externalImports": Map {},
+    "externalImport": Map {},
     "imports": Map {
       "HTTP_GET" => Object {
         "isDefault": false,
@@ -4996,7 +4996,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {
+          "externalImport": Map {
             "ResponseBuilderFactory" => Object {
               "isType": true,
               "modulePath": "../../response-builder-factory.js",
@@ -5039,7 +5039,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "paths/user/$.context.ts",
                 "repository": Repository {
@@ -5091,7 +5091,7 @@ class Context {
         "typeDeclaration": "",
       },
     },
-    "externalImports": Map {
+    "externalImport": Map {
       "ResponseBuilderFactory" => Object {
         "isType": true,
         "modulePath": "../../response-builder-factory.js",
@@ -5134,7 +5134,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {},
+          "externalImport": Map {},
           "imports": Map {},
           "path": "paths/user/$.context.ts",
           "repository": Repository {
@@ -5198,7 +5198,7 @@ class Context {
         "typeDeclaration": "HTTP_DELETE",
       },
     },
-    "externalImports": Map {},
+    "externalImport": Map {},
     "imports": Map {
       "HTTP_GET" => Object {
         "isDefault": false,
@@ -5299,7 +5299,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {
+          "externalImport": Map {
             "ResponseBuilderFactory" => Object {
               "isType": true,
               "modulePath": "../../response-builder-factory.js",
@@ -5342,7 +5342,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "paths/user/$.context.ts",
                 "repository": Repository {
@@ -5373,7 +5373,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "components/User.ts",
                 "repository": Repository {
@@ -5491,7 +5491,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {
+          "externalImport": Map {
             "ResponseBuilderFactory" => Object {
               "isType": true,
               "modulePath": "../../response-builder-factory.js",
@@ -5534,7 +5534,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "paths/user/$.context.ts",
                 "repository": Repository {
@@ -5565,7 +5565,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "components/User.ts",
                 "repository": Repository {
@@ -5683,7 +5683,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {
+          "externalImport": Map {
             "ResponseBuilderFactory" => Object {
               "isType": true,
               "modulePath": "../../response-builder-factory.js",
@@ -5726,7 +5726,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "paths/user/$.context.ts",
                 "repository": Repository {
@@ -5757,7 +5757,7 @@ class Context {
                     "typeDeclaration": "",
                   },
                 },
-                "externalImports": Map {},
+                "externalImport": Map {},
                 "imports": Map {},
                 "path": "components/User.ts",
                 "repository": Repository {
@@ -5879,7 +5879,7 @@ class Context {
         "typeDeclaration": "",
       },
     },
-    "externalImports": Map {
+    "externalImport": Map {
       "ResponseBuilderFactory" => Object {
         "isType": true,
         "modulePath": "../../response-builder-factory.js",
@@ -5922,7 +5922,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {},
+          "externalImport": Map {},
           "imports": Map {},
           "path": "paths/user/$.context.ts",
           "repository": Repository {
@@ -5953,7 +5953,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {},
+          "externalImport": Map {},
           "imports": Map {},
           "path": "components/User.ts",
           "repository": Repository {
@@ -6001,7 +6001,7 @@ class Context {
         "typeDeclaration": "",
       },
     },
-    "externalImports": Map {},
+    "externalImport": Map {},
     "imports": Map {},
     "path": "paths/$.context.ts",
     "repository": Repository {
@@ -6029,7 +6029,7 @@ class Context {
         "typeDeclaration": "",
       },
     },
-    "externalImports": Map {},
+    "externalImport": Map {},
     "imports": Map {
       "Category" => Object {
         "isDefault": false,
@@ -6052,7 +6052,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {},
+          "externalImport": Map {},
           "imports": Map {},
           "path": "components/Category.ts",
           "repository": Repository {
@@ -6083,7 +6083,7 @@ class Context {
               "typeDeclaration": "",
             },
           },
-          "externalImports": Map {},
+          "externalImport": Map {},
           "imports": Map {},
           "path": "components/Tag.ts",
           "repository": Repository {
@@ -6133,7 +6133,7 @@ class Context {
         "typeDeclaration": "",
       },
     },
-    "externalImports": Map {},
+    "externalImport": Map {},
     "imports": Map {},
     "path": "paths/pet/$.context.ts",
     "repository": Repository {
@@ -6174,7 +6174,7 @@ class Context {
         "typeDeclaration": "",
       },
     },
-    "externalImports": Map {},
+    "externalImport": Map {},
     "imports": Map {},
     "path": "paths/pet/{petId}/$.context.ts",
     "repository": Repository {
@@ -6200,7 +6200,7 @@ class Context {
         "typeDeclaration": "",
       },
     },
-    "externalImports": Map {},
+    "externalImport": Map {},
     "imports": Map {},
     "path": "components/ApiResponse.ts",
     "repository": Repository {
@@ -6241,7 +6241,7 @@ class Context {
         "typeDeclaration": "",
       },
     },
-    "externalImports": Map {},
+    "externalImport": Map {},
     "imports": Map {},
     "path": "paths/store/$.context.ts",
     "repository": Repository {
@@ -6267,7 +6267,7 @@ class Context {
         "typeDeclaration": "",
       },
     },
-    "externalImports": Map {},
+    "externalImport": Map {},
     "imports": Map {},
     "path": "components/Order.ts",
     "repository": Repository {
@@ -6308,7 +6308,7 @@ class Context {
         "typeDeclaration": "",
       },
     },
-    "externalImports": Map {},
+    "externalImport": Map {},
     "imports": Map {},
     "path": "paths/store/order/$.context.ts",
     "repository": Repository {
@@ -6334,7 +6334,7 @@ class Context {
         "typeDeclaration": "",
       },
     },
-    "externalImports": Map {},
+    "externalImport": Map {},
     "imports": Map {},
     "path": "components/User.ts",
     "repository": Repository {
@@ -6375,7 +6375,7 @@ class Context {
         "typeDeclaration": "",
       },
     },
-    "externalImports": Map {},
+    "externalImport": Map {},
     "imports": Map {},
     "path": "paths/user/$.context.ts",
     "repository": Repository {
@@ -6401,7 +6401,7 @@ class Context {
         "typeDeclaration": "",
       },
     },
-    "externalImports": Map {},
+    "externalImport": Map {},
     "imports": Map {},
     "path": "components/Category.ts",
     "repository": Repository {
@@ -6427,7 +6427,7 @@ class Context {
         "typeDeclaration": "",
       },
     },
-    "externalImports": Map {},
+    "externalImport": Map {},
     "imports": Map {},
     "path": "components/Tag.ts",
     "repository": Repository {
diff --git a/test/typescript-generator/context-coder.test.js b/test/typescript-generator/context-coder.test.js
index 92456c2..548b83b 100644
--- a/test/typescript-generator/context-coder.test.js
+++ b/test/typescript-generator/context-coder.test.js
@@ -62,11 +62,30 @@ describe("a ContextCoder", () => {
     expect(coder.id).toBe("ContextCoder");
   });
 
-  it("finds the request method in the URL", async () => {
+  it("extends the Context from the parent directory", async () => {
     const coder = new ContextCoder(new Requirement({}, "#/paths/hello/get"));
 
-    await expect(
-      format(coder.write(dummyScript).raw),
-    ).resolves.toMatchSnapshot();
+    await expect(format(coder.write(dummyScript).raw)).resolves.toBe(
+      await format('export { default } from "../$.context.mjs";'),
+    );
+  });
+
+  it("returns the context object if this is the root directory", async () => {
+    const coder = new ContextCoder(new Requirement({}, "#/paths/hello"));
+
+    const rootContextScript = {
+      ...dummyScript,
+      path: "paths/$.context.ts",
+    };
+
+    await expect(format(coder.write(rootContextScript))).resolves.toBe(
+      await format("new Context()"),
+    );
+  });
+
+  it("writes out the Context class", () => {
+    const coder = new ContextCoder(new Requirement({}, "#/paths/hello"));
+
+    expect(coder.beforeExport(dummyScript)).toContain("class Context {");
   });
 });
diff --git a/test/typescript-generator/schema-coder.test.js b/test/typescript-generator/schema-coder.test.js
index 1cae943..e2d78de 100644
--- a/test/typescript-generator/schema-coder.test.js
+++ b/test/typescript-generator/schema-coder.test.js
@@ -59,6 +59,7 @@ describe("a SchemaCoder", () => {
     const coder = new SchemaCoder(
       new Requirement({
         properties: {
+          "@needsQuotes": { type: "string" },
           age: { type: "integer" },
           name: { type: "string" },
         },
@@ -72,7 +73,7 @@ describe("a SchemaCoder", () => {
     const expected = await format(`const x = { 
       type: "object",
       required: [],
-      properties: { "age": {"type":"integer"}, "name": {"type":"string"}  }
+      properties: { "@needsQuotes": { type: "string"}, "age": {"type":"integer"}, "name": {"type":"string"}  }
     }`);
 
     await expect(format(`const x = ${coder.write()}`)).resolves.toStrictEqual(
diff --git a/test/typescript-generator/schema-type-coder.test.js b/test/typescript-generator/schema-type-coder.test.js
index e3637a5..fc095ab 100644
--- a/test/typescript-generator/schema-type-coder.test.js
+++ b/test/typescript-generator/schema-type-coder.test.js
@@ -109,6 +109,7 @@ describe("a SchemaTypeCoder", () => {
           anotherRequiredString: { type: "string" },
           optionalString: { type: "string" },
           requiredString: { required: true, type: "string" },
+          "with spaces": { type: "string" },
         },
 
         required: ["anotherRequiredString"],
@@ -118,7 +119,12 @@ describe("a SchemaTypeCoder", () => {
     );
 
     const expected = await format(
-      "type x = { anotherRequiredString: string, optionalString?: string, requiredString: string,  };",
+      `type x = { 
+        anotherRequiredString: string, 
+        optionalString?: string, 
+        requiredString: string, 
+        "with spaces"?: string  };
+      `,
     );
 
     await expect(format(`type x = ${coder.write()}`)).resolves.toStrictEqual(
@@ -286,11 +292,12 @@ describe("a SchemaTypeCoder", () => {
   it("generates a type declaration for enum", async () => {
     const coder = new SchemaTypeCoder(
       new Requirement({
-        enum: [1, "two"],
+        // eslint-disable-next-line unicorn/no-null
+        enum: [1, "two", null],
       }),
     );
 
-    const expected = await format('type x = 1 | "two";');
+    const expected = await format('type x = 1 | "two" | null;');
 
     await expect(format(`type x = ${coder.write()}`)).resolves.toStrictEqual(
       expected,
@@ -308,4 +315,14 @@ describe("a SchemaTypeCoder", () => {
 
     expect(coder.typeDeclaration(undefined, {})).toBe("");
   });
+
+  it("calculates the modulePath", () => {
+    const coder = new SchemaTypeCoder(
+      new Requirement({
+        $ref: "components/Example",
+      }),
+    );
+
+    expect(coder.modulePath()).toBe("components/Example.ts");
+  });
 });
diff --git a/test/typescript-generator/script.test.js b/test/typescript-generator/script.test.js
index efcb498..32fabfb 100644
--- a/test/typescript-generator/script.test.js
+++ b/test/typescript-generator/script.test.js
@@ -92,6 +92,8 @@ describe("a Script", () => {
     script.importType(coder);
     script.importDefault(coder);
 
+    script.importExternalType("ResponseBuilderFactory", "file.ts");
+
     expect(script.importStatements()).toStrictEqual([
       'import { Account0 } from "./export-from-me.js";',
       'import type { Account1 } from "./export-from-me.js";',
@@ -99,6 +101,20 @@ describe("a Script", () => {
     ]);
   });
 
+  it("creates external import statements", () => {
+    const repository = new Repository("/base/path");
+
+    const script = repository.get("import-to-me.ts");
+
+    script.importExternal("SomeClass", "code.ts");
+    script.importExternalType("SomeType", "type.ts");
+
+    expect(script.externalImportStatements()).toStrictEqual([
+      'import { SomeClass } from "code.ts";',
+      'import type { SomeType } from "type.ts";',
+    ]);
+  });
+
   it("handles relative import statements", () => {
     const repository = new Repository("/base/path");