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

Add support for styled-components syntax highlighting #1714

Closed
wants to merge 11 commits into from

Conversation

mxstbr
Copy link

@mxstbr mxstbr commented Jan 23, 2019

styled-components is a widely used CSS-in-JS library. It lets users write their CSS in JavaScript as CSS strings. For example:

const Button = styled.button`
  color: blue;
  background: red;
`

This PR adds support for highlighting the styled-components template strings as CSS.

We have been running this exact code on the styled-components website for almost two years and it has worked perfectly.

I have added tests for the feature and am willing to maintain this going forward.

/cc @kitten @probablyup the other core maintainers of the styled-components package

[styled-components](https://github.com/styled-components/styled-components) is a widely used CSS-in-JS library. It lets users write their CSS in JavaScript as CSS strings. For example:

```
const Button = styled.button`
  color: blue;
  background: red;
`
```

This PR adds support for highlighting the styled-components template strings as CSS.

We have been running this exact code [on the styled-components website for almost two years](https://github.com/styled-components/styled-components-website/blob/bbfcb0d21aaec9e8202609a67e7bba5278042056/utils/prismTemplateString.js) and it has worked perfectly.

I have added tests for the feature and am willing to maintain this going forward.
Copy link
Member

@RunDevelopment RunDevelopment left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for contributing!

But before we can merge this, there are multiple issue we'll have to address.

  1. Dependencies:
    How should this behave if the CSS language is not present?
    I think the new token should not be added to JS if CSS is not present, so we just need to add a peer dependency to CSS in components.json.
  2. Style:
    We ident using tabs and use single quotes for token names.
  3. Redundancy:
    The new token is basically a modified copy of template-string. My problem is that this increases redundancy and makes the language harder to maintain.
  4. Correctness:
    I don't really get why you modified the string regex. It just causes semi-colons and dollar signs to not be highlighted which is a problem if these characters appear in comments or strings (or after every CSS property...).
    I get that white semi-colons look better on your website (examples) but it's simply not right to strangely cut CSS code into pieces IMO.
    Also, there can be any number of spaces between tag and template string.
  5. Please run gulp to create the minified files (and components.js).

My solution for number 3 and 4 would be this:

/**
 * Creates a new pattern to match a template string with a special tag.
 *
 * @param {string} language The language inside the template string.
 * @param {string} tag The regex pattern to match the tag.
 * @returns {object} The new token.
 * @example
 * createTemplate('css', /\bcss/.source);
 * createTemplate(); // template strings without tags
 */
function createTemplate(language, tag) {
    var string = !language ? /[\s\S]+/ : {
        pattern: /[\s\S]+/,
        alias: 'language-' + language,
        inside: Prism.languages[language]
    };

    return {
        pattern: RegExp((tag ? '((?:' + tag + ')\\s*)' : '') + /`(?:\\[\s\S]|\${[^}]+}|[^\\`])*`/.source),
        lookbehind: !!tag,
        greedy: true,
        inside: {
            'interpolation': {
                pattern: /\${[^}]+}/,
                inside: {
                    'interpolation-punctuation': {
                        pattern: /^\${|}$/,
                        alias: 'punctuation'
                    },
                    rest: Prism.languages.javascript
                }
            },
            'string': string
        }
    }
}

This will also allow us to easily add new tagged template strings in the future (like e.g. Polymer's html; not that this should be part of this PR).

components/prism-javascript.js Outdated Show resolved Hide resolved
@mxstbr
Copy link
Author

mxstbr commented Jan 24, 2019

Thank you so much for that in-depth review!

The latest commits clean everything up apart from 3. and 4.—I'm not entirely sure where to put that util and how to use it. Would you mind quickly implementing that? You'd be much faster than me 👍

@mxstbr
Copy link
Author

mxstbr commented Jan 24, 2019

Actually, this does not correctly highlight CSS, right? 🤔

@RunDevelopment
Copy link
Member

Actually, this does not correctly highlight CSS, right? 🤔

What does?

@mxstbr
Copy link
Author

mxstbr commented Jan 24, 2019

I mean, the CSS part inside the tagged template strings is not highlighted as CSS:

styled.button`
  color: blue;
`

It is treated as a string, rather than as CSS. At least in the tests?

@RunDevelopment
Copy link
Member

Well, you did remove the CSS part. so that's to be expected.
But don't worry, PR is on its way.

@mxstbr
Copy link
Author

mxstbr commented Jan 24, 2019

Oh yeah sorry, I totally messed that up 😅

Thank you! 🙏

Copy link
Member

@RunDevelopment RunDevelopment left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

From my point of view, this is ready to merge.

But reviewing my own code... 😅
So I would like @mAAdhaTTah to have the final word on this.

@mAAdhaTTah
Copy link
Member

Sorry for the dlay.

Besides the conflicts we now have, this feels like it might be better as a js-extras language, rather than part of JS code, since most users won't need this functionality.

Thoughts?

@mxstbr
Copy link
Author

mxstbr commented Mar 10, 2019

@mAAdhaTTah if anything, it should be part of JSX since it is React-specific. What would you think about that?

Happy to resolve the conflicts tomorrow if we can agree on moving this to JSX!

@mAAdhaTTah
Copy link
Member

@mxstbr jsx-extras? Since this is targeted at a subset of people who use JS(X), I'd prefer people to opt into it.

@RunDevelopment
Copy link
Member

RunDevelopment commented Mar 10, 2019

Moving this to JS-Extras also means that the createTemplate method which creates the template-string token has to remain in JS. The only lines moved to JS-Extras will be:

if (Prism.languages.css) {
    Prism.languages.javascript['template-string'].unshift(createTemplate('css', /\b(?:styled(?:\([^)]*\))?(?:\.\w+(?:\([^)]*\))*)*|css|createGlobalStyle|keyframes)/.source));
}

Technically, I also think that this feature should be moved of JS-Extras. All three lines of it...

@RunDevelopment
Copy link
Member

Also, JS-Extras has its dependencies set up in a way so that it doesn't affect TypeScript.
This feature should also be available for TypeScript, so we would have to either

  1. Change the dependencies and make JS-Extras compatible with TypeScript.
  2. Make a new language/extension similar to JS-Extras exclusively for JS/TS and JSX/TSX for just this feature (all three lines of it).
  3. Just leave it as it is now...

It might seem like overkill, but I think that option 2 is the best one.

@RunDevelopment
Copy link
Member

RunDevelopment commented Mar 11, 2019

@mAAdhaTTah
Conflicts are resolved.

Regarding offloading the CSS templates:
How about we leave it as it is for now (so as a part of JS) and make a new 'language' (something like JS-Templates) in the future which only handles different languages embedded in template strings?
When adding this new 'language' we can add support for Polymer's html as well.

@karlhorky
Copy link
Contributor

Ah, just opened a new issue which is a superset of this (#1930 - for handling all languages tagged template literals) and didn't see this while searching for similar issues.

I will mention this in the other issue. This could also be a good starting point for the other languages (and also for other CSS-in-JS libs like styled-jsx).

@karlhorky
Copy link
Contributor

karlhorky commented Jun 7, 2019

How about we leave it as it is for now (so as a part of JS)

I would vote to add the eventual change with all languages as part of the main default JS language, since tagged template strings with different languages are common in places other than React / JSX / other frameworks.

@RunDevelopment
Copy link
Member

I'll close this now as all of the changes in this were also part of #1931.

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

Successfully merging this pull request may close these issues.

4 participants