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

New AMD-style module loader. #254

Merged
merged 14 commits into from
May 1, 2018
Merged

New AMD-style module loader. #254

merged 14 commits into from
May 1, 2018

Conversation

aomarks
Copy link
Member

@aomarks aomarks commented Apr 26, 2018

This is a replacement for our use of RequireJS to use as the loader for our ES modules transform. It is AMD-ish, but not 100% compliant with the AMD spec or RequireJS.

Differences from RequireJS:

  • 0.9 KB minified + compressed vs 6.6 KB for RequireJS.

  • Correctly resolves relative paths above the base URL (see [build/polyserve] AMD transform/requirejs results in multiple requests for same import #211 and possibly [polyserve] Duplicate CE definition errors in Firefox and Edge when combining paths and bare specifiers in tests #203).

  • Is entirely path-based. Does not allow registering a module by name. The only API is define(deps, factory).

  • define() module factories are always executed. RequireJS only starts executing a module dependency graph when triggered with a require(). The Babel AMD transform only emits define() calls, so this eliminates the need for us to inject a require() call during build.

  • Identifies top-level scripts and ensures they execute in order, like type=module scripts do. This would eliminate some complexity in our build pipeline, which does the same thing using code generation, and has to do some ugly tricks to preserve script-order information through the HtmlSplitter process.

  • Modules can request a meta dependency, which is resolved to {url: <moduleUrl>}. This should be able to simplify our build-time import.meta transform, and has the advantage of not being tied to a particular static path (i.e. you could move the module around your tree post-build and still get a correct URL).

  • Modules always provide an exports object to their dependents, even if nothing was exported from the module, which is more similar to native ES modules. This would fix https://github.com/Polymer/polymer-cli/issues/995 in the general case.

Copy link
Member

@bicknellr bicknellr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(just talked offline) It sounds like this needs to work in at least one place that doesn't have URL (IE11), but here's a few things that it could be useful for anyway.

Module.prototype.resolveUrl = function(path) {
if (path.indexOf('://') !== -1) {
// Already a fully qualified URL.
// TODO(aomarks) Is this a good enough check?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you have URL, then I think you can try-catch new URL(path), which throws if it's not given a full URL, IIRC.

return path;
}
// Just let the browser do path resolution/normalization.
Module.resolverAnchor.href = this.basePath + path;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, if you have URL, (new URL(path, this.basePath)).href is safer since it handles joining them properly:

> "http://example.com/page" + "some/path.js"
< "http://example.com/pagesome/path.js"

... when really it should be:

> (new URL("some/path.js", "http://example.com/page")).href
< "http://example.com/some/path.js"

(this.basePath needs to be a full URL though.)

return url + '/';
}
// http://example.com/foo/bar.html -> http://example.com/foo/
return url.substring(0, lastSlash + 1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, if you have URL: const getBasePath = url => new URL("./", url).href;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth noting that IE 11 does not have URL, but the webcomponents polyfill includes it.


# @polymer/amd-loader

A JavaScript library that loads AMD-style modules.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add some text saying that it loads path style modules and not name style modules, and that it's intended to mimic the semantics of es6 modules

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, let's call out that it's a subset and mention the differences you have in the PR description.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just pushed a much more complete README, PTAL.

(function() {

function Registry() {
this.modules = {};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use Object.create(null) for a map-style object, otherwise stuff like this.modules['valueOf'] will be truthy.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

this.modules = {};
this.topLevelIdx = 0;
this.previousTopLevelUrl = undefined;
this.pendingDefine = undefined;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Document these fields please. Maybe use // @ts-check and jsdoc to get basic type checking, or use full typescript and tsc can also handle transpiling to ES5

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 on TypeScript

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Converted to TypeScript, added tslint, and added a fair bit of documentation. PTAL!

function getBasePath(url) {
// TODO(aomarks) Maybe a better way to do this.
var lastSlash = url.lastIndexOf('/');
if (lastSlash === url.indexOf('://') + 2) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about urls like //example.com?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be handled now. All URLs are now normalized using the <a> before we compute the base path, which should always add an explicit scheme. Tested it in Chrome/Firefox/Safari/IE11 to be sure.

return url + '/';
}
// http://example.com/foo/bar.html -> http://example.com/foo/
return url.substring(0, lastSlash + 1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth noting that IE 11 does not have URL, but the webcomponents polyfill includes it.

this.pendingDefine = undefined;
}

Registry.prototype.get = function(url) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Document these methods as well please. For most of them, just the types of the params and return is enough, but for ones with complicated invariants they're maintaining those would be valuable as well.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@justinfagnani
Copy link
Contributor

A few high-level things:

  • @azakus apparently added the URL polyfill to the platform layer of the webcomponents polyfills, so it should be ok to use it.
  • If we consider this to be more of a polyfill for modules, I wonder if it should be loaded as part of the webcomponents polyfills? <- @sorvell @kevinpschaaf
  • It'd be nice to write this in TypeScript to get the type-checking benefits we're used to. TODO'able since we want to not spend too much time on this now.

@aomarks
Copy link
Member Author

aomarks commented Apr 26, 2018

@azakus apparently added the URL polyfill to the platform layer of the webcomponents polyfills, so it should be ok to use it.

Seems like with some unit tests we can get by with a built-in function that just has to find a base path from a URL, no? The URL polyfill is bigger than this entire library, and in theory this loader could be useful to people who don't otherwise need any webcomponents polyfills.


# @polymer/amd-loader

A JavaScript library that loads AMD-style modules.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, let's call out that it's a subset and mention the differences you have in the PR description.


(function() {

function Registry() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does Registry need to be a "class" at all? If it's a singleton you can just put the state and functions in the module closure.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. I thought the class might be easier to read, but this seems simpler. Incidentally it helps with minimization, since neither babel-minify nor uglify-js seem to want to rename prototype properties.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also got rid of the Module class, from offline discussion. Size is now down to 0.7KB :)

this.modules = {};
this.topLevelIdx = 0;
this.previousTopLevelUrl = undefined;
this.pendingDefine = undefined;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 on TypeScript


(function() {

function Registry() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even though what's inside the module closure is top-level, I'd still indent it. Initially I thought some things below were outside of the IIFE for some reason.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just what clang-format does. I think I prefer it?

}
}

for (var i = 0; i < (deps || []).length; i++) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(deps || []).length will be evaluated every iteration

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

} else if (depSpec === 'require') {
factoryArgs[i] = this.makeDynamicImporter();

} else if (depSpec === 'meta') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AMD includes a "module" built-in dependency, but the spec doesn't seem to specify it that I can see. In IMD it has the exports and id properties. Maybe we dug that out of RequireJS source. Seems like in this loader module.id might === import.meta.url.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the AMD spec defers to the CommonJS spec for module, which says:

  1. The "module" object must have a "id" property that is the top-level "id" of the module. The "id" property must be such that require(module.id) will return the exports object from which the module.id originated. (That is to say module.id can be passed to another module, and requiring that must return the original module). When feasible this property should be read-only, don't delete.

  2. The "module" object may have a "uri" String that is the fully-qualified URI to the resource from which the module was created. The "uri" property must not exist in a sandbox.

http://wiki.commonjs.org/wiki/Modules/1.1.1#Module_Context

I'm open to using module.id or module.uri (or both). Although, I do also like the idea of providing it via meta, because we are trying to match the semantics of import.meta. I don't know if import.meta.scriptElement is going to happen, but if it did, then we would have a natural place to put it. I think this discussion depends on what our position is on interopability with other AMD modules/loaders.

};

script.onerror = function() {
throw new Error('error loading module', url);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this need to propagate to dependents?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, and this now happens.


function getBasePath(url) {
// TODO(aomarks) Maybe a better way to do this.
var lastSlash = url.lastIndexOf('/');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might have issues with slashes in query parameters or fragments. They are possible in module specifiers.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added handling for those cases. Will cover in unit tests.

@@ -0,0 +1,16 @@
{
"name": "@polymer/amd-loader",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want a name that indicates that this isn't full AMD: amd-path-loader?

Copy link
Member Author

@aomarks aomarks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Converted to TypeScript, addressed some comments (still some pending), and did a little refactoring (which incidentally dropped the size a bit too). Also added minified output and tslint.


(function() {

function Registry() {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just what clang-format does. I think I prefer it?


(function() {

function Registry() {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. I thought the class might be easier to read, but this seems simpler. Incidentally it helps with minimization, since neither babel-minify nor uglify-js seem to want to rename prototype properties.

(function() {

function Registry() {
this.modules = {};
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

this.modules = {};
this.topLevelIdx = 0;
this.previousTopLevelUrl = undefined;
this.pendingDefine = undefined;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Converted to TypeScript, added tslint, and added a fair bit of documentation. PTAL!

this.pendingDefine = undefined;
}

Registry.prototype.get = function(url) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

function getBasePath(url) {
// TODO(aomarks) Maybe a better way to do this.
var lastSlash = url.lastIndexOf('/');
if (lastSlash === url.indexOf('://') + 2) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be handled now. All URLs are now normalized using the <a> before we compute the base path, which should always add an explicit scheme. Tested it in Chrome/Firefox/Safari/IE11 to be sure.

}
}

for (var i = 0; i < (deps || []).length; i++) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


function getBasePath(url) {
// TODO(aomarks) Maybe a better way to do this.
var lastSlash = url.lastIndexOf('/');
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added handling for those cases. Will cover in unit tests.

} else if (depSpec === 'require') {
factoryArgs[i] = this.makeDynamicImporter();

} else if (depSpec === 'meta') {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the AMD spec defers to the CommonJS spec for module, which says:

  1. The "module" object must have a "id" property that is the top-level "id" of the module. The "id" property must be such that require(module.id) will return the exports object from which the module.id originated. (That is to say module.id can be passed to another module, and requiring that must return the original module). When feasible this property should be read-only, don't delete.

  2. The "module" object may have a "uri" String that is the fully-qualified URI to the resource from which the module was created. The "uri" property must not exist in a sandbox.

http://wiki.commonjs.org/wiki/Modules/1.1.1#Module_Context

I'm open to using module.id or module.uri (or both). Although, I do also like the idea of providing it via meta, because we are trying to match the semantics of import.meta. I don't know if import.meta.scriptElement is going to happen, but if it did, then we would have a natural place to put it. I think this discussion depends on what our position is on interopability with other AMD modules/loaders.

factory.apply(null, args);
}
mod.resolved = true;
for (const callback of mod.notify) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd be careful with for/of loops if we're trying to be small here, or at least explicitly set downlevelIteration to false in tsconfig.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added downlevelIteration: false.

* that Module objects are created and registered before they are loaded,
* which is why this is not simply part of construction.
*/
define(deps: string[], factory?: (...args: {}[]) => void) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does Babel never output factories that return a value?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We think not.

if (this.needsLoad === false) {
return;
}
this.needsLoad = true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

true?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

const mod = this;
script.onload = function() {
if (pendingDefine !== undefined) {
pendingDefine(mod);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you could just create the module object in define() and stash it in a global, and then you know if define was called. If it wasn't, then create one. This should let you get rid of pendingDefine.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed offline. The timing doesn't work out -- the module object has already been created by the parent module at this time (whichever one first declared a dependency on this module), and here we are actually waiting to find out our URL so that we can associate that existing module object with the dependencies and factory that we now have.

args[i] = this.exports;

} else if (depSpec === 'require') {
args[i] = this.require.bind(this);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this right? I think the require that's injected accepts a single string argument? This might need to be:

args[i] = (dep) => this.require([dep]);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, dynamic require takes an array of dependencies.

} else if (depSpec === 'require') {
args[i] = this.require.bind(this);

} else if (depSpec === 'meta') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd drop this for now pending a change to the import.meta transform.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Going with meta for now.

* it doesn't exist yet.
*/
function getModule(url: string): Module {
let mod = registry[url];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want to be really safe against someone using a module name like __proto__, add a prefix to the url.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We know that the keys are all fully qualified URLs (now with a branded type to be really clear), so I don't think this is a problem.

private resolved = false;

/** Callbacks from dependents waiting for this module to resolve. */
private notify: Array<() => void> = [];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than an array of callbacks, keep an array of dependent modules, and inline onDepResolved into the loop where the callbacks are called.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed offline. The current factoring exploits the fact that the same implementation of require can be used both for module initialization ("define"), and as the function we pass to modules requesting a dynamic "require". However, in the latter case, we don't have any dependents to notify, so that happens only in "define" with an additional closure.

@aomarks aomarks requested a review from usergenic April 27, 2018 18:51
@@ -0,0 +1,2 @@
/node_modules/
/lib/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you're going to have a .gitignore, you're going to need a .npmignore because, if memory serves, npm publish honors a .gitignore if no .npmignore is present, because the world would be too predictable and reasonable otherwise.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. Empty .npmignore will do

}
}

for (let i = 0; i < deps.length; i++) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this comment is golfy, but is there a reason not to do:

for (const depSpec of deps) {
   ...
   args.push(...)
   ...
}

instead of args[i] = ...?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

};

script.onerror = () => {
throw new Error('error loading module ' + mod.url);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this do anything functionally different than if you hadn't added the onerror?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, but now it does because there is propagation.

checkDeps();
}

let pendingDefine: ((mod: Module) => void)|undefined = undefined;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is kindof maybe explained inside window.define's leading comment, but it could probably use a comment of its own to better annotate its role in the process and/or why it is a global singleton?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I rearranged the window.define function to the top so that it is closer to this variable.

// fired by this <script>, which will be handled by the loading script,
// which will invoke the callback with our module object.
let defined = false;
pendingDefine = (mod) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Talked about this offline, but I was concerned here that this was overwriting a global singleton and that doing so invites, potentially, a tricky-to-diagnose error condition if there is a possibility of two consecutive define() invocations before the triggering of the pendingDefine() inside script.onload.

@aomarks
Copy link
Member Author

aomarks commented Apr 28, 2018

I just pushed some WCT tests, and some bug fixes that they revealed. PTAL!

The major remaining piece here is error handling, e.g. it doesn't yet support invoking the error callback for dynamic require calls when a dependency cannot be resolved.

@@ -0,0 +1,2 @@
/node_modules/
/lib/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. Empty .npmignore will do

@@ -0,0 +1,237 @@
type modCallback = (...args: {}[]) => void;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

capitalize

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

return mod;
}

type normalizedUrl = string&{_normalized: never};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

capitalize and move to top of file

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

define: (deps: string[], factory: modCallback) => void;
}

(function() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you shouldn't need an IIFE in the source - either the module setting in tsconfig will create one, or just use a block, which should be compiled to an IIFE.

Copy link
Member Author

@aomarks aomarks Apr 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually as far as I can tell, TypeScript will not generate an IIFE even when the target is es5, so it will leak transpiled let and const variables up to function/global scope. (It does at least rename variables to prevent collisions within the compiled program.) There also seems to be no module setting that changes this behavior (anyway, this file is not a module).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default module setting is CommonJS, which might not be obvious in the output here because you have no imports or exports. "module": "none" and using a block might do it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, module: none and target:es5 with a block will just emit a block with vars:

tsconfig.json

{
  "compilerOptions": {
    "target": "es5",
    "module": "none"
  }
}

test.ts

{
  let x = 0;
}

test.js

{
  var x = 0;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, that just seems broken... unless the rationale is without a module setting they can't know that this file isn't the whole program?

Ok, then. Thanks for humoring me! 🤷‍♂️

* - http://example.com/foo/?qu/ery#fr/ag => http://example.com/foo/
*/
function getUrlBase(url: normalizedUrl): normalizedUrl {
url = url.split('?')[0] as normalizedUrl;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you use the anchor for this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.pathname could be useful, but then we need to reconstruct scheme, host, etc., so we think this is simpler for now.

@justinfagnani
Copy link
Contributor

Do you have an idea for a new name that better hints at the specific functionality?

@aomarks
Copy link
Member Author

aomarks commented Apr 29, 2018

Do you have an idea for a new name that better hints at the specific functionality?

Does seem like we need something, but nothing so far sounds great:

amd-path-loader
tiny-amd-loader
require-less
amd-loader-lite
es-module-amd-loader

Any other ideas?

@aomarks
Copy link
Member Author

aomarks commented Apr 29, 2018

Yep. Empty .npmignore will do

Done. Needed some more stuff in .npmignore anyway.

@justinfagnani
Copy link
Contributor

amd-path-loader isn't bad, IMO. js-module-loader?

@aomarks aomarks changed the title [WIP] New AMD-style module loader. New AMD-style module loader. May 1, 2018
@aomarks
Copy link
Member Author

aomarks commented May 1, 2018

amd-path-loader isn't bad, IMO. js-module-loader?

We decided to go with esm-amd-loader because the key distinguishing feature is that it replicates ES module behavior.

Disable AMD loader tests on Windows because WCT is broken.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants