[HN Gopher] JavaScript import maps are now supported cross-browser
___________________________________________________________________
JavaScript import maps are now supported cross-browser
Author : fagnerbrack
Score : 237 points
Date : 2023-05-03 10:54 UTC (12 hours ago)
(HTM) web link (web.dev)
(TXT) w3m dump (web.dev)
| cientifico wrote:
| This is awesome.
|
| This allows to improve my load times after each deploy by several
| factors.
|
| Be aware that the example that is provided in the article is
| quite dangerous. If for any reason the content of the script is
| altered, the user will execute it anyway.
|
| With normal scripts you have the integrity="sha-..." option that
| secures the content. With importmaps we only have a couple of
| github issues and no solution.
|
| https://developer.mozilla.org/en-US/docs/Web/Security/Subres...
| https://github.com/WICG/import-maps/issues/221
| https://github.com/WICG/import-maps/issues/223
| orra wrote:
| > With normal scripts you have the integrity="sha-..." option
| that secures the content. With importmaps we only have a couple
| of github issues and no solution.
|
| Yeah, I think it's a Bad Thing for security, to release
| importmaps without integrity.
| [deleted]
| SquareWheel wrote:
| At the risk of bikeshedding, what's with the syntax? Would it not
| make sense to stick closer to HTML tags? eg.
| <script type="importmap" name="browser-fs-access"
| src="https://unpkg.com/browser-fs-
| access@0.33.0/dist/index.modern.js">
|
| The current implementation seems like something I'd have to look
| up every time I wanted to use it.
| lucideer wrote:
| This likely stems from the proposers building on existing "in-
| the-wild" development patterns: it's become a convention for
| DSLs in the JS development community.
|
| Consider similar for a larger amount of data:
| <script type="custom-dsl">{ "very": { "large": "json",
| "object": "with", "many": "keys" }</script>
|
| Even sticking strictly to JSON (the DSL can be any syntax),
| this has a few advantages to SGML-style attributes:
|
| 1. Supports nesting to any depth
|
| 2. Easier to deserialize from an in-memory object in the most
| common server-side languages
|
| 3. Supports easy developer/hacker copypasta into a JS file for
| debugging.
|
| 4. Broader/better expand/collapse support in text editors (in
| the case of very large lists)
|
| ---
|
| > _I 'd have to look up every time I wanted to use it_
|
| I'd likely have to look up the HTML attribute names just as
| much - I recall having to do this a lot at first with the
| viewport meta when it was introduced - the only real hurdle to
| get over with this is to internalise the following pattern
| (which, as mentioned, some in the JS community are already
| familiar with): <script type="dsl-name">DSL
| SOURCE</script>
| WorldMaker wrote:
| The current implementation is so that you can `<script
| type="importmap" src="path/to/your/importmap.json"></script>`.
| JSON is a well-known on-disk data format with lots of tools
| that support working with it and manipulating it (including
| plenty JS itself including natively in the browser).
|
| You can even `<script type="importmap"
| src="https://your.fave.cdn.example.org/shared-
| importmap.json"></s...` and share an importmap across multiple
| applications/projects.
| ramses0 wrote:
| It matches exactly the on-disk format for deno.json /
| import_map.json.
|
| Based on my limited experience with it, I was super-stoked to
| see it be basically: "oh, what I was messing around with last
| night when trying out deno!"
| msoad wrote:
| Can we use React with JSX in browser without any compiler using
| this? I'm sure people have tried it but I can't find it. I just
| want an `index.html` file that I can write React code in it and
| play around with things...
| postdb wrote:
| There is the 1k jsx lib: https://nanojsx.io
|
| Then again, if we are at this road now again, maybe they should
| just bring a lighter version of e4x as part of ES/JS.
| c-smile wrote:
| In Sciter I did just that - JSX is an integral part of JS
| compiler - patched version of QuickJS : https://gitlab.com/c-
| smile/quickjspp/-/blob/master/quickjs-j...
|
| So in Sciter this works out of the box:
| <html> <body/> <script>
| function HelloWorld() { return <p>Hello
| <b>world</b></p>; }
| document.body.append(<HelloWorld/>); </script>
| </html>
|
| JSX expressions are compiled to calls of JSX(tag,atts,kids)
| function. That function can be redefined enabling different
| libraries to use JSX in the way they want, here is an example
| of using JSX with PReact: import { h,
| render, Component } from 'preact'; JSX = h; //
| let's use PReact::h() as a driver of JSX expressions
|
| See https://gitlab.com/sciter-engine/sciter-js-
| sdk/-/blob/main/s...
| toastal wrote:
| React is already often slow enough. Pushing compilation steps
| to the client is inefficient, especially when you consider that
| JSX compiles to basic functions and you could have used those
| functions since the first version.
| [deleted]
| anton96 wrote:
| They are many libraries that use tagged template string to
| allow this, the most interesting to my eyes is the one made by
| preact dev. It's just named htm [0], it's made by preact but it
| can totally be used with react.
|
| The most interesting thing of this one is that uses no custom
| syntax for js variable, so @attribute something when you must
| pass an object as props.
|
| 0. https://github.com/developit/htm
| throwaway290 wrote:
| If it doesn't have fragments, how do you implement something
| equivalent to this when mapping over items and every item has
| to output multiple roots? <React.Fragment
| key={idx}>
| anton96 wrote:
| You can import the Fragment component of the React package
| or stack them directly, it works too. Normally, the empty
| bracket syntax should work, they have a few issues closed
| and open that talk about it in their repo.
| hobofan wrote:
| That has been possible for some time with Babel[0] and was also
| referenced in the old React docs[1]. But, as stated there and
| in the sibling comment here, it's not really suitable for
| production use.
|
| [0]: https://babeljs.io/docs/babel-standalone#script-tags
|
| [1]: https://legacy.reactjs.org/docs/add-react-to-a-
| website.html#...
| davnicwil wrote:
| From the react docs: "it makes your website slow and isn't
| suitable for production"
|
| I don't really like blanket statements like this or think
| they are appropriate. It's so counter to the spirit of the
| web.
|
| Like, what does 'slow' mean in an absolute statement like
| this? It depends entirely on the system and usecase. It's
| just code at the end of the day. If it works for you, go for
| it!
| knute wrote:
| It's interesting that this is your perception of "the
| spirit of the web" whereas other people are going to say
| that requiring JavaScript at all is "counter to the spirit
| of the web".
| tolmasky wrote:
| It's really wild seeing the tremendous amount of work being put
| in to ESM just to come up with a solution that still isn't as
| fast, secure, or ergonomic as bundling.
|
| 1. Import maps are basically a compile-time feature. I thought
| the point of all of this was to not have to have toolchains?
|
| 2. Imports _significantly_ lag behind "old school" scripts in
| security. You can't use integrity hashes with them, and yet most
| the examples you see are about importing random scripts from all
| over the web. This is actually hilariously related to point (1),
| since you can only escape the realization that this is a feature
| a compiler is supposed to spit out if you direct the imports to
| some third party resource. So in order to contrive a situation
| where you would manually craft an import map, they are forced to
| show an example that is less secure than the stuff that has
| worked in every browser for 10 years.
|
| 3. Not directly related to import maps, but you'll probably end
| up using them together: in order to attempt to achieve similar
| performance to just bundling your code, the amazing solution
| we've come up with is to... put a <link rel="modulepreload"> tag
| in your head element for each script you want to be fetched
| immediately. Yes, so much better than the ridiculous old method
| of using a script tag for each script. Now you just use a link
| tag for each script instead. I'm sure someone will tell you that
| this is better because you don't _have_ to do that. Yes, yes, so
| much better that the default behavior of script loading is slow
| unless you go read about a bunch of "tips and tricks" to get it
| working with reasonable performance.
|
| It's always great to take one concept like "importing a script"
| and turn it in a an entire ecosystem of terminology that spans
| languages and platforms, custom JavaScript syntax for the imports
| themselves, a set of different HTML tags for actually getting
| them into your page (script, link, etc.), and JSON for mapping
| names to URLs. Great. Super simple.
|
| ESM has always suffered from a weird shifting argument. Whenever
| you point out that the performance and security isn't great, all
| of a sudden it's a developer ergonomics feature, not meant to be
| for production. Then when you point you the ergonomics suck (like
| not being able to do bare imports), you're told you should be
| using it in tandem with a toolchain that auto-generates import
| maps or something. So what's the benefit? I already had a tool
| that could do that, and it worked in every browser and didn't
| ignore the the security advancements we made 10 years ago.
| TimTheTinker wrote:
| The web is a very wide world out there, and companies and
| people have a wide diversity of architectures and methods for
| frontend apps that benefit from import maps.
|
| Consider:
|
| - Many people want to skip bundling altogether for as long as
| is possible. It's really nice to have a build-free (or almost
| build-free) app, especially when you're starting a greenfield
| or personal project. Why get mired down in researching the
| latest third-party build tools before writing code?
|
| - Third-party dependencies can be vendored (included in your
| own deployed assets on a domain you own). This ought to be
| everyone's default - CDNs shouldn't be used in production for
| new projects anyway. If you're loading from your own domain,
| hashing the asset isn't necessary.
|
| - Sometimes you may have dependencies you wrote yourself that
| you'd like to load with an import map.
|
| - Some architectures call for _both_ a build /bundle step and
| an import map. Consider a micro-frontend architecture -- this
| allows many decoupled teams to work on different parts of an
| app that are composed together at runtime. Each MFE is
| built/bundled prior to deployment, but an import map helps the
| browser load each one when it is needed. See https://single-
| spa.js.org/docs/recommended-setup/
|
| > I already had a tool that could do that, and it worked in
| every browser and didn't ignore the the security advancements
| we made 10 years ago.
|
| But that tool (WebPack or RequireJS, right?) had to shim a lot
| of non-native features into the browser runtime, and at build
| time it was slow and very complex since there were so many
| module types that needed to be supported. Modern tooling has
| much less build-time complexity, requires little or no runtime
| module library, and is more performant since it's often written
| in Rust. (See Vite, esbuild, etc.)
|
| Browser features that allow simplifying and/or eliminating
| third-party tools are always a net win, IMO.
| tolmasky wrote:
| Many people want to skip bundling altogether for as long as
| is possible. It's really nice to have a build-free (or almost
| build-free) app, especially when you're starting a greenfield
| or personal project. Why get mired down in researching the
| latest third-party build tools before writing code?
|
| But ESM doesn't actually give you this, this is precisely the
| point I'm making. ESM doesn't become ergonomic without the
| use of some sort of tooling. Perhaps you have your setup that
| just spins something up and auto-compiles an import maps or
| something. Great! But that's not at all the promised dream of
| finally escaping tooling. It's the same thing we had before,
| just outputting different stuff. Different stuff that happens
| to be worse in a lot of ways.
|
| Import maps are a perfect representation of this. It's
| essentially asking you to hand-write a manifest file just to
| be able to type "import 'lodash'". This is not an improvement
| or simplification to the web ecosystem. In my experience, the
| people who are actually using any of this stuff, are doing so
| through some build pipeline. And again, this is unfortunate
| since no one should be shipping this to production since it
| is both slower and less secure.
| WorldMaker wrote:
| Handwriting an importmap is no worse than handwriting a
| stack of `<script src="https://some.cdn.somewhere.example.c
| om/jquery"></script>` global imports, _but_ has the
| advantage that importmaps are lazy (aren 't used until
| import time) and don't pollute the global namespace.
|
| Just like the bad old jQuery days maybe you'll see a
| proliferation of importmap CDN lines to just copy/paste
| manually in READMEs, no need for further tooling than your
| OS clipboard.
|
| Some CDNs are even building themselves to be _easy_ to
| handwrite into importmaps (https://esm.sh/ for one example
| mentioned in other threads around here).
|
| It gets complicated again if you don't want to rely on CDNs
| and prefer to have local copies, but that's always been the
| package management game in a nutshell (and before there was
| npm there was the much simpler bower and there are already
| tools trying to be ESM "bower").
|
| (ETA: You can even avoid some handwriting by sharing
| importmaps, such as something like `<script
| type="importmap"
| src="https://your.fave.cdn.example.org/shared-react-
| importmap.jso...`)
| guilherme-puida wrote:
| > ... and is more performant, since it's often written in
| Rust. (See Vite, esbuild, etc.)
|
| Minor nitpick, but Esbuild is written in Go, not Rust. Vite
| also uses Esbuild behind the scenes.
|
| If I'm not mistaken, SWC is the one built in Rust.
| myhf wrote:
| There are obvious benefits to having the granularity of loading
| and caching script files match the granularity of how those
| files are used on different pages or updated at different
| times.
|
| Bundling everything is fine for most apps, but sometimes you
| have a need for code splitting, or sharing libraries between
| scripts from different origins, or some mix of preloading and
| dynamic loading. So it is nice to have a standard for these
| things.
| MatthewPhillips wrote:
| You really don't have to worry about these things until your
| module graph is > 100 modules. You are falling into "all or
| nothing" thinking here, because you need tools in some use-
| cases doesn't mean that a feature is worthless. Not needing
| tools for small projects and then needing them for large
| projects is pretty good tradeoff.
| tolmasky wrote:
| I'm not sure what you mean by all or nothing thinking here.
| The problem with imports is precisely that it is incredibly
| cumbersome to use without a bunch of additional "add-ons".
| You can't just "use a little bit of it." Want to do the
| supremely simple task of importing a JSON file? Better make
| sure your browser supports import assertions (assuming one
| even knows these exist). Want to not have to import a long
| incomprehensible URL? Better write a manifest file called an
| import map that only recently got full browser support. You
| can't even trivially add a script tag with an ESM module
| because it won't work from a file:// domain, you have to
| start up a simple server for your simple.html file. So I have
| no idea where this notion of not needing tools comes from. I
| think what people actually mean is "my tools support ESM out
| of the box". It is not trivial to get a nice experience with
| zero tooling. Again, you at minimum need to run a server (and
| I believe you have to fiddle with the MIME types for python's
| one line simple server too, although perhaps that has now
| been rectified). The simplest projects require strictly more
| tools to use this thing. And on the opposite end of the
| scale, it _appears_ that you agree that of course you will
| use tooling. Only hopefully those tools aren 't generating
| imports but just bundling because if not they are creating
| slower and less secure resources.
|
| ESM imports have fallen into the C++ trap: every year the
| determination is made that "if we just add these 5 extra
| features (both to the the ECMAScript spec and W3C spec), then
| they'll finally be great out of the box!" This is how we
| ended up with import.meta and dynamic import and import maps
| and import assertions and preloadmodule link tags, which are
| all still less flexible than require/bundler, and somehow
| _still_ don 't have any answer whatsoever to integrity
| checksums that originally shipped in 2015. It's absurd.
|
| To be clear, I am not saying there shouldn't have been "an"
| import spec. I am saying this one is tragically flawed and
| remains realistically a bad option (unless you are using a
| compiler that is just going to turn it into roughly the same
| minified mumbo jumbo it was turning your requires into
| before). This is fairly well accepted at TC39. The spec was
| rushed. Nothing would be allowed to ship with that level of
| consideration anymore. Usually someone at this point asks
| "well what would you have done?!". I would have punted on
| import until _after_ async /await. Many of the most
| fundamental issues in imports come from having to have
| conceived them in a pre-async/await world. They are
| essentially a "top-level-await-ish" statement, in a world
| with async/await. However, had async/await and top-level-
| await shipped first, and _then_ import was introduced, you
| could have started with the expression form of import (await
| import()), which would not have required any new syntax (you
| could just use destructuring, the same way you do now), and
| not required a "exercise left to the host environment" hand-
| waivy async-link resolution system, but instead used the
| actual existing semantic of the language. We ended up having
| to do it anyways with dynamic import. It is _so weird_ that
| `x as y` becomes `{ x: y }` and `x` becomes `{ default: x }`
| merely by moving the entrypoint around in the file. It would
| also be a lot more transparent to the user: by typing `const
| x = await import( "blah")`, you aren't (erroneously) lead to
| believe that this is a synchronous action when it isn't. It's
| also way easier to add additional features to a function
| (such as integrity hashes or assertions or whatever), vs. in
| a statement where it requires a parser level change, and for
| no real benefit other than causing people to have to learn a
| bunch of weird new syntax that increasingly feels out of
| place.
| MatthewPhillips wrote:
| > I'm not sure what you mean by all or nothing thinking
| here.
|
| I mean this:
|
| > Want to do the supremely simple task of importing a JSON
| file?
|
| Surely you realize that "importing JSON" is not something
| every project or page needs. By "all or nothing thinking" I
| mean the idea, which you expand in this response, that
| unless it supports _everything_ it 's not worth ever using.
| But that's a false dichotomy. A lot of projects are simple.
| A lot don't need all of these advanced bundler features.
| Those projects can (and do) use bare ESM in the browser.
| And that's ok.
|
| To answer your question more directly, the reason they do
| it this way is because bundling dozens of features into a
| spec and then releasing them all at once has never worked
| in the history of web standards. For better or worse, doing
| one small thing at a time is what works.
| WorldMaker wrote:
| > You can't even trivially add a script tag with an ESM
| module because it won't work from a file:// domain, you
| have to start up a simple server for your simple.html file.
|
| This isn't an ESM problem. Last I checked this is still a
| per-browser security decision that applies to _all_ JS, not
| just ESM, because browsers fear they can 't trust malware
| or adware not to spread if file: were too open. (Thanks to
| such things in the IE days existing.) Too many developers
| are in Chrome 24/7 and forget that Firefox still exists and
| has good development tools _and_ that Firefox allows file:
| // origin for scripts _still_ in 2023 (with some caveats,
| but fewer than "doesn't run at all" as has been the
| Chromium default for a long time).
|
| Also, I recall there are command line flags in Chrome to
| allow it if you really do want to use Chrome for
| development and can't just spin up a simple server on
| localhost.
| tolmasky wrote:
| It is an ESM problem, since it works just fine with non-
| ESM script tags. ESM isn't some theoretical thing, it's
| the reality of how it works, in particular when we are
| specifically discussing the practical reality of using
| it. And the fact is that it absolutely is more cumbersome
| than the old technologies (and more cumbersome than
| alternative approaches that could have been taken). The
| fact that we're now talking about launching Chrome with
| special command line flags demonstrates this. I mean, the
| rest of my comment that delves into all the other
| problems proves it even more, but the fact that it fails
| the bare minimum requirement of just loading when you
| drag and drop a file into the browser is fairly bad.
| WorldMaker wrote:
| > It is an ESM problem, since it works just fine with
| non-ESM script tags.
|
| This is not my experience. I've had many cases where
| Chrome security blocked non-ESM script tags to file: URLs
| (that Firefox was much more fine with).
|
| The security mechanisms in the browser have gotten
| extremely sensitive (and rather baroque) and blocking
| file: by default is a part of that. This isn't an ESM-
| specific thing _in any way_ , this is an overall browser
| security thing. It is a response to the arms race of
| malware.
|
| People that have been writing/developing apps and scripts
| _designed_ for local use (such as Twine) have been
| complaining about Chrome security defaults _for years
| now_ for non-ESM development. "Drag-and-drop" of Twine
| games is generally only _reliable_ on Firefox. There are
| so many obscure Chrome security issues that can break
| such games. This is not a new complaint for ESM.
|
| (I didn't break down the "problems" with the rest of your
| comment because it was a bit of a rant and a lot of it
| felt to me more like opinions and bike shedding rather
| than "problems". I didn't think I can convince you that
| ESMs are good and wasn't going to try. I can point out
| the facts that script loading from local files _is a
| Chrome [and IE] problem_ more than an ESM problem,
| because the facts back that up.)
| MatthewPhillips wrote:
| If you expect to be able to develop with the file
| protocol you're going to be disappointed. Essentially all
| modern features don't work with it; not just ESM. You
| can't use Service Workers with the file protocol either.
| CraigJPerry wrote:
| Does this mean that in theory i could skip the build/bundling
| step entirely?
|
| E.g. i could have a backend project in whatever language i wanted
| but then instead of having an npm frontend project, instead i
| could: 1. use a jsconfig.json and jsdoc to
| gradually type with typescript but without need for tsc since my
| editor gives me feedback anyway 2. use es modules to
| handle splitting and caching the various .js files of my app
| 3. use this import maps feature so that i can still gzip and add
| the digest of the file to the filename so that i can safely say
| cache every .js forever since if theres a change, the filename
| will differ and this feature means i can still import from
| friendlyname rather than friendlyname-hashed-value
|
| What am i missing from the bundle approach:
|
| Optimal file transfer - all in one js bundle, but if i have http2
| then i don't care because the overhead of multiple files is gone
|
| minify - i don't care since i gzip anyway
|
| sourcemaps - i dont care since the files presented to the browser
| are the ones i wrote, maps are redundant
|
| I quite like react but i think i'd like simplifying my tool chain
| more and using something like htmx instead.
|
| EDIT: i want to qualify the "i quite like react" statement, i
| have pure components only, no useState, for state handling in the
| small i'm fine with pure useReducer's but zustand is nicer for my
| cases since i can also use that to prevent subtree re-renders of
| the reducer/context barebones approach.
| slinger wrote:
| I think you're right but if you need treeshaking, some sorting
| of pack (minification of variable names, etc) or jsx you would
| still need a build step. I don't know if treeshaking and pack
| are that relevant for most people tho
| WorldMaker wrote:
| If you _aren 't_ packing anything the browser natively
| treeshakes ESM for you. It doesn't load modules that aren't
| imported, for one obvious thing. In a "loose" package of ESM
| modules the browser may never even see most of the tree.
|
| Beyond that ESM "module objects" (the proxy that the import
| represents and is most directly seen in an `import * as
| someModule` style import) in most of the browsers are also
| built to be readonly treeshakeable things in that they may
| not be immediately JIT compiled and are generally subject to
| garbage collection rules like everything else, and being
| readonly proxies may garbage collect sooner than average. So
| the browser will lazily "treeshake" by garbage collection at
| runtime in a running app. (Though if you are relying heavily
| on that it likely means your modules are too big and you
| should consider smaller, looser, less packed modules and
| relying on the first bit where stuff not imported is never
| seen by the browser.)
| [deleted]
| SahAssar wrote:
| > all in one js bundle, but if i have http2 then i don't care
| because the overhead of multiple files is gone
|
| I think everyone who makes this statement has not tried this.
| While it is somewhat true for A->B, A->C if not using
| compression it is definitely not true for A->B, B->C. The
| deeper your chain is (regardless of if it is within the same
| package or within different) the overhead of fetching, parsing,
| building dependency map, fetching dependencies, repeat is
| pretty huge.
|
| I say this as someone that has deployed both approaches at
| scale and A/B tested them.
|
| The benefits of compression should not be understated either.
| You probably have many repeated things in your files that can
| be efficiently compressed. Even small stuff like `<div` or
| `useState` can make a huge difference when you consider them
| over a larger codebase. This part could have been fixed with
| SDCH, but only chrome and linkedin seemed to care about that
| before it was deprecated and removed.
| thrownaway561 wrote:
| the only thing missing is bundling CSS files. Rails has
| defaulted now to "pinning" javascript however, any css that
| maybe bundled with the package still presents an issue.
| madeofpalk wrote:
| _> instead of having an npm frontend project_
|
| You probably still want to use a dependency manager (npm, yarn,
| etc) to pull in and own host your own dependencies. You don't
| want to pull your dependencies from an external source as that
| will only ever make your site less reliable.
|
| _> use es modules to handle splitting and caching the various
| .js files of my app_
|
| Yeah, I was surprised at how well es modules just worked when i
| put together this tiny repro https://idb-perf-
| repro.netlify.app/ (index.js imports two relative files)
|
| _> use this import maps feature so that i can [...] add the
| digest of the file to the filename_
|
| So you've still got build tooling and a layer of indirection.
| Maybe this is simpler, but you'll still need tooling to manage
| hashing files + creating import maps. I don't think import maps
| really gives you anything here, because if you're renaming all
| your files on deploy you may as well just update the imports as
| well, rather than using import map.
|
| _> Optimal file transfer - all in one js bundle, but if i have
| http2 then i don't care because the overhead of multiple files
| is gone_
|
| Test and validate this. Does HTTP2 really remove all overhead
| from requesting 100s of files vs 1 large file? I don't think
| this is the case.
|
| _> but i think i'd like simplifying my tool chain more and
| using something like htmx instead._
|
| Simplifying your toolchain by removing type safety from your
| views.
| CraigJPerry wrote:
| Hey thanks for this, there was a bunch of useful stuff here.
|
| >> Simplifying your toolchain by removing type safety from
| your views.
|
| And poorer testing. Haven't figured out how to get a nice
| testing setup that i like with htmx.
| e1g wrote:
| You still need a bundler because browsers will process only ~6
| HTTP requests at a time, so if your code (with all
| dependencies) has many JS files you will be throttled by that
| limit real quick. HTTP2/3 makes parallel fetching more
| efficient over the wire but does not change the limit of max
| concurrency imposed by the browser.
| matsemann wrote:
| I actually think the main issue isn't number of requests, but
| that you can't know which additional files you need to load
| before loading some of them. Aka if you have a moduleA
| depending on moduleB depending on moduleC. Only after
| downloading moduleB will you know that you have to download
| moduleC as well. So with a deep tree this quickly becomes
| very slow?
| rektide wrote:
| This is the problem Preload headers & Early Hints are meant
| to help with. https://web.dev/preload-critical-assets/
| https://developer.chrome.com/blog/early-hints/
|
| You need some server-side intelligence to analyze each
| module & determine what preload headers to send. But then
| the browser knows what to request, even before content
| starts coming.
| Leherenn wrote:
| Also, you would need as many roundtrips as your dependency
| depth, no? No matter how parallel it is.
| deathanatos wrote:
| > _HTTP2 /3 [...] but does not change the limit of max
| concurrency imposed by the browser._
|
| No. HTTP/2 is allowed far more than 6 requests at a time;
| within a single connection it's limited by the max concurrent
| streams setting in the SETTINGS frame and the browser's
| willingness to take advantage of it; AIUI, e.g., in Firefox,
| that limit is 100.[1]
|
| From there, you're limited by connection bandwidth and any
| brief HoL blocking caused by dropped packets (but _not_ by
| HoL blocking caused at the server).
|
| [1]: https://stackoverflow.com/questions/36835972/is-the-per-
| host...
| e1g wrote:
| You might be right, and my initial assessment is incorrect.
| The real reason why HTTP2 doesn't solve the loading problem
| with many files is the _depth_ of imports across all
| dependencies - the browser loads the entry file, sees its
| imports, fetches those URLs, then discovers new imports,
| starts fetching those, discovers more, etc recursively. So
| the slowness is caused by the latency of each round trip
| (easily 50ms-500ms), and not by how many files the browser
| has in-flight simultaneously, as I assumed.
| CraigJPerry wrote:
| With HTTP2 would we not use 1 request / stream but with
| multiplexing to allow a huge number of parallel requests?
| e1g wrote:
| HTTP2 improves on that bottleneck but not as much as
| expected. I'm struggling to find relevant benchmarks now,
| but anecdotally even on localhost when using a dev pipeline
| without a bundler (such as Vite), any reasonably complex
| application takes many seconds to fetch thousands of small
| JS files.
| rscrawfo wrote:
| This is something I'm facing now. Even hundreds of files
| slow things down. Code splitting is the answer, but that
| adds some other complexity that we may not want.
| gondaloof wrote:
| > the overhead of multiple files is gone
|
| Unfortunately not. Gzip is applied per-file and multiple small
| zips aren't as compressed as a single zip.
|
| Additionally, you get a cascade of downloads if you have
| multiple levels of imports, so it will download a file, parse
| it, download the its dependencies, etc.
|
| Now this may not be a big deal in some cases, but the overhead
| is still not _gone._
|
| Side note: server push is gone so there's no way to avoid the
| cascade.
|
| > minify - i don't care since i gzip anyway
|
| That's not how it works. The two things are complementary.
| Minification can drop a lot of dead code and comments, gzipping
| alone won't do that.
| thayne wrote:
| Minification can also speed up parsing the code, since there
| is less code to parse.
| aseipp wrote:
| Frankly in the particular case where you have cascading
| downloads of small files, HTTP/1 is so unbelievably bad at
| that compared to HTTP/2 (especially in cases where the user-
| agent throttles TCP connections, like the limits of ~6 in a
| browser) that the "overhead" argument isn't really relevant
| because it might imply they're roughly comparable in some
| way. They aren't, in my experience, it's more like "Does it
| work" versus "Does it not work." I'm talking like one-or-two
| orders of magnitude in performance difference, in practice,
| in my cases.
|
| Server push wasn't ever going to help use cases like this
| because pushing those files doesn't actually work very well;
| only the client knows about the state of its own cache (i.e.
| the server will aggressively push things even when the client
| doesn't need them). I tried making it work in some cases very
| similar to the "recursively download based on import
| structure" problem, and it was always just a complete wash in
| practice.
|
| 103 Early Hints are a better solution for that class of
| problem where the server has some knowledge over the
| request/response pattern and the "structure" of the objects
| its serving for download. They're also easier to support and
| implement, anyway.
| flanbiscuit wrote:
| If you like React but also don't want a build step, take a look
| at Preact (only 3kb gzipped) + HTM, their alternative to JSX
| that uses tagged template literals, so it works in the browser
| (but also can be compiled away in a build step)
|
| https://preactjs.com/guide/v10/getting-started#alternatives-...
|
| https://github.com/developit/htm
| ilrwbwrkhv wrote:
| Or mithril. People sleep on it but it's an amazing little
| library with a router.
| spankalee wrote:
| Lit also requires no build step and is shipped only as
| standard JS modules. It also uses file extensions in all
| imports so that the required import map to access all files
| is very short (one index map + one prefix map * 4 core
| packages). See https://lit.dev
| joduplessis wrote:
| Preact is absolutely amazing. +1 from me - I dropped like
| 100kb (on a very bandwidth constraint project) by using
| Preact.
| javajosh wrote:
| _> Does this mean that in theory i could skip the
| build/bundling step entirely?_
|
| You can but you must write your app in something the browser
| understands (js not ts, css not sass etc) and use native
| modules. For example, here is the test harness for a custom
| module, written in pure html with no build step: https://github
| .com/javajosh/simpatico/blob/master/combine2.h.... Here is a
| more complex (and much older) example from Crockford:
| https://www.jslint.com/
|
| And yes, the experience developing this way is quite nice!
| Having the code in the browser be the code you wrote is...so
| refreshing. I highly recommend it.
| EMM_386 wrote:
| > You can but you must write your app in something the
| browser understands. And yes, the experience developing this
| way is quite nice! Having the code in the browser be the code
| you wrote is...so refreshing.
|
| This just reminds me how quickly the years pass. It's weird
| for me to think that developers may have never really worked
| on anything without a build step.
|
| They may not fully understand how the only things the browser
| only understands are HTML, JS and CSS (yes, and some other
| stuff, but the big 3).
|
| Not TS, JSX, SASS, etc. Which is very strange, but I know it
| can happen ... because I personally know someone I had to
| explain this to, _after_ a career change and a React-focused
| bootcamp.
|
| My first major project that used JavaScript was in 1996, so
| that it probably why. JavaScript back then was a bit
| "primitive". I remember too many years of the abject pain.
| Way too many. Even the next 15 weren't that great until ES6
| arrived.
|
| Now I'll take TypeScript, a build step, and the resulting
| tree-shaken, minified, transpiled result any day.
|
| To me, _this_ is refreshing. :)
| javajosh wrote:
| The browsers have been very good incorporating ideas from
| the community into the browser, and strengthening the
| standards every time. Things started getting really good
| with ES6, HTML5 and CSS3, and when IE went entirely away,
| and most browsers are evergreen, it's actually a much
| different universe now.
|
| Apart from being pleasant and fast to work with, the
| benefit of coding without a build step is to the community,
| as it allows us to learn from each other's code, as in the
| early days.
| jameshart wrote:
| You can actually load babel into the browser and run it there
| if you want to deliver your script in a language other than
| js. I wrote this jsfiddle not that long ago as proof you can
| write JSX direct in the page in a <script type="text/jsx">
| element: https://jsfiddle.net/smLa1bco/
| toastal wrote:
| Reminder: vendor your imported scripts if you're doing this. I
| shouldn't have to trust or give data to a CDN to use your
| website. There isn't a meaningful performance win either since
| browsers now cache per domain for privacy reasons.
|
| https://httptoolkit.com/blog/public-cdn-risks/
| ddalex wrote:
| What does it mean to vendor the script?
| lelandfe wrote:
| They just mean "load your JS from your own site, not some
| other person's site"
| bheadmaster wrote:
| I assume it means the same thing as to vendor anything else:
| to bundle dependencies as a part of your application instead
| of giving users "links" to the dependencies served by a third
| party.
| hnbad wrote:
| To elaborate on the more pragmatic answers: vendoring
| generally refers to adding the source code of a dependency to
| your source control repository rather than having an install
| step (or expecting libraries to be provided by system level
| package managers) as part of your development process.
|
| In JavaScript these days, dependencies are usually loaded
| from npm so avoiding the bundling step for client-side code
| would naively mean replacing those dependencies with links to
| a third-party service like unpkg, which serves code published
| to npm for the browser.
|
| As others have explained, vendoring in this case would mean
| shipping those dependencies as part of your application code
| so they are served from your own domain. Of course at the
| moment a lot of dependencies will likely simply not work if
| imported directly from the browser.
| benatkin wrote:
| To clarify, it isn't vendoring to have an external script
| in your build output. Just because it's technical jargon
| doesn't mean there can't be a clear meaning of it.
| Karellen wrote:
| https://stackoverflow.com/questions/26217488/what-is-
| vendori...
| qbasic_forever wrote:
| You can specify a hash for your CDN hosted dependencies so they
| aren't changed unexpectedly: https://developer.mozilla.org/en-
| US/docs/Web/Security/Subres...
|
| There's zero reason not to trust a CDN if you're using
| integrity checks. It's just a host on the internet sending you
| bits.
| cormacrelf wrote:
| It almost but not quite goes without saying, but if the
| import is (for example) the YouTube embed client script, you
| should not put an integrity check on it. Because you expect
| YouTube to put out a new client script from time to time. If
| there's an integrity check based on an old version of the
| script, it will just break your page.
| lelandfe wrote:
| > There isn't a meaningful performance win
|
| And is usually a significant performance _loss_ , since
| starting a new connection to the CDN domain is quite expensive
| during first page load.
| LoganDark wrote:
| > vendor your imported scripts if you're doing this
|
| FTFY
|
| I vendor everything my webpages need, better to ensure that the
| webpage always works as long as my server is up. Also means I
| can download the page and use it offline.
| jFriedensreich wrote:
| thats really cool but this will only be over when >external<
| importmaps and decent browser dev tool support is there too.
| inlining is a pain in most setups and when things don't work as
| expected its harder than it should be to find out why a prefix
| did not work.
| XCSme wrote:
| Seeing this, it reminded me of an interesting topic: caching at
| browser-level the common external JS libraries, to achieve big
| performance improvements: https://github.com/w3c/webappsec-
| subresource-integrity/issue...
| e1g wrote:
| And that reminds me of the rebuttal: such "embedding/blessing"
| of libraries would give them a considerable tailwind and stifle
| innovation. Suppose React/styled-components/Apollo/Highcharts
| is already available on the client. In that case, it becomes
| much harder to consider alternatives, and any new contenders
| (Vue/Solid/urql/emotion/etc) would never get traction.
| chrisweekly wrote:
| Yeah, cross-domain public caching had mostly theoretical
| benefits and never really worked that well in praxis. Glad
| browser vendors moved away from it.
| Mystery-Machine wrote:
| I'd like to read mor about why it only has theoretical
| benefits. Do you have any links to share where I can learn
| more? Thanks!
| la_fayette wrote:
| I am responsible for a large microfrontend portal application,
| which consists of multiple modules and applications, which are
| wired at runtime with module federation. The build process is
| handled by webpack for each mudule...
|
| I just wonder, if import maps could replace module federation now
| or in the long run?
| wildpeaks wrote:
| That might be handy during development, but production would
| still need a "build step" for generating things like SRI hashes,
| asset filenames, sitemaps, etc.
| qubyte wrote:
| I did a write up of how I use import maps to avoid bundling JS on
| my static site and cache modules more effectively. I mostly use
| JS for little experiments and generative art and such, so I have
| a number of utility modules. These get hashed and the names of
| each resolved in the import map. Original modules are kept for
| browsers without import map support (without the immutable cache
| header).
|
| There are a few gotchas. The browser won't use the import map to
| result an entry point in a script tag for example. Content
| security policy is a painful one too for static sites like mine
| (the import map counts as a script, so you have to hash the map
| and put that in the CSP header).
|
| https://qubyte.codes/blog/progressively-enhanced-caching-of-...
| simonw wrote:
| I built a tool relevant to this the other day. I was fed up of
| how hard it was to download ES modules from a CDN and store them
| locally so I could build apps without that CDN dependency, so I
| made this: pip install download-esm
| download-esm @observablehq/plot
|
| This downloads the ESM version of Observable Plot from the
| jsDelivr CDN, figures out its dependencies, downloads them as
| well (40 files total!) and rewrites their imports to be local,
| not CDN paths.
|
| More details here: https://simonwillison.net/2023/May/2/download-
| esm/
|
| I'm now considering adding import maps support:
| https://github.com/simonw/download-esm/issues/4
| softfalcon wrote:
| Oh no! You've gone through dependency hell so many times you've
| started writing your own package management!
|
| I'm only half kidding, this is pretty cool, but I'm also really
| sorry that it has come to this for you and your tooling!
| zeven7 wrote:
| This is JS. Dependency hell is par for the course.
| jakelazaroff wrote:
| Genuinely curious -- what language do you think has
| good/better package management? Every time I start a Python
| project, for example, there's a bunch of time and
| frustration spent getting pip and virtual envs working.
| sodapopcan wrote:
| Elixir's tooling, Mix, is really great.
| frizkie wrote:
| You didn't ask me this question, but I work in Ruby, and
| off the top of my head, have no complaints about Bundler.
| Switching Ruby versions between projects is not something
| that is handled natively in any way (unsurprisingly?) and
| I know that I've struggled a lot with `rvm`, but since
| switching to *env (rbenv, pyenv, nodenv) I can safely say
| I do not have any struggles on that front either.
| BiteCode_dev wrote:
| Rust is the golden standard. python and js are on part
| with each other, they just have different problems:
|
| - tooling and dependencies are hell in js
|
| - bootstrapping is half the battle in python (see
| https://bitecode.substack.com/p/relieving-your-python-
| packag...)
| k__ wrote:
| Python devs always told me npm was better than pip
| tnorthcutt wrote:
| Of the three languages that I deal with semi-frequently
| (JS, Python, and PHP), I have by far the least trouble
| with PHP's package management.
| noir_lord wrote:
| composer is remarkable in that it causes very few
| problems in practice.
|
| It's an excellent package manager in a mainstream
| language that just does its job.
|
| It helps that the PHP community mostly just saw PSR4 and
| went, yeah, that'll do.
|
| Meanwhile over in JS land issues are still caused because
| different modules use different module formats and it's
| all just...fragile somehow.
|
| Ironic give the reputation of PHP that it nailed its
| package manager.
| chao- wrote:
| In my mind, anyone setting out to create a package
| manager, or a new language that will need one, needs to
| at least match the functionality of Gems/Bundler or
| Hex/Mix. They've been around long enough that it feels
| like table stakes at this point.
|
| Mix does more than just dependencies, however I am
| referring the table stakes as being the more narrow scope
| of dependency management.
| moron4hire wrote:
| I haven't had any package management troubles with .NET
| in over a decade.
| HideousKojima wrote:
| Only times I've run into dependency issues with .NET are
| when a library expects certain native DLLs to already be
| present on the system, but that's pretty rare (and fairly
| easy to solve).
| zovin wrote:
| Especially since moving to SDK-style projects, I haven't
| even thought about assembly bindings in a long time. I've
| come to really like nuget.
| dingledork69 wrote:
| Java/Maven
| hombre_fatal wrote:
| They are talking about client development so none of the
| answers are going to compare.
|
| You aren't grabbing ESMs from CDNs for a Node server app.
| And even if you are, that's just not a situation that's
| happening outside of an ecosystem that straddles client
| development.
| simplotek wrote:
| > This is JS. Dependency hell is par for the course.
|
| Is there any programming language that does not suffer from
| dependency hell?
| pier25 wrote:
| Obviously it's a matter of degree, but languages with a
| better standard library seem less affected by dependency
| hell.
| benatkin wrote:
| You should download from npm. Neither UNPKG nor jsDelivr is
| considered authoritative.
| simonw wrote:
| The problem I'm trying to solve is "give me an ECMAScript
| module version of this package, and fetch all of the
| dependencies too, in a way that I can use in a browser
| without needing a build system".
|
| If you know of the npm recipe for doing that I'd love to
| learn it - I'm fine running npm once to grab the module
| versions of things (which I intend to vendor in my repo), as
| long as I don't have to use any build tooling after that
| point.
| spankalee wrote:
| npm + an import map generator should work. I think you
| might be able to use the jspm import map generator via the
| `jspm link` command: https://jspm.org/docs/jspm-
| cli/stable/#link
| benatkin wrote:
| A wrapper bundle is what I prefer right now, here is a
| wrapper bundle I made for CodeMirror 6. It just provides
| the needed exports from the library. It can also be
| verified on the client side with UNPKG or jsDelivr. My
| client side build prints out the integrity attribute. I
| still need to write the npm bundling code so at the moment
| I am relying on UNPKG.
|
| https://codeberg.org/macchiato/editor-lib-
| codemirror/src/bra...
|
| The rollup output is nice. I don't have terser, though I
| may add it later. When I added a couple exports the diff
| was just the change in my code.
|
| The library using it can include the integrity attribute
| and a can go here and run the build after checking the html
| and JavaScript file and check the output (codeberg.page is
| like GitHub pages in that it the repo contents controls
| what it serves): https://macchiato.codeberg.page/editor-
| lib-codemirror/
| triyambakam wrote:
| Check out https://esm.sh/#cli for a minimalist yet full
| featured solution that amends your import map for you. It does
| require Deno.
| chatmasta wrote:
| Thanks for the tip about esm.sh. For some reason I hadn't
| heard of it before, or maybe I had but it was before I fixed
| issues with ES module exports in my library. Previously I was
| using Skypack, but it didn't bundle dependencies (at least as
| far as I could tell).
|
| Thanks to your tip, I can query public datasets with SQL with
| a one line import in an observable notebook [0] :)
|
| [0] https://observablehq.com/@milesrichardson/madatdata-esm-
| spli...
| Scarbutt wrote:
| Or you just can download/install with npm and create a bundle
| with esbuild.
| simonw wrote:
| I don't want to do any bundling - I want to use modules in my
| browser, at runtime.
| wruza wrote:
| I understand there may be various reasons to avoid js
| bundling, but in case you avoid complexity, check out Vite.
| It's zero config and can work on a regular directory (no
| node_modules, only index.html, styles, scripts). `vite`
| runs devserver with hmr, `vite build` generates dist. It
| may be useful to you even if you stick to esm downloading
| due to out of box support for all tech like ts, sass, less,
| etc.
|
| It's basically an http server like npm/http-server, but
| with all modern tools automagically included.
| laurencerowe wrote:
| If you're happy with a single bundled file, another option is:
| deno bundle 'https://esm.sh/@observablehq/plot' > plot.js
|
| My browser development workflow looks somewhat like the
| following:
|
| Imports / versions in importmap.json: {
| "imports": { "plot":
| "https://esm.sh/@observablehq/plot@0.6.6" } }
|
| Code in app.js: import * as Plot from "plot";
| const plot = Plot.rectY({length: 10000}, Plot.binX({y:
| "count"}, {x: Math.random})).plot(); const div =
| document.querySelector("#myplot"); div.append(plot);
|
| For development use dev.html: <!DOCTYPE html>
| <html> <head> <meta charset="utf-8" />
| <script type="importmap" src="./importmap.json"></script>
| <script type="module" src="./app.js"></script>
| </head> <body> <div id="myplot"></div>
| </body> </html>
|
| Then you can then bundle with: deno bundle
| --import-map=importmap.json app.js > bundle.js
|
| And have as your prod.html: <!DOCTYPE html>
| <html> <head> <meta charset="utf-8" />
| <script type="module" src="./bundle.js"></script>
| </head> <body> <div id="myplot"></div>
| </body> </html>
|
| Note that `deno bundle` is deprecated. You can almost replace
| it with esbuild but it currently lacks builtin support for
| import maps: deno run --allow-all
| https://deno.land/x/esbuild@v0.17.18/mod.js --bundle app.js
| --outfile=bundle.js. # errors, see:
| https://github.com/evanw/esbuild/issues/2230
|
| I'm really looking forward to the ECMAScript proposal on Type
| Annotations making it into browsers some days as this would
| effectively unlock transpiler free typescript development,
| leaving just a simple optional bundling step for production.
|
| https://devblogs.microsoft.com/typescript/a-proposal-for-typ...
|
| https://github.com/tc39/proposal-type-annotations
| easrng wrote:
| i did more or less the same thing but running in the browser
| https://easrng.github.io/modbundler/
|
| you give it an import map and a list of urls and it either
| bundles + minifies to a single .js file or rewrites the paths
| and saves everything as a .zip
| randall wrote:
| Deno has a similar thing... fwiw. esm.sh can do some of it but
| it ends up being kind of a shit show.
| triyambakam wrote:
| What has been your bad experience with it?
| zackmorris wrote:
| Will this work with Subresource Integrity (SRI) so that assets
| can be fetched from a shared CDN securely?
|
| https://www.w3.org/TR/SRI/
| tourgen wrote:
| [dead]
| galaxyLogic wrote:
| What's the main benefit of import-maps?
|
| Reading the article it seems to me you must code the import map
| which basically defines aliases for your scripts. Then you must
| use the aliases to actually get access to the scripts you want to
| use.
|
| You must specify the absolute URL of the imports whether you use
| import-maps or not so what's the benefit? Does this reduce the
| amount of code?
|
| I'm sure there is a benefit I just don't see it on first reading
| of this article.
| randall wrote:
| Basically package.json but without package.json. IE no bundle
| JS is back.
|
| There are still some issues, specifically with things like
| React, where it's kind of hard to guarantee if you use React,
| you'll get the same version if you use it in "react-relay" etc.
|
| Deno has been very informative on this. They're adding
| package.json support to the runtime as a polyfill, but I'm
| hopeful they can swing back to import maps fully over time.
| galaxyLogic wrote:
| I see. So the benefit is that you externalize your imports
| into a file separate from you code. Right? I can see the
| benefit of that. It is like meta-data about your module, kept
| separate from it. And several modules can use the same
| imports-map, right? That means amount of code needed gets
| less.
|
| Thanks for the explanation. I assume I got it right, did I?
|
| Or now I wonder, must the imports-map be defined in the same
| module where it is used?
| WorldMaker wrote:
| No, you might have one "global" importmap in your HTML file
| and that applies to the entire graph of modules you load
| after that, which also means that you can "chain"
| importmaps and can also do things like pin the versions of
| dependencies of dependencies in that top level importmap.
| That sort of thing will need package-management-like
| tooling and dependency "packages" built for it, but that
| gets back to the idea that importmaps in theory are a
| lighter weight replacement for package.json/package-
| lock.json files.
| TimTheTinker wrote:
| It also allows easy loading of micro-frontends, if that's the
| architecture you're shooting for.
|
| https://single-spa.js.org/docs/recommended-setup/
| rektide wrote:
| But not in Workers/worklets. https://github.com/WICG/import-
| maps/issues/2
|
| ESM was specced out in 2015. It hurts so bad that we are in 2023
| & using modules in a modular way is still incredibly ill
| supported in the web ecosystem. :(
| kmeisthax wrote:
| So, any progress on being able to bundle native modules and have
| them import properly into inline script tags?
|
| Right now if you want bundling you have to give up native
| modules, because the _only_ way to get a browser to register a
| native module is by making a separate HTTP request for that
| module.
|
| The closest I could think of to doing this would be an importmap
| with a data URI per module, but I've no idea if you can load
| importmaps as separate files or if they have to be inlined. If
| they have to be inlined that defeats the purpose.
| endorphine wrote:
| FWIW Rails 7 uses importmaps by default, foregoing the need for a
| JS bundler. Coming from backend development, this seems like a
| huge win for me.
| S201 wrote:
| I built a new Rails app with importmap-rails last year and it
| was an absolute joy to work with. Being able to forgo all of
| the JS compilation bs was a massive simplification. I've wasted
| so much time in the past dealing with Babel and Webpack so
| finally being able to kick all of it to the curb was a joyous
| day.
___________________________________________________________________
(page generated 2023-05-03 23:01 UTC)