BVB Source Codes

reactide Show index.js Source code

Return Download reactide: download index.js Source code - Download reactide Source code - Type:.js
  1. "use strict";
  2.  
  3. exports.__esModule = true;
  4.  
  5. var _symbol = require("babel-runtime/core-js/symbol");
  6.  
  7. var _symbol2 = _interopRequireDefault(_symbol);
  8.  
  9. var _create = require("babel-runtime/core-js/object/create");
  10.  
  11. var _create2 = _interopRequireDefault(_create);
  12.  
  13. var _classCallCheck2 = require("babel-runtime/helpers/classCallCheck");
  14.  
  15. var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
  16.  
  17. exports.default = function () {
  18.   return {
  19.     visitor: {
  20.       VariableDeclaration: function VariableDeclaration(path, file) {
  21.         var node = path.node,
  22.             parent = path.parent,
  23.             scope = path.scope;
  24.  
  25.         if (!isBlockScoped(node)) return;
  26.         convertBlockScopedToVar(path, null, parent, scope, true);
  27.  
  28.         if (node._tdzThis) {
  29.           var nodes = [node];
  30.  
  31.           for (var i = 0; i < node.declarations.length; i++) {
  32.             var decl = node.declarations[i];
  33.             if (decl.init) {
  34.               var assign = t.assignmentExpression("=", decl.id, decl.init);
  35.               assign._ignoreBlockScopingTDZ = true;
  36.               nodes.push(t.expressionStatement(assign));
  37.             }
  38.             decl.init = file.addHelper("temporalUndefined");
  39.           }
  40.  
  41.           node._blockHoist = 2;
  42.  
  43.           if (path.isCompletionRecord()) {
  44.             nodes.push(t.expressionStatement(scope.buildUndefinedNode()));
  45.           }
  46.  
  47.           path.replaceWithMultiple(nodes);
  48.         }
  49.       },
  50.       Loop: function Loop(path, file) {
  51.         var node = path.node,
  52.             parent = path.parent,
  53.             scope = path.scope;
  54.  
  55.         t.ensureBlock(node);
  56.         var blockScoping = new BlockScoping(path, path.get("body"), parent, scope, file);
  57.         var replace = blockScoping.run();
  58.         if (replace) path.replaceWith(replace);
  59.       },
  60.       CatchClause: function CatchClause(path, file) {
  61.         var parent = path.parent,
  62.             scope = path.scope;
  63.  
  64.         var blockScoping = new BlockScoping(null, path.get("body"), parent, scope, file);
  65.         blockScoping.run();
  66.       },
  67.       "BlockStatement|SwitchStatement|Program": function BlockStatementSwitchStatementProgram(path, file) {
  68.         if (!ignoreBlock(path)) {
  69.           var blockScoping = new BlockScoping(null, path, path.parent, path.scope, file);
  70.           blockScoping.run();
  71.         }
  72.       }
  73.     }
  74.   };
  75. };
  76.  
  77. var _babelTraverse = require("babel-traverse");
  78.  
  79. var _babelTraverse2 = _interopRequireDefault(_babelTraverse);
  80.  
  81. var _tdz = require("./tdz");
  82.  
  83. var _babelTypes = require("babel-types");
  84.  
  85. var t = _interopRequireWildcard(_babelTypes);
  86.  
  87. var _values = require("lodash/values");
  88.  
  89. var _values2 = _interopRequireDefault(_values);
  90.  
  91. var _extend = require("lodash/extend");
  92.  
  93. var _extend2 = _interopRequireDefault(_extend);
  94.  
  95. var _babelTemplate = require("babel-template");
  96.  
  97. var _babelTemplate2 = _interopRequireDefault(_babelTemplate);
  98.  
  99. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
  100.  
  101. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  102.  
  103. function ignoreBlock(path) {
  104.   return t.isLoop(path.parent) || t.isCatchClause(path.parent);
  105. }
  106.  
  107. var buildRetCheck = (0, _babelTemplate2.default)("\n  if (typeof RETURN === \"object\") return RETURN.v;\n");
  108.  
  109. function isBlockScoped(node) {
  110.   if (!t.isVariableDeclaration(node)) return false;
  111.   if (node[t.BLOCK_SCOPED_SYMBOL]) return true;
  112.   if (node.kind !== "let" && node.kind !== "const") return false;
  113.   return true;
  114. }
  115.  
  116. function convertBlockScopedToVar(path, node, parent, scope) {
  117.   var moveBindingsToParent = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
  118.  
  119.   if (!node) {
  120.     node = path.node;
  121.   }
  122.  
  123.   if (!t.isFor(parent)) {
  124.     for (var i = 0; i < node.declarations.length; i++) {
  125.       var declar = node.declarations[i];
  126.       declar.init = declar.init || scope.buildUndefinedNode();
  127.     }
  128.   }
  129.  
  130.   node[t.BLOCK_SCOPED_SYMBOL] = true;
  131.   node.kind = "var";
  132.  
  133.   if (moveBindingsToParent) {
  134.     var parentScope = scope.getFunctionParent();
  135.     var ids = path.getBindingIdentifiers();
  136.     for (var name in ids) {
  137.       var binding = scope.getOwnBinding(name);
  138.       if (binding) binding.kind = "var";
  139.       scope.moveBindingTo(name, parentScope);
  140.     }
  141.   }
  142. }
  143.  
  144. function isVar(node) {
  145.   return t.isVariableDeclaration(node, { kind: "var" }) && !isBlockScoped(node);
  146. }
  147.  
  148. var letReferenceBlockVisitor = _babelTraverse2.default.visitors.merge([{
  149.   Loop: {
  150.     enter: function enter(path, state) {
  151.       state.loopDepth++;
  152.     },
  153.     exit: function exit(path, state) {
  154.       state.loopDepth--;
  155.     }
  156.   },
  157.   Function: function Function(path, state) {
  158.     if (state.loopDepth > 0) {
  159.       path.traverse(letReferenceFunctionVisitor, state);
  160.     }
  161.     return path.skip();
  162.   }
  163. }, _tdz.visitor]);
  164.  
  165. var letReferenceFunctionVisitor = _babelTraverse2.default.visitors.merge([{
  166.   ReferencedIdentifier: function ReferencedIdentifier(path, state) {
  167.     var ref = state.letReferences[path.node.name];
  168.  
  169.     if (!ref) return;
  170.  
  171.     var localBinding = path.scope.getBindingIdentifier(path.node.name);
  172.     if (localBinding && localBinding !== ref) return;
  173.  
  174.     state.closurify = true;
  175.   }
  176. }, _tdz.visitor]);
  177.  
  178. var hoistVarDeclarationsVisitor = {
  179.   enter: function enter(path, self) {
  180.     var node = path.node,
  181.         parent = path.parent;
  182.  
  183.  
  184.     if (path.isForStatement()) {
  185.       if (isVar(node.init, node)) {
  186.         var nodes = self.pushDeclar(node.init);
  187.         if (nodes.length === 1) {
  188.           node.init = nodes[0];
  189.         } else {
  190.           node.init = t.sequenceExpression(nodes);
  191.         }
  192.       }
  193.     } else if (path.isFor()) {
  194.       if (isVar(node.left, node)) {
  195.         self.pushDeclar(node.left);
  196.         node.left = node.left.declarations[0].id;
  197.       }
  198.     } else if (isVar(node, parent)) {
  199.       path.replaceWithMultiple(self.pushDeclar(node).map(function (expr) {
  200.         return t.expressionStatement(expr);
  201.       }));
  202.     } else if (path.isFunction()) {
  203.       return path.skip();
  204.     }
  205.   }
  206. };
  207.  
  208. var loopLabelVisitor = {
  209.   LabeledStatement: function LabeledStatement(_ref, state) {
  210.     var node = _ref.node;
  211.  
  212.     state.innerLabels.push(node.label.name);
  213.   }
  214. };
  215.  
  216. var continuationVisitor = {
  217.   enter: function enter(path, state) {
  218.     if (path.isAssignmentExpression() || path.isUpdateExpression()) {
  219.       var bindings = path.getBindingIdentifiers();
  220.       for (var name in bindings) {
  221.         if (state.outsideReferences[name] !== path.scope.getBindingIdentifier(name)) continue;
  222.         state.reassignments[name] = true;
  223.       }
  224.     }
  225.   }
  226. };
  227.  
  228. function loopNodeTo(node) {
  229.   if (t.isBreakStatement(node)) {
  230.     return "break";
  231.   } else if (t.isContinueStatement(node)) {
  232.     return "continue";
  233.   }
  234. }
  235.  
  236. var loopVisitor = {
  237.   Loop: function Loop(path, state) {
  238.     var oldIgnoreLabeless = state.ignoreLabeless;
  239.     state.ignoreLabeless = true;
  240.     path.traverse(loopVisitor, state);
  241.     state.ignoreLabeless = oldIgnoreLabeless;
  242.     path.skip();
  243.   },
  244.   Function: function Function(path) {
  245.     path.skip();
  246.   },
  247.   SwitchCase: function SwitchCase(path, state) {
  248.     var oldInSwitchCase = state.inSwitchCase;
  249.     state.inSwitchCase = true;
  250.     path.traverse(loopVisitor, state);
  251.     state.inSwitchCase = oldInSwitchCase;
  252.     path.skip();
  253.   },
  254.   "BreakStatement|ContinueStatement|ReturnStatement": function BreakStatementContinueStatementReturnStatement(path, state) {
  255.     var node = path.node,
  256.         parent = path.parent,
  257.         scope = path.scope;
  258.  
  259.     if (node[this.LOOP_IGNORE]) return;
  260.  
  261.     var replace = void 0;
  262.     var loopText = loopNodeTo(node);
  263.  
  264.     if (loopText) {
  265.       if (node.label) {
  266.         if (state.innerLabels.indexOf(node.label.name) >= 0) {
  267.           return;
  268.         }
  269.  
  270.         loopText = loopText + "|" + node.label.name;
  271.       } else {
  272.         if (state.ignoreLabeless) return;
  273.  
  274.         if (state.inSwitchCase) return;
  275.  
  276.         if (t.isBreakStatement(node) && t.isSwitchCase(parent)) return;
  277.       }
  278.  
  279.       state.hasBreakContinue = true;
  280.       state.map[loopText] = node;
  281.       replace = t.stringLiteral(loopText);
  282.     }
  283.  
  284.     if (path.isReturnStatement()) {
  285.       state.hasReturn = true;
  286.       replace = t.objectExpression([t.objectProperty(t.identifier("v"), node.argument || scope.buildUndefinedNode())]);
  287.     }
  288.  
  289.     if (replace) {
  290.       replace = t.returnStatement(replace);
  291.       replace[this.LOOP_IGNORE] = true;
  292.       path.skip();
  293.       path.replaceWith(t.inherits(replace, node));
  294.     }
  295.   }
  296. };
  297.  
  298. var BlockScoping = function () {
  299.   function BlockScoping(loopPath, blockPath, parent, scope, file) {
  300.     (0, _classCallCheck3.default)(this, BlockScoping);
  301.  
  302.     this.parent = parent;
  303.     this.scope = scope;
  304.     this.file = file;
  305.  
  306.     this.blockPath = blockPath;
  307.     this.block = blockPath.node;
  308.  
  309.     this.outsideLetReferences = (0, _create2.default)(null);
  310.     this.hasLetReferences = false;
  311.     this.letReferences = (0, _create2.default)(null);
  312.     this.body = [];
  313.  
  314.     if (loopPath) {
  315.       this.loopParent = loopPath.parent;
  316.       this.loopLabel = t.isLabeledStatement(this.loopParent) && this.loopParent.label;
  317.       this.loopPath = loopPath;
  318.       this.loop = loopPath.node;
  319.     }
  320.   }
  321.  
  322.   BlockScoping.prototype.run = function run() {
  323.     var block = this.block;
  324.     if (block._letDone) return;
  325.     block._letDone = true;
  326.  
  327.     var needsClosure = this.getLetReferences();
  328.  
  329.     if (t.isFunction(this.parent) || t.isProgram(this.block)) {
  330.       this.updateScopeInfo();
  331.       return;
  332.     }
  333.  
  334.     if (!this.hasLetReferences) return;
  335.  
  336.     if (needsClosure) {
  337.       this.wrapClosure();
  338.     } else {
  339.       this.remap();
  340.     }
  341.  
  342.     this.updateScopeInfo(needsClosure);
  343.  
  344.     if (this.loopLabel && !t.isLabeledStatement(this.loopParent)) {
  345.       return t.labeledStatement(this.loopLabel, this.loop);
  346.     }
  347.   };
  348.  
  349.   BlockScoping.prototype.updateScopeInfo = function updateScopeInfo(wrappedInClosure) {
  350.     var scope = this.scope;
  351.     var parentScope = scope.getFunctionParent();
  352.     var letRefs = this.letReferences;
  353.  
  354.     for (var key in letRefs) {
  355.       var ref = letRefs[key];
  356.       var binding = scope.getBinding(ref.name);
  357.       if (!binding) continue;
  358.       if (binding.kind === "let" || binding.kind === "const") {
  359.         binding.kind = "var";
  360.  
  361.         if (wrappedInClosure) {
  362.           scope.removeBinding(ref.name);
  363.         } else {
  364.           scope.moveBindingTo(ref.name, parentScope);
  365.         }
  366.       }
  367.     }
  368.   };
  369.  
  370.   BlockScoping.prototype.remap = function remap() {
  371.     var letRefs = this.letReferences;
  372.     var scope = this.scope;
  373.  
  374.     for (var key in letRefs) {
  375.       var ref = letRefs[key];
  376.  
  377.       if (scope.parentHasBinding(key) || scope.hasGlobal(key)) {
  378.         if (scope.hasOwnBinding(key)) scope.rename(ref.name);
  379.  
  380.         if (this.blockPath.scope.hasOwnBinding(key)) this.blockPath.scope.rename(ref.name);
  381.       }
  382.     }
  383.   };
  384.  
  385.   BlockScoping.prototype.wrapClosure = function wrapClosure() {
  386.     if (this.file.opts.throwIfClosureRequired) {
  387.       throw this.blockPath.buildCodeFrameError("Compiling let/const in this block would add a closure " + "(throwIfClosureRequired).");
  388.     }
  389.     var block = this.block;
  390.  
  391.     var outsideRefs = this.outsideLetReferences;
  392.  
  393.     if (this.loop) {
  394.       for (var name in outsideRefs) {
  395.         var id = outsideRefs[name];
  396.  
  397.         if (this.scope.hasGlobal(id.name) || this.scope.parentHasBinding(id.name)) {
  398.           delete outsideRefs[id.name];
  399.           delete this.letReferences[id.name];
  400.  
  401.           this.scope.rename(id.name);
  402.  
  403.           this.letReferences[id.name] = id;
  404.           outsideRefs[id.name] = id;
  405.         }
  406.       }
  407.     }
  408.  
  409.     this.has = this.checkLoop();
  410.  
  411.     this.hoistVarDeclarations();
  412.  
  413.     var params = (0, _values2.default)(outsideRefs);
  414.     var args = (0, _values2.default)(outsideRefs);
  415.  
  416.     var isSwitch = this.blockPath.isSwitchStatement();
  417.  
  418.     var fn = t.functionExpression(null, params, t.blockStatement(isSwitch ? [block] : block.body));
  419.     fn.shadow = true;
  420.  
  421.     this.addContinuations(fn);
  422.  
  423.     var ref = fn;
  424.  
  425.     if (this.loop) {
  426.       ref = this.scope.generateUidIdentifier("loop");
  427.       this.loopPath.insertBefore(t.variableDeclaration("var", [t.variableDeclarator(ref, fn)]));
  428.     }
  429.  
  430.     var call = t.callExpression(ref, args);
  431.     var ret = this.scope.generateUidIdentifier("ret");
  432.  
  433.     var hasYield = _babelTraverse2.default.hasType(fn.body, this.scope, "YieldExpression", t.FUNCTION_TYPES);
  434.     if (hasYield) {
  435.       fn.generator = true;
  436.       call = t.yieldExpression(call, true);
  437.     }
  438.  
  439.     var hasAsync = _babelTraverse2.default.hasType(fn.body, this.scope, "AwaitExpression", t.FUNCTION_TYPES);
  440.     if (hasAsync) {
  441.       fn.async = true;
  442.       call = t.awaitExpression(call);
  443.     }
  444.  
  445.     this.buildClosure(ret, call);
  446.  
  447.     if (isSwitch) this.blockPath.replaceWithMultiple(this.body);else block.body = this.body;
  448.   };
  449.  
  450.   BlockScoping.prototype.buildClosure = function buildClosure(ret, call) {
  451.     var has = this.has;
  452.     if (has.hasReturn || has.hasBreakContinue) {
  453.       this.buildHas(ret, call);
  454.     } else {
  455.       this.body.push(t.expressionStatement(call));
  456.     }
  457.   };
  458.  
  459.   BlockScoping.prototype.addContinuations = function addContinuations(fn) {
  460.     var state = {
  461.       reassignments: {},
  462.       outsideReferences: this.outsideLetReferences
  463.     };
  464.  
  465.     this.scope.traverse(fn, continuationVisitor, state);
  466.  
  467.     for (var i = 0; i < fn.params.length; i++) {
  468.       var param = fn.params[i];
  469.       if (!state.reassignments[param.name]) continue;
  470.  
  471.       var newParam = this.scope.generateUidIdentifier(param.name);
  472.       fn.params[i] = newParam;
  473.  
  474.       this.scope.rename(param.name, newParam.name, fn);
  475.  
  476.       fn.body.body.push(t.expressionStatement(t.assignmentExpression("=", param, newParam)));
  477.     }
  478.   };
  479.  
  480.   BlockScoping.prototype.getLetReferences = function getLetReferences() {
  481.     var _this = this;
  482.  
  483.     var block = this.block;
  484.  
  485.     var declarators = [];
  486.  
  487.     if (this.loop) {
  488.       var init = this.loop.left || this.loop.init;
  489.       if (isBlockScoped(init)) {
  490.         declarators.push(init);
  491.         (0, _extend2.default)(this.outsideLetReferences, t.getBindingIdentifiers(init));
  492.       }
  493.     }
  494.  
  495.     var addDeclarationsFromChild = function addDeclarationsFromChild(path, node) {
  496.       node = node || path.node;
  497.       if (t.isClassDeclaration(node) || t.isFunctionDeclaration(node) || isBlockScoped(node)) {
  498.         if (isBlockScoped(node)) {
  499.           convertBlockScopedToVar(path, node, block, _this.scope);
  500.         }
  501.         declarators = declarators.concat(node.declarations || node);
  502.       }
  503.       if (t.isLabeledStatement(node)) {
  504.         addDeclarationsFromChild(path.get("body"), node.body);
  505.       }
  506.     };
  507.  
  508.     if (block.body) {
  509.       for (var i = 0; i < block.body.length; i++) {
  510.         var declarPath = this.blockPath.get("body")[i];
  511.         addDeclarationsFromChild(declarPath);
  512.       }
  513.     }
  514.  
  515.     if (block.cases) {
  516.       for (var _i = 0; _i < block.cases.length; _i++) {
  517.         var consequents = block.cases[_i].consequent;
  518.  
  519.         for (var j = 0; j < consequents.length; j++) {
  520.           var _declarPath = this.blockPath.get("cases")[_i];
  521.           var declar = consequents[j];
  522.           addDeclarationsFromChild(_declarPath, declar);
  523.         }
  524.       }
  525.     }
  526.  
  527.     for (var _i2 = 0; _i2 < declarators.length; _i2++) {
  528.       var _declar = declarators[_i2];
  529.  
  530.       var keys = t.getBindingIdentifiers(_declar, false, true);
  531.       (0, _extend2.default)(this.letReferences, keys);
  532.       this.hasLetReferences = true;
  533.     }
  534.  
  535.     if (!this.hasLetReferences) return;
  536.  
  537.     var state = {
  538.       letReferences: this.letReferences,
  539.       closurify: false,
  540.       file: this.file,
  541.       loopDepth: 0
  542.     };
  543.  
  544.     var loopOrFunctionParent = this.blockPath.find(function (path) {
  545.       return path.isLoop() || path.isFunction();
  546.     });
  547.     if (loopOrFunctionParent && loopOrFunctionParent.isLoop()) {
  548.       state.loopDepth++;
  549.     }
  550.  
  551.     this.blockPath.traverse(letReferenceBlockVisitor, state);
  552.  
  553.     return state.closurify;
  554.   };
  555.  
  556.   BlockScoping.prototype.checkLoop = function checkLoop() {
  557.     var state = {
  558.       hasBreakContinue: false,
  559.       ignoreLabeless: false,
  560.       inSwitchCase: false,
  561.       innerLabels: [],
  562.       hasReturn: false,
  563.       isLoop: !!this.loop,
  564.       map: {},
  565.       LOOP_IGNORE: (0, _symbol2.default)()
  566.     };
  567.  
  568.     this.blockPath.traverse(loopLabelVisitor, state);
  569.     this.blockPath.traverse(loopVisitor, state);
  570.  
  571.     return state;
  572.   };
  573.  
  574.   BlockScoping.prototype.hoistVarDeclarations = function hoistVarDeclarations() {
  575.     this.blockPath.traverse(hoistVarDeclarationsVisitor, this);
  576.   };
  577.  
  578.   BlockScoping.prototype.pushDeclar = function pushDeclar(node) {
  579.     var declars = [];
  580.     var names = t.getBindingIdentifiers(node);
  581.     for (var name in names) {
  582.       declars.push(t.variableDeclarator(names[name]));
  583.     }
  584.  
  585.     this.body.push(t.variableDeclaration(node.kind, declars));
  586.  
  587.     var replace = [];
  588.  
  589.     for (var i = 0; i < node.declarations.length; i++) {
  590.       var declar = node.declarations[i];
  591.       if (!declar.init) continue;
  592.  
  593.       var expr = t.assignmentExpression("=", declar.id, declar.init);
  594.       replace.push(t.inherits(expr, declar));
  595.     }
  596.  
  597.     return replace;
  598.   };
  599.  
  600.   BlockScoping.prototype.buildHas = function buildHas(ret, call) {
  601.     var body = this.body;
  602.  
  603.     body.push(t.variableDeclaration("var", [t.variableDeclarator(ret, call)]));
  604.  
  605.     var retCheck = void 0;
  606.     var has = this.has;
  607.     var cases = [];
  608.  
  609.     if (has.hasReturn) {
  610.       retCheck = buildRetCheck({
  611.         RETURN: ret
  612.       });
  613.     }
  614.  
  615.     if (has.hasBreakContinue) {
  616.       for (var key in has.map) {
  617.         cases.push(t.switchCase(t.stringLiteral(key), [has.map[key]]));
  618.       }
  619.  
  620.       if (has.hasReturn) {
  621.         cases.push(t.switchCase(null, [retCheck]));
  622.       }
  623.  
  624.       if (cases.length === 1) {
  625.         var single = cases[0];
  626.         body.push(t.ifStatement(t.binaryExpression("===", ret, single.test), single.consequent[0]));
  627.       } else {
  628.         if (this.loop) {
  629.           for (var i = 0; i < cases.length; i++) {
  630.             var caseConsequent = cases[i].consequent[0];
  631.             if (t.isBreakStatement(caseConsequent) && !caseConsequent.label) {
  632.               caseConsequent.label = this.loopLabel = this.loopLabel || this.scope.generateUidIdentifier("loop");
  633.             }
  634.           }
  635.         }
  636.  
  637.         body.push(t.switchStatement(ret, cases));
  638.       }
  639.     } else {
  640.       if (has.hasReturn) {
  641.         body.push(retCheck);
  642.       }
  643.     }
  644.   };
  645.  
  646.   return BlockScoping;
  647. }();
  648.  
  649. module.exports = exports["default"];
downloadindex.js Source code - Download reactide Source code
Related Source Codes/Software:
rkt - rkt is a pod-native container engine for Linux. It... 2017-06-11
uWebSockets - Tiny WebSockets https://for... 2017-06-11
realworld - TodoMVC for the RealWorld - Exemplary fullstack Me... 2017-06-11
goreplay - GoReplay is an open-source tool for capturing and ... 2017-06-10
pyenv - Simple Python version management 2017-06-10
postal - 2017-06-11
CRYENGINE - CRYENGINE is a powerful real-time game development... 2017-06-11
reactide - Reactide is the first dedicated IDE for React web ... 2017-06-11
redux-saga - An alternative side effect model for Redux apps ... 2017-06-10
angular-starter - 2017-06-10

 Back to top