Skip to content
This repository has been archived by the owner on Jul 30, 2018. It is now read-only.

Load implementation #39

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
60 changes: 60 additions & 0 deletions src/load.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import Promise from './Promise';

declare var define: {
(...args: any[]): any;
amd: any;
};

export interface AMDRequire {
(moduleIds: string[], callback: (...modules:any[]) => void): void;
}
export interface NodeRequire {
(moduleId: string): any;
}
export type Require = AMDRequire | NodeRequire;

export interface Load {
(require: Require, ...moduleIds: string[]): Promise<any[]>;
(...moduleIds: string[]): Promise<any[]>;
}

const load: Load = (function (): Load {
if (typeof module === 'object' && typeof module.exports === 'object') {
return function (contextualRequire: any, ...moduleIds: string[]): Promise<any[]> {
if (typeof contextualRequire === 'string') {
moduleIds.unshift(contextualRequire);
contextualRequire = require;
}
return new Promise(function (resolve, reject) {
try {
resolve(moduleIds.map(function (moduleId): any {
return contextualRequire(moduleId);
}));
}
catch (error) {
reject(error);
}
});
};
}
else if (typeof define === 'function' && define.amd) {
return function (contextualRequire: any, ...moduleIds: string[]): Promise<any[]> {
if (typeof contextualRequire === 'string') {
moduleIds.unshift(contextualRequire);
contextualRequire = require;
}
return new Promise(function (resolve) {
// TODO: Error path once https://github.com/dojo/loader/issues/14 is figured out
contextualRequire(moduleIds, function (...modules: any[]) {
resolve(modules);
});
});
};
}
else {
return function () {
return Promise.reject(new Error('Unknown loader'));
};
}
})();
Copy link
Member

Choose a reason for hiding this comment

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

I am missing the "value" of the dual wrap of the hostLoad and the load in separate closures. I mean the code is right, but it seems to add another layer that appears to be somewhat unnecessary, especially because hostLoad doesn't get exported.

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 didn't want to repeat the code for calculating the absolute module IDs, but it's not necessary if require is passed instead of module. Ignore this.

export default load;
22 changes: 5 additions & 17 deletions src/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import has from './has';
import { Handle } from './interfaces';
import Promise from './Promise';
import Registry, { Test } from './Registry';
import load from './load';

declare var require: Function;
declare var define: { amd: any };
declare var require: any;

