esbuild icon indicating copy to clipboard operation
esbuild copied to clipboard

[BUG] `serve` does not work with `onEnd`

Open Patrick-clone opened this issue 1 year ago • 4 comments

I've found that serve happens before or ignores what happens in onEnd()? I've have this simple plugin that uses onEnd to modify the output slightly. But I can't get it to work in combination with serve. In my file system the output file(s) are modified, but the files served are not.

I'm using version 0.17.18.

Below is a minimum example project.

// build.js

import * as esbuild from 'esbuild';
import fs from "fs";
import path from "path";

let ctx = await esbuild.context({
  entryPoints: ['src/index.ts'],
  bundle: true,
  outdir: 'dist',
  write: false,
  plugins: [{
    name: 'format',
    setup(build) {
        build.onEnd(result => {
            return new Promise(async (resolve) => {
                for (const [index, outFile] of result.outputFiles.entries()) {
                    // Only modify `.js` files
                    if (outFile.path.match(/\.js$/)) {
                        // You cannot modify .text because its only a getter.
                        // Also see: https://github.com/evanw/esbuild/issues/1792
                        result.outputFiles[index].contents = Buffer.from("console.log('hello world');");
                    }
                }

                for (const outFile of result.outputFiles) {
                    const dirName = path.dirname(outFile.path);
                    //Check if path exits
                    if (!fs.existsSync(dirName)) {
                        // If not, create directory.
                        fs.mkdirSync(dirName, { recursive: true });
                    }

                    fs.writeFileSync(outFile.path, outFile.text, 'utf8');
                }

                resolve();
            });
        });
    }
}]
})

await ctx.watch()

let { host, port } = await ctx.serve({
  servedir: '.',
});
// index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
        <script src="./dist/index.js"></script>
    </head>
    <body>
    </body>
</html>
// src/index.ts
console.log('hello world');
// dist/index.js
console.log('hello world');

Patrick-clone avatar May 03 '23 09:05 Patrick-clone

I have the same issue, I was trying to use an onEnd callback to perform simple string find/replace. It seems like serve caches the build result before the onEnd callbacks run?

As a workaround you can either try switching to an onLoad callback (which may not work because I think only one onLoad callback can return content for a given path), or hook into another plugin. In my case, I am using Svelte so I was able to write a custom Svelte preprocessor to do my find/replace in the Svelte compiler instead of from esbuild. Probably a better solution anyway, but this does feel like an esbuild bug.

tyler-boyd avatar Jun 12 '23 19:06 tyler-boyd

For the moment I'm not using the esbuild server but express.

// configure esbuild.context()
const app = express();

function startExpressServer() {
    app.use(express.static('.'));

    app.listen(port, () => {
        console.log(`Listening on port: ${port}`);
    });

    app.get('/express', (req, res) => {
        // send headers to keep connection alive
        const headers = {
            'Content-Type': 'text/event-stream',
            Connection: 'keep-alive',
            'Cache-Control': 'no-cache',
        };
        res.writeHead(200, headers);

        // store `res` of client to let us send events at will
        client = res;

        // listen for client 'close' requests
        req.on('close', () => {
            client = null;
        });
    });
}

await esbuildContext.watch();
startExpressServer();

Patrick-clone avatar Jun 13 '23 05:06 Patrick-clone

I have the same issue, I was trying to use an onEnd callback to perform simple string find/replace. It seems like serve caches the build result before the onEnd callbacks run?

As a workaround you can either try switching to an onLoad callback (which may not work because I think only one onLoad callback can return content for a given path), or hook into another plugin. In my case, I am using Svelte so I was able to write a custom Svelte preprocessor to do my find/replace in the Svelte compiler instead of from esbuild. Probably a better solution anyway, but this does feel like an esbuild bug.

I try to write outputFiles ,but not working...

	outputFiles[i].contents = new Uint8Array(Buffer.from(code))

YangYongAn avatar Jan 08 '24 06:01 YangYongAn

const app = express();

function startExpressServer() { app.use(express.static('.'));

app.listen(port, () => {
    console.log(`Listening on port: ${port}`);
});

app.get('/express', (req, res) => {
    // send headers to keep connection alive
    const headers = {
        'Content-Type': 'text/event-stream',
        Connection: 'keep-alive',
        'Cache-Control': 'no-cache',
    };
    res.writeHead(200, headers);

    // store `res` of client to let us send events at will
    client = res;

    // listen for client 'close' requests
    req.on('close', () => {
        client = null;
    });
});

}

@evanw pls provide a API to change the code in esbuild server memory code ... I may very much need to modify the content after the construction is completed.

YangYongAn avatar Jan 08 '24 06:01 YangYongAn