Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conditional package maps #55

Closed
guybedford opened this issue Aug 27, 2018 · 15 comments
Closed

Conditional package maps #55

guybedford opened this issue Aug 27, 2018 · 15 comments

Comments

@guybedford
Copy link
Collaborator

It could be useful to define "environment variables" which conditionally define resolution paths in package maps.

Something like:

<script>
  let legacy = false;
  if (!featureDetection())
    legacy = true;
  packagemapEnvironment.set('legacy', legacy);
</script>
<script type="packagemap">
{
  "packages": {
    "lodash": {
      "legacy": "/lodash-legacy-browsers.js",
      "default": "/lodash.js"
    }
  }
}
</script>
@littledan
Copy link
Contributor

Seems a little unfortunate if this is a feature that you can only access through legacy scripts forever...

@ljharb
Copy link

ljharb commented Oct 29, 2018

I believe “legacy” is just an example here; you could call it “use cdn” or “in china” or “on vpn” as well.

@styfle
Copy link

styfle commented Oct 29, 2018

The only problem with this example is that everyone would need to run this feature detection script to get full browser support. Maybe the feature detection logic could be shipped with the package since they are already generating two scripts.

<script src="https://unpkg.com/[email protected]/feature-detection.js"></script>
<script>
  let legacy = !__lodashglobalDetectFeatures();
  packagemapEnvironment.set('legacy', legacy);
</script>
<script type="packagemap">
{
  "packages": {
    "lodash": {
      "legacy": "https://unpkg.com/[email protected]/legacy.js",
      "default": "https://unpkg.com/[email protected]/index.js"
    }
  }
}
</script>

Instead of environments, could you dynamically set the packagemap after running feature detection? Something like this:

<script src="https://unpkg.com/[email protected]/feature-detection.js"></script>
<script>
  let legacy = !__lodashglobalDetectFeatures();
  packagemapEnvironment.set('legacy', legacy);
</script>
<script type="packagemap">
if (__lodashglobalDetectFeatures()) {
  return ({
    "packages": {
      "lodash": "https://unpkg.com/[email protected]/index.js"
    }
  });
} else {
  return ({
    "packages": {
      "lodash": "https://unpkg.com/[email protected]/legacy.js"
    }
  });
}
</script>

@littledan
Copy link
Contributor

@ljharb I didn't mean that the name was legacy, I meant, the package-name-map needs to be set up before any <script type=module> is run, so it could only be a plain <script> that sets up these environment variables. If a goal of modules is to entirely subsume scripts, it's a little unfortunate if we're left with a feature of modules that's only accessible through an old-style script.

@ljharb
Copy link

ljharb commented Oct 29, 2018

Ah, thanks for clarifying. Perhaps it could still run first with the Module goal if it didn’t have any imports?

@littledan
Copy link
Contributor

@ljharb Maybe, I dunno if that would cause no-imports modules to be suddenly different timing-wise though.

@ljharb
Copy link

ljharb commented Oct 29, 2018

I was envisioning only allowing a single no-imports Module to run before the package map to be able to set up these kinds of flags.

@littledan
Copy link
Contributor

I don't see how that would make it any more or less OK for it to have different timing (though I don't have any strong opinions here).

@ljharb
Copy link

ljharb commented Oct 29, 2018

Because all other no-imports modules would have unchanged timing - iow, it would just be a special "module that can prime the package map" - we'd probably want to mark it as such so the timing doesn't change by accident.

(Definitely no strong opinions here either, just thinking out loud)

@domenic
Copy link
Collaborator

domenic commented Nov 2, 2018

I don't see why this would need first-class support. Could you not just generate alternate import maps depending on the environment? Ideally server-side, but client-side if you must.

For example, I would write the OP's example (using new import map syntax) as

<script type="importmap">
{
  "imports": {
    "lodash": "/lodash.js"
  }
}
</script>

<script>
if (!featureDetection()) {
  const im = document.createElement('script');
  im.type = 'importmap';
  im.textContent = '{ "imports": { "lodash": "/lodash-legacy-browsers.js" } }';
  document.currentScript.after(im);
}
</script>

Perhaps the best way to resolve this issue is by incorporating such an example into the README?

@littledan
Copy link
Contributor

That makes sense. Your current proto-spec explains the timing for how this works well; I guess I missed that before.

@kenchris
Copy link

kenchris commented Nov 9, 2018

@domenic wasn't it a security feature that only one importmap could exists, and it could not be modified afterwards?

@domenic domenic closed this as completed in 767b0f4 Nov 9, 2018
domenic added a commit that referenced this issue Nov 9, 2018
domenic added a commit that referenced this issue Nov 9, 2018
@sebinsua
Copy link

sebinsua commented May 20, 2020

Is feature detection to dynamically generate an import-map still considered a good solution if you want to point different browsers at packages which only contain syntax that they support?

It seems to be the example that is shown in the README.md, but there was a conversation on Twitter about this, and I was told by @developit that "preloading (both link rel and the preload scanner) becomes impossible since it can't take this information into account". It doesn't seem good if it constrains the ability of an engineer to fix performance problems.

Is that what issues like "Driving work on a preload spec" and "Solving the waterfall problem with depcache" are looking to fix? /cc @guybedford

The other approach that we discussed was using a CDN like Pika by @FredKSchott which uses the User-Agent to optimize for the environment that requests a package (and which will presumably swap to Sec-Ch-UA once Chrome phases out User-Agent).

(Sorry to post in a closed issue. I felt that if it wouldn't work as intended or would cause difficulties then it is good to put this information where people might find it. And that if it is decided that this is something that should be solved by import-maps but isn't we could then open up a separate issue for it.)

@domenic
Copy link
Collaborator

domenic commented May 20, 2020

It depends on your use case. If you can only detect things at runtime, then you don't have much choice but to use runtime feature detection. But of course, if you can detect them earlier, at request time using the user agent headers, then that'll be even better.

@sebinsua
Copy link

sebinsua commented May 20, 2020

If you can only detect things at runtime, then you don't have much choice but to use runtime feature detection. But of course, if you can detect them earlier, at request time using the user agent headers, then that'll be even better.

The advice makes sense but it's also surprising because for a very long time User-Agent sniffing has been considered bad practice and there are a tonne of websites recommending feature detection over it (StackOverflow, MDN, etc).

Taking a read of the ua-client-hints proposal however does show that "Differential Serving" appears to be one of the main use-cases, so I assume whatever made User-Agent flakey and difficult to trust before is no longer the case.

However, Mozilla's current position on the standard is that it's "non-harmful" but not their preference or worth prototyping.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants