From 4c4300c3ffd81da8c8b7d62cc001eda4396fed89 Mon Sep 17 00:00:00 2001
From: Harminder virk <virk.officials@gmail.com>
Date: Sat, 14 Sep 2019 20:00:46 +0530
Subject: [PATCH] fix(compiler): pass absolute filepath to parser & lexer for
 appropriate stack trace

---
 src/Compiler/index.ts |  10 ++-
 test/edge.spec.ts     | 145 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 153 insertions(+), 2 deletions(-)

diff --git a/src/Compiler/index.ts b/src/Compiler/index.ts
index e1251eb..b8ece52 100644
--- a/src/Compiler/index.ts
+++ b/src/Compiler/index.ts
@@ -136,7 +136,13 @@ export class Compiler implements CompilerContract {
      * and parent template sections together
      */
     if (isBlockToken(firstToken, 'layout')) {
-      const layoutTokens = this.generateLexerTokens(firstToken.properties.jsArg.replace(/'/g, ''))
+      const layoutName = firstToken.properties.jsArg.replace(/'/g, '')
+      /**
+       * Making absolute path, so that lexer errors must point to the
+       * absolute file path
+       */
+      const absPath = this._loader.makePath(layoutName)
+      const layoutTokens = this.generateLexerTokens(absPath)
       templateTokens = this._mergeSections(layoutTokens, templateTokens, parser.options.filename)
     }
 
@@ -208,7 +214,7 @@ export class Compiler implements CompilerContract {
      * instead of the `absPath`, since `templatePath` is relative and readable.
      */
     const parser = new Parser(this._tags, {
-      filename: `${templatePath.replace(/\.edge$/, '')}.edge`,
+      filename: `${absPath.replace(/\.edge$/, '')}.edge`,
     })
 
     /**
diff --git a/test/edge.spec.ts b/test/edge.spec.ts
index d6959b8..486b742 100644
--- a/test/edge.spec.ts
+++ b/test/edge.spec.ts
@@ -102,4 +102,149 @@ test.group('Edge', (group) => {
 
     assert.equal(edge.render('hello::foo', { username: 'virk' }).trim(), 'Hello virk')
   })
+
+  test('pass absolute path of template to lexer errors', async (assert) => {
+    assert.plan(1)
+    await fs.add('foo.edge', '@if(1 + 1)')
+
+    const loader = new Loader()
+    loader.mount('default', fs.basePath)
+
+    const edge = new Edge(new Loader())
+    edge.mount(fs.basePath)
+
+    try {
+      edge.render('foo', false)
+    } catch ({ stack }) {
+      assert.equal(stack.split('\n')[1].trim(), `at (${join(fs.basePath, 'foo.edge')}:1:4)`)
+    }
+  })
+
+  test('pass absolute path of template to parser errors', async (assert) => {
+    assert.plan(1)
+    await fs.add('foo.edge', 'Hello {{ a,:b }}')
+
+    const loader = new Loader()
+    loader.mount('default', fs.basePath)
+
+    const edge = new Edge(new Loader())
+    edge.mount(fs.basePath)
+
+    try {
+      edge.render('foo', false)
+    } catch ({ stack }) {
+      assert.equal(stack.split('\n')[1].trim(), `at (${join(fs.basePath, 'foo.edge')}:1:11)`)
+    }
+  })
+
+  test('pass absolute path of layout to lexer errors', async (assert) => {
+    assert.plan(1)
+    await fs.add('foo.edge', `@layout('bar')`)
+    await fs.add('bar.edge', `@if(username)`)
+
+    const loader = new Loader()
+    loader.mount('default', fs.basePath)
+
+    const edge = new Edge(new Loader())
+    edge.mount(fs.basePath)
+
+    try {
+      edge.render('foo', false)
+    } catch ({ stack }) {
+      assert.equal(stack.split('\n')[1].trim(), `at (${join(fs.basePath, 'bar.edge')}:1:4)`)
+    }
+  })
+
+  /**
+   * Will fix it later
+   */
+  test.skip('pass absolute path of layout to parser errors', async (assert) => {
+    assert.plan(1)
+    await fs.add('foo.edge', `@layout('bar')`)
+    await fs.add('bar.edge', `{{ a:b }}`)
+
+    const loader = new Loader()
+    loader.mount('default', fs.basePath)
+
+    const edge = new Edge(new Loader())
+    edge.mount(fs.basePath)
+
+    try {
+      edge.render('foo', false)
+    } catch ({ stack }) {
+      assert.equal(stack.split('\n')[1].trim(), `at (${join(fs.basePath, 'bar.edge')}:1:4)`)
+    }
+  })
+
+  test('pass absolute path of partial to lexer errors', async (assert) => {
+    assert.plan(1)
+    await fs.add('foo.edge', `@include('bar')`)
+    await fs.add('bar.edge', `@if(username)`)
+
+    const loader = new Loader()
+    loader.mount('default', fs.basePath)
+
+    const edge = new Edge(new Loader())
+    edge.mount(fs.basePath)
+
+    try {
+      edge.render('foo', false)
+    } catch ({ stack }) {
+      assert.equal(stack.split('\n')[1].trim(), `at (${join(fs.basePath, 'bar.edge')}:1:4)`)
+    }
+  })
+
+  test('pass absolute path of partial to parser errors', async (assert) => {
+    assert.plan(1)
+    await fs.add('foo.edge', `@include('bar')`)
+    await fs.add('bar.edge', `{{ a:b }}`)
+
+    const loader = new Loader()
+    loader.mount('default', fs.basePath)
+
+    const edge = new Edge(new Loader())
+    edge.mount(fs.basePath)
+
+    try {
+      edge.render('foo', false)
+    } catch ({ stack }) {
+      assert.equal(stack.split('\n')[1].trim(), `at (${join(fs.basePath, 'bar.edge')}:1:3)`)
+    }
+  })
+
+  test('pass absolute path of component to lexer errors', async (assert) => {
+    assert.plan(1)
+    await fs.add('foo.edge', `@!component('bar')`)
+    await fs.add('bar.edge', `@if(username)`)
+
+    const loader = new Loader()
+    loader.mount('default', fs.basePath)
+
+    const edge = new Edge(new Loader())
+    edge.mount(fs.basePath)
+
+    try {
+      edge.render('foo', false)
+    } catch ({ stack }) {
+      assert.equal(stack.split('\n')[1].trim(), `at (${join(fs.basePath, 'bar.edge')}:1:4)`)
+    }
+  })
+
+  test('pass absolute path of component to parser errors', async (assert) => {
+    assert.plan(1)
+    await fs.add('foo.edge', `@!component('bar')`)
+    await fs.add('bar.edge', `{{ a:b }}`)
+
+    const loader = new Loader()
+    loader.mount('default', fs.basePath)
+
+    const edge = new Edge(new Loader())
+    edge.mount(fs.basePath)
+
+    try {
+      edge.render('foo', false)
+    } catch ({ stack }) {
+      assert.equal(stack.split('\n')[1].trim(), `at (${join(fs.basePath, 'bar.edge')}:1:3)`)
+    }
+  })
 })