@babel/traverse#Node TypeScript Examples

The following examples show how to use @babel/traverse#Node. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: code-utils.ts    From plasmic with MIT License 7 votes vote down vote up
nodeToFormattedCode = (
  n: Node,
  baseDir: string,
  unformatted?: boolean,
  commentsToRemove?: Set<string>
) => {
  const c = generate(n, {
    retainLines: true,
    shouldPrintComment: (c) => !commentsToRemove || !commentsToRemove.has(c),
  }).code;
  return unformatted
    ? c
    : formatAsLocal(c, "/tmp/x.tsx", baseDir, {
        trailingComma: "none",
        arrowParens: "avoid",
      });
}
Example #2
Source File: utils.ts    From plasmic with MIT License 7 votes vote down vote up
code = (
  n: Node | undefined,
  opts?: GeneratorOptions,
  unformatted?: boolean
) => {
  assert(n);
  const c = generate(n, opts).code;
  return unformatted ? c : formatted(c);
}
Example #3
Source File: plasmic-parser-test.ts    From plasmic with MIT License 6 votes vote down vote up
getSource = (n: Node | null, input: string) => {
  if (!n) {
    return undefined;
  }
  if (n.start === null || n.end === null) {
    return undefined;
  }
  return input.substring(n.start, n.end);
}
Example #4
Source File: utils.ts    From plasmic with MIT License 6 votes vote down vote up
nodesDeepEqualIgnoreComments = (n1: Node, n2: Node) => {
  return compactCode(n1) === compactCode(n2);
}
Example #5
Source File: utils.ts    From plasmic with MIT License 6 votes vote down vote up
compactCode = (n: Node) => {
  return code(n, { comments: false, compact: true }, true);
}
Example #6
Source File: plasmic-parser.ts    From plasmic with MIT License 6 votes vote down vote up
isCallWithoutArguments = (
  call: Node,
  object: string,
  member: string
) => {
  return (
    isCallIgnoreArguments(call, object, member) && call.arguments.length === 0
  );
}
Example #7
Source File: plasmic-parser.ts    From plasmic with MIT License 6 votes vote down vote up
isCallIgnoreArguments = (
  call: Node,
  object: string,
  member: string
): call is CallExpression => {
  if (call.type !== "CallExpression") {
    return false;
  }
  return memberExpressionMatch(call.callee, object, member);
}
Example #8
Source File: plasmic-parser.ts    From plasmic with MIT License 6 votes vote down vote up
memberExpressionMatch = (
  node: Node,
  object: string,
  member?: string
) => {
  if (node.type !== "MemberExpression") {
    return false;
  }
  return (
    node.object.type === "Identifier" &&
    node.object.name === object &&
    node.property.type === "Identifier" &&
    node.property.name === member
  );
}
Example #9
Source File: plasmic-parser.ts    From plasmic with MIT License 6 votes vote down vote up
tryExtractPropertyNameOfMemberExpression = (
  callee: Node,
  object: string
) => {
  if (callee.type !== "MemberExpression") {
    return undefined;
  }
  if (
    callee.object.type === "Identifier" &&
    callee.object.name === object &&
    callee.property.type === "Identifier"
  ) {
    return callee.property.name as string;
  }
  return undefined;
}
Example #10
Source File: plasmic-parser.ts    From plasmic with MIT License 6 votes vote down vote up
parseNode = (
  n:
    | Expression
    | JSXEmptyExpression
    | JSXExpressionContainer
    | JSXSpreadChild
    | JSXFragment,
  parent: Node | undefined,
  forceFragmentAsOneNode: boolean
): PlasmicASTNode => {
  let node: PlasmicASTNode | null = null;
  if (n.type === "JSXExpressionContainer") {
    // Always unwrap the expression container
    node = parseNode(n.expression, n, forceFragmentAsOneNode);
  } else if (n.type === "JSXSpreadChild") {
    node = parseAsOneNode(n.expression, n);
  } else if (n.type === "JSXFragment") {
    node = forceFragmentAsOneNode
      ? parseAsOneNode(n, parent)
      : {
          type: "jsx-fragment",
          children: parseChildren(n),
          rawNode: n,
        };
  } else {
    node = parseAsOneNode(n, parent);
  }
  node.rawNode = n;
  return node;
}
Example #11
Source File: plasmic-parser.ts    From plasmic with MIT License 6 votes vote down vote up
tryParseAsPlasmicJsxElement = (
  jsx: JSXElement,
  parent: Node | undefined
): PlasmicJsxElement | undefined => {
  let nodeId: string | undefined = undefined;
  for (const attr of jsx.openingElement.attributes) {
    const curNodeId = tryGetNodeIdFromAttr(attr);
    if (curNodeId) {
      if (nodeId) {
        // The id in className and spreador must match.
        assert(nodeId === curNodeId);
      }
      nodeId = curNodeId;
    }
  }
  return nodeId ? parseJsxElement(jsx, nodeId, parent) : undefined;
}
Example #12
Source File: plasmic-parser.ts    From plasmic with MIT License 6 votes vote down vote up
parseJsxElement = (
  n: JSXElement,
  plasmicId: string,
  parent: Node | undefined
): PlasmicJsxElement => {
  const attrs: Array<[string, PlasmicASTNode | null] | PlasmicASTNode> =
    n.openingElement.attributes.map((attr) => {
      if (attr.type === "JSXAttribute") {
        const name = getAttrName(attr);
        assert(L.isString(name));
        return [
          name,
          attr.value === null
            ? null
            : parseNode(attr.value as any, attr, false),
        ];
      } else {
        // spreador
        return parseNode(attr.argument, attr, false);
      }
    });
  const children = parseChildren(n);
  return {
    attrs,
    children,
    rawNode: n,
    rawParent: parent,
    nameInId: plasmicId,
  };
}
Example #13
Source File: plasmic-parser.ts    From plasmic with MIT License 5 votes vote down vote up
isJsxElementOrFragment = (n: Node) => {
  return n.type === "JSXElement" || n.type === "JSXFragment";
}
Example #14
Source File: plasmic-parser.ts    From plasmic with MIT License 5 votes vote down vote up
parseAsOneNode = (
  n: Expression | JSXEmptyExpression | JSXFragment,
  parent: Node | undefined
): PlasmicASTNode => {
  if (n.type === "JSXEmptyExpression") {
    return {
      type: "opaque",
      rawNode: n,
    };
  }

  if (n.type === "StringLiteral") {
    return {
      type: "string-lit",
      value: n.value,
      rawNode: n,
    };
  }
  if (n.type === "CallExpression") {
    const callee = tryExtractPropertyNameOfMemberExpression(
      n.callee,
      helperObject
    );
    const m = callee?.match(/^childStr(.+)$/);
    if (m) {
      return { type: "child-str-call", plasmicId: ensure(m[1]), rawNode: n };
    }
  }

  // Need to handle this case specially since traverse doesn't visit n itself.
  if (n.type === "JSXElement") {
    const jsxElement = tryParseAsPlasmicJsxElement(n, parent);
    if (jsxElement) {
      return {
        type: "tag-or-component",
        jsxElement,
        rawNode: n,
        secondaryNodes: [],
      };
    }
  }
  const jsxElements: PlasmicJsxElement[] = [];
  traverse(n, {
    noScope: true,
    JSXElement: function (path) {
      const jsxElement = tryParseAsPlasmicJsxElement(path.node, path.parent);
      if (jsxElement) {
        jsxElements.push(jsxElement);
        path.skip();
      }
    },
  });
  return jsxElements.length > 0
    ? {
        type: "tag-or-component",
        jsxElement: jsxElements[0],
        rawNode: n,
        secondaryNodes: jsxElements.slice(1).map((elt) => ({
          type: "tag-or-component",
          jsxElement: elt,
          rawNode:
            elt.rawParent && elt.rawParent.type === "LogicalExpression"
              ? elt.rawParent
              : elt.rawNode,
          secondaryNodes: [],
        })),
      }
    : {
        type: "opaque",
        rawNode: n,
      };
}
Example #15
Source File: plasmic-parser.ts    From plasmic with MIT License 5 votes vote down vote up
renameJsxTree(targetCodeVersion: CodeVersion) {
    const revisedNameInIdToUuid = new Map<string, string>(
      targetCodeVersion.nameInIdToUuid
    );
    const renamedJsx = cloneDeepWithHook(this.root.rawNode, (n: Node) => {
      const helperMember = tryExtractPropertyNameOfMemberExpression(
        n,
        helperObject
      );
      if (helperMember) {
        const eventHandlers = [
          "onMouseUp",
          "onMouseDown",
          "onFocus",
          "onBlur",
          "onMouseEnter",
          "onMouseLeave",
        ];
        const regEx = new RegExp(
          `^(cls|props|show|childStr|${eventHandlers.join("|")})(.+)$`
        );
        const m = helperMember.match(regEx);
        if (m) {
          const prefix = m[1];
          const nameInId = m[2];
          const curUuid = ensure(this.nameInIdToUuid.get(nameInId));
          const newNameInId = targetCodeVersion.findMatchingNameInId({
            nameInId,
            uuid: curUuid,
          });
          if (!newNameInId) {
            // node has been deleted in targetCodeVersion. Fine to keep the name,
            // but need to add this id into the new map.
            revisedNameInIdToUuid.set(nameInId, curUuid);
          } else {
            return makeMemberExpression(
              helperObject,
              `${prefix}${newNameInId}`
            );
          }
        }
        return undefined;
      }
      const argName = tryExtractPropertyNameOfMemberExpression(n, "args");
      if (argName) {
        const newSlotArgName = targetCodeVersion.findMatchingSlotArgName(
          argName,
          this.tryGetSlotArgUuid(argName)
        );
        if (!newSlotArgName) {
          // Either non slot args, or the arg has been deleted. Keep the name
        } else {
          return makeMemberExpression("args", newSlotArgName);
        }
      }

      return undefined;
    });
    assert(babel.types.isExpression(renamedJsx));
    return new CodeVersion(renamedJsx, revisedNameInIdToUuid);
  }
