mirror of
https://github.com/zhigang1992/esbuild.git
synced 2026-04-29 09:55:58 +08:00
3357 lines
89 KiB
Go
3357 lines
89 KiB
Go
package bundler
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/evanw/esbuild/internal/config"
|
|
"github.com/evanw/esbuild/internal/js_ast"
|
|
"github.com/evanw/esbuild/internal/logger"
|
|
)
|
|
|
|
var default_suite = suite{
|
|
name: "default",
|
|
}
|
|
|
|
func TestSimpleES6(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import {fn} from './foo'
|
|
console.log(fn())
|
|
`,
|
|
"/foo.js": `
|
|
export function fn() {
|
|
return 123
|
|
}
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestSimpleCommonJS(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
const fn = require('./foo')
|
|
console.log(fn())
|
|
`,
|
|
"/foo.js": `
|
|
module.exports = function() {
|
|
return 123
|
|
}
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
// This test makes sure that require() calls are still recognized in nested
|
|
// scopes. It guards against bugs where require() calls are only recognized in
|
|
// the top-level module scope.
|
|
func TestNestedCommonJS(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
function nestedScope() {
|
|
const fn = require('./foo')
|
|
console.log(fn())
|
|
}
|
|
nestedScope()
|
|
`,
|
|
"/foo.js": `
|
|
module.exports = function() {
|
|
return 123
|
|
}
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
// This test makes sure that NewExpressions containing require() calls aren't
|
|
// broken.
|
|
func TestNewExpressionCommonJS(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
new (require("./foo.js")).Foo();
|
|
`,
|
|
"/foo.js": `
|
|
class Foo {}
|
|
module.exports = {Foo};
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestCommonJSFromES6(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
const {foo} = require('./foo')
|
|
console.log(foo(), bar())
|
|
const {bar} = require('./bar') // This should not be hoisted
|
|
`,
|
|
"/foo.js": `
|
|
export function foo() {
|
|
return 'foo'
|
|
}
|
|
`,
|
|
"/bar.js": `
|
|
export function bar() {
|
|
return 'bar'
|
|
}
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestES6FromCommonJS(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import {foo} from './foo'
|
|
console.log(foo(), bar())
|
|
import {bar} from './bar' // This should be hoisted
|
|
`,
|
|
"/foo.js": `
|
|
exports.foo = function() {
|
|
return 'foo'
|
|
}
|
|
`,
|
|
"/bar.js": `
|
|
exports.bar = function() {
|
|
return 'bar'
|
|
}
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
// This test makes sure that ES6 imports are still recognized in nested
|
|
// scopes. It guards against bugs where require() calls are only recognized in
|
|
// the top-level module scope.
|
|
func TestNestedES6FromCommonJS(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import {fn} from './foo'
|
|
(() => {
|
|
console.log(fn())
|
|
})()
|
|
`,
|
|
"/foo.js": `
|
|
exports.fn = function() {
|
|
return 123
|
|
}
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestExportFormsES6(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
export default 123
|
|
export var v = 234
|
|
export let l = 234
|
|
export const c = 234
|
|
export {Class as C}
|
|
export function Fn() {}
|
|
export class Class {}
|
|
export * from './a'
|
|
export * as b from './b'
|
|
`,
|
|
"/a.js": "export const abc = undefined",
|
|
"/b.js": "export const xyz = null",
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
OutputFormat: config.FormatESModule,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestExportFormsIIFE(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
export default 123
|
|
export var v = 234
|
|
export let l = 234
|
|
export const c = 234
|
|
export {Class as C}
|
|
export function Fn() {}
|
|
export class Class {}
|
|
export * from './a'
|
|
export * as b from './b'
|
|
`,
|
|
"/a.js": "export const abc = undefined",
|
|
"/b.js": "export const xyz = null",
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
OutputFormat: config.FormatIIFE,
|
|
ModuleName: []string{"moduleName"},
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestExportFormsWithMinifyIdentifiersAndNoBundle(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/a.js": `
|
|
export default 123
|
|
export var varName = 234
|
|
export let letName = 234
|
|
export const constName = 234
|
|
function Func2() {}
|
|
class Class2 {}
|
|
export {Class as Cls, Func2 as Fn2, Class2 as Cls2}
|
|
export function Func() {}
|
|
export class Class {}
|
|
export * from './a'
|
|
export * as fromB from './b'
|
|
`,
|
|
"/b.js": "export default function() {}",
|
|
"/c.js": "export default function foo() {}",
|
|
"/d.js": "export default class {}",
|
|
"/e.js": "export default class Foo {}",
|
|
},
|
|
entryPaths: []string{
|
|
"/a.js",
|
|
"/b.js",
|
|
"/c.js",
|
|
"/d.js",
|
|
"/e.js",
|
|
},
|
|
options: config.Options{
|
|
MinifyIdentifiers: true,
|
|
AbsOutputDir: "/out",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestImportFormsWithNoBundle(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import 'foo'
|
|
import {} from 'foo'
|
|
import * as ns from 'foo'
|
|
import {a, b as c} from 'foo'
|
|
import def from 'foo'
|
|
import def2, * as ns2 from 'foo'
|
|
import def3, {a2, b as c3} from 'foo'
|
|
const imp = [
|
|
import('foo'),
|
|
function nested() { return import('foo') },
|
|
]
|
|
console.log(ns, a, c, def, def2, ns2, def3, a2, c3, imp)
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestImportFormsWithMinifyIdentifiersAndNoBundle(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import 'foo'
|
|
import {} from 'foo'
|
|
import * as ns from 'foo'
|
|
import {a, b as c} from 'foo'
|
|
import def from 'foo'
|
|
import def2, * as ns2 from 'foo'
|
|
import def3, {a2, b as c3} from 'foo'
|
|
const imp = [
|
|
import('foo'),
|
|
function() { return import('foo') },
|
|
]
|
|
console.log(ns, a, c, def, def2, ns2, def3, a2, c3, imp)
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
MinifyIdentifiers: true,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestExportFormsCommonJS(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
require('./commonjs')
|
|
require('./c')
|
|
require('./d')
|
|
require('./e')
|
|
require('./f')
|
|
require('./g')
|
|
require('./h')
|
|
`,
|
|
"/commonjs.js": `
|
|
export default 123
|
|
export var v = 234
|
|
export let l = 234
|
|
export const c = 234
|
|
export {Class as C}
|
|
export function Fn() {}
|
|
export class Class {}
|
|
export * from './a'
|
|
export * as b from './b'
|
|
`,
|
|
"/a.js": "export const abc = undefined",
|
|
"/b.js": "export const xyz = null",
|
|
"/c.js": "export default class {}",
|
|
"/d.js": "export default class Foo {} Foo.prop = 123",
|
|
"/e.js": "export default function() {}",
|
|
"/f.js": "export default function foo() {} foo.prop = 123",
|
|
"/g.js": "export default async function() {}",
|
|
"/h.js": "export default async function foo() {} foo.prop = 123",
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestReExportDefaultCommonJS(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import {foo as entry} from './foo'
|
|
entry()
|
|
`,
|
|
"/foo.js": `
|
|
export {default as foo} from './bar'
|
|
`,
|
|
"/bar.js": `
|
|
export default function foo() {
|
|
return exports // Force this to be a CommonJS module
|
|
}
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestExportChain(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
export {b as a} from './foo'
|
|
`,
|
|
"/foo.js": `
|
|
export {c as b} from './bar'
|
|
`,
|
|
"/bar.js": `
|
|
export const c = 123
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestExportInfiniteCycle1(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
export {a as b} from './entry'
|
|
export {b as c} from './entry'
|
|
export {c as d} from './entry'
|
|
export {d as a} from './entry'
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
expectedCompileLog: `/entry.js: error: Detected cycle while resolving import "a"
|
|
/entry.js: error: Detected cycle while resolving import "b"
|
|
/entry.js: error: Detected cycle while resolving import "c"
|
|
/entry.js: error: Detected cycle while resolving import "d"
|
|
`,
|
|
})
|
|
}
|
|
|
|
func TestExportInfiniteCycle2(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
export {a as b} from './foo'
|
|
export {c as d} from './foo'
|
|
`,
|
|
"/foo.js": `
|
|
export {b as c} from './entry'
|
|
export {d as a} from './entry'
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
expectedCompileLog: `/entry.js: error: Detected cycle while resolving import "a"
|
|
/entry.js: error: Detected cycle while resolving import "c"
|
|
/foo.js: error: Detected cycle while resolving import "b"
|
|
/foo.js: error: Detected cycle while resolving import "d"
|
|
`,
|
|
})
|
|
}
|
|
|
|
func TestJSXImportsCommonJS(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.jsx": `
|
|
import {elem, frag} from './custom-react'
|
|
console.log(<div/>, <>fragment</>)
|
|
`,
|
|
"/custom-react.js": `
|
|
module.exports = {}
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.jsx"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
JSX: config.JSXOptions{
|
|
Factory: []string{"elem"},
|
|
Fragment: []string{"frag"},
|
|
},
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestJSXImportsES6(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.jsx": `
|
|
import {elem, frag} from './custom-react'
|
|
console.log(<div/>, <>fragment</>)
|
|
`,
|
|
"/custom-react.js": `
|
|
export function elem() {}
|
|
export function frag() {}
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.jsx"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
JSX: config.JSXOptions{
|
|
Factory: []string{"elem"},
|
|
Fragment: []string{"frag"},
|
|
},
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestJSXSyntaxInJS(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
console.log(<div/>)
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
expectedScanLog: `/entry.js: error: Unexpected "<"
|
|
`,
|
|
})
|
|
}
|
|
|
|
func TestNodeModules(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/Users/user/project/src/entry.js": `
|
|
import fn from 'demo-pkg'
|
|
console.log(fn())
|
|
`,
|
|
"/Users/user/project/node_modules/demo-pkg/index.js": `
|
|
module.exports = function() {
|
|
return 123
|
|
}
|
|
`,
|
|
},
|
|
entryPaths: []string{"/Users/user/project/src/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/Users/user/project/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestRequireChildDirCommonJS(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/Users/user/project/src/entry.js": `
|
|
console.log(require('./dir'))
|
|
`,
|
|
"/Users/user/project/src/dir/index.js": `
|
|
module.exports = 123
|
|
`,
|
|
},
|
|
entryPaths: []string{"/Users/user/project/src/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestRequireChildDirES6(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/Users/user/project/src/entry.js": `
|
|
import value from './dir'
|
|
console.log(value)
|
|
`,
|
|
"/Users/user/project/src/dir/index.js": `
|
|
export default 123
|
|
`,
|
|
},
|
|
entryPaths: []string{"/Users/user/project/src/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestRequireParentDirCommonJS(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/Users/user/project/src/dir/entry.js": `
|
|
console.log(require('..'))
|
|
`,
|
|
"/Users/user/project/src/index.js": `
|
|
module.exports = 123
|
|
`,
|
|
},
|
|
entryPaths: []string{"/Users/user/project/src/dir/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestRequireParentDirES6(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/Users/user/project/src/dir/entry.js": `
|
|
import value from '..'
|
|
console.log(value)
|
|
`,
|
|
"/Users/user/project/src/index.js": `
|
|
export default 123
|
|
`,
|
|
},
|
|
entryPaths: []string{"/Users/user/project/src/dir/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestImportMissingES6(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import fn, {x as a, y as b} from './foo'
|
|
console.log(fn(a, b))
|
|
`,
|
|
"/foo.js": `
|
|
export const x = 123
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
expectedCompileLog: `/entry.js: error: No matching export for import "default"
|
|
/entry.js: error: No matching export for import "y"
|
|
`,
|
|
})
|
|
}
|
|
|
|
func TestImportMissingUnusedES6(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import fn, {x as a, y as b} from './foo'
|
|
`,
|
|
"/foo.js": `
|
|
export const x = 123
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
expectedCompileLog: `/entry.js: error: No matching export for import "default"
|
|
/entry.js: error: No matching export for import "y"
|
|
`,
|
|
})
|
|
}
|
|
|
|
func TestImportMissingCommonJS(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import fn, {x as a, y as b} from './foo'
|
|
console.log(fn(a, b))
|
|
`,
|
|
"/foo.js": `
|
|
exports.x = 123
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestImportMissingNeitherES6NorCommonJS(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/named.js": `
|
|
import fn, {x as a, y as b} from './foo'
|
|
console.log(fn(a, b))
|
|
`,
|
|
"/star.js": `
|
|
import * as ns from './foo'
|
|
console.log(ns.default(ns.x, ns.y))
|
|
`,
|
|
"/star-capture.js": `
|
|
import * as ns from './foo'
|
|
console.log(ns)
|
|
`,
|
|
"/bare.js": `
|
|
import './foo'
|
|
`,
|
|
"/require.js": `
|
|
console.log(require('./foo'))
|
|
`,
|
|
"/import.js": `
|
|
console.log(import('./foo'))
|
|
`,
|
|
"/foo.js": `
|
|
console.log('no exports here')
|
|
`,
|
|
},
|
|
entryPaths: []string{
|
|
"/named.js",
|
|
"/star.js",
|
|
"/star-capture.js",
|
|
"/bare.js",
|
|
"/require.js",
|
|
"/import.js",
|
|
},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputDir: "/out",
|
|
},
|
|
expectedCompileLog: `/named.js: warning: Import "default" will always be undefined
|
|
/named.js: warning: Import "x" will always be undefined
|
|
/named.js: warning: Import "y" will always be undefined
|
|
/star.js: warning: Import "default" will always be undefined
|
|
/star.js: warning: Import "x" will always be undefined
|
|
/star.js: warning: Import "y" will always be undefined
|
|
`,
|
|
})
|
|
}
|
|
|
|
func TestExportMissingES6(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import * as ns from './foo'
|
|
console.log(ns)
|
|
`,
|
|
"/foo.js": `
|
|
export {nope} from './bar'
|
|
`,
|
|
"/bar.js": `
|
|
export const yep = 123
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
expectedCompileLog: `/foo.js: error: No matching export for import "nope"
|
|
`,
|
|
})
|
|
}
|
|
|
|
func TestDotImport(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import {x} from '.'
|
|
console.log(x)
|
|
`,
|
|
"/index.js": `
|
|
exports.x = 123
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestRequireWithTemplate(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/a.js": `
|
|
console.log(require('./b'))
|
|
console.log(require(` + "`./b`" + `))
|
|
`,
|
|
"/b.js": `
|
|
exports.x = 123
|
|
`,
|
|
},
|
|
entryPaths: []string{"/a.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestDynamicImportWithTemplateIIFE(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/a.js": `
|
|
import('./b').then(ns => console.log(ns))
|
|
import(` + "`./b`" + `).then(ns => console.log(ns))
|
|
`,
|
|
"/b.js": `
|
|
exports.x = 123
|
|
`,
|
|
},
|
|
entryPaths: []string{"/a.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
OutputFormat: config.FormatIIFE,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestRequireAndDynamicImportInvalidTemplate(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
require(tag` + "`./b`" + `)
|
|
require(` + "`./${b}`" + `)
|
|
import(tag` + "`./b`" + `)
|
|
import(` + "`./${b}`" + `)
|
|
|
|
// Try/catch should silence this warning for require()
|
|
try {
|
|
require(tag` + "`./b`" + `)
|
|
require(` + "`./${b}`" + `)
|
|
import(tag` + "`./b`" + `)
|
|
import(` + "`./${b}`" + `)
|
|
} catch {
|
|
}
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
expectedScanLog: `/entry.js: warning: This call to "require" will not be bundled because the argument is not a string literal
|
|
/entry.js: warning: This call to "require" will not be bundled because the argument is not a string literal
|
|
/entry.js: warning: This dynamic import will not be bundled because the argument is not a string literal
|
|
/entry.js: warning: This dynamic import will not be bundled because the argument is not a string literal
|
|
/entry.js: warning: This dynamic import will not be bundled because the argument is not a string literal
|
|
/entry.js: warning: This dynamic import will not be bundled because the argument is not a string literal
|
|
`,
|
|
})
|
|
}
|
|
|
|
func TestRequireBadArgumentCount(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
require()
|
|
require("a", "b")
|
|
|
|
// Try/catch should silence this warning
|
|
try {
|
|
require()
|
|
require("a", "b")
|
|
} catch {
|
|
}
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
expectedScanLog: `/entry.js: warning: This call to "require" will not be bundled because it has 0 arguments
|
|
/entry.js: warning: This call to "require" will not be bundled because it has 2 arguments
|
|
`,
|
|
})
|
|
}
|
|
|
|
func TestRequireJson(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
console.log(require('./test.json'))
|
|
`,
|
|
"/test.json": `
|
|
{
|
|
"a": true,
|
|
"b": 123,
|
|
"c": [null]
|
|
}
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestRequireTxt(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
console.log(require('./test.txt'))
|
|
`,
|
|
"/test.txt": `This is a test.`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestRequireBadExtension(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
console.log(require('./test'))
|
|
`,
|
|
"/test": `This is a test.`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
expectedScanLog: `/entry.js: error: File could not be loaded: /test
|
|
`,
|
|
})
|
|
}
|
|
|
|
func TestFalseRequire(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
(require => require('/test.txt'))()
|
|
`,
|
|
"/test.txt": `This is a test.`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestRequireWithoutCall(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
const req = require
|
|
req('./entry')
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
expectedScanLog: `/entry.js: warning: Indirect calls to "require" will not be bundled (surround with a try/catch to silence this warning)
|
|
`,
|
|
})
|
|
}
|
|
|
|
func TestNestedRequireWithoutCall(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
(() => {
|
|
const req = require
|
|
req('./entry')
|
|
})()
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
expectedScanLog: `/entry.js: warning: Indirect calls to "require" will not be bundled (surround with a try/catch to silence this warning)
|
|
`,
|
|
})
|
|
}
|
|
|
|
// Test a workaround for the "debug" library
|
|
func TestRequireWithCallInsideTry(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
try {
|
|
const supportsColor = require('supports-color');
|
|
if (supportsColor && (supportsColor.stderr || supportsColor).level >= 2) {
|
|
exports.colors = [];
|
|
}
|
|
} catch (error) {
|
|
}
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
// Test a workaround for the "moment" library
|
|
func TestRequireWithoutCallInsideTry(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
try {
|
|
oldLocale = globalLocale._abbr;
|
|
var aliasedRequire = require;
|
|
aliasedRequire('./locale/' + name);
|
|
getSetGlobalLocale(oldLocale);
|
|
} catch (e) {}
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestSourceMap(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/Users/user/project/src/entry.js": `
|
|
import {bar} from './bar'
|
|
function foo() { bar() }
|
|
foo()
|
|
`,
|
|
"/Users/user/project/src/bar.js": `
|
|
export function bar() { throw new Error('test') }
|
|
`,
|
|
},
|
|
entryPaths: []string{"/Users/user/project/src/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
SourceMap: config.SourceMapLinkedWithComment,
|
|
AbsOutputFile: "/Users/user/project/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
// This test covers a bug where a "var" in a nested scope did not correctly
|
|
// bind with references to that symbol in sibling scopes. Instead, the
|
|
// references were incorrectly considered to be unbound even though the symbol
|
|
// should be hoisted. This caused the renamer to name them different things to
|
|
// avoid a collision, which changed the meaning of the code.
|
|
func TestNestedScopeBug(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
(() => {
|
|
function a() {
|
|
b()
|
|
}
|
|
{
|
|
var b = () => {}
|
|
}
|
|
a()
|
|
})()
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestHashbangBundle(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `#!/usr/bin/env a
|
|
import {code} from './code'
|
|
process.exit(code)
|
|
`,
|
|
"/code.js": `#!/usr/bin/env b
|
|
export const code = 0
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestHashbangNoBundle(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `#!/usr/bin/env node
|
|
process.exit(0);
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestTypeofRequireBundle(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
console.log([
|
|
typeof require,
|
|
typeof require == 'function',
|
|
typeof require == 'function' && require,
|
|
'function' == typeof require,
|
|
'function' == typeof require && require,
|
|
]);
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestTypeofRequireNoBundle(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
console.log([
|
|
typeof require,
|
|
typeof require == 'function',
|
|
typeof require == 'function' && require,
|
|
'function' == typeof require,
|
|
'function' == typeof require && require,
|
|
]);
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestTypeofRequireBadPatterns(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
console.log([
|
|
typeof require != 'function' && require,
|
|
typeof require === 'function' && require,
|
|
typeof require == 'function' || require,
|
|
typeof require == 'function' && notRequire,
|
|
typeof notRequire == 'function' && require,
|
|
|
|
'function' != typeof require && require,
|
|
'function' === typeof require && require,
|
|
'function' == typeof require || require,
|
|
'function' == typeof require && notRequire,
|
|
'function' == typeof notRequire && require,
|
|
]);
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
expectedScanLog: `/entry.js: warning: Indirect calls to "require" will not be bundled (surround with a try/catch to silence this warning)
|
|
/entry.js: warning: Indirect calls to "require" will not be bundled (surround with a try/catch to silence this warning)
|
|
/entry.js: warning: Indirect calls to "require" will not be bundled (surround with a try/catch to silence this warning)
|
|
/entry.js: warning: Indirect calls to "require" will not be bundled (surround with a try/catch to silence this warning)
|
|
/entry.js: warning: Indirect calls to "require" will not be bundled (surround with a try/catch to silence this warning)
|
|
/entry.js: warning: Indirect calls to "require" will not be bundled (surround with a try/catch to silence this warning)
|
|
/entry.js: warning: Indirect calls to "require" will not be bundled (surround with a try/catch to silence this warning)
|
|
/entry.js: warning: Indirect calls to "require" will not be bundled (surround with a try/catch to silence this warning)
|
|
`,
|
|
})
|
|
}
|
|
|
|
func TestRequireFSBrowser(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
console.log(require('fs'))
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
Platform: config.PlatformBrowser,
|
|
},
|
|
expectedScanLog: `/entry.js: error: Could not resolve "fs" (set platform to "node" when building for node)
|
|
`,
|
|
})
|
|
}
|
|
|
|
func TestRequireFSNode(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
return require('fs')
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
OutputFormat: config.FormatCommonJS,
|
|
AbsOutputFile: "/out.js",
|
|
Platform: config.PlatformNode,
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestRequireFSNodeMinify(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
return require('fs')
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
RemoveWhitespace: true,
|
|
OutputFormat: config.FormatCommonJS,
|
|
AbsOutputFile: "/out.js",
|
|
Platform: config.PlatformNode,
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestImportFSBrowser(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import 'fs'
|
|
import * as fs from 'fs'
|
|
import defaultValue from 'fs'
|
|
import {readFileSync} from 'fs'
|
|
console.log(fs, readFileSync, defaultValue)
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
Platform: config.PlatformBrowser,
|
|
},
|
|
expectedScanLog: `/entry.js: error: Could not resolve "fs" (set platform to "node" when building for node)
|
|
`,
|
|
})
|
|
}
|
|
|
|
func TestImportFSNodeCommonJS(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import 'fs'
|
|
import * as fs from 'fs'
|
|
import defaultValue from 'fs'
|
|
import {readFileSync} from 'fs'
|
|
console.log(fs, readFileSync, defaultValue)
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
OutputFormat: config.FormatCommonJS,
|
|
AbsOutputFile: "/out.js",
|
|
Platform: config.PlatformNode,
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestImportFSNodeES6(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import 'fs'
|
|
import * as fs from 'fs'
|
|
import defaultValue from 'fs'
|
|
import {readFileSync} from 'fs'
|
|
console.log(fs, readFileSync, defaultValue)
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
OutputFormat: config.FormatESModule,
|
|
AbsOutputFile: "/out.js",
|
|
Platform: config.PlatformNode,
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestExportFSBrowser(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
export * as fs from 'fs'
|
|
export {readFileSync} from 'fs'
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
Platform: config.PlatformBrowser,
|
|
},
|
|
expectedScanLog: `/entry.js: error: Could not resolve "fs" (set platform to "node" when building for node)
|
|
`,
|
|
})
|
|
}
|
|
|
|
func TestExportFSNode(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
export * as fs from 'fs'
|
|
export {readFileSync} from 'fs'
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
Platform: config.PlatformNode,
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestReExportFSNode(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
export {fs as f} from './foo'
|
|
export {readFileSync as rfs} from './foo'
|
|
`,
|
|
"/foo.js": `
|
|
export * as fs from 'fs'
|
|
export {readFileSync} from 'fs'
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
Platform: config.PlatformNode,
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestExportFSNodeInCommonJSModule(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
export * as fs from 'fs'
|
|
export {readFileSync} from 'fs'
|
|
|
|
// Force this to be a CommonJS module
|
|
exports.foo = 123
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
Platform: config.PlatformNode,
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestExportWildcardFSNodeES6(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
export * from 'fs'
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
OutputFormat: config.FormatESModule,
|
|
AbsOutputFile: "/out.js",
|
|
Platform: config.PlatformNode,
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestExportWildcardFSNodeCommonJS(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
export * from 'fs'
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
OutputFormat: config.FormatCommonJS,
|
|
AbsOutputFile: "/out.js",
|
|
Platform: config.PlatformNode,
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestMinifiedBundleES6(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import {foo} from './a'
|
|
console.log(foo())
|
|
`,
|
|
"/a.js": `
|
|
export function foo() {
|
|
return 123
|
|
}
|
|
foo()
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
MangleSyntax: true,
|
|
RemoveWhitespace: true,
|
|
MinifyIdentifiers: true,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestMinifiedBundleCommonJS(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
const {foo} = require('./a')
|
|
console.log(foo(), require('./j.json'))
|
|
`,
|
|
"/a.js": `
|
|
exports.foo = function() {
|
|
return 123
|
|
}
|
|
`,
|
|
"/j.json": `
|
|
{"test": true}
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
MangleSyntax: true,
|
|
RemoveWhitespace: true,
|
|
MinifyIdentifiers: true,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestMinifiedBundleEndingWithImportantSemicolon(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
while(foo()); // This semicolon must not be stripped
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
RemoveWhitespace: true,
|
|
OutputFormat: config.FormatIIFE,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestRuntimeNameCollisionNoBundle(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
function __require() { return 123 }
|
|
console.log(__require())
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestTopLevelReturn(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import {foo} from './foo'
|
|
foo()
|
|
`,
|
|
"/foo.js": `
|
|
// Top-level return must force CommonJS mode
|
|
if (Math.random() < 0.5) return
|
|
|
|
export function foo() {}
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestThisOutsideFunction(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
console.log(this)
|
|
console.log((x = this) => this)
|
|
console.log({x: this})
|
|
console.log(class extends this.foo {})
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestThisInsideFunction(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
function foo(x = this) { console.log(this) }
|
|
const obj = {
|
|
foo(x = this) { console.log(this) }
|
|
}
|
|
class Foo {
|
|
x = this
|
|
static y = this.z
|
|
foo(x = this) { console.log(this) }
|
|
static bar(x = this) { console.log(this) }
|
|
}
|
|
new Foo(foo(obj))
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
// The value of "this" is "exports" in CommonJS modules and undefined in ES6
|
|
// modules. This is determined by the presence of ES6 import/export syntax.
|
|
func TestThisWithES6Syntax(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import './cjs'
|
|
|
|
import './es6-import-stmt'
|
|
import './es6-import-assign'
|
|
import './es6-import-dynamic'
|
|
import './es6-import-meta'
|
|
import './es6-expr-import-dynamic'
|
|
import './es6-expr-import-meta'
|
|
|
|
import './es6-export-variable'
|
|
import './es6-export-function'
|
|
import './es6-export-async-function'
|
|
import './es6-export-enum'
|
|
import './es6-export-const-enum'
|
|
import './es6-export-module'
|
|
import './es6-export-namespace'
|
|
import './es6-export-class'
|
|
import './es6-export-abstract-class'
|
|
import './es6-export-default'
|
|
import './es6-export-clause'
|
|
import './es6-export-clause-from'
|
|
import './es6-export-star'
|
|
import './es6-export-star-as'
|
|
import './es6-export-assign'
|
|
import './es6-export-import-assign'
|
|
|
|
import './es6-ns-export-variable'
|
|
import './es6-ns-export-function'
|
|
import './es6-ns-export-async-function'
|
|
import './es6-ns-export-enum'
|
|
import './es6-ns-export-const-enum'
|
|
import './es6-ns-export-module'
|
|
import './es6-ns-export-namespace'
|
|
import './es6-ns-export-class'
|
|
import './es6-ns-export-abstract-class'
|
|
`,
|
|
"/dummy.js": `export const dummy = 123`,
|
|
"/cjs.js": `console.log(this)`,
|
|
|
|
"/es6-import-stmt.js": `import './dummy'; console.log(this)`,
|
|
"/es6-import-assign.ts": `import x = require('./dummy'); console.log(this)`,
|
|
"/es6-import-dynamic.js": `import('./dummy'); console.log(this)`,
|
|
"/es6-import-meta.js": `import.meta; console.log(this)`,
|
|
"/es6-expr-import-dynamic.js": `(import('./dummy')); console.log(this)`,
|
|
"/es6-expr-import-meta.js": `(import.meta); console.log(this)`,
|
|
|
|
"/es6-export-variable.js": `export const foo = 123; console.log(this)`,
|
|
"/es6-export-function.js": `export function foo() {} console.log(this)`,
|
|
"/es6-export-async-function.js": `export async function foo() {} console.log(this)`,
|
|
"/es6-export-enum.ts": `export enum Foo {} console.log(this)`,
|
|
"/es6-export-const-enum.ts": `export const enum Foo {} console.log(this)`,
|
|
"/es6-export-module.ts": `export module Foo {} console.log(this)`,
|
|
"/es6-export-namespace.ts": `export namespace Foo {} console.log(this)`,
|
|
"/es6-export-class.js": `export class Foo {} console.log(this)`,
|
|
"/es6-export-abstract-class.ts": `export abstract class Foo {} console.log(this)`,
|
|
"/es6-export-default.js": `export default 123; console.log(this)`,
|
|
"/es6-export-clause.js": `export {}; console.log(this)`,
|
|
"/es6-export-clause-from.js": `export {} from './dummy'; console.log(this)`,
|
|
"/es6-export-star.js": `export * from './dummy'; console.log(this)`,
|
|
"/es6-export-star-as.js": `export * as ns from './dummy'; console.log(this)`,
|
|
"/es6-export-assign.ts": `export = 123; console.log(this)`,
|
|
"/es6-export-import-assign.ts": `export import x = require('./dummy'); console.log(this)`,
|
|
|
|
"/es6-ns-export-variable.ts": `namespace ns { export const foo = 123; } console.log(this)`,
|
|
"/es6-ns-export-function.ts": `namespace ns { export function foo() {} } console.log(this)`,
|
|
"/es6-ns-export-async-function.ts": `namespace ns { export async function foo() {} } console.log(this)`,
|
|
"/es6-ns-export-enum.ts": `namespace ns { export enum Foo {} } console.log(this)`,
|
|
"/es6-ns-export-const-enum.ts": `namespace ns { export const enum Foo {} } console.log(this)`,
|
|
"/es6-ns-export-module.ts": `namespace ns { export module Foo {} } console.log(this)`,
|
|
"/es6-ns-export-namespace.ts": `namespace ns { export namespace Foo {} } console.log(this)`,
|
|
"/es6-ns-export-class.ts": `namespace ns { export class Foo {} } console.log(this)`,
|
|
"/es6-ns-export-abstract-class.ts": `namespace ns { export abstract class Foo {} } console.log(this)`,
|
|
},
|
|
entryPaths: []string{
|
|
"/entry.js",
|
|
},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestArrowFnScope(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
tests = {
|
|
0: ((x = y => x + y, y) => x + y),
|
|
1: ((y, x = y => x + y) => x + y),
|
|
2: ((x = (y = z => x + y + z, z) => x + y + z, y, z) => x + y + z),
|
|
3: ((y, z, x = (z, y = z => x + y + z) => x + y + z) => x + y + z),
|
|
4: ((x = y => x + y, y), x + y),
|
|
5: ((y, x = y => x + y), x + y),
|
|
6: ((x = (y = z => x + y + z, z) => x + y + z, y, z), x + y + z),
|
|
7: ((y, z, x = (z, y = z => x + y + z) => x + y + z), x + y + z),
|
|
};
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
MinifyIdentifiers: true,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestSwitchScopeNoBundle(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
switch (foo) { default: var foo }
|
|
switch (bar) { default: let bar }
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
MinifyIdentifiers: true,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestArgumentDefaultValueScopeNoBundle(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
export function a(x = foo) { var foo; return x }
|
|
export class b { fn(x = foo) { var foo; return x } }
|
|
export let c = [
|
|
function(x = foo) { var foo; return x },
|
|
(x = foo) => { var foo; return x },
|
|
{ fn(x = foo) { var foo; return x }},
|
|
class { fn(x = foo) { var foo; return x }},
|
|
]
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
MinifyIdentifiers: true,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestArgumentsSpecialCaseNoBundle(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
(() => {
|
|
var arguments;
|
|
|
|
function foo(x = arguments) { return arguments }
|
|
(function(x = arguments) { return arguments });
|
|
({foo(x = arguments) { return arguments }});
|
|
class Foo { foo(x = arguments) { return arguments } }
|
|
(class { foo(x = arguments) { return arguments } });
|
|
|
|
function foo(x = arguments) { var arguments; return arguments }
|
|
(function(x = arguments) { var arguments; return arguments });
|
|
({foo(x = arguments) { var arguments; return arguments }});
|
|
class Foo2 { foo(x = arguments) { var arguments; return arguments } }
|
|
(class { foo(x = arguments) { var arguments; return arguments } });
|
|
|
|
(x => arguments);
|
|
(() => arguments);
|
|
(async () => arguments);
|
|
((x = arguments) => arguments);
|
|
(async (x = arguments) => arguments);
|
|
|
|
x => arguments;
|
|
() => arguments;
|
|
async () => arguments;
|
|
(x = arguments) => arguments;
|
|
async (x = arguments) => arguments;
|
|
|
|
(x => { return arguments });
|
|
(() => { return arguments });
|
|
(async () => { return arguments });
|
|
((x = arguments) => { return arguments });
|
|
(async (x = arguments) => { return arguments });
|
|
|
|
x => { return arguments };
|
|
() => { return arguments };
|
|
async () => { return arguments };
|
|
(x = arguments) => { return arguments };
|
|
async (x = arguments) => { return arguments };
|
|
})()
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
MinifyIdentifiers: true,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestWithStatementTaintingNoBundle(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
(() => {
|
|
let local = 1
|
|
let outer = 2
|
|
let outerDead = 3
|
|
with ({}) {
|
|
var hoisted = 4
|
|
let local = 5
|
|
hoisted++
|
|
local++
|
|
if (1) outer++
|
|
if (0) outerDead++
|
|
}
|
|
if (1) {
|
|
hoisted++
|
|
local++
|
|
outer++
|
|
outerDead++
|
|
}
|
|
})()
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
MinifyIdentifiers: true,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestDirectEvalTaintingNoBundle(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
function test1() {
|
|
function add(first, second) {
|
|
return first + second
|
|
}
|
|
eval('add(1, 2)')
|
|
}
|
|
|
|
function test2() {
|
|
function add(first, second) {
|
|
return first + second
|
|
}
|
|
(0, eval)('add(1, 2)')
|
|
}
|
|
|
|
function test3() {
|
|
function add(first, second) {
|
|
return first + second
|
|
}
|
|
}
|
|
|
|
function test4(eval) {
|
|
function add(first, second) {
|
|
return first + second
|
|
}
|
|
eval('add(1, 2)')
|
|
}
|
|
|
|
function test5() {
|
|
function containsDirectEval() { eval() }
|
|
if (true) { var shouldNotBeRenamed }
|
|
}
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
MinifyIdentifiers: true,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestImportReExportES6Issue149(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/app.jsx": `
|
|
import { p as Part, h, render } from './import';
|
|
import { Internal } from './in2';
|
|
const App = () => <Part> <Internal /> T </Part>;
|
|
render(<App />, document.getElementById('app'));
|
|
`,
|
|
"/in2.jsx": `
|
|
import { p as Part, h } from './import';
|
|
export const Internal = () => <Part> Test 2 </Part>;
|
|
`,
|
|
"/import.js": `
|
|
import { h, render } from 'preact';
|
|
export const p = "p";
|
|
export { h, render }
|
|
`,
|
|
},
|
|
entryPaths: []string{"/app.jsx"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
JSX: config.JSXOptions{
|
|
Factory: []string{"h"},
|
|
},
|
|
AbsOutputFile: "/out.js",
|
|
ExternalModules: config.ExternalModules{
|
|
NodeModules: map[string]bool{
|
|
"preact": true,
|
|
},
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestExternalModuleExclusionPackage(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/index.js": `
|
|
import { S3 } from 'aws-sdk';
|
|
import { DocumentClient } from 'aws-sdk/clients/dynamodb';
|
|
export const s3 = new S3();
|
|
export const dynamodb = new DocumentClient();
|
|
`,
|
|
},
|
|
entryPaths: []string{"/index.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
ExternalModules: config.ExternalModules{
|
|
NodeModules: map[string]bool{
|
|
"aws-sdk": true,
|
|
},
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestExternalModuleExclusionScopedPackage(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/index.js": `
|
|
import '@a1'
|
|
import '@a1/a2'
|
|
import '@a1-a2'
|
|
|
|
import '@b1'
|
|
import '@b1/b2'
|
|
import '@b1/b2/b3'
|
|
import '@b1/b2-b3'
|
|
|
|
import '@c1'
|
|
import '@c1/c2'
|
|
import '@c1/c2/c3'
|
|
import '@c1/c2/c3/c4'
|
|
import '@c1/c2/c3-c4'
|
|
`,
|
|
},
|
|
entryPaths: []string{"/index.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
ExternalModules: config.ExternalModules{
|
|
NodeModules: map[string]bool{
|
|
"@a1": true,
|
|
"@b1/b2": true,
|
|
"@c1/c2/c3": true,
|
|
},
|
|
},
|
|
},
|
|
expectedScanLog: `/index.js: error: Could not resolve "@a1-a2" (mark it as external to exclude it from the bundle)
|
|
/index.js: error: Could not resolve "@b1" (mark it as external to exclude it from the bundle)
|
|
/index.js: error: Could not resolve "@b1/b2-b3" (mark it as external to exclude it from the bundle)
|
|
/index.js: error: Could not resolve "@c1" (mark it as external to exclude it from the bundle)
|
|
/index.js: error: Could not resolve "@c1/c2" (mark it as external to exclude it from the bundle)
|
|
/index.js: error: Could not resolve "@c1/c2/c3-c4" (mark it as external to exclude it from the bundle)
|
|
`,
|
|
})
|
|
}
|
|
|
|
func TestScopedExternalModuleExclusion(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/index.js": `
|
|
import { Foo } from '@scope/foo';
|
|
import { Bar } from '@scope/foo/bar';
|
|
export const foo = new Foo();
|
|
export const bar = new Bar();
|
|
`,
|
|
},
|
|
entryPaths: []string{"/index.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
ExternalModules: config.ExternalModules{
|
|
NodeModules: map[string]bool{
|
|
"@scope/foo": true,
|
|
},
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestExternalModuleExclusionRelativePath(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/Users/user/project/src/index.js": `
|
|
import './nested/folder/test'
|
|
`,
|
|
"/Users/user/project/src/nested/folder/test.js": `
|
|
import foo from './foo.js'
|
|
import out from '../../../out/in-out-dir.js'
|
|
import sha256 from '../../sha256.min.js'
|
|
import config from '/api/config?a=1&b=2'
|
|
console.log(foo, out, sha256, config)
|
|
`,
|
|
},
|
|
entryPaths: []string{"/Users/user/project/src/index.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputDir: "/Users/user/project/out",
|
|
ExternalModules: config.ExternalModules{
|
|
AbsPaths: map[string]bool{
|
|
"/Users/user/project/out/in-out-dir.js": true,
|
|
"/Users/user/project/src/nested/folder/foo.js": true,
|
|
"/Users/user/project/src/sha256.min.js": true,
|
|
"/api/config?a=1&b=2": true,
|
|
},
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestAutoExternal(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
// These URLs should be external automatically
|
|
import "http://example.com/code.js";
|
|
import "https://example.com/code.js";
|
|
import "//example.com/code.js";
|
|
import "data:application/javascript;base64,ZXhwb3J0IGRlZmF1bHQgMTIz";
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputDir: "/out",
|
|
},
|
|
})
|
|
}
|
|
|
|
// This test case makes sure many entry points don't cause a crash
|
|
func TestManyEntryPoints(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/shared.js": `export default 123`,
|
|
|
|
"/e00.js": `import x from './shared'; console.log(x)`,
|
|
"/e01.js": `import x from './shared'; console.log(x)`,
|
|
"/e02.js": `import x from './shared'; console.log(x)`,
|
|
"/e03.js": `import x from './shared'; console.log(x)`,
|
|
"/e04.js": `import x from './shared'; console.log(x)`,
|
|
"/e05.js": `import x from './shared'; console.log(x)`,
|
|
"/e06.js": `import x from './shared'; console.log(x)`,
|
|
"/e07.js": `import x from './shared'; console.log(x)`,
|
|
"/e08.js": `import x from './shared'; console.log(x)`,
|
|
"/e09.js": `import x from './shared'; console.log(x)`,
|
|
|
|
"/e10.js": `import x from './shared'; console.log(x)`,
|
|
"/e11.js": `import x from './shared'; console.log(x)`,
|
|
"/e12.js": `import x from './shared'; console.log(x)`,
|
|
"/e13.js": `import x from './shared'; console.log(x)`,
|
|
"/e14.js": `import x from './shared'; console.log(x)`,
|
|
"/e15.js": `import x from './shared'; console.log(x)`,
|
|
"/e16.js": `import x from './shared'; console.log(x)`,
|
|
"/e17.js": `import x from './shared'; console.log(x)`,
|
|
"/e18.js": `import x from './shared'; console.log(x)`,
|
|
"/e19.js": `import x from './shared'; console.log(x)`,
|
|
|
|
"/e20.js": `import x from './shared'; console.log(x)`,
|
|
"/e21.js": `import x from './shared'; console.log(x)`,
|
|
"/e22.js": `import x from './shared'; console.log(x)`,
|
|
"/e23.js": `import x from './shared'; console.log(x)`,
|
|
"/e24.js": `import x from './shared'; console.log(x)`,
|
|
"/e25.js": `import x from './shared'; console.log(x)`,
|
|
"/e26.js": `import x from './shared'; console.log(x)`,
|
|
"/e27.js": `import x from './shared'; console.log(x)`,
|
|
"/e28.js": `import x from './shared'; console.log(x)`,
|
|
"/e29.js": `import x from './shared'; console.log(x)`,
|
|
|
|
"/e30.js": `import x from './shared'; console.log(x)`,
|
|
"/e31.js": `import x from './shared'; console.log(x)`,
|
|
"/e32.js": `import x from './shared'; console.log(x)`,
|
|
"/e33.js": `import x from './shared'; console.log(x)`,
|
|
"/e34.js": `import x from './shared'; console.log(x)`,
|
|
"/e35.js": `import x from './shared'; console.log(x)`,
|
|
"/e36.js": `import x from './shared'; console.log(x)`,
|
|
"/e37.js": `import x from './shared'; console.log(x)`,
|
|
"/e38.js": `import x from './shared'; console.log(x)`,
|
|
"/e39.js": `import x from './shared'; console.log(x)`,
|
|
},
|
|
entryPaths: []string{
|
|
"/e00.js", "/e01.js", "/e02.js", "/e03.js", "/e04.js", "/e05.js", "/e06.js", "/e07.js", "/e08.js", "/e09.js",
|
|
"/e10.js", "/e11.js", "/e12.js", "/e13.js", "/e14.js", "/e15.js", "/e16.js", "/e17.js", "/e18.js", "/e19.js",
|
|
"/e20.js", "/e21.js", "/e22.js", "/e23.js", "/e24.js", "/e25.js", "/e26.js", "/e27.js", "/e28.js", "/e29.js",
|
|
"/e30.js", "/e31.js", "/e32.js", "/e33.js", "/e34.js", "/e35.js", "/e36.js", "/e37.js", "/e38.js", "/e39.js",
|
|
},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputDir: "/out",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestRenamePrivateIdentifiersNoBundle(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
class Foo {
|
|
#foo
|
|
foo = class {
|
|
#foo
|
|
#foo2
|
|
#bar
|
|
}
|
|
get #bar() {}
|
|
set #bar(x) {}
|
|
}
|
|
class Bar {
|
|
#foo
|
|
foo = class {
|
|
#foo2
|
|
#foo
|
|
#bar
|
|
}
|
|
get #bar() {}
|
|
set #bar(x) {}
|
|
}
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestMinifyPrivateIdentifiersNoBundle(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
class Foo {
|
|
#foo
|
|
foo = class {
|
|
#foo
|
|
#foo2
|
|
#bar
|
|
}
|
|
get #bar() {}
|
|
set #bar(x) {}
|
|
}
|
|
class Bar {
|
|
#foo
|
|
foo = class {
|
|
#foo2
|
|
#foo
|
|
#bar
|
|
}
|
|
get #bar() {}
|
|
set #bar(x) {}
|
|
}
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
MinifyIdentifiers: true,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestRenameLabelsNoBundle(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
foo: {
|
|
bar: {
|
|
if (x) break bar
|
|
break foo
|
|
}
|
|
}
|
|
foo2: {
|
|
bar2: {
|
|
if (x) break bar2
|
|
break foo2
|
|
}
|
|
}
|
|
foo: {
|
|
bar: {
|
|
if (x) break bar
|
|
break foo
|
|
}
|
|
}
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
// These labels should all share the same minified names
|
|
func TestMinifySiblingLabelsNoBundle(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
foo: {
|
|
bar: {
|
|
if (x) break bar
|
|
break foo
|
|
}
|
|
}
|
|
foo2: {
|
|
bar2: {
|
|
if (x) break bar2
|
|
break foo2
|
|
}
|
|
}
|
|
foo: {
|
|
bar: {
|
|
if (x) break bar
|
|
break foo
|
|
}
|
|
}
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
MinifyIdentifiers: true,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
// We shouldn't ever generate a label with the name "if"
|
|
func TestMinifyNestedLabelsNoBundle(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
L001:{L002:{L003:{L004:{L005:{L006:{L007:{L008:{L009:{L010:{L011:{L012:{L013:{L014:{L015:{L016:{nl('\n')
|
|
L017:{L018:{L019:{L020:{L021:{L022:{L023:{L024:{L025:{L026:{L027:{L028:{L029:{L030:{L031:{L032:{nl('\n')
|
|
L033:{L034:{L035:{L036:{L037:{L038:{L039:{L040:{L041:{L042:{L043:{L044:{L045:{L046:{L047:{L048:{nl('\n')
|
|
L049:{L050:{L051:{L052:{L053:{L054:{L055:{L056:{L057:{L058:{L059:{L060:{L061:{L062:{L063:{L064:{nl('\n')
|
|
L065:{L066:{L067:{L068:{L069:{L070:{L071:{L072:{L073:{L074:{L075:{L076:{L077:{L078:{L079:{L080:{nl('\n')
|
|
L081:{L082:{L083:{L084:{L085:{L086:{L087:{L088:{L089:{L090:{L091:{L092:{L093:{L094:{L095:{L096:{nl('\n')
|
|
L097:{L098:{L099:{L100:{L101:{L102:{L103:{L104:{L105:{L106:{L107:{L108:{L109:{L110:{L111:{L112:{nl('\n')
|
|
L113:{L114:{L115:{L116:{L117:{L118:{L119:{L120:{L121:{L122:{L123:{L124:{L125:{L126:{L127:{L128:{nl('\n')
|
|
L129:{L130:{L131:{L132:{L133:{L134:{L135:{L136:{L137:{L138:{L139:{L140:{L141:{L142:{L143:{L144:{nl('\n')
|
|
L145:{L146:{L147:{L148:{L149:{L150:{L151:{L152:{L153:{L154:{L155:{L156:{L157:{L158:{L159:{L160:{nl('\n')
|
|
L161:{L162:{L163:{L164:{L165:{L166:{L167:{L168:{L169:{L170:{L171:{L172:{L173:{L174:{L175:{L176:{nl('\n')
|
|
L177:{L178:{L179:{L180:{L181:{L182:{L183:{L184:{L185:{L186:{L187:{L188:{L189:{L190:{L191:{L192:{nl('\n')
|
|
L193:{L194:{L195:{L196:{L197:{L198:{L199:{L200:{L201:{L202:{L203:{L204:{L205:{L206:{L207:{L208:{nl('\n')
|
|
L209:{L210:{L211:{L212:{L213:{L214:{L215:{L216:{L217:{L218:{L219:{L220:{L221:{L222:{L223:{L224:{nl('\n')
|
|
L225:{L226:{L227:{L228:{L229:{L230:{L231:{L232:{L233:{L234:{L235:{L236:{L237:{L238:{L239:{L240:{nl('\n')
|
|
L241:{L242:{L243:{L244:{L245:{L246:{L247:{L248:{L249:{L250:{L251:{L252:{L253:{L254:{L255:{L256:{nl('\n')
|
|
L257:{L258:{L259:{L260:{L261:{L262:{L263:{L264:{L265:{L266:{L267:{L268:{L269:{L270:{L271:{L272:{nl('\n')
|
|
L273:{L274:{L275:{L276:{L277:{L278:{L279:{L280:{L281:{L282:{L283:{L284:{L285:{L286:{L287:{L288:{nl('\n')
|
|
L289:{L290:{L291:{L292:{L293:{L294:{L295:{L296:{L297:{L298:{L299:{L300:{L301:{L302:{L303:{L304:{nl('\n')
|
|
L305:{L306:{L307:{L308:{L309:{L310:{L311:{L312:{L313:{L314:{L315:{L316:{L317:{L318:{L319:{L320:{nl('\n')
|
|
L321:{L322:{L323:{L324:{L325:{L326:{L327:{L328:{L329:{L330:{L331:{L332:{L333:{}}}}}}}}}}}}}}}}}}nl('\n')
|
|
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}nl('\n')
|
|
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}nl('\n')
|
|
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}nl('\n')
|
|
}}}}}}}}}}}}}}}}}}}}}}}}}}}
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
RemoveWhitespace: true,
|
|
MinifyIdentifiers: true,
|
|
MangleSyntax: true,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestExportsAndModuleFormatCommonJS(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import * as foo from './foo/test'
|
|
import * as bar from './bar/test'
|
|
console.log(exports, module.exports, foo, bar)
|
|
`,
|
|
"/foo/test.js": `
|
|
export let foo = 123
|
|
`,
|
|
"/bar/test.js": `
|
|
export let bar = 123
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
OutputFormat: config.FormatCommonJS,
|
|
AbsOutputFile: "/out.js",
|
|
Platform: config.PlatformNode,
|
|
},
|
|
|
|
// The "test_exports" names must be different
|
|
})
|
|
}
|
|
|
|
func TestMinifiedExportsAndModuleFormatCommonJS(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import * as foo from './foo/test'
|
|
import * as bar from './bar/test'
|
|
console.log(exports, module.exports, foo, bar)
|
|
`,
|
|
"/foo/test.js": `
|
|
export let foo = 123
|
|
`,
|
|
"/bar/test.js": `
|
|
export let bar = 123
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
MinifyIdentifiers: true,
|
|
OutputFormat: config.FormatCommonJS,
|
|
AbsOutputFile: "/out.js",
|
|
Platform: config.PlatformNode,
|
|
},
|
|
|
|
// The "test_exports" names must be minified, and the "exports" and
|
|
// "module" names must not be minified
|
|
})
|
|
}
|
|
|
|
// The minifier should not remove "use strict" or join it with other expressions
|
|
func TestUseStrictDirectiveMinifyNoBundle(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
'use strict'
|
|
'use loose'
|
|
a
|
|
b
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
MangleSyntax: true,
|
|
RemoveWhitespace: true,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestNoOverwriteInputFileError(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
console.log(123)
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputDir: "/",
|
|
},
|
|
expectedCompileLog: "error: Refusing to overwrite input file: /entry.js\n",
|
|
})
|
|
}
|
|
|
|
func TestDuplicateEntryPointError(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
console.log(123)
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js", "/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputDir: "/out.js",
|
|
},
|
|
expectedScanLog: "error: Duplicate entry point \"/entry.js\"\n",
|
|
})
|
|
}
|
|
|
|
func TestMultipleEntryPointsSameNameCollision(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/a/entry.js": `import {foo} from '../common.js'; console.log(foo)`,
|
|
"/b/entry.js": `import {foo} from '../common.js'; console.log(foo)`,
|
|
"/common.js": `export let foo = 123`,
|
|
},
|
|
entryPaths: []string{"/a/entry.js", "/b/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputDir: "/out/",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestReExportCommonJSAsES6(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
export {bar} from './foo'
|
|
`,
|
|
"/foo.js": `
|
|
exports.bar = 123
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestReExportDefaultInternal(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
export {default as foo} from './foo'
|
|
export {default as bar} from './bar'
|
|
`,
|
|
"/foo.js": `
|
|
export default 'foo'
|
|
`,
|
|
"/bar.js": `
|
|
export default 'bar'
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestReExportDefaultExternalES6(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
export {default as foo} from 'foo'
|
|
export {bar} from './bar'
|
|
`,
|
|
"/bar.js": `
|
|
export {default as bar} from 'bar'
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
OutputFormat: config.FormatESModule,
|
|
ExternalModules: config.ExternalModules{
|
|
NodeModules: map[string]bool{
|
|
"foo": true,
|
|
"bar": true,
|
|
},
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestReExportDefaultExternalCommonJS(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
export {default as foo} from 'foo'
|
|
export {bar} from './bar'
|
|
`,
|
|
"/bar.js": `
|
|
export {default as bar} from 'bar'
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
OutputFormat: config.FormatCommonJS,
|
|
ExternalModules: config.ExternalModules{
|
|
NodeModules: map[string]bool{
|
|
"foo": true,
|
|
"bar": true,
|
|
},
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestReExportDefaultNoBundle(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
export {default as foo} from './foo'
|
|
export {default as bar} from './bar'
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestReExportDefaultNoBundleES6(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
export {default as foo} from './foo'
|
|
export {default as bar} from './bar'
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeConvertFormat,
|
|
OutputFormat: config.FormatESModule,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestReExportDefaultNoBundleCommonJS(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
export {default as foo} from './foo'
|
|
export {default as bar} from './bar'
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeConvertFormat,
|
|
OutputFormat: config.FormatCommonJS,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestImportMetaCommonJS(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
console.log(import.meta.url, import.meta.path)
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
OutputFormat: config.FormatCommonJS,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestImportMetaES6(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
console.log(import.meta.url, import.meta.path)
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
OutputFormat: config.FormatESModule,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestImportMetaNoBundle(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
console.log(import.meta.url, import.meta.path)
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestDeduplicateCommentsInBundle(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import './a'
|
|
import './b'
|
|
import './c'
|
|
`,
|
|
"/a.js": `console.log('in a') //! Copyright notice 1`,
|
|
"/b.js": `console.log('in b') //! Copyright notice 1`,
|
|
"/c.js": `console.log('in c') //! Copyright notice 2`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
RemoveWhitespace: true,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
// The IIFE should not be an arrow function when targeting ES5
|
|
func TestIIFE_ES5(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
console.log('test');
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
UnsupportedJSFeatures: es(5),
|
|
OutputFormat: config.FormatIIFE,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestOutputExtensionRemappingFile(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
console.log('test');
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
OutputExtensions: map[string]string{".js": ".notjs"},
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestOutputExtensionRemappingDir(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
console.log('test');
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
OutputExtensions: map[string]string{".js": ".notjs"},
|
|
AbsOutputDir: "/out",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestTopLevelAwait(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
await foo;
|
|
for await (foo of bar) ;
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
expectedScanLog: `/entry.js: error: Top-level await is currently not supported when bundling
|
|
/entry.js: error: Top-level await is currently not supported when bundling
|
|
`,
|
|
})
|
|
}
|
|
|
|
func TestTopLevelAwaitNoBundle(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
await foo;
|
|
for await (foo of bar) ;
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestTopLevelAwaitNoBundleES6(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
await foo;
|
|
for await (foo of bar) ;
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
OutputFormat: config.FormatESModule,
|
|
Mode: config.ModeConvertFormat,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestTopLevelAwaitNoBundleCommonJS(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
await foo;
|
|
for await (foo of bar) ;
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
OutputFormat: config.FormatCommonJS,
|
|
Mode: config.ModeConvertFormat,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
expectedScanLog: `/entry.js: error: Top-level await is currently not supported with the "cjs" output format
|
|
/entry.js: error: Top-level await is currently not supported with the "cjs" output format
|
|
`,
|
|
})
|
|
}
|
|
|
|
func TestTopLevelAwaitNoBundleIIFE(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
await foo;
|
|
for await (foo of bar) ;
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
OutputFormat: config.FormatIIFE,
|
|
Mode: config.ModeConvertFormat,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
expectedScanLog: `/entry.js: error: Top-level await is currently not supported with the "iife" output format
|
|
/entry.js: error: Top-level await is currently not supported with the "iife" output format
|
|
`,
|
|
})
|
|
}
|
|
|
|
func TestAssignToImport(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import "./bad0.js"
|
|
import "./bad1.js"
|
|
import "./bad2.js"
|
|
import "./bad3.js"
|
|
import "./bad4.js"
|
|
import "./bad5.js"
|
|
import "./bad6.js"
|
|
import "./bad7.js"
|
|
import "./bad8.js"
|
|
import "./bad9.js"
|
|
import "./bad10.js"
|
|
import "./bad11.js"
|
|
import "./bad12.js"
|
|
import "./bad13.js"
|
|
import "./bad14.js"
|
|
import "./bad15.js"
|
|
|
|
import "./good0.js"
|
|
import "./good1.js"
|
|
import "./good2.js"
|
|
import "./good3.js"
|
|
import "./good4.js"
|
|
`,
|
|
"/node_modules/foo/index.js": ``,
|
|
|
|
"/bad0.js": `import x from "foo"; x = 1`,
|
|
"/bad1.js": `import x from "foo"; x++`,
|
|
"/bad2.js": `import x from "foo"; ([x] = 1)`,
|
|
"/bad3.js": `import x from "foo"; ({x} = 1)`,
|
|
"/bad4.js": `import x from "foo"; ({y: x} = 1)`,
|
|
"/bad5.js": `import {x} from "foo"; x++`,
|
|
"/bad6.js": `import * as x from "foo"; x++`,
|
|
"/bad7.js": `import * as x from "foo"; x.y = 1`,
|
|
"/bad8.js": `import * as x from "foo"; x[y] = 1`,
|
|
"/bad9.js": `import * as x from "foo"; x['y'] = 1`,
|
|
"/bad10.js": `import * as x from "foo"; x['y z'] = 1`,
|
|
"/bad11.js": `import x from "foo"; delete x`,
|
|
"/bad12.js": `import {x} from "foo"; delete x`,
|
|
"/bad13.js": `import * as x from "foo"; delete x.y`,
|
|
"/bad14.js": `import * as x from "foo"; delete x['y']`,
|
|
"/bad15.js": `import * as x from "foo"; delete x[y]`,
|
|
|
|
"/good0.js": `import x from "foo"; ({y = x} = 1)`,
|
|
"/good1.js": `import x from "foo"; ({[x]: y} = 1)`,
|
|
"/good2.js": `import x from "foo"; x.y = 1`,
|
|
"/good3.js": `import x from "foo"; x[y] = 1`,
|
|
"/good4.js": `import x from "foo"; x['y'] = 1`,
|
|
"/good5.js": `import x from "foo"; x['y z'] = 1`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
expectedScanLog: `/bad0.js: error: Cannot assign to import "x"
|
|
/bad1.js: error: Cannot assign to import "x"
|
|
/bad10.js: error: Cannot assign to import "y z"
|
|
/bad11.js: error: Cannot assign to import "x"
|
|
/bad12.js: error: Cannot assign to import "x"
|
|
/bad13.js: error: Cannot assign to import "y"
|
|
/bad14.js: error: Cannot assign to import "y"
|
|
/bad15.js: error: Cannot assign to property on import "x"
|
|
/bad2.js: error: Cannot assign to import "x"
|
|
/bad3.js: error: Cannot assign to import "x"
|
|
/bad4.js: error: Cannot assign to import "x"
|
|
/bad5.js: error: Cannot assign to import "x"
|
|
/bad6.js: error: Cannot assign to import "x"
|
|
/bad7.js: error: Cannot assign to import "y"
|
|
/bad8.js: error: Cannot assign to property on import "x"
|
|
/bad9.js: error: Cannot assign to import "y"
|
|
`,
|
|
})
|
|
}
|
|
|
|
func TestMinifyArguments(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
function a(x = arguments) {
|
|
let arguments
|
|
}
|
|
function b(x = arguments) {
|
|
let arguments
|
|
}
|
|
function c(x = arguments) {
|
|
let arguments
|
|
}
|
|
a()
|
|
b()
|
|
c()
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
MinifyIdentifiers: true,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestWarningsInsideNodeModules(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import "./dup-case.js"; import "./node_modules/dup-case.js"
|
|
import "./not-in.js"; import "./node_modules/not-in.js"
|
|
import "./not-instanceof.js"; import "./node_modules/not-instanceof.js"
|
|
import "./return-asi.js"; import "./node_modules/return-asi.js"
|
|
import "./bad-typeof.js"; import "./node_modules/bad-typeof.js"
|
|
import "./equals-neg-zero.js"; import "./node_modules/equals-neg-zero.js"
|
|
import "./equals-nan.js"; import "./node_modules/equals-nan.js"
|
|
import "./equals-object.js"; import "./node_modules/equals-object.js"
|
|
import "./write-getter.js"; import "./node_modules/write-getter.js"
|
|
import "./read-setter.js"; import "./node_modules/read-setter.js"
|
|
import "./delete-super.js"; import "./node_modules/delete-super.js"
|
|
`,
|
|
|
|
"/dup-case.js": "switch (x) { case 0: case 0: }",
|
|
"/node_modules/dup-case.js": "switch (x) { case 0: case 0: }",
|
|
|
|
"/not-in.js": "!a in b",
|
|
"/node_modules/not-in.js": "!a in b",
|
|
|
|
"/not-instanceof.js": "!a instanceof b",
|
|
"/node_modules/not-instanceof.js": "!a instanceof b",
|
|
|
|
"/return-asi.js": "return\n123",
|
|
"/node_modules/return-asi.js": "return\n123",
|
|
|
|
"/bad-typeof.js": "typeof x == 'null'",
|
|
"/node_modules/bad-typeof.js": "typeof x == 'null'",
|
|
|
|
"/equals-neg-zero.js": "x === -0",
|
|
"/node_modules/equals-neg-zero.js": "x === -0",
|
|
|
|
"/equals-nan.js": "x === NaN",
|
|
"/node_modules/equals-nan.js": "x === NaN",
|
|
|
|
"/equals-object.js": "x === []",
|
|
"/node_modules/equals-object.js": "x === []",
|
|
|
|
"/write-getter.js": "class Foo { get #foo() {} foo() { this.#foo = 123 } }",
|
|
"/node_modules/write-getter.js": "class Foo { get #foo() {} foo() { this.#foo = 123 } }",
|
|
|
|
"/read-setter.js": "class Foo { set #foo(x) {} foo() { return this.#foo } }",
|
|
"/node_modules/read-setter.js": "class Foo { set #foo(x) {} foo() { return this.#foo } }",
|
|
|
|
"/delete-super.js": "class Foo extends Bar { foo() { delete super.foo } }",
|
|
"/node_modules/delete-super.js": "class Foo extends Bar { foo() { delete super.foo } }",
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
expectedScanLog: `/bad-typeof.js: warning: The "typeof" operator will never evaluate to "null"
|
|
/delete-super.js: warning: Attempting to delete a property of "super" will throw a ReferenceError
|
|
/dup-case.js: warning: This case clause will never be evaluated because it duplicates an earlier case clause
|
|
/equals-nan.js: warning: Comparison with NaN using the "===" operator here is always false
|
|
/equals-neg-zero.js: warning: Comparison with -0 using the "===" operator will also match 0
|
|
/equals-object.js: warning: Comparison using the "===" operator here is always false
|
|
/not-in.js: warning: Suspicious use of the "!" operator inside the "in" operator
|
|
/not-instanceof.js: warning: Suspicious use of the "!" operator inside the "instanceof" operator
|
|
/read-setter.js: warning: Reading from setter-only property "#foo" will throw
|
|
/return-asi.js: warning: The following expression is not returned because of an automatically-inserted semicolon
|
|
/write-getter.js: warning: Writing to getter-only property "#foo" will throw
|
|
`,
|
|
})
|
|
}
|
|
|
|
func TestRequireResolve(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
console.log(require.resolve)
|
|
console.log(require.resolve())
|
|
console.log(require.resolve(foo))
|
|
console.log(require.resolve('a', 'b'))
|
|
console.log(require.resolve('./present-file'))
|
|
console.log(require.resolve('./missing-file'))
|
|
console.log(require.resolve('./external-file'))
|
|
console.log(require.resolve('missing-pkg'))
|
|
console.log(require.resolve('external-pkg'))
|
|
console.log(require.resolve('@scope/missing-pkg'))
|
|
console.log(require.resolve('@scope/external-pkg'))
|
|
try {
|
|
console.log(require.resolve('inside-try'))
|
|
} catch (e) {
|
|
}
|
|
if (false) {
|
|
console.log(require.resolve('dead-code'))
|
|
}
|
|
console.log(false ? require.resolve('dead-if') : 0)
|
|
console.log(true ? 0 : require.resolve('dead-if'))
|
|
console.log(false && require.resolve('dead-and'))
|
|
console.log(true || require.resolve('dead-or'))
|
|
console.log(true ?? require.resolve('dead-nullish'))
|
|
`,
|
|
"/present-file.js": ``,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
ExternalModules: config.ExternalModules{
|
|
AbsPaths: map[string]bool{
|
|
"/external-file": true,
|
|
},
|
|
NodeModules: map[string]bool{
|
|
"external-pkg": true,
|
|
"@scope/external-pkg": true,
|
|
},
|
|
},
|
|
},
|
|
expectedScanLog: `/entry.js: warning: Indirect calls to "require" will not be bundled (surround with a try/catch to silence this warning)
|
|
/entry.js: warning: Indirect calls to "require" will not be bundled (surround with a try/catch to silence this warning)
|
|
/entry.js: warning: Indirect calls to "require" will not be bundled (surround with a try/catch to silence this warning)
|
|
/entry.js: warning: Indirect calls to "require" will not be bundled (surround with a try/catch to silence this warning)
|
|
/entry.js: warning: "./present-file" should be marked as external for use with "require.resolve"
|
|
/entry.js: warning: "./missing-file" should be marked as external for use with "require.resolve"
|
|
/entry.js: warning: "missing-pkg" should be marked as external for use with "require.resolve"
|
|
/entry.js: warning: "@scope/missing-pkg" should be marked as external for use with "require.resolve"
|
|
`,
|
|
})
|
|
}
|
|
|
|
func TestInjectMissing(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": ``,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
InjectAbsPaths: []string{
|
|
"/inject.js",
|
|
},
|
|
},
|
|
expectedScanLog: `error: Could not read from file: /inject.js
|
|
`,
|
|
})
|
|
}
|
|
|
|
func TestInjectDuplicate(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": ``,
|
|
"/inject.js": ``,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
InjectAbsPaths: []string{
|
|
"/inject.js",
|
|
"/inject.js",
|
|
},
|
|
},
|
|
expectedScanLog: `error: Duplicate injected file "/inject.js"
|
|
`,
|
|
})
|
|
}
|
|
|
|
func TestInject(t *testing.T) {
|
|
defines := config.ProcessDefines(map[string]config.DefineData{
|
|
"chain.prop": config.DefineData{
|
|
DefineFunc: func(loc logger.Loc, findSymbol config.FindSymbol) js_ast.E {
|
|
return &js_ast.EIdentifier{Ref: findSymbol(loc, "replace")}
|
|
},
|
|
},
|
|
})
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
let sideEffects = console.log('this should be renamed')
|
|
let collide = 123
|
|
console.log(obj.prop)
|
|
console.log(chain.prop.test)
|
|
console.log(collide)
|
|
console.log(re_export)
|
|
`,
|
|
"/inject.js": `
|
|
export let obj = {}
|
|
export let sideEffects = console.log('side effects')
|
|
export let noSideEffects = /* @__PURE__ */ console.log('side effects')
|
|
`,
|
|
"/node_modules/unused/index.js": `
|
|
console.log('This is unused but still has side effects')
|
|
`,
|
|
"/node_modules/sideEffects-false/index.js": `
|
|
console.log('This is unused and has no side effects')
|
|
`,
|
|
"/node_modules/sideEffects-false/package.json": `{
|
|
"sideEffects": false
|
|
}`,
|
|
"/replacement.js": `
|
|
export let replace = {
|
|
test() {}
|
|
}
|
|
`,
|
|
"/collision.js": `
|
|
export let collide = 123
|
|
`,
|
|
"/re-export.js": `
|
|
export {re_export} from 'external-pkg'
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
Defines: &defines,
|
|
OutputFormat: config.FormatCommonJS,
|
|
InjectAbsPaths: []string{
|
|
"/inject.js",
|
|
"/node_modules/unused/index.js",
|
|
"/node_modules/sideEffects-false/index.js",
|
|
"/replacement.js",
|
|
"/collision.js",
|
|
"/re-export.js",
|
|
},
|
|
ExternalModules: config.ExternalModules{
|
|
NodeModules: map[string]bool{
|
|
"external-pkg": true,
|
|
},
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestInjectNoBundle(t *testing.T) {
|
|
defines := config.ProcessDefines(map[string]config.DefineData{
|
|
"chain.prop": config.DefineData{
|
|
DefineFunc: func(loc logger.Loc, findSymbol config.FindSymbol) js_ast.E {
|
|
return &js_ast.EIdentifier{Ref: findSymbol(loc, "replace")}
|
|
},
|
|
},
|
|
})
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
let sideEffects = console.log('this should be renamed')
|
|
let collide = 123
|
|
console.log(obj.prop)
|
|
console.log(chain.prop.test)
|
|
console.log(collide)
|
|
console.log(re_export)
|
|
`,
|
|
"/inject.js": `
|
|
export let obj = {}
|
|
export let sideEffects = console.log('side effects')
|
|
export let noSideEffects = /* @__PURE__ */ console.log('side effects')
|
|
`,
|
|
"/node_modules/unused/index.js": `
|
|
console.log('This is unused but still has side effects')
|
|
`,
|
|
"/node_modules/sideEffects-false/index.js": `
|
|
console.log('This is unused and has no side effects')
|
|
`,
|
|
"/node_modules/sideEffects-false/package.json": `{
|
|
"sideEffects": false
|
|
}`,
|
|
"/replacement.js": `
|
|
export let replace = {
|
|
test() {}
|
|
}
|
|
`,
|
|
"/collision.js": `
|
|
export let collide = 123
|
|
`,
|
|
"/re-export.js": `
|
|
export {re_export} from 'external-pkg'
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModePassThrough,
|
|
AbsOutputFile: "/out.js",
|
|
Defines: &defines,
|
|
InjectAbsPaths: []string{
|
|
"/inject.js",
|
|
"/node_modules/unused/index.js",
|
|
"/node_modules/sideEffects-false/index.js",
|
|
"/replacement.js",
|
|
"/collision.js",
|
|
"/re-export.js",
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestInjectJSX(t *testing.T) {
|
|
defines := config.ProcessDefines(map[string]config.DefineData{
|
|
"React.createElement": config.DefineData{
|
|
DefineFunc: func(loc logger.Loc, findSymbol config.FindSymbol) js_ast.E {
|
|
return &js_ast.EIdentifier{Ref: findSymbol(loc, "el")}
|
|
},
|
|
},
|
|
})
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.jsx": `
|
|
console.log(<div/>)
|
|
`,
|
|
"/inject.js": `
|
|
export function el() {}
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.jsx"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
Defines: &defines,
|
|
InjectAbsPaths: []string{
|
|
"/inject.js",
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestInjectImportTS(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.ts": `
|
|
console.log('here')
|
|
`,
|
|
"/inject.js": `
|
|
// Unused imports are automatically removed in TypeScript files (this
|
|
// is a mis-feature of the TypeScript language). However, injected
|
|
// imports are an esbuild feature so we get to decide what the
|
|
// semantics are. We do not want injected imports to disappear unless
|
|
// they have been explicitly marked as having no side effects.
|
|
console.log('must be present')
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.ts"},
|
|
options: config.Options{
|
|
Mode: config.ModeConvertFormat,
|
|
OutputFormat: config.FormatESModule,
|
|
AbsOutputFile: "/out.js",
|
|
InjectAbsPaths: []string{
|
|
"/inject.js",
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestInjectImportOrder(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.ts": `
|
|
import 'third'
|
|
console.log('third')
|
|
`,
|
|
"/inject-1.js": `
|
|
import 'first'
|
|
console.log('first')
|
|
`,
|
|
"/inject-2.js": `
|
|
import 'second'
|
|
console.log('second')
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.ts"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
InjectAbsPaths: []string{
|
|
"/inject-1.js",
|
|
"/inject-2.js",
|
|
},
|
|
ExternalModules: config.ExternalModules{
|
|
NodeModules: map[string]bool{
|
|
"first": true,
|
|
"second": true,
|
|
"third": true,
|
|
},
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestOutbase(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/a/b/c.js": `
|
|
console.log('c')
|
|
`,
|
|
"/a/b/d.js": `
|
|
console.log('d')
|
|
`,
|
|
},
|
|
entryPaths: []string{"/a/b/c.js", "/a/b/d.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputDir: "/out",
|
|
AbsOutputBase: "/",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestAvoidTDZNoBundle(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
class Foo {
|
|
static foo = new Foo
|
|
}
|
|
let foo = Foo.foo
|
|
console.log(foo)
|
|
export class Bar {}
|
|
export let bar = 123
|
|
`,
|
|
"/foo.js": `
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModePassThrough,
|
|
AbsOutputFile: "/out.js",
|
|
AvoidTDZ: true,
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestProcessEnvNodeEnvWarning(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
console.log(
|
|
process.env.NODE_ENV,
|
|
process.env.NODE_ENV,
|
|
)
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
expectedScanLog: `/entry.js: warning: Define "process.env.NODE_ENV" when bundling for the browser
|
|
`,
|
|
})
|
|
}
|
|
|
|
func TestProcessEnvNodeEnvWarningNode(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
console.log(process.env.NODE_ENV)
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
Platform: config.PlatformNode,
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestProcessEnvNodeEnvWarningDefine(t *testing.T) {
|
|
defines := config.ProcessDefines(map[string]config.DefineData{
|
|
"process.env.NODE_ENV": config.DefineData{
|
|
DefineFunc: func(loc logger.Loc, findSymbol config.FindSymbol) js_ast.E {
|
|
return &js_ast.ENull{}
|
|
},
|
|
},
|
|
})
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
console.log(process.env.NODE_ENV)
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
Defines: &defines,
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestProcessEnvNodeEnvWarningNoBundle(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
console.log(process.env.NODE_ENV)
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModePassThrough,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestKeepNamesTreeShaking(t *testing.T) {
|
|
default_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
function fnStmtRemove() {}
|
|
function fnStmtKeep() {}
|
|
fnStmtKeep()
|
|
|
|
let fnExprRemove = function remove() {}
|
|
let fnExprKeep = function keep() {}
|
|
fnExprKeep()
|
|
|
|
class clsStmtRemove {}
|
|
class clsStmtKeep {}
|
|
new clsStmtKeep()
|
|
|
|
let clsExprRemove = class remove {}
|
|
let clsExprKeep = class keep {}
|
|
new clsExprKeep()
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
KeepNames: true,
|
|
MangleSyntax: true,
|
|
},
|
|
})
|
|
}
|