WICG / import-maps

How to control the behavior of JavaScript imports
https://html.spec.whatwg.org/multipage/webappapis.html#import-maps
Other
2.69k stars 70 forks source link

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

Closed kungfooman closed 2 years ago

kungfooman commented 3 years ago

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.

adrianhelvik commented 3 years ago

This is a pretty succinct solution:

Non-working version referred to below ```html ```

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.

ert78gb commented 3 years ago

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

adrianhelvik commented 3 years ago

Ah, good catch. Correcting it.

kungfooman commented 3 years ago

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.

chase-moskal commented 3 years ago

@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

MathiasWP commented 3 years ago

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.

joeldenning commented 3 years ago

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.