React use client directive with parceljs bundler

79 Views Asked by At

Parceljs is unable to deal with "use client" directive. I've a monorepo (pnpm workspaces + turborepo). apps/web is dependent on pkgs/ui that includes UI components. Since components are all client side and need to access client side APIs, I need to add "use client" directive in my ui bundle file. Parcel bundler is misplacing the directive in output bundles, though the source file pkgs/ui/src/index.ts has used the directive at the top. Bundle has the directive somewhere at the bottom.

How I may resolve this issue with minimal work (desireably if parcel could deal it on its side)?

Key considerations is dev / watch mode. When working in development mode with turbo dev that will call all workspaces (apps/web pkgs/lib pkgs/ui) dev scripts - solution should work seamlessly when any of these gets rebuilt.


Source

pkgs/ui/src/index.ts

"use client"

export * from './button'
// ...
// ...
// ...
Bundle

pkgs/ui/dist/module.js

var $k6BiE$reactjsxdevruntime = require("react/jsx-dev-runtime");
var $k6BiE$ar124officialwdlib = require("@ar124officialwd/lib");
var $k6BiE$react = require("react");

// ...
// ...
// ...

"use client";
$parcel$exportWildcard(module.exports, $8aff37b36b5eccbe$exports);


//# sourceMappingURL=module.js.map
Desired Bundle

pkgs/ui/dist/module.js

"use client";
var $k6BiE$reactjsxdevruntime = require("react/jsx-dev-runtime");
var $k6BiE$ar124officialwdlib = require("@ar124officialwd/lib");
var $k6BiE$react = require("react");

// ...
// ...
// ...

$parcel$exportWildcard(module.exports, $8aff37b36b5eccbe$exports);


//# sourceMappingURL=module.js.map
1

There are 1 best solutions below

0
VonC On

(desirably if parcel could deal it on its side)?

That would mean a Parcel plugin, to modify ("Transformer" plugin) the bundle process and make sure "use client" stays at the top of the bundle file.

Create a file for the custom Parcel plugin, e.g., parcel-plugin-client-directive.js.

const { Transformer } = require('@parcel/plugin');

module.exports = new Transformer({
    async transform({ asset }) {
    let code = await asset.getCode();
    if (code.includes('"use client";')) {
        code = '"use client";\n' + code.replace('"use client";', '');
    }
    asset.setCode(code);
    return [asset];
    },
});

Add this plugin to your package.json:

"devDependencies": {
    ...
    "parcel-plugin-client-directive": "file:./path/to/parcel-plugin-client-directive.js"
}

Parcel’s watch mode, which is active during development, automatically rebuilds your files when changes are detected. The custom plugin will be invoked on each rebuild, making sure that the "use client" directive is correctly placed every time.

Once the plugin is part of your Parcel configuration, no extra steps are required from your side when working in dev/watch mode.