diff --git a/.agignore b/.agignore new file mode 100644 index 0000000..849ddff --- /dev/null +++ b/.agignore @@ -0,0 +1 @@ +dist/ diff --git a/Gruntfile.js b/Gruntfile.js index 3259e20..df1d3bc 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -21,47 +21,30 @@ module.exports = function (grunt) { dist: { files: { 'dist/<%= pkg.name %>.src.js': [ - 'node_modules/when/es6-shim/Promise.js', - 'src/polyfill-wrapper-start.js', - 'dist/<%= pkg.name %>.js', - 'src/polyfill-wrapper-end.js' + 'src/wrapper-start.js', + 'src/loader.js', + 'src/dynamic-only.js', + 'src/url-polyfill.js', + 'src/system.js', + 'src/system-resolve.js', + 'src/system-fetch.js', + 'src/wrapper-end.js' ], - 'dist/<%= pkg.name %>-sans-promises.src.js': [ - 'src/polyfill-wrapper-start.js', - 'dist/<%= pkg.name %>.js', - 'src/polyfill-wrapper-end.js' + 'dist/<%= pkg.name %>-dev.src.js': [ + 'src/wrapper-start.js', + 'src/loader.js', + 'src/declarative.js', + 'src/transpiler.js', + 'src/url-polyfill.js', + 'src/system.js', + 'src/system-resolve.js', + 'src/system-fetch.js', + 'src/module-tag.js', + 'src/wrapper-end.js' ] } } }, - esnext: { - dist: { - src: [ - 'src/loader.js', - 'src/transpiler.js', - 'src/system.js' - ], - dest: 'dist/<%= pkg.name %>.js' - } - }, - 'string-replace': { - dist: { - files: { - 'dist/<%= pkg.name %>.js': 'dist/<%= pkg.name %>.js' - }, - options: { - replacements:[{ - pattern: 'var $__Object$getPrototypeOf = Object.getPrototypeOf;\n' + - 'var $__Object$defineProperty = Object.defineProperty;\n' + - 'var $__Object$create = Object.create;', - replacement: '' - }, { - pattern: '$__Object$getPrototypeOf(SystemLoader.prototype).constructor', - replacement: '$__super' - }] - } - } - }, uglify: { options: { banner: '<%= meta.banner %>\n', @@ -77,21 +60,21 @@ module.exports = function (grunt) { src: 'dist/<%= pkg.name %>.src.js', dest: 'dist/<%= pkg.name %>.js' }, - distSansPromises: { - src: 'dist/<%= pkg.name %>-sans-promises.src.js', - dest: 'dist/<%= pkg.name %>-sans-promises.js' + devDist: { + options: { + banner: '<%= meta.banner %>\n' + }, + src: 'dist/<%= pkg.name %>-dev.src.js', + dest: 'dist/<%= pkg.name %>-dev.js' } } }); grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-contrib-uglify'); - grunt.loadNpmTasks('grunt-esnext'); grunt.loadNpmTasks('grunt-contrib-concat'); - grunt.loadNpmTasks('grunt-string-replace'); grunt.registerTask('lint', ['jshint']); - grunt.registerTask('compile', ['esnext', 'string-replace', 'concat']); - grunt.registerTask('default', [/*'jshint', */'esnext', 'string-replace', - 'concat', 'uglify']); + grunt.registerTask('compile', ['concat']); + grunt.registerTask('default', [/*'jshint', */'concat', 'uglify']); }; diff --git a/README.md b/README.md index db9642b..81bf207 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ _For upgrading to ES6 Module Loader 0.16, [read the release notes here](https:// Dynamically loads ES6 modules in browsers and [NodeJS](#nodejs-use) with support for loading existing and custom module formats through loader hooks. -This project implements dynamic module loading through `System` exactly to the previous ES6-specified loader API at [2014-08-24 ES6 Specification Draft Rev 27, Section 15](http://wiki.ecmascript.org/doku.php?id=harmony:specification_drafts#august_24_2014_draft_rev_27) and is being converted to track the newly redrafted specification at https://github.com/whatwg/loader (work in progress at https://github.com/ModuleLoader/es6-module-loader/tree/1.0). +This project implements dynamic module loading through `System` exactly to the previous ES6-specified loader API at [2014-08-24 ES6 Specification Draft Rev 27, Section 15](http://wiki.ecmascript.org/doku.php?id=harmony:specification_drafts#august_24_2014_draft_rev_27). The new loader implementing the [WhatWG loader spec](https://github.com/whatwg/loader) is pending alpha release on the [1.0 branch](https://github.com/ModuleLoader/es6-module-loader/tree/1.0). * Provides an asynchronous loader (`System.import`) to [dynamically load ES6 modules](#getting-started). * Supports both [Traceur](https://github.com/google/traceur-compiler) and [Babel](http://babeljs.io/) for compiling ES6 modules and syntax into ES5 in the browser with source map support. diff --git a/index.js b/index.js index bd03825..694c26a 100644 --- a/index.js +++ b/index.js @@ -1,16 +1,20 @@ -var System = require('./dist/es6-module-loader.src'); +if (typeof Promise === 'undefined') + require('when/es6-shim/Promise'); + +var System = require('./dist/es6-module-loader-dev.src'); + +var filePrefix = 'file:' + (process.platform.match(/^win/) ? '/' : '') + '//'; -System.transpiler = 'traceur'; try { - System.paths.traceur = 'file:' + require.resolve('traceur/bin/traceur.js'); + System.paths.traceur = filePrefix + require.resolve('traceur/bin/traceur.js'); } catch(e) {} try { - System.paths.babel = 'file:' + require.resolve('babel-core/browser.js'); + System.paths.babel = filePrefix + require.resolve('babel-core/browser.js'); } catch(e) {} try { - System.paths.babel = System.paths.babel || 'file:' + require.resolve('babel/browser.js'); + System.paths.babel = System.paths.babel || filePrefix + require.resolve('babel/browser.js'); } catch(e) {} diff --git a/karma.conf.js b/karma.conf.js index 95d46ee..70182d1 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -39,7 +39,8 @@ module.exports = function(config) { [!options.ie8 ? (!options['babel'] ? 'node_modules/traceur/bin/traceur.js' : 'node_modules/babel-core/browser.js') : ''], - 'dist/es6-module-loader' + (options.polyfill ? '' : '-sans-promises') + '.src.js', + [options.polyfill ? 'node_modules/when/es6-shim/Promise' : ''], + 'dist/es6-module-loader-dev.src.js', 'test/_browser.js', 'test/browser-script-type-module.js', diff --git a/package.json b/package.json index 044e09c..5c047e2 100644 --- a/package.json +++ b/package.json @@ -29,8 +29,6 @@ "grunt-contrib-concat": "^0.5.0", "grunt-contrib-jshint": "~0.6.0", "grunt-contrib-uglify": "~0.6.0", - "grunt-esnext": "0.0.3", - "grunt-string-replace": "^0.2.7", "karma": "^0.12.28", "karma-benchmark": "^0.4.0", "karma-benchmark-reporter": "^0.1.1", diff --git a/src/declarative.js b/src/declarative.js new file mode 100644 index 0000000..ec60718 --- /dev/null +++ b/src/declarative.js @@ -0,0 +1,286 @@ +/* + * ES6 Module Declarative Linking Code - Dev Build Only + */ + + // 15.2.5.3 Module Linking Groups + + // 15.2.5.3.2 BuildLinkageGroups alternative implementation + // Adjustments (also see https://bugs.ecmascript.org/show_bug.cgi?id=2755) + // 1. groups is an already-interleaved array of group kinds + // 2. load.groupIndex is set when this function runs + // 3. load.groupIndex is the interleaved index ie 0 declarative, 1 dynamic, 2 declarative, ... (or starting with dynamic) + function buildLinkageGroups(load, loads, groups) { + groups[load.groupIndex] = groups[load.groupIndex] || []; + + // if the load already has a group index and its in its group, its already been done + // this logic naturally handles cycles + if (indexOf.call(groups[load.groupIndex], load) != -1) + return; + + // now add it to the group to indicate its been seen + groups[load.groupIndex].push(load); + + for (var i = 0, l = loads.length; i < l; i++) { + var loadDep = loads[i]; + + // dependencies not found are already linked + for (var j = 0; j < load.dependencies.length; j++) { + if (loadDep.name == load.dependencies[j].value) { + // by definition all loads in linkset are loaded, not linked + console.assert(loadDep.status == 'loaded', 'Load in linkSet not loaded!'); + + // if it is a group transition, the index of the dependency has gone up + // otherwise it is the same as the parent + var loadDepGroupIndex = load.groupIndex + (loadDep.isDeclarative != load.isDeclarative); + + // the group index of an entry is always the maximum + if (loadDep.groupIndex === undefined || loadDep.groupIndex < loadDepGroupIndex) { + + // if already in a group, remove from the old group + if (loadDep.groupIndex !== undefined) { + groups[loadDep.groupIndex].splice(indexOf.call(groups[loadDep.groupIndex], loadDep), 1); + + // if the old group is empty, then we have a mixed depndency cycle + if (groups[loadDep.groupIndex].length == 0) + throw new TypeError("Mixed dependency cycle detected"); + } + + loadDep.groupIndex = loadDepGroupIndex; + } + + buildLinkageGroups(loadDep, loads, groups); + } + } + } + } + + // 15.2.5.4 + // declarative linking implementation + function link(linkSet, linkError) { + + var loader = linkSet.loader; + + if (!linkSet.loads.length) + return; + + // console.log('linking {' + logloads(linkSet.loads) + '}'); + // snapshot(loader); + + // 15.2.5.3.1 LinkageGroups alternative implementation + + // build all the groups + // because the first load represents the top of the tree + // for a given linkset, we can work down from there + var groups = []; + var startingLoad = linkSet.loads[0]; + startingLoad.groupIndex = 0; + buildLinkageGroups(startingLoad, linkSet.loads, groups); + + // determine the kind of the bottom group + var curGroupDeclarative = startingLoad.isDeclarative == groups.length % 2; + + // run through the groups from bottom to top + for (var i = groups.length - 1; i >= 0; i--) { + var group = groups[i]; + for (var j = 0; j < group.length; j++) { + var load = group[j]; + + // 15.2.5.5 LinkDeclarativeModules adjusted + if (curGroupDeclarative) { + linkDeclarativeModule(load, linkSet.loads, loader); + } + // 15.2.5.6 LinkDynamicModules adjusted + else { + var module = doDynamicExecute(linkSet, load, linkError); + if (!module) + return; + load.module = { + name: load.name, + module: module + }; + load.status = 'linked'; + } + + finishLoad(loader, load); + } + + // alternative current kind for next loop + curGroupDeclarative = !curGroupDeclarative; + } + } + + + // custom module records for binding graph + // store linking module records in a separate table + function getOrCreateModuleRecord(name, loader) { + var moduleRecords = loader.moduleRecords; + return moduleRecords[name] || (moduleRecords[name] = { + name: name, + dependencies: [], + module: new Module(), // start from an empty module and extend + importers: [] + }); + } + + // custom declarative linking function + function linkDeclarativeModule(load, loads, loader) { + if (load.module) + return; + + var module = load.module = getOrCreateModuleRecord(load.name, loader); + var moduleObj = load.module.module; + + var registryEntry = load.declare.call(__global, function(name, value) { + // NB This should be an Object.defineProperty, but that is very slow. + // By disaling this module write-protection we gain performance. + // It could be useful to allow an option to enable or disable this. + module.locked = true; + moduleObj[name] = value; + + for (var i = 0, l = module.importers.length; i < l; i++) { + var importerModule = module.importers[i]; + if (!importerModule.locked) { + var importerIndex = indexOf.call(importerModule.dependencies, module); + importerModule.setters[importerIndex](moduleObj); + } + } + + module.locked = false; + return value; + }); + + // setup our setters and execution function + module.setters = registryEntry.setters; + module.execute = registryEntry.execute; + + // now link all the module dependencies + // amending the depMap as we go + for (var i = 0, l = load.dependencies.length; i < l; i++) { + var depName = load.dependencies[i].value; + var depModule = loader.modules[depName]; + + // if dependency not already in the module registry + // then try and link it now + if (!depModule) { + // get the dependency load record + for (var j = 0; j < loads.length; j++) { + if (loads[j].name != depName) + continue; + + // only link if already not already started linking (stops at circular / dynamic) + if (!loads[j].module) { + linkDeclarativeModule(loads[j], loads, loader); + depModule = loads[j].module; + } + // if circular, create the module record + else { + depModule = getOrCreateModuleRecord(depName, loader); + } + } + } + + // only declarative modules have dynamic bindings + if (depModule.importers) { + module.dependencies.push(depModule); + depModule.importers.push(module); + } + else { + // track dynamic records as null module records as already linked + module.dependencies.push(null); + } + + // run the setter for this dependency + if (module.setters[i]) + module.setters[i](depModule.module); + } + + load.status = 'linked'; + } + + /* + * Module Object non-exotic for ES5: + * + * module.module bound module object + * module.execute execution function for module + * module.dependencies list of module objects for dependencies + * See getOrCreateModuleRecord for all properties + * + */ + function doExecute(module) { + try { + module.execute.call(__global); + } + catch(e) { + return e; + } + } + + // 15.2.5.5.1 LinkImports not implemented + // 15.2.5.7 ResolveExportEntries not implemented + // 15.2.5.8 ResolveExports not implemented + // 15.2.5.9 ResolveExport not implemented + // 15.2.5.10 ResolveImportEntries not implemented + + // 15.2.6.1 + function evaluateLoadedModule(loader, load) { + console.assert(load.status == 'linked', 'is linked ' + load.name); + + doEnsureEvaluated(load.module, [], loader); + return load.module.module; + } + + // propogate execution errors + // see https://bugs.ecmascript.org/show_bug.cgi?id=2993 + function doEnsureEvaluated(module, seen, loader) { + var err = ensureEvaluated(module, seen, loader); + if (err) + throw err; + } + // 15.2.6.2 EnsureEvaluated adjusted + function ensureEvaluated(module, seen, loader) { + if (module.evaluated || !module.dependencies) + return; + + seen.push(module); + + var deps = module.dependencies; + var err; + + for (var i = 0, l = deps.length; i < l; i++) { + var dep = deps[i]; + // dynamic dependencies are empty in module.dependencies + // as they are already linked + if (!dep) + continue; + if (indexOf.call(seen, dep) == -1) { + err = ensureEvaluated(dep, seen, loader); + // stop on error, see https://bugs.ecmascript.org/show_bug.cgi?id=2996 + if (err) { + err = addToError(err, 'Error evaluating ' + dep.name); + return err; + } + } + } + + if (module.failed) + return new Error('Module failed execution.'); + + if (module.evaluated) + return; + + module.evaluated = true; + err = doExecute(module); + if (err) { + module.failed = true; + } + else if (Object.preventExtensions) { + // spec variation + // we don't create a new module here because it was created and ammended + // we just disable further extensions instead + Object.preventExtensions(module.module); + } + + module.execute = undefined; + return err; + } +})(); \ No newline at end of file diff --git a/src/dynamic-only.js b/src/dynamic-only.js new file mode 100644 index 0000000..53039a8 --- /dev/null +++ b/src/dynamic-only.js @@ -0,0 +1,35 @@ +/* + * ES6 Module Declarative Linking Code - Dev Build Only + */ + function link(linkSet, linkError) { + + var loader = linkSet.loader; + + if (!linkSet.loads.length) + return; + + var loads = linkSet.loads.concat([]); + + for (var i = 0; i < loads.length; i++) { + var load = loads[i]; + + var module = doDynamicExecute(linkSet, load, linkError); + if (!module) + return; + load.module = { + name: load.name, + module: module + }; + load.status = 'linked'; + + finishLoad(loader, load); + } + } + + function evaluateLoadedModule(loader, load) { + console.assert(load.status == 'linked', 'is linked ' + load.name); + return load.module.module; + } + + function doEnsureEvaluated() {} +})(); \ No newline at end of file diff --git a/src/loader.js b/src/loader.js index 11ee8dc..3600264 100644 --- a/src/loader.js +++ b/src/loader.js @@ -18,6 +18,31 @@ ********************************************************************************************* */ +function Module() {} +function Loader(options) { + this._loader = { + loaderObj: this, + loads: [], + modules: {}, + importPromises: {}, + moduleRecords: {} + }; + + // 26.3.3.6 + defineProperty(this, 'global', { + get: function() { + return __global; + } + }); + + // 26.3.3.13 realm not implemented + + if (this.transpiler) + setupTranspilers(this); +} + +(function() { + // Some Helpers // logs a linkset snapshot for debugging @@ -101,23 +126,6 @@ function logloads(loads) { } } */ - -(function() { - var Promise = __global.Promise || require('when/es6-shim/Promise'); - if (__global.console) - console.assert = console.assert || function() {}; - - // IE8 support - var indexOf = Array.prototype.indexOf || function(item) { - for (var i = 0, thisLen = this.length; i < thisLen; i++) { - if (this[i] === item) { - return i; - } - } - return -1; - }; - var defineProperty = $__Object$defineProperty; - // 15.2.3 - Runtime Semantics: Loader State // 15.2.3.11 @@ -246,7 +254,7 @@ function logloads(loads) { // instead of load.kind, use load.isDeclarative load.isDeclarative = true; - return loader.loaderObj.transpile(load) + return transpile.call(loader.loaderObj, load) .then(function(transpiled) { // Hijack System.register to set declare function var curSystem = __global.System; @@ -261,7 +269,8 @@ function logloads(loads) { load.declare = declare; load.depsList = deps; } - __eval(transpiled, __global, load); + // empty {} context is closest to undefined 'this' we can get + __eval(transpiled, load.address, {}); curSystem.register = curRegister; }); } @@ -512,11 +521,17 @@ function logloads(loads) { function linkSetFailed(linkSet, load, exc) { var loader = linkSet.loader; - if (load && linkSet.loads[0].name != load.name) - exc = addToError(exc, 'Error loading "' + load.name + '" from "' + linkSet.loads[0].name + '" at ' + (linkSet.loads[0].address || '') + '\n'); + if (load) { + if (load && linkSet.loads[0].name != load.name) + exc = addToError(exc, 'Error loading "' + load.name + '" from "' + linkSet.loads[0].name + '" at ' + (linkSet.loads[0].address || '')); + + if (load) + exc = addToError(exc, 'Error loading "' + load.name + '" at ' + (load.address || '')); + } + else { + exc = addToError(exc, 'Error linking "' + linkSet.loads[0].name + '" at ' + (linkSet.loads[0].address || '')); + } - if (load) - exc = addToError(exc, 'Error loading "' + load.name + '" at ' + (load.address || '') + '\n'); var loads = linkSet.loads.concat([]); for (var i = 0, l = loads.length; i < l; i++) { @@ -575,58 +590,6 @@ function logloads(loads) { load.linkSets.splice(0, load.linkSets.length); } - // 15.2.5.3 Module Linking Groups - - // 15.2.5.3.2 BuildLinkageGroups alternative implementation - // Adjustments (also see https://bugs.ecmascript.org/show_bug.cgi?id=2755) - // 1. groups is an already-interleaved array of group kinds - // 2. load.groupIndex is set when this function runs - // 3. load.groupIndex is the interleaved index ie 0 declarative, 1 dynamic, 2 declarative, ... (or starting with dynamic) - function buildLinkageGroups(load, loads, groups) { - groups[load.groupIndex] = groups[load.groupIndex] || []; - - // if the load already has a group index and its in its group, its already been done - // this logic naturally handles cycles - if (indexOf.call(groups[load.groupIndex], load) != -1) - return; - - // now add it to the group to indicate its been seen - groups[load.groupIndex].push(load); - - for (var i = 0, l = loads.length; i < l; i++) { - var loadDep = loads[i]; - - // dependencies not found are already linked - for (var j = 0; j < load.dependencies.length; j++) { - if (loadDep.name == load.dependencies[j].value) { - // by definition all loads in linkset are loaded, not linked - console.assert(loadDep.status == 'loaded', 'Load in linkSet not loaded!'); - - // if it is a group transition, the index of the dependency has gone up - // otherwise it is the same as the parent - var loadDepGroupIndex = load.groupIndex + (loadDep.isDeclarative != load.isDeclarative); - - // the group index of an entry is always the maximum - if (loadDep.groupIndex === undefined || loadDep.groupIndex < loadDepGroupIndex) { - - // if already in a group, remove from the old group - if (loadDep.groupIndex !== undefined) { - groups[loadDep.groupIndex].splice(indexOf.call(groups[loadDep.groupIndex], loadDep), 1); - - // if the old group is empty, then we have a mixed depndency cycle - if (groups[loadDep.groupIndex].length == 0) - throw new TypeError("Mixed dependency cycle detected"); - } - - loadDep.groupIndex = loadDepGroupIndex; - } - - buildLinkageGroups(loadDep, loads, groups); - } - } - } - } - function doDynamicExecute(linkSet, load, linkError) { try { var module = load.execute(); @@ -641,281 +604,10 @@ function logloads(loads) { return module; } - // 15.2.5.4 - function link(linkSet, linkError) { - - var loader = linkSet.loader; - - if (!linkSet.loads.length) - return; - - // console.log('linking {' + logloads(linkSet.loads) + '}'); - // snapshot(loader); - - // 15.2.5.3.1 LinkageGroups alternative implementation - - // build all the groups - // because the first load represents the top of the tree - // for a given linkset, we can work down from there - var groups = []; - var startingLoad = linkSet.loads[0]; - startingLoad.groupIndex = 0; - buildLinkageGroups(startingLoad, linkSet.loads, groups); - - // determine the kind of the bottom group - var curGroupDeclarative = startingLoad.isDeclarative == groups.length % 2; - - // run through the groups from bottom to top - for (var i = groups.length - 1; i >= 0; i--) { - var group = groups[i]; - for (var j = 0; j < group.length; j++) { - var load = group[j]; - - // 15.2.5.5 LinkDeclarativeModules adjusted - if (curGroupDeclarative) { - linkDeclarativeModule(load, linkSet.loads, loader); - } - // 15.2.5.6 LinkDynamicModules adjusted - else { - var module = doDynamicExecute(linkSet, load, linkError); - if (!module) - return; - load.module = { - name: load.name, - module: module - }; - load.status = 'linked'; - } - finishLoad(loader, load); - } - - // alternative current kind for next loop - curGroupDeclarative = !curGroupDeclarative; - } - } - - - // custom module records for binding graph - // store linking module records in a separate table - function getOrCreateModuleRecord(name, loader) { - var moduleRecords = loader.moduleRecords; - return moduleRecords[name] || (moduleRecords[name] = { - name: name, - dependencies: [], - module: new Module(), // start from an empty module and extend - importers: [] - }); - } - - // custom declarative linking function - function linkDeclarativeModule(load, loads, loader) { - if (load.module) - return; - - var module = load.module = getOrCreateModuleRecord(load.name, loader); - var moduleObj = load.module.module; - - var registryEntry = load.declare.call(__global, function(name, value) { - // NB This should be an Object.defineProperty, but that is very slow. - // By disaling this module write-protection we gain performance. - // It could be useful to allow an option to enable or disable this. - module.locked = true; - moduleObj[name] = value; - - for (var i = 0, l = module.importers.length; i < l; i++) { - var importerModule = module.importers[i]; - if (!importerModule.locked) { - var importerIndex = indexOf.call(importerModule.dependencies, module); - importerModule.setters[importerIndex](moduleObj); - } - } - - module.locked = false; - return value; - }); - - // setup our setters and execution function - module.setters = registryEntry.setters; - module.execute = registryEntry.execute; - - // now link all the module dependencies - // amending the depMap as we go - for (var i = 0, l = load.dependencies.length; i < l; i++) { - var depName = load.dependencies[i].value; - var depModule = loader.modules[depName]; - - // if dependency not already in the module registry - // then try and link it now - if (!depModule) { - // get the dependency load record - for (var j = 0; j < loads.length; j++) { - if (loads[j].name != depName) - continue; - - // only link if already not already started linking (stops at circular / dynamic) - if (!loads[j].module) { - linkDeclarativeModule(loads[j], loads, loader); - depModule = loads[j].module; - } - // if circular, create the module record - else { - depModule = getOrCreateModuleRecord(depName, loader); - } - } - } - - // only declarative modules have dynamic bindings - if (depModule.importers) { - module.dependencies.push(depModule); - depModule.importers.push(module); - } - else { - // track dynamic records as null module records as already linked - module.dependencies.push(null); - } - - // run the setter for this dependency - if (module.setters[i]) - module.setters[i](depModule.module); - } - - load.status = 'linked'; - } - - - - // 15.2.5.5.1 LinkImports not implemented - // 15.2.5.7 ResolveExportEntries not implemented - // 15.2.5.8 ResolveExports not implemented - // 15.2.5.9 ResolveExport not implemented - // 15.2.5.10 ResolveImportEntries not implemented - - // 15.2.6.1 - function evaluateLoadedModule(loader, load) { - console.assert(load.status == 'linked', 'is linked ' + load.name); - - doEnsureEvaluated(load.module, [], loader); - return load.module.module; - } - - /* - * Module Object non-exotic for ES5: - * - * module.module bound module object - * module.execute execution function for module - * module.dependencies list of module objects for dependencies - * See getOrCreateModuleRecord for all properties - * - */ - function doExecute(module) { - try { - module.execute.call(__global); - } - catch(e) { - return e; - } - } - - // propogate execution errors - // see https://bugs.ecmascript.org/show_bug.cgi?id=2993 - function doEnsureEvaluated(module, seen, loader) { - var err = ensureEvaluated(module, seen, loader); - if (err) - throw err; - } - // 15.2.6.2 EnsureEvaluated adjusted - function ensureEvaluated(module, seen, loader) { - if (module.evaluated || !module.dependencies) - return; - - seen.push(module); - - var deps = module.dependencies; - var err; - - for (var i = 0, l = deps.length; i < l; i++) { - var dep = deps[i]; - // dynamic dependencies are empty in module.dependencies - // as they are already linked - if (!dep) - continue; - if (indexOf.call(seen, dep) == -1) { - err = ensureEvaluated(dep, seen, loader); - // stop on error, see https://bugs.ecmascript.org/show_bug.cgi?id=2996 - if (err) { - err = addToError(err, 'Error evaluating ' + dep.name + '\n'); - return err; - } - } - } - - if (module.failed) - return new Error('Module failed execution.'); - - if (module.evaluated) - return; - - module.evaluated = true; - err = doExecute(module); - if (err) { - module.failed = true; - } - else if (Object.preventExtensions) { - // spec variation - // we don't create a new module here because it was created and ammended - // we just disable further extensions instead - Object.preventExtensions(module.module); - } - - module.execute = undefined; - return err; - } - - function addToError(err, msg) { - if (err instanceof Error) - err.message = msg + err.message; - else - err = msg + err; - return err; - } - // 26.3 Loader // 26.3.1.1 - function Loader(options) { - if (typeof options != 'object') - throw new TypeError('Options must be an object'); - - if (options.normalize) - this.normalize = options.normalize; - if (options.locate) - this.locate = options.locate; - if (options.fetch) - this.fetch = options.fetch; - if (options.translate) - this.translate = options.translate; - if (options.instantiate) - this.instantiate = options.instantiate; - - this._loader = { - loaderObj: this, - loads: [], - modules: {}, - importPromises: {}, - moduleRecords: {} - }; - - // 26.3.3.6 - defineProperty(this, 'global', { - get: function() { - return __global; - } - }); - - // 26.3.3.13 realm not implemented - } - - function Module() {} + // defined at top // importPromises adds ability to import a module twice without error - https://bugs.ecmascript.org/show_bug.cgi?id=2601 function createImportPromise(loader, name, promise) { @@ -966,12 +658,12 @@ function logloads(loads) { return !!this._loader.modules[name]; }, // 26.3.3.8 - 'import': function(name, options) { + 'import': function(name, parentName, parentAddress) { // run normalize first var loaderObj = this; // added, see https://bugs.ecmascript.org/show_bug.cgi?id=2659 - return Promise.resolve(loaderObj.normalize(name, options && options.name, options && options.address)) + return Promise.resolve(loaderObj.normalize(name, parentName)) .then(function(name) { var loader = loaderObj._loader; @@ -981,7 +673,7 @@ function logloads(loads) { } return loader.importPromises[name] || createImportPromise(loaderObj, name, - loadModule(loader, name, options || {}) + loadModule(loader, name, {}) .then(function(load) { delete loader.importPromises[name]; return evaluateLoadedModule(loader, load); @@ -1066,7 +758,6 @@ function logloads(loads) { }, // 26.3.3.18.3 fetch: function(load) { - throw new TypeError('Fetch not implemented'); }, // 26.3.3.18.4 translate: function(load) { @@ -1078,14 +769,3 @@ function logloads(loads) { }; var _newModule = Loader.prototype.newModule; - - if (typeof exports === 'object') - module.exports = Loader; - - __global.Reflect = __global.Reflect || {}; - __global.Reflect.Loader = __global.Reflect.Loader || Loader; - __global.Reflect.global = __global.Reflect.global || __global; - __global.LoaderPolyfill = Loader; - -})(); - diff --git a/src/module-tag.js b/src/module-tag.js new file mode 100644 index 0000000..ff79fc0 --- /dev/null +++ b/src/module-tag.js @@ -0,0 +1,37 @@ +(function() { + // - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/test/test-traceur.html b/test/test-traceur.html deleted file mode 100644 index ba51137..0000000 --- a/test/test-traceur.html +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/test/test.js b/test/test.js deleted file mode 100644 index 0618206..0000000 --- a/test/test.js +++ /dev/null @@ -1,677 +0,0 @@ - -var System, Loader, Module, tests, test; - -var testCnt = 0, passed = 0, failed = 0; -var test = function(name, initialize) { - if (typeof initialize != 'function') { - var val = initialize; - var exp = arguments[2]; - initialize = function(assert) { - assert(val, exp); - } - } - var testId = testCnt++; - tests.addTest(testId, name); - function assert(value, expected) { - if (value != expected) - return 'Got "' + value + '" instead of "' + expected + '"'; - } - initialize(function(value, expected) { - var failure; - if (value instanceof Array) { - for (var i = 0; i < arguments.length; i++) - failure = failure || assert(arguments[i][0], arguments[i][1]); - } - else - failure = assert(value, expected); - if (failure) - failed++; - else - passed++; - tests.completeTest(testId, name, failure, { passed: passed, failed: failed, total: testCnt }); - }, function(err) { - setTimeout(function() { - throw err; - }); - }); -} - -if (typeof window != 'undefined') { - // browser - document.body.innerHTML = "
Summary
"; - tests = { - addTest: function(id, name) { - var p = document.createElement('tr'); - var td = document.createElement('td'); - td.innerHTML = name; - p.appendChild(td); - td = document.createElement('td'); - td.className = 'result-' + id; - p.appendChild(td); - document.querySelector('.test tbody').appendChild(p); - }, - completeTest: function(id, name, failure, summary) { - document.querySelector('.test .result-' + id).innerHTML = !failure ? 'Passed' : 'Failed: ' + failure; - document.querySelector('.summary').innerHTML = summary.passed + '/' + summary.total + ' tests passed'; - } - } - window.test = test; - window.runTests = runTests; -} -else { - // nodejs - var ml = require('../lib/index-' + process.env.es6compiler); - - if (process.env.es6compiler == '6to5') - require('regenerator/runtime'); - - process.on('uncaughtException', function(err) { - console.log('Caught: ' + err); - }); - - System = ml.System; - Loader = ml.Loader; - Module = ml.Module; - - tests = { - addTest: function(id, name) {}, - completeTest: function(id, name, failure, summary) { - console.log(name + ': ' + (!failure ? 'Passed' : 'Failed: ' + failure)); - console.log(summary.passed + '/' + summary.total + ' passed. '); - if (failure) - process.exit(1); - }, - }; - - runTests(); -} - -function runTests() { - - // Normalize tests - identical to https://github.com/google/traceur-compiler/blob/master/test/unit/runtime/System.js - - var oldBaseURL = System.baseURL; - System.baseURL = 'http://example.org/a/b.html'; - - test('Normalize - No Referer', System.normalize('d/e/f'), 'd/e/f'); - // test('Normalize - Below baseURL', System.normalize('../e/f'), '../e/f'); - - var refererName = 'dir/file'; - test('Normalize - Relative paths', System.normalize('./d/e/f', refererName), 'dir/d/e/f'); - test('Normalize - Relative paths', System.normalize('../e/f', refererName), 'e/f'); - - test('Normalize - name undefined', function(assert) { - try { - System.normalize(undefined, refererName); - } - catch(e) { - assert(e.message, 'Module name must be a string'); - } - }); - - test('Normalize - embedded ..', function(assert) { - try { - System.normalize('a/b/../c'); - } - catch(e) { - assert(e.message, 'Illegal module name "a/b/../c"'); - } - }); - test('Normalize - embedded ..', function(assert) { - try { - System.normalize('a/../b', refererName); - } - catch(e) { - assert(e.message, 'Illegal module name "a/../b"'); - } - }); - test('Normalize - embedded ..', function(assert) { - try { - System.normalize('a/b/../c', refererName); - } - catch(e) { - assert(e.message, 'Illegal module name "a/b/../c"'); - } - }); - - // test('Normalize - below referer', System.normalize('../../e/f', refererName), '../e/f'); - - test('Normalize - backwards compat', System.normalize('./a.js'), 'a.js'); - - test('Normalize - URL', function(assert) { - try { - System.normalize('http://example.org/a/b.html'); - } - catch(e) { - assert(); - } - }); - - System.baseURL = 'http://example.org/a/'; - - test('Locate', System.locate({ name: '@abc/def' }), 'http://example.org/a/@abc/def.js'); - test('Locate', System.locate({ name: 'abc/def' }), 'http://example.org/a/abc/def.js'); - - // paths - System.paths['path/*'] = '/test/*.js'; - test('Locate paths', System.locate({ name: 'path/test' }), 'http://example.org/test/test.js'); - - - System.baseURL = oldBaseURL; - - - - // More Normalize tests - - test('Normalize test 1', function(assert) { - assert(System.normalize('./a/b', 'c'), 'a/b'); - }); - test('Normalize test 2', function(assert) { - assert(System.normalize('./a/b', 'c/d'), 'c/a/b'); - }); - test('Normalize test 3', function(assert) { - assert(System.normalize('./a/b', '../c/d'), '../c/a/b'); - }); - test('Normalize test 4', function(assert) { - assert(System.normalize('./a/b', '../c/d'), '../c/a/b'); - }); - test('Normalize test 5', function(assert) { - assert(System.normalize('../a/b', '../../c/d'), '../../a/b'); - }); - - test('Setting & deleting modules', function(assert, err) { - System['import']('loader/module').then(function(m1) { - System['delete']('loader/module'); - System['import']('loader/module').then(function(m2) { - System['delete']('loader/module'); - System.set('loader/module', System.newModule({custom: 'module'})); - System['import']('loader/module').then(function(m3) { - assert( - [m1.run, 'first'], - [m2.run, 'second'], - [m3.custom, 'module'] - ); - }, err); - }, err); - }, err); - }); - - test('Import a script', function(assert, err) { - System['import']('syntax/script').then(function(m) { - assert(!!m, true); - }, err); - }); - - test('Import a script once loaded', function(assert, err) { - System['import']('syntax/script').then(function(m) { - System['import']('syntax/script').then(function(m) { - assert(!!m, true); - }, err); - }); - }); - - test('Import ES6', function(assert, err) { - System['import']('syntax/es6').then(function(m) { - assert(m.p, 'p'); - }, err); - }); - - test('Import ES6 with dep', function(assert, err) { - System['import']('syntax/es6-withdep').then(function(m) { - assert(m.p, 'p'); - }, err); - }); - - test('Import ES6 Generator', function(assert, err) { - System['import']('syntax/es6-generator').then(function(m) { - assert(!!m.generator, true); - }, err); - }); - - test('Direct import without bindings', function(assert, err) { - System['import']('syntax/direct').then(function(m) { - assert(!!m, true); - }, err); - }); - - test('Circular Dependencies', function(assert, err) { - System['import']('syntax/circular1').then(function(m1) { - System['import']('syntax/circular2').then(function(m2) { - assert( - [m2.output, 'test circular 1'], - [m1.output, 'test circular 2'], - [m2.output1, 'test circular 2'], - [m1.output2, 'test circular 1'] - ); - }, err); - }, err); - }); - - test('Circular Test', function(assert, err) { - System['import']('syntax/even').then(function(m) { - assert( - [m.even(10), true], - [m.counter, 7], - [m.even(15), false], - [m.counter, 15] - ); - }, err); - }); - - test('Load order test: A', function(assert, err) { - System['import']('loads/a').then(function(m) { - assert( - [m.a, 'a'], - [m.b, 'b'] - ); - }, err); - }); - - test('Load order test: C', function(assert, err) { - System['import']('loads/c').then(function(m) { - assert( - [m.c, 'c'], - [m.a, 'a'], - [m.b, 'b'] - ); - }, err); - }); - - test('Load order test: S', function(assert, err) { - System['import']('loads/s').then(function(m) { - assert( - [m.s, 's'], - [m.c, 'c'], - [m.a, 'a'], - [m.b, 'b'] - ); - }, err); - }); - - test('Load order test: _a', function(assert) { - System['import']('loads/_a').then(function(m) { - assert( - [m.b, 'b'], - [m.d, 'd'], - [m.g, 'g'], - [m.a, 'a'] - ); - }) - }); - - test('Load order test: _e', function(assert) { - System['import']('loads/_e').then(function(m) { - assert( - [m.c, 'c'], - [m.e, 'e'] - ); - }) - }); - - test('Load order test: _f', function(assert) { - System['import']('loads/_f').then(function(m) { - assert( - [m.g, 'g'], - [m.f, 'f'] - ); - }) - }); - test('Load order test: _h', function(assert) { - System['import']('loads/_h').then(function(m) { - assert( - [m.i, 'i'], - [m.a, 'a'], - [m.h, 'h'] - ); - }) - }); - - test('Error check 1', function(assert) { - System['import']('loads/main').then(function(m) { - assert(false, true); - }, function(e) { - assert(e, 'Error evaluating loads/deperror\ndep error'); - }); - // System['import']('loads/deperror'); - }); - - test('Unhandled rejection test', function(assert) { - System['import']('loads/load-non-existent') - assert(); - }); - - - test('Export Syntax', function(assert) { - System['import']('syntax/export').then(function(m) { - assert( - [m.p, 5], - [typeof m.foo, 'function'], - [typeof m.q, 'object'], - [typeof m['default'], 'function'], - [m.s, 4], - [m.t, 4], - [typeof m.m, 'object'] - ); - }); - }); - - // test not enabled for Babel - if (System.transpiler != 'babel') - test('Export Star 2', function(assert) { - System['import']('syntax/export-star2').then(function(m) { - assert( - [typeof m.foo, 'function'], - [m.bar, 'bar'] - ); - }); - }); - - test('Export Star', function(assert) { - System['import']('syntax/export-star').then(function(m) { - assert( - [m.foo, 'foo'], - [m.bar, 'bar'] - ); - }); - }); - - test('Export default 1', function(assert, err) { - System['import']('syntax/export-default').then(function(m) { - assert(m['default'](), 'test'); - }, err); - }); - - test('Re-export', function(assert, err) { - System['import']('syntax/reexport1').then(function(m) { - assert(m.p, 5); - }, err); - }); - - test('Re-export with new name', function(assert, err) { - System['import']('syntax/reexport2').then(function(m) { - assert( - [m.q, 4], - [m.z, 5] - ); - }, err); - }); - - test('Re-export binding', function(assert, err) { - System['import']('syntax/reexport-binding').then(function(m) { - System['import']('syntax/rebinding').then(function(m) { - assert(m.p, 4); - }); - }, err); - }); - - test('Import Syntax', function(assert, err) { - System['import']('syntax/import').then(function(m) { - assert( - [typeof m.a, 'function'], - [m.b, 4], - [m.c, 5], - [m.d, 4], - [typeof m.q.foo, 'function'] - ); - }, err); - }); - - test('ES6 Syntax', function(assert, err) { - System['import']('syntax/es6-file').then(function(m) { - setTimeout(function() { - (new m.q()).foo(); - }); - assert( - [typeof m.q, 'function'] - ); - }, err); - }); - - test('Module Name meta', function(assert) { - System['import']('loader/moduleName').then(function(m) { - assert( - [m.name, 'loader/moduleName'], - [m.address, System.baseURL + 'loader/moduleName.js'] - ); - }); - }); - - test('Custom path', function(assert) { - System.paths['bar'] = 'loader/custom-path.js'; - System['import']('bar').then(function(m) { - assert(m.bar, 'bar'); - }) - }); - - test('Custom path wildcard', function(assert) { - System.paths['bar/*'] = 'loader/custom-folder/*.js'; - System['import']('bar/path').then(function(m) { - assert(m.bar, 'baa'); - }); - }); - - test('Custom path most specific', function(assert) { - delete System.paths['bar/*']; - System.paths['bar/bar'] = 'loader/specific-path.js'; - System.paths['bar/*'] = 'loader/custom-folder/*.js'; - System['import']('bar/bar').then(function(m) { - assert(m.path, true); - }); - }); - - test('should load System.define', function(assert) { - var oldLocate = System.locate; - var slaveLocatePromise = new Promise(function(resolve, reject) { - - System.locate = function(load) { - if(load.name === 'slave') { - setTimeout(function() { - System.define('slave', 'var double = [1,2,3].map(i => i * 2);'); - resolve('slave.js'); - }, 1); - return slaveLocatePromise; - } - return oldLocate.apply(this, arguments); - }; - - }); - - System.import('loader/master').then(function() { - assert(true, true, 'Able to load'); - }, function(err) { - assert('Did not resolve'); - }).then(reset, reset); - - function reset() { - System.locate = oldLocate; - } - }); - - var customModules = {}; - var customFactories = {}; - - var executeModule = function(name) { - if (!customFactories[name]) - return; - var module = customFactories[name].apply(null, []); - customModules[name] = module; - return module; - } - - var customLoader = new Reflect.Loader({ - normalize: function(name, parentName, parentAddress) { - return new Promise(function(resolve, reject) { - if (name == 'asdfasdf') { - return setTimeout(function() { - resolve('loader/async-norm'); - }, 500); - } - - if (name == 'error1') - return setTimeout(function(){ reject('error1'); }, 100); - - var normalized = System.normalize(name, parentName, parentAddress); - resolve(normalized); - }); - }, - locate: function(load) { - if (load.name == 'error2') - return new Promise(function(resolve, reject) { - setTimeout(function(){ reject('error2'); }, 100); - }); - - if (load.name.substr(0, 5) == 'path/') - load.name = 'loader/' + load.name.substr(5); - return System.locate(load); - }, - fetch: function(load) { - if (load.name == 'error3') - throw 'error3'; - if (load.name == 'error4' || load.name == 'error5') - return 'asdf'; - return System.fetch.apply(this, arguments); - }, - translate: function(load) { - if (load.name == 'error4') - return new Promise(function(resolve, reject) { - setTimeout(function(){ reject('error4'); }, 100); - }); - return System.translate.apply(this, arguments); - }, - instantiate: function(load) { - if (load.name == this.transpiler) { - var transpiler = this.transpiler; - return System.import(transpiler).then(function() { - return { - deps: [], - execute: function() { - return System.get(transpiler); - } - }; - }); - } - - if (load.name == 'error5') - return new Promise(function(resolve, reject) { - setTimeout(function(){ reject('error5'); }, 100); - }); - // very bad AMD support - if (load.source.indexOf('define') == -1) - return System.instantiate(load); - - var factory, deps; - var define = function(_deps, _factory) { - deps = _deps; - factory = _factory; - } - //console.log(load.source); - eval(load.source); - - customFactories[load.name] = factory; - - // normalize all dependencies now - var normalizePromises = []; - for (var i = 0; i < deps.length; i++) - normalizePromises.push(Promise.resolve(System.normalize(deps[i], load.name))); - - return Promise.all(normalizePromises).then(function(resolvedDeps) { - - return { - deps: deps, - execute: function() { - if (customModules[load.name]) - return System.newModule(customModules[load.name]); - - // first ensure all dependencies have been executed - for (var i = 0; i < resolvedDeps.length; i++) - resolvedDeps[i] = executeModule(resolvedDeps[i]); - - var module = factory.apply(null, resolvedDeps); - - customModules[load.name] = module; - return System.newModule(module); - } - }; - }); - } - }); - customLoader.transpiler = System.transpiler; - - test('Custom loader standard load', function(assert) { - var p = customLoader['import']('loader/test').then(function(m) { - assert(m.loader, 'custom'); - }); - if (p['catch']) - p['catch'](function(e) { - assert(!e, 'standard load failed: ' + e); - }); - }); - - test('Custom loader special rules', function(assert) { - var p = customLoader['import']('path/custom').then(function(m) { - assert(m.path, true); - }); - if (p['catch']) - p['catch'](function(e) { - assert(!e, 'special rules failed: ' + e); - }); - }); - - test('Custom loader AMD support', function(assert) { - customLoader['import']('loader/amd').then(function(m) { - assert(m.format, 'amd'); - })['catch'](function(e) { - setTimeout(function() { - throw e; - }, 1); - }); - }); - - test('Custom loader hook - normalize error', function(assert) { - customLoader['import']('loader/error1-parent').then(function(m) { - })['catch'](function(e) { - assert(e.toString(), 'Error loading "loader/error1-parent" at ' + System.baseURL + 'loader/error1-parent.js\nerror1'); - }); - }); - test('Custom loader hook - locate error', function(assert) { - customLoader['import']('error2').then(function(m) {}, function(e) { - assert(e.toString(), 'Error loading "error2" at \nerror2'); - }); - }); - test('Custom loader hook - fetch error', function(assert) { - customLoader['import']('error3').then(function(m) {}, function(e) { - assert(e.toString(), 'Error loading "error3" at ' + System.baseURL + 'error3.js\nerror3'); - }); - }); - test('Custom loader hook - translate error', function(assert) { - customLoader['import']('error4').then(function(m) {}, function(e) { - assert(e.toString(), 'Error loading "error4" at ' + System.baseURL + 'error4.js\nerror4'); - }); - }); - test('Custom loader hook - instantiate error', function(assert) { - customLoader['import']('error5').then(function(m) {}, function(e) { - assert(e.toString(), 'Error loading "error5" at ' + System.baseURL + 'error5.js\nerror5'); - }); - }); - - test('Async Normalize', function(assert) { - customLoader.normalize('asdfasdf').then(function(normalized) { - return customLoader['import'](normalized); - }).then(function(m) { - assert(m.n, 'n'); - }); - }); - - test('System instanceof Loader', function(assert) { - assert(System instanceof Reflect.Loader, true); - }); - - if (typeof Worker != 'undefined') - test('Loading inside of a Web Worker', function(assert) { - var worker = new Worker('worker/worker-' + System.transpiler + '.js'); - - worker.onmessage = function(e) { - assert(e.data, 'p'); - }; - }); -} diff --git a/test/worker/worker-babel.js b/test/worker/worker-babel.js index 9a7fbea..ae6c930 100644 --- a/test/worker/worker-babel.js +++ b/test/worker/worker-babel.js @@ -1,11 +1,11 @@ importScripts("../../node_modules/when/es6-shim/Promise.js", - "../../dist/es6-module-loader.src.js" + "../../dist/es6-module-loader-dev.src.js" ); System.transpiler = 'babel'; System.paths['babel'] = '../../node_modules/babel-core/browser.js'; -System['import']('es6').then(function(m) { +System['import']('es6.js').then(function(m) { postMessage(m.p); }, function(err) { console.error(err, err.stack); diff --git a/test/worker/worker-traceur.js b/test/worker/worker-traceur.js index c275a77..d8ff14b 100644 --- a/test/worker/worker-traceur.js +++ b/test/worker/worker-traceur.js @@ -1,7 +1,7 @@ importScripts("../../node_modules/when/es6-shim/Promise.js", - "../../dist/es6-module-loader.src.js"); + "../../dist/es6-module-loader-dev.src.js"); System.paths['traceur'] = '../../node_modules/traceur/bin/traceur.js'; -System['import']('es6').then(function(m) { +System['import']('es6.js').then(function(m) { postMessage(m.p); }, function(err) { console.error(err, err.stack);