@babel/types#JSXElement TypeScript Examples

The following examples show how to use @babel/types#JSXElement. 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: js.ts    From css-to-js with MIT License 6 votes vote down vote up
export function parseJsx(input: string): [JSXElement, string] {
  let rawLines: string;
  if (input.trim().match(/^<.*>$/s)) {
    rawLines = input;
  } else {
    rawLines = `<TempComponent ${input} />`;
  }

  let expression: Expression;
  try {
    expression = babelParser.parseExpression(rawLines, {
      plugins: ["jsx"],
    });
  } catch (e) {
    if (e instanceof SyntaxError) {
      const location = getErrorLocation(e);
      const codeFrame = codeFrameColumns(rawLines, { start: location });
      throw new SyntaxError(`${e.message}\n\n${codeFrame}`);
    }
    throw e;
  }

  if (expression.type !== "JSXElement") {
    throw new Error("Expression is not a JSX element");
  }

  return [expression, rawLines];
}
Example #2
Source File: plasmic-parser.ts    From plasmic with MIT License 6 votes vote down vote up
parseChildren = (n: JSXFragment | JSXElement): PlasmicASTNode[] => {
  const nodesList: PlasmicASTNode[] = [];
  n.children.forEach((child) => {
    if (child.type === "JSXText") {
      const text = child.value;
      if (text !== undefined) {
        const trimmed = text.trim();
        if (trimmed) {
          nodesList.push({ type: "text", rawNode: child, value: trimmed });
        }
      }
    } else {
      nodesList.push(parseNode(child, n, true));
    }
  });
  return nodesList;
}
Example #3
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 #4
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 #5
Source File: plasmic-parser.ts    From plasmic with MIT License 6 votes vote down vote up
hasShowFuncCall(node: PlasmicTagOrComponent) {
    if (node.rawNode === node.jsxElement.rawNode) {
      return false;
    }
    let found = false;
    traverse(node.rawNode, {
      noScope: true,
      CallExpression: function (path) {
        if (
          memberExpressionMatch(
            path.node.callee,
            helperObject,
            `show${node.jsxElement.nameInId}`
          )
        ) {
          found = true;
          path.stop();
        }
      },
      JSXElement: function (path) {
        if (path.node === node.jsxElement.rawNode) {
          path.skip();
        }
      },
    });
    return found;
  }
