PageRenderTime 55ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/packages/jsio.js

https://github.com/skysbird/js.io
JavaScript | 783 lines | 603 code | 70 blank | 110 comment | 81 complexity | f74994e9275f8f188bf154a8cbba42bf MD5 | raw file
  1. // Copyright (c) 2010
  2. // Michael Carter (cartermichael@gmail.com)
  3. // Martin Hunt (mghunt@gmail.com)
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. // THE SOFTWARE.
  22. // Initialization of js.io occurs in a closure, preventing local variables
  23. // from entering the global scope. During execution, the method `jsio` is
  24. // added to the global scope.
  25. ;(function() {
  26. function init(cloneFrom) {
  27. // We expect this code to be minified before production use, so we may
  28. // write code slightly more verbosely than we otherwise would.
  29. // Should we parse syntax errors in the browser?
  30. var DEBUG = true;
  31. // Store a reference to the slice function for converting objects of
  32. // type arguments to type array.
  33. var SLICE = Array.prototype.slice;
  34. // js.io supports multiple JavaScript environments such as node.js and
  35. // most web browsers (IE, Firefox, WebKit). The ENV object wraps
  36. // any utility functions that contain environment-specific code (e.g.
  37. // reading a file using node's `fs` library or a browser's
  38. // `XMLHttpRequest`). Running js.io in other JavaScript environments
  39. // is as easy as implementing an environment object that conforms to
  40. // the abstract interface for an environment (provided below) and
  41. // calling `jsio.setEnv()`.
  42. var ENV;
  43. // Checks if the last character in a string is `/`.
  44. var rexpEndSlash = /\/$/;
  45. // Creates an object containing metadata about a module.
  46. function makeModuleDef(path, baseMod, basePath) {
  47. var def = util.splitPath(path + '.js');
  48. if (baseMod) {
  49. def.baseMod = baseMod;
  50. def.basePath = basePath;
  51. }
  52. return def;
  53. }
  54. // Utility functions
  55. var util = {
  56. // `util.bind` returns a function that, when called, will execute
  57. // the method passed in with the provided context and any additional
  58. // arguments passed to `util.bind`.
  59. // util.bind(obj, 'f', a) -> function() { return obj.f(a); }
  60. // util.bind(obj, g, a, b, c) -> function() { return g.call(g, a, b, c); }
  61. bind: function(context, method/*, args... */) {
  62. var args = SLICE.call(arguments, 2);
  63. return function() {
  64. method = (typeof method == 'string' ? context[method] : method);
  65. return method.apply(context, args.concat(SLICE.call(arguments, 0)));
  66. };
  67. },
  68. // `util.addEndSlash` accepts a string. That string is returned with a `/`
  69. // appended if the string did not already end in a `/`.
  70. addEndSlash: function(str) {
  71. return rexpEndSlash.test(str) ? str : str + '/';
  72. },
  73. // `util.removeEndSlash` accepts a string. It removes a trailing `/` if
  74. // one is found.
  75. removeEndSlash: function(str) {
  76. return str.replace(rexpEndSlash, '');
  77. },
  78. // `util.makeRelativePath` accepts two paths (strings) and returns the first path
  79. // made relative to the second. Note: this function needs some work. It currently
  80. // handles the most common use cases, but may fail in unexpected edge cases.
  81. //
  82. // - Simple case: if `path` starts with `relativeTo`, then we can strip `path`
  83. // off the `relativeTo` part and we're done.
  84. //
  85. // util.makeRelativePath('abc/def/', 'abc') -> 'def'
  86. //
  87. // - Harder case: `path` starts with some substring of `relativeTo`. We want to remove this substring and then add `../` for each remaining segment of `relativeTo`.
  88. //
  89. // util.makeRelativePath('abc/def/', 'abc/hij') -> '../def'
  90. //
  91. makeRelativePath: function(path, relativeTo) {
  92. var len = relativeTo.length;
  93. if (path.substring(0, len) == relativeTo) {
  94. /* Note: we're casting a boolean to an int by adding len to it */
  95. return path.slice((path.charAt(len) == '/') + len);
  96. }
  97. var sA = util.removeEndSlash(path).split('/'),
  98. sB = util.removeEndSlash(relativeTo).split('/'),
  99. i = 0;
  100. /* Count how many segments match. */
  101. while(sA[i] == sB[i]) { ++i; }
  102. if (i) {
  103. /* If at least some segments matched, remove them. The result is our new path. */
  104. path = sA.slice(i).join('/');
  105. /* Prepend `../` for each segment remaining in `relativeTo`. */
  106. for (var j = sB.length - i; j > 0; --j) { path = '../' + path; }
  107. }
  108. return path;
  109. },
  110. // `buildPath` accepts an arbitrary number of string arguments to concatenate into a path.
  111. // util.buildPath('a', 'b', 'c/', 'd/') -> 'a/b/c/d/'
  112. buildPath: function() {
  113. return util.resolveRelativePath(Array.prototype.join.call(arguments, '/'));
  114. },
  115. // `resolveRelativePath` removes relative path indicators. For example:
  116. // util.resolveRelativePath('a/../b') -> b
  117. resolveRelativePath: function(path) {
  118. /* If the path starts with a protocol, store it and remove it (add it
  119. back later) so we don't accidently modify it. */
  120. var protocol = path.match(/^(\w+:\/\/)(.*)$/);
  121. if (protocol) { path = protocol[2]; }
  122. /* Remove multiple slashes and trivial dots (`/./ -> /`). */
  123. path = path.replace(/\/+/g, '/').replace(/\/\.\//g, '/');
  124. /* Loop to collapse instances of `../` in the path by matching a previous
  125. path segment. Essentially, we find substrings of the form `/abc/../`
  126. where abc is not `.` or `..` and replace the substrings with `/`.
  127. We loop until the string no longer changes since after collapsing all
  128. possible instances once, we may have created more instances that can
  129. be collapsed.
  130. */
  131. var o;
  132. while((o = path) != (path = path.replace(/(^|\/)(?!\.?\.\/)([^\/]+)\/\.\.\//g, '$1'))) {}
  133. /* Don't forget to prepend any protocol we might have removed earlier. */
  134. return protocol ? protocol[1] + path : path;
  135. },
  136. resolveRelativeModule: function(modulePath, directory) {
  137. var result = [],
  138. parts = modulePath.split('.'),
  139. len = parts.length,
  140. relative = (len > 1 && !parts[0]),
  141. i = relative ? 0 : -1;
  142. while(++i < len) { result.push(parts[i] ? parts[i] : '..'); }
  143. return util.buildPath(relative ? directory : '', result.join('/'));
  144. },
  145. resolveModulePath: function(modulePath, directory) {
  146. // resolve relative paths
  147. if(modulePath.charAt(0) == '.') {
  148. return [makeModuleDef(util.resolveRelativeModule(modulePath, directory))];
  149. }
  150. // resolve absolute paths with respect to jsio packages/
  151. var pathSegments = modulePath.split('.'),
  152. baseMod = pathSegments[0],
  153. pathString = pathSegments.join('/');
  154. if (jsio.path.cache.hasOwnProperty(baseMod)) {
  155. return [makeModuleDef(util.buildPath(jsio.path.cache[baseMod], pathString))];
  156. }
  157. var out = [],
  158. paths = jsio.path.get(),
  159. len = paths.length;
  160. for (var i = 0; i < len; ++i) {
  161. out.push(makeModuleDef(util.buildPath(paths[i], pathString), baseMod, paths[i]));
  162. }
  163. return out;
  164. },
  165. splitPath: function(path) {
  166. var i = path.lastIndexOf('/') + 1;
  167. return {
  168. path: path,
  169. directory: path.substring(0, i),
  170. filename: path.substring(i)
  171. };
  172. }
  173. };
  174. var jsio = util.bind(this, importer, null, null, null);
  175. jsio.__util = util;
  176. jsio.__init__ = init;
  177. // explicitly use jsio.__srcCache to avoid obfuscation with closure compiler
  178. var sourceCache = jsio.__srcCache = {};
  179. if (cloneFrom && cloneFrom.__srcCache) { sourceCache = jsio.__srcCache = cloneFrom.__srcCache; }
  180. (function() {
  181. this.__filename = 'jsio.js';
  182. this.__preprocessors = {};
  183. this.__cmds = [];
  184. this.__jsio = this;
  185. this.__importer = importer;
  186. this.__modules = {preprocessors:{}};
  187. this.path = {
  188. set: function(path) { this.value = (typeof path == 'string' ? [path] : path); },
  189. get: function() { return this.value.slice(0); },
  190. add: function(path) {
  191. var v = this.value, len = v.length;
  192. for (var i = 0; i < len; ++i) {
  193. if (v[i] == path) { return; }
  194. }
  195. v.push(path);
  196. },
  197. remove: function(path) {
  198. var v = this.value, len = v.length;
  199. for (var i = 0; i < len; ++i) {
  200. if (v[i] == path) {
  201. v.splice(i, 1);
  202. }
  203. }
  204. },
  205. value: [],
  206. cache: {}
  207. };
  208. this.addPath = util.bind(this.path, 'add');
  209. this.setCachedSrc = function(path, src) { sourceCache[path] = { path: path, src: src }; }
  210. this.getCachedSrc = function(path) { return sourceCache[path]; }
  211. this.addPreprocessor = function(name, preprocessor) { this.__preprocessors[name] = preprocessor; }
  212. this.addCmd = function(processor) { this.__cmds.push(processor); }
  213. this.setEnv = function(envCtor) {
  214. if (!envCtor && cloneFrom) {
  215. ENV = cloneFrom.__env;
  216. } else if (typeof envCtor == 'string') {
  217. switch(envCtor) {
  218. case 'node':
  219. ENV = new ENV_node(util);
  220. break;
  221. case 'browser':
  222. default:
  223. ENV = new ENV_browser(util);
  224. break;
  225. }
  226. } else {
  227. ENV = new envCtor(util);
  228. }
  229. this.__env = ENV;
  230. this.__dir = ENV.getCwd();
  231. this.path.set(ENV.getPath());
  232. }
  233. }).call(jsio);
  234. if (cloneFrom) {
  235. jsio.setEnv();
  236. } else if (typeof process !== 'undefined' && process.version) {
  237. jsio.setEnv('node');
  238. } else if (typeof XMLHttpRequest != 'undefined' || typeof ActiveXObject != 'undefined') {
  239. jsio.setEnv('browser');
  240. }
  241. /*
  242. function ENV_abstract() {
  243. this.global = null;
  244. this.getCwd = function() {};
  245. this.getPath = function() {};
  246. this.eval = function(code, path) {};
  247. this.fetch = function(path) { return contentsOfPath; };
  248. this.log = function(args...) {};
  249. }
  250. */
  251. function ENV_node() {
  252. var fs = require('fs'),
  253. sys = require('sys');
  254. this.name = 'node';
  255. this.global = GLOBAL;
  256. this.getCwd = process.cwd;
  257. this.log = function() {
  258. var msg;
  259. try {
  260. sys.error(msg = Array.prototype.map.call(arguments, function(a) {
  261. if ((a instanceof Error) && a.message) {
  262. return 'Error:' + a.message + '\nStack:' + a.stack + '\nArguments:' + a.arguments;
  263. }
  264. return typeof a == 'string' ? a : JSON.stringify(a);
  265. }).join(' '));
  266. } catch(e) {
  267. sys.error(msg = Array.prototype.join.call(arguments, ' ') + '\n');
  268. }
  269. return msg;
  270. }
  271. this.getPath = function() {
  272. var segments = __filename.split('/');
  273. segments.pop();
  274. return util.makeRelativePath(segments.join('/') || '.', this.getCwd());
  275. }
  276. this.eval = process.compile;
  277. this.fetch = function(path) {
  278. try { return fs.readFileSync(path, 'utf8'); } catch(e) {}
  279. return false;
  280. }
  281. this.require = require;
  282. }
  283. function ENV_browser() {
  284. var XHR = window.XMLHttpRequest || function() { return new ActiveXObject("Msxml2.XMLHTTP"); },
  285. cwd = null,
  286. path = null;
  287. this.name = 'browser';
  288. this.global = window;
  289. this.global.jsio = jsio;
  290. this.log = function() {
  291. var args = SLICE.call(arguments, 0);
  292. if (typeof console != 'undefined' && console.log) {
  293. //for iOS mobile safari w/ debugger console enabled,
  294. //uncomment the following two lines for more useful
  295. //messages
  296. //console.log(args.join(' '));
  297. //return;
  298. if (console.log.apply) {
  299. console.log.apply(console, arguments);
  300. } else { // IE doesn't support log.apply, and the argument cannot be arguments - it must be an array
  301. console.log(args);
  302. }
  303. }
  304. return args.join(' ');
  305. }
  306. this.getCwd = function() {
  307. if(!cwd) {
  308. var loc = window.location, path = loc.pathname;
  309. cwd = loc.protocol + '//' + loc.host + path.substring(0, path.lastIndexOf('/') + 1);
  310. }
  311. return cwd;
  312. }
  313. this.getPath = function() {
  314. if(!path) {
  315. try {
  316. var filename = new RegExp('(.*?)' + jsio.__filename + '(\\?.*)?$'),
  317. scripts = document.getElementsByTagName('script');
  318. for (var i = 0, script; script = scripts[i]; ++i) {
  319. var result = script.src.match(filename);
  320. if (result) {
  321. path = result[1];
  322. if (/^[A-Za-z]*:\/\//.test(path)) { path = util.makeRelativePath(path, this.getCwd()); }
  323. break;
  324. }
  325. }
  326. } catch(e) {}
  327. if(!path) { path = '.'; }
  328. }
  329. return path;
  330. }
  331. this.debugPath = function(path) { return path; }
  332. // IE6 won't return an anonymous function from eval, so use the function constructor instead
  333. var rawEval = typeof eval('(function(){})') == 'undefined'
  334. ? function(src, path) { return (new Function('return ' + src))(); }
  335. : function(src, path) { var src = src + '\n//@ sourceURL=' + path; return window.eval(src); };
  336. // provide an eval with reasonable debugging
  337. this.eval = function(code, path, origCode) {
  338. try {
  339. return rawEval(code, this.debugPath(path));
  340. } catch(e) {
  341. if(e instanceof SyntaxError) {
  342. ENV.log("a syntax error is preventing execution of " + path);
  343. if (DEBUG && this.checkSyntax) {
  344. this.checkSyntax(origCode, path);
  345. }
  346. }
  347. throw e;
  348. }
  349. }
  350. this.checkSyntax = function(code, path) {
  351. try {
  352. var syntax = jsio('import util.syntax', {suppressErrors: true, dontExport: true}),
  353. result = syntax(code);
  354. syntax.display(result, path);
  355. } catch(e) {}
  356. }
  357. this.fetch = function(path) {
  358. var xhr = new XHR();
  359. try {
  360. xhr.open('GET', path, false);
  361. xhr.send(null);
  362. } catch(e) {
  363. ENV.log('e:', e);
  364. return false; // firefox file://
  365. }
  366. if (xhr.status == 404 || // all browsers, http://
  367. xhr.status == -1100 || // safari file://
  368. // XXX: We have no way to tell in opera if a file exists and is empty, or is 404
  369. // XXX: Use flash?
  370. //(!failed && xhr.status == 0 && !xhr.responseText && EXISTS)) // opera
  371. false)
  372. {
  373. return false;
  374. }
  375. return xhr.responseText;
  376. }
  377. };
  378. var preprocessorCheck = /^"use (.*?)"\s*;\s*\n/,
  379. preprocessorFunc = /^(.+)\(.+\)$/,
  380. failedFetch = {};
  381. function findModule(possibilities, opts) {
  382. var src;
  383. for (var i = 0, possible; possible = possibilities[i]; ++i) {
  384. var path = possible.path,
  385. cachedVersion = sourceCache[path];
  386. if (cachedVersion) {
  387. possible.src = cachedVersion.src;
  388. return possible;
  389. }
  390. /*if (/^\.\//.test(path)) {
  391. // remove one path segment for each dot from the cwd
  392. path = addEndSlash(ENV.getCwd()) + path;
  393. }*/
  394. src = ENV.fetch(path);
  395. if (src !== false) {
  396. possible.src = src;
  397. return possible;
  398. } else {
  399. failedFetch[path] = true;
  400. }
  401. }
  402. return false;
  403. }
  404. // load a module from a file
  405. function loadModule(fromDir, fromFile, modulePath, opts) {
  406. var possibilities = util.resolveModulePath(modulePath, fromDir);
  407. for (var i = 0, p; p = possibilities[i]; ++i) {
  408. var path = possibilities[i].path;
  409. if (!opts.reload && (path in jsio.__modules)) {
  410. return possibilities[i];
  411. }
  412. if (path in failedFetch) { possibilities.splice(i--, 1); }
  413. }
  414. if (!possibilities.length) {
  415. if (opts.suppressErrors) { return false; }
  416. var e = new Error('Module failed to load (again)');
  417. e.jsioLogged = true;
  418. throw e;
  419. }
  420. var moduleDef = findModule(possibilities, opts),
  421. match;
  422. if (!moduleDef) {
  423. if (opts.suppressErrors) { return false; }
  424. var paths = [];
  425. for (var i = 0, p; p = possibilities[i]; ++i) { paths.push(p.path); }
  426. throw new Error(fromDir + fromFile + ": \n\tcurrent directory: " + ENV.getCwd() + "\n\tlooked in:\n\t\t" + paths.join('\n\t\t') + '\n\tImport Stack:\n\t\t' + importStack.join('\n\t\t') + "\n\tError: requested import (" + modulePath + ") not found.");
  427. }
  428. moduleDef.friendlyPath = modulePath;
  429. if (moduleDef.baseMod && !(moduleDef.baseMod in jsio.path.cache)) {
  430. jsio.path.cache[moduleDef.baseMod] = moduleDef.basePath;
  431. }
  432. // the order here is somewhat arbitrary and might be overly restrictive (... or overly powerful)
  433. while (moduleDef.src.charAt(0) == '"' && (match = moduleDef.src.match(preprocessorCheck))) {
  434. moduleDef.src = moduleDef.src.substring(match[0].length - 1);
  435. applyPreprocessors(fromDir, moduleDef, match[1].split(','), opts);
  436. }
  437. if (opts.preprocessors) {
  438. applyPreprocessors(fromDir, moduleDef, opts.preprocessors, opts);
  439. }
  440. return moduleDef;
  441. }
  442. function applyPreprocessors(path, moduleDef, names, opts) {
  443. for (var i = 0, len = names.length; i < len; ++i) {
  444. p = getPreprocessor(names[i]);
  445. if (p) {
  446. p(path, moduleDef, opts);
  447. }
  448. }
  449. }
  450. function getPreprocessor(name) {
  451. return typeof name == 'function'
  452. ? name
  453. : (jsio.__modules['preprocessors.' + name]
  454. || jsio('import preprocessors.' + name, {dontExport: true}));
  455. }
  456. function execModuleDef(context, moduleDef) {
  457. var code = "(function(_){with(_){delete _;return function $$" + moduleDef.friendlyPath.replace(/[\/.]/g, '_') + "(){" + moduleDef.src + "\n}}})";
  458. var fn = ENV.eval(code, moduleDef.path, moduleDef.src);
  459. fn = fn(context);
  460. fn.call(context.exports);
  461. };
  462. function resolveImportRequest(context, request, opts) {
  463. var cmds = jsio.__cmds,
  464. imports = [],
  465. result = false;
  466. for (var i = 0, imp; imp = cmds[i]; ++i) {
  467. if ((result = imp(context, request, opts, imports))) { break; }
  468. }
  469. if (result !== true) {
  470. throw new (typeof SyntaxError != 'undefined' ? SyntaxError : Error)(String(result || 'invalid jsio command: jsio(\'' + request + '\')'));
  471. }
  472. return imports;
  473. };
  474. function makeContext(ctx, modulePath, moduleDef, dontAddBase) {
  475. if (!ctx) { ctx = {}; }
  476. if (!ctx.exports) { ctx.exports = {}; }
  477. ctx.jsio = util.bind(this, importer, ctx, moduleDef.directory, moduleDef.filename);
  478. ctx.require = function(request, opts) {
  479. if (!opts) { opts = {}; }
  480. opts.dontExport = true;
  481. opts.suppressErrors = true;
  482. try {
  483. var ret = ctx.jsio(request, opts);
  484. if (ret === false) {
  485. // need this to trigger require attempt due to suppresserrors = true
  486. throw "module failed to load";
  487. } else {
  488. return ret;
  489. }
  490. } catch(e) {
  491. try {
  492. return require(request);
  493. } catch(e2) {
  494. ENV.log('Error loading request ' + request + ':');
  495. ENV.log(e);
  496. ENV.log('Also could not load using standard CommonJS');
  497. ENV.log(e2);
  498. throw e;
  499. }
  500. }
  501. };
  502. ctx.module = {id: modulePath, exports: ctx.exports};
  503. if (!dontAddBase && modulePath != 'base') {
  504. ctx.jsio('from base import *');
  505. ctx.logging.__create(modulePath, ctx);
  506. }
  507. // TODO: FIX for "trailing ." case
  508. ctx.jsio.__jsio = jsio;
  509. ctx.jsio.__env = jsio.__env;
  510. ctx.jsio.__dir = moduleDef.directory;
  511. ctx.jsio.__filename = moduleDef.filename;
  512. ctx.jsio.__path = modulePath;
  513. ctx.jsio.path = jsio.path;
  514. return ctx;
  515. };
  516. var importStack = [];
  517. function importer(boundContext, fromDir, fromFile, request, opts) {
  518. opts = opts || {};
  519. fromDir = fromDir || './';
  520. fromFile = fromFile || '<initial file>';
  521. // importer is bound to a module's (or global) context -- we can override this
  522. // by using opts.exportInto
  523. var exportInto = opts.exportInto || boundContext || ENV.global;
  524. // parse the import request(s)
  525. var imports = resolveImportRequest(exportInto, request, opts),
  526. numImports = imports.length,
  527. retVal = numImports > 1 ? {} : null;
  528. // import each requested item
  529. for(var i = 0; i < numImports; ++i) {
  530. var item = imports[i],
  531. modulePath = item.from,
  532. modules = jsio.__modules;
  533. try {
  534. var moduleDef = loadModule(fromDir, fromFile, modulePath, opts);
  535. if (moduleDef === false) { return false; }
  536. } catch(e) {
  537. if (!e.jsioLogged) {
  538. ENV.log('\nError loading module:\n\trequested:', modulePath, '\n\tfrom:', fromDir + fromFile, '\n\tfull request:', request, '\n');
  539. e.jsioLogged = true;
  540. }
  541. throw e;
  542. }
  543. importStack.push(importStack.length + ' : ' + moduleDef.friendlyPath + ' (' + moduleDef.path + ')');
  544. // eval any packages that we don't know about already
  545. var path = moduleDef.path;
  546. if(!(path in modules)) {
  547. var newContext = makeContext(opts.context, modulePath, moduleDef, item.dontAddBase);
  548. modules[path] = newContext.exports;
  549. if(item.dontUseExports) {
  550. var src = [';(function(){'], k = 1;
  551. for (var j in item['import']) {
  552. newContext.exports[j] = undefined;
  553. src[k++] = 'if(typeof '+j+'!="undefined"&&exports.'+j+'==undefined)exports.'+j+'='+j+';';
  554. }
  555. src[k] = '})();';
  556. moduleDef.src += src.join('');
  557. }
  558. execModuleDef(newContext, moduleDef);
  559. modules[path] = newContext.exports;
  560. }
  561. importStack.pop();
  562. var module = modules[path];
  563. // return the module if we're only importing one module
  564. if (numImports == 1) { retVal = module; }
  565. if (!opts.dontExport) {
  566. // add the module to the current context
  567. if (item.as) {
  568. // remove trailing/leading dots
  569. var as = item.as.match(/^\.*(.*?)\.*$/)[1],
  570. segments = as.split('.'),
  571. kMax = segments.length - 1,
  572. c = exportInto;
  573. // build the object in the context
  574. for(var k = 0; k < kMax; ++k) {
  575. var segment = segments[k];
  576. if (!segment) continue;
  577. if (!c[segment]) { c[segment] = {}; }
  578. c = c[segment];
  579. }
  580. c[segments[kMax]] = module;
  581. // there can be multiple module imports with this syntax (import foo, bar)
  582. if (numImports > 1) {
  583. retVal[as] = module;
  584. }
  585. } else if(item['import']) {
  586. // there can only be one module import with this syntax
  587. // (from foo import bar), so retVal will already be set here
  588. if(item['import']['*']) {
  589. for(var k in modules[path]) { exportInto[k] = module[k]; }
  590. } else {
  591. try {
  592. for(var k in item['import']) { exportInto[item['import'][k]] = module[k]; }
  593. } catch(e) {
  594. ENV.log('module: ', modules);
  595. throw e;
  596. }
  597. }
  598. }
  599. }
  600. }
  601. return retVal;
  602. }
  603. // DEFINE SYNTAX FOR JSIO('cmd')
  604. // from myPackage import myFunc
  605. // external myPackage import myFunc
  606. jsio.addCmd(function(context, request, opts, imports) {
  607. var match = request.match(/^\s*(from|external)\s+([\w.$]+)\s+(import|grab)\s+(.*)$/);
  608. if(match) {
  609. imports.push({
  610. from: match[2],
  611. dontAddBase: match[1] == 'external',
  612. dontUseExports: match[3] == 'grab' || match[1] == 'external',
  613. 'import': {}
  614. });
  615. match[4].replace(/\s*([\w.$*]+)(?:\s+as\s+([\w.$]+))?/g, function(_, item, as) {
  616. imports[0]['import'][item] = as || item;
  617. });
  618. return true;
  619. }
  620. });
  621. // import myPackage
  622. jsio.addCmd(function(context, request, opts, imports) {
  623. var match = request.match(/^\s*import\s+(.*)$/);
  624. if (match) {
  625. match[1].replace(/\s*([\w.$]+)(?:\s+as\s+([\w.$]+))?,?/g, function(_, fullPath, as) {
  626. imports.push(
  627. as ? {
  628. from: fullPath,
  629. as: as
  630. } : {
  631. from: fullPath,
  632. as: fullPath
  633. });
  634. });
  635. return true;
  636. }
  637. });
  638. // CommonJS syntax
  639. jsio.addCmd(function(context, request, opts, imports) {
  640. // ./../b -> ..b
  641. // ../../b -> ...b
  642. // ../b -> ..b
  643. // ./b -> .b
  644. var match = request.match(/^\s*[\w.0-9$\/]+\s*$/);
  645. if (match) {
  646. var req = util.resolveRelativePath(match[0]),
  647. isRelative = req.charAt(0) == '.';
  648. req = req
  649. // .replace(/^\//, '') // remove any leading slash
  650. .replace(/\.\.\//g, '.') // replace relative path indicators with dots
  651. .replace(/\.\//g, '')
  652. .replace(/\//g, '.'); // any remaining slashes are path separators
  653. imports[0] = { from: (isRelative ? '.' : '') + req };
  654. return true;
  655. }
  656. });
  657. jsio.install = function(){
  658. jsio('from base import *');
  659. GLOBAL['logger'] = logging.get('jsiocore');
  660. };
  661. jsio.clone = function() {
  662. var copy = jsio.__init__(jsio);
  663. if (ENV.name == 'browser') { window.jsio = jsio; }
  664. return copy;
  665. }
  666. return jsio;
  667. }
  668. jsio = init();
  669. })();