'use strict'const assert = require('assert')const dedent = require('dedent')const path = require('path')const recast = require('recast')const FileRequireTransform = require('../../lib/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.barconst d = c.X | c.Y | c.Zvar ee = c.econst f = b.ffunction 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 efunction get_e() {return e = e || get_c().e;};const f = b.ffunction 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 = 1process.b = 2window.c = 3document.d = 4function inner () {const window = {}global.e = 4process.f = 5window.g = 6document.h = 7}`assert.equal(new FileRequireTransform({source, didFindRequire: (mod) => mod === 'a'}).apply(),dedent`get_global().a = 1get_process().b = 2get_window().c = 3get_document().d = 4function inner () {const window = {}get_global().e = 4get_process().f = 5window.g = 6get_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').foofunction 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 packfunction 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 = packmodule.exports = packexports.pack = packexports = pack}`assert.equal(new FileRequireTransform({source, didFindRequire: (mod) => mod === 'pack'}).apply(),dedent`var packfunction 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()}`)})})