Skip to content

Commit

Permalink
Merge pull request #59 from microsoft/ben/support-js-ending
Browse files Browse the repository at this point in the history
Allow relative module identifiers that end with `.js`
  • Loading branch information
alexdima authored Aug 21, 2024
2 parents 634f761 + a4ce7fc commit bcebd7f
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 17 deletions.
21 changes: 16 additions & 5 deletions src/core/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ namespace AMDLoader {
};

export interface IConfigurationOptions {
/**
* Allow module ids to end with .js
*/
allowJsExtension?: boolean;
/**
* The prefix that will be aplied to all modules when they are resolved to a location
*/
Expand Down Expand Up @@ -193,6 +197,7 @@ namespace AMDLoader {
}

export interface IValidatedConfigurationOptions extends IConfigurationOptions {
allowJsExtension: boolean;
baseUrl: string;
paths: { [path: string]: any; };
config: { [moduleId: string]: IModuleConfiguration };
Expand Down Expand Up @@ -232,6 +237,9 @@ namespace AMDLoader {
}

options = options || {};
if (typeof options.allowJsExtension !== 'boolean') {
options.allowJsExtension = false;
}
if (typeof options.baseUrl !== 'string') {
options.baseUrl = '';
}
Expand Down Expand Up @@ -428,12 +436,12 @@ namespace AMDLoader {
/**
* Transform a module id to a location. Appends .js to module ids
*/
public moduleIdToPaths(moduleId: string): string[] {
public moduleIdToPaths(moduleIdOrPath: string): string[] {

if (this._env.isNode) {
const isNodeModule = (
this.options.amdModulesPattern instanceof RegExp
&& !this.options.amdModulesPattern.test(moduleId)
&& !this.options.amdModulesPattern.test(moduleIdOrPath)
);

if (isNodeModule) {
Expand All @@ -443,15 +451,18 @@ namespace AMDLoader {
return ['empty:'];
} else {
// ...and at runtime we create a `shortcut`-path
return ['node|' + moduleId];
return ['node|' + moduleIdOrPath];
}
}
}

let result = moduleId;
const isAbsolutePath = Utilities.isAbsolutePath(moduleIdOrPath);
const isJSFilePath = (this.options.allowJsExtension ? false : Utilities.endsWith(moduleIdOrPath, '.js'));
const isModuleId = !(isAbsolutePath || isJSFilePath);

let result = moduleIdOrPath;
let results: string[];
if (!Utilities.endsWith(result, '.js') && !Utilities.isAbsolutePath(result)) {
if (isModuleId) {
results = this._applyPaths(result);

for (let i = 0, len = results.length; i < len; i++) {
Expand Down
10 changes: 6 additions & 4 deletions src/core/moduleManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ namespace AMDLoader {
private readonly _scriptLoader: IScriptLoader;
private readonly _loaderAvailableTimestamp: number;
private readonly _defineFunc: IDefineFunc;
private readonly _requireFunc: IRequireFunc;
private readonly _requireFunc: IRequireFunc | null;

private _moduleIdProvider: ModuleIdProvider;
private _config: Configuration;
Expand Down Expand Up @@ -367,7 +367,7 @@ namespace AMDLoader {
private _buildInfoDefineStack: (string | null)[];
private _buildInfoDependencies: string[][];

constructor(env: Environment, scriptLoader: IScriptLoader, defineFunc: IDefineFunc, requireFunc: IRequireFunc, loaderAvailableTimestamp: number = 0) {
constructor(env: Environment, scriptLoader: IScriptLoader, defineFunc: IDefineFunc, requireFunc: IRequireFunc | null, loaderAvailableTimestamp: number = 0) {
this._env = env;
this._scriptLoader = scriptLoader;
this._loaderAvailableTimestamp = loaderAvailableTimestamp;
Expand All @@ -387,7 +387,9 @@ namespace AMDLoader {
this._buildInfoDefineStack = [];
this._buildInfoDependencies = [];

this._requireFunc.moduleManager = this;
if (this._requireFunc) {
this._requireFunc.moduleManager = this;
}
}

public reset(): ModuleManager {
Expand All @@ -399,7 +401,7 @@ namespace AMDLoader {
}

public getGlobalAMDRequireFunc(): IRequireFunc {
return this._requireFunc;
return this._requireFunc!;
}

private static _findRelevantLocationInStack(needle: string, stack: string): IPosition {
Expand Down
9 changes: 7 additions & 2 deletions src/loader.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,10 @@ declare namespace AMDLoader {
writeDelay?: number;
}
interface IConfigurationOptions {
/**
* Allow module ids to end with .js
*/
allowJsExtension?: boolean;
/**
* The prefix that will be aplied to all modules when they are resolved to a location
*/
Expand Down Expand Up @@ -267,6 +271,7 @@ declare namespace AMDLoader {
nodeCachedData?: INodeCachedDataConfiguration;
}
interface IValidatedConfigurationOptions extends IConfigurationOptions {
allowJsExtension: boolean;
baseUrl: string;
paths: {
[path: string]: any;
Expand Down Expand Up @@ -321,7 +326,7 @@ declare namespace AMDLoader {
/**
* Transform a module id to a location. Appends .js to module ids
*/
moduleIdToPaths(moduleId: string): string[];
moduleIdToPaths(moduleIdOrPath: string): string[];
/**
* Transform a module id or url to a location.
*/
Expand Down Expand Up @@ -508,7 +513,7 @@ declare namespace AMDLoader {
private _buildInfoPath;
private _buildInfoDefineStack;
private _buildInfoDependencies;
constructor(env: Environment, scriptLoader: IScriptLoader, defineFunc: IDefineFunc, requireFunc: IRequireFunc, loaderAvailableTimestamp?: number);
constructor(env: Environment, scriptLoader: IScriptLoader, defineFunc: IDefineFunc, requireFunc: IRequireFunc | null, loaderAvailableTimestamp?: number);
reset(): ModuleManager;
getGlobalAMDDefineFunc(): IDefineFunc;
getGlobalAMDRequireFunc(): IRequireFunc;
Expand Down
20 changes: 14 additions & 6 deletions src/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,9 @@ var AMDLoader;
}
}
options = options || {};
if (typeof options.allowJsExtension !== 'boolean') {
options.allowJsExtension = false;
}
if (typeof options.baseUrl !== 'string') {
options.baseUrl = '';
}
Expand Down Expand Up @@ -425,10 +428,10 @@ var AMDLoader;
/**
* Transform a module id to a location. Appends .js to module ids
*/
moduleIdToPaths(moduleId) {
moduleIdToPaths(moduleIdOrPath) {
if (this._env.isNode) {
const isNodeModule = (this.options.amdModulesPattern instanceof RegExp
&& !this.options.amdModulesPattern.test(moduleId));
&& !this.options.amdModulesPattern.test(moduleIdOrPath));
if (isNodeModule) {
// This is a node module...
if (this.isBuild()) {
Expand All @@ -437,13 +440,16 @@ var AMDLoader;
}
else {
// ...and at runtime we create a `shortcut`-path
return ['node|' + moduleId];
return ['node|' + moduleIdOrPath];
}
}
}
let result = moduleId;
const isAbsolutePath = AMDLoader.Utilities.isAbsolutePath(moduleIdOrPath);
const isJSFilePath = (this.options.allowJsExtension ? false : AMDLoader.Utilities.endsWith(moduleIdOrPath, '.js'));
const isModuleId = !(isAbsolutePath || isJSFilePath);
let result = moduleIdOrPath;
let results;
if (!AMDLoader.Utilities.endsWith(result, '.js') && !AMDLoader.Utilities.isAbsolutePath(result)) {
if (isModuleId) {
results = this._applyPaths(result);
for (let i = 0, len = results.length; i < len; i++) {
if (this.isBuild() && results[i] === 'empty:') {
Expand Down Expand Up @@ -1248,7 +1254,9 @@ var AMDLoader;
this._buildInfoPath = [];
this._buildInfoDefineStack = [];
this._buildInfoDependencies = [];
this._requireFunc.moduleManager = this;
if (this._requireFunc) {
this._requireFunc.moduleManager = this;
}
}
reset() {
return new ModuleManager(this._env, this._scriptLoader, this._defineFunc, this._requireFunc, this._loaderAvailableTimestamp);
Expand Down
63 changes: 63 additions & 0 deletions tests/loader.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ function assertConfigurationIs(actual, expected) {
QUnit.test('Default configuration', () => {
var result = loader.ConfigurationOptionsUtil.mergeConfigurationOptions();
assertConfigurationIs(result, {
allowJsExtension: false,
baseUrl: '',
catchError: false,
ignoreDuplicateModules: [],
Expand All @@ -31,6 +32,7 @@ QUnit.test('Default configuration', () => {
});
function createSimpleKnownConfigurationOptions() {
return loader.ConfigurationOptionsUtil.mergeConfigurationOptions({
allowJsExtension: false,
baseUrl: 'myBaseUrl',
catchError: true,
ignoreDuplicateModules: ['a'],
Expand All @@ -46,6 +48,7 @@ function createSimpleKnownConfigurationOptions() {
QUnit.test('Simple known configuration options', () => {
var result = createSimpleKnownConfigurationOptions();
assertConfigurationIs(result, {
allowJsExtension: false,
baseUrl: 'myBaseUrl/',
catchError: true,
ignoreDuplicateModules: ['a'],
Expand All @@ -64,6 +67,7 @@ QUnit.test('Overwriting known configuration options', () => {
baseUrl: ''
}, createSimpleKnownConfigurationOptions());
assertConfigurationIs(result, {
allowJsExtension: false,
baseUrl: '',
catchError: true,
ignoreDuplicateModules: ['a'],
Expand All @@ -80,6 +84,7 @@ QUnit.test('Overwriting known configuration options', () => {
baseUrl: '/'
}, createSimpleKnownConfigurationOptions());
assertConfigurationIs(result, {
allowJsExtension: false,
baseUrl: '/',
catchError: true,
ignoreDuplicateModules: ['a'],
Expand All @@ -96,6 +101,7 @@ QUnit.test('Overwriting known configuration options', () => {
catchError: false
}, createSimpleKnownConfigurationOptions());
assertConfigurationIs(result, {
allowJsExtension: false,
baseUrl: 'myBaseUrl/',
catchError: false,
ignoreDuplicateModules: ['a'],
Expand All @@ -112,6 +118,7 @@ QUnit.test('Overwriting known configuration options', () => {
ignoreDuplicateModules: ['b']
}, createSimpleKnownConfigurationOptions());
assertConfigurationIs(result, {
allowJsExtension: false,
baseUrl: 'myBaseUrl/',
catchError: true,
ignoreDuplicateModules: ['a', 'b'],
Expand All @@ -128,6 +135,7 @@ QUnit.test('Overwriting known configuration options', () => {
paths: { 'a': 'c' }
}, createSimpleKnownConfigurationOptions());
assertConfigurationIs(result, {
allowJsExtension: false,
baseUrl: 'myBaseUrl/',
catchError: true,
ignoreDuplicateModules: ['a'],
Expand All @@ -144,6 +152,7 @@ QUnit.test('Overwriting known configuration options', () => {
config: { 'e': {} }
}, createSimpleKnownConfigurationOptions());
assertConfigurationIs(result, {
allowJsExtension: false,
baseUrl: 'myBaseUrl/',
catchError: true,
ignoreDuplicateModules: ['a'],
Expand All @@ -160,6 +169,7 @@ QUnit.test('Overwriting known configuration options', () => {
config: { 'd': { 'a': 'a' } }
}, createSimpleKnownConfigurationOptions());
assertConfigurationIs(result, {
allowJsExtension: false,
baseUrl: 'myBaseUrl/',
catchError: true,
ignoreDuplicateModules: ['a'],
Expand All @@ -175,6 +185,7 @@ QUnit.test('Overwriting known configuration options', () => {
QUnit.test('Overwriting unknown configuration options', () => {
var result = loader.ConfigurationOptionsUtil.mergeConfigurationOptions();
assertConfigurationIs(result, {
allowJsExtension: false,
baseUrl: '',
catchError: false,
ignoreDuplicateModules: [],
Expand All @@ -191,6 +202,7 @@ QUnit.test('Overwriting unknown configuration options', () => {
unknownKey1: 'value1'
}, result);
assertConfigurationIs(result, {
allowJsExtension: false,
baseUrl: '',
catchError: false,
ignoreDuplicateModules: [],
Expand All @@ -208,6 +220,7 @@ QUnit.test('Overwriting unknown configuration options', () => {
unknownKey2: 'value2'
}, result);
assertConfigurationIs(result, {
allowJsExtension: false,
baseUrl: '',
catchError: false,
ignoreDuplicateModules: [],
Expand All @@ -226,6 +239,7 @@ QUnit.test('Overwriting unknown configuration options', () => {
unknownKey2: 'new-value2'
}, result);
assertConfigurationIs(result, {
allowJsExtension: false,
baseUrl: '',
catchError: false,
ignoreDuplicateModules: [],
Expand Down Expand Up @@ -277,6 +291,55 @@ QUnit.test('moduleIdToPath', () => {
QUnit.equal(config.moduleIdToPaths('https://a/b/c/d'), 'https://a/b/c/d.js?suffix');
QUnit.equal(config.moduleIdToPaths('https://a'), 'https://a.js?suffix');
});
QUnit.test('moduleIdToPath with allowJsExtension', () => {
var config = new loader.Configuration(new loader.Environment(), {
allowJsExtension: true,
baseUrl: 'prefix',
urlArgs: 'suffix',
paths: {
'a': 'newa',
'knockout': 'http://ajax.aspnetcdn.com/ajax/knockout/knockout-2.2.1.js',
'knockout.js': 'http://ajax.aspnetcdn.com/ajax/knockout/knockout-2.2.1.js',
'editor': '/src/editor'
}
});
// baseUrl is applied
QUnit.equal(config.moduleIdToPaths('b/c/d'), 'prefix/b/c/d.js?suffix');
QUnit.equal(config.moduleIdToPaths('b/c/d.js'), 'prefix/b/c/d.js?suffix');
// paths rules are applied
QUnit.equal(config.moduleIdToPaths('a'), 'prefix/newa.js?suffix');
QUnit.equal(config.moduleIdToPaths('a.js'), 'prefix/newa.js?suffix');
QUnit.equal(config.moduleIdToPaths('a/b/c/d'), 'prefix/newa/b/c/d.js?suffix');
QUnit.equal(config.moduleIdToPaths('a/b/c/d.js'), 'prefix/newa/b/c/d.js?suffix');
// paths rules check if value is an absolute path
QUnit.equal(config.moduleIdToPaths('knockout'), 'http://ajax.aspnetcdn.com/ajax/knockout/knockout-2.2.1.js?suffix');
QUnit.equal(config.moduleIdToPaths('knockout.js'), 'http://ajax.aspnetcdn.com/ajax/knockout/knockout-2.2.1.js?suffix');
// modules redirected to / still get .js appended
QUnit.equal(config.moduleIdToPaths('editor/x'), '/src/editor/x.js?suffix');
QUnit.equal(config.moduleIdToPaths('editor/x.js'), '/src/editor/x.js?suffix');
// modules starting with / skip baseUrl + paths rules
QUnit.equal(config.moduleIdToPaths('/b/c/d'), '/b/c/d.js?suffix');
QUnit.equal(config.moduleIdToPaths('/b/c/d.js'), '/b/c/d.js?suffix');
QUnit.equal(config.moduleIdToPaths('/a/b/c/d'), '/a/b/c/d.js?suffix');
QUnit.equal(config.moduleIdToPaths('/a/b/c/d.js'), '/a/b/c/d.js?suffix');
QUnit.equal(config.moduleIdToPaths('/a'), '/a.js?suffix');
QUnit.equal(config.moduleIdToPaths('/a.js'), '/a.js?suffix');
// modules starting with http:// or https:// skip baseUrl + paths rules
QUnit.equal(config.moduleIdToPaths('file:///c:/a/b/c'), 'file:///c:/a/b/c.js?suffix');
QUnit.equal(config.moduleIdToPaths('file:///c:/a/b/c.js'), 'file:///c:/a/b/c.js?suffix');
QUnit.equal(config.moduleIdToPaths('http://b/c/d'), 'http://b/c/d.js?suffix');
QUnit.equal(config.moduleIdToPaths('http://b/c/d.js'), 'http://b/c/d.js?suffix');
QUnit.equal(config.moduleIdToPaths('http://a/b/c/d'), 'http://a/b/c/d.js?suffix');
QUnit.equal(config.moduleIdToPaths('http://a/b/c/d.js'), 'http://a/b/c/d.js?suffix');
QUnit.equal(config.moduleIdToPaths('http://a'), 'http://a.js?suffix');
QUnit.equal(config.moduleIdToPaths('http://a.js'), 'http://a.js?suffix');
QUnit.equal(config.moduleIdToPaths('https://b/c/d'), 'https://b/c/d.js?suffix');
QUnit.equal(config.moduleIdToPaths('https://b/c/d.js'), 'https://b/c/d.js?suffix');
QUnit.equal(config.moduleIdToPaths('https://a/b/c/d'), 'https://a/b/c/d.js?suffix');
QUnit.equal(config.moduleIdToPaths('https://a/b/c/d.js'), 'https://a/b/c/d.js?suffix');
QUnit.equal(config.moduleIdToPaths('https://a'), 'https://a.js?suffix');
QUnit.equal(config.moduleIdToPaths('https://a.js'), 'https://a.js?suffix');
});
QUnit.test('requireToUrl', () => {
var config = new loader.Configuration(new loader.Environment(), {
baseUrl: 'prefix',
Expand Down
Loading

0 comments on commit bcebd7f

Please sign in to comment.