Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integration with typescript #41

Closed
IIIristraM opened this issue Apr 11, 2017 · 36 comments
Closed

Integration with typescript #41

IIIristraM opened this issue Apr 11, 2017 · 36 comments

Comments

@IIIristraM
Copy link

IIIristraM commented Apr 11, 2017

I'm using babel to transpile ES6 made by typescript to ES5. And it looks like this plugin doesn't work at all in this case.

typescript v2.2.2
styled-components v2.0.0-15
awesome-typescript-loader v3.1.2
babel-plugin-styled-components v1.1.4

@IIIristraM IIIristraM changed the title Integration with awesome-typescript-loader Integration with typescript Apr 11, 2017
@kitten
Copy link
Member

kitten commented Apr 11, 2017

@IIIristraM The awesome-typescript-loader has support to trigger a babel transpilation as well. The option is called useBabel.

https://github.com/s-panferov/awesome-typescript-loader#usebabel-boolean-defaultfalse

It's not something we can do on our side, since it's a build pipeline problem ;)

@kitten kitten closed this as completed Apr 11, 2017
@IIIristraM
Copy link
Author

Well I found one place where plugin was applied, so at least it takes part in a build process. And I'm going to disagree with you.

This is output from the same file

var LoginButton = styles_1.styled.div(_templateObject, function (props) {
    return props.theme.mainColor;
});
function xxx(styled) {
    var XXX = styled.div.withConfig({
        displayName: "login__XXX"
    })([""]);
}

So the place with import was skipped, while the function where was string like const XXX = styled.div``; was processed. It looks like parsing issue, so I suppose it's responsibility of the plugin.

@kitten
Copy link
Member

kitten commented Apr 11, 2017

@IIIristraM Hm, I was thinking maybe TypeScript transpiles the above LoginButton tagged template literal and thus the babel plugin doesn't find any template literals anymore. However, the second one is indeed being transpiled.

Can you provide your .babelrc and a test file, so I can find out what's going on? ;)

@kitten kitten reopened this Apr 11, 2017
@IIIristraM
Copy link
Author

IIIristraM commented Apr 12, 2017

.babelrc

{
  "presets": [
    "es2015",
    "stage-0"
  ],
  "plugins": [
    "styled-components"
  ]
}

original file

import { styled } from 'styles';

const LoginButton = styled.div`
  display: block;
  width: 140px;
  height: 48px;
`;

output

var styles_1 = __webpack_require__("./app/scripts/styles/index.tsx");
var LoginButton = styles_1.styled.div(_templateObject, function (props) {
    return props.theme.mainColor;
});

styles file

import * as React from 'react';
import * as styledComponents from 'styled-components';
import { ThemedStyledComponentsModule, ThemeProps } from 'styled-components';
import { Theme } from 'types';

// -------------------------------CONFIG CUSTOM THEME----------------------------------------

const {
    default: styled,
    css,
    injectGlobal,
    keyframes,
    ThemeProvider,
} = styledComponents as ThemedStyledComponentsModule<Theme>;

export { css, injectGlobal, keyframes, ThemeProvider, ThemeProps, styled };

// ------------------------------------HELPERS-----------------------------------------------

export function styledWithProps<TProps>() {
  const Div = styled.div``; // This line will be transpliled correctly

  class Wrap extends React.PureComponent<TProps & React.HTMLProps<{}>, {}> {
    public node: HTMLElement;

    public render() {
      return <Div {...this.props} innerRef={n => this.node = n} />;
    }
  }

  return styled(Wrap);
}

output

var React = __webpack_require__("./node_modules/react/react.js");
var styledComponents = __webpack_require__("./node_modules/styled-components/dist/styled-components.es.js");
// -------------------------------CONFIG CUSTOM THEME----------------------------------------
var styled = styledComponents.default,
    css = styledComponents.css,
    injectGlobal = styledComponents.injectGlobal,
    keyframes = styledComponents.keyframes,
    ThemeProvider = styledComponents.ThemeProvider;

exports.styled = styled;
exports.css = css;
exports.injectGlobal = injectGlobal;
exports.keyframes = keyframes;
exports.ThemeProvider = ThemeProvider;
// ------------------------------------HELPERS-----------------------------------------------
function styledWithProps() {
    var Div = styled.div.withConfig({
        displayName: "styles__Div"
    })([""]);

    var Wrap = function (_React$PureComponent) {
        _inherits(Wrap, _React$PureComponent);

        function Wrap() {
            _classCallCheck(this, Wrap);

            return _possibleConstructorReturn(this, (Wrap.__proto__ || Object.getPrototypeOf(Wrap)).apply(this, arguments));
        }

        _createClass(Wrap, [{
            key: "render",
            value: function render() {
                var _this2 = this;

                return React.createElement(Div, Object.assign({}, this.props, { innerRef: function innerRef(n) {
                        return _this2.node = n;
                    } }));
            }
        }]);

        return Wrap;
    }(React.PureComponent);

    return styled(Wrap);
}
exports.styledWithProps = styledWithProps;

so the only place that had been convered correctly was

export function styledWithProps<TProps>() {
  const Div = styled.div``; // This line will be transpliled correctly
...

@vdanchenkov
Copy link
Member

@IIIristraM could you try to change original file to

import * as styles from 'styles';

const styled = styles.styled

const LoginButton = styled.div`
  display: block;
  width: 140px;
  height: 48px;
`;

And make sure you do not import 'styled-components' directly in this file.

If it works, we will know the source of the problem.

@IIIristraM
Copy link
Author

Yes, it works

@vdanchenkov
Copy link
Member

vdanchenkov commented Apr 26, 2017

So the problem is that styled.div was converted to styles_1.styled before it got passed to the plugin. Probably it is done by the typescript, I'm not sure.

As a result, plugin failed to identify styles_1.styled as a styled component helper. We probably need to make the logic more vague, but not too much.

I think that some.name.styled.div should not be considered a styled component. While somename.styled.div could be.

@IIIristraM If you want to look into it, it's in the https://github.com/styled-components/babel-plugin-styled-components/blob/master/src/utils/detectors.js#L16

Currently it correctly identifies following expressions:

styled.div``
styled.div.something``
styled(OtherComponent).something``

@kitten
Copy link
Member

kitten commented Apr 28, 2017

@vdanchenkov I think we can solve that with the webpack plugin — once I actually get to writing it — since we want to run our transformations before typescript etc

@raphaelsaunier
Copy link

I came to the same conclusions as @vdanchenkov after running into issues when using it with a next.js application.

Since, in this case, the recommendation is to first compile the TypeScript sources before letting Babel apply its transformations, it might be worth tweaking the detector after all?

alloy added a commit to artsy/reaction that referenced this issue May 30, 2017
Otherwise tsc outputs `require` calls, and Babel doesn’t register those
separately. The styled-components Babel plugin uses registered imports
to find the name of the namespace variable, which in the case of tsc is
something like `styled_components_1`. Without finding the namespace
variable, the styled-components plugin will not be able to recognise the
`styled` calls and apply the correct transformations.

See styled-components/babel-plugin-styled-components#41
alloy added a commit to artsy/reaction that referenced this issue Jun 3, 2017
Otherwise tsc outputs `require` calls, and Babel doesn’t register those
separately. The styled-components Babel plugin uses registered imports
to find the name of the namespace variable, which in the case of tsc is
something like `styled_components_1`. Without finding the namespace
variable, the styled-components plugin will not be able to recognise the
`styled` calls and apply the correct transformations.

See styled-components/babel-plugin-styled-components#41
alloy added a commit to artsy/reaction that referenced this issue Jun 8, 2017
Otherwise tsc outputs `require` calls, and Babel doesn’t register those
separately. The styled-components Babel plugin uses registered imports
to find the name of the namespace variable, which in the case of tsc is
something like `styled_components_1`. Without finding the namespace
variable, the styled-components plugin will not be able to recognise the
`styled` calls and apply the correct transformations.

See styled-components/babel-plugin-styled-components#41
@Igorbek
Copy link

Igorbek commented Jun 15, 2017

I'm currently working on a plugin for TypeScript that would do mostly the same as this one, so that typescript users don't need to use Babel (in most cases it's problematic). The prototype is working pretty well, however nothing is documented, that's next on the list: https://github.com/Igorbek/typescript-plugin-styled-components

@kitten
Copy link
Member

kitten commented Jun 15, 2017

@Igorbek since in v3 we'll have a webpack plugin as well, this issue will be solved then as well. I don't see a lot of trouble anymore going forward with TS. Keep in mind? That the plugin will still need to be lean, since this plugin will probably be either mostly or fully replaced, depending on the implementation of the preprocessing. The library for that is planned to be compatible with other css-in-js libs as well

@Igorbek
Copy link

Igorbek commented Jun 15, 2017

@philpl interesting, where can I read plans/discussions about future plans for v3 and webpack plugin that would be helpful for TS users? I've heard of idea of css preprocessing and saving it into faster data structure. is that it?

@kitten
Copy link
Member

kitten commented Jun 15, 2017

@Igorbek I'm going to touch base with Sunil tomorrow to actually discuss the preprocessor lib and it's spec. There's also a spec for a CSS AST for css-in-js 😉 cssinjs/istf-spec#17

Other than that there's not much yet.

We're already doing css preprocessing in this plugin, but we'll make it so that it can run during the runtime as well, making updates to components faster. The whole point is basically that the build-step and the run-time will be able to share the same code.

@huy-nguyen
Copy link

huy-nguyen commented Jun 21, 2017

I ran into this same problem while using typescript 2.3 and used @vdanchenkov's solution and it worked. However, today after upgrading to typescript 2.4 and changing the module option in tsconfig.json to esnext, this problem goes away i.e. import styled from 'styled-components' doesn't interfere with this babel plugin anymore.

@cryve
Copy link

cryve commented Aug 6, 2018

Thanks a lot @huy-nguyen! With "module": "esnext" in tsconfig.json I was able to get babel-plugin-styled-components to work with the following setup:

  • parcel-bundler 1.9.7
  • typescript 3.0.1
  • styled-components 3.3.3
  • babel-plugin-styled-components 1.5.1

@kitten
Copy link
Member

kitten commented Aug 6, 2018

Just as a quick note here, it’s now possible to run the Typescript checker separately and instead of using it to also transpilation one can nowbuse Babel which is a lot less tedious ✨ https://babeljs.io/docs/en/next/babel-preset-typescript.html

cc @probablyup: Should we close this and maybe file an issue to add this approach to the readme?

@quantizor
Copy link
Collaborator

quantizor commented Aug 6, 2018 via email

@kitten kitten closed this as completed Aug 6, 2018
kotarella1110 added a commit to kotarella1110-sandbox/react-ts-boilerplate that referenced this issue Sep 10, 2018
react-app-rewire-styled-components と
react-app-rewire-styled-components-typescript を適用し styled-components
の displayName: true に設定。
Webpack 環境下だけでなく Jest 環境下でも適用したいが、単に .babelrc
を作成し、plugin を適用しても上手くいかない。
kulshekhar/ts-jest#480
styled-components/babel-plugin-styled-components#41
@fbenedetti-zinio
Copy link

Following the Microsoft post (https://blogs.msdn.microsoft.com/typescript/2018/08/27/typescript-and-babel-7/) the Typescript compiler is still the preferred solution over Babel. Indeed it would have a huge impact for our stack to migrate to babel (some of Typescript syntax is not even supported by the Babel compiler). Is there any chance to solve the issue without the need of configuring Babel?

@quantizor
Copy link
Collaborator

quantizor commented Sep 19, 2018 via email

@Igorbek
Copy link

Igorbek commented Sep 19, 2018

Here's it https://github.com/Igorbek/typescript-plugin-styled-components

@quantizor
Copy link
Collaborator

quantizor commented Sep 19, 2018 via email

@Igorbek
Copy link

Igorbek commented Sep 19, 2018

It is not. I can make PR to the docs site, if you don't mind.

@quantizor
Copy link
Collaborator

quantizor commented Sep 19, 2018 via email

@fbenedetti-zinio
Copy link

fbenedetti-zinio commented Sep 20, 2018

Igor's plugin worked well on webpack, but I use directly the typescript compiler to transpile the server code. If I understand correctly I need to setup a compiler host in order to pass the transformer the same way Igor documented for ts-loader.

What is the reason why the styled-component classnames cannot be customized by the framework itself?

@quantizor
Copy link
Collaborator

@fbenedetti-zinio upvote this if you want tsc plugins

What is the reason why the styled-component classnames cannot be customized by the framework itself?

It's not about customization, it's about knowing the file metadata to be able to create unique classNames for the environment.

@cloutiertyler
Copy link

@probablyup Is there no way to do it with just the class metadata alone, plus some randomness? As far as I can tell it's impossible to get debugging working with Typescript, StyledComponents, and SSR.

Even just a little hint at what classes the elements are would go a very long way.

@quantizor
Copy link
Collaborator

Nope. We’ve tried. The only way to ensure total uniqueness is to also include file path in the hashing because they’re guaranteed to be unique.

displayName + variable name combinations can collide relatively easily.

@quantizor
Copy link
Collaborator

Unique and more importantly, stable.

@cloutiertyler
Copy link

Thanks for the quick response! That's the pits though. :/

@cloutiertyler
Copy link

cloutiertyler commented Sep 21, 2018

@probablyup There's no way to hash the content of the styles to arrive at a unique class name is there? That would be both unique and stable, right? e.g. MyComponent-<md5("width:100%;")>

I suppose it would be less stable than file names, since the content changes a lot more frequently.

Admittedly, I definitely don't know the full scope of the problem, so I'm kind of drive-by engineering here.

@quantizor
Copy link
Collaborator

quantizor commented Sep 21, 2018 via email

@correttojs
Copy link

I'm trying to modify the Igor's plugin adding a componentId config. Would it work if I hash the file path and the content?

@quantizor
Copy link
Collaborator

quantizor commented Sep 22, 2018 via email

@correttojs
Copy link

I was able to setup SSR with typescript updating the Iogr's plugin.
I added the componentId to the withConfig, in a similar way the babel plugin works.
In addition, as I'm not using webpack for the server compilation, I found https://github.com/cevek/ttypescript to transpile using the same transformer.
I created a PR if anyone is interested.

@cloutiertyler
Copy link

cloutiertyler commented Oct 5, 2018

Just out of curiosity @probablyup, it looks like styled-components already chooses the name based on the content of the CSS of the component:

https://github.com/styled-components/styled-components/blob/5bddab8c0a3272c8b617600d10727c155224186d/src/models/ComponentStyle.js#L89

It just evaluates the dynamic rules and flattens the whole thing. What does the file path have to do with this again?

I'm sorry for all the questions, but I legitimately do not understand the need for the babel plugins or whether I need to compile my typescript with them for both the bundle I send to the client and also the compiled code I run in node JS. I cannot for the life of me figure out what the devil those plugins do. Why would transpiling the JS code for the client bundle change the output classNames of the code when run? What is the plugin actually changing to make the names consistent on the client and server?

Also, how could adding a human readable name to an already unique string make it neither unique or stable?

@mxstbr
Copy link
Member

mxstbr commented Oct 6, 2018

it looks like styled-components already chooses the name based on the content of the CSS of the component:

@cloutiertyler yes, but that is the instance-specific class. Every component has two classes, one is the instance-specific one and one is shared between all instances of the same component. Unfortunately the hash will differ between different instances, so we need to create that shared class somehow else, and for that we need to know the name of the component, which JavaScript the language doesn't let us do. That's why we need a build-time transform.

See styled-components/styled-components#2064 (comment) for more details.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests