Skip to content

Commit

Permalink
Allow loading modules from http(s) specifiers
Browse files Browse the repository at this point in the history
This enables module specifiers like:

* `import "https://unpkg.com/can@5/core.mjs";`
* `import "http://unpkg.com/can@5/core.mjs";`
* `import "//unpkg.com/can@5/core.mjs";`

Closes #1450
  • Loading branch information
matthewp committed Jul 30, 2018
1 parent 79a5651 commit 80e1242
Show file tree
Hide file tree
Showing 18 changed files with 520 additions and 138 deletions.
3 changes: 3 additions & 0 deletions Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ module.exports = function (grunt) {
"src/extension-stack-trace.js",
"src/extension-pretty-name.js",
"src/extension-tree-shaking.js",
"src/extension-mjs.js",
"src/trace/trace.js",
"src/json/json.js",
"src/cache-bust/cache-bust.js",
Expand Down Expand Up @@ -132,6 +133,7 @@ module.exports = function (grunt) {
"src/extension-stack-trace.js",
"src/extension-pretty-name.js",
"src/extension-tree-shaking.js",
"src/extension-mjs.js",
"src/trace/trace.js",
"src/json/json.js",
"src/cache-bust/cache-bust.js",
Expand Down Expand Up @@ -160,6 +162,7 @@ module.exports = function (grunt) {
"src/extension-stack-trace.js",
"src/extension-pretty-name.js",
"src/extension-tree-shaking.js",
"src/extension-mjs.js",
"src/trace/trace.js",
"src/json/json.js",
"src/cache-bust/cache-bust.js",
Expand Down
22 changes: 22 additions & 0 deletions main.js
Original file line number Diff line number Diff line change
Expand Up @@ -1276,6 +1276,28 @@ addStealExtension(function(loader) {
};
});

addStealExtension(function(loader){
var mjsExp = /\.mjs$/;
var jsExp = /\.js$/;

var locate = loader.locate;
loader.locate = function(load){
var isMJS = mjsExp.test(load.name);
var p = locate.apply(this, arguments);

if(isMJS) {
return Promise.resolve(p).then(function(address) {
if(jsExp.test(address)) {
return address.substr(0, address.length - 3);
}
return address;
});
}

return p;
};
});

addStealExtension(function applyTraceExtension(loader) {
if(loader._extensions) {
loader._extensions.push(applyTraceExtension);
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"http-server": "^0.10.0",
"jquery": "^3.1.1",
"live-reload-testing": "^6.0.0",
"nock": "^9.4.3",
"qunitjs": "^2.3.0",
"regenerator-runtime": "^0.10.3",
"saucelabs": "^1.3.0",
Expand Down
21 changes: 21 additions & 0 deletions src/extension-mjs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
addStealExtension(function(loader){
var mjsExp = /\.mjs$/;
var jsExp = /\.js$/;

var locate = loader.locate;
loader.locate = function(load){
var isMJS = mjsExp.test(load.name);
var p = locate.apply(this, arguments);

if(isMJS) {
return Promise.resolve(p).then(function(address) {
if(jsExp.test(address)) {
return address.substr(0, address.length - 3);
}
return address;
});
}

return p;
};
});
71 changes: 49 additions & 22 deletions src/loader/lib/system.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,16 @@
});
return output.join('').replace(/^\//, input.charAt(0) === '/' ? '/' : '');
}
var doubleSlash = /^\/\//;
function toAbsoluteURL(inBase, inHref) {
var href = inHref;
var base = inBase

if(doubleSlash.test(inHref)) {
// Default to http
return 'http:' + inHref;
}

if (isWindows)
href = href.replace(/\\/g, '/');

Expand Down Expand Up @@ -115,28 +121,49 @@
}
}
else if (typeof require != 'undefined') {
var fs, fourOhFourFS = /ENOENT/;
var fs, http, https, fourOhFourFS = /ENOENT/;
fetchTextFromURL = function(rawUrl, fulfill, reject) {
if (rawUrl.substr(0, 5) != 'file:')
throw 'Only file URLs of the form file: allowed running in Node.';
fs = fs || require('fs');
var url = rawUrl.substr(5);
if (isWindows)
url = url.replace(/\//g, '\\');
return fs.readFile(url, function(err, data) {
if (err) {
// Mark this error as a 404, so that the npm extension
// will know to retry.
if(fourOhFourFS.test(err.message)) {
err.statusCode = 404;
err.url = rawUrl;
}
if (rawUrl.substr(0, 5) === 'file:') {
fs = fs || require('fs');
var url = rawUrl.substr(5);
if (isWindows)
url = url.replace(/\//g, '\\');
return fs.readFile(url, function(err, data) {
if (err) {
// Mark this error as a 404, so that the npm extension
// will know to retry.
if(fourOhFourFS.test(err.message)) {
err.statusCode = 404;
err.url = rawUrl;
}

return reject(err);
} else {
fulfill(data + '');
}
});
return reject(err);
} else {
fulfill(data + '');
}
});
} else if(rawUrl.substr(0, 4) === 'http') {
var h;
if(rawUrl.substr(0, 6) === 'https:') {
h = https = https || require('https');
} else {
h = http = http || require('http');
}
return h.get(rawUrl, function(res) {
if(res.statusCode !== 200) {
reject(new Error('Request failed. Status: ' + res.statusCode));
} else {
var rawData = "";
res.setEncoding("utf8");
res.on("data", function(chunk) {
rawData += chunk;
});
res.on("end", function(){
fulfill(rawData);
});
}
})
}
}
}
else if(typeof fetch === 'function') {
Expand Down Expand Up @@ -255,11 +282,11 @@
dotdots = i;
}

for (var j = i; j < segments.length; j++) {
/*for (var j = i; j < segments.length; j++) {
var segment = segments[j];
if (segment == '' || segment == '.' || segment == '..')
throw new TypeError('Illegal module name "' + name + '"');
}
}*/

if (!rel)
return name;
Expand Down
72 changes: 50 additions & 22 deletions src/loader/loader-with-promises.js
Original file line number Diff line number Diff line change
Expand Up @@ -3209,10 +3209,17 @@ function logloads(loads) {
return output.join('').replace(/^\//, input.charAt(0) === '/' ? '/' : '');
}

var doubleSlash = /^\/\//;

function toAbsoluteURL(inBase, inHref) {
var href = inHref;
var base = inBase

if(doubleSlash.test(inHref)) {
// Default to http
return 'http:' + inHref;
}

if (isWindows)
href = href.replace(/\\/g, '/');

Expand Down Expand Up @@ -3283,28 +3290,49 @@ function logloads(loads) {
}
}
else if (typeof require != 'undefined') {
var fs, fourOhFourFS = /ENOENT/;
var fs, http, https, fourOhFourFS = /ENOENT/;
fetchTextFromURL = function(rawUrl, fulfill, reject) {
if (rawUrl.substr(0, 5) != 'file:')
throw 'Only file URLs of the form file: allowed running in Node.';
fs = fs || require('fs');
var url = rawUrl.substr(5);
if (isWindows)
url = url.replace(/\//g, '\\');
return fs.readFile(url, function(err, data) {
if (err) {
// Mark this error as a 404, so that the npm extension
// will know to retry.
if(fourOhFourFS.test(err.message)) {
err.statusCode = 404;
err.url = rawUrl;
}
if (rawUrl.substr(0, 5) === 'file:') {
fs = fs || require('fs');
var url = rawUrl.substr(5);
if (isWindows)
url = url.replace(/\//g, '\\');
return fs.readFile(url, function(err, data) {
if (err) {
// Mark this error as a 404, so that the npm extension
// will know to retry.
if(fourOhFourFS.test(err.message)) {
err.statusCode = 404;
err.url = rawUrl;
}

return reject(err);
} else {
fulfill(data + '');
}
});
return reject(err);
} else {
fulfill(data + '');
}
});
} else if(rawUrl.substr(0, 4) === 'http') {
var h;
if(rawUrl.substr(0, 6) === 'https:') {
h = https = https || require('https');
} else {
h = http = http || require('http');
}
return h.get(rawUrl, function(res) {
if(res.statusCode !== 200) {
reject(new Error('Request failed. Status: ' + res.statusCode));
} else {
var rawData = "";
res.setEncoding("utf8");
res.on("data", function(chunk) {
rawData += chunk;
});
res.on("end", function(){
fulfill(rawData);
});
}
})
}
}
}
else if(typeof fetch === 'function') {
Expand Down Expand Up @@ -3437,11 +3465,11 @@ function logloads(loads) {
dotdots = i;
}

for (var j = i; j < segments.length; j++) {
/*for (var j = i; j < segments.length; j++) {
var segment = segments[j];
if (segment == '' || segment == '.' || segment == '..')
throw new TypeError('Illegal module name "' + name + '"');
}
}*/

if (!rel)
return name;
Expand Down
72 changes: 50 additions & 22 deletions src/loader/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -1939,10 +1939,17 @@ function logloads(loads) {
return output.join('').replace(/^\//, input.charAt(0) === '/' ? '/' : '');
}

var doubleSlash = /^\/\//;

function toAbsoluteURL(inBase, inHref) {
var href = inHref;
var base = inBase

if(doubleSlash.test(inHref)) {
// Default to http
return 'http:' + inHref;
}

if (isWindows)
href = href.replace(/\\/g, '/');

Expand Down Expand Up @@ -2013,28 +2020,49 @@ function logloads(loads) {
}
}
else if (typeof require != 'undefined') {
var fs, fourOhFourFS = /ENOENT/;
var fs, http, https, fourOhFourFS = /ENOENT/;
fetchTextFromURL = function(rawUrl, fulfill, reject) {
if (rawUrl.substr(0, 5) != 'file:')
throw 'Only file URLs of the form file: allowed running in Node.';
fs = fs || require('fs');
var url = rawUrl.substr(5);
if (isWindows)
url = url.replace(/\//g, '\\');
return fs.readFile(url, function(err, data) {
if (err) {
// Mark this error as a 404, so that the npm extension
// will know to retry.
if(fourOhFourFS.test(err.message)) {
err.statusCode = 404;
err.url = rawUrl;
}
if (rawUrl.substr(0, 5) === 'file:') {
fs = fs || require('fs');
var url = rawUrl.substr(5);
if (isWindows)
url = url.replace(/\//g, '\\');
return fs.readFile(url, function(err, data) {
if (err) {
// Mark this error as a 404, so that the npm extension
// will know to retry.
if(fourOhFourFS.test(err.message)) {
err.statusCode = 404;
err.url = rawUrl;
}

return reject(err);
} else {
fulfill(data + '');
}
});
return reject(err);
} else {
fulfill(data + '');
}
});
} else if(rawUrl.substr(0, 4) === 'http') {
var h;
if(rawUrl.substr(0, 6) === 'https:') {
h = https = https || require('https');
} else {
h = http = http || require('http');
}
return h.get(rawUrl, function(res) {
if(res.statusCode !== 200) {
reject(new Error('Request failed. Status: ' + res.statusCode));
} else {
var rawData = "";
res.setEncoding("utf8");
res.on("data", function(chunk) {
rawData += chunk;
});
res.on("end", function(){
fulfill(rawData);
});
}
})
}
}
}
else if(typeof fetch === 'function') {
Expand Down Expand Up @@ -2167,11 +2195,11 @@ function logloads(loads) {
dotdots = i;
}

for (var j = i; j < segments.length; j++) {
/*for (var j = i; j < segments.length; j++) {
var segment = segments[j];
if (segment == '' || segment == '.' || segment == '..')
throw new TypeError('Illegal module name "' + name + '"');
}
}*/

if (!rel)
return name;
Expand Down
Loading

0 comments on commit 80e1242

Please sign in to comment.