Skip to content

Commit

Permalink
feat(macro): add babel-plugin-macros compatible macro (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
Kent C. Dodds committed Jan 31, 2018
1 parent 7ea6289 commit c408be4
Show file tree
Hide file tree
Showing 10 changed files with 310 additions and 134 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ const MyStyledButton = glamorous.button.withConfig({
* [Via `.babelrc` (Recommended)](#via-babelrc-recommended)
* [Via CLI](#via-cli)
* [Via Node API](#via-node-api)
* [Use with `babel-plugin-macros`](#use-with-babel-plugin-macros)
* [Inspiration](#inspiration)
* [Other Solutions](#other-solutions)
* [Contributors](#contributors)
Expand Down Expand Up @@ -101,6 +102,22 @@ require('babel').transform('code', {
})
```

## Use with `babel-plugin-macros`

Once you've [configured `babel-plugin-macros`](https://github.com/kentcdodds/babel-plugin-macros/blob/master/other/docs/user.md)
you can import/require the `glamorous` macro at `babel-plugin-glamorous/macro`.
For example:

```javascript
import glamorous from 'babel-plugin-glamorous/macro'

const MyStyledInput = glamorous.input({
/* your styles */
})
```

> You could also use [`glamorous.macro`][glamorous.macro] if you'd prefer to type less 😀
## Inspiration

* [styled-components](https://github.com/styled-components/babel-plugin-styled-components)
Expand Down Expand Up @@ -154,3 +171,4 @@ MIT
[twitter-badge]: https://img.shields.io/twitter/url/https/github.com/bernard-lin/babel-plugin-glamorous-displayname.svg?style=social
[emojis]: https://github.com/kentcdodds/all-contributors#emoji-key
[all-contributors]: https://github.com/kentcdodds/all-contributors
[glamorous.macro]: https://www.npmjs.com/package/glamorous.macro
1 change: 1 addition & 0 deletions macro.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('./dist/macro')
14 changes: 12 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,28 @@
"setup": "npm install && npm run validate -s",
"precommit": "kcd-scripts precommit"
},
"files": ["dist"],
"keywords": ["babel", "babel-plugin", "react", "glamorous"],
"files": ["dist", "macro.js"],
"keywords": [
"babel",
"babel-plugin",
"react",
"glamorous",
"babel-plugin-macros"
],
"author": "Kent C. Dodds, Bernard Lin",
"license": "MIT",
"dependencies": {
"babel-plugin-macros": "^2.1.0",
"babel-runtime": "^6.26.0"
},
"devDependencies": {
"ast-pretty-print": "^2.0.0",
"babel-plugin-tester": "^4.0.0",
"kcd-scripts": "^0.32.2"
},
"eslintConfig": {
"extends": ["./node_modules/kcd-scripts/eslint.js"]
},
"eslintIgnore": ["node_modules", "coverage", "dist"],
"repository": {
"type": "git",
Expand Down
2 changes: 2 additions & 0 deletions prettier.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// here only so the editor can find our config
module.exports = require('kcd-scripts/dist/config/prettierrc')
60 changes: 60 additions & 0 deletions src/__tests__/__snapshots__/macro.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`macros different name: different name 1`] = `
import g from '../macro'
const MyComp = g.div()
const MyOtherComp = g.span()
↓ ↓ ↓ ↓ ↓ ↓
import g from 'glamorous';
const MyComp = g.div.withConfig({
displayName: 'SomeComponent__MyComp'
})();
const MyOtherComp = g.span.withConfig({
displayName: 'SomeComponent__MyOtherComp'
})();
`;

exports[`macros multiple usages: multiple usages 1`] = `
import glamorous from '../macro'
const MyComp = glamorous.div()
const MyOtherComp = glamorous.span()
↓ ↓ ↓ ↓ ↓ ↓
import glamorous from 'glamorous';
const MyComp = glamorous.div.withConfig({
displayName: 'SomeComponent__MyComp'
})();
const MyOtherComp = glamorous.span.withConfig({
displayName: 'SomeComponent__MyOtherComp'
})();
`;

exports[`macros simple use case: simple use case 1`] = `
import glamorous from '../macro'
const MyComp = glamorous.div()
↓ ↓ ↓ ↓ ↓ ↓
import glamorous from 'glamorous';
const MyComp = glamorous.div.withConfig({
displayName: 'SomeComponent__MyComp'
})();
`;
47 changes: 47 additions & 0 deletions src/__tests__/macro.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import path from 'path'
import pluginTester from 'babel-plugin-tester'
import plugin from 'babel-plugin-macros'

// removes annoying quotes around string-only stuff
expect.addSnapshotSerializer({
print(val) {
return val
},
test(val) {
return typeof val === 'string'
},
})

pluginTester({
plugin,
snapshot: true,
babelOptions: {filename: path.join(__dirname, 'SomeComponent.js')},
tests: [
{
title: 'simple use case',
code: `
import glamorous from '../macro'
const MyComp = glamorous.div()
`,
},
{
title: 'multiple usages',
code: `
import glamorous from '../macro'
const MyComp = glamorous.div()
const MyOtherComp = glamorous.span()
`,
},
{
title: 'different name',
code: `
import g from '../macro'
const MyComp = g.div()
const MyOtherComp = g.span()
`,
},
],
})
116 changes: 116 additions & 0 deletions src/handle-glamorous-references.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// import printAST from 'ast-pretty-print'
import nodePath from 'path'
import looksLike from './looks-like'

function handleGlamorousReferences(references, file, babel) {
const {types: t, template} = babel
const buildBuiltInWithConfig = template(`
GLAMOROUS.BUILT_IN.withConfig({displayName: DISPLAY_NAME})
`)
const buildCustomWithConfig = template(`
GLAMOROUS(ARGUMENTS).withConfig({displayName: DISPLAY_NAME})
`)

references.forEach(reference => {
const displayName = getDisplayName(reference)

handleBuiltIns(reference, displayName)
handleCustomComponent(reference, displayName)
})

function handleBuiltIns(path, displayName) {
const isBuiltIn = looksLike(path, {
parentPath: {
type: 'MemberExpression',
node: {
property: {
type: 'Identifier',
},
},
parent: {
type: 'CallExpression',
},
},
})
if (!isBuiltIn) {
return
}
path.parentPath.replaceWith(
buildBuiltInWithConfig({
GLAMOROUS: path.node,
BUILT_IN: path.parent.property,
DISPLAY_NAME: t.stringLiteral(displayName),
}),
)
}

function handleCustomComponent(path, displayName) {
const isCustom = looksLike(path, {
parent: {
type: 'CallExpression',
},
})
if (!isCustom) {
return
}
path.parentPath.replaceWith(
buildCustomWithConfig({
GLAMOROUS: path.node,
ARGUMENTS: path.parent.arguments,
DISPLAY_NAME: t.stringLiteral(displayName),
}),
)
}

// credit: https://github.com/styled-components/babel-plugin-styled-components/blob/37a13e9c21c52148ce6e403100df54c0b1561a88/src/visitors/displayNameAndId.js
function getDisplayName(path) {
const componentName = getName(path)
const filename = getFileName(file)
if (filename) {
if (filename === componentName) {
return componentName
}
return componentName ? `${filename}__${componentName}` : filename
} else {
return componentName
}
}

// credit: https://github.com/styled-components/babel-plugin-styled-components/blob/37a13e9c21c52148ce6e403100df54c0b1561a88/src/utils/getName.js
function getName(path) {
let namedNode

path.find(parentPath => {
if (parentPath.isObjectProperty()) {
// const X = { Y: glamorous }
namedNode = parentPath.node.key
} else if (parentPath.isVariableDeclarator()) {
// let X; X = glamorous
namedNode = parentPath.node.id
} else if (parentPath.isStatement()) {
// we've hit a statement, we should stop crawling up
return true
}

// we've got an displayName (if we need it) no need to continue
if (namedNode) {
return true
}
return false
})

// identifiers are the only thing we can reliably get a name from
return t.isIdentifier(namedNode) ? namedNode.name : undefined
}
}

function getFileName(file) {
if (!file || file.opts.filename === 'unknown') {
return ''
}
return file.opts.basename === 'index'
? nodePath.basename(nodePath.dirname(file.opts.filename))
: file.opts.basename
}

export default handleGlamorousReferences

0 comments on commit c408be4

Please sign in to comment.