-
Notifications
You must be signed in to change notification settings - Fork 24
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
Module specifier: npm-style "@prefix/module" or something else? #12
Comments
hey @zkat thanks for chiming in! For myself I've been hedging on the importance of making an explicit difference between built in modules and ones from the file system. Could you expand on some of the value you see in keeping them the same? This is currently being discussed in the Node.js repo in nodejs/node#21551, very much appreciate hashing this out and reaching consensus on this and shipping something that is aligned with various bodies / platforms interests. Another concerns that I had was flexibility in namespaces. Picking the protocol-ish solution would allow us to pick a variety of namespaces without competing with userland, and would be able to be shimmed / polyfilled with import-maps and loaders. If we were to use the scoped module syntax we would then be competing with existing namespaces (the exact problem we are trying to solve in nodejs). Can you imagine a longer term solution that could guarantee flexibility here? |
i actually see this as a reason not to use that namespace style. I would want something that sits above npm's (or anyone else's) namespaces, so that namespace ownership is unable to become a problem. |
Name collision is a big CON; who owns the std org? |
Yes, @jdalton does, but it only contains one package and that package has been deprecated in favor of its non-namespaced version. (Personally I kinda like the empty string, i.e. |
Namespace ownership is always going to be a big problem - by using npm's system, we can use their existing solutions for that, instead of immediately finding ourselves in that difficult swamp. |
@Mouvedia @bakkot FWIW I've already been pinged by @littledan regarding the |
If the format is Are we talking about only one std scope or will this be the foundation of many more to come? |
@zkat Thanks for bringing the discussion here. Those numbers are some interesting context. If we use Regardless of whether we go with Using a scope looks like a JS module, and using a scheme looks like a special built-in thing. Do we want built-in modules to look special or ordinary? |
@littledan who's "we"? @tc39? |
@littledan using a scope means users can use what they know. Using a scheme means users now have code like: import {Connection} from "std:worker"
import {map} from "@lodash/functional"
import minimist from "minimist" And then you need to explain what all 3 do. |
@Mouvedia I meant we = the JavaScript, Web and Node community |
users don't know any namespace for standardized functionality from the language because no such thing exists yet. i think many people's first assumption to an import specifier they don't recognize is that it came from npm (for ex. in node when we have people asking about how to install crypto or whatever), which could be confusing. There are still a lot of additional points that need to be considered here. off the top of my head:
i really like @littledan's idea for a shared repo (possibly via the js foundation?), and a namespace above resolution specific to any platform/tooling would be a fantastic step toward that. |
@littledan that's why I wanted to know what you meant by "we". Even if it's off topic, it's important. |
Thanks for bringing this up. Like @MylesBorins, I waffle on whether using scoped syntax for built-in modules is a good or bad idea. Last we talked, he mildly convinced me that different syntax was better. Let me ask a few pointed questions in the hopes of drawing out folks' intuitions.
This doesn't seem obvious to me. For example, if we pick
What existing solutions are those? The one I'm aware of is emailing support@npmjs.com, which makes a judgment call whether to reallocate the scope or not. The way I see it, the solutions are pretty unsatisfactory and non-applicable for cases that are not actually using the npm registry or filesystem. For example, if we pick
Is that a bad thing? In the particular case of Node.js, should the syntax for accessing files that you control on your local filesystem (i.e. I'm genuinely curious on folks' thoughts here. Other points:
Since module specifiers aren't URLs in most environments, I'm not sure how important this is. (Some URLs are module specifiers, but that's the extent of the relationship.) Similar to the npm scopes issue, assuming that a registry designed for one thing should also be used for built-in module prefixes is bound to lead to confusion and weirdness.
Clever!
Yes. |
I think that Node.js should use People will polyfill and such, no matter what. One value of having node builtins in the same global-space type of pattern (since they predate npm namespaces) is that they allowed userland and platform modules to feel similar to people using them. One of the values of a lot of the new developments in the JS language (such as Proxies, weakmaps, and so on) is that they allow userland code to do things that were previously only available to the host system. Doing this makes the language feel more integrated and less fractured. I can create my own classes that are iterable just like Arrays, and then-able objects can be awaited. This integration is a very good thing, and it makes JavaScript fun. This is ultimately a bikeshed, and a purely cosmetic issue about how to express a structured namespace in a module identifier. None is inherently better than the other. But one of them is already adopted by a surpassing majority of JavaScript developers. The concern about namespace ownership is easily addressed by making "But what about if some This isn't even an example of paving a cowpath. It's already paved. The cows are gone. It's a highway through a city that's 30% bigger than NYC. There's subways and onramps and stuff. |
@isaacs did you mean to post that on nodejs/node#21551? |
I was midway writing a response but @isaacs' has summarized my feelings very well. The reason I'm an employee at npm, Inc is that I care a lot about JavaScript and its community. I see JavaScript, the corpus of open-source on npm, Node.js, and modern browser technology, as all part of the same whole. When we make design decisions that don't look at this collection of technologies holistically, not only do I think it leads to worse products, I think it can serve to fragment the community and create contention.
The open-source JavaScript community already has a syntax for scoping packages ( Folks are going to want to shim standard modules, rather than coming up with a way to make this difficult, let's make it possible in an elegant secure way for folks. |
i think that depends on if you view npm as the entirety of the open source corpus for js code. npm alone has syntax to refer to packages from github ( |
I think it's important to separate shimmability from built-in module specifiers. At least on the web with import maps, and likely on Node with loaders, you can intercept any arbitrary specifier (including What I'm hearing from the last two posts, if I take out the misunderstandings about shimmability, is that it's more about wanting uniformity between files-on-disks and libraries-shipped-with-the-platform, assuming default usage with no shims or polyfills or loaders or import maps involved. Which comes back to the questions I tried to ask. Is that uniformity good, or bad? |
It is good. I believe this was implicit in my comment above, which contains much more justification, but tl;dr, yes, that uniformity is good, and without exception, deviations from it are bad.
Arguments to
No, but the same comment could apply equally there. Thank you for the nudge, I will cross-link it. |
Stray thought: it would be valuable to compare the approaches taken by other languages, I think. I don't have time right now to do so, but will try to get back to it if no one else gets around to it. |
@devsnek certainly there's open-source JavaScript outside of the npm registry. but, npm represents the largest corpus of open-source JavaScript code, and has practices developed across millions of developers. @domenic to better explain where I'm coming from with the overloaded term shims: Myself, @boneskull, @iansu, @guybedford, and a few other folks have been exploring how the Node Tooling Working Group can back-port new core-module features, e.g., One thought being we'd release modules like I also like that modules on npm could similarly be published that provide the same behavior as ☝️ I'd put the ability to do this in the |
is this an accurate view of the disagreement? "npm is super popular/ingrained in the ecosystem, it would be great for users of js to design std modules around it" vs "js is used in a lot of places, it would be great for users of js to keep the std modules distinct/disconnected from any specific environment or tooling" |
I would like to mention that it's worth to try to separate the standard library from npm. Yes, npm is practically the standard registry, but it doesn't need to be. Also, standard library "packages" are not really packages/modules in the same way a regular module is. As such I would like to recommend not using string identifiers for standard modules. import { Instant } from temporal;
// insead of
import { Instant } from '@std/temporal';
// or
import { Instant } from 'std:temporal'; I may be wrong, but it seems standard library packages are not supposed to be url resolvable packages. As such, it would make sense that the names be consired syntax instead of urls. |
It may be a weird thing for me to say, since it certainly serves my personal and professional interests to equate "npm" with "JavaScript" in whatever way possible, but I'd really like to stop equating this pattern with "npm" per se. The interesting thing is that millions of JavaScript developers are today using the You're seeing people from npm, Inc. getting very passionate about this because we care a lot about modular JavaScript. That's why we choose to work on the problems that we do. But frankly, it ultimately benefits "npm" not at all to do it one way or another, and the pattern isn't tied to npm as a platform in any particular way. Others can and have implemented the same naming convention for JavaScript, and nothing's stopping them from doing so. So, yes, I agree that it's worth separating the standard library from npm. I also thing that has no bearing on whether the naming convention chosen mirrors that already in use by the vast majority of JavaScript users today. |
@bcoe the ability to publish shims, and then configure Node to use them in place of built-ins, works no matter what name you publish those shims under. If you are shimming @isaacs you state that being the same is good, but without any reasoning. The rest of your posts seem to just do the same thing, e.g. talk about how it benefits everyone to choose the same naming convention for filesystem-located modules vs. built-in modules, without explaining why. Perhaps you could expand? |
@domenic I agree technically we could use either. So it comes down to whether we want users to be aware that these APIs are different to those they import from regular userland sources. My opinion is that these APIs truly are different AND users should value them differently because:
So let's signal this value to users in the naming scheme. It will aid adoption. |
I think some of my FUD is coming from the fact that how loaders and maps will work, from the perspective of a module author, feels a ways away from being fleshed out. Specifically for overrides to be valuable, it would need to be something that a module author can control (making a library that progressively enhances to new Node.js features) vs., something that a consumer of modules configures. Also, I was picturing someone would be able to write this code today (pre new loader):
And have it already work, if the bridge module has been published.. and write the same code in Node 14 (or some future node), and have it use the built in module. perhaps I'm just surfacing the requirement that loaders and module mapping functionality be back-ported to older Node.js.
I think some valuable points have been made by various folks, regarding some of the benefits of making standard libraries stand out like a sore thumb. |
at this point i'm confused on if we're talking about node.js builtins or the ecmascript standard library. these two topics, while obviously related, definitely have different constraints and goals, and i don't think we should be directly equating them. |
Note, it's a little hard to introduce new keywords, since they can be used as variables. Recently, TC39 has been a little chilly to the introduction of the sorts of grammar complexity and edge cases that it takes to make contextual keywords work. |
I am aware of this, and the main reason I'm not sure how to work this out. But it's an idea worth discussing. A possible solution to the new keyword problem might be a new directive. I also thought of import native { Instant } from 'temporal'; |
@littledan it doesn't seem out of the question we wouldn't introduce more "local" schemes of some kind (and browsers support more than Fetch lists, though mostly for internal use). (I could see networking happening too if something like the decentralized web ever became feasible.) |
I'm not suggesting that a runtime clobbering userland modules is a wise thing to do, or should ever be the first resort. I pointed that out to show that no runtime is, ultimately, beholden to npm, regardless of the naming convention used. The runtime has all the power here, as always. If a new runtime called "blinker" wanted to use the However, consider if a runtime for embedded devices or something came out, and called itself "fooblx". They could register the (currently unused) scope on npm, and provide polyfills for their device APIs so that people could test their programs in Node.js or web browsers really easily. That's not a bad thing, it is a good thing. That's not the only benefit, it's just the most obvious counter example to the "oh no collisions!" fud. There's a benefit to runtime designers to this kind of design, because this sort of thing works today. Using the conventions in practice in a community is a smart and kind thing to do. It opens the door for more innovation. |
@isaacs I think my big overarching point throughout this thread is that if an implementer feels pressured to support the whims of an entity external to the specification in order to have a successful implementation, I think the specification has failed. Like you yourself said, if blinker doesn't deal with the existence of npm (and others) and avoid these issues, they face problems down the road. I don't think a language specification should even begin to go into the territory of having these issues. We should directly avoid designs where these issues come up. To your point earlier about "three large companies making browsers", if google suggested using |
We have 4 locations to resolve an import: URL, local file, native/builtin and import maps. Something like this Import {sin} from builtn '@std/math' It can be ignored but the it will follow some order like: import maps, npm modules, file, builtin. |
Who's talking about the "whims of an external entity"? If an implementer does not recognize the code currently in use by their potential users, then they yes, will not be as successful. No specification choice will change that, it's basic developer product design.
I did not say that. This is a mischaracterization of my position. I said that if blinker doesn't deal with the existence of userland code in the blinker namespace, then they'll face challenges gaining adoption. Are you actually suggesting that a successful specification would mean that platform implementors don't have to consider what code their potential users are already writing? |
(Note: I am not @devsnek, I'm not speaking on his behalf) If the specification is designed in such a way that existing user code cannot conflict with the new features, then it works just fine. This has been successfully done many times in the past. And there have been multiple suggestions on how to implement the JS stdlib in such a way that it doesn't conflict with user code.
When adding new features? Yes. When new features are added to JavaScript, it's done in such a way that it cannot impact existing code. This is a core part of 1JS and avoiding breaking the web. And when it hasn't been designed that way, it's caused problems, which then required workarounds like I understand Node does things differently, and that's fine. But the way that browser JS has done things is also fine. It's done that way for good reasons. Any solution must work in all environments, both the browser and Node (and others). Let's try to look at all the perspectives and options, and not dismiss legitimate concerns by calling them FUD. |
When I say "fud", I am not casually dismissing legitimate concerns. I'm using that term to specifically refer to the fear, uncertainty, and doubt around using a "npm-style" namespace, simply because npm is a company. Nothing in the proposal is npm-specific. It's the way that people identify and recognize namespaces today, and there are numerous benefits to following that existing cowpath. I guess, what I'm saying is, I'm dismissing (some of) the concerns, because they are not legitimate.
There is no approach which guarantees that userland code will never conflict with a runtime's chosen namespace. Qv: Babel's implementation of That being said, of course |
Isn't this a potential issue if we allow polyfilling of built-in modules at all, regardless of the specifier syntax? |
Fair enough. My concern has nothing to do with npm as a company, it has to do with cleanly separating user and language features, matching the user's expectations.
There's a big difference: with your example, that's a polyfill, which is expected to behave according to the spec (and if it doesn't, that's a bug which gets fixed). On the other hand, using npm-style namespaces conflicts with completely arbitrary user code, which might not behave according to the spec at all. That has significant practical ramifications.
I don't really buy that argument. With npm-style namespaces, you have to do Instead, polyfills would be provided by some sort of plugin (whether that be Babel, Webpack, import maps, whatever). In other words, a mechanism completely outside of the npm package manager. That's how things are done today, and I see no reason why that would change. |
My assumption is that it shouldn't be possible to create an npm package name which contains In other words, importing Obviously an application can use import maps (or a Webpack plugin, or whatever) to arbitrarily assign std modules to anything they want, but that's controlled by the application author, not the library author, so there's still no conflict. |
It sounds like this actually comes down to how polyfills for built-in modules are distributed and deployed, and how much we want this to be by the library author vs importer. I think this is a somewhat different issue--I can imagine many possibilities here. It just seems like a separate question. For one, we probably want a way to distribute/use polyfills which are not just the ones approved by the owner of the scope. |
You already can add |
Before thinking about the syntax, I think it's best to agree on some of the requirements/open questions that needs to be discussed first:
I think that answering those questions would help clarify and decide one way or another. |
@mcollina It's probably best to create separate issues for those questions (if they don't already exist). |
These issues are already widely under discussion:
|
I think we are talking as something different here. As an example, who owns the standard namespace and the API definition in it? As an example, Node.js could add its own stream implementation under that namespace. WHATWG could recommend the browser vendors to do the same for WHATWG streams. Then we have a name clash. I highly recommend to keep the namespace of the standard library to TC39, and enable WHATWG to have its own namespace that it governs. If every platform and runtime can change and extend that standard library, then it's not a standard library anymore. Shipping features unprefixed is going to create a possible name clash situations. I think it would be best for the JS language to provide a way for runtimes/platform to avoid such name clash. Or we could disregard the name clash issue completely and push it to the community. https://github.com/domenic/async-local-storage is 100% a web specific feature that makes sense only in browser-like environments. Should it go in the standard library? I don't think so. I think the standard library should focus on the language primitives and low-level data structures. The standard library proposal should enable runtimes to maintain their own namespace, that they can use at will. Something like:
Note that using the
Who owns the namespace? If Browsers or Node.js start overriding part of it, we are fracturing the community and the ecosystem, and create friction where there should not be. I think runtimes should put their native modules under different namespaces. |
Note that if you divide the namespace that way it'll be much harder for the next ArrayBuffer, streams, TextEncoder, or URL API to be used across host languages. Or perhaps it's considered acceptable that there's eventual duplication? |
I prefer |
On the web, I can state that we are not interested in separating by standards bodies or environments. Developers must not need to know whether setTimeout (or its future promise-based standard-library ilk) is defined by WHATWG, W3C, WICG, Khronos, or TC39; they must just be able to get it from We have faith in the ability of the multiple standards bodies that contribute to the platform to coordinate, as they have done with the global namespace, to ensure a harmonious experience for everyone. As @annevk notes, this has been a fruitful collaboration in the past, e.g. ArrayBuffers moving from web-specific (Khronos) to JS-language-level (TC39). We are not OK with shifting the burden onto web developers to know the difference between groups, or the movement of specs between groups, in order to make our lives as standards developers slightly easier by allowing us to work in non-coordinated silos. This follows very directly from the priority of constituencies: "consider users over authors over implementors over specifiers over theoretical purity". Whether Node.js wants to use the same policy as the web, or different, is of course up to them. |
I think @domenic is right about this. I'd expect to see Node.js perhaps namespace things that are node-specific, but use |
We have a separate issue for the split vs unified namespace question: #14 |
Thanks, I deleted my comments since I was off topic. I'll continue discussion on #14 👍 |
I was planning to bring this up during discussion around "Standard Library" which appears to have been removed from the agenda. Is it too late to add this? Requesting 30 minutes but would settle for even 5 minutes Refs: tc39/proposal-built-in-modules#12 Refs: nodejs/node#21551
Myself and several others at npm (a voting member) feel that we should be using the existing cowpath for scoped module syntax (
@std/foo
) instead ofstd:foo
or other such alternatives.While this would usually come down to a simple matter of taste, in this case, we have 254,091 scoped packages in the registry already using this syntax. That's more than most other package registries for other languages, as-is, and is a significant % of our ~950k packages on the main npm registry.
This syntax would also be beneficial because it allows an existing mechanism for polyfills that will work with all current and past versions of node, and can easily be made to work with the browser module system through this proposal and others specifying what non-
./
specifiers are supposed to do to resolve.The text was updated successfully, but these errors were encountered: