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

Module specifier: npm-style "@prefix/module" or something else? #12

Open
zkat opened this issue Dec 13, 2018 · 104 comments
Open

Module specifier: npm-style "@prefix/module" or something else? #12

zkat opened this issue Dec 13, 2018 · 104 comments

Comments

@zkat
Copy link

zkat commented Dec 13, 2018

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 of std: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.

@MylesBorins
Copy link
Member

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?

@devsnek
Copy link
Member

devsnek commented Dec 13, 2018

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.

@Mouvedia
Copy link

Mouvedia commented Dec 13, 2018

Name collision is a big CON; who owns the std org?

@bakkot
Copy link
Contributor

bakkot commented Dec 13, 2018

Name collision is a big CON; anyone got the @std scope?

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. @/foo, as a namespace. No risk of collision there!)

@ljharb
Copy link
Member

ljharb commented Dec 13, 2018

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.

@jdalton
Copy link
Member

jdalton commented Dec 13, 2018

@Mouvedia @bakkot FWIW I've already been pinged by @littledan regarding the std scope and totally willing to donate it to help out the effort if needed.

@Mouvedia
Copy link

Mouvedia commented Dec 13, 2018

If the format is @scope/module, you will have fishing going on.

Are we talking about only one std scope or will this be the foundation of many more to come?
If it's the former I don't care about the name conflict.

@littledan
Copy link
Member

littledan commented Dec 13, 2018

@zkat Thanks for bringing the discussion here. Those numbers are some interesting context.

If we use scheme:module, we might want to register each prefix with IANA as a scheme, to make sure URLs don't reuse the same thing. That process is a bit more heavy-weight than registering a scope in npm, but might be fine if we only have a few. I've heard the suggestion that scheme::module could avoid being a valid URL; maybe that's a way out (and then we can run our own registry!).

Regardless of whether we go with @scope/module or scheme:module, I think import-maps could be used for polyfills (I don't know enough about loaders), couldn't they?


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?

@Mouvedia
Copy link

then we can run our own registry

@littledan who's "we"? @tc39?

@zkat
Copy link
Author

zkat commented Dec 13, 2018

@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.

@littledan
Copy link
Member

@Mouvedia I meant we = the JavaScript, Web and Node community

@devsnek
Copy link
Member

devsnek commented Dec 13, 2018

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:

  • lots and lots and lots of js users are working in environments where npm isn't used or considered (embedded hardware, game engine addons, etc). what would the best syntax/etc be for someone who isn't using <package manager x>
  • given the current monopoly npm has on the js ecosystem's packages (not that this is a bad thing), using a specific namespace that fits for npm opens some doors to potential namespace ownership issues down the road (again i'm not suggesting npm is going to do bad things, but a sun/oracle type situation with the defacto place to put standard polyfills would be bad, and take the ecosystem a long time to recover from)
  • various security things (like phishing misspellings of @std)
  • how fallbacks are mapped in an environment generic way
  • the possible area of conflict for npm with this change (to be 100% clear, i am not suggesting npm has any ulterior motive here, but due diligence is important)

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.

@Mouvedia
Copy link

possibly via the js foundation

@littledan that's why I wanted to know what you meant by "we". Even if it's off topic, it's important.

@domenic
Copy link
Member

domenic commented Dec 14, 2018

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.

Namespace ownership is always going to be a big problem

This doesn't seem obvious to me. For example, if we pick :, then hosts (Node, web, etc.) can completely control the resolution of these spaces, with no overlap with the npm registry. There's not a big problem; instead there's no problem, because collisions are impossible in a centrally-controlled system for governing the behavior of :.

by using npm's system, we can use their existing solutions for that, instead of immediately finding ourselves in that difficult swamp.

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 @, and later a new IoT product named e.g. blinker wants to introduce blinker@/... modules for its own environment-specific functionality, that introduces several problems:

  • Blinker's built-in modules are provided with npm-like syntax, and their users might then expect they could do npm install @blinker/util, but that won't work, because actually all the @blinker/ modules are baked into the Blinker device firmware.
  • If someone gets wind of the launch of blinker before that company settles on a final name or otherwise reserves the npm namespace, they can cause a confusing situation for people who have such an expectation, by publishing their own @blinker/util that does arbitrary work. (Assume the work is non-abusive and legitimate, i.e. not worthy of a takedown---just confusing for users expecting Blinker's built-in functionality.)
  • Even if Blinker, Inc. does everything right, they end up reserving the @blinker scope on npm only to not fill it with anything. (Because all their built-in modules are baked into the firmware, not distributed on npm.) So we end up encouraging an ecosystem of "scope-squatting" which doesn't even get useful packages published on npm!

And then you need to explain what all 3 do.

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. @lodash/functional) be the same, or different, from the syntax for accessing libraries implemented as part of the runtime? Especially as libraries implemented as part of the runtime may change as you upgrade Node, even in backward-incompatible ways.

I'm genuinely curious on folks' thoughts here.


Other points:

If we use scheme:module, we might want to register each prefix with IANA as a scheme, to make sure URLs don't reuse the same thing.

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.

(Personally I kinda like the empty string, i.e. @/foo, as a namespace. No risk of collision there!)

Clever!

Regardless of whether we go with @scope/module or scheme:module, I think import-maps could be used for polyfills (I don't know enough about loaders), couldn't they?

Yes.

@isaacs
Copy link

isaacs commented Dec 14, 2018

I think that Node.js should use @nodejs/fs for its builtins, not nodejs:fs. Also, I think the standard language features belong under a namespace that is reserved by the language, but looks similar; ie, @std/Date rather than std:Date.

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 @std/ a reserved namespace at the language level. @jdalton is fine with giving it up, and we at npm would be happy to keep people from using it for userland modules (since it'd only cause confusion anyway). Same thing could be done with the @nodejs/ module namespace, just have the host lock it down, and the community will adapt accordingly, since it's a much smaller shift than having to use a whole new naming scheme.

"But what about if some @foobar/ platform comes along, and wants to use that, and there are already @foobar/ modules!" This has already happened, numerous times. There is a module on npm called fs, another called domain. Those were co-opted by the platform, and it wasn't that big a deal, really. Some other node-internal-named packages are used for shimming node-like APIs into browser environments. That's actually a benefit of using the same namespace, not a cost.

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.

@devsnek
Copy link
Member

devsnek commented Dec 14, 2018

@isaacs did you mean to post that on nodejs/node#21551?

@bcoe
Copy link

bcoe commented Dec 14, 2018

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.

  • module authors should look at trends in language features and use these to shift practices (see: the amazing growth in the language babel has facilitated).
  • at the same time, TC39 (Node.js, and other folks helping define platform standards) should look at habits that have grown up in the open-source community and use these to shape decisions

The open-source JavaScript community already has a syntax for scoping packages (@foo/bar) and already has a culture of shimming modules (see @jdalton's esm).

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.

@devsnek
Copy link
Member

devsnek commented Dec 14, 2018

The open-source JavaScript community already has a syntax for scoping packages

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 (name/repo or git://), gists (gist:id), direct urls or paths to tarballs, etc. and it's not even the only package manager. this seemingly confuses the idea of a "one true namespace"

@domenic
Copy link
Member

domenic commented Dec 14, 2018

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 std:foo) and replace it with a shim or polyfill. : vs. @ has no impact on that.

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?

@isaacs
Copy link

isaacs commented Dec 14, 2018

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.

npm alone has syntax to refer to packages from github (name/repo or git://), gists (gist:id), direct urls or paths to tarballs, etc. and it's not even the only package manager. this seemingly confuses the idea of a "one true namespace"

Arguments to require() and import are not things like git:// urls, though. They're just the names, either of the form foo or @foo/bar, or @foo/bar/path/to/file.js, always.

@isaacs did you mean to post that on nodejs/node#21551?

No, but the same comment could apply equally there. Thank you for the nudge, I will cross-link it.

@bakkot
Copy link
Contributor

bakkot commented Dec 14, 2018

Which comes back to the questions I tried to ask. Is that uniformity good, or bad?

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.

@bcoe
Copy link

bcoe commented Dec 14, 2018

@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., mkdir --recursive, as they're added to Node.js.

One thought being we'd release modules like @node/mkdir which provide the behavior conditionally on Node version (shimming otherwise). I could imagine a world where we do something very similar for internal modules, e.g., @std/fs is published to npm by Node.js, and would provide a standardized fs module experience across older Node.js versions.

I also like that modules on npm could similarly be published that provide the same behavior as @std/foo in the browser.

☝️ I'd put the ability to do this in the good column.

@devsnek
Copy link
Member

devsnek commented Dec 14, 2018

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"

@obedm503
Copy link

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.

@isaacs
Copy link

isaacs commented Dec 14, 2018

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 @foo/bar pattern for namespacing modules. The fact that they are doing this with npm, and that npm (the tool, service, and company) has been a part of establishing this pattern, is somewhat orthogonal to the fact that the pattern is established at this point.

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.

@domenic
Copy link
Member

domenic commented Dec 14, 2018

@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 std:fs, you can publish that as @bencoe/fs and then configure your loader to map std:fs to @bencode/fs. Whether built-in modules use std: or @std/ is really immaterial.

@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?

@robpalme
Copy link

robpalme commented Dec 14, 2018

@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:

  • they come with a stronger compatibility promise than the average package, e.g. spec, conformance tests
  • users ought not to need to do any security/backgrounds check on stdlib APIs as you do when taking third-party code and assessing the trustworthiness of the source (assuming you avoid polyfills)
  • there's an implication stdlib APIs are cheaper to load than userland packages, e.g. No network fetch and the VM might even share the native DLL in memory across processes.
  • stdlib APIs come with an expectation that they will correctly work in all JS host environments

So let's signal this value to users in the naming scheme. It will aid adoption.

@bcoe
Copy link

bcoe commented Dec 14, 2018

you can publish that as @bencoe/fs and then configure your loader to map std:fs

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):

const {mkdir} = require('@std/fs')

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.

My opinion is that these APIs truly are different AND users should value them differently because...

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.

@devsnek
Copy link
Member

devsnek commented Dec 14, 2018

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.

@littledan
Copy link
Member

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.

@obedm503
Copy link

obedm503 commented Jan 2, 2019

it's a little hard to introduce new keywords, since they can be used as variables

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. "use standard"; could be used to make use a reserved keyword. this would also work outside of modules in the case of dynamic use(). But I would understand if TC39 is apprehensive about new directives too.


I also thought of import native (double keyword), native being the thing that differentiates. But I have even less of an idea of how this would work with dynamic imports

import native { Instant } from 'temporal';

@annevk
Copy link
Member

annevk commented Jan 2, 2019

@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.)

@isaacs
Copy link

isaacs commented Jan 2, 2019

@glen-84

My point was that the language specification doesn't need the namespace on npm. TC-39 can just clobber it
Wow. That's the solution? So a new runtime "blinker" (Domenic's example) simply shadows a userland scope that may have been used in thousands of dependent packages for multiple years? So a user of an npm package/scope really has no guarantee that they'll get to keep it?

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 @blinker scope for their builtin modules, then yes, this would collide with an existing userland packages in that scope. It's probably a bad idea to do that, if for no other reason than it would likely reduce adoption of their platform. No one who isn't using blinker would be affected, of course, so browsers and node.js would just keep going on as normal.

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.

@devsnek
Copy link
Member

devsnek commented Jan 2, 2019

@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 ajax: because it's a cowpath, I would bring up these same points. This isn't a problem with npm specifically, it's a problem with the larger design.

@marcthe12
Copy link

marcthe12 commented Jan 2, 2019

We have 4 locations to resolve an import: URL, local file, native/builtin and import maps.
These 4 locations need a way to specifed explicit at times. It also should be optional and instead follow a standard resolution logic.

Something like this
Import {app} from file './App'
Import('./App', file)

Import {sin} from builtn '@std/math'

It can be ignored but the it will follow some order like: import maps, npm modules, file, builtin.

@isaacs
Copy link

isaacs commented Jan 2, 2019

@devsnek

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.

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.

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 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?

@Pauan
Copy link

Pauan commented Jan 2, 2019

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.

(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.

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?

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 @@unscopables.

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.

@isaacs
Copy link

isaacs commented Jan 3, 2019

@Pauan

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.

When new features are added to JavaScript, it's done in such a way that it cannot impact existing code.

There is no approach which guarantees that userland code will never conflict with a runtime's chosen namespace. Qv: Babel's implementation of import. I guarantee you that people would be writing import {readFile} from native 'fs' in no time, because transpilation. We are already in that place.

That being said, of course import {foo} from '@bar/baz' is easier to polyfill than import {foo} from \\polywog '::bar::baz', but my argument is that that's a good thing, not a bad thing. The costs are minor, and the benefits are high.

@littledan
Copy link
Member

if blinker doesn't deal with the existence of userland code in the blinker namespace, then they'll face challenges gaining adoption.

Isn't this a potential issue if we allow polyfilling of built-in modules at all, regardless of the specifier syntax?

@Pauan
Copy link

Pauan commented Jan 3, 2019

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.

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 is no approach which guarantees that userland code will never conflict with a runtime's chosen namespace. Qv: Babel's implementation of import. I guarantee you that people would be writing import {readFile} from native 'fs' in no time, because transpilation. We are already in that place.

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.

That being said, of course import {foo} from '@bar/baz' is easier to polyfill than import {foo} from \polywog '::bar::baz', but my argument is that that's a good thing, not a bad thing. The costs are minor, and the benefits are high.

I don't really buy that argument. With npm-style namespaces, you have to do npm install @bar/baz, and manage versions, which is a pretty weird way to do polyfills (people don't do npm install fs to install an fs polyfill).

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.

@Pauan
Copy link

Pauan commented Jan 3, 2019

@littledan Isn't this a potential issue if we allow polyfilling of built-in modules at all, regardless of the specifier syntax?

My assumption is that it shouldn't be possible to create an npm package name which contains std:, so there's no possibility of conflict.

In other words, importing std:blinker isn't the same as importing blinker or @blinker, so there's no confusion or conflict, and thus adoption shouldn't be hindered either.

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.

@littledan
Copy link
Member

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.

@ljharb
Copy link
Member

ljharb commented Jan 3, 2019

You already can add "@standard/somethingNew": "github URL to alternative repo", for example, to package.json, so "control of the scope" doesn't block anyone from being installed under that name (except via the npm registry itself, of course).

@mcollina
Copy link

mcollina commented Jan 3, 2019

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:

  1. Can the runtime augment the standard library? Or is the standard library only developed/standardized at TC39?
  2. Will there be a "web" library as well, with web specific modules standardized at WHATWG?
  3. Will there be a runtime specific ("chrome", "firefox" or "node") namespace?

I think that answering those questions would help clarify and decide one way or another.

@glen-84
Copy link

glen-84 commented Jan 3, 2019

@mcollina It's probably best to create separate issues for those questions (if they don't already exist).

