linkjs.py 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. #!/usr/bin/python
  2. from optparse import OptionParser
  3. import os
  4. import re
  5. import sys
  6. def extract_require(s):
  7. m = re.search('(//)?(.*)require\\("(.*)"\\)', s)
  8. if not m or m.group(1):
  9. return None
  10. prefix = m.group(2)
  11. if prefix and len(prefix):
  12. prefix = prefix[-1]
  13. if prefix.isalpha() or prefix.isdigit() or prefix == '_':
  14. return None
  15. return m.group(3)
  16. def resolve_path(file, dirs):
  17. if os.path.isabs(file):
  18. return file
  19. for d in dirs:
  20. result = os.path.join(d, file)
  21. if os.path.exists(result):
  22. return result
  23. raise Exception('cannot find "%s" in "%s"' % (file, dirs))
  24. def process(path, out, resolved, resolving, dirs):
  25. module_name = os.path.splitext(os.path.basename(path))[0]
  26. if module_name in resolving:
  27. raise Exception('cyclic import detected: "%s"' % module_name)
  28. result = 'imports["%s"] = {};\n' % path
  29. result += '(function module$%s(exports){\n' % module_name
  30. src_path = resolve_path(path, dirs)
  31. with open(src_path) as f:
  32. for l in f:
  33. req = extract_require(l)
  34. if req and not req in resolved:
  35. try:
  36. process(req, out, resolved, resolving + [module_name], dirs)
  37. except Exception:
  38. print('while resolving "%s"...' % module_name)
  39. raise sys.exc_info()[1]
  40. result += l
  41. result += '\n})(imports["%s"]);\n' % path
  42. out.write(result)
  43. resolved += [path]
  44. def encode_to_js_string(s):
  45. escape = [('\n', '\\n'), ('\r', '\\r'), ('"', '\\"')]
  46. for e in escape:
  47. s = s.replace(e[0], e[1])
  48. return '"%s"' % s
  49. def link(input_paths, output_path, dirs, version = None):
  50. with open(output_path, "w") as out:
  51. prolog = ""
  52. if not version is None:
  53. prolog += 'var buildVersion = %s;\n' % encode_to_js_string(version)
  54. prolog += "var GLOBAL = this;\n"
  55. prolog += "var imports = {};\n"
  56. prolog += 'function require(module){return imports[module];}\n'
  57. out.write(prolog)
  58. resolved = []
  59. resolving = []
  60. for input_path in input_paths:
  61. process(input_path, out, resolved, resolving, dirs)
  62. def parse_args(args):
  63. parser = OptionParser('Usage: linkjs.py [options] <input js> <output js>')
  64. parser.add_option('-I', '--include', action='append', metavar='<directory>',
  65. default=[],
  66. help='additional search directory')
  67. opts, args = parser.parse_args(args)
  68. if len(args) != 2:
  69. parser.print_help()
  70. sys.exit(-1)
  71. return args[0], args[1], opts.include
  72. if __name__ == '__main__':
  73. src, dst, include = parse_args(sys.argv[1:])
  74. link(src, dst, include + ['.'])