Reviewers: arv-chromium, slightlylate, johnjbarton,
Message:
Note: This is more of an RFC at this point, because we should really
fix the upstream semicolon bugs before integrating the new source-map.
But this does bring up the issue of traceur always being in danger of
being broken by a dependency update.
----
I actually have three implementations:
- Patch Set 1 is the simplest,
- Patch Set 2 is slightly more complex (but cleaner, I think), and
- the third one handles relative paths between modules, and is overkill
for the task of wrapping source-map. I ended up not including that
because there's a lot of code that we may never exercise, and so bugs
may stay there for a long time.
See the last (non-comment) section for a demo.
I originally considered generating the include directives, but if you
want to do the entire thing (including deciding which files to include
in the first place) automatically, then you really have to have much
smarter code, and you may really have to do a test load of a module in
order to find out the true dependencies.
// this can probably be statically analyzed, but I'm sure it gets
// complicated fast.
var modules = ['module1', 'module2'].map(require);
Too big of a project for the simple goal of wrapping a single library,
so I stopped there.
----
Here is a quick demo of the third implementation mentioned earlier:
#--cut--
cat > require.js <<"END"
'use strict';
/**
* @param {string} b Base path to add to module relative paths. Assumed
to be
* normalized.
* @param {string} p Path to normalize
* @return {string} A path normalized according to normal rules, except
that
* paths starting with dot segments always retain at least one dot
segment.
* This is so that module relative paths remain relative, and module
* top-level paths remain top-level.
*/
function normalize(b, p) {
var pi = p.split('/');
var po = [];
var segs = 0;
function addSeg(seg) {
switch (seg) {
case '..':
if (segs) {
segs--;
if (po.pop() !== '.')
return;
}
po.push('..');
return;
case '.':
if (!po.length) {
segs++;
po.push('.');
}
return;
default:
if (po.length) {
segs++;
}
po.push(seg);
}
}
if (pi[0] === '.' || pi[0] === '..') {
b.split('/').forEach(addSeg);
}
p.split('/').forEach(addSeg);
return po.join('/');
}
/**
* @param {object} mapping Maps between module ids and exports.
* @param {string} id A module id.
* @return {function} a 'define' function that initializes |mapping[id]|
with
* the exports from |factory|.
*/
function makeDefine(mapping, id) {
return function(factory) {
mapping[id] = factory;
};
}
function makeRequire(mapping) {
var bases = ['.'];
function resolve(id) {
if (!/\.{0,2}\//.exec(id))
return id;
var base = bases[bases.length - 1];
return normalize(base, id);
}
function dirname(id) {
if (!/\//.test(id)) {
return id;
}
return /(.*)(?:\/.*)$/.exec(id)[1];
}
function require(id) {
var factory, exports, module = {};
switch (typeof mapping[id = resolve(id)]) {
case 'undefined':
throw new TypeError(id + ' not defined.');
case 'function':
console.log('id:%s', id);
factory = mapping[id];
exports = mapping[id] = {};
bases.push(dirname(id));
factory(require, exports, module);
bases.pop();
return exports;
case 'object':
return mapping[id];
}
}
return require;
}
var mapping = {};
var define, require = makeRequire(mapping);
define = makeDefine(mapping, 'hello');
define(function(r,exports,c){
var world = require('./dir/world').world;
exports.hello = 'hello ' + world;
exports.hola = require('./hola').hola;
})
define = makeDefine(mapping, 'hello/hola');
define(function(r,exports,c){
exports.hola = 'hola';
})
define = makeDefine(mapping, 'hello/dir/world');
define(function(r,exports,c){
var rld = require('./rld').rld;
exports.world = 'wo' + rld;
})
define = makeDefine(mapping, 'hello/dir/rld');
define(function(r,exports,c){
exports.rld = 'rld';
})
define = makeDefine(mapping, './hwdir/hw');
define(function(r,exports,c){
exports.hw = require('hello');
exports.jo = require('../jo').jo;
})
define = makeDefine(mapping, './jo');
define(function(r,exports,c){
exports.jo = 'jo';
})
console.log(require('./hwdir/hw'));
END
node require.js
#--cut--
https://codereview.appspot.com/7789051/diff/2001/Makefile
File Makefile (left):
https://codereview.appspot.com/7789051/diff/2001/Makefile#oldcode11
Makefile:11: TFLAGS = --strict-semicolons --
The newest version of source-map (0.1.16 currently) has some missing
semicolons, so I've temporarily taken out this flag in order to allow
the build to complete successfully.
Description:
Wrap the newest version of 'source-map'.
- Add 'source-map' to package.json
BUG=
https://code.google.com/p/traceur-compiler/issues/detail?id=222
TEST=None
Please review this at
https://codereview.appspot.com/7789051/
Affected files:
M Makefile
M package.json
M src/outputgeneration/SourceMapIntegration.js-template.js
Index: Makefile
diff --git a/Makefile b/Makefile
index
70fabfadf7e21946ee9584806724be4154c987de..201794b3ce1bba8851adc8708125bece339a45ff
100644
--- a/Makefile
+++ b/Makefile
@@ -8,7 +8,7 @@ GENSRC = \
src/syntax/trees/ParseTrees.js \
src/syntax/ParseTreeVisitor.js
-TFLAGS = --strict-semicolons --
+TFLAGS = --
build: bin/traceur.js
Index: package.json
diff --git a/package.json b/package.json
index
465fc4570f530881242eb52650d37afb65dcadbe..08880f7caa335cbd93491c72cf065d4a80277a27
100644
--- a/package.json
+++ b/package.json
@@ -35,6 +35,7 @@
"commander": ">=1.1"
},
"devDependencies": {
- "closure-library": ">=1.0"
+ "closure-library": ">=1.0",
+ "source-map": ">=0.1.16"
}
}
Index: src/outputgeneration/SourceMapIntegration.js-template.js
diff --git a/src/outputgeneration/SourceMapIntegration.js-template.js
b/src/outputgeneration/SourceMapIntegration.js-template.js
index
8ae6fe705702f2a32a74028387733e4eea452b89..2c59b8163375ac25c28817a60d45106607a28a8f
100644
--- a/src/outputgeneration/SourceMapIntegration.js-template.js
+++ b/src/outputgeneration/SourceMapIntegration.js-template.js
@@ -12,34 +12,61 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-var moduleExports = {};
+// 'source-map' (>=0.1.16) requires a 'define' function that matches the
+// CommonJS module specification. Since it only uses a subset of that
+// functionality, we cheat here and only provide that minimum subset.
-// 'source-map' changes its loading behavior depending on what
the 'exports'
-// and 'define' symbols refer to.
-//
-// For the behavior we want, we set up the environment such that:
-//
-// exports === undefined
-// define === undefined
-// this === moduleExports // Receives the 'sourceMapModule' export.
+/**
+ * @param {object} mapping Maps between module ids and exports.
+ * @param {string} id A module id.
+ * @return {function} a 'define' function that initializes |mapping[id]|
with
+ * the exports from |factory|.
+ */
+function makeDefine(mapping, id) {
+ return function(factory) {
+ mapping[id] = factory;
+ };
+}
+
+function makeRequire(mapping) {
+ function require(id) {
+ var factory, exports, module = {};
+ switch (typeof mapping[id]) {
+ case 'undefined':
+ throw new TypeError(id + ' not defined.');
+ case 'function':
+ factory = mapping[id];
+ exports = mapping[id] = {};
+ factory(require, exports, module);
+ return exports;
+ case 'object':
+ return mapping[id];
+ }
+ }
+ return require;
+}
-(function(exports, define) {
-// #include ../../third_party/source-map/lib/source-map/array-set.js
-// #include ../../third_party/source-map/lib/source-map/base64.js
-// #include ../../third_party/source-map/lib/source-map/base64-vlq.js
-// #include ../../third_party/source-map/lib/source-map/binary-search.js
-// #include ../../third_party/source-map/lib/source-map/util.js
-//
#include ../../third_party/source-map/lib/source-map/source-map-generator.js
-//
#include ../../third_party/source-map/lib/source-map/source-map-consumer.js
-// #include ../../third_party/source-map/lib/source-map/source-node.js
-}).call(moduleExports);
+var mapping = {};
+var define, require = makeRequire(mapping);
-var sourceMapModule = moduleExports.sourceMapModule;
+define = makeDefine(mapping, './util');
+// #include ../../node_modules/source-map/lib/source-map/util.js
+define = makeDefine(mapping, './array-set');
+// #include ../../node_modules/source-map/lib/source-map/array-set.js
+define = makeDefine(mapping, './base64');
+// #include ../../node_modules/source-map/lib/source-map/base64.js
+define = makeDefine(mapping, './base64-vlq');
+// #include ../../node_modules/source-map/lib/source-map/base64-vlq.js
+define = makeDefine(mapping, './binary-search');
+// #include ../../node_modules/source-map/lib/source-map/binary-search.js
+define = makeDefine(mapping, './source-map-generator');
+//
#include ../../node_modules/source-map/lib/source-map/source-map-generator.js
+define = makeDefine(mapping, './source-map-consumer');
+//
#include ../../node_modules/source-map/lib/source-map/source-map-consumer.js
+define = makeDefine(mapping, './source-node');
+// #include ../../node_modules/source-map/lib/source-map/source-node.js
-export var base64 = sourceMapModule['base64'];
-export var base64Vlq = sourceMapModule['base64-vlq'];
-export var binarySearch = sourceMapModule['binary-search'];
-export var util = sourceMapModule['util'];
-export var SourceMapGenerator = sourceMapModule['source-map-generator'];
-export var SourceMapConsumer = sourceMapModule['source-map-consumer'];
-export var SourceNode = sourceMapModule['source-node'];
+export var
+ SourceMapGenerator =
require('./source-map-generator').SourceMapGenerator,
+ SourceMapConsumer = require('./source-map-consumer').SourceMapConsumer,
+ SourceNode = require('./source-node').SourceNode;