├── tests ├── files │ └── colors.less ├── test.js ├── diffreport.py ├── test_jsinterpreter.py ├── test_reactjs.py ├── test_evaljs.py ├── run_with_env.cmd ├── test_transpilers.py └── test_webassets_filter.py ├── setup.cfg ├── MANIFEST.in ├── dukpy ├── jsmodules │ ├── less │ │ ├── less │ │ │ ├── data │ │ │ │ ├── index.js │ │ │ │ ├── unit-conversions.js │ │ │ │ └── colors.js │ │ │ ├── tree │ │ │ │ ├── unicode-descriptor.js │ │ │ │ ├── paren.js │ │ │ │ ├── keyword.js │ │ │ │ ├── ruleset-call.js │ │ │ │ ├── negative.js │ │ │ │ ├── alpha.js │ │ │ │ ├── combinator.js │ │ │ │ ├── assignment.js │ │ │ │ ├── comment.js │ │ │ │ ├── detached-ruleset.js │ │ │ │ ├── attribute.js │ │ │ │ ├── javascript.js │ │ │ │ ├── value.js │ │ │ │ ├── anonymous.js │ │ │ │ ├── condition.js │ │ │ │ ├── debug-info.js │ │ │ │ ├── index.js │ │ │ │ ├── operation.js │ │ │ │ ├── variable.js │ │ │ │ ├── url.js │ │ │ │ ├── expression.js │ │ │ │ ├── js-eval-node.js │ │ │ │ ├── extend.js │ │ │ │ ├── element.js │ │ │ │ ├── quoted.js │ │ │ │ ├── call.js │ │ │ │ ├── rule.js │ │ │ │ ├── selector.js │ │ │ │ ├── node.js │ │ │ │ ├── unit.js │ │ │ │ └── directive.js │ │ │ ├── visitors │ │ │ │ ├── index.js │ │ │ │ ├── set-tree-visibility-visitor.js │ │ │ │ ├── import-sequencer.js │ │ │ │ ├── join-selector-visitor.js │ │ │ │ └── visitor.js │ │ │ ├── functions │ │ │ │ ├── math-helper.js │ │ │ │ ├── index.js │ │ │ │ ├── default.js │ │ │ │ ├── math.js │ │ │ │ ├── function-registry.js │ │ │ │ ├── function-caller.js │ │ │ │ ├── string.js │ │ │ │ ├── color-blending.js │ │ │ │ ├── types.js │ │ │ │ ├── data-uri.js │ │ │ │ ├── number.js │ │ │ │ └── svg.js │ │ │ ├── utils.js │ │ │ ├── environment │ │ │ │ ├── environment-api.js │ │ │ │ ├── environment.js │ │ │ │ ├── file-manager-api.js │ │ │ │ └── abstract-file-manager.js │ │ │ ├── logger.js │ │ │ ├── plugins │ │ │ │ └── function-importer.js │ │ │ ├── less-error.js │ │ │ ├── render.js │ │ │ ├── index.js │ │ │ ├── parse-tree.js │ │ │ ├── transform-tree.js │ │ │ ├── parse.js │ │ │ ├── source-map-builder.js │ │ │ ├── plugin-manager.js │ │ │ └── contexts.js │ │ └── less-node │ │ │ ├── fs.js │ │ │ ├── environment.js │ │ │ ├── image-size.js │ │ │ ├── url-file-manager.js │ │ │ ├── index.js │ │ │ ├── plugin-loader.js │ │ │ └── file-manager.js │ └── react │ │ └── react-dom-server.js ├── webassets │ ├── __init__.py │ ├── typescriptfilter.py │ ├── lessfilter.py │ ├── jsxfilter.py │ └── babelfilter.py ├── __init__.py ├── coffee.py ├── tsc.py ├── run.py ├── lessc.py ├── babel.py ├── jscore │ ├── fs.js │ └── path.js ├── nodelike.py ├── module_loader.py ├── install.py └── evaljs.py ├── .gitattributes ├── src ├── _support.h ├── duktape │ ├── duk_module_duktape.h │ ├── duk_v1_compat.h │ └── duk_v1_compat.c ├── _support.c └── capsulethunk.h ├── .gitignore ├── LICENSE ├── .github └── workflows │ ├── run-tests.yml │ └── build-wheels.yml └── setup.py /tests/files/colors.less: -------------------------------------------------------------------------------- 1 | @green: #7bab2e; -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | license_file = LICENSE 3 | -------------------------------------------------------------------------------- /tests/test.js: -------------------------------------------------------------------------------- 1 | if (i === undefined) 2 | var i = 5; 3 | 4 | function sum() { 5 | i += 3; 6 | return i; 7 | } 8 | 9 | sum(); -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include src *.h 2 | recursive-include dukpy/jscore *.js 3 | recursive-include dukpy/jsmodules *.js 4 | include LICENSE 5 | -------------------------------------------------------------------------------- /dukpy/jsmodules/less/less/data/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | colors: require("./colors"), 3 | unitConversions: require("./unit-conversions") 4 | }; 5 | -------------------------------------------------------------------------------- /dukpy/webassets/__init__.py: -------------------------------------------------------------------------------- 1 | from .babelfilter import BabelJS 2 | from .typescriptfilter import TypeScript 3 | from .lessfilter import CompileLess 4 | from .jsxfilter import BabelJSX -------------------------------------------------------------------------------- /dukpy/jsmodules/less/less-node/fs.js: -------------------------------------------------------------------------------- 1 | var fs; 2 | try 3 | { 4 | fs = require("graceful-fs"); 5 | } 6 | catch(e) 7 | { 8 | fs = require("fs"); 9 | } 10 | module.exports = fs; 11 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | dukpy/jsmodules/* linguist-vendored 2 | dukpy/jsmodules/**/* linguist-vendored 3 | dukpy/jsmodules/**/**/* linguist-vendored 4 | src/duktape/* linguist-vendored 5 | *.py linguist-detectable linguist-language=python 6 | tests/*.py linguist-detectable linguist-language=python 7 | -------------------------------------------------------------------------------- /dukpy/__init__.py: -------------------------------------------------------------------------------- 1 | from .evaljs import evaljs, JSInterpreter 2 | from ._dukpy import JSRuntimeError 3 | from .install import install_jspackage 4 | 5 | from .coffee import coffee_compile 6 | from .babel import babel_compile, jsx_compile 7 | from .tsc import typescript_compile 8 | from .lessc import less_compile -------------------------------------------------------------------------------- /dukpy/jsmodules/less/less/tree/unicode-descriptor.js: -------------------------------------------------------------------------------- 1 | var Node = require("./node"); 2 | 3 | var UnicodeDescriptor = function (value) { 4 | this.value = value; 5 | }; 6 | UnicodeDescriptor.prototype = new Node(); 7 | UnicodeDescriptor.prototype.type = "UnicodeDescriptor"; 8 | 9 | module.exports = UnicodeDescriptor; 10 | -------------------------------------------------------------------------------- /dukpy/jsmodules/less/less/visitors/index.js: -------------------------------------------------------------------------------- 1 | var visitors = { 2 | Visitor: require("./visitor"), 3 | ImportVisitor: require('./import-visitor'), 4 | MarkVisibleSelectorsVisitor: require("./set-tree-visibility-visitor"), 5 | ExtendVisitor: require('./extend-visitor'), 6 | JoinSelectorVisitor: require('./join-selector-visitor'), 7 | ToCSSVisitor: require('./to-css-visitor') 8 | }; 9 | 10 | module.exports = visitors; 11 | -------------------------------------------------------------------------------- /tests/diffreport.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import difflib 3 | 4 | 5 | def report_diff(expected, ans): 6 | sqm = difflib.SequenceMatcher() 7 | sqm.set_seq1(ans) 8 | sqm.set_seq2(expected) 9 | 10 | out = ['DIFFERENCE : RESULT -> EXPECTED'] 11 | for action, sq1s, sq1e, sq2s, sq2e in sqm.get_opcodes(): 12 | out.append(action + ' : ' + repr(ans[sq1s:sq1e]) + ' -> ' + repr(expected[sq2s:sq2e])) 13 | return '\n'.join(out) -------------------------------------------------------------------------------- /dukpy/webassets/typescriptfilter.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, print_function 3 | from webassets.filter import Filter 4 | 5 | import dukpy 6 | 7 | 8 | __all__ = ('TypeScript', ) 9 | 10 | 11 | class TypeScript(Filter): 12 | name = 'typescript' 13 | max_debug_level = None 14 | 15 | def input(self, _in, out, **kw): 16 | src = dukpy.typescript_compile(_in.read()) 17 | out.write(src) 18 | -------------------------------------------------------------------------------- /dukpy/jsmodules/less/less/tree/paren.js: -------------------------------------------------------------------------------- 1 | var Node = require("./node"); 2 | 3 | var Paren = function (node) { 4 | this.value = node; 5 | }; 6 | Paren.prototype = new Node(); 7 | Paren.prototype.type = "Paren"; 8 | Paren.prototype.genCSS = function (context, output) { 9 | output.add('('); 10 | this.value.genCSS(context, output); 11 | output.add(')'); 12 | }; 13 | Paren.prototype.eval = function (context) { 14 | return new Paren(this.value.eval(context)); 15 | }; 16 | module.exports = Paren; 17 | -------------------------------------------------------------------------------- /dukpy/jsmodules/less/less/data/unit-conversions.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | length: { 3 | 'm': 1, 4 | 'cm': 0.01, 5 | 'mm': 0.001, 6 | 'in': 0.0254, 7 | 'px': 0.0254 / 96, 8 | 'pt': 0.0254 / 72, 9 | 'pc': 0.0254 / 72 * 12 10 | }, 11 | duration: { 12 | 's': 1, 13 | 'ms': 0.001 14 | }, 15 | angle: { 16 | 'rad': 1 / (2 * Math.PI), 17 | 'deg': 1 / 360, 18 | 'grad': 1 / 400, 19 | 'turn': 1 20 | } 21 | }; -------------------------------------------------------------------------------- /dukpy/coffee.py: -------------------------------------------------------------------------------- 1 | import os 2 | from .evaljs import evaljs 3 | 4 | COFFEE_COMPILER = os.path.join(os.path.dirname(__file__), 'jsmodules', 'coffeescript.js') 5 | 6 | 7 | def coffee_compile(source): 8 | """Compiles the given ``source`` from CoffeeScript to JavaScript""" 9 | with open(COFFEE_COMPILER, 'rb') as coffeescript_js: 10 | return evaljs( 11 | (coffeescript_js.read().decode('utf-8'), 12 | 'CoffeeScript.compile(dukpy.coffeecode)'), 13 | coffeecode=source 14 | ) 15 | -------------------------------------------------------------------------------- /dukpy/jsmodules/less/less/functions/math-helper.js: -------------------------------------------------------------------------------- 1 | var Dimension = require("../tree/dimension"); 2 | 3 | var MathHelper = function() { 4 | }; 5 | MathHelper._math = function (fn, unit, n) { 6 | if (!(n instanceof Dimension)) { 7 | throw { type: "Argument", message: "argument must be a number" }; 8 | } 9 | if (unit == null) { 10 | unit = n.unit; 11 | } else { 12 | n = n.unify(); 13 | } 14 | return new Dimension(fn(parseFloat(n.value)), unit); 15 | }; 16 | module.exports = MathHelper; -------------------------------------------------------------------------------- /dukpy/jsmodules/less/less/tree/keyword.js: -------------------------------------------------------------------------------- 1 | var Node = require("./node"); 2 | 3 | var Keyword = function (value) { this.value = value; }; 4 | Keyword.prototype = new Node(); 5 | Keyword.prototype.type = "Keyword"; 6 | Keyword.prototype.genCSS = function (context, output) { 7 | if (this.value === '%') { throw { type: "Syntax", message: "Invalid % without number" }; } 8 | output.add(this.value); 9 | }; 10 | 11 | Keyword.True = new Keyword('true'); 12 | Keyword.False = new Keyword('false'); 13 | 14 | module.exports = Keyword; 15 | -------------------------------------------------------------------------------- /dukpy/jsmodules/less/less-node/environment.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | encodeBase64: function encodeBase64(str) { 3 | return new Buffer(str).toString('base64'); 4 | }, 5 | mimeLookup: function (filename) { 6 | return require('mime').lookup(filename); 7 | }, 8 | charsetLookup: function (mime) { 9 | return require('mime').charsets.lookup(mime); 10 | }, 11 | getSourceMapGenerator: function getSourceMapGenerator() { 12 | return require("source-map").SourceMapGenerator; 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /dukpy/jsmodules/less/less/tree/ruleset-call.js: -------------------------------------------------------------------------------- 1 | var Node = require("./node"), 2 | Variable = require("./variable"); 3 | 4 | var RulesetCall = function (variable) { 5 | this.variable = variable; 6 | this.allowRoot = true; 7 | }; 8 | RulesetCall.prototype = new Node(); 9 | RulesetCall.prototype.type = "RulesetCall"; 10 | RulesetCall.prototype.eval = function (context) { 11 | var detachedRuleset = new Variable(this.variable).eval(context); 12 | return detachedRuleset.callEval(context); 13 | }; 14 | module.exports = RulesetCall; 15 | -------------------------------------------------------------------------------- /dukpy/jsmodules/less/less/utils.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | getLocation: function(index, inputStream) { 3 | var n = index + 1, 4 | line = null, 5 | column = -1; 6 | 7 | while (--n >= 0 && inputStream.charAt(n) !== '\n') { 8 | column++; 9 | } 10 | 11 | if (typeof index === 'number') { 12 | line = (inputStream.slice(0, index).match(/\n/g) || "").length; 13 | } 14 | 15 | return { 16 | line: line, 17 | column: column 18 | }; 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /src/_support.h: -------------------------------------------------------------------------------- 1 | #ifndef __DUKPY_SUPPORT_H__ 2 | #define __DUKPY_SUPPORT_H__ 3 | 4 | 5 | duk_ret_t stack_json_encode(duk_context *ctx, void *ptr); 6 | void duktape_fatal_error_handler(void *ctx, const char *msg); 7 | duk_context *get_context_from_capsule(PyObject* pyctx); 8 | PyObject *make_capsule_for_context(duk_context *ctx); 9 | int call_py_function(duk_context *ctx); 10 | int require_set_module_id(duk_context *ctx); 11 | 12 | #if PY_MAJOR_VERSION >= 3 13 | #define CONDITIONAL_PY3(three, two) (three) 14 | #else 15 | #define CONDITIONAL_PY3(three, two) (two) 16 | #endif 17 | 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /dukpy/jsmodules/less/less/functions/index.js: -------------------------------------------------------------------------------- 1 | module.exports = function(environment) { 2 | var functions = { 3 | functionRegistry: require("./function-registry"), 4 | functionCaller: require("./function-caller") 5 | }; 6 | 7 | //register functions 8 | require("./default"); 9 | require("./color"); 10 | require("./color-blending"); 11 | require("./data-uri")(environment); 12 | require("./math"); 13 | require("./number"); 14 | require("./string"); 15 | require("./svg")(environment); 16 | require("./types"); 17 | 18 | return functions; 19 | }; 20 | -------------------------------------------------------------------------------- /dukpy/tsc.py: -------------------------------------------------------------------------------- 1 | import os 2 | from .evaljs import evaljs 3 | 4 | TS_COMPILER = os.path.join(os.path.dirname(__file__), 'jsmodules', 'typescriptServices.js') 5 | TSC_OPTIONS = '{ module: ts.ModuleKind.System, target: ts.ScriptTarget.ES5, newLine: 1 }' 6 | 7 | 8 | def typescript_compile(source): 9 | """Compiles the given ``source`` from TypeScript to ES5 using TypescriptServices.js""" 10 | with open(TS_COMPILER, 'r') as tsservices_js: 11 | return evaljs( 12 | (tsservices_js.read(), 13 | 'ts.transpile(dukpy.tscode, {options});'.format(options=TSC_OPTIONS)), 14 | tscode=source 15 | ) 16 | -------------------------------------------------------------------------------- /src/duktape/duk_module_duktape.h: -------------------------------------------------------------------------------- 1 | #if !defined(DUK_MODULE_DUKTAPE_H_INCLUDED) 2 | #define DUK_MODULE_DUKTAPE_H_INCLUDED 3 | 4 | #include "duktape.h" 5 | 6 | #if defined(__cplusplus) 7 | extern "C" { 8 | #endif 9 | 10 | /* Maximum length of CommonJS module identifier to resolve. Length includes 11 | * both current module ID, requested (possibly relative) module ID, and a 12 | * slash in between. 13 | */ 14 | #define DUK_COMMONJS_MODULE_ID_LIMIT 256 15 | 16 | extern void duk_module_duktape_init(duk_context *ctx); 17 | 18 | #if defined(__cplusplus) 19 | } 20 | #endif /* end 'extern "C"' wrapper */ 21 | 22 | #endif /* DUK_MODULE_DUKTAPE_H_INCLUDED */ 23 | -------------------------------------------------------------------------------- /dukpy/jsmodules/less/less/environment/environment-api.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | /** 3 | * Converts a string to a base 64 string 4 | * @param str 5 | */ 6 | encodeBase64: function(str) { 7 | }, 8 | /** 9 | * Lookup the mime-type of a filename 10 | * @param filename 11 | */ 12 | mimeLookup: function (filename) { 13 | }, 14 | /** 15 | * Look up the charset of a mime type 16 | * @param mime 17 | */ 18 | charsetLookup: function (mime) { 19 | }, 20 | /** 21 | * Gets a source map generator 22 | */ 23 | getSourceMapGenerator: function getSourceMapGenerator() { 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /dukpy/run.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import argparse 3 | import sys 4 | from .nodelike import NodeLikeInterpreter 5 | 6 | 7 | def main(): 8 | parser = argparse.ArgumentParser(description='Run a javascript script') 9 | parser.add_argument('filename', help='path of the script to run') 10 | args = parser.parse_args(sys.argv[1:]) 11 | 12 | with open(args.filename) as f: 13 | sourcecode = f.read() 14 | 15 | if sourcecode.startswith('#!'): 16 | # Remove shebang 17 | _, sourcecode = sourcecode.split('\n', 1) 18 | sourcecode = '\n' + sourcecode 19 | 20 | runner = NodeLikeInterpreter() 21 | runner.evaljs(sourcecode) -------------------------------------------------------------------------------- /dukpy/jsmodules/less/less/tree/negative.js: -------------------------------------------------------------------------------- 1 | var Node = require("./node"), 2 | Operation = require("./operation"), 3 | Dimension = require("./dimension"); 4 | 5 | var Negative = function (node) { 6 | this.value = node; 7 | }; 8 | Negative.prototype = new Node(); 9 | Negative.prototype.type = "Negative"; 10 | Negative.prototype.genCSS = function (context, output) { 11 | output.add('-'); 12 | this.value.genCSS(context, output); 13 | }; 14 | Negative.prototype.eval = function (context) { 15 | if (context.isMathOn()) { 16 | return (new Operation('*', [new Dimension(-1), this.value])).eval(context); 17 | } 18 | return new Negative(this.value.eval(context)); 19 | }; 20 | module.exports = Negative; 21 | -------------------------------------------------------------------------------- /dukpy/jsmodules/less/less/functions/default.js: -------------------------------------------------------------------------------- 1 | var Keyword = require("../tree/keyword"), 2 | functionRegistry = require("./function-registry"); 3 | 4 | var defaultFunc = { 5 | eval: function () { 6 | var v = this.value_, e = this.error_; 7 | if (e) { 8 | throw e; 9 | } 10 | if (v != null) { 11 | return v ? Keyword.True : Keyword.False; 12 | } 13 | }, 14 | value: function (v) { 15 | this.value_ = v; 16 | }, 17 | error: function (e) { 18 | this.error_ = e; 19 | }, 20 | reset: function () { 21 | this.value_ = this.error_ = null; 22 | } 23 | }; 24 | 25 | functionRegistry.add("default", defaultFunc.eval.bind(defaultFunc)); 26 | 27 | module.exports = defaultFunc; 28 | -------------------------------------------------------------------------------- /dukpy/jsmodules/less/less/tree/alpha.js: -------------------------------------------------------------------------------- 1 | var Node = require("./node"); 2 | 3 | var Alpha = function (val) { 4 | this.value = val; 5 | }; 6 | Alpha.prototype = new Node(); 7 | Alpha.prototype.type = "Alpha"; 8 | 9 | Alpha.prototype.accept = function (visitor) { 10 | this.value = visitor.visit(this.value); 11 | }; 12 | Alpha.prototype.eval = function (context) { 13 | if (this.value.eval) { return new Alpha(this.value.eval(context)); } 14 | return this; 15 | }; 16 | Alpha.prototype.genCSS = function (context, output) { 17 | output.add("alpha(opacity="); 18 | 19 | if (this.value.genCSS) { 20 | this.value.genCSS(context, output); 21 | } else { 22 | output.add(this.value); 23 | } 24 | 25 | output.add(")"); 26 | }; 27 | 28 | module.exports = Alpha; 29 | -------------------------------------------------------------------------------- /dukpy/jsmodules/less/less/tree/combinator.js: -------------------------------------------------------------------------------- 1 | var Node = require("./node"); 2 | 3 | var Combinator = function (value) { 4 | if (value === ' ') { 5 | this.value = ' '; 6 | this.emptyOrWhitespace = true; 7 | } else { 8 | this.value = value ? value.trim() : ""; 9 | this.emptyOrWhitespace = this.value === ""; 10 | } 11 | }; 12 | Combinator.prototype = new Node(); 13 | Combinator.prototype.type = "Combinator"; 14 | var _noSpaceCombinators = { 15 | '': true, 16 | ' ': true, 17 | '|': true 18 | }; 19 | Combinator.prototype.genCSS = function (context, output) { 20 | var spaceOrEmpty = (context.compress || _noSpaceCombinators[this.value]) ? '' : ' '; 21 | output.add(spaceOrEmpty + this.value + spaceOrEmpty); 22 | }; 23 | module.exports = Combinator; 24 | -------------------------------------------------------------------------------- /dukpy/webassets/lessfilter.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, print_function 3 | 4 | import os 5 | 6 | from webassets.filter import Filter, option 7 | 8 | import dukpy 9 | 10 | 11 | __all__ = ('CompileLess', ) 12 | 13 | 14 | class CompileLess(Filter): 15 | name = 'lessc' 16 | max_debug_level = None 17 | options = { 18 | 'less_includes': option('LIBSASS_INCLUDES', type=list) 19 | } 20 | 21 | def input(self, _in, out, **kw): 22 | options = {'paths': []} 23 | if self.less_includes: 24 | options['paths'].extend(self.less_includes) 25 | if 'source_path' in kw: 26 | options['paths'].append(os.path.dirname(kw['source_path'])) 27 | 28 | src = dukpy.less_compile(_in.read(), options=options) 29 | out.write(src) 30 | -------------------------------------------------------------------------------- /dukpy/webassets/jsxfilter.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, print_function 3 | 4 | import os 5 | 6 | from webassets.filter import Filter 7 | 8 | import dukpy 9 | 10 | 11 | __all__ = ('BabelJSX', ) 12 | 13 | 14 | class BabelJSX(Filter): 15 | name = 'babeljsx' 16 | max_debug_level = None 17 | options = { 18 | 'loader': 'BABEL_MODULES_LOADER' 19 | } 20 | 21 | def input(self, _in, out, **kw): 22 | options = {'filename': os.path.basename(kw['source_path'])} 23 | if self.loader == 'systemjs': 24 | options['plugins'] = ['transform-es2015-modules-systemjs'] 25 | elif self.loader == 'umd': 26 | options['plugins'] = ['transform-es2015-modules-umd'] 27 | src = dukpy.jsx_compile(_in.read(), **options) 28 | out.write(src) 29 | -------------------------------------------------------------------------------- /dukpy/webassets/babelfilter.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, print_function 3 | 4 | import os 5 | 6 | from webassets.filter import Filter 7 | 8 | import dukpy 9 | 10 | 11 | __all__ = ('BabelJS', ) 12 | 13 | 14 | class BabelJS(Filter): 15 | name = 'babeljs' 16 | max_debug_level = None 17 | options = { 18 | 'loader': 'BABEL_MODULES_LOADER' 19 | } 20 | 21 | def input(self, _in, out, **kw): 22 | options = {'filename': os.path.basename(kw['source_path'])} 23 | if self.loader == 'systemjs': 24 | options['plugins'] = ['transform-es2015-modules-systemjs'] 25 | elif self.loader == 'umd': 26 | options['plugins'] = ['transform-es2015-modules-umd'] 27 | src = dukpy.babel_compile(_in.read(), **options) 28 | out.write(src['code']) 29 | -------------------------------------------------------------------------------- /dukpy/jsmodules/less/less/tree/assignment.js: -------------------------------------------------------------------------------- 1 | var Node = require("./node"); 2 | 3 | var Assignment = function (key, val) { 4 | this.key = key; 5 | this.value = val; 6 | }; 7 | 8 | Assignment.prototype = new Node(); 9 | Assignment.prototype.type = "Assignment"; 10 | Assignment.prototype.accept = function (visitor) { 11 | this.value = visitor.visit(this.value); 12 | }; 13 | Assignment.prototype.eval = function (context) { 14 | if (this.value.eval) { 15 | return new Assignment(this.key, this.value.eval(context)); 16 | } 17 | return this; 18 | }; 19 | Assignment.prototype.genCSS = function (context, output) { 20 | output.add(this.key + '='); 21 | if (this.value.genCSS) { 22 | this.value.genCSS(context, output); 23 | } else { 24 | output.add(this.value); 25 | } 26 | }; 27 | module.exports = Assignment; 28 | -------------------------------------------------------------------------------- /dukpy/jsmodules/less/less/tree/comment.js: -------------------------------------------------------------------------------- 1 | var Node = require("./node"), 2 | getDebugInfo = require("./debug-info"); 3 | 4 | var Comment = function (value, isLineComment, index, currentFileInfo) { 5 | this.value = value; 6 | this.isLineComment = isLineComment; 7 | this.currentFileInfo = currentFileInfo; 8 | this.allowRoot = true; 9 | }; 10 | Comment.prototype = new Node(); 11 | Comment.prototype.type = "Comment"; 12 | Comment.prototype.genCSS = function (context, output) { 13 | if (this.debugInfo) { 14 | output.add(getDebugInfo(context, this), this.currentFileInfo, this.index); 15 | } 16 | output.add(this.value); 17 | }; 18 | Comment.prototype.isSilent = function(context) { 19 | var isCompressed = context.compress && this.value[2] !== "!"; 20 | return this.isLineComment || isCompressed; 21 | }; 22 | module.exports = Comment; 23 | -------------------------------------------------------------------------------- /dukpy/jsmodules/less/less/functions/math.js: -------------------------------------------------------------------------------- 1 | var functionRegistry = require("./function-registry"), 2 | mathHelper = require("./math-helper.js"); 3 | 4 | var mathFunctions = { 5 | // name, unit 6 | ceil: null, 7 | floor: null, 8 | sqrt: null, 9 | abs: null, 10 | tan: "", 11 | sin: "", 12 | cos: "", 13 | atan: "rad", 14 | asin: "rad", 15 | acos: "rad" 16 | }; 17 | 18 | for (var f in mathFunctions) { 19 | if (mathFunctions.hasOwnProperty(f)) { 20 | mathFunctions[f] = mathHelper._math.bind(null, Math[f], mathFunctions[f]); 21 | } 22 | } 23 | 24 | mathFunctions.round = function (n, f) { 25 | var fraction = typeof f === "undefined" ? 0 : f.value; 26 | return mathHelper._math(function(num) { return num.toFixed(fraction); }, null, n); 27 | }; 28 | 29 | functionRegistry.addMultiple(mathFunctions); 30 | -------------------------------------------------------------------------------- /dukpy/lessc.py: -------------------------------------------------------------------------------- 1 | from .nodelike import NodeLikeInterpreter 2 | 3 | 4 | def less_compile(source, options=None): 5 | """Compiles the given ``source`` from LESS to CSS""" 6 | options = options or {} 7 | res = NodeLikeInterpreter().evaljs( 8 | ('var result = null;' 9 | 'var less = require("less/less-node");', 10 | 'less.render(dukpy.lesscode, dukpy.lessoptions, function(error, output) {' 11 | ' result = {"error": error, "output": output};' 12 | '});' 13 | 'result;'), 14 | lesscode=source, 15 | lessoptions=options 16 | ) 17 | if not res: 18 | raise RuntimeError('Results or errors unavailable') 19 | 20 | if res.get('error'): 21 | raise LessCompilerError(res['error']['message']) 22 | 23 | return res['output']['css'] 24 | 25 | 26 | class LessCompilerError(Exception): 27 | pass 28 | 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | lib/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | 25 | # PyInstaller 26 | # Usually these files are written by a python script from a template 27 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 28 | *.manifest 29 | *.spec 30 | 31 | # Installer logs 32 | pip-log.txt 33 | pip-delete-this-directory.txt 34 | 35 | # Unit test / coverage reports 36 | htmlcov/ 37 | .tox/ 38 | .coverage 39 | .cache 40 | nosetests.xml 41 | coverage.xml 42 | 43 | # Translations 44 | *.mo 45 | *.pot 46 | 47 | # Django stuff: 48 | *.log 49 | 50 | # Sphinx documentation 51 | docs/_build/ 52 | 53 | # PyBuilder 54 | target/ 55 | -------------------------------------------------------------------------------- /dukpy/jsmodules/less/less/tree/detached-ruleset.js: -------------------------------------------------------------------------------- 1 | var Node = require("./node"), 2 | contexts = require("../contexts"); 3 | 4 | var DetachedRuleset = function (ruleset, frames) { 5 | this.ruleset = ruleset; 6 | this.frames = frames; 7 | }; 8 | DetachedRuleset.prototype = new Node(); 9 | DetachedRuleset.prototype.type = "DetachedRuleset"; 10 | DetachedRuleset.prototype.evalFirst = true; 11 | DetachedRuleset.prototype.accept = function (visitor) { 12 | this.ruleset = visitor.visit(this.ruleset); 13 | }; 14 | DetachedRuleset.prototype.eval = function (context) { 15 | var frames = this.frames || context.frames.slice(0); 16 | return new DetachedRuleset(this.ruleset, frames); 17 | }; 18 | DetachedRuleset.prototype.callEval = function (context) { 19 | return this.ruleset.eval(this.frames ? new contexts.Eval(context, this.frames.concat(context.frames)) : context); 20 | }; 21 | module.exports = DetachedRuleset; 22 | -------------------------------------------------------------------------------- /dukpy/babel.py: -------------------------------------------------------------------------------- 1 | import os 2 | from .evaljs import evaljs 3 | 4 | BABEL_COMPILER = os.path.join(os.path.dirname(__file__), 'jsmodules', 'babel-6.26.0.min.js') 5 | 6 | 7 | def babel_compile(source, **kwargs): 8 | """Compiles the given ``source`` from ES6 to ES5 using Babeljs""" 9 | presets = kwargs.get('presets') 10 | if not presets: 11 | kwargs['presets'] = ["es2015"] 12 | with open(BABEL_COMPILER, 'rb') as babel_js: 13 | return evaljs( 14 | (babel_js.read().decode('utf-8'), 15 | 'var bres, res;' 16 | 'bres = Babel.transform(dukpy.es6code, dukpy.babel_options);', 17 | 'res = {map: bres.map, code: bres.code};'), 18 | es6code=source, 19 | babel_options=kwargs 20 | ) 21 | 22 | 23 | def jsx_compile(source, **kwargs): 24 | kwargs['presets'] = ['es2015', 'react'] 25 | return babel_compile(source, **kwargs)['code'] 26 | -------------------------------------------------------------------------------- /dukpy/jsmodules/less/less/tree/attribute.js: -------------------------------------------------------------------------------- 1 | var Node = require("./node"); 2 | 3 | var Attribute = function (key, op, value) { 4 | this.key = key; 5 | this.op = op; 6 | this.value = value; 7 | }; 8 | Attribute.prototype = new Node(); 9 | Attribute.prototype.type = "Attribute"; 10 | Attribute.prototype.eval = function (context) { 11 | return new Attribute(this.key.eval ? this.key.eval(context) : this.key, 12 | this.op, (this.value && this.value.eval) ? this.value.eval(context) : this.value); 13 | }; 14 | Attribute.prototype.genCSS = function (context, output) { 15 | output.add(this.toCSS(context)); 16 | }; 17 | Attribute.prototype.toCSS = function (context) { 18 | var value = this.key.toCSS ? this.key.toCSS(context) : this.key; 19 | 20 | if (this.op) { 21 | value += this.op; 22 | value += (this.value.toCSS ? this.value.toCSS(context) : this.value); 23 | } 24 | 25 | return '[' + value + ']'; 26 | }; 27 | module.exports = Attribute; 28 | -------------------------------------------------------------------------------- /dukpy/jscore/fs.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.statSync = function(path) { 4 | /* Totally fake implementation only to make less.js happy */ 5 | if (call_python('file.exists', path)) 6 | return {}; 7 | else 8 | throw {'error': 'Unable to access file'}; 9 | }; 10 | 11 | exports.readFileSync = function(path, options) { 12 | /* Stripped down implementation only to make less.js happy */ 13 | if (!options) { 14 | options = { encoding: null, flag: 'r' }; 15 | } else if (typeof options === 'string') { 16 | options = { encoding: options, flag: 'r' }; 17 | } else if (typeof options !== 'object') { 18 | throwOptionsError(options); 19 | } 20 | 21 | var encoding = options.encoding; 22 | var data = call_python('file.read', path, encoding); 23 | return data 24 | }; 25 | 26 | function throwOptionsError(options) { 27 | throw new TypeError('Expected options to be either an object or a string, ' + 28 | 'but got ' + typeof options + ' instead'); 29 | } -------------------------------------------------------------------------------- /dukpy/jsmodules/less/less/functions/function-registry.js: -------------------------------------------------------------------------------- 1 | function makeRegistry( base ) { 2 | return { 3 | _data: {}, 4 | add: function(name, func) { 5 | // precautionary case conversion, as later querying of 6 | // the registry by function-caller uses lower case as well. 7 | name = name.toLowerCase(); 8 | 9 | if (this._data.hasOwnProperty(name)) { 10 | //TODO warn 11 | } 12 | this._data[name] = func; 13 | }, 14 | addMultiple: function(functions) { 15 | Object.keys(functions).forEach( 16 | function(name) { 17 | this.add(name, functions[name]); 18 | }.bind(this)); 19 | }, 20 | get: function(name) { 21 | return this._data[name] || ( base && base.get( name )); 22 | }, 23 | inherit : function() { 24 | return makeRegistry( this ); 25 | } 26 | }; 27 | } 28 | 29 | module.exports = makeRegistry( null ); -------------------------------------------------------------------------------- /dukpy/nodelike.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | from .evaljs import JSInterpreter 4 | 5 | 6 | class NodeLikeInterpreter(JSInterpreter): 7 | """A DukPy Interpreter that provides a minimal compatibility layer with NodeJS""" 8 | def __init__(self): 9 | super(NodeLikeInterpreter, self).__init__() 10 | self.loader.register_path(os.path.join(os.path.dirname(__file__), 'jscore')) 11 | self.export_function('file.exists', FS.exists) 12 | self.export_function('file.read', FS.read) 13 | 14 | 15 | class FS: 16 | """Provides oversimplified fs.js native functions.""" 17 | @classmethod 18 | def exists(cls, filepath): 19 | try: 20 | os.stat(filepath) 21 | return True 22 | except: 23 | return False 24 | 25 | @classmethod 26 | def read(cls, path, encoding): 27 | with open(path, 'rb') as f: 28 | data = f.read() 29 | 30 | if encoding is not None: 31 | return data.decode(encoding) 32 | else: 33 | return data 34 | -------------------------------------------------------------------------------- /dukpy/jsmodules/less/less/logger.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | error: function(msg) { 3 | this._fireEvent("error", msg); 4 | }, 5 | warn: function(msg) { 6 | this._fireEvent("warn", msg); 7 | }, 8 | info: function(msg) { 9 | this._fireEvent("info", msg); 10 | }, 11 | debug: function(msg) { 12 | this._fireEvent("debug", msg); 13 | }, 14 | addListener: function(listener) { 15 | this._listeners.push(listener); 16 | }, 17 | removeListener: function(listener) { 18 | for (var i = 0; i < this._listeners.length; i++) { 19 | if (this._listeners[i] === listener) { 20 | this._listeners.splice(i, 1); 21 | return; 22 | } 23 | } 24 | }, 25 | _fireEvent: function(type, msg) { 26 | for (var i = 0; i < this._listeners.length; i++) { 27 | var logFunction = this._listeners[i][type]; 28 | if (logFunction) { 29 | logFunction(msg); 30 | } 31 | } 32 | }, 33 | _listeners: [] 34 | }; 35 | -------------------------------------------------------------------------------- /dukpy/jsmodules/less/less/visitors/set-tree-visibility-visitor.js: -------------------------------------------------------------------------------- 1 | var SetTreeVisibilityVisitor = function(visible) { 2 | this.visible = visible; 3 | }; 4 | SetTreeVisibilityVisitor.prototype.run = function(root) { 5 | this.visit(root); 6 | }; 7 | SetTreeVisibilityVisitor.prototype.visitArray = function(nodes) { 8 | if (!nodes) { 9 | return nodes; 10 | } 11 | 12 | var cnt = nodes.length, i; 13 | for (i = 0; i < cnt; i++) { 14 | this.visit(nodes[i]); 15 | } 16 | return nodes; 17 | }; 18 | SetTreeVisibilityVisitor.prototype.visit = function(node) { 19 | if (!node) { 20 | return node; 21 | } 22 | if (node.constructor === Array) { 23 | return this.visitArray(node); 24 | } 25 | 26 | if (!node.blocksVisibility || node.blocksVisibility()) { 27 | return node; 28 | } 29 | if (this.visible) { 30 | node.ensureVisibility(); 31 | } else { 32 | node.ensureInvisibility(); 33 | } 34 | 35 | node.accept(this); 36 | return node; 37 | }; 38 | module.exports = SetTreeVisibilityVisitor; -------------------------------------------------------------------------------- /dukpy/jsmodules/less/less/tree/javascript.js: -------------------------------------------------------------------------------- 1 | var JsEvalNode = require("./js-eval-node"), 2 | Dimension = require("./dimension"), 3 | Quoted = require("./quoted"), 4 | Anonymous = require("./anonymous"); 5 | 6 | var JavaScript = function (string, escaped, index, currentFileInfo) { 7 | this.escaped = escaped; 8 | this.expression = string; 9 | this.index = index; 10 | this.currentFileInfo = currentFileInfo; 11 | }; 12 | JavaScript.prototype = new JsEvalNode(); 13 | JavaScript.prototype.type = "JavaScript"; 14 | JavaScript.prototype.eval = function(context) { 15 | var result = this.evaluateJavaScript(this.expression, context); 16 | 17 | if (typeof result === 'number') { 18 | return new Dimension(result); 19 | } else if (typeof result === 'string') { 20 | return new Quoted('"' + result + '"', result, this.escaped, this.index); 21 | } else if (Array.isArray(result)) { 22 | return new Anonymous(result.join(', ')); 23 | } else { 24 | return new Anonymous(result); 25 | } 26 | }; 27 | 28 | module.exports = JavaScript; 29 | -------------------------------------------------------------------------------- /dukpy/jsmodules/less/less/tree/value.js: -------------------------------------------------------------------------------- 1 | var Node = require("./node"); 2 | 3 | var Value = function (value) { 4 | this.value = value; 5 | if (!value) { 6 | throw new Error("Value requires an array argument"); 7 | } 8 | }; 9 | Value.prototype = new Node(); 10 | Value.prototype.type = "Value"; 11 | Value.prototype.accept = function (visitor) { 12 | if (this.value) { 13 | this.value = visitor.visitArray(this.value); 14 | } 15 | }; 16 | Value.prototype.eval = function (context) { 17 | if (this.value.length === 1) { 18 | return this.value[0].eval(context); 19 | } else { 20 | return new Value(this.value.map(function (v) { 21 | return v.eval(context); 22 | })); 23 | } 24 | }; 25 | Value.prototype.genCSS = function (context, output) { 26 | var i; 27 | for (i = 0; i < this.value.length; i++) { 28 | this.value[i].genCSS(context, output); 29 | if (i + 1 < this.value.length) { 30 | output.add((context && context.compress) ? ',' : ', '); 31 | } 32 | } 33 | }; 34 | module.exports = Value; 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Alessandro Molina 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 all 13 | 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 THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /dukpy/jsmodules/less/less/tree/anonymous.js: -------------------------------------------------------------------------------- 1 | var Node = require("./node"); 2 | 3 | var Anonymous = function (value, index, currentFileInfo, mapLines, rulesetLike, visibilityInfo) { 4 | this.value = value; 5 | this.index = index; 6 | this.mapLines = mapLines; 7 | this.currentFileInfo = currentFileInfo; 8 | this.rulesetLike = (typeof rulesetLike === 'undefined') ? false : rulesetLike; 9 | this.allowRoot = true; 10 | this.copyVisibilityInfo(visibilityInfo); 11 | }; 12 | Anonymous.prototype = new Node(); 13 | Anonymous.prototype.type = "Anonymous"; 14 | Anonymous.prototype.eval = function () { 15 | return new Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines, this.rulesetLike, this.visibilityInfo()); 16 | }; 17 | Anonymous.prototype.compare = function (other) { 18 | return other.toCSS && this.toCSS() === other.toCSS() ? 0 : undefined; 19 | }; 20 | Anonymous.prototype.isRulesetLike = function() { 21 | return this.rulesetLike; 22 | }; 23 | Anonymous.prototype.genCSS = function (context, output) { 24 | output.add(this.value, this.currentFileInfo, this.index, this.mapLines); 25 | }; 26 | module.exports = Anonymous; 27 | -------------------------------------------------------------------------------- /dukpy/jsmodules/less/less/plugins/function-importer.js: -------------------------------------------------------------------------------- 1 | var LessError = require('../less-error'), 2 | tree = require("../tree"); 3 | 4 | var FunctionImporter = module.exports = function FunctionImporter(context, fileInfo) { 5 | this.fileInfo = fileInfo; 6 | }; 7 | 8 | FunctionImporter.prototype.eval = function(contents, callback) { 9 | var loaded = {}, 10 | loader, 11 | registry; 12 | 13 | registry = { 14 | add: function(name, func) { 15 | loaded[name] = func; 16 | }, 17 | addMultiple: function(functions) { 18 | Object.keys(functions).forEach(function(name) { 19 | loaded[name] = functions[name]; 20 | }); 21 | } 22 | }; 23 | 24 | try { 25 | loader = new Function("functions", "tree", "fileInfo", contents); 26 | loader(registry, tree, this.fileInfo); 27 | } catch(e) { 28 | callback(new LessError({ 29 | message: "Plugin evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" , 30 | filename: this.fileInfo.filename 31 | }), null ); 32 | } 33 | 34 | callback(null, { functions: loaded }); 35 | }; 36 | -------------------------------------------------------------------------------- /dukpy/jsmodules/less/less/tree/condition.js: -------------------------------------------------------------------------------- 1 | var Node = require("./node"); 2 | 3 | var Condition = function (op, l, r, i, negate) { 4 | this.op = op.trim(); 5 | this.lvalue = l; 6 | this.rvalue = r; 7 | this.index = i; 8 | this.negate = negate; 9 | }; 10 | Condition.prototype = new Node(); 11 | Condition.prototype.type = "Condition"; 12 | Condition.prototype.accept = function (visitor) { 13 | this.lvalue = visitor.visit(this.lvalue); 14 | this.rvalue = visitor.visit(this.rvalue); 15 | }; 16 | Condition.prototype.eval = function (context) { 17 | var result = (function (op, a, b) { 18 | switch (op) { 19 | case 'and': return a && b; 20 | case 'or': return a || b; 21 | default: 22 | switch (Node.compare(a, b)) { 23 | case -1: 24 | return op === '<' || op === '=<' || op === '<='; 25 | case 0: 26 | return op === '=' || op === '>=' || op === '=<' || op === '<='; 27 | case 1: 28 | return op === '>' || op === '>='; 29 | default: 30 | return false; 31 | } 32 | } 33 | })(this.op, this.lvalue.eval(context), this.rvalue.eval(context)); 34 | 35 | return this.negate ? !result : result; 36 | }; 37 | module.exports = Condition; 38 | -------------------------------------------------------------------------------- /dukpy/jsmodules/react/react-dom-server.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ReactDOMServer v0.14.8 3 | * 4 | * Copyright 2013-2015, Facebook, Inc. 5 | * All rights reserved. 6 | * 7 | * This source code is licensed under the BSD-style license found in the 8 | * LICENSE file in the root directory of this source tree. An additional grant 9 | * of patent rights can be found in the PATENTS file in the same directory. 10 | * 11 | */ 12 | // Based off https://github.com/ForbesLindesay/umd/blob/master/template.js 13 | ;(function(f) { 14 | // CommonJS 15 | if (typeof exports === "object" && typeof module !== "undefined") { 16 | module.exports = f(require('./react')); 17 | 18 | // RequireJS 19 | } else if (typeof define === "function" && define.amd) { 20 | define(['react'], f); 21 | 22 | //