mirror of
https://github.com/hanzoai/esbuild.git
synced 2026-09-24 16:58:50 +00:00
1967 lines
53 KiB
Go
1967 lines
53 KiB
Go
package bundler_tests
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/evanw/esbuild/internal/bundler"
|
|
"github.com/evanw/esbuild/internal/compat"
|
|
"github.com/evanw/esbuild/internal/config"
|
|
)
|
|
|
|
var loader_suite = suite{
|
|
name: "loader",
|
|
}
|
|
|
|
func TestLoaderFile(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
console.log(require('./test.svg'))
|
|
`,
|
|
"/test.svg": "<svg></svg>",
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputDir: "/out/",
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".js": config.LoaderJS,
|
|
".svg": config.LoaderFile,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderFileMultipleNoCollision(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
console.log(
|
|
require('./a/test.txt'),
|
|
require('./b/test.txt'),
|
|
)
|
|
`,
|
|
|
|
// Two files with the same contents but different paths
|
|
"/a/test.txt": "test",
|
|
"/b/test.txt": "test",
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/dist/out.js",
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".js": config.LoaderJS,
|
|
".txt": config.LoaderFile,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestJSXSyntaxInJSWithJSXLoader(t *testing.T) {
|
|
loader_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",
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".js": config.LoaderJSX,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestJSXPreserveCapitalLetter(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.jsx": `
|
|
import { mustStartWithUpperCaseLetter as Test } from './foo'
|
|
console.log(<Test/>)
|
|
`,
|
|
"/foo.js": `
|
|
export class mustStartWithUpperCaseLetter {}
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.jsx"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
JSX: config.JSXOptions{
|
|
Parse: true,
|
|
Preserve: true,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestJSXPreserveCapitalLetterMinify(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.jsx": `
|
|
import { mustStartWithUpperCaseLetter as XYYYY } from './foo'
|
|
console.log(<XYYYY tag-must-start-with-capital-letter />)
|
|
`,
|
|
"/foo.js": `
|
|
export class mustStartWithUpperCaseLetter {}
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.jsx"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
MinifyIdentifiers: true,
|
|
JSX: config.JSXOptions{
|
|
Parse: true,
|
|
Preserve: true,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestJSXPreserveCapitalLetterMinifyNested(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.jsx": `
|
|
x = () => {
|
|
class XYYYYY {} // This should be named "Y" due to frequency analysis
|
|
return <XYYYYY tag-must-start-with-capital-letter />
|
|
}
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.jsx"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
MinifyIdentifiers: true,
|
|
JSX: config.JSXOptions{
|
|
Parse: true,
|
|
Preserve: true,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestRequireCustomExtensionString(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
console.log(require('./test.custom'))
|
|
`,
|
|
"/test.custom": `#include <stdio.h>`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".js": config.LoaderJS,
|
|
".custom": config.LoaderText,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestRequireCustomExtensionBase64(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
console.log(require('./test.custom'))
|
|
`,
|
|
"/test.custom": "a\x00b\x80c\xFFd",
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".js": config.LoaderJS,
|
|
".custom": config.LoaderBase64,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestRequireCustomExtensionDataURL(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
console.log(require('./test.custom'))
|
|
`,
|
|
"/test.custom": "a\x00b\x80c\xFFd",
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".js": config.LoaderJS,
|
|
".custom": config.LoaderDataURL,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestRequireCustomExtensionPreferLongest(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
console.log(require('./test.txt'), require('./test.base64.txt'))
|
|
`,
|
|
"/test.txt": `test.txt`,
|
|
"/test.base64.txt": `test.base64.txt`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".js": config.LoaderJS,
|
|
".txt": config.LoaderText,
|
|
".base64.txt": config.LoaderBase64,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestAutoDetectMimeTypeFromExtension(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
console.log(require('./test.svg'))
|
|
`,
|
|
"/test.svg": "a\x00b\x80c\xFFd",
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".js": config.LoaderJS,
|
|
".svg": config.LoaderDataURL,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderJSONCommonJSAndES6(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
const x_json = require('./x.json')
|
|
import y_json from './y.json'
|
|
import {small, if as fi} from './z.json'
|
|
console.log(x_json, y_json, small, fi)
|
|
`,
|
|
"/x.json": `{"x": true}`,
|
|
"/y.json": `{"y1": true, "y2": false}`,
|
|
"/z.json": `{
|
|
"big": "this is a big long line of text that should be discarded",
|
|
"small": "some small text",
|
|
"if": "test keyword imports"
|
|
}`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderJSONInvalidIdentifierES6(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import * as ns from './test.json'
|
|
import * as ns2 from './test2.json'
|
|
console.log(ns['invalid-identifier'], ns2)
|
|
`,
|
|
"/test.json": `{"invalid-identifier": true}`,
|
|
"/test2.json": `{"invalid-identifier": true}`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderJSONMissingES6(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import {missing} from './test.json'
|
|
`,
|
|
"/test.json": `{"present": true}`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
expectedCompileLog: `entry.js: ERROR: No matching export in "test.json" for import "missing"
|
|
`,
|
|
})
|
|
}
|
|
|
|
func TestLoaderTextCommonJSAndES6(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
const x_txt = require('./x.txt')
|
|
import y_txt from './y.txt'
|
|
console.log(x_txt, y_txt)
|
|
`,
|
|
"/x.txt": "x",
|
|
"/y.txt": "y",
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderBase64CommonJSAndES6(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
const x_b64 = require('./x.b64')
|
|
import y_b64 from './y.b64'
|
|
console.log(x_b64, y_b64)
|
|
`,
|
|
"/x.b64": "x",
|
|
"/y.b64": "y",
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".js": config.LoaderJS,
|
|
".b64": config.LoaderBase64,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderDataURLCommonJSAndES6(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
const x_url = require('./x.txt')
|
|
import y_url from './y.txt'
|
|
console.log(x_url, y_url)
|
|
`,
|
|
"/x.txt": "x",
|
|
"/y.txt": "y",
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".js": config.LoaderJS,
|
|
".txt": config.LoaderDataURL,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderFileCommonJSAndES6(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
const x_url = require('./x.txt')
|
|
import y_url from './y.txt'
|
|
console.log(x_url, y_url)
|
|
`,
|
|
"/x.txt": "x",
|
|
"/y.txt": "y",
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".js": config.LoaderJS,
|
|
".txt": config.LoaderFile,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderFileRelativePathJS(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/src/entries/entry.js": `
|
|
import x from '../images/image.png'
|
|
console.log(x)
|
|
`,
|
|
"/src/images/image.png": "x",
|
|
},
|
|
entryPaths: []string{"/src/entries/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputBase: "/src",
|
|
AbsOutputDir: "/out",
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".js": config.LoaderJS,
|
|
".png": config.LoaderFile,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderFileRelativePathCSS(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/src/entries/entry.css": `
|
|
div {
|
|
background: url(../images/image.png);
|
|
}
|
|
`,
|
|
"/src/images/image.png": "x",
|
|
},
|
|
entryPaths: []string{"/src/entries/entry.css"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputBase: "/src",
|
|
AbsOutputDir: "/out",
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".css": config.LoaderCSS,
|
|
".png": config.LoaderFile,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderFileRelativePathAssetNamesJS(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/src/entries/entry.js": `
|
|
import x from '../images/image.png'
|
|
console.log(x)
|
|
`,
|
|
"/src/images/image.png": "x",
|
|
},
|
|
entryPaths: []string{"/src/entries/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputBase: "/src",
|
|
AbsOutputDir: "/out",
|
|
AssetPathTemplate: []config.PathTemplate{
|
|
{Data: "", Placeholder: config.DirPlaceholder},
|
|
{Data: "/", Placeholder: config.NamePlaceholder},
|
|
{Data: "-", Placeholder: config.HashPlaceholder},
|
|
},
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".js": config.LoaderJS,
|
|
".png": config.LoaderFile,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderFileExtPathAssetNamesJS(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/src/entries/entry.js": `
|
|
import x from '../images/image.png'
|
|
import y from '../uploads/file.txt'
|
|
console.log(x, y)
|
|
`,
|
|
"/src/images/image.png": "x",
|
|
"/src/uploads/file.txt": "y",
|
|
},
|
|
entryPaths: []string{"/src/entries/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputBase: "/src",
|
|
AbsOutputDir: "/out",
|
|
AssetPathTemplate: []config.PathTemplate{
|
|
{Data: "", Placeholder: config.ExtPlaceholder},
|
|
{Data: "/", Placeholder: config.NamePlaceholder},
|
|
{Data: "-", Placeholder: config.HashPlaceholder},
|
|
},
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".js": config.LoaderJS,
|
|
".png": config.LoaderFile,
|
|
".txt": config.LoaderFile,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderFileRelativePathAssetNamesCSS(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/src/entries/entry.css": `
|
|
div {
|
|
background: url(../images/image.png);
|
|
}
|
|
`,
|
|
"/src/images/image.png": "x",
|
|
},
|
|
entryPaths: []string{"/src/entries/entry.css"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputBase: "/src",
|
|
AbsOutputDir: "/out",
|
|
AssetPathTemplate: []config.PathTemplate{
|
|
{Data: "", Placeholder: config.DirPlaceholder},
|
|
{Data: "/", Placeholder: config.NamePlaceholder},
|
|
{Data: "-", Placeholder: config.HashPlaceholder},
|
|
},
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".css": config.LoaderCSS,
|
|
".png": config.LoaderFile,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderFilePublicPathJS(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/src/entries/entry.js": `
|
|
import x from '../images/image.png'
|
|
console.log(x)
|
|
`,
|
|
"/src/images/image.png": "x",
|
|
},
|
|
entryPaths: []string{"/src/entries/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputBase: "/src",
|
|
AbsOutputDir: "/out",
|
|
PublicPath: "https://example.com",
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".js": config.LoaderJS,
|
|
".png": config.LoaderFile,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderFilePublicPathCSS(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/src/entries/entry.css": `
|
|
div {
|
|
background: url(../images/image.png);
|
|
}
|
|
`,
|
|
"/src/images/image.png": "x",
|
|
},
|
|
entryPaths: []string{"/src/entries/entry.css"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputBase: "/src",
|
|
AbsOutputDir: "/out",
|
|
PublicPath: "https://example.com",
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".css": config.LoaderCSS,
|
|
".png": config.LoaderFile,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderFilePublicPathAssetNamesJS(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/src/entries/entry.js": `
|
|
import x from '../images/image.png'
|
|
console.log(x)
|
|
`,
|
|
"/src/images/image.png": "x",
|
|
},
|
|
entryPaths: []string{"/src/entries/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputBase: "/src",
|
|
AbsOutputDir: "/out",
|
|
PublicPath: "https://example.com",
|
|
AssetPathTemplate: []config.PathTemplate{
|
|
{Data: "", Placeholder: config.DirPlaceholder},
|
|
{Data: "/", Placeholder: config.NamePlaceholder},
|
|
{Data: "-", Placeholder: config.HashPlaceholder},
|
|
},
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".js": config.LoaderJS,
|
|
".png": config.LoaderFile,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderFilePublicPathAssetNamesCSS(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/src/entries/entry.css": `
|
|
div {
|
|
background: url(../images/image.png);
|
|
}
|
|
`,
|
|
"/src/images/image.png": "x",
|
|
},
|
|
entryPaths: []string{"/src/entries/entry.css"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputBase: "/src",
|
|
AbsOutputDir: "/out",
|
|
PublicPath: "https://example.com",
|
|
AssetPathTemplate: []config.PathTemplate{
|
|
{Data: "", Placeholder: config.DirPlaceholder},
|
|
{Data: "/", Placeholder: config.NamePlaceholder},
|
|
{Data: "-", Placeholder: config.HashPlaceholder},
|
|
},
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".css": config.LoaderCSS,
|
|
".png": config.LoaderFile,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderFileOneSourceTwoDifferentOutputPathsJS(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/src/entries/entry.js": `
|
|
import '../shared/common.js'
|
|
`,
|
|
"/src/entries/other/entry.js": `
|
|
import '../../shared/common.js'
|
|
`,
|
|
"/src/shared/common.js": `
|
|
import x from './common.png'
|
|
console.log(x)
|
|
`,
|
|
"/src/shared/common.png": "x",
|
|
},
|
|
entryPaths: []string{
|
|
"/src/entries/entry.js",
|
|
"/src/entries/other/entry.js",
|
|
},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputBase: "/src",
|
|
AbsOutputDir: "/out",
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".js": config.LoaderJS,
|
|
".png": config.LoaderFile,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderFileOneSourceTwoDifferentOutputPathsCSS(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/src/entries/entry.css": `
|
|
@import "../shared/common.css";
|
|
`,
|
|
"/src/entries/other/entry.css": `
|
|
@import "../../shared/common.css";
|
|
`,
|
|
"/src/shared/common.css": `
|
|
div {
|
|
background: url(common.png);
|
|
}
|
|
`,
|
|
"/src/shared/common.png": "x",
|
|
},
|
|
entryPaths: []string{
|
|
"/src/entries/entry.css",
|
|
"/src/entries/other/entry.css",
|
|
},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputBase: "/src",
|
|
AbsOutputDir: "/out",
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".css": config.LoaderCSS,
|
|
".png": config.LoaderFile,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderJSONNoBundle(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/test.json": `{"test": 123, "invalid-identifier": true}`,
|
|
},
|
|
entryPaths: []string{"/test.json"},
|
|
options: config.Options{
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderJSONNoBundleES6(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/test.json": `{"test": 123, "invalid-identifier": true}`,
|
|
},
|
|
entryPaths: []string{"/test.json"},
|
|
options: config.Options{
|
|
Mode: config.ModeConvertFormat,
|
|
OutputFormat: config.FormatESModule,
|
|
UnsupportedJSFeatures: compat.ArbitraryModuleNamespaceNames,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderJSONNoBundleES6ArbitraryModuleNamespaceNames(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/test.json": `{"test": 123, "invalid-identifier": true}`,
|
|
},
|
|
entryPaths: []string{"/test.json"},
|
|
options: config.Options{
|
|
Mode: config.ModeConvertFormat,
|
|
OutputFormat: config.FormatESModule,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderJSONNoBundleCommonJS(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/test.json": `{"test": 123, "invalid-identifier": true}`,
|
|
},
|
|
entryPaths: []string{"/test.json"},
|
|
options: config.Options{
|
|
Mode: config.ModeConvertFormat,
|
|
OutputFormat: config.FormatCommonJS,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderJSONNoBundleIIFE(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/test.json": `{"test": 123, "invalid-identifier": true}`,
|
|
},
|
|
entryPaths: []string{"/test.json"},
|
|
options: config.Options{
|
|
Mode: config.ModeConvertFormat,
|
|
OutputFormat: config.FormatIIFE,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderJSONSharedWithMultipleEntriesIssue413(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/a.js": `
|
|
import data from './data.json'
|
|
console.log('a:', data)
|
|
`,
|
|
"/b.js": `
|
|
import data from './data.json'
|
|
console.log('b:', data)
|
|
`,
|
|
"/data.json": `{"test": 123}`,
|
|
},
|
|
entryPaths: []string{"/a.js", "/b.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
OutputFormat: config.FormatESModule,
|
|
AbsOutputDir: "/out",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderFileWithQueryParameter(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
// Each of these should have a separate identity (i.e. end up in the output file twice)
|
|
import foo from './file.txt?foo'
|
|
import bar from './file.txt?bar'
|
|
console.log(foo, bar)
|
|
`,
|
|
"/file.txt": `This is some text`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputDir: "/out",
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".js": config.LoaderJS,
|
|
".txt": config.LoaderFile,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderFromExtensionWithQueryParameter(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import foo from './file.abc?query.xyz'
|
|
console.log(foo)
|
|
`,
|
|
"/file.abc": `This should not be base64 encoded`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputDir: "/out",
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".js": config.LoaderJS,
|
|
".abc": config.LoaderText,
|
|
".xyz": config.LoaderBase64,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderDataURLTextCSS(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.css": `
|
|
@import "data:text/css,body{color:%72%65%64}";
|
|
@import "data:text/css;base64,Ym9keXtiYWNrZ3JvdW5kOmJsdWV9";
|
|
@import "data:text/css;charset=UTF-8,body{color:%72%65%64}";
|
|
@import "data:text/css;charset=UTF-8;base64,Ym9keXtiYWNrZ3JvdW5kOmJsdWV9";
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.css"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputDir: "/out",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderDataURLTextCSSCannotImport(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.css": `
|
|
@import "data:text/css,@import './other.css';";
|
|
`,
|
|
"/other.css": `
|
|
div { should-not-be-imported: true }
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.css"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputDir: "/out",
|
|
},
|
|
expectedScanLog: `<data:text/css,@import './other.css';>: ERROR: Could not resolve "./other.css"
|
|
`,
|
|
})
|
|
}
|
|
|
|
func TestLoaderDataURLTextJavaScript(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import "data:text/javascript,console.log('%31%32%33')";
|
|
import "data:text/javascript;base64,Y29uc29sZS5sb2coMjM0KQ==";
|
|
import "data:text/javascript;charset=UTF-8,console.log(%31%32%33)";
|
|
import "data:text/javascript;charset=UTF-8;base64,Y29uc29sZS5sb2coMjM0KQ==";
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputDir: "/out",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderDataURLTextJavaScriptCannotImport(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import "data:text/javascript,import './other.js'"
|
|
`,
|
|
"/other.js": `
|
|
shouldNotBeImported = true
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputDir: "/out",
|
|
},
|
|
expectedScanLog: `<data:text/javascript,import './other.js'>: ERROR: Could not resolve "./other.js"
|
|
`,
|
|
})
|
|
}
|
|
|
|
// The "+" character must not be interpreted as a " " character
|
|
func TestLoaderDataURLTextJavaScriptPlusCharacter(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import "data:text/javascript,console.log(1+2)";
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputDir: "/out",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderDataURLApplicationJSON(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import a from 'data:application/json,"%31%32%33"';
|
|
import b from 'data:application/json;base64,eyJ3b3JrcyI6dHJ1ZX0=';
|
|
import c from 'data:application/json;charset=UTF-8,%31%32%33';
|
|
import d from 'data:application/json;charset=UTF-8;base64,eyJ3b3JrcyI6dHJ1ZX0=';
|
|
console.log([
|
|
a, b, c, d,
|
|
])
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputDir: "/out",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderDataURLUnknownMIME(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import a from 'data:some/thing;what,someData%31%32%33';
|
|
import b from 'data:other/thing;stuff;base64,c29tZURhdGEyMzQ=';
|
|
console.log(a, b)
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputDir: "/out",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderDataURLExtensionBasedMIME(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.foo": `
|
|
export { default as css } from "./example.css"
|
|
export { default as eot } from "./example.eot"
|
|
export { default as gif } from "./example.gif"
|
|
export { default as htm } from "./example.htm"
|
|
export { default as html } from "./example.html"
|
|
export { default as jpeg } from "./example.jpeg"
|
|
export { default as jpg } from "./example.jpg"
|
|
export { default as js } from "./example.js"
|
|
export { default as json } from "./example.json"
|
|
export { default as mjs } from "./example.mjs"
|
|
export { default as otf } from "./example.otf"
|
|
export { default as pdf } from "./example.pdf"
|
|
export { default as png } from "./example.png"
|
|
export { default as sfnt } from "./example.sfnt"
|
|
export { default as svg } from "./example.svg"
|
|
export { default as ttf } from "./example.ttf"
|
|
export { default as wasm } from "./example.wasm"
|
|
export { default as webp } from "./example.webp"
|
|
export { default as woff } from "./example.woff"
|
|
export { default as woff2 } from "./example.woff2"
|
|
export { default as xml } from "./example.xml"
|
|
`,
|
|
"/example.css": `css`,
|
|
"/example.eot": `eot`,
|
|
"/example.gif": `gif`,
|
|
"/example.htm": `htm`,
|
|
"/example.html": `html`,
|
|
"/example.jpeg": `jpeg`,
|
|
"/example.jpg": `jpg`,
|
|
"/example.js": `js`,
|
|
"/example.json": `json`,
|
|
"/example.mjs": `mjs`,
|
|
"/example.otf": `otf`,
|
|
"/example.pdf": `pdf`,
|
|
"/example.png": `png`,
|
|
"/example.sfnt": `sfnt`,
|
|
"/example.svg": `svg`,
|
|
"/example.ttf": `ttf`,
|
|
"/example.wasm": `wasm`,
|
|
"/example.webp": `webp`,
|
|
"/example.woff": `woff`,
|
|
"/example.woff2": `woff2`,
|
|
"/example.xml": `xml`,
|
|
},
|
|
entryPaths: []string{"/entry.foo"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputDir: "/out",
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".foo": config.LoaderJS,
|
|
".css": config.LoaderDataURL,
|
|
".eot": config.LoaderDataURL,
|
|
".gif": config.LoaderDataURL,
|
|
".htm": config.LoaderDataURL,
|
|
".html": config.LoaderDataURL,
|
|
".jpeg": config.LoaderDataURL,
|
|
".jpg": config.LoaderDataURL,
|
|
".js": config.LoaderDataURL,
|
|
".json": config.LoaderDataURL,
|
|
".mjs": config.LoaderDataURL,
|
|
".otf": config.LoaderDataURL,
|
|
".pdf": config.LoaderDataURL,
|
|
".png": config.LoaderDataURL,
|
|
".sfnt": config.LoaderDataURL,
|
|
".svg": config.LoaderDataURL,
|
|
".ttf": config.LoaderDataURL,
|
|
".wasm": config.LoaderDataURL,
|
|
".webp": config.LoaderDataURL,
|
|
".woff": config.LoaderDataURL,
|
|
".woff2": config.LoaderDataURL,
|
|
".xml": config.LoaderDataURL,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
// Percent-encoded data URLs should switch over to base64
|
|
// data URLs if it would result in a smaller size
|
|
func TestLoaderDataURLBase64VsPercentEncoding(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import a from './shouldUsePercent_1.txt'
|
|
import b from './shouldUsePercent_2.txt'
|
|
import c from './shouldUseBase64_1.txt'
|
|
import d from './shouldUseBase64_2.txt'
|
|
console.log(
|
|
a,
|
|
b,
|
|
c,
|
|
d,
|
|
)
|
|
`,
|
|
"/shouldUsePercent_1.txt": "\n\n\n",
|
|
"/shouldUsePercent_2.txt": "\n\n\n\n",
|
|
"/shouldUseBase64_1.txt": "\n\n\n\n\n",
|
|
"/shouldUseBase64_2.txt": "\n\n\n\n\n\n",
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".js": config.LoaderJS,
|
|
".txt": config.LoaderDataURL,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderDataURLBase64InvalidUTF8(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import a from './binary.txt'
|
|
console.log(a)
|
|
`,
|
|
"/binary.txt": "\xFF",
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".js": config.LoaderJS,
|
|
".txt": config.LoaderDataURL,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderDataURLEscapePercents(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import a from './percents.txt'
|
|
console.log(a)
|
|
`,
|
|
"/percents.txt": `
|
|
%, %3, %33, %333
|
|
%, %e, %ee, %eee
|
|
%, %E, %EE, %EEE
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".js": config.LoaderJS,
|
|
".txt": config.LoaderDataURL,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderCopyWithBundleFromJS(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/Users/user/project/src/entry.js": `
|
|
import x from "../assets/some.file"
|
|
console.log(x)
|
|
`,
|
|
"/Users/user/project/assets/some.file": `stuff`,
|
|
},
|
|
entryPaths: []string{"/Users/user/project/src/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputBase: "/Users/user/project",
|
|
AbsOutputDir: "/out",
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".js": config.LoaderJS,
|
|
".file": config.LoaderCopy,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderCopyWithBundleFromCSS(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/Users/user/project/src/entry.css": `
|
|
body {
|
|
background: url(../assets/some.file);
|
|
}
|
|
`,
|
|
"/Users/user/project/assets/some.file": `stuff`,
|
|
},
|
|
entryPaths: []string{"/Users/user/project/src/entry.css"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputBase: "/Users/user/project",
|
|
AbsOutputDir: "/out",
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".css": config.LoaderCSS,
|
|
".file": config.LoaderCopy,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderCopyWithBundleEntryPoint(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/Users/user/project/src/entry.js": `
|
|
import x from "../assets/some.file"
|
|
console.log(x)
|
|
`,
|
|
"/Users/user/project/src/entry.css": `
|
|
body {
|
|
background: url(../assets/some.file);
|
|
}
|
|
`,
|
|
"/Users/user/project/assets/some.file": `stuff`,
|
|
},
|
|
entryPaths: []string{
|
|
"/Users/user/project/src/entry.js",
|
|
"/Users/user/project/src/entry.css",
|
|
"/Users/user/project/assets/some.file",
|
|
},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputBase: "/Users/user/project",
|
|
AbsOutputDir: "/out",
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".js": config.LoaderJS,
|
|
".css": config.LoaderCSS,
|
|
".file": config.LoaderCopy,
|
|
},
|
|
NeedsMetafile: true,
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderCopyWithTransform(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/Users/user/project/src/entry.js": `console.log('entry')`,
|
|
"/Users/user/project/assets/some.file": `stuff`,
|
|
},
|
|
entryPaths: []string{
|
|
"/Users/user/project/src/entry.js",
|
|
"/Users/user/project/assets/some.file",
|
|
},
|
|
options: config.Options{
|
|
Mode: config.ModePassThrough,
|
|
AbsOutputBase: "/Users/user/project",
|
|
AbsOutputDir: "/out",
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".js": config.LoaderJS,
|
|
".file": config.LoaderCopy,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderCopyWithFormat(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/Users/user/project/src/entry.js": `console.log('entry')`,
|
|
"/Users/user/project/assets/some.file": `stuff`,
|
|
},
|
|
entryPaths: []string{
|
|
"/Users/user/project/src/entry.js",
|
|
"/Users/user/project/assets/some.file",
|
|
},
|
|
options: config.Options{
|
|
Mode: config.ModeConvertFormat,
|
|
OutputFormat: config.FormatIIFE,
|
|
AbsOutputBase: "/Users/user/project",
|
|
AbsOutputDir: "/out",
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".js": config.LoaderJS,
|
|
".file": config.LoaderCopy,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestJSXAutomaticNoNameCollision(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.jsx": `
|
|
import { Link } from "@remix-run/react"
|
|
const x = <Link {...y} key={z} />
|
|
`,
|
|
},
|
|
entryPaths: []string{"/entry.jsx"},
|
|
options: config.Options{
|
|
Mode: config.ModeConvertFormat,
|
|
OutputFormat: config.FormatCommonJS,
|
|
AbsOutputFile: "/out.js",
|
|
JSX: config.JSXOptions{
|
|
AutomaticRuntime: true,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestAssertTypeJSONWrongLoader(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import foo from './foo.json' assert { type: 'json' }
|
|
console.log(foo)
|
|
`,
|
|
"/foo.json": `{}`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".js": config.LoaderJS,
|
|
".json": config.LoaderJS,
|
|
},
|
|
},
|
|
expectedScanLog: `entry.js: ERROR: The file "foo.json" was loaded with the "js" loader
|
|
entry.js: NOTE: This import assertion requires the loader to be "json" instead:
|
|
NOTE: You need to either reconfigure esbuild to ensure that the loader for this file is "json" or you need to remove this import assertion.
|
|
`,
|
|
})
|
|
}
|
|
|
|
func TestWithTypeJSONOverrideLoader(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import foo from './foo.js' with { type: 'json' }
|
|
console.log(foo)
|
|
`,
|
|
"/foo.js": `{ "this is json not js": true }`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestWithTypeJSONOverrideLoaderGlob(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import("./foo" + bar, { with: { type: 'json' } }).then(console.log)
|
|
`,
|
|
"/foo.js": `{ "this is json not js": true }`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestWithTypeBytesOverrideLoader(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import foo from './foo.js' with { type: 'bytes' }
|
|
console.log(foo)
|
|
`,
|
|
"/foo.js": `export default 'js'`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestWithTypeBytesOverrideLoaderGlob(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import("./foo" + bar, { with: { type: 'bytes' } }).then(console.log)
|
|
`,
|
|
"/foo.js": `export default 'js'`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestWithTypeTextOverrideLoader(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import foo from './foo.js' with { type: 'text' }
|
|
console.log(foo)
|
|
`,
|
|
"/foo.js": `export default 'js'`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestWithTypeTextOverrideLoaderGlob(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import("./foo" + bar, { with: { type: 'text' } }).then(console.log)
|
|
`,
|
|
"/foo.js": `export default 'js'`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestWithBadType(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import foo from './foo.json' with { type: '' }
|
|
import bar from './foo.json' with { type: 'garbage' }
|
|
console.log(bar)
|
|
`,
|
|
"/foo.json": `{}`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
},
|
|
expectedScanLog: `entry.js: ERROR: Importing with a type attribute of "" is not supported
|
|
entry.js: ERROR: Importing with a type attribute of "garbage" is not supported
|
|
`,
|
|
})
|
|
}
|
|
|
|
func TestWithBadAttribute(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import foo from './foo.json' with { '': 'json' }
|
|
import bar from './foo.json' with { garbage: 'json' }
|
|
console.log(bar)
|
|
`,
|
|
"/foo.json": `{}`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
},
|
|
expectedScanLog: `entry.js: ERROR: Importing with the "" attribute is not supported
|
|
entry.js: ERROR: Importing with the "garbage" attribute is not supported
|
|
`,
|
|
})
|
|
}
|
|
|
|
func TestEmptyLoaderJS(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import './a.empty'
|
|
import * as ns from './b.empty'
|
|
import def from './c.empty'
|
|
import { named } from './d.empty'
|
|
console.log(ns, def, named)
|
|
`,
|
|
"/a.empty": `throw 'FAIL'`,
|
|
"/b.empty": `throw 'FAIL'`,
|
|
"/c.empty": `throw 'FAIL'`,
|
|
"/d.empty": `throw 'FAIL'`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
SourceMap: config.SourceMapExternalWithoutComment,
|
|
NeedsMetafile: true,
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".js": config.LoaderJS,
|
|
".empty": config.LoaderEmpty,
|
|
},
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
expectedCompileLog: `entry.js: WARNING: Import "named" will always be undefined because the file "d.empty" has no exports
|
|
`,
|
|
})
|
|
}
|
|
|
|
func TestEmptyLoaderCSS(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.css": `
|
|
@import 'a.empty';
|
|
a { background: url(b.empty) }
|
|
`,
|
|
"/a.empty": `body { color: fail }`,
|
|
"/b.empty": `fail`,
|
|
},
|
|
entryPaths: []string{"/entry.css"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
SourceMap: config.SourceMapExternalWithoutComment,
|
|
NeedsMetafile: true,
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".css": config.LoaderCSS,
|
|
".empty": config.LoaderEmpty,
|
|
},
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestExtensionlessLoaderJS(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import './what'
|
|
`,
|
|
"/what": `foo()`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".js": config.LoaderJS,
|
|
"": config.LoaderJS,
|
|
},
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestExtensionlessLoaderCSS(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.css": `
|
|
@import './what';
|
|
`,
|
|
"/what": `.foo { color: red }`,
|
|
},
|
|
entryPaths: []string{"/entry.css"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".css": config.LoaderCSS,
|
|
"": config.LoaderCSS,
|
|
},
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
// Make sure custom entry point output names are respected for the copy loader
|
|
func TestLoaderCopyEntryPointAdvanced(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/project/entry.js": `
|
|
import xyz from './xyz.copy'
|
|
console.log(xyz)
|
|
`,
|
|
"/project/TEST FAILED.copy": `some stuff`,
|
|
"/project/xyz.copy": `more stuff`,
|
|
},
|
|
entryPathsAdvanced: []bundler.EntryPoint{
|
|
{
|
|
InputPath: "/project/entry.js",
|
|
OutputPath: "js/input/path",
|
|
InputPathInFileNamespace: true,
|
|
},
|
|
{
|
|
InputPath: "/project/TEST FAILED.copy",
|
|
OutputPath: "copy/input/path",
|
|
InputPathInFileNamespace: true,
|
|
},
|
|
},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputDir: "/out",
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".js": config.LoaderJS,
|
|
".copy": config.LoaderCopy,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
// Make sure we don't turn "src/index.copy" into "src.copy" for files copied
|
|
// via the file loader. This is sometimes done for JS files to try to generate
|
|
// more useful names because lots of developers name their code "index.js" due
|
|
// to node's implicit "index.js" path resolution logic.
|
|
func TestLoaderCopyUseIndex(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/Users/user/project/src/index.copy": `some stuff`,
|
|
},
|
|
entryPaths: []string{"/Users/user/project/src/index.copy"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputDir: "/out",
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".copy": config.LoaderCopy,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
// Make sure that if "outfile" is used, a file copied with the copy loader is
|
|
// written out to that path. We don't want the file name to come from the
|
|
// original source name instead of the "outfile" name, for example.
|
|
func TestLoaderCopyExplicitOutputFile(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/project/TEST FAILED.copy": `some stuff`,
|
|
},
|
|
entryPaths: []string{"/project/TEST FAILED.copy"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out/this.worked",
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".copy": config.LoaderCopy,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderCopyStartsWithDotAbsPath(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/project/src/.htaccess": `some stuff`,
|
|
"/project/src/entry.js": `some.stuff()`,
|
|
"/project/src/.ts": `foo as number`,
|
|
},
|
|
entryPaths: []string{
|
|
"/project/src/.htaccess",
|
|
"/project/src/entry.js",
|
|
"/project/src/.ts",
|
|
},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputDir: "/out",
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".js": config.LoaderJS,
|
|
".ts": config.LoaderTS,
|
|
".htaccess": config.LoaderCopy,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderCopyStartsWithDotRelPath(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/project/src/.htaccess": `some stuff`,
|
|
"/project/src/entry.js": `some.stuff()`,
|
|
"/project/src/.ts": `foo as number`,
|
|
},
|
|
entryPaths: []string{
|
|
"./.htaccess",
|
|
"./entry.js",
|
|
"./.ts",
|
|
},
|
|
absWorkingDir: "/project/src",
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputDir: "/out",
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".js": config.LoaderJS,
|
|
".ts": config.LoaderTS,
|
|
".htaccess": config.LoaderCopy,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderCopyWithInjectedFileNoBundle(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/src/entry.ts": `console.log('in entry.ts')`,
|
|
"/src/inject.js": `console.log('in inject.js')`,
|
|
},
|
|
entryPaths: []string{"/src/entry.ts"},
|
|
options: config.Options{
|
|
AbsOutputDir: "/out",
|
|
InjectPaths: []string{"/src/inject.js"},
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".ts": config.LoaderTS,
|
|
".js": config.LoaderCopy,
|
|
},
|
|
},
|
|
expectedScanLog: `ERROR: Cannot inject "src/inject.js" with the "copy" loader without bundling enabled
|
|
`,
|
|
})
|
|
}
|
|
|
|
func TestLoaderCopyWithInjectedFileBundle(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/src/entry.ts": `console.log('in entry.ts')`,
|
|
"/src/inject.js": `console.log('in inject.js')`,
|
|
},
|
|
entryPaths: []string{"/src/entry.ts"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputDir: "/out",
|
|
InjectPaths: []string{"/src/inject.js"},
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".ts": config.LoaderTS,
|
|
".js": config.LoaderCopy,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderBundleWithImportAttributes(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import x from "./data.json"
|
|
import y from "./data.json" assert { type: 'json' }
|
|
import z from "./data.json" with { type: 'json' }
|
|
console.log(x === y, x !== z)
|
|
`,
|
|
"/data.json": `{ "works": true }`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderBundleWithUnknownImportAttributesAndJSLoader(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import foo from "./foo.js" with { type: 'js' }
|
|
import bar from "./bar.js" with { js: 'true' }
|
|
import foo2 from "data:text/javascript,foo" with { type: 'js' }
|
|
import bar2 from "data:text/javascript,bar" with { js: 'true' }
|
|
console.log(foo, bar, foo2, bar2)
|
|
`,
|
|
"/foo.js": `...`,
|
|
"/bar.js": `,,,`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
expectedScanLog: `entry.js: ERROR: Importing with a type attribute of "js" is not supported
|
|
entry.js: ERROR: Importing with the "js" attribute is not supported
|
|
entry.js: ERROR: Importing with a type attribute of "js" is not supported
|
|
entry.js: ERROR: Importing with the "js" attribute is not supported
|
|
`,
|
|
})
|
|
}
|
|
|
|
func TestLoaderBundleWithUnknownImportAttributesAndCopyLoader(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import foo from "./foo.thing" with { type: 'whatever' }
|
|
import bar from "./bar.thing" with { whatever: 'true' }
|
|
console.log(foo, bar)
|
|
`,
|
|
"/foo.thing": `...`,
|
|
"/bar.thing": `,,,`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".js": config.LoaderJS,
|
|
".thing": config.LoaderCopy,
|
|
},
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderBundleWithTypeJSONOnlyDefaultExport(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import x, {foo as x2} from "./data.json"
|
|
import y, {foo as y2} from "./data.json" with { type: 'json' }
|
|
`,
|
|
"/data.json": `{ "foo": 123 }`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
expectedCompileLog: `entry.js: ERROR: No matching export in "data.json with { type: 'json' }" for import "foo"
|
|
`,
|
|
})
|
|
}
|
|
|
|
func TestLoaderJSONPrototype(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import data from "./data.json"
|
|
console.log(data)
|
|
`,
|
|
"/data.json": `{
|
|
"": "The property below should be converted to a computed property:",
|
|
"__proto__": { "foo": "bar" }
|
|
}`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
MinifySyntax: true,
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderJSONPrototypeES5(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import data from "./data.json"
|
|
console.log(data)
|
|
`,
|
|
"/data.json": `{
|
|
"": "The property below should NOT be converted to a computed property for ES5:",
|
|
"__proto__": { "foo": "bar" }
|
|
}`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
MinifySyntax: true,
|
|
UnsupportedJSFeatures: es(5),
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestLoaderJSONWithBigInt(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import data from "./data.json"
|
|
console.log(data)
|
|
`,
|
|
"/data.json": `{
|
|
"invalid": [123n]
|
|
}`,
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
expectedScanLog: `data.json: ERROR: Unexpected "123n" in JSON
|
|
`,
|
|
})
|
|
}
|
|
|
|
func TestLoaderTextUTF8BOM(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/entry.js": `
|
|
import data1 from "./data1.txt"
|
|
import data2 from "./data2.txt"
|
|
console.log(data1, data2)
|
|
`,
|
|
"/data1.txt": "\xEF\xBB\xBFtext",
|
|
"/data2.txt": "text\xEF\xBB\xBF",
|
|
},
|
|
entryPaths: []string{"/entry.js"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputFile: "/out.js",
|
|
},
|
|
})
|
|
}
|
|
|
|
// See: https://github.com/evanw/esbuild/issues/4075
|
|
func TestLoaderInlineSourceMapAbsolutePathIssue4075Unix(t *testing.T) {
|
|
urlEncodedUnix := "%22file%3A%2F%2F%2Fout%2Fsrc%2Fstyles1.scss%22" // file:///out/src/styles1.scss
|
|
pathEncodedUnix := "%22%2Fout%2Fsrc%2Fstyles2.scss%22" // /out/src/styles2.scss
|
|
|
|
loader_suite.expectBundledUnix(t, bundled{
|
|
files: map[string]string{
|
|
"/home/user/project/src/entry.css": `
|
|
@import "./styles1.css";
|
|
@import "./styles2.css";
|
|
`,
|
|
"/home/user/project/src/styles1.css": `/* You can add global styles to this file, and also import other style files */
|
|
* {
|
|
content: "foo";
|
|
}
|
|
|
|
/*# sourceMappingURL=data:application/json;charset=utf-8,%7B%22version%22:3,` +
|
|
`%22sourceRoot%22:%22%22,%22sources%22:%5B` + urlEncodedUnix + `%5D,%22n` +
|
|
`ames%22:%5B%5D,%22mappings%22:%22AAAA;AACA;EACE,SAAS%22,%22file%22:%22o` +
|
|
`ut%22,%22sourcesContent%22:%5B%22/*%20You%20can%20add%20global%20styles` +
|
|
`%20to%20this%20file,%20and%20also%20import%20other%20style%20files%20%2` +
|
|
`A/%5Cn*%20%7B%5Cn%20%20content:%20%5C%22foo%5C%22%5Cn%7D%5Cn%22%5D%7D */`,
|
|
"/home/user/project/src/styles2.css": `/* You can add global styles to this file, and also import other style files */
|
|
* {
|
|
content: "bar";
|
|
}
|
|
|
|
/*# sourceMappingURL=data:application/json;charset=utf-8,%7B%22version%22:3,` +
|
|
`%22sourceRoot%22:%22%22,%22sources%22:%5B` + pathEncodedUnix + `%5D,%22` +
|
|
`names%22:%5B%5D,%22mappings%22:%22AAAA;AACA;EACE,SAAS%22,%22file%22:%22` +
|
|
`out%22,%22sourcesContent%22:%5B%22/*%20You%20can%20add%20global%20style` +
|
|
`s%20to%20this%20file,%20and%20also%20import%20other%20style%20files%20%` +
|
|
`2A/%5Cn*%20%7B%5Cn%20%20content:%20%5C%22bar%5C%22%5Cn%7D%5Cn%22%5D%7D */`,
|
|
},
|
|
entryPaths: []string{"/home/user/project/src/entry.css"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
SourceMap: config.SourceMapLinkedWithComment,
|
|
AbsOutputDir: "/out",
|
|
},
|
|
})
|
|
}
|
|
|
|
// See: https://github.com/evanw/esbuild/issues/4075
|
|
func TestLoaderInlineSourceMapAbsolutePathIssue4075Windows(t *testing.T) {
|
|
urlEncodedWin := "%22file%3A%2F%2F%2FC%3A%2Fout%2Fsrc%2Fstyles1.scss%22" // file:///C:/out/src/styles1.scss
|
|
pathEncodedWin := "%22C%3A%5C%5Cout%5C%5Csrc%5C%5Cstyles2.scss%22" // C:\out\src\styles2.scss
|
|
|
|
loader_suite.expectBundledWindows(t, bundled{
|
|
files: map[string]string{
|
|
"C:\\home\\user\\project\\src\\entry.css": `
|
|
@import "./styles1.css";
|
|
@import "./styles2.css";
|
|
`,
|
|
"C:\\home\\user\\project\\src\\styles1.css": `/* You can add global styles to this file, and also import other style files */
|
|
* {
|
|
content: "foo";
|
|
}
|
|
|
|
/*# sourceMappingURL=data:application/json;charset=utf-8,%7B%22version%22:3,` +
|
|
`%22sourceRoot%22:%22%22,%22sources%22:%5B` + urlEncodedWin + `%5D,%22n` +
|
|
`ames%22:%5B%5D,%22mappings%22:%22AAAA;AACA;EACE,SAAS%22,%22file%22:%22o` +
|
|
`ut%22,%22sourcesContent%22:%5B%22/*%20You%20can%20add%20global%20styles` +
|
|
`%20to%20this%20file,%20and%20also%20import%20other%20style%20files%20%2` +
|
|
`A/%5Cn*%20%7B%5Cn%20%20content:%20%5C%22foo%5C%22%5Cn%7D%5Cn%22%5D%7D */`,
|
|
"C:\\home\\user\\project\\src\\styles2.css": `/* You can add global styles to this file, and also import other style files */
|
|
* {
|
|
content: "bar";
|
|
}
|
|
|
|
/*# sourceMappingURL=data:application/json;charset=utf-8,%7B%22version%22:3,` +
|
|
`%22sourceRoot%22:%22%22,%22sources%22:%5B` + pathEncodedWin + `%5D,%22` +
|
|
`names%22:%5B%5D,%22mappings%22:%22AAAA;AACA;EACE,SAAS%22,%22file%22:%22` +
|
|
`out%22,%22sourcesContent%22:%5B%22/*%20You%20can%20add%20global%20style` +
|
|
`s%20to%20this%20file,%20and%20also%20import%20other%20style%20files%20%` +
|
|
`2A/%5Cn*%20%7B%5Cn%20%20content:%20%5C%22bar%5C%22%5Cn%7D%5Cn%22%5D%7D */`,
|
|
},
|
|
entryPaths: []string{"C:\\home\\user\\project\\src\\entry.css"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
SourceMap: config.SourceMapLinkedWithComment,
|
|
AbsOutputDir: "C:\\out",
|
|
},
|
|
})
|
|
}
|
|
|
|
// See: https://github.com/evanw/esbuild/issues/4370
|
|
func TestLoaderDataURLHashSuffixIssue4370(t *testing.T) {
|
|
loader_suite.expectBundled(t, bundled{
|
|
files: map[string]string{
|
|
"/icons.css": `
|
|
.triangle {
|
|
width: 10px;
|
|
height: 10px;
|
|
background: currentColor;
|
|
clip-path: url(./triangle.svg#x);
|
|
}
|
|
`,
|
|
"/triangle.svg": `<svg xmlns="http://www.w3.org/2000/svg"><defs><clipPath id="x"><path d="M0 0H10V10Z"/></clipPath></defs></svg>`,
|
|
},
|
|
entryPaths: []string{"/icons.css"},
|
|
options: config.Options{
|
|
Mode: config.ModeBundle,
|
|
AbsOutputDir: "/out/",
|
|
ExtensionToLoader: map[string]config.Loader{
|
|
".css": config.LoaderCSS,
|
|
".svg": config.LoaderDataURL,
|
|
},
|
|
},
|
|
})
|
|
}
|