Example #6
Source File: utils.ts    From plasmic with MIT License 6 votes vote down vote up
tagName = (jsxElement: JSXElement) => {
  // babel generator append ";" to the name. stirp it.
  return code(jsxElement.openingElement.name).replace(";", "").trim();
}
Example #7
Source File: index.ts    From plasmic with MIT License 6 votes vote down vote up
getRawNamedAttrs = (jsxElement: JSXElement) => {
  const attrs = new Map<string, JSXAttribute>();
  for (const attr of jsxElement.openingElement.attributes) {
    if (attr.type !== "JSXAttribute") {
      continue;
    }
    assert(L.isString(attr.name.name));
    attrs.set(attr.name.name, attr);
  }
  return attrs;
}
Example #8
Source File: resolveJsxComponent.ts    From react-optimized-image with MIT License 6 votes vote down vote up
resolveJsxComponent = (types: Babel['types'], path: NodePath<JSXElement>): string | undefined => {
  // check if it is a possible react-optimized-image component before proceeding further
  const srcAttribute = getAttribute(path, 'src');

  if (!srcAttribute) {
    return;
  }

  const requireName = getRelevantRequireString(types, srcAttribute);

  // check if the imported src is not an image in case an extension is present
  if ((!requireName || !requireName.match(/\.(jpe?g|png|svg|gif|webp)($|\?)/gi)) && requireName !== '') {
    return;
  }

  // it is now likely to be a react-optimized-image component, so start resolving

  // check for a normal opening element (<Img ...)
  if (path.node.openingElement.name.type === 'JSXIdentifier') {
    const binding = path.scope.getBinding(path.node.openingElement.name.name);
    const component = getImportedJsxComponent(binding);

    return component;
  }

  // check for an object opening element (<styles.Img ...)
  if (path.node.openingElement.name.type === 'JSXMemberExpression') {
    const objectBindings = resolveJsxMemberExpression(path.node.openingElement.name);
    const resolvedBinding = resolveObject(types, path, objectBindings);
    const component = getImportedJsxComponent(resolvedBinding);

    return component;
  }
}
Example #9
Source File: jsx.ts    From react-optimized-image with MIT License 6 votes vote down vote up
getNumberedArrayAttribute = (path: NodePath<JSXElement>, attributeName: string): number[] | undefined => {
  const attribute = getAttribute(path, attributeName);

  if (attribute) {
    if (
      attribute.node.value &&
      attribute.node.value.type === 'JSXExpressionContainer' &&
      attribute.node.value.expression.type === 'ArrayExpression'
    ) {
      const values: number[] = [];

      attribute.node.value.expression.elements.forEach((element, i) => {
        if (element && element.type === 'NumericLiteral') {
          values.push(element.value);
        } else if (element) {
          // todo: better error message with link to docs when ready & create test for this error
          throw (((attribute.get('value') as NodePath<JSXExpressionContainer>).get('expression') as NodePath<
            ArrayExpression
          >).get(`elements.${i}`) as NodePath).buildCodeFrameError('Only static number values are allowed');
        }
      });

      return values;
    }

    // todo: better error message with link to docs when ready & create test for this error
    throw attribute.get('value').buildCodeFrameError('Only static array with number values is allowed');
  }

  return undefined;
}
Example #10
Source File: jsx.ts    From react-optimized-image with MIT License 6 votes vote down vote up
getTypeAttribute = (path: NodePath<JSXElement>, types: string[]): string | undefined => {
  const attribute = getAttribute(path, 'type');

  if (attribute && attribute.node.value && attribute.node.value.type === 'StringLiteral') {
    const type = attribute.node.value.value;

    if (types.indexOf(type) < 0) {
      throw (attribute.get('value') as NodePath<JSXExpressionContainer>).buildCodeFrameError(
        `Type ${type} not found in images.config.js`,
      );
    }

    return type;
  }

  if (attribute && attribute.node) {
    throw (attribute.get('value') as NodePath).buildCodeFrameError('Only static string values are allowed');
  }
}
Example #11
Source File: jsx.ts    From react-optimized-image with MIT License 6 votes vote down vote up
getBooleanAttribute = (path: NodePath<JSXElement>, attributeName: string): boolean | undefined => {
  const attribute = getAttribute(path, attributeName);

  if (attribute) {
    if (attribute.node.value === null) {
      return true;
    }

    if (
      attribute.node.value.type === 'JSXExpressionContainer' &&
      attribute.node.value.expression.type === 'BooleanLiteral'
    ) {
      return attribute.node.value.expression.value;
    }

    // todo: better error message with link to docs when ready & create test for this error
    throw attribute.get('value').buildCodeFrameError('Only static boolean values are allowed');
  }

  return undefined;
}
Example #12
Source File: jsx.ts    From react-optimized-image with MIT License 6 votes vote down vote up
getAttribute = (path: NodePath<JSXElement>, attributeName: string): NodePath<JSXAttribute> | undefined => {
  if (path.node.openingElement.attributes) {
    let attribue;

    path.get('openingElement').traverse({
      JSXAttribute(attributePath) {
        if (attributePath.node.name.name === attributeName) {
          attribue = attributePath;
          attributePath.stop();
        }
      },
    });

    return attribue;
  }

  return undefined;
}
Example #13
Source File: svg.ts    From react-optimized-image with MIT License 6 votes vote down vote up
transformSvgComponent = (types: Babel['types'], path: NodePath<JSXElement>): void => {
  // abort if it has already the rawSrc attribute
  if (getAttribute(path, 'rawSrc')) {
    return;
  }

  const src = getAttribute(path, 'src');
  const requireArgs = src ? getRequireArguments(types, src) : undefined;

  if (!src || !requireArgs) {
    return;
  }

  const rawSrc = buildRawSrcAttribute(types, requireArgs);
  (path.get('openingElement') as NodePath<JSXOpeningElement>).pushContainer('attributes', rawSrc);
  src.remove();
}
Example #14
Source File: img.ts    From react-optimized-image with MIT License 6 votes vote down vote up
transformImgComponent = (types: Babel['types'], path: NodePath<JSXElement>): void => {
  // abort if it has already the rawSrc attribute
  if (getAttribute(path, 'rawSrc')) {
    return;
  }

  // get src attribute
  const src = getAttribute(path, 'src');
  const requireArgs = src ? getRequireArguments(types, src) : undefined;

  if (!src || !requireArgs) {
    return;
  }

  const config = buildConfig(types, path);

  const query: Record<string, string> = {};

  // add boolean queries
  ['inline', 'url', 'original'].forEach((attr) => {
    if ((config as Record<string, unknown>)[attr] === true) {
      query[attr] = '';
    }
  });

  // transfer original src attribute if a new query param needs to be set
  if (Object.keys(query).length > 0) {
    (src.get('value') as NodePath).replaceWith(
      types.jsxExpressionContainer(buildRequireStatement(types, clone(requireArgs), query)),
    );
  }

  const rawSrc = buildRawSrcAttribute(types, requireArgs, config, query);
  (path.get('openingElement') as NodePath<JSXOpeningElement>).pushContainer('attributes', rawSrc);
}
Example #15
Source File: jsxConverter.ts    From react-native-decompiler with GNU Affero General Public License v3.0 5 votes vote down vote up
private parseJsx(node: Expression): JSXText | JSXExpressionContainer | JSXSpreadChild | JSXElement | JSXFragment {
    if (isStringLiteral(node)) {
      return jsxText(node.value);
    }
    if (isCallExpression(node)) {
      const args = node.arguments;

      let name: JSXIdentifier | JSXMemberExpression | undefined;
      if (isIdentifier(args[0]) || isStringLiteral(args[0])) {
        name = jsxIdentifier(isIdentifier(args[0]) ? args[0].name : args[0].value);
      } else if (isMemberExpression(args[0]) && isIdentifier(args[0].object) && isIdentifier(args[0].property)) {
        name = jsxMemberExpression(jsxIdentifier(args[0].object.name), jsxIdentifier(args[0].property.name));
      } else {
        this.debugLog(`fail to parse component ${args[0].type} inside callExpression`);
        return jsxExpressionContainer(node);
      }

      let props: JSXAttribute[] = [];
      if (isObjectExpression(args[1])) {
        props = args[1].properties.map((prop) => {
          if (!isObjectProperty(prop) || !isIdentifier(prop.key)) return null;
          if (isStringLiteral(prop.value)) {
            return jsxAttribute(jsxIdentifier(prop.key.name), prop.value);
          }
          if (isBooleanLiteral(prop.value) && prop.value.value) {
            return jsxAttribute(jsxIdentifier(prop.key.name), null);
          }
          if (isExpression(prop.value)) {
            return jsxAttribute(jsxIdentifier(prop.key.name), jsxExpressionContainer(prop.value));
          }
          return null;
        }).filter((e): e is JSXAttribute => e != null);
      }

      const children = args.slice(2).map((e) => (isExpression(e) ? this.parseJsx(e) : null)).filter((e): e is JSXElement => e != null);

      if (children.length) {
        return jsxElement(jsxOpeningElement(name, props), jsxClosingElement(name), children);
      }

      return jsxElement(jsxOpeningElement(name, props, true), null, []);
    }

    this.debugLog(`fail to parse component ${node.type}`);
    return jsxExpressionContainer(node);
  }