export class FilterRegistry extends Registry<RequestFilter> {
register(test: string | RegExp | RequestFilterTest, value: RequestFilter, first?: boolean): Handle {
Expand Down Expand Up @@ -65,21 +65,9 @@ export class ProviderRegistry extends Registry<RequestProvider> {
// provider. While that import is in-flight, subsequent requests will queue up while
// waiting for the provider to be fulfilled.
this._defaultValue = (url: string, options?: RequestOptions): ResponsePromise<any> => {
this._providerPromise = new Promise((resolve, reject) => {
if (typeof define === 'function' && define.amd) {
require([ defaultProvider ], (provider: { default: RequestProvider; }) => {
this._defaultValue = provider.default;
resolve(provider.default);
});
}
else if (has('host-node')) {
const provider: { default: RequestProvider; } = require(defaultProvider);
this._defaultValue = provider.default;
resolve(provider.default);
}
else {
reject(new Error('Unknown environment or loader'));
}
this._providerPromise = load(require, defaultProvider).then(([ providerModule ]: [ { default: RequestProvider } ]): RequestProvider => {
this._defaultValue = providerModule.default;
return providerModule.default;
});
this._defaultValue = deferRequest;
return deferRequest(url, options);
Expand Down
1 change: 1 addition & 0 deletions tests/unit/all.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import './encoding';
import './global';
import './has';
import './lang';
import './load';
import './math';
import './Map';
import './number';
Expand Down
86 changes: 86 additions & 0 deletions tests/unit/load.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import assert = require('intern/chai!assert');
Copy link
Member

Choose a reason for hiding this comment

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

import * as assert from 'intern/chai!assert';

Copy link
Member Author

Choose a reason for hiding this comment

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

The assert module is not an ES6 module, so it (more than likely) shouldn't be imported with ES6 semantics. This may have been fixed in later builds of TS (I've seen instances where import * as ... isn't callable), but it's still confusing since in ES6, assert would be a non-callable object.

Copy link
Member

Choose a reason for hiding this comment

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

Ok.

import registerSuite = require('intern!object');
import has from 'src/has';
import load from 'src/load';
import Promise from 'src/Promise';

const suite: any = {
name: 'load',

'global load'() {
var def = this.async(5000);

load('src/has', 'src/Promise').then(def.callback(function ([ hasModule, promiseModule ]: [ any, any ]) {
assert.strictEqual(hasModule.default, has);
assert.strictEqual(promiseModule.default, Promise);
}));
},

'contextual load'() {
var def = this.async(5000);

load(require, './load/a', './load/b').then(def.callback(function ([ a, b ]: [ any, any ]) {
assert.deepEqual(a, { one: 1, two: 2 });
assert.deepEqual(b, { three: 3, four: 4 });
}));
}

// TODO: once AMD error handling is figured out, add tests for the failure case
};

if (has('host-node')) {
const nodeRequire: any = global.require.nodeRequire;
const path: any = nodeRequire('path');
const buildDir: string = path.join(process.cwd(), '_build');

suite.node = {
'different than AMD load'() {
const nodeLoad: typeof load = nodeRequire(path.join(buildDir, 'src', 'load')).default;
assert.notStrictEqual(nodeLoad, load);
},

'global load succeeds'() {
var def = this.async(5000);

var result: Promise<any[]> = nodeRequire(path.join(buildDir, 'tests', 'unit', 'load', 'node')).globalSucceed;
result.then(def.callback(function ([ fs, path ]: [ any, any ]) {
assert.strictEqual(fs, nodeRequire('fs'));
assert.strictEqual(path, nodeRequire('path'));
}));
},

'global load with relative path fails'() {
var def = this.async(5000);

var result: Promise<any[]> = nodeRequire(path.join(buildDir, 'tests', 'unit', 'load', 'node')).globalFail;
result.then(function () {
def.reject(new Error('load should not have succeeded'));
}, def.callback(function (error: Error) {
assert.instanceOf(error, Error);
}));
},

'contextual load succeeds'() {
var def = this.async(5000);

var result: Promise<any[]> = nodeRequire(path.join(buildDir, 'tests', 'unit', 'load', 'node')).succeed;
result.then(def.callback(function ([ a, b ]: [ any, any ]) {
assert.deepEqual(a, { one: 1, two: 2 });
assert.deepEqual(b, { three: 3, four: 4 });
}));
},

'contextual load with non-existent module fails'() {
var def = this.async(5000);

var result: Promise<any[]> = nodeRequire(path.join(buildDir, 'tests', 'unit', 'load', 'node')).fail;
result.then(function () {
def.reject(new Error('load should not have succeeded'));
}, def.callback(function (error: Error) {
assert.instanceOf(error, Error);
}));
}
};
}

registerSuite(suite);
2 changes: 2 additions & 0 deletions tests/unit/load/a.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const one = 1;
export const two = 2;
2 changes: 2 additions & 0 deletions tests/unit/load/b.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const three = 3;
export const four = 4;
7 changes: 7 additions & 0 deletions tests/unit/load/node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import load from '../../../src/load';

export const succeed = load(require, './a', './b');
export const fail = load(require, './a', './c');

export const globalSucceed = load('fs', 'path');
export const globalFail = load('fs', './a');
1 change: 0 additions & 1 deletion tests/unit/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,6 @@ if (has('host-node')) {
nodeRequest = request;

response.writeHead(200, {
'Content-Length': body.length,
'Content-Type': 'application/json'
});
response.write(body);
Expand Down
5 changes: 5 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"./src/has.ts",
"./src/interfaces.d.ts",
"./src/lang.ts",
"./src/load.ts",
"./src/math.ts",
"./src/number.ts",
"./src/object.ts",
Expand Down Expand Up @@ -101,6 +102,10 @@
"./tests/unit/global.ts",
"./tests/unit/has.ts",
"./tests/unit/lang.ts",
"./tests/unit/load.ts",
"./tests/unit/load/a.ts",
"./tests/unit/load/b.ts",
"./tests/unit/load/node.ts",
"./tests/unit/math.ts",
"./tests/unit/module.d.ts",
"./tests/unit/number.ts",
Expand Down