How does SystemJS support multiple versions of the same library? - systemjs

I'm working on a jspm/systemjs app and would like to better understand how SystemJS handles multiple versions of the same dependency. Consider the following (simplified) SystemJS config example:
map: {
"react": "npm:react#0.14.8"
"npm:foo": {
"react": "npm:react#0.14.7"
}
}
I assumed when my code imported react it would get v0.14.8, while code in the "foo" dependency would receive v0.14.7. However, my browser console prints warnings about multiple copies of react being found.
Questions:
How does SystemJS supports multiple versions of dependencies,
Why would I be receiving an error like this if the different versions are kept separate?

Since SystemJS uses import-maps, you can use this approach:
Using Multiple Versions of the Same Module
It's easy to require multiple versions of the same package with import maps. All you need to do is use a different import specifier in the mapping as shown below:
<script type="importmap">
{
"imports": {
"lodash#3/": "https://unpkg.com/lodash-es#3.10.1/",
"lodash#4/": "https://unpkg.com/lodash-es#4.17.21/"
}
}
</script>
You can also use the same import specifier to refer to different versions of the same package through the use of scopes. This allows you to change the meaning of an import specifier within a given scope.
<script type="importmap">
{
"imports": {
"lodash/": "https://unpkg.com/lodash-es#4.17.21/"
},
"scopes": {
"/static/js": {
"lodash/": "https://unpkg.com/lodash-es#3.10.1/"
}
}
}
</script>
With this mapping, any modules in the /static/js path will use the https://unpkg.com/lodash-es#3.10.1/ URL when referring to the lodash/ specifier in an import statement, while other modules will use https://unpkg.com/lodash-es#4.17.21/.
Source: https://www.honeybadger.io/blog/import-maps/

Related

How do react-css-modules (babel) and css-loader (webpack) work together?

