Skip to content

Instantly share code, notes, and snippets.

@shawnbot
Last active April 12, 2018 18:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shawnbot/5c02e077dd063ee16ab3494eb897b987 to your computer and use it in GitHub Desktop.
Save shawnbot/5c02e077dd063ee16ab3494eb897b987 to your computer and use it in GitHub Desktop.
const invariant = require('invariant')
const HIDE = 'd-none'
const SHOW = ['d-block', 'd-flex', 'd-inline', 'd-inline-block']
const DISPLAY = [HIDE].concat(SHOW)
module.exports = function displayClassToHidden(fileInfo, api, options) {
const j = api.jscodeshift
let replaced = false
const source = j(fileInfo.source)
.find(j.CallExpression)
.filter(node => {
const arg0 = node.value.arguments[0]
const {object} = node.value.callee
return object && object.property && object.property.name === 'classList'
&& arg0 && arg0.type === 'Literal' && DISPLAY.some(klass => arg0.value === klass)
})
.forEach((node, i) => {
const {parentPath} = node
const {property} = node.value.callee
const parentNode = parentPath.node
const args = node.value.arguments
const klass = node.value.arguments[0].value
let hidden
if (property.name === 'contains') {
if (klass === HIDE) {
node.replace(
j.memberExpression(
node.value.callee.object.object,
j.identifier('hidden')
)
)
} else {
// TODO: replace
// > this.classList.contains('d-block')
// with:
// > !this.hidden
// ???
throw new Error(
`Sorry, we don't support transforming classList.contains('d-<block|flex|inline|inline-block>') yet`
)
}
replaced = true
return
} else {
}
let target = node
let object = node.value.callee.object.object
if (parentNode.type === 'ExpressionStatement') {
target = parentPath
object = parentNode.expression.callee.object.object
} else {
// console.warn(`target: ${j(target).toSource()}`)
// console.warn(`object: ${j(object).toSource()}`)
}
invariant(object,
`no node.value.callee.object (parentNode.type == "${parentNode.type}")`)
switch (property.name) {
case 'add':
if (args.length !== 1) {
console.warn("Can't convert classList.add() with %d args", args.length)
return
}
hidden = j.literal(klass === HIDE)
break
case 'remove':
if (args.length !== 1) {
console.warn("Can't convert classList.remove() with %d args", args.length)
return
} else if (klass !== HIDE) {
console.warn("WARNING: classList.remove('%s') does not *necessarily* imply hidden = true", klass)
}
hidden = j.literal(klass !== HIDE)
break
case 'toggle':
let value = node.value.arguments[1]
hidden = (klass === HIDE)
? value
: j.unaryExpression('!', value, true)
break
default:
console.warn(`unrecognized classList method call: ${property.name}()`)
return
}
if (hidden) {
target.replace(
j.expressionStatement(
j.assignmentExpression(
'=',
j.memberExpression(
object,
j.identifier('hidden')
),
hidden
)
)
)
replaced = true
return
}
})
.toSource(options)
return replaced
// FIXME recast inserts semicolons UGGGHHHH
? source.replace(/;(\n|$|\))/g, (_, c) => c)
: source
}
Object.assign(module.exports, {SHOW, HIDE})
const {defineInlineTest} = require('jscodeshift/dist/testUtils')
const transform = require('./displayClassToHidden')
const {SHOW, HIDE} = transform
describe(`classList.add('d-*') => {hidden = ...}`, () => {
defineInlineTest(transform, {},
`this.classList.add('d-none')`,
`this.hidden = true`,
`classList.add('d-none') => {hidden = true}`)
defineInlineTest(transform, {},
`els.forEach(el => el.classList.add('d-none'))`,
`els.forEach(el => el.hidden = true)`,
`classList.add('d-none') => {hidden = true}`)
defineInlineTest(transform, {},
`this.querySelector('g-emoji').classList.add('d-none')`,
`this.querySelector('g-emoji').hidden = true`,
`classList.add('d-none') => {hidden = true} (with complex expression)`)
for (const klass of SHOW) {
defineInlineTest(transform, {},
`this.classList.add('${klass}')`,
`this.hidden = false`,
`classList.add('${klass}') => {hidden = false}`)
}
// make sure that we don't transform unqualified classList identifiers
defineInlineTest(transform, {},
`classList.add('d-none')`,
`classList.add('d-none')`,
`does not transform unqualified classList identifiers`)
xdescribe('...with mixed display and non-display classes', () => {
defineInlineTest(transform, {},
`this.classList.add('d-none', 'x-foo')`,
`this.hidden = true;\nthis.classList.add('x-foo')`)
})
})
describe(`classList.remove('d-*') => {hidden = ...}`, () => {
defineInlineTest(transform, {},
`this.classList.remove('d-none')`,
`this.hidden = false`,
`classList.remove('d-none') => {hidden = false}`)
for (const klass of SHOW) {
defineInlineTest(transform, {},
`this.classList.remove('${klass}')`,
`this.hidden = true`,
`classList.add('${klass}') => {hidden = true}`)
}
xdescribe('...with mixed display and non-display classes', () => {
defineInlineTest(transform, {},
`this.classList.remove('d-none', 'x-foo')`,
`this.hidden = false;\nthis.classList.add('x-foo')`)
})
})
describe(`classList.toggle('d-*') => {hidden = ...}`, () => {
defineInlineTest(transform, {},
`this.classList.toggle('d-none', true)`,
`this.hidden = true`,
`classList.toggle('d-none', true) => {hidden = true}`)
defineInlineTest(transform, {},
`this.classList.toggle('d-none', false)`,
`this.hidden = false`,
`classList.toggle('d-none', false) => {hidden = false}`)
defineInlineTest(transform, {},
`this.classList.toggle('d-none', x !== 1)`,
`this.hidden = x !== 1`,
`classList.toggle('d-none', <expression>) => {hidden = <expression>}`)
for (const klass of SHOW) {
defineInlineTest(transform, {},
`this.classList.toggle('${klass}', x === 1)`,
`this.hidden = !(x === 1)`,
`classList.toggle('${klass}', <expr>) => {hidden = !<expr>}`)
}
})
describe(`classList.contains('d-none') => {hidden}`, () => {
defineInlineTest(transform, {},
`if (this.classList.contains('d-none')) return`,
`if (this.hidden) return`,
`classList.contains('d-none') => {hidden} (in an if statement)`)
defineInlineTest(transform, {},
`assert(el.classList.contains('d-none'))`,
`assert(el.hidden)`,
`classList.contains('d-none') => {hidden} (in a call expression)`)
})
{
"name": "github-codeshift",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "jest"
},
"jest": {
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"invariant": "^2.2.4",
"jscodeshift": "^0.5.0"
},
"devDependencies": {
"jest": "^22.4.3"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment