Add a babel-macro (#2032)

* create macro

* add sourceType module when using babel.template

* babel.types needs to be passed down to visitors

* add tests

* add pureAnnotation visitor from babel-plugin-styled-components

* add test with require()

* export 'styled-components/macro'

* add css to macro

* add keyframes to macro

* update test snapshots

* allow to import all helpers from src/index.js

* add error when tags are not used with template literals

* add test : should allow all helpers exported from styled-components

* add injectGlobal to taggedTemplateImports

* add missing tests and refactor tests

* add typescript types

* add flow types

* replace injectGlobal by createGlobalStyle

* just use babel-plugin-styled-components and apply to the whole file

* update babel-plugin-styled-components to 1.8.0

* remove typescript files

* add comments and fix styling of code

* generate allowedImports from src/index.js

* Remove unnecessary test to check allowedImports

* move babel-plugin-styled-components to peerDependencies

* fix tests : add babel-plugin-styled-components to devDependencies

* move babel-plugin-styled-components to deps, with a lenient version

* add macro to files

* add changelog entry
This commit is contained in:
Luc
2018-10-09 05:37:34 +08:00
committed by Evan Jacobs
parent c249cbbdf2
commit 4e40f85134
13 changed files with 558 additions and 3 deletions

View File

@@ -10,6 +10,10 @@ _The format is based on [Keep a Changelog](http://keepachangelog.com/) and this
- Fix how ampersand is handled in self-referential selector combinations, e.g. `& + &` (see [#2071](https://github.com/styled-components/styled-components/pull/2071))
- Add babel macro for full-featured interop with create react app v2+, by [@lucleray](https://github.com/lucleray) (see [#2032](https://github.com/styled-components/styled-components/pull/2032))
When using CRA v2, import styled components from `styled-components/macro` instead to gain all the benefits of [our babel plugin](https://github.com/styled-components/babel-plugin-styled-components).
## [v4.0.0-beta.10] - 2018-10-04
- Add support for `as` to be used with `attrs` for better polymorphism, by [@imbhargav5](https://github.com/imbhargav5) (see [#2055](https://github.com/styled-components/styled-components/pull/2055))

View File

@@ -0,0 +1,32 @@
// flow-typed signature: c336fbc01143bb77e83c42fd26d5321b
// flow-typed version: <<STUB>>/babel-plugin-macros_v2.4.2/flow_v0.73.0
/**
* This is an autogenerated libdef stub for:
*
* 'babel-plugin-macros'
*
* Fill this stub out by replacing all the `any` types.
*
* Once filled out, we encourage you to share your work with the
* community by sending a pull request to:
* https://github.com/flowtype/flow-typed
*/
declare module 'babel-plugin-macros' {
declare module.exports: any;
}
/**
* We include stubs for each file inside this npm package in case you need to
* require those files directly. Feel free to delete any files that aren't
* needed.
*/
declare module 'babel-plugin-macros/dist/index' {
declare module.exports: any;
}
// Filename aliases
declare module 'babel-plugin-macros/dist/index.js' {
declare module.exports: $Exports<'babel-plugin-macros/dist/index'>;
}

View File

@@ -0,0 +1,123 @@
// flow-typed signature: 77ee95ba3387196697c9cdff07b8ff04
// flow-typed version: <<STUB>>/babel-plugin-styled-components_v1.7.1/flow_v0.73.0
/**
* This is an autogenerated libdef stub for:
*
* 'babel-plugin-styled-components'
*
* Fill this stub out by replacing all the `any` types.
*
* Once filled out, we encourage you to share your work with the
* community by sending a pull request to:
* https://github.com/flowtype/flow-typed
*/
declare module 'babel-plugin-styled-components' {
declare module.exports: any;
}
/**
* We include stubs for each file inside this npm package in case you need to
* require those files directly. Feel free to delete any files that aren't
* needed.
*/
declare module 'babel-plugin-styled-components/lib/css/placeholderUtils' {
declare module.exports: any;
}
declare module 'babel-plugin-styled-components/lib/index' {
declare module.exports: any;
}
declare module 'babel-plugin-styled-components/lib/minify/index' {
declare module.exports: any;
}
declare module 'babel-plugin-styled-components/lib/utils/detectors' {
declare module.exports: any;
}
declare module 'babel-plugin-styled-components/lib/utils/getName' {
declare module.exports: any;
}
declare module 'babel-plugin-styled-components/lib/utils/hash' {
declare module.exports: any;
}
declare module 'babel-plugin-styled-components/lib/utils/options' {
declare module.exports: any;
}
declare module 'babel-plugin-styled-components/lib/utils/prefixDigit' {
declare module.exports: any;
}
declare module 'babel-plugin-styled-components/lib/visitors/assignStyledRequired' {
declare module.exports: any;
}
declare module 'babel-plugin-styled-components/lib/visitors/displayNameAndId' {
declare module.exports: any;
}
declare module 'babel-plugin-styled-components/lib/visitors/minify' {
declare module.exports: any;
}
declare module 'babel-plugin-styled-components/lib/visitors/pure' {
declare module.exports: any;
}
declare module 'babel-plugin-styled-components/lib/visitors/templateLiterals/index' {
declare module.exports: any;
}
declare module 'babel-plugin-styled-components/lib/visitors/templateLiterals/transpile' {
declare module.exports: any;
}
// Filename aliases
declare module 'babel-plugin-styled-components/lib/css/placeholderUtils.js' {
declare module.exports: $Exports<'babel-plugin-styled-components/lib/css/placeholderUtils'>;
}
declare module 'babel-plugin-styled-components/lib/index.js' {
declare module.exports: $Exports<'babel-plugin-styled-components/lib/index'>;
}
declare module 'babel-plugin-styled-components/lib/minify/index.js' {
declare module.exports: $Exports<'babel-plugin-styled-components/lib/minify/index'>;
}
declare module 'babel-plugin-styled-components/lib/utils/detectors.js' {
declare module.exports: $Exports<'babel-plugin-styled-components/lib/utils/detectors'>;
}
declare module 'babel-plugin-styled-components/lib/utils/getName.js' {
declare module.exports: $Exports<'babel-plugin-styled-components/lib/utils/getName'>;
}
declare module 'babel-plugin-styled-components/lib/utils/hash.js' {
declare module.exports: $Exports<'babel-plugin-styled-components/lib/utils/hash'>;
}
declare module 'babel-plugin-styled-components/lib/utils/options.js' {
declare module.exports: $Exports<'babel-plugin-styled-components/lib/utils/options'>;
}
declare module 'babel-plugin-styled-components/lib/utils/prefixDigit.js' {
declare module.exports: $Exports<'babel-plugin-styled-components/lib/utils/prefixDigit'>;
}
declare module 'babel-plugin-styled-components/lib/visitors/assignStyledRequired.js' {
declare module.exports: $Exports<'babel-plugin-styled-components/lib/visitors/assignStyledRequired'>;
}
declare module 'babel-plugin-styled-components/lib/visitors/displayNameAndId.js' {
declare module.exports: $Exports<'babel-plugin-styled-components/lib/visitors/displayNameAndId'>;
}
declare module 'babel-plugin-styled-components/lib/visitors/minify.js' {
declare module.exports: $Exports<'babel-plugin-styled-components/lib/visitors/minify'>;
}
declare module 'babel-plugin-styled-components/lib/visitors/pure.js' {
declare module.exports: $Exports<'babel-plugin-styled-components/lib/visitors/pure'>;
}
declare module 'babel-plugin-styled-components/lib/visitors/templateLiterals/index.js' {
declare module.exports: $Exports<'babel-plugin-styled-components/lib/visitors/templateLiterals/index'>;
}
declare module 'babel-plugin-styled-components/lib/visitors/templateLiterals/transpile.js' {
declare module.exports: $Exports<'babel-plugin-styled-components/lib/visitors/templateLiterals/transpile'>;
}

7
macro/package.json Normal file
View File

@@ -0,0 +1,7 @@
{
"name": "styled-components/macro",
"private": true,
"main": "../dist/styled-components-macro.cjs.js",
"module": "../dist/styled-components-macro.esm.js",
"jsnext:main": "../dist/styled-components-macro.esm.js"
}

View File

@@ -47,7 +47,8 @@
"native",
"primitives",
"scripts",
"test-utils"
"test-utils",
"macro"
],
"keywords": [
"react",
@@ -63,6 +64,7 @@
"homepage": "https://styled-components.com",
"dependencies": {
"@emotion/is-prop-valid": "^0.6.8",
"babel-plugin-styled-components": ">= 1",
"css-to-react-native": "^2.2.2",
"memoize-one": "^4.0.0",
"prop-types": "^15.5.4",
@@ -76,7 +78,9 @@
"babel-eslint": "^10.0.1",
"babel-plugin-add-module-exports": "^1.0.0",
"babel-plugin-external-helpers": "^6.22.0",
"babel-plugin-macros": "^2.4.2",
"babel-plugin-preval": "^3.0.1",
"babel-plugin-tester": "^5.5.1",
"babel-plugin-transform-class-properties": "^6.22.0",
"babel-plugin-transform-object-rest-spread": "^6.22.0",
"babel-plugin-transform-react-remove-prop-types": "0.4.14",

View File

@@ -165,6 +165,14 @@ const primitivesConfig = {
),
};
const macroConfig = Object.assign({}, configBase, {
input: './src/macro/index.js',
output: [
getESM({ file: 'dist/styled-components-macro.esm.js' }),
getCJS({ file: 'dist/styled-components-macro.cjs.js' }),
],
});
export default [
standaloneConfig,
standaloneProdConfig,
@@ -172,4 +180,5 @@ export default [
browserConfig,
nativeConfig,
primitivesConfig,
macroConfig,
];

52
src/macro/index.js Normal file
View File

@@ -0,0 +1,52 @@
// @flow
import { createMacro, MacroError } from 'babel-plugin-macros';
import babelPlugin from 'babel-plugin-styled-components';
import * as styled from '..';
const allowedImports: Array<string> = Object.keys(styled).filter(helper => helper !== '__esModule');
function styledComponentsMacro({ references, state, babel: { types: t }, config = {} }) {
const program = state.file.path;
// replace `styled-components/macro` by `styled-components`
// create a node for styled-components's imports
const imports = t.importDeclaration([], t.stringLiteral('styled-components'));
// and add it to top of the document
program.node.body.unshift(imports);
// references looks like this
// { default: [path, path], css: [path], ... }
Object.keys(references).forEach(refName => {
if (!allowedImports.includes(refName)) {
throw new MacroError(
`Invalid import: ${refName}. You can only import ${allowedImports.join(
', '
)} from 'styled-components/macro'.`
);
}
// generate new identifier and add to imports
let id;
if (refName === 'default') {
id = program.scope.generateUidIdentifier('styled');
imports.specifiers.push(t.importDefaultSpecifier(id));
} else {
id = program.scope.generateUidIdentifier(refName);
imports.specifiers.push(t.importSpecifier(id, t.identifier(refName)));
}
// update references with the new identifiers
references[refName].forEach(referencePath => {
// eslint-disable-next-line no-param-reassign
referencePath.node.name = id.name;
});
});
// apply babel-plugin-styled-components to the file
const stateWithOpts = { ...state, opts: config };
program.traverse(babelPlugin({ types: t }).visitor, stateWithOpts);
}
const configName = 'styledComponents';
export default createMacro(styledComponentsMacro, { configName });

View File

@@ -0,0 +1,140 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`macro should work when extending a component: should work when extending a component 1`] = `
"
import React from 'react'
import styled from '../../macro'
const Hello = () => React.createComponent(div, null, 'hello')
styled(Hello)\`
background: red;
\`
↓ ↓ ↓ ↓ ↓ ↓
import _styled from 'styled-components';
import React from 'react';
const Hello = () => React.createComponent(div, null, 'hello');
_styled(Hello).withConfig({
displayName: 'macrotest',
componentId: 'sc-11gvsec-0'
})(['background:red;']);
"
`;
exports[`macro should work with { ThemeProvider }: should work with { ThemeProvider } 1`] = `
"
import { ThemeProvider } from '../../macro'
React.createComponent(
ThemeProvider,
{ theme: { color: 'red' }},
'hello'
)
↓ ↓ ↓ ↓ ↓ ↓
import { ThemeProvider as _ThemeProvider } from 'styled-components';
React.createComponent(_ThemeProvider, { theme: { color: 'red' } }, 'hello');
"
`;
exports[`macro should work with { createGlobalStyle }: should work with { createGlobalStyle } 1`] = `
"
import { createGlobalStyle } from '../../macro'
createGlobalStyle\`
background: red;
\`
↓ ↓ ↓ ↓ ↓ ↓
import { createGlobalStyle as _createGlobalStyle } from 'styled-components';
_createGlobalStyle\`
background: red;
\`;
"
`;
exports[`macro should work with { css }: should work with { css } 1`] = `
"
import { css } from '../../macro'
css\`
color: \${props => (props.whiteColor ? 'white' : 'black')};
\`
↓ ↓ ↓ ↓ ↓ ↓
import { css as _css } from 'styled-components';
_css(['color:', ';'], props => props.whiteColor ? 'white' : 'black');
"
`;
exports[`macro should work with { keyframes }: should work with { keyframes } 1`] = `
"
import { keyframes } from '../../macro'
keyframes\`
0% { opacity: 0; }
100% { opacity: 1; }
\`
↓ ↓ ↓ ↓ ↓ ↓
import { keyframes as _keyframes } from 'styled-components';
_keyframes(['0%{opacity:0;}100%{opacity:1;}']);
"
`;
exports[`macro should work with require() to import styled-components: should work with require() to import styled-components 1`] = `
"
const myStyled = require('../../macro')
myStyled.div\`
background: red;
\`
↓ ↓ ↓ ↓ ↓ ↓
import _styled from 'styled-components';
_styled.div.withConfig({
displayName: 'macrotest',
componentId: 'sc-11gvsec-0'
})(['background:red;']);
"
`;
exports[`macro should work with styled: should work with styled 1`] = `
"
import styled from '../../macro'
styled.div\`
background: \${p => (p.error ? 'red' : 'green')};
\`
↓ ↓ ↓ ↓ ↓ ↓
import _styled from 'styled-components';
_styled.div.withConfig({
displayName: 'macrotest',
componentId: 'sc-11gvsec-0'
})(['background:', ';'], p => p.error ? 'red' : 'green');
"
`;

View File

@@ -0,0 +1,89 @@
import pluginTester from 'babel-plugin-tester';
import plugin from 'babel-plugin-macros';
const styledExampleCode = `
import styled from '../../macro'
styled.div\`
background: \${p => (p.error ? 'red' : 'green')};
\`
`;
const cssExampleCode = `
import { css } from '../../macro'
css\`
color: \${props => (props.whiteColor ? 'white' : 'black')};
\`
`;
const keyframesExampleCode = `
import { keyframes } from '../../macro'
keyframes\`
0% { opacity: 0; }
100% { opacity: 1; }
\`
`;
const createGlobalStyleExampleCode = `
import { createGlobalStyle } from '../../macro'
createGlobalStyle\`
background: red;
\`
`;
const ThemeProviderExampleCode = `
import { ThemeProvider } from '../../macro'
React.createComponent(
ThemeProvider,
{ theme: { color: 'red' }},
'hello'
)
`;
const extendsExampleCode = `
import React from 'react'
import styled from '../../macro'
const Hello = () => React.createComponent(div, null, 'hello')
styled(Hello)\`
background: red;
\`
`;
const requireExampleCode = `
const myStyled = require('../../macro')
myStyled.div\`
background: red;
\`
`;
const invalidExampleCode = `
import { UnknownImport } from '../../macro'
`;
pluginTester({
title: 'macro',
plugin,
snapshot: true,
babelOptions: { filename: __filename },
tests: {
'should work with styled': styledExampleCode,
'should work with { css }': cssExampleCode,
'should work with { keyframes }': keyframesExampleCode,
'should work with { createGlobalStyle }': createGlobalStyleExampleCode,
'should work with { ThemeProvider }': ThemeProviderExampleCode,
'should work when extending a component': extendsExampleCode,
'should work with require() to import styled-components': requireExampleCode,
'should throw error when importing { UnknownImport }': {
code: invalidExampleCode,
error: true,
snapshot: false,
},
},
});

View File

@@ -0,0 +1,5 @@
{
"styledComponents": {
"ssr": false
}
}

View File

@@ -0,0 +1,20 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`macro with config should not add componentId with a config disabling ssr: should not add componentId with a config disabling ssr 1`] = `
"
import styled from '../../../macro'
styled.div\`
background: red;
\`
↓ ↓ ↓ ↓ ↓ ↓
import _styled from 'styled-components';
_styled.div.withConfig({
displayName: 'macro-with-configtest'
})(['background:red;']);
"
`;

View File

@@ -0,0 +1,20 @@
import pluginTester from 'babel-plugin-tester';
import plugin from 'babel-plugin-macros';
const exampleCode = `
import styled from '../../../macro'
styled.div\`
background: red;
\`
`;
pluginTester({
title: 'macro with config',
plugin,
snapshot: true,
babelOptions: { filename: __filename },
tests: {
'should not add componentId with a config disabling ssr': exampleCode,
},
});

View File

@@ -60,6 +60,12 @@
dependencies:
"@babel/types" "7.0.0-beta.47"
"@babel/helper-annotate-as-pure@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz#323d39dd0b50e10c7c06ca7d7638e6864d8c5c32"
dependencies:
"@babel/types" "^7.0.0"
"@babel/helper-builder-binary-assignment-operator-visitor@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.0.0-beta.47.tgz#d5917c29ee3d68abc2c72f604bc043f6e056e907"
@@ -546,7 +552,15 @@
lodash "^4.17.5"
to-fast-properties "^2.0.0"
"@babel/types@^7.0.0", "@babel/types@^7.1.2":
"@babel/types@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0.tgz#6e191793d3c854d19c6749989e3bc55f0e962118"
dependencies:
esutils "^2.0.2"
lodash "^4.17.10"
to-fast-properties "^2.0.0"
"@babel/types@^7.1.2":
version "7.1.2"
resolved "https://registry.npmjs.org/@babel/types/-/types-7.1.2.tgz#183e7952cf6691628afdc2e2b90d03240bac80c0"
dependencies:
@@ -1181,6 +1195,13 @@ babel-plugin-macros@^2.2.2:
dependencies:
cosmiconfig "^5.0.5"
babel-plugin-macros@^2.4.2:
version "2.4.2"
resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.4.2.tgz#21b1a2e82e2130403c5ff785cba6548e9b644b28"
dependencies:
cosmiconfig "^5.0.5"
resolve "^1.8.1"
babel-plugin-preval@^3.0.1:
version "3.0.1"
resolved "https://registry.npmjs.org/babel-plugin-preval/-/babel-plugin-preval-3.0.1.tgz#a26f9690114a864a54a5cbdf865496ebf541a9c3"
@@ -1188,6 +1209,13 @@ babel-plugin-preval@^3.0.1:
babel-plugin-macros "^2.2.2"
require-from-string "^2.0.2"
"babel-plugin-styled-components@>= 1":
version "1.8.0"
resolved "https://registry.yarnpkg.com/babel-plugin-styled-components/-/babel-plugin-styled-components-1.8.0.tgz#9dd054c8e86825203449a852a5746f29f2dab857"
dependencies:
"@babel/helper-annotate-as-pure" "^7.0.0"
lodash "^4.17.10"
babel-plugin-syntax-async-functions@^6.8.0:
version "6.13.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95"
@@ -1216,6 +1244,16 @@ babel-plugin-syntax-trailing-function-commas@^6.22.0, babel-plugin-syntax-traili
version "6.22.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz#ba0360937f8d06e40180a43fe0d5616fff532cf3"
babel-plugin-tester@^5.5.1:
version "5.5.1"
resolved "https://registry.yarnpkg.com/babel-plugin-tester/-/babel-plugin-tester-5.5.1.tgz#93648d140e67a81fa8474a73d3382b4fd8e91ca6"
dependencies:
common-tags "^1.4.0"
invariant "^2.2.2"
lodash.merge "^4.6.0"
path-exists "^3.0.0"
strip-indent "^2.0.0"
babel-plugin-transform-async-to-generator@^6.22.0:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz#6536e378aff6cb1d5517ac0e40eb3e9fc8d08761"
@@ -2144,6 +2182,10 @@ commander@~2.13.0:
version "2.13.0"
resolved "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c"
common-tags@^1.4.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937"
commondir@^1.0.1:
version "1.0.1"
resolved "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
@@ -4834,6 +4876,10 @@ lodash.debounce@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
lodash.merge@^4.6.0:
version "4.6.1"
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.1.tgz#adc25d9cb99b9391c59624f379fbba60d7111d54"
lodash.pad@^4.1.0:
version "4.5.1"
resolved "https://registry.yarnpkg.com/lodash.pad/-/lodash.pad-4.5.1.tgz#4330949a833a7c8da22cc20f6a26c4d59debba70"
@@ -6573,7 +6619,7 @@ resolve@1.1.7:
version "1.1.7"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
resolve@^1.1.6, resolve@^1.3.2, resolve@^1.5.0, resolve@^1.6.0:
resolve@^1.1.6, resolve@^1.3.2, resolve@^1.5.0, resolve@^1.6.0, resolve@^1.8.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26"
dependencies:
@@ -7179,6 +7225,10 @@ strip-eof@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
strip-indent@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68"
strip-json-comments@^2.0.1, strip-json-comments@~2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"