-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(macro): add babel-plugin-macros compatible macro (#18)
- Loading branch information
Kent C. Dodds
committed
Jan 31, 2018
1 parent
7ea6289
commit c408be4
Showing
10 changed files
with
310 additions
and
134 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module.exports = require('./dist/macro') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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' | ||
})(); | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
`, | ||
}, | ||
], | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.