Example #16
Source File: plasmic-parser-test.ts    From plasmic with MIT License 5 votes vote down vote up
assertJsxExpressionContainer = (node: Node | undefined, tag: string) => {
  assert(node);
  assert(node.type === "JSXExpressionContainer");
}
Example #17
Source File: plasmic-parser-test.ts    From plasmic with MIT License 5 votes vote down vote up
assertJsxElement = (node: Node | undefined) => {
  assert(node);
  assert(node.type === "JSXElement");
}
Example #18
Source File: index.ts    From plasmic with MIT License 4 votes vote down vote up
mergeFiles = async (
  componentByUuid: Map<string, ComponentInfoForMerge>,
  projectId: string,
  projectSyncDataProvider: ProjectSyncDataProviderType,
  preMergeFile?: (
    compId: string,
    baseSrc: string,
    baseNameInIdToUuid: Map<string, string>,
    newSrc: string,
    newNameInIdToUuid: Map<string, string>
  ) => void,
  appendJsxTreeOnMissingBase?: boolean,
  // Output parameter, which is used to collect warning information
  warningInfos?: Map<string, WarningInfo>
) => {
  const updateableByComponentUuid = new Map<
    string,
    PlasmicComponentSkeletonFile
  >();
  componentByUuid.forEach((codeVersions, uuid) => {
    const parsedEdited = tryParseComponentSkeletonFile(codeVersions.editedFile);
    if (parsedEdited) {
      updateableByComponentUuid.set(uuid, parsedEdited);
    }
  });
  if (updateableByComponentUuid.size === 0) {
    // Nothing to update
    return undefined;
  }

  const mergedFiles = new Map<string, string>();

  for (const [componentUuid, parsedEdited] of updateableByComponentUuid) {
    const warnInfo = new WarningInfo();
    warningInfos?.set(componentUuid, warnInfo);
    let baseMetadata: ComponentSkeletonModel | undefined = undefined;
    try {
      const projectSyncData = await projectSyncDataProvider(
        projectId,
        parsedEdited.revision
      );
      baseMetadata = projectSyncData.components.find(
        (c) => c.uuid === componentUuid
      );
    } catch {
      warnInfo.addRawWarn(
        `missing merging base for ${projectId} at revision ${parsedEdited.revision}`
      );
    }
    const component = ensure(componentByUuid.get(componentUuid));
    const parsedNew = ensure(tryParseComponentSkeletonFile(component.newFile));

    if (!baseMetadata) {
      if (appendJsxTreeOnMissingBase) {
        mergedFiles.set(
          componentUuid,
          formatted(`${component.editedFile}

          // Please perform merge with the following JSX manually.
           \`// plasmic-managed-jsx/${parsedNew.revision}
  return (${code(parsedNew.jsx).trimEnd()});\``)
        );
        continue;
      } else {
        throw new Error(
          `Cannot perform three way merge due to missing base version. For Plasmic CLI users, please add '--append-jsx-on-missing-base' so that you can perform merging by yourselves.`
        );
      }
    }

    if (preMergeFile) {
      preMergeFile(
        componentUuid,
        baseMetadata.fileContent,
        baseMetadata.nameInIdToUuid,
        component.newFile,
        component.newNameInIdToUuid
      );
    }
    const parsedBase = ensure(
      tryParseComponentSkeletonFile(baseMetadata.fileContent)
    );
    const newCodeVersion = new CodeVersion(
      parsedNew.jsx,
      component.newNameInIdToUuid
    );
    const baseCodeVersion = new CodeVersion(
      parsedBase.jsx,
      baseMetadata.nameInIdToUuid
    );

    // All other metadata
    const editedCodeVersion = new CodeVersion(
      parsedEdited.jsx,
      // edited version share the same nameInIdtoUuid mapping
      baseMetadata.nameInIdToUuid
    );
    warnInfo.setSecondaryNodes([
      ...editedCodeVersion.secondaryTagsOrComponents.values(),
    ]);

    const newJsx = renameAndSerializePlasmicASTNode(newCodeVersion.root, {
      newVersion: newCodeVersion,
      editedVersion: editedCodeVersion,
      baseVersion: baseCodeVersion,
    });

    // Ideally, we should keep parsedEdited read-only, but, it is not a big deal
    // to modify the comment.
    parsedEdited.identifyingComment.value = ` plasmic-managed-jsx/${parsedNew.revision}`;
    const mergedFile = cloneDeepWithHook(parsedEdited.file, (n: Node) => {
      if (n === parsedEdited.jsx) {
        return newJsx;
      }
      return undefined;
    });
    mergePlasmicImports(mergedFile, parsedNew, parsedEdited);
    const mergedCode = code(mergedFile, { retainLines: true });
    mergedFiles.set(componentUuid, formatted(mergedCode));
  }
  return mergedFiles;
}
Example #19
Source File: index.ts    From plasmic with MIT License 4 votes vote down vote up
serializeTagOrComponent = (
  newNode: PlasmicTagOrComponent,
  codeVersions: CodeVersions
): Expression | JSXExpressionContainer | undefined => {
  const { newVersion, editedVersion, baseVersion } = codeVersions;

  // find node with same id in edited version.
  const editedNode = editedVersion.findTagOrComponent(
    newNode.jsxElement.nameInId
  );
  const baseNode = baseVersion.findTagOrComponent(newNode.jsxElement.nameInId);
  if (editedNode) {
    // the node must exist in base version.
    assert(!!baseNode);
    const editedNodeJsxElementClone = babel.types.cloneDeep(
      editedNode.jsxElement.rawNode
    );

    editedNodeJsxElementClone.openingElement.name = mergedTag(
      newNode,
      editedNode,
      baseNode
    );
    if (editedNodeJsxElementClone.closingElement) {
      editedNodeJsxElementClone.closingElement.name =
        editedNodeJsxElementClone.openingElement.name;
    }

    editedNodeJsxElementClone.openingElement.attributes = mergeAttributes(
      newNode,
      editedNode,
      baseNode,
      codeVersions
    );

    editedNodeJsxElementClone.children = mergedChildren(
      newNode,
      editedNode,
      baseNode,
      codeVersions
    );
    if (
      !editedNodeJsxElementClone.closingElement &&
      editedNodeJsxElementClone.children.length > 0
    ) {
      editedNodeJsxElementClone.closingElement = babel.types.jsxClosingElement(
        editedNodeJsxElementClone.openingElement.name
      );
      editedNodeJsxElementClone.openingElement.selfClosing = false;
    }

    const secondaryNodes = new Map<Node, Node | undefined>(
      editedNode.secondaryNodes.map((n) => {
        const newSecondaryNode = newVersion.findTagOrComponent(
          n.jsxElement.nameInId
        );
        if (!newSecondaryNode) {
          return [n.rawNode, undefined];
        }
        const rawReplacement = serializePlasmicASTNode(
          newSecondaryNode,
          codeVersions
        );
        return [n.rawNode, rawReplacement];
      })
    );

    const newNodeCallShowFunc = newVersion.hasShowFuncCall(newNode);
    const editedNodeCallShowFunc = editedVersion.hasShowFuncCall(editedNode);

    const editedNodeClone = cloneDeepWithHook(editedNode.rawNode, (n: Node) => {
      if (n === editedNode.jsxElement.rawNode) {
        if (newNodeCallShowFunc && !editedNodeCallShowFunc) {
          // add the show call
          const expr = makeJsxElementWithShowCall(
            editedNodeJsxElementClone,
            editedNode.jsxElement.nameInId
          );
          // add an expression container if the parent is JSXElement or Fragment
          return editedNode.jsxElement.rawParent &&
            isJsxElementOrFragment(editedNode.jsxElement.rawParent)
            ? wrapInJsxExprContainer(expr)
            : expr;
        }
        return editedNodeJsxElementClone;
      }

      if (secondaryNodes.has(n)) {
        const replacement = secondaryNodes.get(n);
        // If deleted, an empty fragment instead
        return replacement || mkJsxFragment([]);
      }
      return undefined;
    });

    if (editedNodeCallShowFunc && !newNodeCallShowFunc) {
      traverse(editedNodeClone, {
        noScope: true,
        CallExpression: function (path) {
          if (
            isCallIgnoreArguments(
              path.node,
              helperObject,
              `show${editedNode.jsxElement.nameInId}`
            )
          ) {
            path.replaceWithSourceString("true");
          }
        },
      });
    }
    return editedNodeClone;
  }
  // check if the node has been deleted.
  if (baseNode) {
    // If so, don't output anything
    return undefined;
  }
  // This is new node. Just output self.
  const childrenReplacement = new Map<Node, Node>();
  newNode.jsxElement.children.forEach((child) => {
    // Plasmic never emit opaque node.
    assert(child.type !== "opaque");
    const childReplacement = serializeNonOpaquePlasmicASTNode(
      child,
      codeVersions
    );
    if (childReplacement) {
      if (babel.types.isExpression(childReplacement)) {
        // need to wrap in expression container
        const maybeWrapped =
          childReplacement.type !== "JSXElement" &&
          childReplacement.type !== "JSXFragment"
            ? babel.types.jsxExpressionContainer(childReplacement)
            : childReplacement;
        childrenReplacement.set(child.rawNode, maybeWrapped);
      } else {
        childrenReplacement.set(child.rawNode, childReplacement);
      }
    }
  });
  // Attribute replacement
  const attrsReplacement = new Map<Node, Node>();
  newNode.jsxElement.attrs.forEach((attr) => {
    if (L.isArray(attr)) {
      const [key, value] = attr;
      // className is an opaque attribute!
      if (value && value.type !== "opaque") {
        const attrReplacement = serializeNonOpaquePlasmicASTNode(
          value,
          codeVersions
        );
        if (attrReplacement) {
          if (attrReplacement.type !== "JSXExpressionContainer") {
            assert(attrReplacement.type !== "JSXText");
            attrsReplacement.set(
              value.rawNode,
              babel.types.jsxExpressionContainer(attrReplacement)
            );
          } else {
            attrsReplacement.set(value.rawNode, attrReplacement);
          }
        }
      }
    }
  });
  return cloneDeepWithHook(
    newNode.rawNode,
    (n: Node) => childrenReplacement.get(n) || attrsReplacement.get(n)
  );
}