Closed guybedford closed 10 years ago
I would be interested in something like this. I have been trying to figure out how to do something close to it.
My guess for the standard future workflow for building large web apps would be structured something like this.
My webapp would be composed of many different components, each being complex enough to be described as a web app feature. So taking examples from the domain I'm most familiar with, an FXTile, a FXMarketTicket, a OrderTicket, a live updating instrument grid (showing FX currencies/Bonds for example), each of these would be a feature of the application.
Each should be developed separately as stand alone npm
packages which may depend on other npm
packages containing Custom Elements and libraries etc. So I would imagine an FXTile may contain many modules (inside it's src/
directory) which can refer to each other in a relative fashion but which would refer to their npm
dependencies absolutely.
FXTile/ src/ FxTile.js node_modules/ MyFxTradeFactory/
I would not want to have the value node_modules
as part of the identifiers that each of these components will use when referring to required npm
dependencies. Inside my FxTile
module I would like to write import {FxTradeFactory} from "MyFxTradeFactory";
and not import {FxTradeFactory} from "node_modules/MyFxTradeFactory";
.
At the same time I would like to use relative paths within the src/
directories and have them resolve to files within src/
and for any relative import IDs within node_modules/
to resolve to their own local package values. I figured the way to do that would be two loaders one that is rooted at the dependency folder node_modules/
and another at the src/
folder, I just wasn't sure how I'd wire that up as the FxTile could either be used as the src/
root or as another node_modules/
dependency when it's pulled into the main application so it couldn't create a new Loader
in it's main package class...
Setting a global value to change the behavior of subsequent function calls is prone to errors, is difficult to debug, and makes composition difficult.
I think you have introduced the problem in part by insisting that dependencies be named with a special prefix 'lib/' or 'js/'. If we drop that requirement, the problem changes. Don't allow the choice 'lib/jquery' or 'js/jquery': insist on 'jquery'. This is the solution that npm uses with great success.
While I'm not keen on the proposed solution, a solution to this issue broadly must be part of the standard.
The distinction between app and library code is a slippery one, and when CDN deployment is taken into consideration, where the app and library JS may be on the CDN and the index.html is under the main hostname, I do not think this convention will be that helpful vs the mistakes a developer may make trying to use it. I prefer to always use the same resolution logic, and not have this specialized pathway.
James raises a good point here.
We are actually dealing with two URL concepts here, so perhaps it would make more sense to make this explicit:
System.baseURL = '/lib';
System.relativeURL = '/app';
System.import('jquery') // -> '/lib/jquery.js'
System.import('./main') // -> '/app/main.js'
I like this idea a lot, and I think it cuts out a lot of issues that developers will have with the loader. The ./
creates a natural hole in our standard baseURL resolution, which is sorely needed for app code.
That said, I know introducing a new concept is not ideal. Feedback still very much welcome.
Note that the module tag could also use this same default relativeURL
convention allowing the following to work:
<script>
System.baseURL = '/lib';
System.relativeURL = '/app';
</script>
<module>
import "jquery";
import "./local/module";
</module>
By default, both the baseURL
and relativeURL
would be the current page. With either customizable.
Perhaps it is just more complexity on top of paths, but otherwise why have baseURL
at all when the same can be accomplished with:
<script>
System.paths['*'] = '/lib/*.js';
System.paths['app/*'] = '/app/*.js';
</script>
The whole point is to try and make the standard use case the simplest. And this is the standard use case.
I'm just quoting you out of order: "A strong motivation for working with normalized module names, is to try and ensure that users refer to external libraries in the same way." "The whole point is to try and make the standard use case the simplest. " "...why have baseURL at all when the same can be accomplished with: " (paths).
I don't see the critical motivation for System.baseURL or System.relativeURL, given that (IMO) we must have paths anyway.
It depends on how System.paths is defined, but it does seem that either baseURL + paths or just paths is enough, no need for relativeURL.
One note on choosing a System.paths style: System.paths['*'] = '/lib/*.js'
gives me some concern because, at least for AMD systems, we have used ID-to-path resolution for strings like 'some/thing.html', where some/thing is a module ID turned to a path, then the .html suffix is added. So that has been useful: just translating a module ID to a path without extension. Specifying a .js
in the paths entry seems to complicate the rules around it. Maybe it is workable though.
I don't think it needs further discussion here, just something to consider later when a paths config is getting more worked out. For this bug, it seems like a special relative import setting is not needed on its own.
I actually agree with both of you here, and was wondering myself today why we even need baseURL
.
System.paths does solve the problem very nicely as far as we need, keeping everything within a single model:
<script>
System.paths['*'] = 'lib/*';
System.paths['app/*'] = 'app/*';
</script>
<module>
import "jquery"; // -> lib/jquery.js
import "app/local/module"; // -> app/local/module.js
</module>
I'm all for this, keeping these concepts simple. I've summarized the suggestions in https://github.com/jorendorff/js-loaders/issues/25
Proposal retracted!
This is for the browser spec only.
If Loader.prototype.import does run normalization (as discussed in https://github.com/jorendorff/js-loaders/issues/89), then there may be an interesting possible role for the default parentName in normalizations.
When creating an application, we have two major types of code - application code and library code.
Typically we can think of these as two folders (say
app
andlib
).A strong motivation for working with normalized module names, is to try and ensure that users refer to external libraries in the same way.
But with a
lib
folder, one user might refer tolib/jquery
and another user might refer tojs/jquery
.The code is now diverging in meaning.
The ideal configuration for the
baseURL
is actually to set it directly to the library code folder.This way requires can work easily of the form
jquery
orunderscore
.But the question then is how we refer to application code.
Typically, one would define a new path, that separates the
app
folder from the baseURL:I'm wondering if we can do this better.
If the
System
loader acted as if the relative import name was the current document path, then we can use relative names to refer to application code.For example:
Would load "my-app-code.js" from the HTML page path.
But we can also then load from the baseURL as well:
The restrictions of such a system are:
These are strong conventions, but good ones, encouraging modular paths and sensible code separation.
That said, I understand that the spec may not want to impose strong conventions.
I hope I've explained this properly. Any questions just ask. I don't want to push an idea that isn't useful, I just thought maybe this could solve a common problem.
@jrburke @unscriptable @johnjbarton @probins I'd value your feedback too on whether this might be useful. If not, no worries at all.