-
Notifications
You must be signed in to change notification settings - Fork 0
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
General discussion #1
Comments
This will result in unexpected behavior later when the module is used. It might be difficult to track it back to the fact that the module wasnt loaded. The error we get now isnt very helpful in requirejs but it blows up early!
What about compatibility with modules that were authored with packages in mind? Excludes and includes are powerful optimization knobs.
Local requires are useful when authoring packages. Wish List: I would love a loader which can figure out dependencies based on Things I cannot live without: RequireJS has a complicated build config. But it allows me to package my applications according to real world usage. For example I have a script which will inspect the page and dynamically load other bundles at runtime. This can be complicated to configure but its a very powerful production optimization! |
Hey @treasonx thanks for commenting! Gonna address in order:
Since this rejects a promise (or throws an error), if it happens in one of your modules, it will not execute with
As for real-world usage, that's exactly where I'm coming from as well, and I want to ensure that this doesn't ruin the flexibility. |
Oh right, I want to add that I'm not going to remove the |
Crazy example of behavior with undefined return for unloaded module.
I am trying to illustrate that the dev might not use the module until much later in the applications life. This would result in an error. An experienced dev will be able to quickly figure this out, but a novice will lose a lot of time trying to figure out how their module became undefined! Maybe at least a warning in some form of debug mode would help. |
Right, but since that's simplified commonjs all dependencies scanned via |
AMD conflates module format and loader, so it’s important to clarify which parts are important and which parts go away. I personally have no qualms about AMD dying to be replaced by a high-quality standardised module format and/or loader that covers all known use cases.
IIRC contexts are some RequireJS-specific thing, and exclude/include are part of the build system, which are mandatory for shaping built layers.
As you’ve described it, this is a bad API design as @treasonx already points out. Silent failure is the worst operation mode and should never be included. If this were done then this would also need to introduce a new different failure signaling mechanism. Like, say, Promises.
This is what
I don’t understand this.
This seems like a good enhancement, and one that I believe exists in an incomplete form in the WHATWG loader.
I don’t understand what this means. Are you describing
Are you talking about removing
This would cause an inconsistency when switching between unbuilt and built code. I don’t think named modules should be a thing at all, and would instead say that AMD loaders need to have a standard for introducing modules to a loader cache. I think the WHATWG loader has some API for doing this but I am not sure which one it is. In Dojo loader and Node.js loader this involves adding keys to
Plugins are not just used for loading other files! Look at We are working on new loader proposal right now for Dojo 2 and were going to bring this up for public feedback soon. Some of the contributors have been already talking to other platform-agnostic loader vendors (like James Burke) so would be happy to subsume wishlist under that initiative and help bring it to completion. That said, it’s not clear what is going to happen with the WHATWG loader at this point but I think that it doesn’t make sense to start an “AMD 2” without knowing what is going to happen upstream. |
Awesome, I like to think of AMD v2 as a shift in that direction. I'm not saying this is going to be that spec, but I hope it can at least spark some discussion.
Right, as you have probably guessed, I meant...
...very literally. I'm intentionally conflating to get use cases and implementations on the table. Currently no loaders operate in harmony and that's really sad. I've authored numerous plugins that support: Require, Curl, and Dojo and they have all been super complicated and inconsistent.
Maybe you and a handful of others don't find this confusing, but everyone else does.
I'm thinking Promises or throwing, so errors are loud and clear. A rejection will fail the dependency requirement for a module and not execute the callback. You can see an example of the implementation I'm thinking of in the above screenshot to @treasonx.
require.config({
packages: [
{ name: 'my-component', main: 'index.js', location: '../components/my-component' },
{ name: 'their-component', main: 'index.js', location: '../components/their-component' },
{ name: 'our-component', main: 'index.js', location: '../components/our-component' },
{ name: 'your-component', main: 'index.js', location: '../components/your-component' }
]
});
require('my-component');
require('their-component');
require('our-component');
require('your-component'); Compared to: require('../components/my-component/');
require('../components/their-component/');
require('../components/our-component/');
require('../components/your-component/');
Ha, I wasn't brave enough to volunteer discarding them completely, even though I agree completely.
Plugins with any kind of configuration in the identifier should be abolished imo. They are awful to look at and configure. Use the shared loader configuration that's what it's there for! Use.js is still totally usable with this approach. require.config({
paths: { shim: { 'use-js' } }
});
require.load('some-module.shim'); It's the same semantics as |
I’d like to try to avoid hyperbole during this discussion (“handful of others”, “everyone else”) to keep it focused on facts for the time being. If people don’t understand If one can understand that a URL is not the same as a file path, one can also understand that a module ID is not the same as a file path. To make an nginx analogy,
I think there are two unresolvable problems with this.
If you have a module that you want to distribute that is usable across multiple platforms, and in the browser case it has a dependency on a different module that assumes the DOM API is available (say it accesses Thanks, |
My point was that you were trivializing complexity. I want to limit the amount of abstract concepts that surround a module being loaded. Thinking only about local and global paths, absolute module names that get loaded via a registered resolver, and plugins. I do think map is important, and I'm easily swayed in that regard, since I've used it quite a bit for DI during testing. Answering the unresolvable problems:
// index.js
define(function(require, exports, module) {
'use strict';
exports.template = require('./template.html');
});
I wrote: https://gist.github.com/tbranyen/9667269 to solve that problem slightly differently via configuration. I suppose this will be one place of contention. I like simple and it seems most people like simplicity. Why complicate a basic premise of requiring and sometimes transforming? |
OK, sorry if it seemed like that is what I was doing! I thought all I was saying was that
Why would you only use a resolver for absolute module ids? Are you thinking that resolver and transformer are separate steps? IIRC that is how the WHATWG loader was designed, it had a few different separate steps for these things.
OK, so I guess I am not sure why the loader should have this index thing at all? How is
I think I did not explain myself clearly. Let’s say you are writing some hypothetical module that generates output. It wants to write output to the DOM in the browser or write output to a stream in not-a-browser. For the DOM part you want to use jQuery for . jQuery will throw an error if you load it into an environment without a DOM API. How do you propose this module will work? With an AMD plugin today it is trivial: define([ "dojo/has!host-browser?jquery" ], function (jquery) {
return {
log() {
if (jquery) {
// ...
}
}
};
}); Best, |
In general, I think what you are looking for are changes in loader implementation and config API for that loader. That is a fine thing to push for, but I would avoid creating a new declarative module API (the However, AMD users adopting a new loader seems perfectly fine thing to do and the sort of freshness people like to entertain. So, I would position this effort as a "a modern AMD loader and toolchain for a modern time", and not call it AMDv2. For me, I am interested in a loader that had this sort of API: https://github.com/jrburke/module/blob/master/docs/loader-config.md but loaded AMD modules, so did not have Some notes on your items given that context:
The
As others have pointed out, this is hazardous. ES modules won't work that way, and if the concern is needing a callback for a require() done in a browser web console, That will also be a concern in ES modules. Maybe ES7 await/async stuff will help with that long term. For now, in the browser console type
This sounds like switching the default for packages config from main.js to index.js. That seems like a reasonable thing to do, but as others have mentioned, a way to indicate what the package .json metadata specifies for "main" is still needed.
I didn't follow this one. This is maybe influenced by how node goes about module resolution? But node's resolution mixes up paths and module IDs too much, which does not make sense in particular once modules can be bundled. Module IDs need to be separate entities from paths.
If "resolve" means use node's resolve to find a module path, then that seems like a possibility. Although not very portable for loaders loaded in the browser that do dynamic loads. Anything that requires a file system scan, would be bad in the browser. Dojo did this way back for i18n locales, and there was a constant issue of developers thinking something was wrong when they looked in the web console and saw a bunch of 404 errors. If "resolve" means use node's module system to create the module, then r.js does this today when run in node. There are limits to its effectiveness because a mixed case, where a path-found module loaded by r.js -> node-instantiated module -> define'd module that should be found via r.js config. This is because of the node module system though. For any case of "resolve" though it seems like it would only be for a specific environment, and specifically hard to support for dynamically loaded code in the browser. In general, node's file layout and package.json scanning is not friendly for web-based module loaders. Same with bower's default layout. If those package managers would do the equivalent of adapt-pkg-main that would help a lot, avoid config. Then map/alias config for any nested dependencies that cannot be flattened.
That sounds like map config.
This can be encouraged through user practice, or even by a loader that just wanted to be stricter, but as mentioned above, I do not think it is worth baking this into a new AMD declarative module API due to developer fatigue about module formats.
This sounds like "if a named module is loaded but the name expected by the loader is different, the loader should use the name it expects". That may be possible in modern browsers and if limited to IE 10+. It seems a bit early to cut out IE 9, but again, something a new loader could decide to do. It might complicate things a bit, as related to files loaded that have multiple named modules, so I would probably place this one low on the list, would need some good tests and experiments before promising this feature.
@csnover addressed talked to this, but to add a bit more: What about jquery plugins that are like 'jquery.scrollbar'? Does that qualify? The main issue is treating a module ID like a path. It is a lot cleaner to treat it as just a module ID. Plus, different plugins can operate on multiple file extensions, and have some that operate on the same file. A 'text' plugin can load .txt and .html, and a 'template' plugin could also handle .html files. If both plugins are used in the same project, I do not see how to resolve that conflict, without a lot more config. It is best to treat loader plugins as something that can be explicitly parsed out. What could give you something like this would be a loader that had those explicit function hooks for the module lifecycle as mentioned in the loader-config.md link, and if you wanted to create a shortcut for that sort of behavior on a per-project basis, overriding the normalize hook to convert select file extensions to prefixed 'plugin!' module IDs would be possible. |
@jrburke awesome, thanks for the feedback definitely a lot to think through.
This is how I initially approached it, but I've been convinced that we need more. I want to implement a future loader and optimizer to a reproducible standard. I'd be happy to change the name from AMD v2, but I chose that right now so that everyone knows where I'm coming from.
Yes, I like
That's exactly how this works, except without needing
Yeah, that would actually be perfect. Makes it more explicit than
This comes as inspiration from Node. If you do not use a relative path, it's a module identifier and is passed on to a resolver (like node_modules lookup). I've had so many less problems with AMD and configuring paths if I stick to relative.
I disagree very much here. I think we look over the fact that
Yeah, I was specifically alluding to
It just drives me nuts that there are sooooo many ways to define a module and some of them are wicked confusing, for instance lodash uses: define(function() {
return _;
}); Is this AMD? Maybe Simplified CommonJS? Is it mixed? The fact that mixed even exists speaks to a very large problem with the specification. It gets even more confusing when you see an example like this and try and suss out what the module value is: define(function(require, exports) {
exports.value = 'am i a property on the module value?';
return { value: 'or am i?' };
});
It would if you had a "scrollbar" plugin registered. Although maybe this is better served under something new called Thanks again! |
Some things I'd like to pitch in:
Just my two cents (and wish list). Been using AMD eversince and still prefer it over Browserify. |
I wonder if mostly what you are looking for is more uniformity across amd-based projects. If projects are laid out to convention, it avoids a lot of configuration. I can see the case for just better project setup tooling and evangelizing it. This is really what node has too, all projects are laid out the same, so it avoids some configuration issues. They can do directory scanning too, but we can avoid that with some tools that know about the AMD conventions. The tooling can also just be focused on creation/install time actions, not needed to run tooling for every app file change. So one of the great benefits of AMD loading in the browser, no need for build tools to start, is still maintained. I think the tooling could work out to be the following. I will focus on npm-based tooling, but a similar set could be made for bower. The names are just placeholders, to illustrate the concept:
For all files in in the 'app' directory, './relativeId' require() calls are used for modules in the app dir. For third party code, like 'jquery', those are still top level ID references, and would be loaded from the node_modules directory.
If the node_modules ended up having some nested node_modules because npm dedupe did not completely work, then it can insert a map config in the app.js for that case. This assumes the packages installed are AMD compatible, but there could be a flag in the app's package.json to convert cjs modules on amd-npm install. With those tools, I believe that will homogenize projects, and the bonus is that the user does not deal with config manually any more, unless they want to get fancy for things like waitSeconds. Some feedback from your previous reply to me:
I feel like this is perhaps a lack of better messaging around how to refer to modules. If wanting something that is relative to another module in a directory (package), then './' is the thing to use. Maybe the confusing part is that AMD allows you to just lay all your modules flat under baseUrl, and those modules could be a mix of local and third party code. If that is the case, then I believe the project layout above, that sets baseUrl to the node_modules and then just one paths config for app, with all the local modules in app, would address this concern.
Maybe what you mean to suggest here is that the loader wants to resolve 'pkgName', it should ask for pkgName/package.json via an XHR call, JSON.parse it, then find the "main" module and then create an internal mapping so that when a module asks for 'pkgName', the 'pkgName/mainValue' is used for that dependency? If so, then that is what adapt-pkg-main does, but just writes out a module at 'pkgName.js' that points to the main module, and does it at install-time. This avoids the need for CORS for when the JS is on a different domain than the web page. It also optimizes uniformly, does not require any special adapters in build tools or almond-like simpler AMD API shims. If the concern is perhaps a tool modifying contents in node_modules, npm does this today with pacakge.json files, and to compile binary pieces. So an amd-npm tool should be fine in doing this.
AMD was made that way to try to reduce the amount of boilerplate that a devloper had to type, since that has been a criticism of a module system that uses a wrapping. While it would be nice to reduce the combinations now, I think there would be some pushback from some developers on the added verbosity. Also see my comments about the social lack of interest in entertaining more module systems. If we wanted to really make a pass at a new declarative form, then I would favor going further than just massaging the define signature, and going more with something like jrburke/module, but with module.define(function(){}) as the define(function(){}) replacement. That module system fixes other issues like inline nested module defines, and clearer top-level loading APIs. I just do not see any appetite from developers for it given the ES cloud of uncertainty, and node's unwillingness to consider changes in their module system, even though it is getting dated as more people want things to work well for dynamic loading in the browser. In my fantasy world, we could work out something with the node folks that was better, and just skip waiting for ES. I just don't see it with the node/io.js split and the illusion that ES modules are "just around the corner". But focusing on tooling to create homogenized AMD setups could go very far, as it is directly useful to developers today. They get immediate benefit vs. having to learn and understand new specs for unclear benefits. If the tooling for project setups is very successful, then that tooling can choose stricter forms for define() calls and config and allow us then to later make loaders that just target only those smaller stricter forms. |
npm@3 will have a much more predictable install/dedupe behaviour. It might Also, for npm to work well, the loader must support CJS+xhr out of the box. This is the direction Rave.js was going to. It's a pity John has no more On Wed, 18 Mar 2015 at 18:26 James Burke notifications@github.com wrote:
|
Will there continue to be a need for loaders or will ES6 modules eliminate that need? Does ES6 solve the performance problems that bundling solves? |
I absolutely agree. We become overly attached to the identity of our tools.
This is surprising behavior to say the least. It makes it hard to reason about behavior and defeats attempts at tooling and code analysis. I would also argue it is well in the vein of conflating module formats with with module loaders in its own right. One of the unpleasant things is that more and more people are using CommonJS as a transpilation target. The static nature of ESModules is a major step in the right direction.
I agree that it makes no sense to create an "AMD 2", fragmenting an already fragmented community of web developers trying to make sense of simultaneous trend towards ES6 module syntax and CommonJS transpilation.
💯
That's all well and good until you consider the loader itself as a platform. Once something is written like that and shipped, every external tool has to parse all variants of the syntax. Plugin syntax hurts interop between module formats.
And every external consumer of said customer(s) packages hates working with them.
Yeah it is awful. And another module format will make things worse.
As @fskreuz said,
Both layouts are problematic and incredibly far from ideal. npm's
That's a good point. That is undeniably confusing. I realize I arrived at this discussion pretty late, but I wanted to make these remarks regardless. One thing that I am frustrated by is that I feel that there is a general and unhelpful attitude of AMD vs WebPack/Browserify, and that people are forgetting that there are projects like SystemJS + jspm that are also browser oriented. We need better browser oriented tools that are not built on Node/CommonJS assumptions but are still able to consume modules written for them. I think that is a shared goal of the AMD and SystemJS + jspm communities and of the whatwg loader specification. |
Let's say someone was working on improving AMD including a new loader and optimizer. What would you hope was different?
As developers interested in AMD, we need to break down the current status-quo of how we think and use this specification. It's a powerful concept that has unfortunately aged very poorly. Think, do we really need this many different ways to define and require a module: https://gist.github.com/tbranyen/8480792?
Can you help me make a wishlist of possible changes, lets not see AMD die because we didn't care enough to try and improve it.
My wishlist:
Note: there are specs and implementations, they are tightly coupled so I want feedback on both
paths
andmap
will need to be implementedindex.js
from folder pathssrc/
would loadsrc/index.js
require('a')
vsrequire('./a')
define(Array, fn)
or SCJS-style:define(fn)
no ambiguity in betweenThe text was updated successfully, but these errors were encountered: