Specifiers
I have been trying to find a good way to paraphrase intuitive concerns regarding scheme-less specifiers and I think that while import:
is different from other ideas proposed for somewhat related purposes, it may be coming from similar but more tangible consideration.
Yesterday I did a little experiment with URL in node, Chrome, and Safari , and Firefox. My general thought is that new URL(x, y)
is a resolution formula that follows closest to the web standards so my assumption is that it pairs well with the whatwg expectations on specifiers.
The bottom line is that constructing scheme-less specifiers that depend on scheme-less referrers is not possible, and the hack used just to get through the list of valid specifiers was to resolve the referrer against file://y:/
which is not a proposed solution by all means.
Given the importance of specifier resolution (ie x -> y = z
) I am really not comfortable with writing this off to the general sentiment that "there is a package that does that already". (Sorry, nothing against anyone's packages)
I needed a way to parse specifiers independent from URL, not as a solution or a package, just to be able to explore my concerns a little further.
So naturally, I spent the rest of that devising the ugliest Regular Expression I have ever seen, and I think that most of them are ugly anyways, but:
const Specifier = /^(?=(\S*(?=,|;)|\S*(\/)|)[^#?,]*|\S*:)(?:((?:blob:|[a-z]+[+])?[a-z]+[^/\s]*?:(?!\d+\/))?(?:\/{2})?((?:(?:[^/,\s]+)?|\b[^/,\s]+)(?=\/[^#?,]+(?:[#?].*)?$))?)?(?:([.]{0,2}\/)?([^\s,#?]* *)(\?[^,#]*?)?(#.*?)?|(?:([a-z]+\/[^,]*),)?(.*))$/i;
Which comes with it's key:
// prettier-ignore
Specifier.parse = string => {
let scope, resource;
const [url, base, separator, scheme, domain, root, path, query, fragment, type, body] =
Specifier.exec(string.replace(/\\/g, '/')) || '';
if (domain || path || root) {
scope = `${scheme ? `${scheme}//` : ''}${domain || ''}${root || ''}` || undefined;
resource = path ? path.replace(/^\//, '') : undefined;
} else if (scheme) {
scope = `${scheme || ''}${type || ''}` || undefined;
resource = body || undefined;
}
// prettier-ignore
return { url, base, separator , scheme, domain, path, root, query, fragment, type, body, scope, resource };
};
So running a little script, I was able to compare the RegExp with the hacky URL approach:
specifier | scope | resource | URL |
---|---|---|---|
b.mjs | b.mjs | b.mjs | |
file:b.mjs | file:// | b.mjs | file:b.mjs |
http:b.mjs | http:// | b.mjs | http:b.mjs |
/b.mjs | / | b.mjs | /b.mjs |
file:/b.mjs | file:/// | b.mjs | file:/b.mjs |
http:/b.mjs | http:/// | b.mjs | http:/b.mjs |
//a/b.mjs | a/ | b.mjs | //a/b.mjs |
file://a/b.mjs | file://a/ | b.mjs | file://a/b.mjs |
http://a/b.mjs | http://a/ | b.mjs | http://a/b.mjs |
//a:/b.mjs | a:/ | b.mjs | //a:/b.mjs |
file://a:/b.mjs | file://a:/ | b.mjs | file://a:/b.mjs |
http://a:/b.mjs | http://a:/ | b.mjs | http://a:/b.mjs |
///a:/b.mjs | / | a:/b.mjs | ///a:/b.mjs |
file:///a:/b.mjs | file:/// | a:/b.mjs | file:///a:/b.mjs |
http:///a:/b.mjs | http:/// | a:/b.mjs | http:///a:/b.mjs |
specifier | scope | resource | URL |
---|---|---|---|
file:/// | file:/// | file:/// | |
file://a/ | file:// | a/ | file://a/ |
file://a:/ | file:// | a:/ | file://a:/ |
file:///a/ | file:/// | a/ | file:///a/ |
file:///m.mjs | file:/// | m.mjs | file:///m.mjs |
file://a/m.mjs | file://a/ | m.mjs | file://a/m.mjs |
file://a:/m.mjs | file://a:/ | m.mjs | file://a:/m.mjs |
file:///a/m.mjs | file:/// | a/m.mjs | file:///a/m.mjs |
localhost:8080/m.mjs | localhost:8080/ | m.mjs | localhost:8080/m.mjs |
https://localhost:8080/m.mjs | https://localhost:8080/ | m.mjs | https://localhost:8080/m.mjs |
[::FFFF:129.144.52.38]:8080/m.mjs | [::FFFF:129.144.52.38]:8080/ | m.mjs | [::FFFF:129.144.52.38]:8080/m.mjs |
https://[::FFFF:129.144.52.38]:8080/m.mjs | https://[::FFFF:129.144.52.38]:8080/ | m.mjs | https://[::ffff:8190:3426]:8080/m.mjs |
specifier | scope | resource | URL |
---|---|---|---|
https://example.com/apples.mjs | https://example.com/ | apples.mjs | https://example.com/apples.mjs |
http:example.com\pears.js | http://example.com/ | pears.js | http:example.com/pears.js |
//example.com/bananas | example.com/ | bananas | //example.com/bananas |
./strawberries.mjs.cgi | ./ | strawberries.mjs.cgi | strawberries.mjs.cgi |
../lychees | ../ | lychees | lychees |
./../../lychees | ./ | ../../lychees | lychees |
./../../lychees/../a.mjs | ./ | ../../lychees/../a.mjs | a.mjs |
/limes.jsx | / | limes.jsx | /limes.jsx |
//limes.jsx | limes.jsx | //limes.jsx | |
data:text/javascript,export default 'grapes'; | data:text/javascript | export default 'grapes'; | data:text/javascript,export%20default%20'grapes'; |
blob:https://whatwg.org/d0360e2f-caee-469f-9a2f-87d5b0456f6f | blob:https://whatwg.org/ | d0360e2f-caee-469f-9a2f-87d5b0456f6f | blob:https://whatwg.org/d0360e2f-caee-469f-9a2f-87d5b0456f6f |
javascript:export default 'artichokes'; | javascript: | export default 'artichokes'; | javascript:export%20default%20'artichokes'; |
data:text/plain,export default 'kale'; | data:text/plain | export default 'kale'; | data:text/plain,export%20default%20'kale'; |
data:application/node+vnd;m,export default 'kale'; | data:application/node+vnd;m | export default 'kale'; | data:application/node+vnd;m,export%20default%20'kale'; |
about:legumes | about:// | legumes | about:legumes |
wss://example.com/celery | wss://example.com/ | celery | wss://example.com/celery |
https://eggplant:b/c | https://eggplant:b/ | c | |
pumpkins.js | pumpkins.js | pumpkins.js | |
.tomato | .tomato | .tomato | |
..zucchini.mjs | ..zucchini.mjs | ..zucchini.mjs | |
.yam.es | .yam.es | .yam.es |