485 lines
11 KiB
JavaScript
485 lines
11 KiB
JavaScript
'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.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()
|
|
}
|
|
`
|
|
)
|
|
})
|
|
|
|
|
|
})
|