When using webpack and babel together, one needs to configure both in order to use React CSS Modules. For example:
webpack.config.js will need a rule like this:
{
// Translates CSS into CommonJS modules
loader: 'css-loader',
options: {
modules: {
mode: "local",
localIdentName: CSS_CLASS_NAME_PATTERN,
},
sourceMap: true
}
babel.config.js will need a plugin like this:
[
'react-css-modules',
{
generateScopedName: CSS_CLASS_NAME_PATTERN,
filetypes: {
'.scss': {
syntax: 'postcss-scss',
plugins: ['postcss-nested']
}
},
}
]
Why the need to configure CSS Modules in two places? How the two work together? I.e. what happens in what order?
They don't. css-loader does its own thing: class name transformation in CSS, and replacement of CSS imports in JS code by mappings between original and generated names.
babel-plugin-react-css-modules works independently, and it replaces styleName attributes of react components by className with correct generated names. To do so it calculates class name mappings independently from css-loader, that's why it needs separate configuration matching that of css-loader, and that's why after a few years being abandoned by its creators it has compatibility issues with latest css-loader (css-loader changed internal class name generation logic).
Shameless self-promo: I maintain an up-to-date fork of babel-plugin-react-css-modules which solves compatibility issues with latest css-loader versions.

Cannot resolve 'mathjs/number'

I am using Math.js to parse and evaluate a mathematical expression, and am following the example at https://mathjs.org/docs/custom_bundling.html#numbers-only as I only need basic number support. "mathjs": "^8.1.1", is listed in my package.json dependencies.
When I run the example code below, I get Module not found: Error: Can't resolve 'mathjs/number':
// use light-weight, numbers only implementations of functions
import { create, all } from 'mathjs/number'
const math = create(all)
Looks like maybe the documentation hasn't caught up. I was able to get this working by changing the line
import { create, all } from 'mathjs/number';
to
import { create, all } from 'mathjs/lib/esm/number';

Three.js - How to convert import methods into regular js files

On this fiddle https://jsfiddle.net/k2c5upfo/1/, extern modules are called using the import method. I don't use node on my project. I'd like to convert all these import files into regular javascript files. How can I built them without using node.js ?
import * as THREE from "https://cdn.jsdelivr.net/npm/three#0.118.2/build/three.module.js";
import { OrbitControls } from "https://cdn.jsdelivr.net/npm/three#0.118.2/examples/jsm/controls/OrbitControls.js";
import { EffectComposer } from 'https://cdn.jsdelivr.net/npm/three#0.118.2/examples/jsm/postprocessing/EffectComposer.js';
import { ShaderPass } from 'https://cdn.jsdelivr.net/npm/three#0.118.2/examples/jsm/postprocessing/ShaderPass.js';
import { RenderPass } from 'https://cdn.jsdelivr.net/npm/three#0.118.2/examples/jsm/postprocessing/RenderPass.js';
import { ClearPass } from 'https://cdn.jsdelivr.net/npm/three#0.118.2/examples/jsm/postprocessing/ClearPass.js';
import { MaskPass, ClearMaskPass } from 'https://cdn.jsdelivr.net/npm/three#0.118.2/examples/jsm/postprocessing/MaskPass.js';
import { CopyShader } from 'https://cdn.jsdelivr.net/npm/three#0.118.2/examples/jsm/shaders/CopyShader.js';
For example, I was able to call 'OrbitControls.js' on a older version of three.js by simply add another file. Can I still use this method ? Thank you
EDIT :
I managed to convert my workflow using es6 modules. I've been wondering if there's a way to only import specific modules. My generated output file has the same weight with theses two different lines.
import {Scene, PerspectiveCamera, WebGLRenderer, CylinderBufferGeometry, MeshNormalMaterial, Mesh} from "../node_modules/three/build/three.module.js";
import * THREE from "../node_modules/three/build/three.module.js";
Is there a way to only have the part of code that I need in my final output ? Thank you.
Using global scripts is actually deprecate since r117. At the end of the year, using ES6 modules is the only way of importing example files.
I don't use node on my project.
Not sure I understand this sentence. The above fiddle is unrelated to node.js. You can import ES6 modules directly in HTML files as long as you put the import statements into script tags that look like so:
<script type="module">
</script>
This approach is also used by the official examples.

(How) can I use System.import in babel along with require.js?

I'm playing a little bit with the new ES6 functionalities and Babel. I'm successfully using the modules export/import functionalities by means of require.js (transpiling into AMD), but the experimental module loader doesn't want to work. Here is my code and configurations:
extract of front-app/tst.js
import {tstimp as functocall} from "front-app/tstimp.js";
...
/**
* LOADING MODULES DYNAMICALLY
*/
System.import('front-app/tst_dyn_mod')
.then(some_module => {
console('using the module!');
some_module.sayHello();
})
.catch(error => {
console.log('error!');
console.log(error);
});
My .babelrc looks like this:
{
"presets": ["es2015", "react"],
"plugins": ["transform-es2015-modules-amd"]
}
and the scripts I import are these ones, in that order:
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="node_modules/babel-polyfill/dist/polyfill.min.js"></script>
<script data-main="front-app/tst" src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.1.8/require.min.js"></script>
<script src="node_modules/es6-module-loader/dist/es6-module-loader.js"></script>
</head>
<body></body>
</html>
Unfortunately what I get is the following error by using firefox:
error! tst.js:695:9
Error: a is undefined
Error loading http://localhost/es6r1/front-app/tst_dyn_mod
what's that a? Am I missing something? Keep in mind my code is transpiled into AMD, but System is supposed to stay there in the transpiled code (and it IS there). The polyfill should do the dirty job, right?
I successfully get the thing working on Babel 6 with a slighlty different config (babel-node cli & thus commonjs) thanks to this plugin: https://www.npmjs.com/package/babel-plugin-system-import (npm install babel-plugin-system-import-transformer). Here's a excerpt of my .babelrc:
…
"plugins": [
"system-import-transformer",
{
"modules": "common"
}
]
…
Setting amd instead of common like indicated in the documentation should do the trick for you.
Only limitation of this tiny plugin, you should get a plain string module name like System.import("plainString") and not a computed one (nor string concatenation with +, nor new ES6 template literal and neither variable name). It seemds linked to that line of code.
I will try to PR a fix on that limitation if I can.
Just an update on this, https://github.com/thgreasi/babel-plugin-system-import-transformer has support for non string parameters. Just make sure not to use the updated alias package.

Import .jsx files with SystemJS

Since the JSX plugin is deprecated I've been struggling to have Babel handle my jsx files. I finally managed to convince SystemJS to load my app with:
System.import('scripts/app.jsx!babel')
But this doesn't import any imported jsx files like:
import Login from './components/Login' // File is Login.jsx
With the old plugin this worked but now I am not sure how to get it working now.
One step in the right direction would be adding this to your config:
"packages": {
"components": { // Packages could of course be replaced with what you want
// to affect. Even "." is valid.
meta: {
'*.jsx': {
loader: 'babel'
}
}
}
}
This allows you to load files as such: import .. from './components/Login.jsx'.
You could take this one step further by adding "defaultExtension": "jsx" under "components". I'd only use this if the folder/modules was jsx-only though. That would allow you to import as import .. from './components/Login' as you wanted to.