How to create jspm bundle with fabric.js v2 - javascript

I'm trying to create a javascript bundle with JSPM that includes fabric.js version 2. Alas, performing jspm bundle fails.
$ jspm bundle src/main --minify --inject
Building the bundle tree for src/main...
err Error on fetch for #empty/lib/jsdom/living/generated/utils.js at file:///Users/dkoerner/projects/JSPMFabricTestCase/#empty/lib/jsdom/living/generated/utils.js
Loading npm:fabric#2.0.0-rc.4/dist/fabric.js
Loading npm:fabric#2.0.0-rc.4.js
Loading src/bootstrap.js
Loading src/main.js
Error: ENOENT: no such file or directory, open '/Users/dkoerner/projects/JSPMFabricTestCase/#empty/lib/jsdom/living/generated/utils.js'
I have created a test project (https://github.com/dkoerner85/JSPMFabricTestCase) following the jspm and fabric examples and verified that bundle creation works with the current fabric stable release v1.7.22. Raising the version to the release candidate v2.0.0-rc4 however breaks bundling.
I am fairly new to javascript app development and hence do not understand the reason why this fails and how to repair it. I am grateful for any pointers or explanation.
npm: v5.5.1
node: v8.9.3

Solution
Add an override section to your project's package.json:
"overrides": {
"npm:fabric#2.0.0-rc.4": {
"map": {
"canvas": "#empty",
"fs": "#empty",
"jsdom/lib/jsdom/living/generated/utils": "#empty",
"jsdom/lib/jsdom/utils": "#empty",
"jsdom": "#empty",
"http": "#empty",
"https": "#empty",
"xmldom": "#empty",
"url": "#empty"
}
}
}
Insight
After reading about jspm and systemJS, I have found a solution to this problem. The critical code is in the fabric.js and package.json files of the fabric package.
dist/fabric.js
...
fabric.document = require('jsdom').jsdom(decodeURIComponent('%3C!DOCTYPE%20html%3E%3Chtml%3E%3Chead%3E%3C%2Fhead%3E%3Cbody%3E%3C%2Fbody%3E%3C%2Fhtml%3E'),{ features: {FetchExternalResources: ['img']}});
fabric.jsdomImplForWrapper = require('jsdom/lib/jsdom/living/generated/utils').implForWrapper;
fabric.nodeCanvas = require('jsdom/lib/jsdom/utils').Canvas;
fabric.window = fabric.document.defaultView;
DOMParser = require('xmldom').DOMParser;
...
package.json
...
"browser" : {
"canvas": false,
"fs": false,
"jsdom": false,
"jsdom/lib/jsdom/living/generated/utils": false,
"jsdom/lib/jsdom/utils": false,
"http": false,
"https": false,
"xmldom": false,
"url": false
},
...
Now, when installing the package with jspm install npm:fabric#2.0.0-rc.4, jspm will modify the require statements, resulting in the following code:
jspm_packages/npm/fabric#2.0.0-rc.4/dist/fabric.js
...
fabric.document = require('#empty').jsdom(decodeURIComponent('%3C!DOCTYPE%20html%3E%3Chtml%3E%3Chead%3E%3C%2Fhead%3E%3Cbody%3E%3C%2Fbody%3E%3C%2Fhtml%3E'), {features: {FetchExternalResources: ['img']}});
fabric.jsdomImplForWrapper = require('#empty/lib/jsdom/living/generated/utils').implForWrapper;
fabric.nodeCanvas = require('#empty/lib/jsdom/utils').Canvas;
fabric.window = fabric.document.defaultView;
DOMParser = require('#empty').DOMParser;
...
When trying to create a bundle, jspm will stumble over the '#empty/lib/jsdom/living/generated/utils' require statement.
Error: ENOENT: no such file or directory, open '/Users/dkoerner/projects/JSPMFabricTestCase/#empty/lib/jsdom/living/generated/utils.js'
jspm treated the jsdom entries first as required by the package.json, thereby failing to recognize the more detailled entry 'jsdom/lib/jsdom/living/generated/utils'. It is necessary to provide the map in the correct order - long paths before short paths.
This can be achieved with a local override as depicted in the solution above. Please note that jsdom is ordered below #empty/lib/jsdom/living/generated/utils in the override, as opposed to the original package.json.

Related

Importing leaflet into module from cdn with typescript support

I'm trying to import leaflet into a javascript module with typescript support but can't get it to work.
I've installed #types/leaflet and have tried to following:
import 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.8.0/leaflet.js'
This works fine in the browser but typescript gives the following error:
'L' refers to a UMD global, but the current file is a module. Consider adding an import instead.ts(2686)
(I call L.map() in my code)
I've also tried:
import * as L from 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.8.0/leaflet.js'
This works fine in typescript, but gives the following error in chrome:
Uncaught TypeError: L.map is not a function
Anyone know how to fix this?
I don't want to use a bundler.
Edit
As jsejcksn suggested in the comments, importing a ES module version of leaflet and adding a path alias to typescript fixed this.
I'm now using the following import:
import * as L from 'https://unpkg.com/leaflet#1.8.0/dist/leaflet-src.esm.js'
The problem with this though, as stated on Leaflet's Download page, is that this imports Leaflet's source files including unit tests, files for debugging, build scripts, etc. This doesn't seem very efficient to me.
Any other ideas?
Edit 2
It seems like Leaflet 1.9 will solve my problem when it will be released. This pull request adds an ES module entrypoint to Leaflet meaning you can do this:
import L from 'leaflet'
or in our case:
import L from 'cdn.com/link/to/leaflet/1.9/esm'
In the mean time, jsejcksn's answer provides a good alternative so I will accept that answer.
Preface
I've already written an answer about how to do this with ES modules at the question How to include modules in client side in typescript projects?. Be sure to read through that question and answer for context here.
You said:
I don't want to use a bundler.
and also:
The problem with this though, as stated on Leaflet's Download page, is that this imports Leaflet's source files including unit tests, files for debugging, build scripts, etc. This doesn't seem very efficient to me.
These ideas seem at odds to me. Without more information, I can only assume that by "not very efficient" you mean that the byte size of the presumably not-tree-shaken ES module is larger than the UMD module. If that's the case, why not just import Leaflet into your bundle and tree-shake away all of the parts that you don't use during build, resulting in even fewer bytes than the UMD?
Ignoring that for now, and responding purely to your stated criteria, below I will provide a self-contained, reproducible example demonstrating how you can use a CDN-hosted UMD module that has a corresponding #types package in a TypeScript ES module without augmenting the definition of globalThis.window in the compiler and without bundling.
Note that some of what I show will be purely ceremonial, but the technique will allow for you to later swap out the shim module URL in the import map with a CDN ES module URL if/when you find one that suits you.
Download the files in this example
I'm going to provide a complete reproducible example here, including all the content of each file in a code block. If you want to use this example to reproduce it and don't want to copy + paste every individual code block into a new file with a matching name on your device, then you can copy + paste the following script and run it in your browser console to download the repo as a zip archive file:
function download (data, fileName, mediaType) {
const a = document.createElement('a');
const blob = new Blob([data], mediaType ? {type: mediaType} : undefined);
const url = URL.createObjectURL(blob);
a.href = url;
a.download = fileName;
a.click();
a.remove();
// URL.revokeObjectURL(url);
}
download(
new Uint8Array([80,75,3,4,20,0,0,0,8,0,173,178,247,84,63,79,13,255,254,0,0,0,4,24,0,0,9,0,28,0,46,68,83,95,83,116,111,114,101,85,84,9,0,3,182,186,220,98,78,194,220,98,117,120,11,0,1,4,245,1,0,0,4,20,0,0,0,237,152,49,78,196,48,16,69,255,132,72,88,162,113,73,233,43,236,13,204,42,112,129,109,232,64,75,34,40,178,202,22,32,218,92,135,123,112,27,46,65,140,255,66,68,130,4,5,218,21,252,39,89,47,146,199,246,164,177,61,6,96,203,135,122,1,120,0,14,217,150,62,102,112,108,19,10,186,76,131,223,230,104,112,143,166,189,93,172,110,186,249,185,14,142,148,251,49,174,80,15,185,183,227,252,55,219,81,212,167,136,77,87,85,235,182,91,15,61,238,165,121,186,124,188,120,62,155,139,170,191,17,181,189,155,172,38,132,16,66,252,38,150,229,78,246,155,134,16,226,0,73,251,67,160,35,221,103,27,251,11,186,28,141,241,116,160,35,221,103,27,227,10,186,164,29,237,233,64,71,186,207,230,166,101,44,62,140,43,239,138,23,243,116,160,227,143,126,89,136,127,195,81,150,79,231,255,249,215,245,191,16,226,15,99,101,181,170,150,120,47,8,38,164,179,54,12,237,122,55,0,60,205,49,189,4,20,249,177,240,20,31,253,129,142,116,159,173,139,128,16,251,226,21,80,75,3,4,10,0,0,0,0,0,135,182,247,84,0,0,0,0,0,0,0,0,0,0,0,0,18,0,28,0,46,109,101,116,97,95,105,103,110,111,114,101,95,116,104,105,115,47,85,84,9,0,3,237,193,220,98,62,194,220,98,117,120,11,0,1,4,245,1,0,0,4,20,0,0,0,80,75,3,4,20,0,0,0,8,0,210,178,247,84,4,129,138,94,194,1,0,0,133,3,0,0,50,0,28,0,46,109,101,116,97,95,105,103,110,111,114,101,95,116,104,105,115,47,99,114,101,97,116,101,45,105,110,108,105,110,101,45,97,114,99,104,105,118,101,45,115,99,114,105,112,116,46,109,106,115,85,84,9,0,3,252,186,220,98,252,186,220,98,117,120,11,0,1,4,245,1,0,0,4,20,0,0,0,117,146,77,139,219,48,16,134,239,254,21,115,88,176,93,130,12,61,149,132,165,236,210,221,195,210,238,150,182,123,10,129,40,210,56,81,99,75,70,146,29,82,227,255,222,81,28,39,74,161,167,120,70,207,188,239,124,68,213,141,177,30,62,0,119,208,112,191,131,210,154,26,82,109,36,206,67,156,46,18,53,34,189,217,7,136,59,135,214,15,49,55,166,10,231,173,18,62,42,176,200,229,179,170,240,6,46,93,209,80,164,28,58,66,19,97,180,243,176,44,9,251,78,118,43,184,7,122,22,232,28,227,118,219,49,87,41,129,217,199,124,145,140,46,217,68,206,32,125,176,98,167,58,132,144,26,155,167,146,182,70,237,65,27,31,116,58,37,81,166,249,197,231,143,106,190,112,207,31,219,146,124,248,129,43,15,83,147,23,97,162,111,224,23,103,52,209,107,141,7,120,87,218,127,122,176,150,31,179,187,254,229,231,219,43,11,67,235,173,42,143,217,146,49,118,213,95,229,67,190,142,148,130,197,43,175,241,172,246,79,109,104,158,109,184,67,77,200,181,147,184,149,111,40,21,255,117,108,254,163,144,242,166,161,85,113,175,140,46,8,143,134,118,194,170,198,135,17,202,86,139,0,128,52,7,93,25,46,33,147,212,238,236,180,193,208,221,12,234,201,38,135,62,1,24,21,56,21,75,35,78,171,101,130,54,230,241,169,194,16,145,111,112,154,192,77,101,54,196,134,85,61,210,103,182,12,242,171,72,21,62,67,239,233,119,126,77,13,48,135,86,75,44,149,70,25,105,181,182,34,169,247,31,95,207,142,111,155,223,40,60,197,89,112,57,129,156,237,44,134,83,18,59,198,151,193,238,47,51,141,15,130,150,179,207,206,85,22,107,211,225,24,21,197,201,195,98,103,246,145,7,41,210,243,144,36,147,98,70,236,93,31,253,39,134,217,148,137,111,123,205,222,28,140,210,249,98,125,62,137,169,144,85,102,155,141,135,33,155,191,80,75,3,4,20,0,0,0,8,0,128,182,247,84,134,228,110,93,72,0,0,0,86,0,0,0,28,0,28,0,46,109,101,116,97,95,105,103,110,111,114,101,95,116,104,105,115,47,82,69,65,68,77,69,46,116,120,116,85,84,9,0,3,224,193,220,98,224,193,220,98,117,120,11,0,1,4,245,1,0,0,4,20,0,0,0,29,202,209,9,128,48,12,5,192,127,167,120,67,185,64,104,35,13,132,68,146,104,209,233,45,194,125,222,62,36,177,184,233,131,43,185,163,28,45,152,138,81,131,33,166,98,140,87,78,80,180,33,55,227,16,101,116,159,166,78,29,217,66,206,90,239,239,100,57,57,182,15,80,75,3,4,20,0,0,0,8,0,39,179,247,84,89,60,189,69,88,0,0,0,119,0,0,0,21,0,28,0,46,109,101,116,97,95,105,103,110,111,114,101,95,116,104,105,115,47,114,117,110,85,84,9,0,3,153,187,220,98,153,187,220,98,117,120,11,0,1,4,245,1,0,0,4,20,0,0,0,101,204,59,14,128,32,12,0,208,221,83,112,129,22,127,137,241,52,132,96,35,53,242,177,16,7,78,175,179,174,111,120,141,179,130,75,84,73,176,76,253,58,244,243,136,237,53,236,98,218,72,161,198,64,213,26,222,99,18,50,213,115,209,78,200,86,2,142,39,71,2,43,206,243,77,80,156,112,174,24,142,242,189,58,9,63,122,0,80,75,3,4,10,0,0,0,0,0,198,181,247,84,0,0,0,0,0,0,0,0,0,0,0,0,7,0,28,0,112,117,98,108,105,99,47,85,84,9,0,3,131,192,220,98,140,192,220,98,117,120,11,0,1,4,245,1,0,0,4,20,0,0,0,80,75,3,4,20,0,0,0,8,0,138,181,247,84,83,75,4,148,85,1,0,0,77,2,0,0,17,0,28,0,112,117,98,108,105,99,47,105,110,100,101,120,46,104,116,109,108,85,84,9,0,3,19,192,220,98,19,192,220,98,117,120,11,0,1,4,245,1,0,0,4,20,0,0,0,125,82,77,79,195,48,12,189,239,87,152,156,23,186,1,18,48,181,61,1,55,196,97,112,70,33,117,137,33,31,85,226,110,154,16,255,157,38,29,236,198,37,78,252,158,253,252,145,250,172,11,154,15,3,130,97,103,219,69,157,13,88,229,223,27,129,94,180,139,201,131,170,107,23,0,181,67,86,160,141,138,9,185,17,35,247,242,70,64,117,130,188,114,216,136,29,225,126,8,145,5,232,224,25,253,68,221,83,199,166,233,112,71,26,101,121,44,129,60,49,41,43,147,86,22,155,117,73,148,51,49,177,197,118,203,74,127,194,211,14,99,111,195,30,174,47,87,183,235,213,213,69,93,205,112,97,158,73,9,15,20,19,47,161,195,158,60,130,242,64,46,107,131,83,3,244,33,2,27,4,97,81,245,22,167,130,92,232,70,139,144,6,212,212,19,198,41,203,222,144,54,48,4,242,156,128,67,9,120,121,188,147,28,228,253,246,17,146,33,119,12,219,128,148,165,215,164,35,13,12,121,104,141,152,245,38,57,145,49,128,175,114,2,28,129,36,54,127,46,56,85,178,1,81,29,239,175,163,235,94,179,204,249,71,18,71,230,119,177,223,249,200,130,213,172,120,234,250,217,160,95,254,182,154,43,158,166,28,15,165,137,127,139,157,49,1,41,234,70,84,78,145,207,162,237,73,160,174,230,101,47,234,183,208,29,74,2,179,110,183,136,121,151,41,76,179,11,35,15,35,79,188,117,102,207,172,28,86,62,207,15,80,75,3,4,20,0,0,0,8,0,72,187,247,84,206,95,24,128,103,0,0,0,156,0,0,0,14,0,28,0,112,117,98,108,105,99,47,109,97,105,110,46,106,115,85,84,9,0,3,232,201,220,98,234,201,220,98,117,120,11,0,1,4,245,1,0,0,4,20,0,0,0,203,204,45,200,47,42,81,240,81,72,43,202,207,85,80,207,73,77,76,203,73,45,81,183,230,74,206,207,43,46,81,72,84,176,85,240,209,203,73,44,241,201,75,215,48,53,208,51,213,81,48,6,146,154,48,249,36,20,121,67,12,249,148,204,226,146,196,188,228,84,160,178,68,61,24,39,36,95,35,9,170,34,63,39,85,47,39,63,93,163,90,33,81,71,33,73,7,161,190,22,168,0,0,80,75,3,4,20,0,0,0,8,0,72,187,247,84,107,96,82,240,103,0,0,0,114,0,0,0,26,0,28,0,112,117,98,108,105,99,47,108,101,97,102,108,101,116,95,117,109,100,95,115,104,105,109,46,106,115,85,84,9,0,3,232,201,220,98,234,201,220,98,117,120,11,0,1,4,245,1,0,0,4,20,0,0,0,53,204,49,10,195,48,12,0,192,221,175,208,150,77,106,183,82,211,31,228,19,174,45,83,27,197,10,145,66,2,165,127,47,20,58,222,114,109,89,117,115,152,94,238,171,221,137,114,25,221,48,139,238,165,74,218,24,179,46,148,122,58,73,218,211,72,56,85,97,167,43,222,240,242,23,118,155,98,200,58,204,225,13,51,124,224,1,71,27,69,143,24,248,252,253,133,107,218,197,97,142,225,11,80,75,3,4,20,0,0,0,8,0,197,174,247,84,125,16,35,51,209,0,0,0,89,1,0,0,12,0,28,0,112,97,99,107,97,103,101,46,106,115,111,110,85,84,9,0,3,81,180,220,98,180,186,220,98,117,120,11,0,1,4,245,1,0,0,4,20,0,0,0,85,79,187,170,2,49,16,237,247,43,66,234,187,113,162,139,47,16,44,110,115,11,59,107,33,102,71,12,100,147,176,137,11,23,241,223,77,38,136,10,83,204,156,71,206,201,189,97,140,59,53,32,223,50,30,125,187,90,192,70,66,55,231,63,133,152,112,140,198,187,194,129,144,2,42,154,254,3,201,7,223,223,44,86,44,234,209,132,20,51,124,207,103,6,180,31,130,177,164,75,81,147,168,200,112,156,106,84,82,201,104,214,182,193,143,105,183,6,128,188,107,165,175,184,187,40,27,145,133,219,217,26,205,179,237,65,1,249,64,23,201,123,248,59,214,208,30,167,95,12,232,122,116,218,224,71,248,190,84,140,51,139,234,98,49,21,207,73,138,149,144,242,213,195,249,30,219,218,129,88,248,98,201,77,31,34,178,203,100,247,110,50,121,155,212,59,171,60,85,100,114,41,242,0,233,154,71,243,4,80,75,3,4,20,0,0,0,8,0,239,181,247,84,19,66,213,63,231,1,0,0,179,3,0,0,13,0,28,0,116,115,99,111,110,102,105,103,46,106,115,111,110,85,84,9,0,3,210,192,220,98,212,192,220,98,117,120,11,0,1,4,245,1,0,0,4,20,0,0,0,141,83,193,110,219,48,12,189,231,43,8,239,178,21,107,124,207,105,93,183,67,128,21,43,154,116,151,162,24,100,137,137,181,72,162,65,209,113,140,162,255,62,201,142,155,164,167,2,190,232,233,241,241,209,143,122,153,1,20,154,124,99,29,242,239,70,44,133,88,44,224,37,193,0,101,9,43,97,171,5,104,188,129,207,235,218,70,72,223,122,181,0,233,27,132,168,54,40,125,134,172,111,136,69,5,249,50,20,23,120,80,90,70,73,229,238,153,26,100,233,215,169,38,55,16,110,241,235,200,179,145,156,18,52,119,100,90,247,254,50,208,99,208,53,234,29,154,101,48,120,64,115,163,53,198,119,172,56,184,188,196,218,136,143,97,23,168,11,203,112,171,68,215,127,20,91,85,157,119,152,134,252,185,186,59,90,142,163,137,101,16,76,134,47,5,253,112,149,176,68,11,120,144,226,2,127,192,52,70,155,135,205,140,64,6,143,156,145,228,108,149,240,167,225,0,39,133,220,253,87,26,62,74,50,1,177,15,162,14,176,65,37,45,99,156,200,134,252,200,252,206,212,69,100,224,54,136,245,8,24,246,150,41,248,32,103,212,185,77,222,243,156,197,7,74,158,39,127,162,120,139,114,62,92,174,190,49,255,218,100,77,114,232,66,208,83,203,80,29,21,199,10,8,136,38,30,69,168,149,31,150,179,72,211,86,206,234,81,36,65,168,133,184,135,13,147,135,174,182,186,78,146,105,117,146,83,232,172,115,80,165,3,242,30,205,91,36,247,74,106,240,170,105,108,216,78,216,3,110,22,80,139,52,113,81,150,93,215,205,243,2,70,205,182,17,167,194,118,78,188,45,13,233,88,214,42,152,138,104,87,142,209,92,243,91,54,243,90,188,251,212,36,241,235,115,241,162,82,105,91,216,101,235,243,41,215,204,58,189,133,156,33,170,141,27,126,210,211,16,240,223,81,62,150,223,6,35,229,116,255,60,20,188,158,246,107,122,4,105,72,145,212,115,76,182,96,244,180,199,91,242,30,131,76,91,57,27,42,243,179,8,218,181,6,135,110,145,117,121,117,85,94,37,233,215,217,127,80,75,3,4,10,0,0,0,0,0,236,157,247,84,0,0,0,0,0,0,0,0,0,0,0,0,4,0,28,0,115,114,99,47,85,84,9,0,3,156,150,220,98,45,152,220,98,117,120,11,0,1,4,245,1,0,0,4,20,0,0,0,80,75,3,4,20,0,0,0,8,0,50,187,247,84,183,28,133,184,171,1,0,0,246,2,0,0,11,0,28,0,115,114,99,47,109,97,105,110,46,116,115,85,84,9,0,3,191,201,220,98,192,201,220,98,117,120,11,0,1,4,245,1,0,0,4,20,0,0,0,125,82,77,111,219,48,12,189,235,87,16,190,196,14,50,123,217,146,174,117,208,219,122,24,144,1,195,154,158,91,197,166,109,161,250,200,100,186,73,17,228,191,143,114,236,0,93,128,233,32,144,143,212,227,211,147,178,169,16,241,111,172,208,163,45,16,10,103,12,90,106,65,89,136,52,202,74,35,61,119,166,124,110,27,101,82,106,35,168,156,231,46,75,120,160,68,60,28,164,217,105,4,87,129,117,246,211,222,249,87,101,107,176,210,96,9,202,236,156,167,92,136,115,0,71,82,26,215,242,29,253,9,42,239,12,76,134,1,147,149,16,63,8,246,174,211,37,80,227,221,158,119,228,73,90,187,125,224,243,157,37,101,16,240,80,224,142,148,179,185,136,158,108,33,187,186,33,120,124,183,36,15,15,222,59,159,195,134,207,121,252,211,97,75,172,192,184,178,99,117,151,57,80,58,108,89,41,193,206,187,55,85,162,144,150,73,123,117,103,205,147,139,198,73,36,196,166,81,45,16,22,141,85,76,217,19,43,207,12,231,11,5,101,65,39,251,197,40,60,253,252,62,14,116,22,100,79,8,49,190,161,5,85,113,163,36,209,67,76,25,149,88,201,78,83,148,176,59,211,76,136,44,131,199,80,147,45,188,140,110,13,61,1,91,255,107,216,75,62,154,186,190,182,146,95,167,229,99,112,15,235,84,75,90,219,58,94,126,78,151,51,248,202,123,178,26,234,219,15,245,249,85,189,84,45,201,240,37,238,65,166,99,178,113,241,54,25,70,56,141,169,118,117,124,148,51,216,206,46,253,167,100,5,217,20,126,121,21,190,209,135,119,228,203,30,5,64,36,163,28,66,192,33,207,231,164,151,55,0,182,102,32,40,225,252,20,192,104,123,213,62,255,95,251,40,132,241,57,175,187,69,122,247,229,230,102,177,88,46,111,191,45,196,169,55,252,47,80,75,3,4,20,0,0,0,8,0,242,180,247,84,94,247,21,86,106,2,0,0,125,4,0,0,23,0,28,0,115,114,99,47,108,101,97,102,108,101,116,95,117,109,100,95,115,104,105,109,46,116,115,85,84,9,0,3,247,190,220,98,248,190,220,98,117,120,11,0,1,4,245,1,0,0,4,20,0,0,0,117,83,75,110,219,48,16,221,243,20,3,111,236,4,174,212,238,138,4,93,37,5,186,80,178,104,154,189,41,114,100,209,161,72,133,31,203,66,16,160,135,232,9,123,146,14,37,202,73,81,20,208,98,40,206,231,125,56,101,9,55,14,121,64,15,28,122,103,79,35,4,11,161,69,120,188,187,133,10,121,163,49,128,173,15,40,2,236,6,101,164,29,138,106,7,60,229,135,177,199,15,158,55,8,95,31,238,0,79,189,117,129,177,242,146,177,31,173,242,160,45,151,30,54,220,72,186,67,17,105,200,197,63,173,189,112,170,15,211,239,70,57,79,145,234,144,142,60,176,144,154,116,86,70,141,64,145,234,82,127,148,160,76,134,152,239,246,142,247,237,22,134,86,137,150,128,121,181,55,158,253,135,2,85,190,177,40,96,194,73,159,23,182,71,232,173,214,49,40,107,182,144,64,43,207,172,65,176,205,52,140,208,152,189,159,144,17,221,60,155,84,56,90,37,183,80,199,144,250,24,20,232,61,119,35,180,232,16,106,20,60,122,100,163,141,73,30,71,119,132,95,42,74,9,202,55,92,164,97,192,103,250,252,200,149,230,53,17,90,48,159,199,128,237,83,166,103,148,138,39,229,3,65,129,155,219,123,120,252,94,249,34,203,157,221,67,209,26,245,28,17,86,131,117,79,126,5,3,106,253,23,156,165,63,163,34,110,22,101,102,251,74,195,59,223,115,129,89,1,144,86,196,14,77,210,157,180,171,17,98,162,48,89,51,240,177,128,111,118,192,35,186,45,51,54,204,190,193,239,159,191,160,177,14,44,177,114,103,161,102,123,8,228,81,73,178,46,234,160,122,98,54,143,205,186,114,135,140,220,69,35,207,227,222,185,46,21,149,70,174,245,8,245,8,4,20,167,81,19,150,55,218,131,141,90,154,117,160,98,198,97,111,173,164,135,21,206,220,41,116,248,28,21,121,49,169,78,212,40,158,158,74,214,122,158,219,113,249,206,18,150,108,2,79,178,83,194,38,141,150,23,25,122,1,155,251,51,245,212,81,98,195,137,93,190,78,26,30,34,189,107,62,33,150,203,154,196,212,108,202,159,136,172,114,213,234,130,236,188,44,25,155,121,195,186,13,161,247,87,101,41,164,57,248,66,104,27,101,163,73,167,66,216,174,228,7,126,42,181,170,125,169,103,75,203,79,197,231,226,227,114,42,14,126,125,125,110,149,182,21,46,211,230,46,239,171,113,182,131,117,78,78,153,130,222,88,128,151,234,21,190,192,188,37,41,61,154,39,99,7,147,194,23,168,174,166,70,180,20,75,151,87,42,44,75,120,72,52,40,101,151,121,191,84,233,148,105,189,238,174,88,254,191,200,83,93,179,63,80,75,1,2,30,3,20,0,0,0,8,0,173,178,247,84,63,79,13,255,254,0,0,0,4,24,0,0,9,0,24,0,0,0,0,0,0,0,0,0,164,129,0,0,0,0,46,68,83,95,83,116,111,114,101,85,84,5,0,3,182,186,220,98,117,120,11,0,1,4,245,1,0,0,4,20,0,0,0,80,75,1,2,30,3,10,0,0,0,0,0,135,182,247,84,0,0,0,0,0,0,0,0,0,0,0,0,18,0,24,0,0,0,0,0,0,0,16,0,237,65,65,1,0,0,46,109,101,116,97,95,105,103,110,111,114,101,95,116,104,105,115,47,85,84,5,0,3,237,193,220,98,117,120,11,0,1,4,245,1,0,0,4,20,0,0,0,80,75,1,2,30,3,20,0,0,0,8,0,210,178,247,84,4,129,138,94,194,1,0,0,133,3,0,0,50,0,24,0,0,0,0,0,1,0,0,0,164,129,141,1,0,0,46,109,101,116,97,95,105,103,110,111,114,101,95,116,104,105,115,47,99,114,101,97,116,101,45,105,110,108,105,110,101,45,97,114,99,104,105,118,101,45,115,99,114,105,112,116,46,109,106,115,85,84,5,0,3,252,186,220,98,117,120,11,0,1,4,245,1,0,0,4,20,0,0,0,80,75,1,2,30,3,20,0,0,0,8,0,128,182,247,84,134,228,110,93,72,0,0,0,86,0,0,0,28,0,24,0,0,0,0,0,1,0,0,0,164,129,187,3,0,0,46,109,101,116,97,95,105,103,110,111,114,101,95,116,104,105,115,47,82,69,65,68,77,69,46,116,120,116,85,84,5,0,3,224,193,220,98,117,120,11,0,1,4,245,1,0,0,4,20,0,0,0,80,75,1,2,30,3,20,0,0,0,8,0,39,179,247,84,89,60,189,69,88,0,0,0,119,0,0,0,21,0,24,0,0,0,0,0,1,0,0,0,228,129,89,4,0,0,46,109,101,116,97,95,105,103,110,111,114,101,95,116,104,105,115,47,114,117,110,85,84,5,0,3,153,187,220,98,117,120,11,0,1,4,245,1,0,0,4,20,0,0,0,80,75,1,2,30,3,10,0,0,0,0,0,198,181,247,84,0,0,0,0,0,0,0,0,0,0,0,0,7,0,24,0,0,0,0,0,0,0,16,0,237,65,0,5,0,0,112,117,98,108,105,99,47,85,84,5,0,3,131,192,220,98,117,120,11,0,1,4,245,1,0,0,4,20,0,0,0,80,75,1,2,30,3,20,0,0,0,8,0,138,181,247,84,83,75,4,148,85,1,0,0,77,2,0,0,17,0,24,0,0,0,0,0,1,0,0,0,164,129,65,5,0,0,112,117,98,108,105,99,47,105,110,100,101,120,46,104,116,109,108,85,84,5,0,3,19,192,220,98,117,120,11,0,1,4,245,1,0,0,4,20,0,0,0,80,75,1,2,30,3,20,0,0,0,8,0,72,187,247,84,206,95,24,128,103,0,0,0,156,0,0,0,14,0,24,0,0,0,0,0,1,0,0,0,164,129,225,6,0,0,112,117,98,108,105,99,47,109,97,105,110,46,106,115,85,84,5,0,3,232,201,220,98,117,120,11,0,1,4,245,1,0,0,4,20,0,0,0,80,75,1,2,30,3,20,0,0,0,8,0,72,187,247,84,107,96,82,240,103,0,0,0,114,0,0,0,26,0,24,0,0,0,0,0,1,0,0,0,164,129,144,7,0,0,112,117,98,108,105,99,47,108,101,97,102,108,101,116,95,117,109,100,95,115,104,105,109,46,106,115,85,84,5,0,3,232,201,220,98,117,120,11,0,1,4,245,1,0,0,4,20,0,0,0,80,75,1,2,30,3,20,0,0,0,8,0,197,174,247,84,125,16,35,51,209,0,0,0,89,1,0,0,12,0,24,0,0,0,0,0,1,0,0,0,164,129,75,8,0,0,112,97,99,107,97,103,101,46,106,115,111,110,85,84,5,0,3,81,180,220,98,117,120,11,0,1,4,245,1,0,0,4,20,0,0,0,80,75,1,2,30,3,20,0,0,0,8,0,239,181,247,84,19,66,213,63,231,1,0,0,179,3,0,0,13,0,24,0,0,0,0,0,1,0,0,0,164,129,98,9,0,0,116,115,99,111,110,102,105,103,46,106,115,111,110,85,84,5,0,3,210,192,220,98,117,120,11,0,1,4,245,1,0,0,4,20,0,0,0,80,75,1,2,30,3,10,0,0,0,0,0,236,157,247,84,0,0,0,0,0,0,0,0,0,0,0,0,4,0,24,0,0,0,0,0,0,0,16,0,237,65,144,11,0,0,115,114,99,47,85,84,5,0,3,156,150,220,98,117,120,11,0,1,4,245,1,0,0,4,20,0,0,0,80,75,1,2,30,3,20,0,0,0,8,0,50,187,247,84,183,28,133,184,171,1,0,0,246,2,0,0,11,0,24,0,0,0,0,0,1,0,0,0,164,129,206,11,0,0,115,114,99,47,109,97,105,110,46,116,115,85,84,5,0,3,191,201,220,98,117,120,11,0,1,4,245,1,0,0,4,20,0,0,0,80,75,1,2,30,3,20,0,0,0,8,0,242,180,247,84,94,247,21,86,106,2,0,0,125,4,0,0,23,0,24,0,0,0,0,0,1,0,0,0,164,129,190,13,0,0,115,114,99,47,108,101,97,102,108,101,116,95,117,109,100,95,115,104,105,109,46,116,115,85,84,5,0,3,247,190,220,98,117,120,11,0,1,4,245,1,0,0,4,20,0,0,0,80,75,5,6,0,0,0,0,14,0,14,0,209,4,0,0,121,16,0,0,0,0]),
"so-73091042.zip",
"application/zip",
);
Example
This example will be served using a static file server from the ./public directory, and the entrypoint html file is here:
./public/index.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Stack Overflow 73091042</title>
<!-- First, define an import map for the "leaflet" module specifier
which points to the UMD-to-ESM shim module: -->
<script type="importmap">
{
"imports": {
"leaflet": "/leaflet_umd_shim.js"
}
}
</script>
<!-- Then, import the entrypoint module: -->
<script type="module" src="/main.js"></script>
</head>
<body>
<h1>See console output</h1>
</body>
</html>
Nothing special there except maybe the import map if you aren't familiar: WICG/import-maps: How to control the behavior of JavaScript imports
Next, there are two TypeScript source files in the ./src directory which are transpiled to JavaScript and copied into the ./public directory during compilation.
The first is a shim:
./src/leaflet_umd_shim.ts:
// Creates a proxy to the UMD Leaflet object `window.L` as a type-safe ESM export
/*
This loads (and executes) the UMD Leaflet script the first time that
this module is imported into the module graph, which assigns
the UMD Leaflet object to `window.L`. This is scope pollution, and is
one of the things that ES modules avoid, but is necessary here because
you expressed dissatisfaction at the available Leaflet ES module options
at existing CDN URLs.
This proxy technique "works" well here because Leaflet
is an object export/namspace and is documented to be used this way. However,
note that — for other modules which provide multiple exports that are
intended to be imported individually by name — this technique wouldn't be
a good fit because it requires the entire UMD module to be made available
on a single (named) export. (Note that the default export is just a named export
using the name "default").
*/
import 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.8.0/leaflet.js';
import type * as Leaflet from 'leaflet';
const {L} = window as unknown as { L: typeof Leaflet };
// Same as `export {L as default}`:
export default L;
The "leaflet" import specifier in the import map is bound to the location of the JavaScript module produced by this file during compilation.
That specifier is used in the entrypoint module:
./src/main.ts:
/*
(Reference comments in "leaflet_umd_shim.ts" for context)
Example of non-working named import:
import {tileLayer} from 'leaflet';
It would throw the following runtime exception:
"Uncaught SyntaxError: The requested module 'leaflet' does not provide
an export named 'tileLayer'"
This technique requires importing the entire UMD module on a name (even if that
name is "default"):
*/
// Same as `import {default as L} from 'leaflet'`:
import L from 'leaflet';
const a = L.latLng(50.5, 30.5);
const b = L.latLng(51.5, 30.5);
const distance = a.distanceTo(b);
console.log({a, b, distance}); /* Prints the following:
{
"a": {
"lat": 50.5,
"lng": 30.5
},
"b": {
"lat": 51.5,
"lng": 30.5
},
"distance": 111194.92664455874
}
*/
When you serve this site on a local http server and access the HTML file, the main module will execute and print that information to the console using those methods from Leaflet to create the data that is logged, proving that this technique works.
Here's my TSConfig:
./tsconfig.json:
{
"compilerOptions": {
// Strict options (This is TS: type safety is important)
"exactOptionalPropertyTypes": true,
"isolatedModules": true,
"noUncheckedIndexedAccess": true,
"strict": true,
"useUnknownInCatchVariables": true,
// ESM
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "nodenext",
"lib": [
"esnext", // Latest ES syntax features
"dom", // Browser runtime environmnt
"dom.iterable" // Browser runtime environmnt
],
"target": "esnext", // Adjust this to your browser target needs
"outDir": "public", // Directory from which the site will be served
// Path mapping
// Ref: https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping
"baseUrl": ".",
"paths": {
"leaflet": ["node_modules/#types/leaflet"]
},
// Optional settings
"removeComments": true
},
"include": ["src/**/*"]
}
And, lastly, here's the npm package file:
./package.json:
{
"name": "so-73091042",
"version": "0.1.0",
"type": "module",
"scripts": {
"compile": "tsc",
"serve": "static --port=8000 --cache=false public"
},
"license": "MIT",
"devDependencies": {
"#types/leaflet": "^1.7.11",
"node-static": "^0.7.11",
"typescript": "^4.7.4"
},
"volta": {
"node": "16.16.0"
}
}
You can ignore the volta property: it just shows that I was using Volta and the version of Node that I had installed when creating this repository example.
The node-static dependency is the package recommended by the official Node.js website for serving static files in the article How to serve static files | Node.js.
First use npm install to install the dependencies:
% npm install
added 9 packages, and audited 10 packages in 4s
Then — to compile the TS files and emit them — use npm run compile:
% npm run compile
> so-73091042#0.1.0 compile
> tsc
There should now be two new files in the ./public directory after compilation:
./public/leaflet_umd_shim.js:
import 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.8.0/leaflet.js';
const { L } = window;
export default L;
./public/main.js:
import L from 'leaflet';
const a = L.latLng(50.5, 30.5);
const b = L.latLng(51.5, 30.5);
const distance = a.distanceTo(b);
console.log({ a, b, distance });
Now everything is ready to be served by the static file server, so use the command npm run serve:
% npm run serve
> so-73091042#0.1.0 serve
> static --port=8000 --cache=false public
serving "public" at http://127.0.0.1:8000
If you navigate to http://127.0.0.1:8000 in your browser (this is the same as http://localhost:8000), you'll see the index page with the text "See console output", and if you look at your browser's JS console, you'll see a representation of the object data that was logged to the console in the main.js module.
When you're ready to stop the server, just use ctrl + c in your terminal
And that's it!

How to import js module when Typescript declaration file is located in a separate directory?

Question:
When I run npm run build with the configuration below, rollup.js is unable to resolve the dependency (import) and displays the following message below. Is there any way to make rollup happy while also referencing the Typescript declaration file?
Message from rollup:
(!) Unresolved dependencies
https://rollupjs.org/guide/en/#warning-treating-module-as-external-dependency
pdfjs-dist/types/web/ui_utils (imported by index.ts)
Here is my index.ts:
import { RendererType } from 'pdfjs-dist/types/web/ui_utils'
const renderType = RendererType.CANVAS;
My package.json:
{
"name": "myproject",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "rollup --config"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"#rollup/plugin-node-resolve": "^13.2.1",
"#rollup/plugin-typescript": "^8.3.2",
"pdfjs-dist": "^2.13.216",
"rollup": "^2.70.2",
"typescript": "^4.6.4"
}
}
My rollup.config.js:
import typescript from '#rollup/plugin-typescript';
import { nodeResolve } from '#rollup/plugin-node-resolve';
export default [
{
input: 'index.ts',
output: {
format: 'es',
file: 'index.js'
},
plugins: [
typescript(),
nodeResolve({ browser: true })
]
}
]
Here are the exact steps to reproduce the error above:
Create an empty folder and then run npm -y init
Run the following command:
npm install typescript pdfjs-dist rollup #rollup/plugin-node-resolve #rollup/plugin-typescript --save-dev
Add "build": "rollup --config" to your package.json
Create the rollup.config.js file shown above
Run npm run build in the terminal
More background:
Now, I should point out that the file pdfjs-dist/types/web/ui_utils is a typescript declaration file (ui_utils.d.ts). The actual js file is in pdfjs-dist/lib/web.
If I copy the typescript declaration file so that it is located in the same directory as the js file, dependency resolution works. However, since I will be writing a wrapper around pdf js, I would have to do this for every typescript declaration file which is very tedious and upgrading would also become an issue.
So another way to word the question would be how to resolve a module *.d.ts when the js file is located in another directory?
I came up with the following solution to the problem.
Create a d.ts with the following and name it the same as the module name (ui_utils.d.ts in my case)
declare module 'pdfjs-dist/lib/web/ui_utils' {
export * from 'pdfjs-dist/types/web/ui_utils'
}
Using the above, now I can reference the actual location of the module and Typescript will pick up the declarations as well.
import { RendererType } from 'pdfjs-dist/lib/web/ui_utils'
Side note: When using rollup, you may also need to use #rollup/plugin-commonjs to be able to resolve dependencies.

