text: 'use strict'
  
  const assert = require('assert')
  const dedent = require('dedent')
  const path = require('path')
  const recast = require('recast')
  const FileRequireTransform = require('../../src/file-require-transform')
  
  suite('FileRequireTransform', () => {
    test('simple require', () => {
      const source = dedent`
        const a = require('a')
        const b = require('b')
        function main () {
          const c = {a: b, b: a}
          return a + b
        }
      `
      assert.equal(
        new FileRequireTransform({source, didFindRequire: (mod) => mod === 'a'}).apply(),
        dedent`
          let a;
  
          function get_a() {
            return a = a || require('a');
          }
  
          const b = require('b')
          function main () {
            const c = {a: b, b: get_a()}
            return get_a() + b;
          }
        `
      )
    })
  
    test('conditional requires', () => {
      const source = dedent`
        let a, b;
        if (condition) {
          a = require('a')
          b = require('b')
        } else {
          a = require('c')
          b = require('d')
        }
  
        function main () {
          return a + b
        }
      `
      assert.equal(
        new FileRequireTransform({source, didFindRequire: (mod) => mod === 'a' || mod === 'c'}).apply(),
        dedent`
          let a, b;
          let get_a;
          if (condition) {
            get_a = function() {
              return a = a || require('a');
            }
            b = require('b')
          } else {
            get_a = function() {
              return a = a || require('c');
            }
            b = require('d')
          }
  
          function main () {
            return get_a() + b;
          }
        `
      )
    })
  
    test('top-level variables assignments that depend on previous requires', () => {
      const source = dedent`
        const a = require('a')
        const b = require('b')
        const c = require('c').foo.bar
        const d = c.X | c.Y | c.Z
        var e
        e = c.e
        const f = b.f
        function main () {
          c.qux()
          console.log(d)
          e()
        }
      `
      assert.equal(
        new FileRequireTransform({source, didFindRequire: (mod) => ['a', 'c'].indexOf(mod) >= 0}).apply(),
        dedent`
          let a;
  
          function get_a() {
            return a = a || require('a');
          }
  
          const b = require('b')
          let c;
  
          function get_c() {
            return c = c || require('c').foo.bar;
          }
  
          let d;
  
          function get_d() {
            return d = d || get_c().X | get_c().Y | get_c().Z;
          }
  
          var e
          function get_e() {
            return e = e || get_c().e;
          };
          const f = b.f
          function main () {
            get_c().qux()
            get_console().log(get_d())
            get_e()()
          }
      `)
    })
  
    test('requires that appear in a closure wrapper defined in the top-level scope (e.g. CoffeeScript)', () => {
      const source = dedent`
        (function () {
          const a = require('a')
          const b = require('b')
          function main () {
            return a + b
          }
        }).call(this)
  
        (function () {
          const a = require('a')
          const b = require('b')
          function main () {
            return a + b
          }
        })()
  
        foo(function () {
          const b = require('b')
          const c = require('c')
          function main () {
            return b + c
          }
        })
      `
      assert.equal(
        new FileRequireTransform({source, didFindRequire: (mod) => mod === 'a' || mod === 'c'}).apply(),
        dedent`
          (function () {
            let a;
  
            function get_a() {
              return a = a || require('a');
            }
  
            const b = require('b')
            function main () {
              return get_a() + b;
            }
          }).call(this)
  
          (function () {
            let a;
  
            function get_a() {
              return a = a || require('a');
            }
  
            const b = require('b')
            function main () {
              return get_a() + b;
            }
          })()
  
          foo(function () {
            const b = require('b')
            const c = require('c')
            function main () {
              return b + c
            }
          })
        `
      )
    })
  
    test('references to shadowed variables', () => {
      const source = dedent`
        const a = require('a')
        function outer () {
          console.log(a)
          function inner () {
            console.log(a)
          }
          let a = []
        }
  
        function other () {
          console.log(a)
          function inner () {
            let a = []
            console.log(a)
          }
        }
      `
      assert.equal(
        new FileRequireTransform({source, didFindRequire: (mod) => mod === 'a'}).apply(),
        dedent`
          let a;
  
          function get_a() {
            return a = a || require('a');
          }
  
          function outer () {
            get_console().log(a)
            function inner () {
              get_console().log(a)
            }
            let a = []
          }
  
          function other () {
            get_console().log(get_a())
            function inner () {
              let a = []
              get_console().log(a)
            }
          }
        `
      )
    })
  
    test('references to globals', () => {
      const source = dedent`
        global.a = 1
        process.b = 2
        window.c = 3
        document.d = 4
  
        function inner () {
          const window = {}
          global.e = 4
          process.f = 5
          window.g = 6
          document.h = 7
        }
      `
      assert.equal(
        new FileRequireTransform({source, didFindRequire: (mod) => mod === 'a'}).apply(),
        dedent`
          get_global().a = 1
          get_process().b = 2
          get_window().c = 3
          get_document().d = 4
  
          function inner () {
            const window = {}
            get_global().e = 4
            get_process().f = 5
            window.g = 6
            get_document().h = 7
          }
        `
      )
    })
  
    test('multiple assignments separated by commas referencing deferred modules', () => {
      const source = dedent`
        let a, b, c, d, e, f;
        a = 1, b = 2, c = 3;
        d = require("d"), e = d.e, f = e.f;
      `
      assert.equal(
        new FileRequireTransform({source, didFindRequire: () => true}).apply(),
        dedent`
          let a, b, c, d, e, f;
          a = 1, b = 2, c = 3;
  
          function get_d() {
            return d = d || require("d");
          }
  
          function get_e() {
            return e = e || get_d().e;
          }
  
          function get_f() {
            return f = f || get_e().f;
          }
        `
      )
    })
  
    test('require with destructuring assignment', () => {
      const source = dedent`
        const {a, b, c} = require('module').foo
  
        function main() {
          a.bar()
        }
      `
      assert.equal(
        new FileRequireTransform({source, didFindRequire: () => true}).apply(),
        dedent`
          let {a, b, c} = {};
  
          function get_a() {
            return a = a || require('module').foo.a;
          }
  
          function get_b() {
            return b = b || require('module').foo.b;
          }
  
          function get_c() {
            return c = c || require('module').foo.c;
          }
  
          function main() {
            get_a().bar()
          }
        `
      )
    })
  
    test('JSON source', () => {
      const filePath = 'something.json'
      const source = '{"a": 1, "b": 2}'
      assert.equal(
        new FileRequireTransform({filePath, source, didFindRequire: () => false}).apply(),
        dedent`
          module.exports = {"a": 1, "b": 2}
        `
      )
    })
  
    test('Object spread properties', () => {
      const source = 'let {a, b, ...rest} = {a: 1, b: 2, c: 3}'
      assert.equal(
        new FileRequireTransform({source, didFindRequire: () => false}).apply(),
        dedent`
          let {a, b, ...rest} = {a: 1, b: 2, c: 3}
        `
      )
    })
  
    test('path resolution', () => {
      const baseDirPath = path.resolve(__dirname, '..', 'fixtures', 'module-1')
      const filePath = path.join(baseDirPath, 'dir', 'entry.js')
      const source = dedent`
        const a = require('a')
        const b = require('./subdir/b')
        const c = require('c')
        const fs = require('fs')
  
        function inner () {
          require('./subdir/b')
          require.resolve('a')
          require.resolve('d')
          require('d')
        }
      `
      const requiredModules = []
      assert.equal(
        new FileRequireTransform({baseDirPath, filePath, source, didFindRequire: (unresolvedPath, resolvedPath) => {
          requiredModules.push({unresolvedPath, resolvedPath})
          return true
        }}).apply(),
        dedent`
          let a;
  
          function get_a() {
            return a = a || require("./node_modules/a/index.js");
          }
  
          let b;
  
          function get_b() {
            return b = b || require("./dir/subdir/b.js");
          }
  
          let c;
  
          function get_c() {
            return c = c || require('c');
          }
  
          let fs;
  
          function get_fs() {
            return fs = fs || require('fs');
          }
  
          function inner () {
            require("./dir/subdir/b.js")
            require.resolve("./node_modules/a/index.js")
            require.resolve('d')
            require('d')
          }
        `
      )
      assert.deepEqual(requiredModules, [
        {unresolvedPath: 'a' , resolvedPath: path.join(baseDirPath, 'node_modules', 'a', 'index.js')},
        {unresolvedPath: './subdir/b' , resolvedPath: path.join(baseDirPath, 'dir', 'subdir', 'b.js')},
        {unresolvedPath: 'c' , resolvedPath: 'c'},
        {unresolvedPath: './subdir/b' , resolvedPath: path.join(baseDirPath, 'dir', 'subdir', 'b.js')},
        {unresolvedPath: 'd' , resolvedPath: 'd'},
      ])
    })
  
    test('use reference directly', () => {
      const source = dedent`
        var pack = require('pack')
        
        const x = console.log(pack);
        if (condition) {
            pack
        } else {
          Object.keys(pack).forEach(function (prop) {
            exports[prop] = pack[prop]
          })
        }
      `
      assert.equal(
          new FileRequireTransform({source, didFindRequire: (mod) => mod === 'pack'}).apply(),
          dedent`
            var pack
            
            function get_pack() {
              return pack = pack || require('pack');
            }
            
            let x;
            
            function get_x() {
              return x = x || get_console().log(get_pack());
            }
            
            if (condition) {
                get_pack()
            } else {
              Object.keys(get_pack()).forEach(function (prop) {
                exports[prop] = get_pack()[prop]
              })
            }
        `
      )
    })
    test('assign to `module` or `exports`', () => {
      const source = dedent`
        var pack = require('pack')      
        if (condition) {
            module.exports.pack = pack
            module.exports = pack
            exports.pack = pack
            exports = pack
        }
      `
      assert.equal(
          new FileRequireTransform({source, didFindRequire: (mod) => mod === 'pack'}).apply(),
          dedent`
          var pack
          
          function get_pack() {
              return pack = pack || require('pack');
          }
          
          if (condition) {
              module.exports.pack = get_pack()
              module.exports = get_pack()
              exports.pack = get_pack()
              exports = get_pack()
          }
        `
      )
    })
  
  
  })
  