WICG/import-maps

Conditional import map via e.g. location.hostname == "127.0.0.1"

Closed this issue ยท 7 comments

How would conditional importing work? Lets say for local development I wanna use my local server:

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

But for deploying I want to use:

<script type="importmap">
{
  "imports": {
    "lodash": "https://cdn.jsdelivr.net/npm/lodash@4.17.20/lodash.min.js"
  }
}
</script>

Wouldn't it be nice to make it just a pure JS API? Something like ScriptImportRule:

var rule;
if (location.hostname == "127.0.0.1") {
  rule = {
    "imports": {
      "lodash": "/node_modules/lodash-es/lodash.js"
    }
  };
} else {
  rule = {
    "imports": {
      "lodash": "https://cdn.jsdelivr.net/npm/lodash@4.17.20/lodash.min.js"
    }
  };
}
new ScriptImportRule(rule);

IMO that would make it way more powerful/fine-tunable.

This is a pretty succinct solution:

Non-working version referred to below
<script type="text/plain" id="devmap">
{
  "imports": {
    "lodash": "/node_modules/lodash-es/lodash.js"
  }
}
</script>
<script type="text/plain" id="prodmap">
{
  "imports": {
    "lodash": "https://cdn.jsdelivr.net/npm/lodash@4.17.20/lodash.min.js"
  }
}
</script>
<script>
if (location.hostname === '127.0.0.1')
  document.getElementById('devmap').type = 'importmap'
else
  document.getElementById('prodmap').type = 'importmap'
</script>

Update: Working version:

<!doctype html>
<meta charset="utf-8">
<script type="application/json" data-importmap-env="development">
{
  "imports": {
    "main": "/main.dev.js"
  }
}
</script>
<script type="application/json" data-importmap-env="production">
{
  "imports": {
    "main": "/main.prod.js"
  }
}
</script>
<script>
{
  const DEV = (
    location.hostname === '127.0.0.1' ||
    location.hostname === 'localhost' ||
    location.hostname.endsWith('.local')
  )

  window.process = {
    env: {
      NODE_ENV: DEV ? 'development' : 'production'
    }
  }
}
</script>
<script>
{
  const script = document.createElement('script')
  script.type = 'importmap'
  script.textContent = document.querySelector(`[data-importmap-env="${process.env.NODE_ENV}"]`).textContent
  document.head.appendChild(script)
}
</script>
<script type="module">
import 'main'
</script>

Though I agree that this may cause issues with tooling. I'm all for a standard way of doing it.

I think you wanted to write if (location.hostname === '127.0.0.1')

Ah, good catch. Correcting it.

That doesn't even work in latest Chrome and feels like hacky DOM manipulation, while editors can't help with Intellisense:

<script type="text/plain" id="alert">
  alert(123);
</script>
<script>
  document.getElementById('alert').type = 'text/javascript';
</script>

Instead of a simple/streamlined API, which Editors could properly autocomplete:

<script>
  alert(123);
</script>

You just added a bunch of lines to my solution, loosing editor support while calling it succinct and at least one guy is happy about that... and it wouldn't even work.

@kungfooman

perhaps an approach like this will suite you

const element = new document.createElement("script")
element.type = "importmap"
element.textContent = JSON.stringify({
  imports: location.hostname === "127.0.0.1"
    ? {"lodash": "/node_modules/lodash-es/lodash.js"}
    : {"lodash": "https://cdn.jsdelivr.net/npm/lodash@4.17.20/lodash.min.js"}
})

document.head.appendChild(element)

i could imagine wrapping that up as a handy function

import {registerImportMap} from "./toolbox/register-import-map.js"

registerImportMap({
  imports: location.hostname === "127.0.0.1"
    ? {"lodash": "/node_modules/lodash-es/lodash.js"}
    : {"lodash": "https://cdn.jsdelivr.net/npm/lodash@4.17.20/lodash.min.js"}
})

and one could write a typescript interface, to enforce that the import map is reasonably well-formed

it makes some sense to me that dom interaction is necessary here. following the precedent of stylesheets, any addition must be effected through the dom, so that the dom remains the source of truth about the document

Isn't it easier to do this from you server? Simply send a development version of you HTML file (with the dev-importmap) when running locally, and send a different version of the HTML-file in production.

Agreed that server rendering helps a lot here, but many websites host static index.html files in object stores such as s3 + cloudfront, so presumption of ability to server render isn't a good one.