Example #16
Source File: jsxConverter.ts    From react-native-decompiler with GNU Affero General Public License v3.0 5 votes vote down vote up
private parseJsx(node: Expression): JSXText | JSXExpressionContainer | JSXSpreadChild | JSXElement | JSXFragment {
    if (isStringLiteral(node)) {
      return jsxText(node.value);
    }
    if (isCallExpression(node)) {
      const args = node.arguments;

      let name: JSXIdentifier | JSXMemberExpression | undefined;
      if (isIdentifier(args[0]) || isStringLiteral(args[0])) {
        name = jsxIdentifier(isIdentifier(args[0]) ? args[0].name : args[0].value);
      } else if (isMemberExpression(args[0]) && isIdentifier(args[0].object) && isIdentifier(args[0].property)) {
        name = jsxMemberExpression(jsxIdentifier(args[0].object.name), jsxIdentifier(args[0].property.name));
      } else {
        this.debugLog(`fail to parse component ${args[0].type} inside callExpression`);
        return jsxExpressionContainer(node);
      }

      let props: JSXAttribute[] = [];
      if (isObjectExpression(args[1])) {
        props = args[1].properties.map((prop) => {
          if (!isObjectProperty(prop) || !isIdentifier(prop.key)) return null;
          if (isStringLiteral(prop.value)) {
            return jsxAttribute(jsxIdentifier(prop.key.name), prop.value);
          }
          if (isBooleanLiteral(prop.value) && prop.value.value) {
            return jsxAttribute(jsxIdentifier(prop.key.name), null);
          }
          if (isExpression(prop.value)) {
            return jsxAttribute(jsxIdentifier(prop.key.name), jsxExpressionContainer(prop.value));
          }
          return null;
        }).filter((e): e is JSXAttribute => e != null);
      }

      const children = args.slice(2).map((e) => (isExpression(e) ? this.parseJsx(e) : null)).filter((e): e is JSXElement => e != null);

      if (children.length) {
        return jsxElement(jsxOpeningElement(name, props), jsxClosingElement(name), children);
      }

      return jsxElement(jsxOpeningElement(name, props, true), null, []);
    }

    this.debugLog(`fail to parse component ${node.type}`);
    return jsxExpressionContainer(node);
  }
