From b5782653c0dc4dfba765853abc4b8d562b93fcbc Mon Sep 17 00:00:00 2001 From: Maciek Stasieluk Date: Tue, 14 Jul 2015 23:07:28 +0200 Subject: [PATCH] version 0.3.0 --- .npm/package/.gitignore | 1 - .npm/package/README | 7 -- .npm/package/npm-shrinkwrap.json | 7 -- CHANGELOG.md | 12 +- README.md | 7 +- build-plugin.js | 3 + package.js | 14 +-- require-polyfill.js | 1 + system-config.js | 89 +++++++++++++ vendor/system-polyfills.js | 130 +++++++++---------- vendor/system.js | 208 +++++++++++++++++++++---------- vendor/xhr2.js | 1 - 12 files changed, 316 insertions(+), 164 deletions(-) delete mode 100644 .npm/package/.gitignore delete mode 100644 .npm/package/README delete mode 100644 .npm/package/npm-shrinkwrap.json create mode 100644 require-polyfill.js create mode 100644 system-config.js delete mode 100644 vendor/xhr2.js diff --git a/.npm/package/.gitignore b/.npm/package/.gitignore deleted file mode 100644 index 3c3629e..0000000 --- a/.npm/package/.gitignore +++ /dev/null @@ -1 +0,0 @@ -node_modules diff --git a/.npm/package/README b/.npm/package/README deleted file mode 100644 index 3d49255..0000000 --- a/.npm/package/README +++ /dev/null @@ -1,7 +0,0 @@ -This directory and the files immediately inside it are automatically generated -when you change this package's NPM dependencies. Commit the files in this -directory (npm-shrinkwrap.json, .gitignore, and this README) to source control -so that others run the same versions of sub-dependencies. - -You should NOT check in the node_modules directory that Meteor automatically -creates; if you are using git, the .gitignore file tells git to ignore it. diff --git a/.npm/package/npm-shrinkwrap.json b/.npm/package/npm-shrinkwrap.json deleted file mode 100644 index 8ab9901..0000000 --- a/.npm/package/npm-shrinkwrap.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "dependencies": { - "xhr2": { - "version": "0.1.2" - } - } -} diff --git a/CHANGELOG.md b/CHANGELOG.md index 7906c77..1c77f2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,17 @@ -## 0.2 +## 0.3 -### 0.2.1 +- Update SystemJS to 0.18.4 +- Remove dependency on xhr2 +- Use `meteor://` protocol on modules +- Alternative import name `foomodule.import` to allow TypeScript support +- Improve error reporting + +## 0.2.1 - Update babel-compiler to 5.6.15 -### 0.2.0 +## 0.2.0 - Switch to MDG Babel compiler package diff --git a/README.md b/README.md index 706bbca..45a2e0b 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ All `*.import.js` files **have full ES6 support** provided by Meteor's Babel.js API is compatible with new ES6 modules spec. Under the hood [Babel.js](https://babeljs.io) and [SystemJS](https://github.com/systemjs/systemjs) take care of everything, so you can use modules today! -This package adds SystemJS to your project. +*This package adds SystemJS to your project.* ## Benefits of this approach @@ -48,7 +48,7 @@ If you want to see it in action, see our todo example app: You can also check out great `meteor-react-example` app by [optilude](https://github.com/optilude). -- Source code: https://github.com/optilude/meteor-react-example/tree/modules +- Source code: https://github.com/optilude/meteor-react-example ### Basic usage @@ -126,12 +126,11 @@ You can set alternative name for a module, below is an example from `universe:re ## Troubleshooting -- `Uncaught SyntaxError: Unexpected token <` or `Potentially unhandled rejection [2] Uncaught SyntaxError: Unexpected token <` +#### `[Universe Modules]: Module XXX does not exist!` You misspelled import name/path. SystemJS tries to download this file from remote location and fails. Check if all files are at their location and import paths are OK. -You'll find misspelled code in the error stack trace. diff --git a/build-plugin.js b/build-plugin.js index c102ed8..edb4339 100644 --- a/build-plugin.js +++ b/build-plugin.js @@ -10,6 +10,9 @@ var handler = function (compileStep) { moduleId = compileStep.packageName + '/' + moduleId; } + // add meteor:// protocol to unify behavior between client/server/packages + moduleId = 'meteor://' + moduleId; + var extraWhitelist = ['es6.modules']; if(path[1] === 'jsx'){ extraWhitelist.push('react'); diff --git a/package.js b/package.js index b261f40..a0f227f 100644 --- a/package.js +++ b/package.js @@ -1,6 +1,6 @@ Package.describe({ name: 'universe:modules', - version: '0.2.1', + version: '0.3.0', summary: 'Use ES6 / ES2015 modules in Meteor with SystemJS!', git: 'https://github.com/vazco/universe-modules', documentation: 'README.md' @@ -12,20 +12,16 @@ Package.registerBuildPlugin({ sources: ['build-plugin.js'] }); -Npm.depends({ - 'xhr2': '0.1.2' -}); - Package.onUse(function (api) { - // We need XMLHttpRequest on the server side for SystemJS remote fetching (or SystemJS won't run) - // This may change in near future if we find better way to run SystemJS - api.addFiles('vendor/xhr2.js', 'server'); + // we need this for System.js to run on the server side without core changes + api.addFiles('require-polyfill.js', 'server'); // Load SystemJS api.addFiles([ 'vendor/system-polyfills.js', - 'vendor/system.js' // There is a core change in this file! Meteor uses `Npm.require` instead of `require` + 'vendor/system.js', + 'system-config.js' ]); }); diff --git a/require-polyfill.js b/require-polyfill.js new file mode 100644 index 0000000..e6960ed --- /dev/null +++ b/require-polyfill.js @@ -0,0 +1 @@ +require = Npm.require; \ No newline at end of file diff --git a/system-config.js b/system-config.js new file mode 100644 index 0000000..8c432f2 --- /dev/null +++ b/system-config.js @@ -0,0 +1,89 @@ +// backup original SystemJS methods +var _System = { + normalize: System.normalize, + locate: System.locate, + fetch: System.fetch, + translate: System.translate, + instantiate: System.instantiate +}; + +System.baseURL = 'meteor://'; +System.config({ + meta: { + 'meteor://*': { + format: 'register' + } + } +}); + +/* + * name: the unnormalized module name + * parentName: the canonical module name for the requesting module + * parentAddress: the address of the requesting module + */ +System.normalize = function (name, parentName, parentAddress) { + // add meteor prefix + if (name[0] !== '.' && name[0] !== '/' && name.indexOf('://') === -1) { + name = 'meteor://' + name; + } + + // allow foomodule.import syntax in import name + if (name.slice(-7) === '.import') { + name = name.slice(0, -7); + } + + // load original normalize + return _System.normalize.call(this, name, parentName, parentAddress); +}; + +/* + * load.name the canonical module name + * load.metadata a metadata object that can be used to store + * derived metadata for reference in other hooks + */ +//System.locate = function (load) { +// return _System.locate.call(this, load); +//}; + +/* + * load.name: the canonical module name + * load.address: the URL returned from locate + * load.metadata: the same metadata object by reference, which + * can be modified + */ +System.fetch = function (load) { + var promise = _System.fetch.call(this, load); + + if (!promise) { + // not really a promise + return promise; + } + + if(load.name.slice(0, 9) !== 'meteor://'){ + // not our protocol, ignore + return promise; + } + + // show our warning + return promise.catch(function (err) { + console.warn('[Universe Modules]: Module ' + load.name.slice(9) + ' does not exist! You will probably see other errors in the console because of that.'); + }); +}; + +/* + * load.name + * load.address + * load.metadata + * load.source: the fetched source + */ +//System.translate = function (load) { +// return _System.translate.call(this, load); +//}; + +/* + * load identical to previous hooks, but load.source + * is now the translated source + */ +//System.instantiate = function (load) { +// return _System.instantiate.call(this, load); +//}; \ No newline at end of file diff --git a/vendor/system-polyfills.js b/vendor/system-polyfills.js index d59efb7..1a4c128 100644 --- a/vendor/system-polyfills.js +++ b/vendor/system-polyfills.js @@ -2,73 +2,75 @@ * SystemJS Polyfills for URL and Promise providing IE8+ Support */ // from https://gist.github.com/Yaffle/1088850 -function URLPolyfill(url, baseURL) { - if (typeof url != 'string') - throw new TypeError('URL must be a string'); - var m = String(url).replace(/^\s+|\s+$/g, "").match(/^([^:\/?#]+:)?(?:\/\/(?:([^:@\/?#]*)(?::([^:@\/?#]*))?@)?(([^:\/?#]*)(?::(\d*))?))?([^?#]*)(\?[^#]*)?(#[\s\S]*)?/); - if (!m) { - throw new RangeError(); - } - var protocol = m[1] || ""; - var username = m[2] || ""; - var password = m[3] || ""; - var host = m[4] || ""; - var hostname = m[5] || ""; - var port = m[6] || ""; - var pathname = m[7] || ""; - var search = m[8] || ""; - var hash = m[9] || ""; - if (baseURL !== undefined) { - var base = baseURL instanceof URLPolyfill ? baseURL : new URLPolyfill(baseURL); - var flag = protocol === "" && host === "" && username === ""; - if (flag && pathname === "" && search === "") { - search = base.search; - } - if (flag && pathname.charAt(0) !== "/") { - pathname = (pathname !== "" ? (((base.host !== "" || base.username !== "") && base.pathname === "" ? "/" : "") + base.pathname.slice(0, base.pathname.lastIndexOf("/") + 1) + pathname) : base.pathname); +(function(global) { + function URLPolyfill(url, baseURL) { + if (typeof url != 'string') + throw new TypeError('URL must be a string'); + var m = String(url).replace(/^\s+|\s+$/g, "").match(/^([^:\/?#]+:)?(?:\/\/(?:([^:@\/?#]*)(?::([^:@\/?#]*))?@)?(([^:\/?#]*)(?::(\d*))?))?([^?#]*)(\?[^#]*)?(#[\s\S]*)?/); + if (!m) { + throw new RangeError(); } - // dot segments removal - var output = []; - pathname.replace(/^(\.\.?(\/|$))+/, "") - .replace(/\/(\.(\/|$))+/g, "/") - .replace(/\/\.\.$/, "/../") - .replace(/\/?[^\/]*/g, function (p) { - if (p === "/..") { - output.pop(); - } else { - output.push(p); - } - }); - pathname = output.join("").replace(/^\//, pathname.charAt(0) === "/" ? "/" : ""); - if (flag) { - port = base.port; - hostname = base.hostname; - host = base.host; - password = base.password; - username = base.username; - } - if (protocol === "") { - protocol = base.protocol; + var protocol = m[1] || ""; + var username = m[2] || ""; + var password = m[3] || ""; + var host = m[4] || ""; + var hostname = m[5] || ""; + var port = m[6] || ""; + var pathname = m[7] || ""; + var search = m[8] || ""; + var hash = m[9] || ""; + if (baseURL !== undefined) { + var base = baseURL instanceof URLPolyfill ? baseURL : new URLPolyfill(baseURL); + var flag = protocol === "" && host === "" && username === ""; + if (flag && pathname === "" && search === "") { + search = base.search; + } + if (flag && pathname.charAt(0) !== "/") { + pathname = (pathname !== "" ? (((base.host !== "" || base.username !== "") && base.pathname === "" ? "/" : "") + base.pathname.slice(0, base.pathname.lastIndexOf("/") + 1) + pathname) : base.pathname); + } + // dot segments removal + var output = []; + pathname.replace(/^(\.\.?(\/|$))+/, "") + .replace(/\/(\.(\/|$))+/g, "/") + .replace(/\/\.\.$/, "/../") + .replace(/\/?[^\/]*/g, function (p) { + if (p === "/..") { + output.pop(); + } else { + output.push(p); + } + }); + pathname = output.join("").replace(/^\//, pathname.charAt(0) === "/" ? "/" : ""); + if (flag) { + port = base.port; + hostname = base.hostname; + host = base.host; + password = base.password; + username = base.username; + } + if (protocol === "") { + protocol = base.protocol; + } } - } - // convert windows file URLs to use / - if (protocol == 'file:') - pathname = pathname.replace(/\\/g, '/'); - - this.origin = protocol + (protocol !== "" || host !== "" ? "//" : "") + host; - this.href = protocol + (protocol !== "" || host !== "" ? "//" : "") + (username !== "" ? username + (password !== "" ? ":" + password : "") + "@" : "") + host + pathname + search + hash; - this.protocol = protocol; - this.username = username; - this.password = password; - this.host = host; - this.hostname = hostname; - this.port = port; - this.pathname = pathname; - this.search = search; - this.hash = hash; -} -(typeof self != 'undefined' ? self : global).URLPolyfill = URLPolyfill;!function(e){"object"==typeof exports?module.exports=e():"function"==typeof define&&define.amd?define(e):"undefined"!=typeof window?window.Promise=e():"undefined"!=typeof global?global.Promise=e():"undefined"!=typeof self&&(self.Promise=e())}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o lib/test.js * './test.js': './lib/test.js', - * } - * } - * } + * }, + * env: { + * 'browser': { + * main: 'browser.js' + * } + * } + * } * }; * * Then: @@ -2977,6 +2993,43 @@ } } + function getPackageConfig(loader, pkgName) { + var pkgConfig = loader.packages[pkgName]; + + if (!pkgConfig.env) + return Promise.resolve(pkgConfig); + + // check environment conditions + // default environment condition is '@env' in package or '@system-env' globally + return loader['import'](pkgConfig.map['@env'] || '@system-env', pkgName) + .then(function(env) { + // derived config object + var pkg = {}; + for (var p in pkgConfig) + if (p !== 'map' & p !== 'env') + pkg[p] = pkgConfig[p]; + + pkg.map = {}; + for (var p in pkgConfig.map) + pkg.map[p] = pkgConfig.map[p]; + + for (var e in pkgConfig.env) { + if (env[e]) { + var envConfig = pkgConfig.env[e]; + if (envConfig.main) + pkg.main = envConfig.main; + for (var m in envConfig.map) + pkg.map[m] = envConfig.map[m]; + } + } + + // store the derived environment config so we have this cached for next time + loader.packages[pkgName] = pkg; + + return pkg; + }); + } + function applyMap(map, name) { var bestMatch, bestMatchLength = 0; @@ -2993,6 +3046,8 @@ return map[bestMatch] + name.substr(bestMatch.length); } + SystemJSLoader.prototype.normalizeSync = SystemJSLoader.prototype.normalize; + hook('normalize', function(normalize) { return function(name, parentName) { // apply contextual package map first @@ -3028,46 +3083,52 @@ var pkgName = getPackage.call(this, normalized); if (pkgName) { - var pkg = this.packages[pkgName]; - - // main - if (pkgName === normalized && pkg.main) - normalized += '/' + (pkg.main.substr(0, 2) == './' ? pkg.main.substr(2) : pkg.main); - - // defaultExtension & defaultJSExtension - // if we have meta for this package, don't do defaultExtensions - var defaultExtension = ''; - if (!pkg.meta || !pkg.meta[normalized.substr(pkgName.length + 1)]) { - // apply defaultExtension - if (pkg.defaultExtension) { - if (normalized.split('/').pop().indexOf('.') == -1) - defaultExtension = '.' + pkg.defaultExtension; - } - // apply defaultJSExtensions if defaultExtension not set - else if (defaultJSExtension) { - defaultExtension = '.js'; - } - } + return getPackageConfig(this, pkgName) + .then(function(pkg) { + // main + if (pkgName === normalized && pkg.main) + normalized += '/' + (pkg.main.substr(0, 2) == './' ? pkg.main.substr(2) : pkg.main); + + if (normalized.substr(pkgName.length) == '/') + return normalized; + + // defaultExtension & defaultJSExtension + // if we have meta for this package, don't do defaultExtensions + var defaultExtension = ''; + if (!pkg.meta || !pkg.meta[normalized.substr(pkgName.length + 1)]) { + // apply defaultExtension + + if ('defaultExtension' in pkg) { + if (pkg.defaultExtension !== false && normalized.split('/').pop().indexOf('.') == -1) + defaultExtension = '.' + pkg.defaultExtension; + } + // apply defaultJSExtensions if defaultExtension not set + else if (defaultJSExtension) { + defaultExtension = '.js'; + } + } - // apply submap checking without then with defaultExtension - var subPath = '.' + normalized.substr(pkgName.length); - var mapped = applyMap(pkg.map, subPath) || defaultExtension && applyMap(pkg.map, subPath + defaultExtension); - if (mapped) - normalized = mapped.substr(0, 2) == './' ? pkgName + mapped.substr(1) : mapped; - else - normalized += defaultExtension; + // apply submap checking without then with defaultExtension + var subPath = '.' + normalized.substr(pkgName.length); + var mapped = applyMap(pkg.map, subPath) || defaultExtension && applyMap(pkg.map, subPath + defaultExtension); + if (mapped) + normalized = mapped.substr(0, 2) == './' ? pkgName + mapped.substr(1) : mapped; + else + normalized += defaultExtension; + + + return normalized; + }); } + // add back defaultJSExtension if not a package - else if (defaultJSExtension) { + if (defaultJSExtension) normalized += '.js'; - } return normalized; }; }); - SystemJSLoader.prototype.normalizeSync = SystemJSLoader.prototype.normalize; - hook('locate', function(locate) { return function(load) { var loader = this; @@ -3167,7 +3228,7 @@ ]) .then(function(normalized) { argumentName = normalized[0]; - if (defaultExtension) + if (defaultExtension && argumentName.substr(argumentName.length - 3, 3) == '.js') argumentName = argumentName.substr(0, argumentName.length - 3); return argumentName + '!' + normalized[1]; }); @@ -3618,6 +3679,17 @@ var conditionalRegEx = /#\{[^\}]+\}|#\?.+$/; + hookConstructor(function(constructor) { + return function() { + constructor.call(this); + + // standard environment module, starting small as backwards-compat matters! + this.set('@system-env', this.newModule({ + browser: isBrowser + })); + }; + }); + hook('normalize', function(normalize) { return function(name, parentName, parentAddress) { var loader = this; diff --git a/vendor/xhr2.js b/vendor/xhr2.js deleted file mode 100644 index 075a4a5..0000000 --- a/vendor/xhr2.js +++ /dev/null @@ -1 +0,0 @@ -XMLHttpRequest = Npm.require('xhr2'); \ No newline at end of file