ES6 module, what counts as the first import? - javascript

Here's my module:
console.log("module imported");
export function call(){};
In main.ts:
import * as mod from './module';
// other code that doesn't use mod.
I would have expected this to log "module imported" to the console. In fact, the example seems pretty much the same as this one. And they say:
A module code is evaluated only the first time when imported
But there are no console logs. However, after the following edits to main.ts the log message appears:
import * as mod from './module';
if(false){
mod.call();
}
It would make sense if only the first time the module is actually used counted as the first import. But here the log message seems to be based on static analysis alone. The code path that uses the module is never executed.
How does this work? What counts as the first import of an ES6 module?
Also, my gut feeling says that this might be about the bundler. Does it optimize away an unused import like this? I'm running these code snippets in a react app, created with:
npx create-react-app my-app --template typescript
cd my-app
# add the module and import it to index.tsx
npm i
npm run start
# browser opens, check the console
On the other hand, the typescript react app also has imports like './index.css' and they are only there for the bundler to package them. It seems common to import something only for its side-effects.
I have searched for related questions but so far haven't found something with this specific problem:
Run ES6 code only if module is executed directly
`if __name__ == '__main__'` equivalent in javascript es6 modules
In browser JS code that imports from ES6 module is not executed
The last of these looks like a duplicate, but it is about a specific syntax error in the module resolution.

Your guess is correct, it's happening because of bundler. Its a feature of bundler known as Dead code elimination. To know more about it, search for Tree Shaking or Dead code elimination.
If you are not going to use anything from imported module, source code of module will not be included in your build.
I think create-react-app use Webpack for bundling. If you want to disable the feature, starting the app in development mode may solve it. BTW, its good to remove unused code while building.

Related

Is import of CommonJS module a sideEffect or not?

I've written down all files with side effects into package.json
//package.json
"sideEffects": [
"./src/a.js",
"./src/b.js",
]
So, I thought that's all, but then I've got an error produced by our bundle checker:
In the file 'c.js'
ModuleConcatenation bailout: Cannot concat with ./node_modules/json-stable-stringify/index.js: Module is not an ECMAScript module
It tells that we have the import of CommonJS module (there is module.export inside third party package).
So, what should I do? Is import of CommonJS module a side effect or not?
If I would write down c.js into sideEffect it'll help me out?
I cannot just try it out without confidence because we have a long release cycle.

how import works in modules imported into Angular