@littledan
Copy link
Member

These issues are already widely under discussion:

  1. Many people are treating this capability as a requirement. See Polyfilling modules #2
  2. Yes, this is under discussion, see https://github.com/domenic/async-local-storage and https://github.com/valdrinkoshi/virtual-scroller
  3. I haven't heard ideas about browser-specific features--i think everyone came away from the previous prefixed approach with the conclusion that it was a bad idea, and browsers should ship features unprefixed and headed towards an eventual standards track. But I imagine Node.js may continue to add its own built-in modules.

@mcollina
Copy link

mcollina commented Jan 3, 2019

Many people are treating this capability as a requirement. See #2

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:

import { Instant } from std 'temporal';
// or
import { Instant } from '@std/temporal';
import { storage } from web "async-local-storage";
// or
import { storage } from "@web/async-local-storage";

Note that using the @std/ and @web/ prefixes work the same.

I haven't heard ideas about browser-specific features--i think everyone came away from the previous prefixed approach with the conclusion that it was a bad idea, and browsers should ship features unprefixed and headed towards an eventual standards track. But I imagine Node.js may continue to add its own built-in modules.

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.

@annevk
Copy link
Member

annevk commented Jan 3, 2019

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?

@glen-84
Copy link

glen-84 commented Jan 3, 2019

I prefer std and web to the actual names of standards organizations. Userland developers should not need to know what tc39 and whatwg refers to (and it might even change?).

@domenic
Copy link
Member

domenic commented Jan 3, 2019

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 "std:" (or whatever shared prefix).

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.

@isaacs
Copy link

isaacs commented Jan 3, 2019

I think @domenic is right about this. I'd expect to see Node.js perhaps namespace things that are node-specific, but use std for anything that's part of the standard.

@littledan
Copy link
Member

We have a separate issue for the split vs unified namespace question: #14

@styfle
Copy link

styfle commented Jan 3, 2019

Thanks, I deleted my comments since I was off topic. I'll continue discussion on #14 👍

MylesBorins added a commit to MylesBorins/agendas that referenced this issue Jan 29, 2019
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
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