Configure jest with latest version of d3-path

For some reason, my jest configuration doesn't work with the latest version of d3-path#3.0.1. It worked fine with version 2.0.0. I guess it has something to do with d3-path switching to ESM, but I was already using ES6 in my own code, so I don't get why it suddenly doesn't work anymore. I have the following packages installed:
"dependencies": {
"d3-path": "^3.0.1"
},
"devDependencies": {
"#babel/core": "^7.15.8",
"#babel/preset-env": "^7.15.8",
"babel-jest": "^27.3.1",
"jest": "^27.3.1"
}
My babel.config.js:
module.exports = {
presets: [['#babel/preset-env', {targets: {node: 'current'}}]],
};
My index.js:
import { path } from 'd3-path'
export default () => path()
The test file:
import fn from '../src/index.js'
describe('test', () => {
it('works', () => {
fn()
expect(2 + 2).toBe(4)
})
})
The error message:
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,jest){export {default as path} from "./path.js";
^^^^^^
SyntaxError: Unexpected token 'export'
> 1 | import { path } from 'd3-path'
To reproduce:
git clone https://github.com/luucvanderzee/jest-problem.git
cd jest-problem
npm i
npm run test
// The test runs without failure- this is because we're currently still using d3-path#2.0.0
npm uninstall d3-path && npm install d3-path // (upgrade to d3-path#3.0.1)
npm run test
// Now the test fails.
How should I configure jest and/or babel to solve this issue?
EDIT:
I already tried the following (from this page of the jest docs):
Creating a jest.config.js file with the following:
module.exports = {
transform: {}
}
Changing my "test" command from "jest" to "node --experimental-vm-modules node_modules/jest/bin/jest.js"
This gives me another error:
/home/luuc/Projects/javascript/jest-problem/test/test.test.js:1
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,jest){import fn from '../src/index.js'
^^^^^^
SyntaxError: Cannot use import statement outside a module
I also don't get what is meant by
• To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
Isn't the problem that the module is not transformed? Would adding an ignore pattern not just lead to the module not getting transformed?
Problem
The error happens because jest does not send the content of node_modules to be transformed by babel by default.
The following output line of npm run test indicates one way to solve the problem:
• To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
Solution
The configuration of jest should be updated in order to instruct it to transform the ESM code present in d3-path dependency.
To do so, add the following to a jest.config.js file in the root directory of the project:
module.exports = {
transformIgnorePatterns: ['node_modules/(?!(d3-path)/)']
}
npm run test runs fine after that.
The transformIgnorePatterns option is documented here.
Edit - including more modules
In order to include all modules starting with d3, the following syntax may be used:
transformIgnorePatterns: ['/node_modules/(?!(d3.*)/)']
TLDR;
transformIgnorePatterns: [
"/node_modules/(?!d3|d3-array|d3-axis|d3-brush|d3-chord|d3-color|d3-contour|d3-delaunay|d3-dispatch|d3-drag|d3-dsv|d3-ease|d3-fetch|d3-force|d3-format|d3-geo|d3-hierarchy|d3-interpolate|d3-path|d3-polygon|d3-quadtree|d3-random|d3-scale|d3-scale-chromatic|d3-selection|d3-shape|d3-time|d3-time-format|d3-timer|d3-transition|d3-zoom}|internmap|d3-delaunay|delaunator|robust-predicates)"
]
For the ones reaching this page after updating recharts dependency, here I found the solution, provided by them.

Test two different npm package versions at the same time

When I create an npm package, sometimes it would face the need to backward old dependency package version.
If the new version has new api, I may write the code in this pattern:
import pkg from 'some-pkg';
const isNewVersion = pkg.newVersionApi !== 'undefined';
if (isNewversion) {
pkg.newVersionApi();
} else {
pkg.oldVersionApi(); // backward compatible api
}
And with this pattern, when I want to write the test, I only can test the installed version code. The other version's code can't be tested.
For real example, in React v15 and v16, React v16 has new API Portal. Before Portal release, v15 has unstable_renderSubtreeIntoContainer api to realize similar feature.
So the code for React would be like:
import ReactDOM from 'react-dom';
const isV16 = ReactDOM.createPortal !== 'undefined';
if (isV16) {
ReactDOM.createPortal(...);
} else {
ReactDOM.unstable_renderSubtreeIntoContainer(...);
}
So I want to ask is there any method to test with different dependency version?
Currently, one method I think of is to install the other version again and test it. But it only can do on local. It can't work on ci and it can't count in coverage together.
I think that is not only for react test. It may face on node.js test. Any suggestion can be discussed.
Updated
This question maybe is related to install two versions dependency in npm. But I know currently installing two versions dependency is not workable.
Here is a might be solution, not sure it will work as you expect. But, you will have a direction to move forward.
package.json
{
"name": "express-demo",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "node ./bin/www"
},
"dependencies": {
"cookie-parser": "~1.4.3",
"debug": "~2.6.3",
"express": "~4.15.2",
"jade": "~1.11.0",
"morgan": "~1.8.1",
"serve-favicon": "~2.4.2",
"webpack": "^3.8.1",
"webpack-dev-middleware": "^1.12.0",
"webpack-hot-middleware": "^2.20.0"
},
"customDependecies": {
"body-parser": [
"",
"1.18.1",
"1.18.0"
]
}
}
Note in above package.json file, I have added a new key customDependecies which I will use for installing multiple dependencies. Here I am using body-parser package for demo. Next you need file, that can read this key and install the deps.
install-deps.js
const {spawnSync} = require('child_process');
const fs = require('fs');
const customDependencies = require('./package.json').customDependecies;
spawnSync('mkdir', ['./node_modules/.tmp']);
for (var dependency in customDependencies) {
customDependencies[dependency].forEach((version) => {
console.log(`Installing ${dependency}#${version}`);
if (version) {
spawnSync('npm', ['install', `${dependency}#${version}`]);
spawnSync('mv', [`./node_modules/${dependency}`, `./node_modules/.tmp/${dependency}#${version}`]);
} else {
spawnSync('npm', ['install', `${dependency}`]);
spawnSync('mv', [`./node_modules/${dependency}`, `./node_modules/.tmp/${dependency}`]);
}
});
customDependencies[dependency].forEach((version) => {
console.log(`Moving ${dependency}#${version}`);
if (version) {
spawnSync('mv', [`./node_modules/.tmp/${dependency}#${version}`, `./node_modules/${dependency}#${version}`]);
} else {
spawnSync('mv', [`./node_modules/.tmp/${dependency}`, `./node_modules/${dependency}`]);
}
});
}
spawnSync('rm', ['-rf', './node_modules/.tmp']);
console.log(`Installing Deps finished.`);
Here, I am installing deps one by one in tmp folder and once installed, I am moving them to ./node_modules folder.
Once, everything is installed, you can check the versions like below
index.js
var bodyParser = require('body-parser/package.json');
var bodyParser1181 = require('body-parser#1.18.1/package.json');
var bodyParser1182 = require('body-parser#1.18.0/package.json');
console.log(bodyParser.version);
console.log(bodyParser1181.version);
console.log(bodyParser1182.version);
Hope, this will serve your purpose.
Create 3 separate projects (folders with package.json) and a shared folder:
A shared folder containing the test module (my-test). Export a function to run the test;
A client project importing my-test and dependency v1. Export a function that calls the test function in my-test.
A client project importing my-test and dependency v2. Export a function that calls the test function in my-test.
A master project that imports both client projects. Run each exported function.
You're going to have to run them separately. Create a separate project folder for each dependency version. Ex. React10, React11, React12. Each will have its own package.json, specified for the correct version. When you run the integration and/or versioned tests, you'll run your standard unit tests across each version, but it may also be advisable to add any version specific unit tests to that folder.
Creating a make file would make your life easier when running your full testing suite. If you do this you can easily integrate this into CI.
In addition to the other suggestions, you can try the approach described at Testing multiple versions of a module dependency.
Here's an example of using this approach to test against multiple webpack versions:
npm install --save-dev module-alias
npm install --save-dev webpack-v4#npm:webpack#4.46.0
npm install --save-dev webpack-v5#npm:webpack#5.45.1
The module-alias package handles the magic of switching between package versions while still supporting normal require('webpack') (or whatever your module is) calls.
The other installs will create two versions of your dependency, each with a distinct directory name within your local node_modules/.
Then, within your test code, you can set up the dependency alias via:
const path = require('path');
require('module-alias').addAlias(
'webpack',
path.resolve('node_modules', 'webpack-v4'),
);
// require('webpack') will now pull in webpack-v4
You'd do the same thing for 'webpack-v5' in a different test harness.
If any of your sub-dependencies have a hardcoded require('webpack') anywhere in their own code, this will ensure that they also pull in the correct webpack version.

