Parcel: Fake 'fs' import is problematic. What about an export from parcel-bundler instead?

Created on 3 Jul 2018  路  7Comments  路  Source: parcel-bundler/parcel

馃挰 RFC

Currently, Parcel uses a limited shim of the node fs module to provide the ability to read in file contents at build time. This is prone to errors (conflicts with actual fs for isomorphic modules, confusion for people new to Parcel, etc) and also conceptually clunky.

I would propose that this gets replaced with an export from the parcel-bundler module instead. This would more explicitly be documented as a "magic" value that only exists at build time, with a default static export of the same name from parcel-bundler that can easily be error-checked for isomorphic modules.

馃敠 Context

  • I want to use Parcel to build modules that are isomorphic without needing to exhaustively check the shape of fs
  • I want to use Parcel to build modules that are targeting a web platform, but that have native node APIs available

    • #1244 is a prominent example of the problem here, where Parcel is clobbering the actual fs module provided by Electron.

  • I want to avoid conflicts and case-specific handling caused by only specific shimmed pieces of Node APIs existing at build time

    • For example, needing to disable ESLint rules about using path.resolve

馃捇 Examples

An example of the new method in use at build time (names subject to change):

import { dirname, inlineFileContents } from 'parcel-bundler';

const jsonContents = inlineFileContents(dirname, 'subdirectory', 'jsonfile.json');

An example of attempting to use the method outside of build time:

import { dirname, inlineFileContents } from 'parcel-bundler';

dirname; // null
inlineFileContents; // null
inlineFileContents(dirname, 'subdirectory', 'jsonfile.json'); // 'is not a function' exception

An example of an isomorphic module:

import fs from 'fs';
import { dirname, inlineFileContents } from 'parcel-bundler';

if (fs) {
  // Running in Node context
} else if (inlineFileContents) {
  // Running in Parcel build context
} else {
  // Running in the browser
}
RFC Stale

Most helpful comment

It's not really parcel specific at all. We're shimming the node API so it works everywhere.

I've already mentioned the problems with this:

  • It's not a real shim unless you're also going to shim all of path and anything else that could plausibly interact with fs. Anything short of that and you're still writing for Parcel-specific behavior.
  • If I want to use the actual fs, I can't both do that and inline resources at build time. This isn't a problem for targeting the web, but can be hugely annoying if using -t node for something meant to be used as a library in a web build.

For a simple example of the latter, a project I'm currently working on uses a secondary repo that provides an Icon component. I can't actually inline the icons themselves (svgs) at build time for the secondary repo without using a parcel plugin, which then raises more issues (for example, I can't have an Icon with inlined svgs and a Wallpaper with imported svgs)

All 7 comments

Commented here with some potential solutions: https://github.com/parcel-bundler/parcel/issues/1244#issuecomment-402370984. We allow disabling fs inlining, but it's pretty common across different tools - both webpack and browserify have plugins for it.

I don't think we want to introduce something like the above API because it is Parcel specific. We don't want users to have Parcel specific things in their code so that it is interoperable with other tools.

I don't think we want to introduce something like the above API because it is Parcel specific. We don't want users to have Parcel specific things in their code so that it is interoperable with other tools.

The basic problem here, though, is that the current behavior is Parcel specific. I can't write readFileSync calls the same way as actually using real fs because path doesn't exist, for example, and I can't write code the same way when compiling for a Node or Electron target as when compiling for a web target, even when I absolutely want to (like inlining resource files into the source at build time, but keeping that distinct from runtime fs usage).

If the concern is about being generic, it still feels like a better solution would be to define an API interface for a nonexistent package (e.g. import bundlerUtils from 'compile-time-bundler-utils';, where compile-time-bundler-utils is actually an empty package that exports nothing) and leave that open for other bundlers/compilers to use, rather than pretending that there isn't anything special going on.

@icopp for node and electron you can use 鈥攖arget node or 鈥攖arget electron and Devon already explained how to disable fs for browser. Im pretty sure every bundler does this though (with config probably)

Sent with GitHawk

for node and electron you can use 鈥攖arget node or 鈥攖arget electron and Devon already explained how to disable fs for browser.

That just reinforces my point, though: how you have to write the code depends on which flag you're passing to Parcel, which means the code you have is already Parcel-specific. It also doesn't help with the case of intentionally wanting to inline some files at build time while reading others at runtime, which could itself be done in an isomorphic way (e.g. check for both fs and fetch and do different things depending on the run environment).

It's not really parcel specific at all. We're shimming the node API so it works everywhere.

It's not really parcel specific at all. We're shimming the node API so it works everywhere.

I've already mentioned the problems with this:

  • It's not a real shim unless you're also going to shim all of path and anything else that could plausibly interact with fs. Anything short of that and you're still writing for Parcel-specific behavior.
  • If I want to use the actual fs, I can't both do that and inline resources at build time. This isn't a problem for targeting the web, but can be hugely annoying if using -t node for something meant to be used as a library in a web build.

For a simple example of the latter, a project I'm currently working on uses a secondary repo that provides an Icon component. I can't actually inline the icons themselves (svgs) at build time for the secondary repo without using a parcel plugin, which then raises more issues (for example, I can't have an Icon with inlined svgs and a Wallpaper with imported svgs)

This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 14 days if no further activity occurs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

WayneHiller picture WayneHiller  路  51Comments

natqe picture natqe  路  40Comments

shiloa picture shiloa  路  39Comments

jpsc picture jpsc  路  81Comments

edo-codes picture edo-codes  路  42Comments