Repository: Redocly/openapi-cli (ts)

Issue:
Bundle doesn't support OpenAPI yaml with $ref's
**Describe the bug**
Trying to run `openapi bundle -o openapi.json openapi.yaml` returns a heap of `cannot resolve $ref` errors. <br>
Running `openapi preview-docs` works fine though.

**To Reproduce**
Create any  YAML file with component refs (like schemas or routes) and try to bundle it.

**Expected behavior**
A bundled file

**Logs**
Exerpt:
```
[100] openapi.yaml:177:7 at #/components/schemas/Foo

Can't resolve $ref: ENOENT: no such file or directory, open '/home/user/MYPROJECT/docs/components/responses/components/schemas/foo.yaml'

175 |   $ref: "./components/schemas/object.yaml"
176 | Foo:
177 |   $ref: "./components/schemas/foo.yaml"
178 | Bar:
179 |   $ref: "./components/schemas/bar.yaml"

Error was generated by the bundler rule.
< ... 22 more problems hidden > increase with `--max-problems N`
```

**OpenAPI definition**
not possible

**Version(s)**
1.0.0-beta.8

**Additional context**
I'm looking for an easy way to run a yaml to json bundling through docker.


Pull request:
fix: crash with backrefs
Closes #184 

The problem was that when we went down the ref, we don't change the absolute path in rootNodeDocAbsoluteRef and that results in failures to resolve next $refs.

Patch:
diff --git a/src/resolve.ts b/src/resolve.ts
index 94d112ee..efa58a8e 100644
--- a/src/resolve.ts
+++ b/src/resolve.ts
@@ -302,7 +302,7 @@ export async function resolveDocument(opts: {
       let targetDoc: Document;
       try {
         targetDoc = isRemote
-          ? ((await externalRefResolver.resolveDocument(rootNodeDocAbsoluteRef, uri!)) as Document)
+          ? ((await externalRefResolver.resolveDocument(document.source.absoluteRef, uri!)) as Document)
           : document;
       } catch (error) {
         const resolvedRef = {


Test patch:
diff --git a/src/__tests__/fixtures/resolve/openapi-with-back.yaml b/src/__tests__/fixtures/resolve/openapi-with-back.yaml
new file mode 100644
index 00000000..9d035243
--- /dev/null
+++ b/src/__tests__/fixtures/resolve/openapi-with-back.yaml
@@ -0,0 +1,13 @@
+openapi: "3.0.1"
+info:
+  title: Open API
+  description: Open API
+  version: v0
+  contact:
+    email: some@body.com
+components:
+  schemas:
+    TypeA:
+      $ref: ./schemas/type-a.yaml#/
+    TypeB:
+      $ref: ./schemas/type-b.yaml#/
\ No newline at end of file
diff --git a/src/__tests__/fixtures/resolve/schemas/type-a.yaml b/src/__tests__/fixtures/resolve/schemas/type-a.yaml
new file mode 100644
index 00000000..1e75deb7
--- /dev/null
+++ b/src/__tests__/fixtures/resolve/schemas/type-a.yaml
@@ -0,0 +1,10 @@
+allOf:
+  - type: object
+    required:
+      - name
+      - integration_type
+    properties:
+      integration_type:
+        $ref: "../openapi-with-back.yaml#/components/schemas/TypeB"
+      name:
+        type: string
\ No newline at end of file
diff --git a/src/__tests__/fixtures/resolve/schemas/type-b.yaml b/src/__tests__/fixtures/resolve/schemas/type-b.yaml
new file mode 100644
index 00000000..179023e3
--- /dev/null
+++ b/src/__tests__/fixtures/resolve/schemas/type-b.yaml
@@ -0,0 +1,6 @@
+type: string
+enum:
+  - webhook
+  - api_key
+  - sftp
+  - netsuite
\ No newline at end of file
diff --git a/src/__tests__/resolve.test.ts b/src/__tests__/resolve.test.ts
index 4786fed8..d3d2cb34 100644
--- a/src/__tests__/resolve.test.ts
+++ b/src/__tests__/resolve.test.ts
@@ -176,6 +176,74 @@ describe('collect refs', () => {
     ]);
   });
 
+  it('should resolve back references', async () => {
+    const cwd = path.join(__dirname, 'fixtures/resolve');
+    const externalRefResolver = new BaseResolver();
+    const rootDocument = await externalRefResolver.resolveDocument(
+      null,
+      `${cwd}/openapi-with-back.yaml`,
+    );
+
+    const resolvedRefs = await resolveDocument({
+      rootDocument: rootDocument as Document,
+      externalRefResolver: externalRefResolver,
+      rootType: normalizeTypes(Oas3Types).DefinitionRoot,
+    });
+
+    expect(resolvedRefs).toBeDefined();
+
+    expect(Array.from(resolvedRefs.keys()).map((ref) => ref.substring(cwd.length + 1)))
+      .toMatchInlineSnapshot(`
+      Array [
+        "openapi-with-back.yaml::./schemas/type-a.yaml#/",
+        "openapi-with-back.yaml::./schemas/type-b.yaml#/",
+        "schemas/type-a.yaml::../openapi-with-back.yaml#/components/schemas/TypeB",
+      ]
+    `);
+
+    expect(Array.from(resolvedRefs.values()).map((val) => val.node)).toMatchInlineSnapshot(`
+      Array [
+        Object {
+          "allOf": Array [
+            Object {
+              "properties": Object {
+                "integration_type": Object {
+                  "$ref": "../openapi-with-back.yaml#/components/schemas/TypeB",
+                },
+                "name": Object {
+                  "type": "string",
+                },
+              },
+              "required": Array [
+                "name",
+                "integration_type",
+              ],
+              "type": "object",
+            },
+          ],
+        },
+        Object {
+          "enum": Array [
+            "webhook",
+            "api_key",
+            "sftp",
+            "netsuite",
+          ],
+          "type": "string",
+        },
+        Object {
+          "enum": Array [
+            "webhook",
+            "api_key",
+            "sftp",
+            "netsuite",
+          ],
+          "type": "string",
+        },
+      ]
+    `);
+  });
+
   it('should resolve external refs with circular', async () => {
     const cwd = path.join(__dirname, 'fixtures/resolve');
     const externalRefResolver = new BaseResolver();