Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Directive usage for libraries #9050

Open
bvaughn opened this issue May 27, 2023 · 7 comments
Open

Directive usage for libraries #9050

bvaughn opened this issue May 27, 2023 · 7 comments

Comments

@bvaughn
Copy link

bvaughn commented May 27, 2023

Related issue: bvaughn/react-error-boundary#143

I'm currently using ParcelJS to build several React component libraries, including react-error-boundary. The main entry point for this library, index.ts, declares a "use client" directive:

"use client"; 
  
 export * from "./ErrorBoundary"; 
 export * from "./ErrorBoundaryContext"; 
 export * from "./useErrorBoundary"; 
 export * from "./withErrorBoundary"; 
  
 // TypeScript types 
 export * from "./types"; 

In the bundled output for this library though, the directive has been moved near the end of the file (as can be seen here).

I'm unaware of any way to instruct Parcel to preserve the location of the directive.

I've considered the following:

  • Insert the directive in a post-bundling step, but I assume this would break the source-maps generated by Parcel
  • Using Parcel's bundle-text syntax to import the built module as text (but I don't know how to make this work with a TypeScript module)
@mischnic
Copy link
Member

With RSC getting traction, doing this automatically might be a good idea. Would work similar to the existing shebang handling that also extract #!/usr/bin/node from the entry file and puts it at the top of the output.

A manual solution/workaround that produces correct sourcemaps would be adding an optimizer plugin:

import {Optimizer} from '@parcel/plugin';
import {blobToBuffer} from '@parcel/utils';
import SourceMap from '@parcel/source-map';

export default new Optimizer({
  async optimize({contents, map, options}) {
    let correctMap;
    if (map != null) {
      correctMap = new SourceMap(options.projectRoot);
      correctMap.addSourceMap(map, 1); // 1 = offset lines by 1
    }
    return {
      contents: `"use client";\n` + (await blobToBuffer(contents)).toString(),
      map: correctMap,
    };
  },
});

and then

{
  "extends": "@parcel/config-default",
  "optimizers": {
    "*.{js,mjs,cjs}": ["...", "that-new-optimizer"]
  }
}

@bvaughn
Copy link
Author

bvaughn commented May 27, 2023

I appreciate that pointer.

Tried it locally with:

"devDependencies": {
    "@parcel/config-default": "^2.9.0",
    "@parcel/core": "^2.9.0",
    "@parcel/packager-ts": "^2.9.0",
    "@parcel/plugin": "^2.9.0",
    "@parcel/source-map": "^2.1.1",
    "@parcel/transformer-typescript-types": "^2.9.0",
    "@parcel/utils": "^2.9.0",
    "parcel": "^2.9.0",

I hit a snag using import/export syntax. Even if I used a .mjs extension, it seemed the transitive imports weren't properly handled. So I ended up just using .js extension and require:

const { Optimizer } = require("@parcel/plugin");
const { default: SourceMap } = require("@parcel/source-map/dist/node.js");
const { blobToBuffer } = require("@parcel/utils");

module.exports = new Optimizer({
  async optimize({ contents, map, options }) {
    let correctMap;
    if (map != null) {
      correctMap = new SourceMap(options.projectRoot);
      correctMap.addSourceMap(map, 2); // Offset lines by 2
    }
    return {
      contents: `"use client";\n\n` + (await blobToBuffer(contents)).toString(),
      map: correctMap,
    };
  },
});

Seems to work well enough though 👍🏼

@mischnic
Copy link
Member

mischnic commented May 27, 2023

Looks like it would have to be this with mjs:

import utils from "@parcel/utils";
import SourceMap from "@parcel/source-map";

new SourceMap.default()
utils.blobToBuffer()

So we'll have to look into publishing ESM versions of the packages...

@danieltroger
Copy link
Contributor

So we'll have to look into publishing ESM versions of the packages...

+1, I just ran into that @parcel/fs doesn't have an ESM export

daniel@mmmmmmmmmm parcel-test % yarn node --loader @swc-node/register/esm test.ts 
file:///private/tmp/parcel-test/test.ts.mjs:2
import { MemoryFS } from "@parcel/fs";
         ^^^^^^^^
SyntaxError: Named export 'MemoryFS' not found. The requested module '@parcel/fs' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:

import pkg from '@parcel/fs';
const { MemoryFS } = pkg;

    at ModuleJob._instantiate (node:internal/modules/esm/module_job:122:21)
    at async ModuleJob.run (node:internal/modules/esm/module_job:188:5)
    at async CustomizedModuleLoader.import (node:internal/modules/esm/loader:228:24)
    at async loadESM (node:internal/process/esm_loader:40:7)
    at async handleMainPromise (node:internal/modules/run_main:66:12)

Node.js v20.5.0

@ar124officialwd
Copy link

Is there any update regarding this. I'm facing same issue with a UI library in pnpm workspaces + turborepo. Does parcel plans to deal with it or we've to deal with it manually (One would never desire)?

@sandrahoang686
Copy link

sandrahoang686 commented Jun 4, 2024

I'm hitting an issue where I see


  Error: The sourcemap provided to addSourceMap is not a valid sourcemap instance
      at NodeSourceMap.addSourceMap (...)
      at Object.optimize (...)
      at PackagerRunner.optimize 

I've followed the examples above but am still getting this error?

@Slamerz
Copy link

Slamerz commented Jul 23, 2024

@sandrahoang686 @ar124officialwd
My team and I just ran into this issue in a component library we're developing.

Put out our workaround as a plugin you can try.
parcel-optimizer-react-use-client

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants