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

Sugary defaults: can we create a simple case for advanced ahead-of-time tools? #7

Open
domenic opened this issue Mar 15, 2018 · 17 comments
Milestone

Comments

@domenic
Copy link
Collaborator

domenic commented Mar 15, 2018

As mentioned in "A convention-based flat mapping", it'd be ideal to also support a way of doing a very simple package map for applications which are laid out ahead of time in a conventional way on-server. For example, you could imagine something like

{
  "imports": {
    "*": "/node_modules_flattened/$1/index.js"
  }
}

In offline discussions, @nyaxt cautioned that this would add a lot of complexity to what was so far a simple format. For example, as-written this would presumably work at any level of the tree. Still, it'd sure be nice...

An alternative is to not reuse the same format, but instead come up with something specifically tailored for this use case, that only works at top level. As a straw-person, you could do:

{
  "pattern": "/node_modules_flattened/*/index.js"
}
@bmeck
Copy link

bmeck commented Mar 15, 2018

package-community/discussions#2 is relevant

I would like to note my dislike if this would cause diverging package ecosystems.

@domenic
Copy link
Collaborator Author

domenic commented Mar 15, 2018

I'll note that this also is important if we want to enable people who are not using package managers to use bare import specifiers without a lot of manual synchronization between their filesystem and their package map.

For example, imagine someone who downloads libraries as zip files and unzips them into a /js/assets/ directory. I don't think too many folks visiting this repository will still be doing that, but it's worth having sympathy with a good fraction of developers who still might. (Or do we think that any libraries that produce native modules will insist you download them via packages? I guess it's not quite clear yet...)

Or imagine a tutorial that says something like "insert these lines into your page and now you can use any package on npm:"

{
  "pattern": "https://normalized.unpkg.com/*/index.js"
}

(Here "normalized.unpkg.com" is a hypothetical variant of unpkg that ensures a top-level index.js is always the main module, somehow.)

@nyaxt
Copy link

nyaxt commented Mar 15, 2018

I'm not against adding this, but I strongly prefer to not have this in the first cut. I think we can carefully add this later.

@trusktr
Copy link

trusktr commented Nov 3, 2019

Adding .js to every import is redundant. Can this feature cover a simple way to automatically make it so all extension-less specifiers use .js?

@jkrems
Copy link
Contributor

jkrems commented Sep 16, 2020

After trying out prefix-matching with many existing packages in node.js (and based on feedback from bundlers), it looks like the bare specifier mapping there may move away from prefix and instead always suggest using a syntax that's very similar to this.

Since import maps haven't shipped widely yet, this may be an opportunity to prevent confusion on the browser side and "just" never add prefix-matching. For the cases where prefix-matching behavior is the actual desired behavior, it can be expressed with fairly limited overhead using basic pattern support:

{
  "imports": {
    // before:
    "components/": "/static/app/components/",
    // after:
    "components/*": "/static/app/components/*",
    // or with the syntax in OP:
    "components/*": "/static/app/components/$1",
  }
}

@guybedford
Copy link
Collaborator

To add to @jkrems's point here, Node.js is currently considering landing effectively an analog to this feature here - nodejs/node#34718. The PR would effectively then seek to deprecate the trailing slash path exports if it lands as we're finding these pattern definitions more flexible.

@sdwlig
Copy link

sdwlig commented Nov 10, 2020

I've been using a method similar to convention based flat mapping: I prefix, usually with a tool, all bare imports with /lib. Then I have both dynamic (nginx) and static (my keaton static cache builder) ways of mapping /lib to particular libraries not only from node_modules but also various other application directories. Publishing the tools and technique soon.

@brianmhunt
Copy link

A workaround in the absence of this is a simple Service Worker that remaps the path based on any Javascript logic. This is less than ideal because it would require the complexity, overhead, and browser-support of a Service Worker, but it's available today, and may be useful as a thought exercise.

@dmail
Copy link
Contributor

dmail commented May 25, 2021

In the way I see ServiceWorker, they cannot be used to control import behaviour as they are installed concurrently with the main page. To me they can start to control import behaviour (if worker installed successfully) only the next time page is loaded.

@brianmhunt
Copy link

brianmhunt commented May 26, 2021

@dmail This is part of the overhead and complexity, and your concern is noteworthy — it's not immediately obvious how to solve the many complex needs of ServiceWorkers. It looks like the case you are noting may be solved with ServiceWorkerContainer.ready.

The ready read-only property of the ServiceWorkerContainer interface provides a way of delaying code execution until a service worker is active. It returns a Promise that will never reject, and which waits indefinitely until the ServiceWorkerRegistration associated with the current page has an active worker. Once that condition is met, it resolves with the ServiceWorkerRegistration.

For example: importing /entry will wait for the ServiceWorker, meaning it can be rewritten/redirected:

navigator.serviceWorker.ready.then(() => import('/entry'))

However, your comment is illustrative of the fact that Service Workers are a complex mechanic, versus an import maps glob.

@joeldenning
Copy link
Contributor

joeldenning commented May 26, 2021

Waiting to execute javascript until the service worker is installed is not just complexity, but a performance limitation.

This documentation talks about service workers as an alternative that was considered: https://github.com/WICG/import-maps#service-workers

@daKmoR
Copy link

daKmoR commented Aug 4, 2022

How about in a first iteration we "allow" * to be only in the end?

This would mean the functionality would be exactly like the existing trailing slash functionality.

{
  "imports": {
     "lodash/": "/node_modules/lodash-es/",
     "lodash/*": "/node_modules/lodash-es/*",     
  }
}

👆 both of these imports are exactly the same

We would however gain

  1. More intuitive API
  2. Sime alignment with how node package entry points work

What do you think?

@GabrielDelepine
Copy link

I would suggest mimicking the behavior well-defined for the exports field of the package.json file.
https://nodejs.org/api/packages.html#packages_subpath_patterns

{
  "exports": {
    "./features/*.js": "./src/features/*.js"
  }
}

@trusktr
Copy link

trusktr commented Aug 28, 2023

This globbing feature would be generic and would cover extensionless specifiers, while the idea in

is simpler, with no globbing, specifically for extensions in a way similar to scopes.

@trusktr
Copy link

trusktr commented Apr 3, 2024

This would be very useful for certain libraries, for example here's an importmap for Shoelace:

          "@shoelace-style/shoelace/": "/node_modules/@shoelace-style/shoelace/",

and usage:

import '@shoelace-style/shoelace/dist/components/card/card.js'

Importing from components/ is a very common thing, so it would be nice to make a shortcut for it in the importmap like so:

          "shoelace/*": "/node_modules/@shoelace-style/shoelace/dist/components/*/*.js",

and the usage would then be:

import 'shoelace/card'

Wildcards would also play well with TypeScript tsconfig.json, and we can write the same sort of maps in tsconfig such that the types are still picked up from the customized import statements.

@mantou132
Copy link

I hope import-map can provide a Subpath patterns function similar to node exports, so that I can run the module in the browser without adding the .js suffix myself.

@justinfagnani
Copy link
Collaborator

I think the core reasons from the npm world to support subpath patterns are:

  1. To enable much more compact import maps, given that Node shipped subpath patterns and many people are using extension-less imports.
  2. To enable simpler import map generation for npm projects that can just look at the package.json and doesn't have to scan the file system. You can get the package.json with a call to the registry, so you wouldn't have to install a package. I imagine module CDNs could use this to serve long-cached modules that work against any compatible dependency versions.

It's a little unfortunate that Node shipped a pattern capability that import maps don't support, but here we are and I think it'd be a pragmatic thing to do.

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