Skip to content

Commit

Permalink
Add no-import-module-exports rule
Browse files Browse the repository at this point in the history
  • Loading branch information
ttmarek committed Apr 14, 2017
1 parent 106740f commit 6f19984
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const rules = {
'no-dynamic-require': require('./rules/no-dynamic-require'),
'unambiguous': require('./rules/unambiguous'),
'no-unassigned-import': require('./rules/no-unassigned-import'),
'no-import-module-exports': require('./rules/no-import-module-exports'),

// metadata-based
'no-deprecated': require('./rules/no-deprecated'),
Expand Down
72 changes: 72 additions & 0 deletions src/rules/no-import-module-exports.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import minimatch from 'minimatch'
import path from 'path'
import readPkgUp from 'read-pkg-up'

function getEntryPoint(context) {
try {
const pkg = readPkgUp.sync({cwd: context.getFilename(), normalize: false})
return pkg && pkg.pkg
? path.join(process.cwd(), pkg.pkg.main)
: null
} catch (e) {
return null
}
}

module.exports = {
meta: {
docs: {
description: 'Disallow import statements with module.exports',
category: 'Best Practices',
recommended: true,
},
fixable: 'code',
schema: [
{
'type': 'object',
'properties': {
'exceptions': { 'type': 'array' },
},
'additionalProperties': false,
},
],
},
create(context) {
const importDeclarations = []
const entryPoint = getEntryPoint(context)
const options = context.options[0] || {}
let alreadyReported = false

function report(node) {
const fileName = context.getFilename()
const isEntryPoint = entryPoint === fileName
const isIdentifier = node.object.type === 'Identifier'
const hasKeywords = (/^(module|exports)$/).test(node.object.name)
const isException = options.exceptions
? options.exceptions.some(glob => minimatch(fileName, glob))
: false

if (isIdentifier && hasKeywords && !isEntryPoint && !isException) {
importDeclarations.forEach(importDeclaration => {
context.report({
node: importDeclaration,
message: `Cannot use import declarations in modules that export using ` +
`CommonJS (module.exports = 'foo' or exports.bar = 'hi')`,
})
})
alreadyReported = true
}
}

return {
ImportDeclaration(node) {
importDeclarations.push(node)
},
MemberExpression(node) {
if (!alreadyReported) {
report(node)
}
},
}
},
}
1 change: 1 addition & 0 deletions tests/files/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"dummy": true,
"main": "tests/files/lib/main.js",
"devDependencies": {
"glob": "1.0.0",
"eslint": "2.x"
Expand Down
98 changes: 98 additions & 0 deletions tests/src/rules/no-import-module-exports.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import path from 'path'
import { RuleTester } from 'eslint'

import { test, testFilePath } from '../utils'

const ruleTester = new RuleTester({
parserOptions: { ecmaVersion: 6, sourceType: 'module' }
})
const rule = require('rules/no-import-module-exports')

const error = {
message: `Cannot use import declarations in modules that export using CommonJS ` +
`(module.exports = 'foo' or exports.bar = 'hi')`,
type: 'ImportDeclaration',
}

ruleTester.run('no-import-module-exports', rule, {
valid: [
test({
code: `
const thing = require('thing')
module.exports = thing
`,
}),
test({
code: `
import thing from 'otherthing'
console.log(thing.module.exports)
`,
}),
test({
code: `
import thing from 'other-thing'
export default thing
`,
}),
test({
code: `
import foo from 'path';
module.exports = foo;
`,
// When the file matches the entry point defined in package.json
// See tests/files/package.json
filename: path.join(process.cwd(), 'tests/files/lib/main.js'),
}),
test({
code: `
import foo from 'path';
module.exports = foo;
`,
filename: path.join(process.cwd(), 'tests/files/some/other/entry-point.js'),
options: [{ exceptions: ['**/*/other/entry-point.js'] }],
}),
],
invalid: [
test({
code: `
import { stuff } from 'starwars'
module.exports = thing
`,
errors: [error],
}),
test({
code: `
import thing from 'starwars'
const baz = module.exports = thing
console.log(baz)
`,
errors: [error],
}),
test({
code: `
import * as allThings from 'starwars'
exports.bar = thing
`,
errors: [error],
}),
test({
code: `
import foo from 'path';
module.exports = foo;
`,
// When the file does not match the entry point defined in package.json
// See tests/files/package.json
filename: path.join(process.cwd(), 'tests/files/not/lib/main.js'),
errors: [error],
}),
test({
code: `
import foo from 'path';
module.exports = foo;
`,
filename: path.join(process.cwd(), 'tests/files/some/other/entry-point.js'),
options: [{ exceptions: ['**/*/other/file.js'] }],
errors: [error],
}),
],
})

0 comments on commit 6f19984

Please sign in to comment.