Example #17
Source File: jsxConverter.ts    From react-native-decompiler with GNU Affero General Public License v3.0 5 votes vote down vote up
private parseJsx(node: Expression): JSXText | JSXExpressionContainer | JSXSpreadChild | JSXElement | JSXFragment {
    if (isStringLiteral(node)) {
      return jsxText(node.value);
    }
    if (isCallExpression(node)) {
      const args = node.arguments;

      let name: JSXIdentifier | JSXMemberExpression | undefined;
      if (isIdentifier(args[0]) || isStringLiteral(args[0])) {
        name = jsxIdentifier(isIdentifier(args[0]) ? args[0].name : args[0].value);
      } else if (isMemberExpression(args[0]) && isIdentifier(args[0].object) && isIdentifier(args[0].property)) {
        name = jsxMemberExpression(jsxIdentifier(args[0].object.name), jsxIdentifier(args[0].property.name));
      } else {
        this.debugLog(`fail to parse component ${args[0].type} inside callExpression`);
        return jsxExpressionContainer(node);
      }

      let props: JSXAttribute[] = [];
      if (isObjectExpression(args[1])) {
        props = args[1].properties.map((prop) => {
          if (!isObjectProperty(prop) || !isIdentifier(prop.key)) return null;
          if (isStringLiteral(prop.value)) {
            return jsxAttribute(jsxIdentifier(prop.key.name), prop.value);
          }
          if (isBooleanLiteral(prop.value) && prop.value.value) {
            return jsxAttribute(jsxIdentifier(prop.key.name), null);
          }
          if (isExpression(prop.value)) {
            return jsxAttribute(jsxIdentifier(prop.key.name), jsxExpressionContainer(prop.value));
          }
          return null;
        }).filter((e): e is JSXAttribute => e != null);
      }

      const children = args.slice(2).map((e) => (isExpression(e) ? this.parseJsx(e) : null)).filter((e): e is JSXElement => e != null);

      if (children.length) {
        return jsxElement(jsxOpeningElement(name, props), jsxClosingElement(name), children);
      }

      return jsxElement(jsxOpeningElement(name, props, true), null, []);
    }

    this.debugLog(`fail to parse component ${node.type}`);
    return jsxExpressionContainer(node);
  }
