Skip to content

Commit

Permalink
chore: code refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
59naga committed Jul 1, 2018
1 parent 92046d1 commit 9bb6101
Showing 1 changed file with 81 additions and 65 deletions.
146 changes: 81 additions & 65 deletions src/index.js
@@ -1,78 +1,94 @@
class AssignmentReminder {
constructor () {
this.hasExportDefault = false
this.hasExportNamed = false
}
evaluateExpression (path, property = 'expression') {
// Not `exports.anything`, skip
if (!path.get(`${property}.left`).node || !path.get(`${property}.left.object`).node) {
return
}

const objectName = path.get(`${property}.left.object.name`).node
const propertyName = path.get(`${property}.left.property.name`).node
const name = `${objectName}.${propertyName}`
if (name === 'exports.default') {
this.hasExportDefault = true
} else if (objectName === 'exports') {
this.hasExportNamed = true
}
}
}

module.exports = ({types}) => ({
visitor: {
module.exports = ({template, types}) => {
let pluginOptions
const visitor = {
CallExpression: {
exit (path, state) {
exit (path) {
// Not `Object.defineProperty`, skip
if (!path.get('callee.object').node) {
return
}

const callee = `${path.get('callee.object.name').node}.${path.get('callee.property.name').node}`
const args = path.get('arguments').map(path => {
if (path.isStringLiteral()) {
return path.get('value').node
}
if (path.get('properties').length) {
return `${path.get('properties.0.key.name').node}:${path.get('properties.0.value.value').node}`
}
}).join(' ')
if (isExportsModuleDeclaration(path)) {
const finder = new ExportFinder(path)

const isLegacyModule = `${callee}${args}` === 'Object.defineProperty __esModule value:true'
if (isLegacyModule) {
const reminder = new AssignmentReminder()
const program = path.parentPath.parentPath
program.get('body').forEach((path) => {
if (path.isVariableDeclaration()) {
reminder.evaluateExpression(path.get('declarations.0'), 'init')
} else {
if (path.isExpressionStatement() && path.get('expression').isAssignmentExpression()) {
reminder.evaluateExpression(path)
}
}
})
if (finder.isOnlyDefaultExport()) {
const root = finder.getRootNode()

if (reminder.hasExportDefault && !reminder.hasExportNamed) {
program.pushContainer('body', [
types.expressionStatement(types.assignmentExpression(
'=',
types.memberExpression(types.identifier('module'), types.identifier('exports')),
types.memberExpression(types.identifier('exports'), types.stringLiteral('default'), true)
))
])
}
if (state.opts.addDefaultProperty) {
program.pushContainer('body', [
types.expressionStatement(types.assignmentExpression(
'=',
types.memberExpression(types.memberExpression(types.identifier('module'), types.identifier('exports')), types.identifier('default')),
types.memberExpression(types.identifier('exports'), types.stringLiteral('default'), true)
))
])
// HACK: `program.node.body.push` instead of pushContainer(due doesn't work in Plugin.post)
root.node.body.push(template('module.exports = exports.default')())
if (pluginOptions.addDefaultProperty) {
root.node.body.push(template('module.exports.default = exports.default')())
}
}
}
}
}
}
})

return {
visitor: {
Program (path, state) {
// HACK: can't get plugin options in Plugin.post
pluginOptions = state.opts
}
},
post (fileMap) {
fileMap.path.traverse(visitor)
}
}
}

function isExportsModuleDeclaration (path) {
const callee = `${path.get('callee.object.name').node}.${path.get('callee.property.name').node}`
const args = path.get('arguments').map(path => {
if (path.isStringLiteral()) {
return path.get('value').node
}
if (path.get('properties').length) {
return `${path.get('properties.0.key.name').node}:${path.get('properties.0.value.value').node}`
}
}).join(' ')

// TODO: support loose syntax ` exports.__esModule = true;`
return `${callee}${args}` === 'Object.defineProperty __esModule value:true'
}

class ExportFinder {
constructor (path) {
this.path = path
this.hasExportDefault = false
this.hasExportNamed = false
}
getRootNode () {
return this.path.parentPath.parentPath
}
isOnlyDefaultExport () {
this.getRootNode().get('body').forEach((path) => {
if (path.isVariableDeclaration()) {
this.findExport(path.get('declarations.0'), 'init')
} else {
if (path.isExpressionStatement() && path.get('expression').isAssignmentExpression()) {
this.findExport(path)
}
}
})
return this.hasExportDefault && !this.hasExportNamed
}
findExport (path, property = 'expression') {
// Not `exports.anything`, skip
if (!path.get(`${property}.left`).node || !path.get(`${property}.left.object`).node) {
return
}

const objectName = path.get(`${property}.left.object.name`).node
const propertyName = path.get(`${property}.left.property.name`).node
if (objectName === 'exports') {
if (propertyName === 'default') {
this.hasExportDefault = true
} else {
this.hasExportNamed = true
}
}
return null
}
}

0 comments on commit 9bb6101

Please sign in to comment.