The documentation I've read is rather hand-wavy about what exactly import does in Javascript, particularly in the Angular framework. I get that it imports modules from another file that has one or more exports. But there are many permutations of its syntax, and not all are discussed with much detail. I'm currently having a very hard time with the #asymmetik/ngx-leaflet-markercluster module. When I try to compile my Angular app, I get a message reading "Can't resolve 'leaflet.markercluster' in 'C:\sca_root\city8\node_modules#asymmetrik\ngx-leaflet-markercluster\dist\leaflet-markercluster" -- this is in reference to a line that reads simply
import 'leaflet.markercluster';
This seems to me (and I know, perhaps I am making too many assumptions here) that there should be a file in that same directory named leaflet.markercluster.js or perhaps leaflet.markercluster.ts (it's Javascript, not TypeScript, so it will be the former). But there is no file named leaflet.markercluster.js in that directory. These are the files in that directory:
leaflet-markercluster.directive.js.map
leaflet-markercluster.directive.metadata.json
leaflet-markercluster.module.d.ts
leaflet-markercluster.module.js
leaflet-markercluster.module.js.map
leaflet-markercluster.module.metadata.json
leaflet-markercluster.directive.d.ts
eaflet-markercluster.directive.js
Which one would that import statement import? If not any of them, where outside this directory would it import that file from? What other information (perhaps in tsconfig.json or angular.json) might affect where this import statement imports from?
the problem in this case was that leaflet.markercluster ALSO needs #types installed. i had to issue a
npm install #types/leaflet #types/leaflet.markercluster
i don't know why the error had nothing to do with #types, but this is in keeping with the soul-crushing nature of Angular development.

How do I properly load the lit-html module in Electron

I'm trying to use lit-html to save my self some time, but I'm having trouble getting everything set up correctly.
Electron 4.1.1
Node 11.15
As of 5 minutes before posting this, I've run npm install and electron-rebuild, no luck.
I use require() as one would with any other NPM package
var render = require('lit-html').render
var html = require('lit-html').html
console.log(require("lit-html"))
Unfortunately, I'm greeted with this error
In reference to the three lines of code above.
I don't see any problems with my code.
I've tried reinstalling lit-html through NPM to no avail. I would really love to use this library, but first I have to get over this hurdle. If I'm being honest, I don't know if this error is reproducible, but nothing I do seems to fix it. The problem seems to lie with node and the way that imports are handled.
Am I missing something here? Is this a common issue? If so, what can I do to fix it?
You need to transpile lit-html before you can require it
I tested require('lit-html') and I was greeted with this error:
/home/chbphone55/Workspace/test/node_modules/lit-html/lit-html.js:31
import { defaultTemplateProcessor } from './lib/default-template-processor.js';
It clearly states that the error is coming from lit-html/lit-html.js:31 where the line uses ES Module import syntax.
You can transpile it using tools like Babel or similar ones. However, you may want to try using ES Module syntax so you can import lit-html without transpiling it.
Example:
<!-- HTML File -->
<script type="module" src="index.js"></script>
// index.js
import { html } from 'lit-html';
What if you can't use type="module"
If you are unable to use the type="module" method above, you can also use the ESM package.
ESM is a brilliantly simple, babel-less, bundle-less ECMAScript module loader.
Here are a few examples of how to use it:
Using the node require flag (-r) to load esm before everything else
node -r esm index.js
Loading esm in your main file then loading the rest of your code.
// Set options as a parameter, environment variable, or rc file.
require = require('esm')(module/*, options*/)
module.exports = require('./main.js')

how can I setup #types for a third party react javascript module so I can access it from typescript and package it with webpack?

There is a 3rd party all-javascript npm scoped package, let's call it #foo, with a module inside called bar. I wanted to use the react component #foo/bar/X from within my typescript .tsx file. I immediately ran into "module not found" when I tried to import X from '#foo/bar/X'. How can I resolve this using #types typescript to resolve the module X and get webpack to run without errors?
My starting point was this introduction on how to how to use react and webpack with typescript: https://www.typescriptlang.org/docs/handbook/react-&-webpack.html. However, said instructions do not tackle the problem that the react component I want to use has no #types. So my first step is I needed to add #types.
There was one important nuance about the scoped module #foo/bar, which was that it broke all its subcomponents into individual sub modules like #foo/bar/X, #foo/bar/Y, and #foo/bar/Z with #foo/bar itself having no functionality. As we will see that was an important nuance which made solving this a bit trickier than if I had not had to import a scoped module into my typescript.
There is a nice blog post here about how to import a vanilla javascript into typescript. Unfortunately there is a shortcoming to the proposed approach. Namely, the idea of adding "index.d.ts' to a src/#typings has two problems:
It works only for running the typescript compiler (tsc) locally. That is to say it will make tsc happy, by allowing tsc to resolve the module from your your local ".d.ts" file, but webpack will still fail with module not found.
The proposed approach didn't teach me how to deal with scoped modules which have different rules for how folders under #types are named
Whatever approach I tried, I knew I needed to have this import statement in my typescript file:
import X from '#foo/bar/X';
ReactDOM.render(
<X/>,
document.getElementById("example")
);
Somewhere, I needed to have the following in a ".d.ts" declarations file like index.d.ts, that much was obvious:
declare module '#foo/bar';
declare module '#foo/bar/X';
But running 'webpack' yielding Module not found: Error: Can't resolve '#foo/bar/X'
I decided to run the typescript compiler (tsc) with this flag to see the possible places where tsc would try to resolve the module.
tsc --traceresolution
Using the traceresolution flag I was able to see something very interesting: that the location under node_modules/#types where tsc searches was quite unexepected (and thereby where webpack searches, since webpack follows the same rules as tsc). It was expecting to find my index.d.ts file under node_modules/#types/foo__bar NOTE THE DOUBLE UNDERSCORE. That's right, for scope packages, you cannot have node_modules/#types/#foo/bar/index.d.ts. Instead you must use a single folder under #types named foo__bar with DOUBLE UNDERSCORE.
With this knowledge I created #types/foo__bar. I copied package.json from the #foo/bar module into #types/foo__bar. I cleaned out all the scripts and other useless stuff that the #types would not need, and stripped it down to this:
{
"name": "#types/#sfoo/bar",
"version": "2.6.0",
"peerDependencies": {
"react": "^16.3",
"react-dom": "^16",
"styled-components": "^3"
},
"dependencies": {
...
},
"engines": {
"node": ">=6"
},
"gitHead": "ac0288aaa47a4f15e56db3a5eff4424fb7905419",
"main": "",
"types": "index"
}
But yet there was one more problem! Even after tsc could resolve my #types, webpack still yielded module not found: can't resolve #foo/bar. So why the heck can't webpack find it? Again, I found a flag that could show me more:
webpack --verbose
But here I had less luck since the webpack rules were just like the typescript rules for module resolution. However from the --verbose output I did notice that one file webpack was interested in was node_modules/#foo/bar/index.js. However, the bar module lacked any index.js since it was really just an empty enclosing module around submodules like #foo/bar/X. Whether this is a bug or a feature of webpack, I don't know, but ADDING AN EMPTY index.js to node_modules/#foo/bar pacified webpack, and my simple example now worked with component X displayed in the browser when I loaded the HTML page.

Using Vue Design System in Nuxt is throwing errors about export in system.js

I am trying to get the components imported into a Nuxt project, following the steps here:
https://github.com/viljamis/vue-design-system/wiki/getting-started#using-design-system-as-an-npm-module
Nuxt does not have a main.js (everything is plugin based), so what I have done is create a "plugin" and then do the import code in there like so (Nuxt recommends this for other libraries too and works fine):
vue-design-system.js
import Vue from 'vue'
import system from 'fp-design-system'
import 'fp-design-system/dist/system/system.css'
Vue.use(system)
Then in my config I do (removed other code in config):
nuxt.config.js
module.exports = {
css: [
{ src: 'fp-design-system/dist/system/system.css', lang: 'css' }
],
plugins: [
{ src: '~plugins/vue-design-system', ssr: true }
]
}
When I run npm run dev in my theme, it builds, but I get a warning:
WARNING Compiled with 1 warnings warning in
./plugins/vue-design-system.js 7:8-14 "export 'default' (imported as
'system') was not found in 'fp-design-system'
Seems to have an issue with the generated system.js regarding the export (the command npm run build:system).
In my page on screen I get the following error when trying to use a component in the design system:
NuxtServerError Cannot find module
'fp-design-system/src/elements/TextStyle' from
'/Users/paranoidandroid/Documents/websites/Nuxt-SSR'
If I hard refresh the page, I then get another message:
NuxtServerError render function or template not defined in component:
anonymous
Any idea what's happening here? It would be really great to get this working somehow.
At this current time, I'm not sure if it's a Nuxt issue or a Vue Design System issue. I think the latter, just because the Nuxt setup I have right now is very bare-bones...so it's not something else causing this.
Thanks.
Repository on GitHub:
Here is the repo for my "theme", but in order to get this going, you will need to create a design system separate from this with the same name and follow the steps to use the design system as a local (file) NPM module.
https://github.com/michaelpumo/Nuxt-SSR
console.log of system (from the JS import statement)
As for your first error (""export 'default' (imported as 'system') was not found in 'fp-design-system'"), the UMD built JS from vue-design-system does not export a "default" object. But you can simply workaround the issue by importing it as:
import * as system from 'fp-design-system'
instead of:
import system from 'fp-design-system'
Then another issue comes quickly as you noticed in your comments: "window is not defined", due again to the UMD built JS that expects window to be globally available, instead of the usual trick to use this (which equals window in a browser). Therefore as it is, the build is not comptible with SSR.
You could however slightly rework the built JS by replacing the first occurrence of window by this, but I am not sure if the result will still work.
Most probably you should better keep this module for client rendering only.
It seems Vue is looking for the ES6 pattern for importing module, which you should use for external javascript modules/files.
in ES6 it is
export default myModule
in ES5 it was
module.exports = myModule
Hope it will help.

Categories

Resources