Example #18
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 #19
Source File: index.ts    From plasmic with MIT License 5 votes vote down vote up
makeJsxElementWithShowCall = (jsxElement: JSXElement, nodeId: string) =>
  babel.types.logicalExpression(
    "&&",
    makeCallExpression(helperObject, `show${nodeId}`),
    jsxElement
  )
Example #20
Source File: jsxConverter.ts    From react-native-decompiler with GNU Affero General Public License v3.0 5 votes vote down vote up
private parseJsx(node: Expression): JSXText | JSXExpressionContainer | JSXSpreadChild | JSXElement | JSXFragment {
    if (isStringLiteral(node)) {
      return jsxText(node.value);
    }
    if (isCallExpression(node)) {
      const args = node.arguments;

      let name: JSXIdentifier | JSXMemberExpression | undefined;
      if (isIdentifier(args[0]) || isStringLiteral(args[0])) {
        name = jsxIdentifier(isIdentifier(args[0]) ? args[0].name : args[0].value);
      } else if (isMemberExpression(args[0]) && isIdentifier(args[0].object) && isIdentifier(args[0].property)) {
        name = jsxMemberExpression(jsxIdentifier(args[0].object.name), jsxIdentifier(args[0].property.name));
      } else {
        this.debugLog(`fail to parse component ${args[0].type} inside callExpression`);
        return jsxExpressionContainer(node);
      }

      let props: JSXAttribute[] = [];
      if (isObjectExpression(args[1])) {
        props = args[1].properties.map((prop) => {
          if (!isObjectProperty(prop) || !isIdentifier(prop.key)) return null;
          if (isStringLiteral(prop.value)) {
            return jsxAttribute(jsxIdentifier(prop.key.name), prop.value);
          }
          if (isBooleanLiteral(prop.value) && prop.value.value) {
            return jsxAttribute(jsxIdentifier(prop.key.name), null);
          }
          if (isExpression(prop.value)) {
            return jsxAttribute(jsxIdentifier(prop.key.name), jsxExpressionContainer(prop.value));
          }
          return null;
        }).filter((e): e is JSXAttribute => e != null);
      }

      const children = args.slice(2).map((e) => (isExpression(e) ? this.parseJsx(e) : null)).filter((e): e is JSXElement => e != null);

      if (children.length) {
        return jsxElement(jsxOpeningElement(name, props), jsxClosingElement(name), children);
      }

      return jsxElement(jsxOpeningElement(name, props, true), null, []);
    }

    this.debugLog(`fail to parse component ${node.type}`);
    return jsxExpressionContainer(node);
  }
