Skip to content

Commit

Permalink
fix #2423: update text getter on output files
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Jul 31, 2022
1 parent 40ef633 commit 2f6c899
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 1 deletion.
8 changes: 8 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,13 @@
# Changelog

## Unreleased

* Update the getter for `text` in build results ([#2423](https://github.com/evanw/esbuild/issues/2423))

Output files in build results returned from esbuild's JavaScript API have both a `contents` and a `text` property to return the contents of the output file. The `contents` property is a binary UTF-8 Uint8Array and the `text` property is a JavaScript UTF-16 string. The `text` property is a getter that does the UTF-8 to UTF-16 conversion only if it's needed for better performance.

Previously if you mutate the build results object, you had to overwrite both `contents` and `text` since the value returned from the `text` getter is the original text returned by esbuild. Some people find this confusing so with this release, the getter for `text` has been updated to do the UTF-8 to UTF-16 conversion on the current value of the `contents` property instead of the original value.

## 0.14.51

* Add support for React 17's `automatic` JSX transform ([#334](https://github.com/evanw/esbuild/issues/334), [#718](https://github.com/evanw/esbuild/issues/718), [#1172](https://github.com/evanw/esbuild/issues/1172), [#2318](https://github.com/evanw/esbuild/issues/2318), [#2349](https://github.com/evanw/esbuild/pull/2349))
Expand Down
18 changes: 17 additions & 1 deletion lib/shared/common.ts
Expand Up @@ -1689,12 +1689,28 @@ function sanitizeStringArray(values: any[], property: string): string[] {
}

function convertOutputFiles({ path, contents }: protocol.BuildOutputFile): types.OutputFile {
// The text is lazily-generated for performance reasons. If no one asks for
// it, then it never needs to be generated.
let text: string | null = null;
return {
path,
contents,
get text() {
if (text === null) text = protocol.decodeUTF8(contents);
// People want to be able to set "contents" and have esbuild automatically
// derive "text" for them, so grab the contents off of this object instead
// of using our original value.
const binary = this.contents;

// This deliberately doesn't do bidirectional derivation because that could
// result in the inefficiency. For example, if we did do this and then you
// set "contents" and "text" and then asked for "contents", the second
// setter for "text" will have erased our cached "contents" value so we'd
// need to regenerate it again. Instead, "contents" is unambiguously the
// primary value and "text" is unambiguously the derived value.
if (text === null || binary !== contents) {
contents = binary;
text = protocol.decodeUTF8(binary);
}
return text;
},
}
Expand Down
38 changes: 38 additions & 0 deletions scripts/plugin-tests.js
Expand Up @@ -2768,6 +2768,44 @@ let syncTests = {
result.rebuild.dispose()
},

async onEndCallbackMutateContents({ esbuild, testDir }) {
const input = path.join(testDir, 'in.js')
await writeFileAsync(input, `x=y`)

let onEndTimes = 0

const result = await esbuild.build({
entryPoints: [input],
write: false,
plugins: [
{
name: 'some-plugin',
setup(build) {
build.onEnd(result => {
onEndTimes++

assert.deepStrictEqual(result.outputFiles[0].contents, new Uint8Array([120, 32, 61, 32, 121, 59, 10]))
assert.deepStrictEqual(result.outputFiles[0].text, 'x = y;\n')

result.outputFiles[0].contents = new Uint8Array([120, 61, 121])
assert.deepStrictEqual(result.outputFiles[0].contents, new Uint8Array([120, 61, 121]))
assert.deepStrictEqual(result.outputFiles[0].text, 'x=y')

result.outputFiles[0].contents = new Uint8Array([121, 61, 120])
assert.deepStrictEqual(result.outputFiles[0].contents, new Uint8Array([121, 61, 120]))
assert.deepStrictEqual(result.outputFiles[0].text, 'y=x')
})
},
},
],
})

assert.deepStrictEqual(onEndTimes, 1)
assert.deepStrictEqual(result.outputFiles.length, 1)
assert.deepStrictEqual(result.outputFiles[0].contents, new Uint8Array([121, 61, 120]))
assert.deepStrictEqual(result.outputFiles[0].text, 'y=x')
},

async onStartOnEndWatchMode({ esbuild, testDir }) {
const srcDir = path.join(testDir, 'src')
const outfile = path.join(testDir, 'out.js')
Expand Down

0 comments on commit 2f6c899

Please sign in to comment.