Node 14 ECMAScript Modules and Packages - javascript

In the documentation for Node's native support of ECMAScript modules, they state
There are three types of specifiers:
...
Bare specifiers like 'some-package' or 'some-package/shuffle'. They can refer to the main entry point of a package by the package name, or a specific feature module within a package prefixed by the package name as per the examples respectively. Including the file extension is only necessary for packages without an "exports" field.
...
The definition of bare specifiers indicate you can import ECMAScript Modules from "packages".
What's considered a "package" in Node.js? Does node just search the entire node_modules folder for any folder with a package.json file and consider that a package? Or is it more complicated than that? Is it the same for CommonJS modules and ESMAScript modules?

What's considered a "package" in Node.js?
In the sense of "bare" named packages for ESM, any name that matches a node builtin package, or is in node_modules/{{ bare name }} in the current directory or subsequently any parent directory, that has a package.json that loads and also has the correct name field.
Does node just search the entire node_modules folder for any folder with a package.json file and consider that a package? Or is it more complicated than that?
Basically, node doesn't care about anything you haven't named as an import though. It's always more complicated, the algorithm is documented further down on the ESM modules page.
Is it the same for CommonJS modules and ESMAScript modules?
No, although the part that resolves the package exports is shared once a "package" is located and vetted by the ESM or CommonJS rules). Big differences are
Global node_modules are not considered in ESM (i.e. traversing $NODE_PATH, $HOME and the node $PREFIX).
Outside of the process for loading this subset or "bare names" there are more differences.
ESM modules or mjs files will not load via require.
No native or JSON imports
Generally the ESM loader is a much more strict subset of CommonJS as anything Node specific doesn't apply.

Related

How to allow both import ES5 and ES6 to my npm package?

I am new to npmjs package publishing and recently published my 1st package successfully. But since it's written in ES5 style. It is accessed only via "require"
const smiley = require('smiley-id');
What should be done to make it accessible via import statement also? like this
import smiley from 'smiley-id';
or/and
import { smileyId } from 'smiley-id';
require is part of the CommonJS module system (also known as cjs modules) whereas import and export are used in es2015 module system (also known as JavaScript modules, es6 modules or esm modules).
You can use import for both cjs and esm modules, but you cannot use require for esm modules. This means that in an esm module (or source file which you will compile with Typescript tsc or Babel or bundle with Webpack) you can already import your package, as it is, with import. However, you cannot use named imports. Instead you have to destructure the imported item afterwards.
If you want to distribute it as an esm package, this is also possible and it comes with some advantages, out of which the most important is related to tree-shaking. To distribute it as an esm module, you tell your build tool to output esm modules. This is different for different tools. Fortsc for example you specify --module es6 (there are multiple targets that output esm modules). For Webpack, Babel and Rollup, the procedure is different.

Why do some npm packages have an es directory?

I've noticed some libraries have duplicated code in an es folder. Why do developers do that?
examples:
Developers can ship their packages in a few different flavours, depending on how the user (e.g. you) wants to use them.
If you want to use the module code (i.e. import), or you want to use es2015 (i.e. the require), or you even might want to use it in a browser environment (standalone - think .min file).
The folders names are meaningless, developers can call them whatever they want, but they'll probably put their non-transpiled (i.e. import) modules in an es or esm directory
Keep in mind that just because you are "import"-ing their module doesn't mean that their module uses imports. Most nowadays are still transpiled before being shipped so the code you're import-ing is probably require-ing stuff.
It's kind of complicated...

How to transpile node_module dependencies coming as ES module?

A dependency package that I'm using only has module and main as entry points. By default, the webpack version I'm using resolves to module entry point first. If I set resolve.mainFields to main in webpack configuration, I might affect other dependencies that were being resolved by 'browser' entry point by default.
So, the question is, how can I transpile a dependency like that and have it bundled in my bundle in ES5?
Some package writers distributes their ES6 sources, some other don't.
If you can figure out what "modern JavaScript syntax" the dependency is using, In general, the standard format for distributing a module is called CJS or Common JS.
If the author of the package you are trying to use did not export CJS from it's bundle (in 99% they do), then you don't need to transpile anything.
What do you mean by "the other ones"?
Be sure to import the right sources.

are dist folders always available in node modules

i am building a tool of my own to trans compile and pack the related js files (that are written in ES6) into a bundle. so far it goes as expected with local files, but when i come to public modules, for example, react and redux etc, it's different. and i am wondering how to include these modules into the bundle? i found that there are always dist folders in most of the public modules with distributed versions residing in. so, are the dist folders always available in any module directory?
Webpack uses the same module resolution as Node.js. node_modules have a package.json which has a main field, that determines which file is being imported when you import the module in your code. Additionally webpack looks for the browser or module fields in package.json and prefers them over main, if they are present. This makes it easy to publish a build that is different from the regular Node.js build (for instance to use ES modules (import/export), which are not supported by yet Node.js but by bundlers like webpack). This behaviour can be configured with the option resolve.mainFields. For an example have a look at the package.json of Redux.
None of these fields are mandatory, but at least main is supposed to be present, so you can simply import a module with:
import module from 'module';
Or with require:
const module = require('module');
Webpack automatically includes the modules you import into the bundle.
The dist directory is not any special, but it's very common to have a dist directory that contains an UMD build. Especially as Unpkg allows you to import a node module without having to publish it manually to a CDN, it uses the dist or umd by default (as described at the bottom of the homepage).

Get list of all declared module imports (SystemJS)

I'm using jspm and SystemJS to import ES2015 modules.
Is it possible to get a list of all of the imported modules in a project through the System object, or anywhere else? I can access my project-specific modules through System._loader.moduleRecords, but the modules that I've installed through jspm (e.g., d3, jquery) do not appear in this list.
System._loader.modules contains a list of all modules, but unfortunately also includes a list of modules required to transpile my code and modules-loading packages.
System._loader.moduleRecords (project modules)
System._loader.modules (project modules, libraries, transpile packages)
I want only a list of declared imports, specifically, those that I've imported using import x from 'x'. This should include both project modules and libraries, but not babel/module-loading related modules. I'd like the solution to not involve filtering using regular expressions.
SystemJS Debugger is probably what you are looking for.

Categories