Skip to content

Instantly share code, notes, and snippets.

@SMotaal
Last active November 22, 2018 10:56
Show Gist options
  • Save SMotaal/7980dafa81675717153b5d76ee9e0b00 to your computer and use it in GitHub Desktop.
Save SMotaal/7980dafa81675717153b5d76ee9e0b00 to your computer and use it in GitHub Desktop.
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:

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment