styled-components/babel-plugin-styled-components

1.13.2 generates cannot read property 'property' of undefined

Kolombiken opened this issue ยท 19 comments

I'm using Next.js and when trying 1.13.2 I get the following webpack-error for a component:

TypeError: Form.tsx: Cannot read property 'property' of undefined

The only css in the component are three declarations looking like this:

const StyledFormComponent = styled.divdisplay: flex;;

With 1.13.1 there are no errors.

Going to need some sort of reproduction

I am getting something similar when using an object as a css value
<div css={{ position: 'relative', zIndex: 1 }}>
Cannot read property 'toString' of undefined

but if I extract the value like this

const zIndex = 1
  return (
    <div css={{ position: 'relative', zIndex }}>

then it is not complaining

and it is only failing with zIndex, no issues with position

I've tried last version 1.13.2 and 1.12.0

Same here,

TypeError: components/Button.js: Cannot read property 'property' of undefined
      at node_modules/babel-plugin-styled-components/lib/utils/detectors.js:85:686
      at node_modules/babel-plugin-styled-components/lib/visitors/pure.js:18:36
      at PluginPass.CallExpression (node_modules/babel-plugin-styled-components/lib/index.js:45:30)
      at newFn (node_modules/@babel/traverse/lib/visitors.js:171:21)

Wonder if it is an issue with the way TS converts template literals, as the outputted code is:

var StyledButton = styled_components_1.default.button(templateObject_1 || (templateObject_1 = tslib_1.__makeTemplateObject(["\n  ", ";\n"], ["\n  ", ";\n"])), getButtonStyles_1.default);
StyledButton.displayName = 'StyledButton';
// Target the <a> here to override a:hover specificity.
var StyledLink = styled_components_1.default.a(templateObject_2 || (templateObject_2 = tslib_1.__makeTemplateObject(["\n  a& {\n    ", ";\n  }\n"], ["\n  a& {\n    ", ";\n  }\n"])), getButtonStyles_1.default);
StyledLink.displayName = 'StyledLink';
var StyledSpan = styled_components_1.default.span(templateObject_3 || (templateObject_3 = tslib_1.__makeTemplateObject(["\n  ", ";\n"], ["\n  ", ";\n"])), getButtonStyles_1.default);
StyledSpan.displayName = 'StyledSpan';

Oh, btw found a workaround: setting "pure": false skips the code path that blows up

Same here,

TypeError: components/Button.js: Cannot read property 'property' of undefined
      at node_modules/babel-plugin-styled-components/lib/utils/detectors.js:85:686
      at node_modules/babel-plugin-styled-components/lib/visitors/pure.js:18:36
      at PluginPass.CallExpression (node_modules/babel-plugin-styled-components/lib/index.js:45:30)
      at newFn (node_modules/@babel/traverse/lib/visitors.js:171:21)

Wonder if it is an issue with the way TS converts template literals, as the outputted code is:

var StyledButton = styled_components_1.default.button(templateObject_1 || (templateObject_1 = tslib_1.__makeTemplateObject(["\n  ", ";\n"], ["\n  ", ";\n"])), getButtonStyles_1.default);
StyledButton.displayName = 'StyledButton';
// Target the <a> here to override a:hover specificity.
var StyledLink = styled_components_1.default.a(templateObject_2 || (templateObject_2 = tslib_1.__makeTemplateObject(["\n  a& {\n    ", ";\n  }\n"], ["\n  a& {\n    ", ";\n  }\n"])), getButtonStyles_1.default);
StyledLink.displayName = 'StyledLink';
var StyledSpan = styled_components_1.default.span(templateObject_3 || (templateObject_3 = tslib_1.__makeTemplateObject(["\n  ", ";\n"], ["\n  ", ";\n"])), getButtonStyles_1.default);
StyledSpan.displayName = 'StyledSpan';

Can you post the code that leads to this error so I can make a test case for it? Ty.

I am also facing the same issue. To reproduce the issue you can checkout this branch feat/use-medly-linting-configs of https://github.com/medly/medly-components and then run yarn build & yarn test. For a quick glance below is the error I am getting.

Error

TypeError: /Users/mukul/workspace/medly-open-source/medly-components/packages/icons/dist/cjs/SvgIcon/SvgIcon.styled.js: Cannot read property 'property' of undefined

      at node_modules/babel-plugin-styled-components/lib/utils/detectors.js:85:686
      at node_modules/babel-plugin-styled-components/lib/utils/detectors.js:71:25
      at node_modules/babel-plugin-styled-components/lib/utils/detectors.js:71:25
      at node_modules/babel-plugin-styled-components/lib/visitors/displayNameAndId.js:158:30
      at PluginPass.CallExpression (node_modules/babel-plugin-styled-components/lib/index.js:44:42)
      at newFn (node_modules/@babel/traverse/lib/visitors.js:177:21)
      at NodePath._call (node_modules/@babel/traverse/lib/path/context.js:53:20)
      at NodePath.call (node_modules/@babel/traverse/lib/path/context.js:40:17)
      at NodePath.visit (node_modules/@babel/traverse/lib/path/context.js:90:31)
      at TraversalContext.visitQueue (node_modules/@babel/traverse/lib/context.js:103:16)

dist/cjs/SvgIcon/SvgIcon.styled.js

'use strict';
Object.defineProperty(exports, '__esModule', { value: !0 });
var o = require('@medly-components/utils'),
    r = require('styled-components');
function e(o) {
    return o && 'object' == typeof o && 'default' in o ? o : { default: o };
}
var n = function (o) {
        var e = o.hoverBgColor,
            n = o.hoverIconColor;
        return r.css(
            ['&:hover{background-color:', ';*{fill:', ';}}'],
            function (o) {
                var r = o.theme;
                return e || r.icon.colors.hovered.bgColor;
            },
            function (o) {
                var r = o.theme;
                return n || r.icon.colors.hovered.iconColor;
            }
        );
    },
    i = function (o) {
        var e = o.size,
            i = o.disabled,
            t = o.bgColor,
            l = o.withHoverEffect;
        return r.css(
            ['padding:', ';border-radius:', ';background-color:', ';', ''],
            function (o) {
                return o.theme.icon.sizes[e].padding;
            },
            function (o) {
                return o.theme.icon.borderRadius;
            },
            function (o) {
                var r = o.theme;
                return i ? r.icon.colors.disabled.bgColor : t || r.icon.colors.default.bgColor;
            },
            !i && l && n
        );
    },
    t = e(r)
        .default(o.InjectClassName)
        .attrs(function (o) {
            var r = o.theme.icon;
            return { colors: r.colors, sizes: r.sizes };
        })
        .withConfig({ displayName: 'SvgIconstyled__SvgIconStyled', componentId: 'sc-1pf3x8a-0' })(
        [
            'overflow:visible;font-size:',
            ';transition:all 100ms linear;margin:',
            ';cursor:',
            ';*{fill-opacity:',
            ';transition:all 100ms linear;fill:',
            ';}',
            ''
        ],
        function (o) {
            var r = o.theme,
                e = o.size;
            return r.icon.sizes[e].iconSize;
        },
        function (o) {
            return o.margin;
        },
        function (o) {
            var r = o.onClick;
            return o.disabled ? 'not-allowed' : r ? 'pointer' : 'inherit';
        },
        function (o) {
            return o.fillOpacity;
        },
        function (o) {
            var r = o.disabled,
                e = o.colors,
                n = o.iconColor;
            return r ? e.disabled.iconColor : n || e.default.iconColor;
        },
        function (o) {
            return 'solid' === o.variant && i;
        }
    );
(t.displayName = 'SvgIcon'), (t.defaultProps = { size: 'M', fillOpacity: 1 }), (exports.SvgIconStyled = t);

SVGIcon.styled.tsx

import { InjectClassName } from '@medly-components/utils';
import styled, { css } from 'styled-components';
import { SvgIconProps } from './types';

const hoverStyle = ({ hoverBgColor, hoverIconColor }: SvgIconProps) => css`
    &:hover {
        background-color: ${({ theme }) => hoverBgColor || theme.icon.colors.hovered.bgColor};
        * {
            fill: ${({ theme }) => hoverIconColor || theme.icon.colors.hovered.iconColor};
        }
    }
`;

const solidStyle = ({ size, disabled, bgColor, withHoverEffect }: SvgIconProps) => css`
    padding: ${({ theme }) => theme.icon.sizes[size!].padding};
    border-radius: ${({ theme }) => theme.icon.borderRadius};
    background-color: ${({ theme }) => (disabled ? theme.icon.colors.disabled.bgColor : bgColor || theme.icon.colors.default.bgColor)};

    ${!disabled && withHoverEffect && hoverStyle}
`;

export const SvgIconStyled = styled(InjectClassName).attrs(
    ({
        theme: {
            icon: { colors, sizes }
        }
    }) => ({
        colors,
        sizes
    })
)<SvgIconProps>`
    overflow: visible;
    font-size: ${({ theme, size }) => theme.icon.sizes[size!].iconSize};
    transition: all 100ms linear;
    margin: ${({ margin }) => margin};
    cursor: ${({ onClick, disabled }) => (disabled ? 'not-allowed' : onClick ? 'pointer' : 'inherit')};
    * {
        fill-opacity: ${({ fillOpacity }) => fillOpacity};
        transition: all 100ms linear;
        fill: ${({ disabled, colors, iconColor }) => (disabled ? colors.disabled.iconColor : iconColor || colors.default.iconColor)};
    }

    ${({ variant }) => variant === 'solid' && solidStyle}
`;

SvgIconStyled.displayName = 'SvgIcon';
SvgIconStyled.defaultProps = {
    size: 'M',
    fillOpacity: 1
};

@probablyup Actually the error occurs only in files where I am using css helper.

Actually this was happening because of one of babel version. You can checkout the latest version being used in medly-components

It's still a problem in 2.0.6. Here's a simple reproduction:

import styled from "styled-components";

const WrappedComponent = styled.default()``;

You can paste that into one of your tests and it will fail.
The issue is with this code in detectors.js:

(importLocalName('default', state) &&
        t.isCallExpression(tag) &&
        t.isMemberExpression(tag.callee) &&
        tag.object.property.name === 'default' &&
        tag.object.object.name === importLocalName('default', state))

tag.object is undefined. There's tag.callee in one place and tag.object in another. I assume that's the problem.

@probablyup The above comment already describes the error, probably a simple copy-paste error.

I guess the correct line would be:

(importLocalName('default', state) &&
        t.isCallExpression(tag) &&
        t.isMemberExpression(tag.callee) &&
        tag.callee.property.name === 'default' &&
        tag.callee.object.name === importLocalName('default', state))

I can provide a PR if you come to the same fix/conclusion as above

Without a fix, importing minified ES6 code, which uses sc, doesn't work when the babel plugin is utilized.

I'm honestly afraid to touch that code at this point. I've made updates multiple times that worked fine in testing and then caused all sorts of havoc in people's production setups :/

Mh, understandable.

We will have to go for forking then! Thanks for keeping the project open-source so this is even possible ๐Ÿ‘๐Ÿผ

Is there any update on this issue? Facing it in the latest version on a Next.js application.

Is there any update on this issue? Facing it in the latest version on a Next.js application.

Turned out to be a config issue in our application.

Hi tanmay-pnaik, you can say what are bad config in your app?

Having the same issue w/ rollup generated code that changed from:

styled__default["default"] which works to
styled__default.default which fails here.

Any update on this?

It failse for me at Cannot read properties of undefined (reading 'property') on tag.object.property because tag.object seems undefined.

FYI: Rollup v3 output.generatedCode.reservedNamesAsProps setting on true is not compatible with this plugin due to this bug. Thus for now whenever you generate code containing styled-components from rollup, set output.generatedCode.reservedNamesAsProps to false in rollup.

Adding on @andi1984 I also had added output.interop: "auto" to act the same as tsconfig.json@compilerOptions.esModuleInterop: true

Setting the rollup options mentioned by @andi1984 and @renarsvilnis solved this issue for us as well:

output: {
  interop: "auto",
  generatedCode: {
    reservedNamesAsProps: false,
  },
}