Loading multi versioned Web Component dependencies with SystemJS / JSPM? - ecmascript-6

Just a disclaimer - this is a hypothetical scenario as I'm trying to figure out the best way to handle web component direct and transitive dependencies using something like JSPM and SystemJS.
Scenario 1
Suppose I have 2 web components - component-a and component-b. One is built with momentjs#1.2.4 and another is built with momentjs#1.6.4.
Each component author listed Polymer as a peer dependency and momentjs as a direct dependency in their package.json file like this:
"dependencies": {
"moment.js": ">= 1.0.1 <= 1.8.0"
}
"peerDependencies": {
"Polymer.js": "0.5.0^",
}
So in this case when the developer declares the package.json dependencies on both of these components the package manager could figure out the best version of moment.js to install and make it available. Lets assume that's version 1.8.0.
Great - just one version of moment.js to deal with for both components. Now how do the components load / inject the dependency? Does JSPM and SystemJS currently have the ability to support this?
For example in this article Taming Polymer with SystemJS and Typescript the author performs imports that delegate to SystemJS like this:
import 'elements/app-frontend';
So I suppose momentjs would follow a similar approach and import like this (Within the typescript files of component-a and component-b:
import 'js/momentjs';
In the above case the momentjs dependency does not know what version will be fetched since the import declaration is unaware of the version (since it written at design time).
Later on when the component is used in and component is used in an application JSPM figures out the best for the momentjs depedency to install.
In this case lets assume that it install it in a layout like this:
`jspm_packages/momentjs/momentjs#1.8.0`
So how does JSPM become aware the import statement import 'js/momentjs' translates to the import of jspm_packages/momentjs/momentjs#1.8.0?
In this case it's fairly trivial, but it get more tricky in scenario 2 below ... I think.
Scenario 2
Same as scenario one except the version requirements on momentjs are mutually exclusive. For example component-a requires version 1.2.4 and component-b requires version 2.4.4.
So both components perform the import like this:
import 'js/momentjs';
But jspm_packages installs both versions like this:
`jspm_packages/momentjs/momentjs#1.4.4`
`jspm_packages/momentjs/momentjs#2.4.4`
So now how does SystemJS know to that component-a needs version 1.4.4 and component-b needs version 2.4.4?
To summarize:
1) Is JSPM ok with having multiple versions of the same dependency?
According to this article Introduction to the Jspm package manager and the SystemJs module loader and an answer from guybeford it does, but how then does each component get the right version?
2) Does SystemJS / JSPM have a way of configuring version meta data for each component?
3) Does SystemJS have a way of understanding and injecting the right version of a dependency into the web component?
TIA,
Ole

Also what happens if the component requires several other modules, like CSS etc?
I'm not sure what you mean. You can import multiple modules and other contents (likely via plugins) usting SystemJS.
1) Is JSPM ok with having multiple versions of the same dependency?
I think you partially answered this question. JSPM will always select and install only one version for each component, which best matches the requirements from other components. If there is a conflict, JSPM will ask to choose manually.
So how does JSPM become aware the import statement import 'js/momentjs' translates to the import of jspm_packages/momentjs/momentjs#1.8.0?
The config.js file assigns import names (not sure the nomenclature) for installed packages. It is actually possible to change those names using jspm install x=npm:package syntax. This will result in config like
"x": "npm:package#1.2.3"
You can then both import x and import npm:package#1.2.3.
2) Does SystemJS / JSPM have a way of configuring version meta data for each component?
I think that the structure of config.js has all you need. It contains dependencies with their versions.
"component-a": {
"momentjs": "momentjs#1.2.4"
},
"component-b": {
"momentjs": "momentjs#1.6.4"
}
3) Does SystemJS have a way of understanding and injecting the right version of a dependency into the web component?
I would expect SystemJS to use this information to import the correct version when either component-a or component-b is requested.
I am not sure though how to have JSPM install packages this way, retaining multiple versions of a package. I think it deserves a separate, specialized question.

Related

PhpStorm can't resolve package import even though it exists (react-router-dom)

I am using React (16.12.0) with PhpStorm (2019.3.1).
The package I am importing is react-router-dom (5.1.2)
I will write the following import:
import { NavLink, Switch } from "react-router-dom";
Both packages are imported properly by webpack/babel and the page renders properly when I use both Switch and NavLink.
However purely from the IDE-perspective, I get a warning about Switch: Cannot resolve symbol 'Switch'
It is weird because it is obviously there, and I checked in /node_modules/react-router-dom and Switch.js is there.
Strangely on previous versions of PhpStorm (before several updates and plugin imports and other changes), the Switch import was accurately found by PhpStorm.
I have not updated react-router-dom recently and am using its latest stable build.
Any ideas as to why it might be missing Switch?
EDIT:
I have realized it may be because PhpStorm is having difficulty importing a commonJS module.
I tried changing the Javascript compilation method from React JSX to ECMA 6. That didn't work.
I also tried importing some build libraries for react-router-dom, and that worked for recognizing the Route import strangely but not Switch.
I also tried invalidating the PhpStorm cache and restarting the app, but that didn't work either.
EDIT 2:
Per an answer, I have tried to use option+enter (alt+enter on windows) while my cursor is within react-router-dom to try to install a typescript package, but I don't get the same suggested action as shown on the JetBrains website: https://www.jetbrains.com/help/webstorm/configuring-javascript-libraries.html
All it suggests is switching single quotes to double quotes.
Also I have already installed #types/react-router-dom. Perhaps there is another one that I am missing?
EDIT 3:
The correct answer was to install #types/react-router. For some reason #types/react-router-dom is deceivingly NOT what solves this.
node_modules/react-router-dom/esm/react-router-dom.js exports BrowserRouter, HashRouter, Link and NavLink, but doesn't export Switch, it's not explicitly defined there.
For better completion/type hinting, you can install Typescript stubs for the package: put cursor on "react-router-dom" in import statement, hit Alt+Enter, choose Install TypeScript definitions for better type information::
See https://www.jetbrains.com/help/webstorm/2019.3/configuring-javascript-libraries.html#ws_jsconfigure_libraries_ts_definition_files

