mirror of
				https://github.com/zulip/zulip.git
				synced 2025-10-31 03:53:50 +00:00 
			
		
		
		
	And convert the corresponding function expressions to arrow style
while we’re here.
import * as babelParser from "recast/parsers/babel";
import * as recast from "recast";
import * as tsParser from "recast/parsers/typescript";
import { builders as b, namedTypes as n } from "ast-types";
import K from "ast-types/gen/kinds";
import fs from "fs";
import path from "path";
import process from "process";
const checkExpression = (node: n.Node): node is K.ExpressionKind =>
  n.Expression.check(node);
for (const file of process.argv.slice(2)) {
  console.log("Parsing", file);
  const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), {
    parser: path.extname(file) === ".ts" ? tsParser : babelParser,
  });
  let changed = false;
  recast.visit(ast, {
    visitCallExpression(path) {
      const { callee, arguments: args } = path.node;
      if (
        n.MemberExpression.check(callee) &&
        !callee.computed &&
        n.Identifier.check(callee.object) &&
        callee.object.name === "_" &&
        n.Identifier.check(callee.property) &&
        callee.property.name === "map" &&
        args.length === 2 &&
        checkExpression(args[0]) &&
        checkExpression(args[1])
      ) {
        const [arr, fn] = args;
        path.replace(
          b.callExpression(b.memberExpression(arr, b.identifier("map")), [
            n.FunctionExpression.check(fn) ||
            n.ArrowFunctionExpression.check(fn)
              ? b.arrowFunctionExpression(
                  fn.params,
                  n.BlockStatement.check(fn.body) &&
                    fn.body.body.length === 1 &&
                    n.ReturnStatement.check(fn.body.body[0])
                    ? fn.body.body[0].argument || b.identifier("undefined")
                    : fn.body
                )
              : fn,
          ])
        );
        changed = true;
      }
      this.traverse(path);
    },
  });
  if (changed) {
    console.log("Writing", file);
    fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" });
  }
}
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
		
	
		
			
				
	
	
		
			190 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			190 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| exports.eq_array = (a, b, eq) => {
 | |
|     if (a === b) {
 | |
|         // either both are undefined, or they
 | |
|         // are referentially equal
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     if (a === undefined || b === undefined) {
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     if (a.length !== b.length) {
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     return _.all(a, (item, i) => {
 | |
|         return eq(item, b[i]);
 | |
|     });
 | |
| };
 | |
| 
 | |
| exports.ul = (opts) => {
 | |
|     return {
 | |
|         tag_name: 'ul',
 | |
|         opts: opts,
 | |
|     };
 | |
| };
 | |
| 
 | |
| exports.render_tag = (tag) => {
 | |
|     /*
 | |
|         This renders a tag into a string.  It will
 | |
|         automatically escape attributes, but it's your
 | |
|         responsibility to make sure keyed_nodes provide
 | |
|         a `render` method that escapes HTML properly.
 | |
|         (One option is to use templates.)
 | |
| 
 | |
|         Do NOT call this method directly, except for
 | |
|         testing.  The vdom scheme expects you to use
 | |
|         the `update` method.
 | |
|     */
 | |
|     const opts = tag.opts;
 | |
|     const tag_name = tag.tag_name;
 | |
|     const attr_str = opts.attrs.map(attr => ' ' + attr[0] + '="' + util.escape_html(attr[1]) + '"').join('');
 | |
| 
 | |
|     const start_tag = '<' + tag_name + attr_str + '>';
 | |
|     const end_tag = '</' + tag_name + '>';
 | |
| 
 | |
|     if (opts.keyed_nodes === undefined) {
 | |
|         blueslip.error("We need keyed_nodes to render innards.");
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     const innards = opts.keyed_nodes.map(node => node.render()).join('\n');
 | |
|     return start_tag + '\n' + innards + '\n' + end_tag;
 | |
| };
 | |
| 
 | |
| exports.update = (replace_content, find, new_dom, old_dom) => {
 | |
|     /*
 | |
|         The update method allows you to continually
 | |
|         update a "virtual" representation of your DOM,
 | |
|         and then this method actually updates the
 | |
|         real DOM using jQuery.  The caller will pass
 | |
|         in a method called `replace_content` that will replace
 | |
|         the entire html and a method called `find` to
 | |
|         find the existing DOM for more surgical updates.
 | |
| 
 | |
|         The first "update" will be more like a create,
 | |
|         because your `old_dom` should be undefined.
 | |
|         After that initial call, it is important that
 | |
|         you always pass in a correct value of `old_dom`;
 | |
|         otherwise, things will be incredibly confusing.
 | |
| 
 | |
|         The basic scheme here is simple:
 | |
| 
 | |
|             1) If old_dom is undefined, we render
 | |
|                everything for the first time.
 | |
| 
 | |
|             2) If the keys of your new children are no
 | |
|                longer the same order as the old
 | |
|                children, then we just render
 | |
|                everything anew.
 | |
|                (We may refine this in the future.)
 | |
| 
 | |
|             3) If your key structure remains the same,
 | |
|                then we update your child nodes on
 | |
|                a child-by-child basis, and we avoid
 | |
|                updates where the data had remained
 | |
|                the same.
 | |
| 
 | |
|         The key to making this all work is that
 | |
|         `new_dom` should include a `keyed_nodes` option
 | |
|         where each `keyed_node` has a `key` and supports
 | |
|         these methods:
 | |
| 
 | |
|             eq - can compare itself to similar nodes
 | |
|                  for data equality
 | |
| 
 | |
|             render - can create an HTML representation
 | |
|                      of itself
 | |
| 
 | |
|         The `new_dom` should generally be created with
 | |
|         something like `vdom.ul`, which will set a
 | |
|         tag field internally and which will want options
 | |
|         like `attrs` for attributes.
 | |
| 
 | |
|         For examples of creating vdom objects, look at
 | |
|         `pm_list_dom.js`.
 | |
|     */
 | |
|     function do_full_update() {
 | |
|         const rendered_dom = exports.render_tag(new_dom);
 | |
|         replace_content(rendered_dom);
 | |
|     }
 | |
| 
 | |
|     if (old_dom === undefined) {
 | |
|         do_full_update();
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     const new_opts = new_dom.opts;
 | |
|     const old_opts = old_dom.opts;
 | |
| 
 | |
|     if (new_opts.keyed_nodes === undefined) {
 | |
|         // We generally want to use vdom on lists, and
 | |
|         // adding keys for childrens lets us avoid unnecessary
 | |
|         // redraws (or lets us know we should just rebuild
 | |
|         // the dom).
 | |
|         blueslip.error("We need keyed_nodes for updates.");
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     const same_structure = exports.eq_array(
 | |
|         new_opts.keyed_nodes,
 | |
|         old_opts.keyed_nodes,
 | |
|         (a, b) => a.key === b.key
 | |
|     );
 | |
| 
 | |
|     if (!same_structure) {
 | |
|         /* We could do something smarter like detecting row
 | |
|            moves, but it's overkill for small lists.
 | |
|         */
 | |
|         do_full_update();
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|         DO "QUICK" UPDATES:
 | |
| 
 | |
|         We've gotten this far, so we know we have the
 | |
|         same overall structure for our parent tag, and
 | |
|         the only thing left to do with our child nodes
 | |
|         is to possibly update them in place (via jQuery).
 | |
|         We will only update nodes whose data has changed.
 | |
|     */
 | |
| 
 | |
|     const child_elems = find().children();
 | |
| 
 | |
|     for (const [i, new_node] of new_opts.keyed_nodes.entries()) {
 | |
|         const old_node = old_opts.keyed_nodes[i];
 | |
|         if (new_node.eq(old_node)) {
 | |
|             continue;
 | |
|         }
 | |
|         const rendered_dom = new_node.render();
 | |
|         child_elems.eq(i).replaceWith(rendered_dom);
 | |
|     }
 | |
| 
 | |
|     exports.update_attrs(
 | |
|         find(),
 | |
|         new_opts.attrs,
 | |
|         old_opts.attrs
 | |
|     );
 | |
| };
 | |
| 
 | |
| exports.update_attrs = (elem, new_attrs, old_attrs) => {
 | |
|     const new_dict = new Map(new_attrs);
 | |
|     const old_dict = new Map(old_attrs);
 | |
| 
 | |
|     for (const [k, v] of new_attrs) {
 | |
|         if (v !== old_dict.get(k)) {
 | |
|             elem.attr(k, v);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     for (const [k] of old_attrs) {
 | |
|         if (!new_dict.has(k)) {
 | |
|             elem.removeAttr(k);
 | |
|         }
 | |
|     }
 | |
| };
 | |
| 
 | |
| window.vdom = exports;
 |