Parcel: Bundle API rebuild specific asset

Created on 9 May 2020  路  6Comments  路  Source: parcel-bundler/parcel

馃檵 feature request

In case we are not using the build-in watch mode from parcel bundler, would be great to be able to rebuild the bundle without to have to rebuild the full bundle.

馃槸 Current Behavior

import * as ParcelBundler from 'parcel-bundler';
const bundler = new ParcelBundler('path/to/my/index.js', {
        outDir: './dist',
        watch: false,
});
bundler.bundle();
// some time a bit later, we change a file from the bundle so we trigger another build
bundler.bundle();
// but here the second bundle is identical to first one,
// so we would need a way to inform the bundler that something changed

馃拋 Possible Solution

Right now I found a hack, but since the code might change, it's not reliable for long terms solution:

await bundler.bundle();
// some time a bit later, we change a file from the bundle so we trigger another build

let asset = await bundler.resolveAsset(path_of_file);
await bundler.buildQueue.add(asset, true);
await bundler.bundle();
// now bundle is up to date

馃敠 Context

I am building transpiler to generate static html websites using JSX (without React).
See https://github.com/apiel/radka

Current use case is located in my watch mode script: https://github.com/apiel/radka/blob/master/src/dev.ts#L108

In this case, the build-in watch mode from parcel is not working. I need only to build a JS file and somehow because of my setup, the parcel watch mode was breaking (building for ever).

Waiting Question

All 6 comments

Parcel is supposed to determine by itself which assets changed.

a hack

Indeed 馃槃


This seems to work for me:

const fs = require("fs");
const ParcelBundler = require("parcel-bundler");

async function bundle() {
    const bundler = new ParcelBundler("./index.js", {
        outDir: "./dist",
        watch: false,
        logLevel: 1,
    });
    await bundler.bundle();
}

async function test(v) {
    fs.writeFileSync("index.js", `module.exports = ${v};`);
    await bundle(0);
}

(async function () {
    await test(0);
    console.log("bundle exports: ", require("./dist/index.js"));

    delete require.cache[require.resolve("./dist/index.js")];

    await test(1);
    console.log("bundle exports: ", require("./dist/index.js"));
})();

Unfortunately it doesn't seem to detect any changes. I first thought this was because I first delete the folder containing all the files to bundle and then write them again, but even if I don't delete the folder, it still not detect change.

Also in your example, you re-build everything from scratch. Or this is not the goal, I only want to reload the file(s) that have been changing. I will update your example to fit my request:

const fs = require("fs");
const ParcelBundler = require("parcel-bundler");

const bundler = new ParcelBundler("./index.js", {
    outDir: "./dist",
    watch: false,
    logLevel: 1,
});

async function test(v) {
    fs.writeFileSync("index.js", `module.exports = ${v};`);
    await bundler.bundle();
}

(async function () {
    await test(0);
    console.log("bundle exports: ", require("./dist/index.js"));

    delete require.cache[require.resolve("./dist/index.js")];

    await test(1);
    console.log("bundle exports: ", require("./dist/index.js"));
})();

The out put will be:

bundle exports:  0
bundle exports:  0

Now, with my hack:

const fs = require("fs");
const { resolve } = require("path");
const ParcelBundler = require("parcel-bundler");

const bundler = new ParcelBundler("./index.js", {
    outDir: "./dist",
    watch: false,
    logLevel: 1,
});

async function test(v) {
    fs.writeFileSync("./index.js", `module.exports = ${v};`);
    if (v > 0) {
        let asset = await bundler.resolveAsset(resolve("./index.js"));
        await bundler.buildQueue.add(asset, true);
    }
    await bundler.bundle();
}

(async function () {
    await test(0);
    console.log("bundle exports: ", require("./dist/index.js"));

    delete require.cache[require.resolve("./dist/index.js")];

    await test(1);
    console.log("bundle exports: ", require("./dist/index.js"));
})();

We get the expected result:

bundle exports:  0
bundle exports:  1

Also in your example, you re-build everything from scratch.

No, Parcel will determine which files changed, and internally requeue changed files

The out put will be:

bundle exports: 0
bundle exports: 0

That invoking build on a single Parcel instance multiple times doesn't work is a bug. I'm afraid you'll have to use your hack for now

I don't really understand, class Bundler in packages/core/parcel-bundler/src/Bundler.js is not singleton, also I can't see much see any singleton pattern to load the assets (after the code is big, I might miss something), e.g. this.loadedAssets = new Map(); in the constructor. So how can parcel keep history to determine if file(s) changed or not?

Also when I use new Bundler each build take 14 sec _(in my test for radka js)_, but when I use my hack, the first build get 14 sec and then the next builds take only 4 sec, that is a huge improvement...

That invoking build on a single Parcel instance multiple times doesn't work is a bug. I'm afraid you'll have to use your hack for now

Do you plan to fix this at some point?

Do you plan to fix this at some point?

For Parcel 2: yes https://github.com/parcel-bundler/parcel/pull/4290

Waoooo, parcel v2 seem very good and the caching system is impressive. Also seem that the issues I had in watch mode with the current version is solved in the v2, so I would not even need to use my own watcher.Since v2 is also much more configurable, maybe I can use all the power of parcel as main engine for my tool.

Therefor, I close the issue and hope we get this v2 soon to the final release ;-)

Was this page helpful?
0 / 5 - 0 ratings