smotaal
11/21/2018 - 8:07 PM

Specifiers

Specifiers

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:

specifierscoperesourceURL
b.mjsb.mjsb.mjs
file:b.mjsfile://b.mjsfile:b.mjs
http:b.mjshttp://b.mjshttp:b.mjs
/b.mjs/b.mjs/b.mjs
file:/b.mjsfile:///b.mjsfile:/b.mjs
http:/b.mjshttp:///b.mjshttp:/b.mjs
//a/b.mjsa/b.mjs//a/b.mjs
file://a/b.mjsfile://a/b.mjsfile://a/b.mjs
http://a/b.mjshttp://a/b.mjshttp://a/b.mjs
//a:/b.mjsa:/b.mjs//a:/b.mjs
file://a:/b.mjsfile://a:/b.mjsfile://a:/b.mjs
http://a:/b.mjshttp://a:/b.mjshttp://a:/b.mjs
///a:/b.mjs/a:/b.mjs///a:/b.mjs
file:///a:/b.mjsfile:///a:/b.mjsfile:///a:/b.mjs
http:///a:/b.mjshttp:///a:/b.mjshttp:///a:/b.mjs
specifierscoperesourceURL
file:///file:///file:///
file://a/file://a/file://a/
file://a:/file://a:/file://a:/
file:///a/file:///a/file:///a/
file:///m.mjsfile:///m.mjsfile:///m.mjs
file://a/m.mjsfile://a/m.mjsfile://a/m.mjs
file://a:/m.mjsfile://a:/m.mjsfile://a:/m.mjs
file:///a/m.mjsfile:///a/m.mjsfile:///a/m.mjs
localhost:8080/m.mjslocalhost:8080/m.mjslocalhost:8080/m.mjs
https://localhost:8080/m.mjshttps://localhost:8080/m.mjshttps://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.mjshttps://[::FFFF:129.144.52.38]:8080/m.mjshttps://[::ffff:8190:3426]:8080/m.mjs
specifierscoperesourceURL
https://example.com/apples.mjshttps://example.com/apples.mjshttps://example.com/apples.mjs
http:example.com\pears.jshttp://example.com/pears.jshttp:example.com/pears.js
//example.com/bananasexample.com/bananas//example.com/bananas
./strawberries.mjs.cgi./strawberries.mjs.cgistrawberries.mjs.cgi
../lychees../lycheeslychees
./../../lychees./../../lycheeslychees
./../../lychees/../a.mjs./../../lychees/../a.mjsa.mjs
/limes.jsx/limes.jsx/limes.jsx
//limes.jsxlimes.jsx//limes.jsx
data:text/javascript,export default 'grapes';data:text/javascriptexport default 'grapes';data:text/javascript,export%20default%20'grapes';
blob:https://whatwg.org/d0360e2f-caee-469f-9a2f-87d5b0456f6fblob:https://whatwg.org/d0360e2f-caee-469f-9a2f-87d5b0456f6fblob: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/plainexport default 'kale';data:text/plain,export%20default%20'kale';
data:application/node+vnd;m,export default 'kale';data:application/node+vnd;mexport default 'kale';data:application/node+vnd;m,export%20default%20'kale';
about:legumesabout://legumesabout:legumes
wss://example.com/celerywss://example.com/celerywss://example.com/celery
https://eggplant:b/chttps://eggplant:b/c
pumpkins.jspumpkins.jspumpkins.js
.tomato.tomato.tomato
..zucchini.mjs..zucchini.mjs..zucchini.mjs
.yam.es.yam.es.yam.es