Example #21
Source File: resolveJsxComponent.ts    From react-optimized-image with MIT License 5 votes vote down vote up
resolveObject = (types: Babel['types'], path: NodePath<JSXElement>, bindings: string[]): Binding | undefined => {
  if (bindings.length < 2) {
    return;
  }

  const variableName = bindings[bindings.length - 1];
  const object = path.scope.getBinding(bindings[0]);
  if (!object) {
    return;
  }

  const program = path.findParent((node) => node.isProgram());
  let declarationPath: any = null; // eslint-disable-line
  let initializer;

  // search for object declaration
  program.traverse({
    // styles.StyledImg = ...
    MemberExpression(exPath: NodePath<MemberExpression>) {
      if (exPath.node.property && exPath.node.property.name === variableName) {
        const exBindings = resolveMemberExpression(exPath.node);

        if (arraysMatch(bindings, exBindings) && exPath.parent.type === 'AssignmentExpression') {
          declarationPath = exPath;
          initializer = exPath.parent.right;
          exPath.stop();
        }
      }
    },

    // const styles = { StyledImg: ... }
    ObjectProperty(opPath: NodePath<ObjectProperty>) {
      if (opPath.node.key && opPath.node.key.type === 'Identifier' && opPath.node.key.name === variableName) {
        const exBindings = resolveObjectProperty(
          opPath.findParent(() => true) as NodePath<ObjectExpression>,
          opPath.node,
        );

        if (arraysMatch(bindings, exBindings)) {
          declarationPath = opPath;
          initializer = opPath.node.value;
          opPath.stop();
        }
      }
    },
  });

  if (!declarationPath) {
    return;
  }

  declarationPath = declarationPath as NodePath<MemberExpression>;

  // mock a binding
  const binding: Partial<Binding> = {
    kind: 'const',
    scope: declarationPath.scope,
    identifier: types.identifier(variableName),
    path: {
      ...(declarationPath as any), // eslint-disable-line
      node: types.variableDeclarator(
        types.objectPattern([types.objectProperty(types.identifier(variableName), types.identifier(variableName))]),
        initializer,
      ),
    },
  };

  return binding as Binding;
}
Example #22
Source File: img.ts    From react-optimized-image with MIT License 5 votes vote down vote up
buildConfig = (types: Babel['types'], path: NodePath<JSXElement>): ImageConfig => {
  // build config
  let config: ImageConfig = { ...(globalImageConfig.default || {}) };

  // check if a specific type is set
  const type = getTypeAttribute(path, Object.keys(globalImageConfig.types || {}));

  // add type configs
  if (type && globalImageConfig.types && globalImageConfig.types[type]) {
    config = { ...config, ...globalImageConfig.types[type] };
  }

  // check boolean attributes: webp, inline, url, original
  ['webp', 'inline', 'url', 'original'].forEach((attr) => {
    const value = getBooleanAttribute(path, attr);

    if (typeof value !== 'undefined') {
      (config as Record<string, unknown>)[attr] = value;
    } else if (typeof value === 'undefined' && (config as Record<string, unknown>)[attr] === true) {
      // add attr from global image config
      (path.get('openingElement') as NodePath<JSXOpeningElement>).pushContainer(
        'attributes',
        types.jsxAttribute(types.jsxIdentifier(attr), null),
      );
    }
  });

  // get sizes
  const sizes = getNumberedArrayAttribute(path, 'sizes');

  if (typeof sizes !== 'undefined') {
    config.sizes = sizes;
  } else if (config.sizes) {
    // add sizes attr from global image config
    (path.get('openingElement') as NodePath<JSXOpeningElement>).pushContainer(
      'attributes',
      types.jsxAttribute(
        types.jsxIdentifier('sizes'),
        types.jsxExpressionContainer(types.arrayExpression(config.sizes.map((size) => types.numericLiteral(size)))),
      ),
    );
  }

  // get densities
  const densities = getNumberedArrayAttribute(path, 'densities');

  if (typeof densities !== 'undefined') {
    config.densities = densities;
  } else if (config.densities) {
    // add densities attr from global image config
    (path.get('openingElement') as NodePath<JSXOpeningElement>).pushContainer(
      'attributes',
      types.jsxAttribute(
        types.jsxIdentifier('densities'),
        types.jsxExpressionContainer(types.arrayExpression(config.densities.map((size) => types.numericLiteral(size)))),
      ),
    );
  }

  // get breakpoints
  const breakpoints = getNumberedArrayAttribute(path, 'breakpoints');

  if (typeof breakpoints !== 'undefined') {
    config.breakpoints = breakpoints;
  } else if (config.breakpoints) {
    // add breakpoints attr from global image config
    (path.get('openingElement') as NodePath<JSXOpeningElement>).pushContainer(
      'attributes',
      types.jsxAttribute(
        types.jsxIdentifier('breakpoints'),
        types.jsxExpressionContainer(
          types.arrayExpression(config.breakpoints.map((size) => types.numericLiteral(size))),
        ),
      ),
    );
  }

  return config;
}