/app/scripts/vendor/hm.js
https://bitbucket.org/ceoaliongroo/torredelprior · JavaScript · 459 lines · 439 code · 7 blank · 13 comment · 7 complexity · 1e89fc681249dae1c7c138c9b1931aa3 MD5 · raw file
- /**
- * @license hm 0.2.1 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.
- * Available via the MIT or new BSD license.
- * see: http://github.com/jrburke/require-hm for details
- */
- /*jslint plusplus: true, regexp: true */
- /*global require, XMLHttpRequest, ActiveXObject, define, process, window,
- console */
- define(['esprima', 'module'], function (esprima, module) {
- 'use strict';
- var fs, getXhr,
- progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'],
- exportRegExp = /export\s+([A-Za-z\d\_]+)(\s+([A-Za-z\d\_]+))?/g,
- commentRegExp = /(\/\*([\s\S]*?)\*\/|[^\:]\/\/(.*)$)/mg,
- importModuleRegExp = /module|import/g,
- commaRegExp = /\,\s*$/,
- spaceRegExp = /\s+/,
- quoteRegExp = /['"]/,
- endingPuncRegExp = /[\,\;]\s*$/,
- moduleNameRegExp = /['"]([^'"]+)['"]/,
- startQuoteRegExp = /^['"]/,
- braceRegExp = /[\{\}]/g,
- buildMap = {},
- fetchText = function () {
- throw new Error('Environment unsupported.');
- };
- if (typeof window !== "undefined" && window.navigator && window.document) {
- // Browser action
- getXhr = function () {
- //Would love to dump the ActiveX crap in here. Need IE 6 to die first.
- var xhr, i, progId;
- if (typeof XMLHttpRequest !== "undefined") {
- return new XMLHttpRequest();
- } else {
- for (i = 0; i < 3; i++) {
- progId = progIds[i];
- try {
- xhr = new ActiveXObject(progId);
- } catch (e) {}
- if (xhr) {
- progIds = [progId]; // so faster next time
- break;
- }
- }
- }
- if (!xhr) {
- throw new Error("getXhr(): XMLHttpRequest not available");
- }
- return xhr;
- };
- fetchText = function (url, callback) {
- var xhr = getXhr();
- xhr.open('GET', url, true);
- xhr.onreadystatechange = function (evt) {
- //Do not explicitly handle errors, those should be
- //visible via console output in the browser.
- if (xhr.readyState === 4) {
- callback(xhr.responseText);
- }
- };
- xhr.send(null);
- };
- } else if (typeof process !== "undefined" &&
- process.versions &&
- !!process.versions.node) {
- //Using special require.nodeRequire, something added by r.js.
- fs = require.nodeRequire('fs');
- fetchText = function (path, callback) {
- callback(fs.readFileSync(path, 'utf8'));
- };
- }
- /**
- * Helper function for iterating over an array. If the func returns
- * a true value, it will break out of the loop.
- */
- function each(ary, func) {
- if (ary) {
- var i;
- for (i = 0; i < ary.length; i += 1) {
- if (ary[i] && func(ary[i], i, ary)) {
- break;
- }
- }
- }
- }
- /**
- * Cycles over properties in an object and calls a function for each
- * property value. If the function returns a truthy value, then the
- * iteration is stopped.
- */
- function eachProp(obj, func) {
- var prop;
- for (prop in obj) {
- if (obj.hasOwnProperty(prop)) {
- if (func(obj[prop], prop)) {
- break;
- }
- }
- }
- }
- /**
- * Inserts the hm! loader plugin prefix if necessary. If
- * there is already a ! in the string, then leave it be, and if it
- * starts with a ! it means "use normal AMD loading for this dependency".
- *
- * @param {String} id
- * @returns id
- */
- function cleanModuleId(id) {
- id = moduleNameRegExp.exec(id)[1];
- var index = id.indexOf('!');
- if (index === -1) {
- // Needs the hm prefix.
- id = 'hm!' + id;
- } else if (index === 0) {
- //Normal AMD loading, strip off the ! sign.
- id = id.substring(1);
- }
- return id;
- }
- function convertImportSyntax(tokens, start, end, moduleTarget) {
- var token = tokens[start],
- cursor = start,
- replacement = '',
- localVars = {},
- moduleRef,
- moduleId,
- star,
- currentVar;
- //Convert module target to an AMD usable name. If a string,
- //then needs to be accessed via require()
- if (startQuoteRegExp.test(moduleTarget)) {
- moduleId = cleanModuleId(moduleTarget);
- moduleRef = 'require("' + moduleId + '")';
- } else {
- moduleRef = moduleTarget;
- }
- if (token.type === 'Punctuator' && token.value === '*') {
- //import * from z
- //If not using a module ID that is a require call, then
- //discard it.
- if (moduleId) {
- star = moduleId;
- replacement = '/*IMPORTSTAR:' + star + '*/\n';
- } else {
- throw new Error('import * on local reference ' + moduleTarget +
- ' no supported.');
- }
- } else if (token.type === 'Identifier') {
- //import y from z
- replacement += 'var ' + token.value + ' = ' +
- moduleRef + '.' + token.value + ';';
- } else if (token.type === 'Punctuator' && token.value === '{') {
- //import {y} from z
- //import {x, y} from z
- //import {x: localX, y: localY} from z
- cursor += 1;
- token = tokens[cursor];
- while (cursor !== end && token.value !== '}') {
- if (token.type === 'Identifier') {
- if (currentVar) {
- localVars[currentVar] = token.value;
- currentVar = null;
- } else {
- currentVar = token.value;
- }
- } else if (token.type === 'Punctuator') {
- if (token.value === ',') {
- if (currentVar) {
- localVars[currentVar] = currentVar;
- currentVar = null;
- }
- }
- }
- cursor += 1;
- token = tokens[cursor];
- }
- if (currentVar) {
- localVars[currentVar] = currentVar;
- }
- //Now serialize the localVars
- eachProp(localVars, function (localName, importProp) {
- replacement += 'var ' + localName + ' = ' +
- moduleRef + '.' + importProp + ';\n';
- });
- } else {
- throw new Error('Invalid import: import ' +
- token.value + ' ' + tokens[start + 1].value +
- ' ' + tokens[start + 2].value);
- }
- return {
- star: star,
- replacement: replacement
- };
- }
- function convertModuleSyntax(tokens, i) {
- //Converts `foo = 'bar'` to `foo = require('bar')`
- var varName = tokens[i],
- eq = tokens[i + 1],
- id = tokens[i + 2];
- if (varName.type === 'Identifier' &&
- eq.type === 'Punctuator' && eq.value === '=' &&
- id.type === 'String') {
- return varName.value + ' = require("' + cleanModuleId(id.value) + '")';
- } else {
- throw new Error('Invalid module reference: module ' +
- varName.value + ' ' + eq.value + ' ' + id.value);
- }
- }
- function compile(path, text) {
- var stars = [],
- moduleMap = {},
- transforms = {},
- targets = [],
- currentIndex = 0,
- //Remove comments from the text to be scanned
- scanText = text.replace(commentRegExp, ""),
- transformedText = text,
- transformInputText,
- startIndex,
- segmentIndex,
- match,
- tempText,
- transformed,
- tokens;
- try {
- tokens = esprima.parse(text, {
- tokens: true,
- range: true
- }).tokens;
- } catch (e) {
- throw new Error('Esprima cannot parse: ' + path + ': ' + e);
- }
- each(tokens, function (token, i) {
- if (token.type !== 'Keyword' && token.type !== 'Identifier') {
- //Not relevant, skip
- return;
- }
- var next = tokens[i + 1],
- next2 = tokens[i + 2],
- next3 = tokens[i + 3],
- cursor = i,
- replacement,
- moduleTarget,
- target,
- convertedImport;
- if (token.value === 'export') {
- // EXPORTS
- if (next.type === 'Keyword') {
- if (next.value === 'var' || next.value === 'let') {
- targets.push({
- start: token.range[0],
- end: next2.range[0],
- replacement: 'exports.'
- });
- } else if (next.value === 'function' && next2.type === 'Identifier') {
- targets.push({
- start: token.range[0],
- end: next2.range[1],
- replacement: 'exports.' + next2.value +
- ' = function '
- });
- } else {
- throw new Error('Invalid export: ' + token.value +
- ' ' + next.value + ' ' + tokens[i + 2]);
- }
- } else if (next.type === 'Identifier') {
- targets.push({
- start: token.range[0],
- end: next.range[1],
- replacement: 'exports.' + next.value +
- ' = ' + next.value
- });
- } else {
- throw new Error('Invalid export: ' + token.value +
- ' ' + next.value + ' ' + tokens[i + 2]);
- }
- } else if (token.value === 'module') {
- // MODULE
- // module Bar = "bar.js";
- replacement = 'var ';
- target = {
- start: token.range[0]
- };
- while (token.value === 'module' || (token.type === 'Punctuator'
- && token.value === ',')) {
- cursor = cursor + 1;
- replacement += convertModuleSyntax(tokens, cursor);
- token = tokens[cursor + 3];
- //Current module spec does not allow for
- //module a = 'a', b = 'b';
- //must end in semicolon. But keep this in case for later,
- //as comma separators would be nice.
- //esprima will throw if comma is not allowed.
- if ((token.type === 'Punctuator' && token.value === ',')) {
- replacement += ',\n';
- }
- }
- target.end = token.range[0];
- target.replacement = replacement;
- targets.push(target);
- } else if (token.value === 'import') {
- // IMPORT
- //import * from z;
- //import y from z;
- //import {y} from z;
- //import {x, y} from z;
- //import {x: localX, y: localY} from z;
- cursor = i;
- //Find the "from" in the stream
- while (tokens[cursor] &&
- (tokens[cursor].type !== 'Identifier' ||
- tokens[cursor].value !== 'from')) {
- cursor += 1;
- }
- //Increase cursor one more value to find the module target
- moduleTarget = tokens[cursor + 1].value;
- convertedImport = convertImportSyntax(tokens, i + 1, cursor - 1, moduleTarget);
- replacement = convertedImport.replacement;
- if (convertedImport.star) {
- stars.push(convertedImport.star);
- }
- targets.push({
- start: token.range[0],
- end: tokens[cursor + 3].range[0],
- replacement: replacement
- });
- }
- });
- //Now sort all the targets, but by start position, with the
- //furthest start position first, since we need to transpile
- //in reverse order.
- targets.sort(function (a, b) {
- return a.start > b.start ? -1 : 1;
- });
- //Now walk backwards through targets and do source modifications
- //to AMD. Going backwards is important since the modifications will
- //modify the length of the string.
- each(targets, function (target, i) {
- transformedText = transformedText.substring(0, target.start) +
- target.replacement +
- transformedText.substring(target.end, transformedText.length);
- });
- return {
- text: "define(function (require, exports, module) {\n" +
- transformedText +
- '\n});',
- stars: stars
- };
- }
- function finishLoad(require, load, name, transformedText, text, isBuild) {
- //Hold on to the transformed text if a build.
- if (isBuild) {
- buildMap[name] = transformedText;
- }
- load.fromText(name, transformedText);
- if (module.config().logTransform) {
- console.log("INPUT:\n" + text + "\n\nTRANSFORMED:\n" + transformedText);
- }
- //Give result to load. Need to wait until the module
- //is fully parsed, which will happen after this
- //execution.
- require([name], function (value) {
- load(value);
- });
- }
- return {
- version: '0.2.1',
- write: function (pluginName, name, write) {
- if (buildMap.hasOwnProperty(name)) {
- var text = buildMap[name];
- write.asModule(pluginName + "!" + name, text);
- }
- },
- load: function (name, require, load, config) {
- var path = require.toUrl(name + '.hm');
- fetchText(path, function (text) {
- var result = compile(path, text),
- transformedText = result.text;
- //IE with conditional comments on cannot handle the
- //sourceURL trick, so skip it if enabled.
- /*@if (@_jscript) @else @*/
- if (!config.isBuild) {
- transformedText += "\r\n//@ sourceURL=" + path;
- }
- /*@end@*/
- if (result.stars && result.stars.length) {
- //First load any imports that require recursive analysis
- //TODO: this will break if there is a circular
- //dependency with each file doing an import * on each other.
- require(result.stars, function () {
- var i, star, mod, starText, prop;
- //Now fix up the import * items for each module.
- for (i = 0; i < result.stars.length; i++) {
- star = result.stars[i];
- starText = '';
- mod = arguments[i];
- for (prop in mod) {
- if (mod.hasOwnProperty(prop)) {
- starText += 'var ' + prop + ' = require("' + star + '").' + prop + '; ';
- }
- }
- transformedText = transformedText.replace('/*IMPORTSTAR:' + star + '*/', starText);
- }
- finishLoad(require, load, name, transformedText, text, config.isBuild);
- });
- } else {
- finishLoad(require, load, name, transformedText, text, config.isBuild);
- }
- });
- }
- };
- });