3

I have a weird behavior involving parcel and Syncfusion Spreadsheet component. Unfortunately, I think it might be specific to both parcel and/or Syncfusion's component, so it might be tricky figuring out.

I have a React application with the following package.json:

{
  "name": "parcel-test-app",
  "version": "1.0.0",
  "description": "",
  "source": "index.html",
  "scripts": {
    "cleanup": "if exist dist rmdir /q /s dist",
    "build": "npm run cleanup && parcel build --no-optimize",
    "watch": "npm run cleanup && parcel watch",
    "start": "parcel"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@parcel/transformer-sass": "^2.8.3",
    "parcel": "^2.8.3",
    "process": "^0.11.10"
  },
  "dependencies": {
    "@syncfusion/ej2-base": "^20.4.44",
    "@syncfusion/ej2-icons": "^20.4.42",
    "@syncfusion/ej2-popups": "^20.4.44",
    "@syncfusion/ej2-react-buttons": "^20.4.44",
    "@syncfusion/ej2-react-diagrams": "^20.4.42",
    "@syncfusion/ej2-react-dropdowns": "^20.4.43",
    "@syncfusion/ej2-react-inputs": "^20.4.42",
    "@syncfusion/ej2-react-lists": "^20.4.42",
    "@syncfusion/ej2-react-navigations": "^20.4.44",
    "@syncfusion/ej2-react-popups": "^20.4.44",
    "@syncfusion/ej2-react-richtexteditor": "^20.4.44",
    "@syncfusion/ej2-react-splitbuttons": "^20.4.42",
    "@syncfusion/ej2-react-spreadsheet": "^20.4.44",
    "@syncfusion/ej2-richtexteditor": "^20.4.44",
    "@types/react": "^18.0.27",
    "@types/react-dom": "^18.0.10",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "typescript": "^4.9.4"
  }
}

As you can see, it's React, TypeScript, Parcel and Syncfusion's components.

index.html is a classic placeholder, which is used by React App component:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TypeSCript & Syncfusion Spreadsheet</title>
</head>

<body>
    <div id="app"></div>
    <script type="module" src="./index.tsx"></script>
</body>

</html>
import { SyncfusionSpreadsheet } from "./SyncfusionSpreadsheet";

export const App = () => {
  return <SyncfusionSpreadsheet />;
};

The only thing I'm doing inside SyncfusionSpreadsheet is importing CSS files from its npm packages:

// Syncfusion Spreadsheet
import "./node_modules/@syncfusion/ej2-inputs/styles/bootstrap5.css";
import "./node_modules/@syncfusion/ej2-buttons/styles/bootstrap5.css";
import "./node_modules/@syncfusion/ej2-lists/styles/bootstrap5.css";
import "./node_modules/@syncfusion/ej2-dropdowns/styles/bootstrap5.css";
import "./node_modules/@syncfusion/ej2-grids/styles/bootstrap5.css";
import "./node_modules/@syncfusion/ej2-react-spreadsheet/styles/bootstrap5.css";

export const SyncfusionSpreadsheet = () => {
  return <div>SyncfusionSpreadsheet</div>;
};

Now the weird part starts. If you use npm run watch to run parcel in watching mode, you can see that it outputs a CSS bundle with spreadsheet classes inside (which come from Syncfusion's CSS imported files):

CSS bundle built in parcel watch mode

However, if you use npm run build which uses parcel build command (--no-optimize is added to disable minification as per documentation), there's no CSS bundle built at all:

no css bundle built with parcel build

What I've tried

I tried importing the external CSS files in another ways, e.g. in .css or .scss files. However, then I have another issues with parcel throwing errors about duplicated bundles (in real production app, I use multiple parcel's sources in package.json).

I also tried changing CSS imports to the following form:

import "@syncfusion/ej2-inputs/styles/bootstrap5.css";

but it didn't change anything.

I've been fighting with this issue for a long time and honestly I have no idea what's happening here. Fun part is that with external CSSes imported from other packages (e.g. from Syncfusion's Diagram npm packages) it works fine with both watch and build.

Does anyone have any idea what might be happening here? Why parcel outputs different bundles results in watch and build?

Dawid Sibiński
  • 1,657
  • 3
  • 21
  • 40

1 Answers1

2

Tracing it from the source code, we find a few differences:

  • watch always builds bundles in development mode, while build uses production
  • watch has NODE_ENV set default to development and enables auto-installation
  • watch builds in watch mode.
  • watch mode adds HMR support
  • watch mode doesn't use scope hoisting

Surprisingly enough, it's scope hoisting! When the Parcel CLI is explicitly modified to set scope hoisting to false, the bundle then builds the wanted CSS modules.

Scope hoisting has a feature that handles side effects, and would usually load in all of the CSS modules if it wasn't for the fact that our @syncfusion library has decided to say that their modules are side-effect free.

It seems that Parcel, when it notices that the package.json has this, decides to ignore all modules that don't have explicit imports when importing, including CSS modules.

This is not a bug: #7825, but rather intended behavior, and also an error on the @syncfusion maintainers for not saying that their CSS modules are not side-effect free.

In their package.json, they should have this declaration for their sideEffect field:

  "sideEffects": [
    "*.css"
  ],

However, this is also a chance for Parcel to improve -- what they should have done is raised a warning that there was an "unnecessary import of a pure file" to guide others onto finding this.

I've raised an issue on syncfusion related to this, and am waiting for a response by the maintainers.

LeoDog896
  • 3,472
  • 1
  • 15
  • 40
  • wow, thanks a lot for your time and investigation. As I understand correctly, it would be the same issue with webpack? – Dawid Sibiński Jan 30 '23 at 15:53
  • 1
    yep! It actually seems that syncfunction [had an issue related to that](https://github.com/syncfusion/ej2-javascript-ui-controls/blob/c565462a3c4f61386b1aa9bee0a0c26b4c5c54e8/controls/grids/CHANGELOG.md#bug-fixes-142), but forgot to fix it everywhere else. – LeoDog896 Jan 30 '23 at 15:58
  • funny stuff Thanks a lot of that, I knew there must've been something weird happening here, just couldn't figure it out. The bounty is yours tomorrow as it answers my question I hope Syncfusion guys fix that – Dawid Sibiński Jan 30 '23 at 16:45
  • on the other hand, it sucks that the error messages from Parcel are so meaningless in such cases... I wonder how webpack would warn about this case – Dawid Sibiński Jan 30 '23 at 16:47
  • parcel actually has a history of bad error messages -- you can find the issue tracker riddled with these, but it seems they're trying to work on it more. – LeoDog896 Jan 30 '23 at 17:19
  • It seems that syncfusion responded! https://github.com/syncfusion/ej2-javascript-ui-controls/issues/161 – LeoDog896 Feb 15 '23 at 17:17
  • yep, with a kind of workaround which I've already implemented, but the issue is still there ;) – Dawid Sibiński Feb 15 '23 at 17:24
  • Just to update on this topic - Syncfusion said they won't fix it. See: https://www.syncfusion.com/feedback/41355/need-to-solve-the-issue-on-importing-css-from-tsx-file-in-parcel-framework-using – Dawid Sibiński Jul 05 '23 at 10:55
  • Shame then... I guess the other way to do it instead of using that css importing system, you can patch the module (I know pnpm and yarn has it, not sure about npm) – LeoDog896 Jul 05 '23 at 11:43
  • Finally I kept the CSS imports in an .SCSS file, which does the job But I see some issues around parcel in quite a few other libraries as well, which are actually unsolvable or not clear where to solve them (in parcel or in library X). – Dawid Sibiński Jul 06 '23 at 07:59