Why did it fail to import css of Cesium in the packaging tool?

Posted by mherr170 on Mon, 28 Feb 2022 12:06:49 +0100

1. Cause of the problem

I use vite2 + vanillajs template to create CesiumJS project, where, main JS is like this:

import { Viewer } from 'cesium'
import './style.css'
import 'cesium/Source/Widgets/widgets.css'

let viewer
const main = () => {
  const dom = document.getElementById('app')
  viewer = new Viewer(dom)
}

document.addEventListener('DOMContentLoaded', () => {
  main()
})

It seems that the logic is perfect, the idea is clear, and there are no special questions. So I started to run npm script:

pnpm dev

However, Vite reported me a mistake on the console:

[vite] Internal server error: Missing "./Source/Widgets/widgets.css" export in "cesium" package

It seems that this problem will not appear in the templates of various front-end frameworks. I'm not sure. Some people have encountered this similar situation in Webpack. I think the export of cesium package is incomplete. See the analysis in Section 2 below.

To put it simply, when Vite's built-in pre build tool esbuild searched the dependency tree, it did not find a file with a path of ". / Source/Widgets/widgets.css" exported by the "cesium" package.

2 find solutions

But when I open node_modules/cesium/Source/Widgets / directory, widgets The CSS file is really there.

So I opened Google and found a similar issue: github.com/CesiumGS/cesium issue#9212 , I also replied to my situation in August 2021.

I didn't find a solution, so I skipped it for the time being.

Later, a foreign friend replied with a post, and the general reason was found:

Package of cesium package JSON does not export style files, mainly package The exports attribute in JSON.

So I opened the official source package JSON, find the corresponding part:

{
  "exports": {
    "./package.json": "./package.json",
    ".": {
      "require": "./index.cjs",
      "import": "./Source/Cesium.js"
    }
  }
}

2.1. Historical reasons

As we all know, the first modularization mechanism used by NodeJS is CommonJS, which was later supported by ESModule. Now NodeJS still defaults that the newly created package is CommonJS module.

Foreigners allow dual modularization in the package of NodeJS, which is prone to compatibility problems. Let's take a look at how to realize dual modularization at the beginning. Here, the default ESModule is used as modularization:

  • Set package JSON's "type": "module", so that all js files are ESM
  • Set package JSON's "module": "./dist/esm/index.js", which means where the ESM module will find the main file when importing using import syntax
  • Set package JSON's "main": "./index.cjs", which means which is the main file of CommonJS when importing a module using the require function

Later, as ESModule became the mainstream standard, NodeJS improved the above configuration method. You can still set "type": "module" to make the modularization of the current package ESM, but for the configuration of the multi modularization mechanism of the package, use the "exports" field instead, just like the configuration of cesium above.

I checked my NodeJS version:

> node -v
> v16.14.0

Obviously, it is relatively new, so it should be the export information read from "exports".

2.2. Add export

Therefore, I added the export field of "exports", so that the packaging tool can correctly identify widgets when identifying the export of cesium package CSS file.

{
  "exports": {
    "./package.json": "./package.json",
    ".": {
      "require": "./index.cjs",
      "import": "./Source/Cesium.js"
    },
+   "./Source/Widgets/widgets.css": "./Source/Widgets/widgets.css"
  }
}

In this way, the following two import statements:

import { Viewer } from 'cesium'
import 'cesium/Source/Widgets/widgets.css'

In fact:

import { Viewer } from 'cesium/Source/Cesium.js'
import 'cesium/Source/Widgets/widgets.css'

2.3. Play a trick

I think it's still too long to import css files like this. You might as well change its name in "exports":

{
  "exports": {
    "./package.json": "./package.json",
    ".": {
      "require": "./index.cjs",
      "import": "./Source/Cesium.js"
    },
+   "./index.css": "./Source/Widgets/widgets.css"
  }
}

Then you can happily use the short path import:

import { Viewer } from 'cesium'
import 'cesium/index.css'

In fact, package The exports attribute in JSON plays a role similar to exporting aliases, where "." It is equivalent to the root path of the package.

3. Where did the type prompt come from

Consider importing cesium API s as follows:

import {
  Viewer,
  Cartesian3,
  Camera
} from 'cesium'

When you use these classes, you will get good type tips. Reviewing the previous content, the sub module imported from "cesium" is actually imported from the "cesium/Source/Cesium.js" file, and next to this file is a "Cesium.d.ts" file, which plays the role of type prompt.

This type declaration file is output when Cesium uses gulp packaging.

Having said so much, the root cause is various problems caused by the historical burden of JavaScript, and the official has not modified the package for the time being For the plan of exports in JSON, if you report this error, you just need to modify it slightly according to the above method.

Topics: css cesium