Load ordinary JavaScripts in Polymer 3 elements or lit-elements

I am in the middle of converting a Polymer 2 app to Polymer 3. Modulizer did not work for me so I converted it manually. Thanks to the great upgrade guide it has been mostly straight forward so far.
One task is left though:
in my Polymer 2 app I had a special html import (d3-import.html) that brought in the d3.js lib version 3 which comes as a plain JavaScript file (no ES6 module!). This import was dynamically loaded in only two out of overall 20 pages because the other 18 pages did not need it.
In Polymer 3 I can not import it as an ES6 module because it is not a module. Loading it in my main start.html would mean it gets loaded even if the user only uses the other 18 pages that don't need it.
I tried writing script-tags in my web component templates but that doesn't seem to work. Unfortunately I don't see any error in the browser tools. The template simply stops to load at the line of the script-tags.
Any idea how to do this?
Additional question:
since I start using lit-element in the same application. How to solve the same problem with lit-element?
Edit: note that I currently don't use any build steps/tools except for polymer-build to replace the module paths with actual file paths.
Note that this challenge has nothing to do with Polymer or LitElement, this is only an issue with how to load non-module resources from a module.
The most straightforward way that I know of is to use a bundler like Rollup that can support CommonJS or UMD. Rollup has the commonjs plugin for this: https://github.com/rollup/plugins/tree/master/packages/commonjs
The other option is to upgrade to D3 5.x, which appears to be published as standard modules itself. Given the number of files involved, you'll still likely want a bundler to reduce network roundtrips.

How to determine ES version of a module

The Testing React Native Apps section of jest docs says,
The transformIgnorePatterns option can be used to whitelist or blacklist files from being transformed with Babel. Many react-native npm modules unfortunately don't pre-compile their source code before publishing.
Lets say I add a new npm module(say X) to my project.
Now, I would now like to know if this(X) module is precompiled to maybe ES5 version, or is it still exported as a es6/es7/typescript module.
This will help me to figure out if I need to whitelist/blacklist the(X) module from being transformed.
Is there a way to find out the ES version of a exported module?
I think, this NPM package is exactly what you need :)

Import Polymer 2 components in Polymer 3

I am developing a web component using Polymer v3, and need to include some custom elements defined in legacy Polymer 2 components in the template HTML of my new component.
Since HTML imports are no longer supported in Polymer 3, what approach should I take to include them? If I was using Polymer 2 I could just add the following in my component's HTML file:
<link rel="import" href="../my-legacy-component.html">
I have tried adding the above link into the template HTML of my component, but it appears that doesn't work. I have also tried various import commands to reference the JS files inside the legacy component directly, but received various inscrutable JS errors so I'm not sure if that is the correct way to go either.
I can't believe there isn't a simple way to do this - would the Polymer team really introduce a new version of the library that is completely incompatible with all the components created using older versions?
Did you try to use polymer-modulizer?
Modulizer performs many different upgrade tasks, like:
Detects which .html files are used as HTML Imports and moves them to .js
Rewrites in HTML to import in JS.
Removes "module wrappers" - IIFEs that scopes your code.
Converts bower.json to package.json, using the corresponding packages on npm.
Converts "namespace references" to the proper JS module import, ie: Polymer.Async.timeOut to timeOut as imported from #polymer/polymer/lib/util/async.
Creates exports for values assigned to namespace referencs. ie, Foo.bar = {...} becomes export const bar = {...}
Rewrites namespace objects - an object with many members intended to be used as a module-like object, to JS modules.
Moves Polymer element templates from HTML into a JS template string.
Removes s if they only contained a template.
Moves other generic HTML in the document into a JS string and creates it when the module runs.
more on github
I have ran into the same problem with the module js-yaml earlier. I don't have enough reputation for a comment yet so I just write it down here.
Run this sudo npm install -g js-yaml -> This will install the missing package for the tool
Then at the root of your project, run modulizer --import-style name --out . -> This will convert your component from Polymer 2 to Polymer 3. The option --import-style name tells the tool to use package name instead of path. --out will make the tool writes those files to the directory.
After that, if no error prompts. Try to serve it with polymer serve --module-resolution=node -> Since we are using node modules now, we have to provide the --module-resolution=node option.

Sharing components across multiple Aurelia projects

we started our project with ES6 javascript skeleton.
we would like to extract some styles and custom attributes to a common folder so we can use these at any Aurelia module we will build in the future.
the problem is with the bundle files. we don't know how to config them to bundle external folder out of the main ES6 folder.
can you please tell us what to do?
It sounds like you want to build an Aurelia plugin that you can import into any project. I would start by taking a look at the Aurelia plugin skeleton.
Once you've built your plugin with the custom styles and attributes you want, you'll want to either register it with jspm as a link, or publically through a registry such as npm or github.
Once published, you will be able to jspm install registry:my-package in any new project, and add the following line to your main.js file:
export function configure(aurelia) {
aurelia.use
.standardConfiguration()
.plugin('my-package');
}
For more information on this last step, see the brilliant answer provided by Ashley Grant here.