Angular2 too many file requests on load

I'm making a website using Angular2 and I'm having what i suppose is an issue. On the first load of my angular page, SystemJS is making more than 500 hundred requests to retrieve every Angular2 file in angular2/src directory. In total, the first load downloads more than 4MB and it takes more than 14 seconds to start.
My index.html does the following scripts includes:
<script src="libs/angular2/bundles/angular2-polyfills.js"></script>
<script src="libs/systemjs/dist/system.src.js"></script>
<script src="libs/rxjs/bundles/Rx.js"></script>
<script src="libs/angular2/bundles/angular2.min.js"></script>
<script src="libs/angular2/bundles/http.dev.js"></script>
<script src="libs/jquery/jquery.js"></script>
<script src="libs/lodash/lodash.js"></script>
<script src="libs/bootstrap/js/bootstrap.js"></script>
And my systemJs initialization code looks like this:
<script>
System.config({
defaultJSExtensions: true,
paths: {
'*': 'libs/*',
'app/*': 'app/*'
},
packageConfigPaths: ['libs/*/package.json'],
packages: {
app: {
format: 'register',
defaultExtension: 'js'
}
}
});
System.import('app/main')
.then(null, console.error.bind(console));
</script>
My public folder has the following structure:
.
├── img
├── styles
├── app
├── libs
| └── angular2
| └── systemjs
| └── rxjs
| └── jquery
| └── lodash
| └── bootstrap
└── index.html
A couple of screenshots of some of the js files that are being requested:
Is there a way to avoid all of those requests?
I had the exact same problem, was actually looking at this post for an answer. Here is what I did to solve the problem.
Modify your project to use webpack. Follow this short tutorial:
Angular2 QuickStart SystemJS To Webpack
This method will give you a single javascript file however it is quite large (my project file was over 5MB) and needs to be minified. To do this I installed webpack globaly:npm install webpack -g. Once installed, run webpack -p from your apps root directory. This brought my file size down to about 700KB
From 20 seconds and 350 requests down to 3 seconds and 7 requests.
I see you already have a response, which is good of course.
BUT for those who want to use systemjs (like I also do), and not go to webpack, you can still bundle the files. However, it does involve using another tool also (I use gulp).
So... you would have the folowing systemjs config (not in the html, but in a separate file - let's call it "system.config.js"):
(function(global) {
// map tells the System loader where to look for things
var map = {
'app': 'dist/app', // this is where your transpiled files live
'rxjs': 'node_modules/rxjs',
'angular2-in-memory-web-api': 'node_modules/angular2-in-memory-web-api', // this is something new since angular2 rc.0, don't know what it does
'#angular': 'node_modules/#angular'
};
// packages tells the System loader how to load when no filename and/or no extension
var packages = {
'app': { main: 'boot.js', defaultExtension: 'js' },
'rxjs': { defaultExtension: 'js' },
'angular2-in-memory-web-api': { defaultExtension: 'js' }
};
var packageNames = [
'#angular/common',
'#angular/compiler',
'#angular/core',
'#angular/http',
'#angular/platform-browser',
'#angular/platform-browser-dynamic',
//'#angular/router', // I still use "router-deprecated", haven't yet modified my code to use the new router that came with rc.0
'#angular/router-deprecated',
'#angular/http',
'#angular/testing',
'#angular/upgrade'
];
// add package entries for angular packages in the form '#angular/common': { main: 'index.js', defaultExtension: 'js' }
packageNames.forEach(function(pkgName) {
packages[pkgName] = { main: 'index.js', defaultExtension: 'js' };
});
var config = {
map: map,
packages: packages
};
// filterSystemConfig - index.html's chance to modify config before we register it.
if (global.filterSystemConfig) { global.filterSystemConfig(config); }
System.config(config);
})(this);
Then, in your gulpfile.js you would build a bundle like this (using the info from system.config.js and tsconfig.json files):
var gulp = require('gulp'),
path = require('path'),
Builder = require('systemjs-builder'),
ts = require('gulp-typescript'),
sourcemaps = require('gulp-sourcemaps');
var tsProject = ts.createProject('tsconfig.json');
var appDev = 'dev/app'; // where your ts files are, whatever the folder structure in this folder, it will be recreated in the below 'dist/app' folder
var appProd = 'dist/app';
/** first transpile your ts files */
gulp.task('ts', () => {
return gulp.src(appDev + '/**/*.ts')
.pipe(sourcemaps.init({
loadMaps: true
}))
.pipe(ts(tsProject))
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest(appProd));
});
/** then bundle */
gulp.task('bundle', function() {
// optional constructor options
// sets the baseURL and loads the configuration file
var builder = new Builder('', 'dist/system.config.js');
/*
the parameters of the below buildStatic() method are:
- your transcompiled application boot file (the one wich would contain the bootstrap(MyApp, [PROVIDERS]) function - in my case 'dist/app/boot.js'
- the output (file into which it would output the bundled code)
- options {}
*/
return builder
.buildStatic(appProd + '/boot.js', appProd + '/bundle.js', { minify: true, sourceMaps: true})
.then(function() {
console.log('Build complete');
})
.catch(function(err) {
console.log('Build error');
console.log(err);
});
});
/** this runs the above in order. uses gulp4 */
gulp.task('build', gulp.series(['ts', 'bundle']));
So, when running "gulp build", you will get the "bundle.js" file with everything you need.
Sure, you also need a few more packages for this gulp bundle task to work:
npm install --save-dev github:gulpjs/gulp#4.0 gulp-typescript gulp-sourcemaps path systemjs-builder
Also, make sure that in your tsconfig.json you have "module":"commonjs".
Here is my tsconfig.json which is used in my 'ts' gulp task:
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": false
},
"exclude": [
"node_modules",
"typings/main",
"typings/main.d.ts"
]
}
Then, in your html file you only need to include this:
<!-- Polyfill(s) for older browsers -->
<script src="node_modules/es6-shim/es6-shim.min.js"></script>
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/reflect-metadata/Reflect.js"></script>
<script src="dist/app/bundle.js"></script>
And that's it... I got from 600 requests, 4mb in about 5 seconds... to 20 requests, 1.4mb in 1.6 seconds (local development machine). But these 20 requests ~1.4mb in 1.6 seconds also include some other js and css that the admin theme came with plus a few html templates that get required on the first load, I prefer to use external templates - templateUrl: '', instead of inline ones, written in my component.ts file.
Sure, for an app that would have millions of users, this still wouldn't be enough. Also server-side rendering for initial load and cache system should be implemented, I actually managed to do that with angular universal, but on Angular2 beta (took about 200-240 milliseconds to load the initial render of the same admin app that above takes 1.6 seconds - I know: WOW!). Now it's incompatible since Angular2 RC came out, but I'm sure the guys doing universal will get it up to speed soon, specially since ng-conf is coming up. Plus, they're also planing to make Angular Universal for PHP, ASP and a few other - right now it's only for Nodejs.
Edit:
Actually, I've just found out that on NG-CONF they said Angular Universal already supports ASP (but it doesn't support Angular2 > beta.15 :)) ... but let's give them some time, RC just came out a few days ago
I think that your question is related to this one:
My angular 2 app takes a long time to load for first time users, I need help to speed it up
To have something ready for production (and speed it up), you need to package it.
I mean transpiling all files into JavaScript ones and concat them the same way Angular2 does for example. This way you will have several modules contained into a single JS file. This way you will reduce the number of HTTP calls to load your application code into the browser.
I found a simple solution, using browserify & uglifyjs on mgechev's angular2-seed repository
Here's my version:
pacakge.json:
{
...
"scripts": {
"build_prod": "npm run clean && npm run browserify",
"clean": "del /S/Q public\\dist",
"browserify": "browserify -s main public/YourMainModule.js > public/dist/bundle.js && npm run minify",
"minify": "uglifyjs public/dist/bundle.js --screw-ie8 --compress --mangle --output public/dist/bundle.min.js"
},
...
"devDependencies": {
"browserify": "^13.0.1",
"typescript": "^1.9.0-dev.20160625-1.0",
"typings": "1.0.4",
"uglifyjs": "^2.4.10"
}
}
Build your project.
Run: npm run build_prod
It'll create bundle.js & bundle.min.js under public\dist directory.
Edit your index.html file:
Instead of running System.import('YourMainModule')... ,
add <script src="/dist/bundle.min.js"></script>
On the first load of my angular page, systemjs is making more than 500 hundred requests to retrieve every angular2 file in angular2/src directory. In total, the first load downloads more than 4mb and it takes more than 14s to start.
The SystemJs workflows are fairly new and don't have enough research in them for best deployment.
Suggest going back to commonjs + webpack. More : https://basarat.gitbooks.io/typescript/content/docs/quick/browser.html
Here is an example : https://github.com/AngularClass/angular2-webpack-starter
#FreeBird72 Your answer is awesome.
If you want to use SystemJS for development and speed up the production server like I do. Check this out.
NOTE: Only import the components that you use, DO NOT import from the whole package.
Eg: If you want to use Modal from ng2-bootstrap.
import {MODAL_DIRECTIVES} from "ng2-bootstrap/components/modal";
Instead of:
import {MODAL_DIRECTIVES} from "ng2-bootstrap/ng2-bootstrap";
This will import the modal component instead of the whole ng2-bootstrap
Then follow the answer from #FreeBird72
Add this package.json
{
...
"scripts": {
...
"prod": "npm run tsc && npm run browserify",
"browserify": "browserify -s main dist/main.js > dist/bundle.js && npm run minify",
"minify": "uglifyjs dist/bundle.js --screw-ie8 --compress --mangle --output dist/bundle.min.js",
...
},
"devDependencies": {
...
"browserify": "^13.0.1",
"uglifyjs": "^2.4.10",
...
}
...
}
Then you can npm run tsc on development and npm run prod on production server
Also remove System.import(.... from your index.html and change it to <script src="/dist/bundle.min.js"></script>
If you want to stick with SystemJS, you can bundle your app with JSPM. I've had good success with this so far, using JSPM's bundle-sfx command to make single JS files for Angular 2 apps.
There's some useful information in this Gist, and there's a seed project.
I am using AG2 RC version
While using MrCroft's solution with systemjs-builder, i was hitting a lot of issues like:
error TS2304: Cannot find name 'Map'
error TS2304: Cannot find name 'Promise'...
After many tries, i added:
///<reference path="../../typings/index.d.ts" />
into my boot.ts and now I got my bundle file compiled.
The Angular command line interface now supports bundling (with tree-shaking to strip out unused code from imports), minification, and ahead-of-time template compilation, which not only hugely minimises the number of requests made, but also makes the bundle very small. It uses WebPack underneath.
It's incredibly easy to make production builds with it:
ng build --prod --aot
https://github.com/angular/angular-cli

Categories

Resources