Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add no-import-module-exports #760

Closed
kentcdodds opened this issue Mar 3, 2017 · 6 comments · Fixed by #804
Closed

add no-import-module-exports #760

kentcdodds opened this issue Mar 3, 2017 · 6 comments · Fixed by #804

Comments

@kentcdodds
Copy link
Contributor

kentcdodds commented Mar 3, 2017

Due to this issue with webpack tree-shaking, and the fact that some people cannot adopt no-commonjs quite yet, I think this would be really handy. I've written the rule for my own stuff and would be happy to add it here (or retrofit no-commonjs with options to cover this use case).

Here's my rule:

'use strict'

module.exports = {
	meta: {
		docs: {
			description: 'Disallow import statements with module.exports',
			category: 'Best Practices',
			recommended: true,
		},
		fixable: 'code',
		schema: [],
	},
	create(context) {
		const importDeclarations = []
		let hasReported = false
		function report() {
			if (hasReported) {
				return
			}
			hasReported = true
			importDeclarations.forEach(node => {
				context.report({
					node,
					message: 'Cannot use import declarations in modules that export using CommonJS (`module.exports = \'foo\'` or `exports.bar = \'hi\'`)',
				})
			})
		}
		return {
			ImportDeclaration(node) {
				importDeclarations.push(node)
			},
			MemberExpression(node) {
				if (isModuleExports(node)) {
					report()
				}

				function isModuleExports(n) {
					return n.object.type === 'Identifier' && (/^(module|exports)$/).test(n.object.name)
				}
			},
		}
	},
}

And the associated tests:

'use strict'

const { RuleTester } = require('eslint')
const rule = require('./no-import-module-exports')

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

const ruleTester = new RuleTester()
ruleTester.run('no-blockless-if', rule, {
	valid: [
		{
			code: `
				const thing = require('thing')
				module.exports = thing
			`,
			parserOptions,
		},
		{
			code: `
				import thing from 'otherthing'
				console.log(thing.module.exports)
			`,
			parserOptions,
		},
		{
			code: `
				import thing from 'other-thing'
				export default thing
			`,
			parserOptions,
		},
	],
	invalid: [
		{
			code: `
				import { stuff } from 'starwars'
				module.exports = thing
			`,
			errors: [error],
			parserOptions,
		},
		{
			code: `
				import thing from 'starwars'
				const baz = module.exports = thing
				console.log(baz)
			`,
			errors: [error],
			parserOptions,
		},
		{
			code: `
				import * as allThings from 'starwars'
				exports.bar = thing
			`,
			errors: [error],
			parserOptions,
		},
	],
})

Let me know what you all think :)

@ljharb
Copy link
Member

ljharb commented Mar 3, 2017

I like this in most cases, but this seems like it would prevent the very best practice of, in your package's entry point, doing import foo from 'path'; module.exports = foo; so as not to expose consumers to babel's .default (which they should never see).

@kentcdodds
Copy link
Contributor Author

Sure, doesn't have to be enabled by default 👍

@ljharb
Copy link
Member

ljharb commented Mar 3, 2017

Right, but it means I wouldn't be able to enable it at all in anything that wasn't a top-level app.

What if it:
a) automatically allowed it in a file that was the "main" or "jsnext:main" (since this plugin supports it) in your package.json, and
b) allowed an "entrypoints" or "exceptions" config array that allowed it in any file/glob pattern matched?

That way by default it allows your entry point to do the right thing (so I can both use and recommend it) and it's configurable if you have multiple entry points.

@kentcdodds
Copy link
Contributor Author

Sounds solid 👍

@ttmarek
Copy link
Contributor

ttmarek commented Apr 15, 2017

@kentcdodds which issue with webpack 2 tree shaking are you referring to? I'd like to add a blurb about it in the docs. Also, any other motivating factors for the rule would be welcome.

@kentcdodds
Copy link
Contributor Author

Whoops! I forgot to link to the issue. It's this one :)

@ljharb ljharb reopened this Oct 15, 2017
ljharb pushed a commit to ttmarek/eslint-plugin-import that referenced this issue Jan 31, 2021
ljharb pushed a commit to ttmarek/eslint-plugin-import that referenced this issue Jan 31, 2021
ljharb pushed a commit to ttmarek/eslint-plugin-import that referenced this issue Jan 31, 2021
@ljharb ljharb closed this as completed in a45661b Jan 31, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging a pull request may close this issue.

3 participants