Skip to content
This repository has been archived by the owner on Aug 18, 2020. It is now read-only.

Commit

Permalink
Add images-no-direct-imports rule
Browse files Browse the repository at this point in the history
  • Loading branch information
BPScott committed Feb 9, 2019
1 parent 3849073 commit 7078350
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 0 deletions.
30 changes: 30 additions & 0 deletions docs/rules/images-no-direct-imports.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Prevent directly importing image files and instead force usage of an index file

Image files should live in a directory (e.g. `icons`, `illustrations` or `images`) that must contain a dedicated index file that reexports all the images in that folder.

Files that consume images must import from that index file instead of importing images directly.

## Rule Details

Example of **incorrect** code for this rule:


```js
// components/Foo/Foo.js
import icon1 from './icons/icon1.svg';
```


Example of **correct** code for this rule:

```js
// components/Foo/icons/index.js
export {default as icon1} from './icon1.svg';

// components/Foo/Foo.js
import {icon1} from './icons';
```

## When Not To Use It

If you do not wish to enforce imports locations for images, then you can safely disable this rule.
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module.exports = {
rules: {
'binary-assignment-parens': require('./lib/rules/binary-assignment-parens'),
'class-property-semi': require('./lib/rules/class-property-semi'),
'images-no-direct-imports': require('./lib/rules/images-no-direct-imports'),
'jest/no-snapshots': require('./lib/rules/jest/no-snapshots'),
'jest/no-vague-titles': require('./lib/rules/jest/no-vague-titles'),
'jquery-dollar-sign-reference': require('./lib/rules/jquery-dollar-sign-reference'),
Expand Down
49 changes: 49 additions & 0 deletions lib/rules/images-no-direct-imports.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
const {basename, dirname, extname} = require('path');
const resolve = require('eslint-module-utils/resolve').default;
const {docsUrl} = require('../utilities');

function isImageImport(resolvedSource) {
return /\.(svg|png|jpg)$/.test(resolvedSource);
}

function isImportFromCurrentFolderIndex(contextFilename, resolvedSource) {
const isIndex =
basename(contextFilename, extname(contextFilename)) === 'index';

return isIndex && dirname(resolvedSource) === dirname(contextFilename);
}

module.exports = {
meta: {
docs: {
description:
"Prefer that imports of image files go through an index file within the image's directory instead of directly referencing images within a component.",
category: 'Best Practices',
recommended: false,
uri: docsUrl('images-no-direct-imports'),
},
fixable: null,
},
create(context) {
function checkNode(node) {
const resolvedSource = resolve(node.source.value, context);

if (
resolvedSource &&
isImageImport(resolvedSource) &&
!isImportFromCurrentFolderIndex(context.getFilename(), resolvedSource)
) {
context.report({
node,
message:
'Do not import image files directly, instead import from the index folder that the icon is contained within',
});
}
}
return {
ImportDeclaration: checkNode,
ExportNamedDeclaration: checkNode,
ExportAllDeclaration: checkNode,
};
},
};
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file.
104 changes: 104 additions & 0 deletions tests/lib/rules/images-no-direct-imports.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
const {RuleTester} = require('eslint');
const {fixtureFile} = require('../../utilities');
const rule = require('../../../lib/rules/images-no-direct-imports');

const ruleTester = new RuleTester({
parserOptions: {
ecmaVersion: 6,
sourceType: 'module',
},
});

function errors(type) {
return [
{
type,
message:
'Do not import image files directly, instead import from the index folder that the icon is contained within',
},
];
}

ruleTester.run('no-ancestor-directory-import', rule, {
valid: [
// Importing / Exporting svg files from the folder index is valid
{
code: "import icon1 from './icon1.svg'",
filename: fixtureFile('basic-app/app/components/Foo/icons/index.js'),
},
{
code: "import * as icon1 from './icon1.svg'",
filename: fixtureFile('basic-app/app/components/Foo/icons/index.js'),
},
{
code: "export {default as icon1} from './icon1.svg'",
filename: fixtureFile('basic-app/app/components/Foo/icons/index.js'),
},
{
code: "export * from './icon1.svg'",
filename: fixtureFile('basic-app/app/components/Foo/icons/index.js'),
},
// Importing / Exporting icon index file contents from a component is valid
{
code: "import {icon1} from './icons'",
filename: fixtureFile('basic-app/app/components/Foo/Foo.js'),
},
{
code: "import * as icon1 from './icons'",
filename: fixtureFile('basic-app/app/components/Foo/Foo.js'),
},
{
code: "export {default as icon1} from './icons'",
filename: fixtureFile('basic-app/app/components/Foo/Foo.js'),
},
{
code: "export * from './icons'",
filename: fixtureFile('basic-app/app/components/Foo/Foo.js'),
},
],

invalid: [
// Importing / Exporting an icon directly from component file is invalid
{
code: "import icon1 from './icons/icon1.svg'",
errors: errors('ImportDeclaration'),
filename: fixtureFile('basic-app/app/components/Foo/Foo.js'),
},
{
code: "import * as icon1 from './icons/icon1.svg'",
errors: errors('ImportDeclaration'),
filename: fixtureFile('basic-app/app/components/Foo/index.js'),
},
{
code: "export {default as icon1} from './icons/icon1.svg'",
errors: errors('ExportNamedDeclaration'),
filename: fixtureFile('basic-app/app/components/Foo/Foo.js'),
},
{
code: "export * from './icons/icon1.svg'",
errors: errors('ExportAllDeclaration'),
filename: fixtureFile('basic-app/app/components/Foo/Foo.js'),
},
// Importing / Exporting an icon directly from some other index file
{
code: "import icon1 from './icons/icon1.svg'",
errors: errors('ImportDeclaration'),
filename: fixtureFile('basic-app/app/components/Foo/index.js'),
},
{
code: "import * as icon1 from './icons/icon1.svg'",
errors: errors('ImportDeclaration'),
filename: fixtureFile('basic-app/app/components/Foo/index.js'),
},
{
code: "export {default as icon1} from './icons/icon1.svg'",
errors: errors('ExportNamedDeclaration'),
filename: fixtureFile('basic-app/app/components/Foo/index.js'),
},
{
code: "export * from './icons/icon1.svg'",
errors: errors('ExportAllDeclaration'),
filename: fixtureFile('basic-app/app/components/Foo/index.js'),
},
],
});

0 comments on commit 7078350

Please sign in to comment.