You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

166 lines
5.2 KiB

# -*- coding: utf-8 -*-
"""
Functionality for creating JS modules of various formats, including AMD and UMD.
"""
from __future__ import print_function, absolute_import, with_statement, unicode_literals, division
import re
# Immediately Invoked Function Expression (IIFE)
HIDDEN = """
(function () {
"use strict";
{code}
})();
""".lstrip()
SIMPLE = """
(function (root, factory) {
root.{save_name} = factory();
}(this, function () {
"use strict";
{code}
return {exports};
}));
""".lstrip()
AMD = """
define("{name}", [{dep_strings}], function ({dep_names}) {
"use strict";
{code}
return {exports};
});
""".lstrip()
AMD_FLEXX = "flexx." + AMD
# https://github.com/umdjs/umd/blob/master/returnExports.js
UMD = """
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define("{name}", [{dep_strings}], factory);
} else if (typeof exports !== 'undefined') {
// Node or CommonJS
module.exports = factory({dep_requires});
if (typeof window === 'undefined') {
root.{save_name} = module.exports; // also create global module in Node
}
} else {
// Browser globals (root is window)
root.{save_name} = factory({dep_fullnames});
}
}(this, function ({dep_names}) {
"use strict";
{code}
return {exports};
}));
""".lstrip()
def isidentifier(s):
# http://stackoverflow.com/questions/2544972/
if not isinstance(s, basestring):
return False
return re.match(r'^\w+$', s, re.UNICODE) and re.match(r'^[0-9]', s) is None
def create_js_module(name, code, imports, exports, type='umd'):
""" Wrap the given code in an AMD module.
Note that "use strict" is added to the top of the module body. PScript
does not deal with license strings; the caller should do that.
Parameters:
name (str): the name of the module.
code (str): the JS code to wrap.
imports (list): the imports for this module, as string names of the
dependencies. Optionally, 'as' can be used to make a dependency
available under a specific name (e.g. 'foo.js as foo').
exports (str, list): the result of this module (i.e. what other modules
get when they import this module. Can be a JS expression or a list
of names to export.
type (str): the type of module to export, valid values are
'hidden', 'simple' (save module on root), 'amd' , 'amd-flexx' and
'umd' (case insensitive). Default 'umd'.
"""
# Check input args
if not isinstance(name, basestring) or not name:
raise ValueError('create_module() name arg must be a (nonempty) string.')
if not isinstance(code, basestring):
raise ValueError('create_module() code arg must be a string.')
if not isinstance(imports, (tuple, list)):
raise ValueError('create_module() imports arg must be a string.')
if not isinstance(exports, (basestring, tuple, list)):
raise ValueError('create_module() exports arg must be a string or list.')
# Process imports
deps, dep_names = [], []
for imp in imports:
if not isinstance(imp, basestring):
raise ValueError('Elements in create_module() imports must be str.')
if ' as ' in imp:
dep, dep_name = imp.split(' as ', 1)
else:
dep = dep_name = imp
if '.' in dep_name:
raise ValueError('Import %r has dots, have you used "as"?' % dep_name)
deps.append(dep)
dep_names.append(dep_name)
# Process exports
if isinstance(exports, basestring):
return_val = exports
else: # list
for exp in exports:
if not isinstance(exp, basestring):
raise ValueError('Elements in create_module() exports must be str.')
return_val = ', '.join(['%s: %s' % (exp, exp) for exp in exports])
return_val = '{' + return_val + '}'
# Process type -> select template
types = {'hidden': HIDDEN, 'simple': SIMPLE,
'amd': AMD, 'umd': UMD, 'amd-flexx': AMD_FLEXX}
if not isinstance(type, basestring):
raise ValueError('create_js_module() type must be str.')
if type.lower() not in types:
raise ValueError('create_js_module() got invalid type %r' % type)
template = types[type.lower()]
# Derived information needed to populate the module templates
save_name = lambda n: n.split('/')[-1].split('.')[0].replace('-', '_')
dep_strings = ['"%s"' % dep for dep in deps]
dep_fullnames = ['root.' + save_name(dep) for dep in deps]
dep_requires = ['require("%s")' % dep for dep in deps]
# Fill in the template
for key, val in [('{name}', name),
('{save_name}', save_name(name)),
('{exports}', return_val),
('{dep_names}', ', '.join(dep_names)),
('{dep_strings}', ', '.join(dep_strings)),
('{dep_fullnames}', ', '.join(dep_fullnames)),
('{dep_requires}', ', '.join(dep_requires)),
('{code}', code), # last!
]:
template = template.replace(key, val)
return template