UNPKG

eslint-plugin-css-modules

Version:

Checks that you are using the existent css/scss/less classes, no more no less

141 lines (113 loc) 13.2 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _fp = require('lodash/fp'); var _fp2 = _interopRequireDefault(_fp); var _lodash = require('lodash'); var _lodash2 = _interopRequireDefault(_lodash); var _core = require('../core'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } exports.default = { meta: { docs: { description: 'Checks that you are using all css/scss/less classes', recommended: true }, schema: [{ type: 'object', properties: { camelCase: { enum: [true, 'dashes', 'only', 'dashes-only'] }, markAsUsed: { type: 'array' } } }] }, create: function create(context) { var markAsUsed = _lodash2.default.get(context, 'options[0].markAsUsed'); var camelCase = _lodash2.default.get(context, 'options[0].camelCase'); /* maps variable name to property Object map = { [variableName]: { classes: { foo: false, 'foo-bar': false }, classesMap: { foo: 'foo', fooBar: 'foo-bar', 'foo-bar': 'foo-bar' }, node: {...} } } example: import s from './foo.scss'; s is variable name property Object has two keys 1. classes: an object with className as key and a boolean as value. The boolean is marked if it is used in file 2. classesMap: an object with propertyName as key and its className as value 3. node: node that correspond to s (see example above) */ var map = {}; return { ImportDeclaration: function ImportDeclaration(node) { var styleImportNodeData = (0, _core.getStyleImportNodeData)(node); if (!styleImportNodeData) { return; } var importName = styleImportNodeData.importName, styleFilePath = styleImportNodeData.styleFilePath, importNode = styleImportNodeData.importNode; var styleFileAbsolutePath = (0, _core.getFilePath)(context, styleFilePath); // this will be used to mark s.foo as used in MemberExpression var classes = (0, _core.getStyleClasses)(styleFileAbsolutePath); var classesMap = classes && (0, _core.getClassesMap)(classes, camelCase); _lodash2.default.set(map, importName + '.classes', classes); _lodash2.default.set(map, importName + '.classesMap', classesMap); // save node for reporting unused styles _lodash2.default.set(map, importName + '.node', importNode); }, MemberExpression: function MemberExpression(node) { /* Check if property exists in css/scss file as class */ var objectName = node.object.name; var propertyName = (0, _core.getPropertyName)(node, camelCase); if (!propertyName) { return; } var className = _lodash2.default.get(map, objectName + '.classesMap.' + propertyName); if (className == null) { return; } // mark this property has used _lodash2.default.set(map, objectName + '.classes.' + className, true); }, 'Program:exit': function ProgramExit() { /* Check if all classes defined in css/scss file are used */ /* we are looping over each import style node in program example: ``` import s from './foo.css'; import x from './bar.scss'; ``` then the loop will be run 2 times */ _lodash2.default.forIn(map, function (o) { var classes = o.classes, node = o.node; /* if option is passed to mark a class as used, example: eslint css-modules/no-unused-class: [2, { markAsUsed: ['container'] }] */ _lodash2.default.forEach(markAsUsed, function (usedClass) { classes[usedClass] = true; }); // classNames not marked as true are unused var unusedClasses = _fp2.default.compose(_fp2.default.keys, _fp2.default.omitBy(_fp2.default.identity))(classes); if (!_lodash2.default.isEmpty(unusedClasses)) { context.report(node, 'Unused classes found: ' + unusedClasses.join(', ')); } }); } }; } }; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL2xpYi9ydWxlcy9uby11bnVzZWQtY2xhc3MuanMiXSwibmFtZXMiOlsibWV0YSIsImRvY3MiLCJkZXNjcmlwdGlvbiIsInJlY29tbWVuZGVkIiwic2NoZW1hIiwidHlwZSIsInByb3BlcnRpZXMiLCJjYW1lbENhc2UiLCJlbnVtIiwibWFya0FzVXNlZCIsImNyZWF0ZSIsImNvbnRleHQiLCJnZXQiLCJtYXAiLCJJbXBvcnREZWNsYXJhdGlvbiIsIm5vZGUiLCJzdHlsZUltcG9ydE5vZGVEYXRhIiwiaW1wb3J0TmFtZSIsInN0eWxlRmlsZVBhdGgiLCJpbXBvcnROb2RlIiwic3R5bGVGaWxlQWJzb2x1dGVQYXRoIiwiY2xhc3NlcyIsImNsYXNzZXNNYXAiLCJzZXQiLCJNZW1iZXJFeHByZXNzaW9uIiwib2JqZWN0TmFtZSIsIm9iamVjdCIsIm5hbWUiLCJwcm9wZXJ0eU5hbWUiLCJjbGFzc05hbWUiLCJmb3JJbiIsIm8iLCJmb3JFYWNoIiwidXNlZENsYXNzIiwidW51c2VkQ2xhc3NlcyIsImNvbXBvc2UiLCJrZXlzIiwib21pdEJ5IiwiaWRlbnRpdHkiLCJpc0VtcHR5IiwicmVwb3J0Iiwiam9pbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBQ0E7Ozs7QUFDQTs7OztBQUVBOzs7O2tCQVVlO0FBQ2JBLFFBQU07QUFDSkMsVUFBTTtBQUNKQyxtQkFBYSxxREFEVDtBQUVKQyxtQkFBYTtBQUZULEtBREY7QUFLSkMsWUFBUSxDQUNOO0FBQ0VDLFlBQU0sUUFEUjtBQUVFQyxrQkFBWTtBQUNWQyxtQkFBVyxFQUFFQyxNQUFNLENBQUMsSUFBRCxFQUFPLFFBQVAsRUFBaUIsTUFBakIsRUFBeUIsYUFBekIsQ0FBUixFQUREO0FBRVZDLG9CQUFZLEVBQUVKLE1BQU0sT0FBUjtBQUZGO0FBRmQsS0FETTtBQUxKLEdBRE87QUFnQmJLLFFBaEJhLGtCQWdCTEMsT0FoQkssRUFnQlk7QUFDdkIsUUFBTUYsYUFBYSxpQkFBRUcsR0FBRixDQUFNRCxPQUFOLEVBQWUsdUJBQWYsQ0FBbkI7QUFDQSxRQUFNSixZQUFZLGlCQUFFSyxHQUFGLENBQU1ELE9BQU4sRUFBZSxzQkFBZixDQUFsQjs7QUFFQTs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFtQkEsUUFBTUUsTUFBTSxFQUFaOztBQUVBLFdBQU87QUFDTEMsdUJBREssNkJBQ2NDLElBRGQsRUFDNEI7QUFDL0IsWUFBTUMsc0JBQXNCLGtDQUF1QkQsSUFBdkIsQ0FBNUI7O0FBRUEsWUFBSSxDQUFDQyxtQkFBTCxFQUEwQjtBQUN4QjtBQUNEOztBQUw4QixZQVE3QkMsVUFSNkIsR0FXM0JELG1CQVgyQixDQVE3QkMsVUFSNkI7QUFBQSxZQVM3QkMsYUFUNkIsR0FXM0JGLG1CQVgyQixDQVM3QkUsYUFUNkI7QUFBQSxZQVU3QkMsVUFWNkIsR0FXM0JILG1CQVgyQixDQVU3QkcsVUFWNkI7OztBQWEvQixZQUFNQyx3QkFBd0IsdUJBQVlULE9BQVosRUFBcUJPLGFBQXJCLENBQTlCOztBQUVBO0FBQ0EsWUFBTUcsVUFBVSwyQkFBZ0JELHFCQUFoQixDQUFoQjtBQUNBLFlBQU1FLGFBQWFELFdBQVcseUJBQWNBLE9BQWQsRUFBdUJkLFNBQXZCLENBQTlCOztBQUVBLHlCQUFFZ0IsR0FBRixDQUFNVixHQUFOLEVBQWNJLFVBQWQsZUFBb0NJLE9BQXBDO0FBQ0EseUJBQUVFLEdBQUYsQ0FBTVYsR0FBTixFQUFjSSxVQUFkLGtCQUF1Q0ssVUFBdkM7O0FBRUE7QUFDQSx5QkFBRUMsR0FBRixDQUFNVixHQUFOLEVBQWNJLFVBQWQsWUFBaUNFLFVBQWpDO0FBQ0QsT0F6Qkk7O0FBMEJMSyx3QkFBa0IsMEJBQUNULElBQUQsRUFBa0I7QUFDbEM7Ozs7QUFJQSxZQUFNVSxhQUFhVixLQUFLVyxNQUFMLENBQVlDLElBQS9CO0FBQ0EsWUFBTUMsZUFBZSwyQkFBZ0JiLElBQWhCLEVBQXNCUixTQUF0QixDQUFyQjs7QUFFQSxZQUFJLENBQUNxQixZQUFMLEVBQW1CO0FBQ2pCO0FBQ0Q7O0FBRUQsWUFBTUMsWUFBWSxpQkFBRWpCLEdBQUYsQ0FBTUMsR0FBTixFQUFjWSxVQUFkLG9CQUF1Q0csWUFBdkMsQ0FBbEI7O0FBRUEsWUFBSUMsYUFBYSxJQUFqQixFQUF1QjtBQUNyQjtBQUNEOztBQUVEO0FBQ0EseUJBQUVOLEdBQUYsQ0FBTVYsR0FBTixFQUFjWSxVQUFkLGlCQUFvQ0ksU0FBcEMsRUFBaUQsSUFBakQ7QUFDRCxPQTlDSTtBQStDTCxvQkEvQ0sseUJBK0NhO0FBQ2hCOzs7O0FBSUE7Ozs7Ozs7OztBQVNBLHlCQUFFQyxLQUFGLENBQVFqQixHQUFSLEVBQWEsVUFBQ2tCLENBQUQsRUFBTztBQUFBLGNBQ1ZWLE9BRFUsR0FDUVUsQ0FEUixDQUNWVixPQURVO0FBQUEsY0FDRE4sSUFEQyxHQUNRZ0IsQ0FEUixDQUNEaEIsSUFEQzs7QUFHbEI7Ozs7O0FBSUEsMkJBQUVpQixPQUFGLENBQVV2QixVQUFWLEVBQXNCLFVBQUN3QixTQUFELEVBQWU7QUFDbkNaLG9CQUFRWSxTQUFSLElBQXFCLElBQXJCO0FBQ0QsV0FGRDs7QUFJQTtBQUNBLGNBQU1DLGdCQUFnQixhQUFHQyxPQUFILENBQ3BCLGFBQUdDLElBRGlCLEVBRXBCLGFBQUdDLE1BQUgsQ0FBVSxhQUFHQyxRQUFiLENBRm9CLEVBR3BCakIsT0FIb0IsQ0FBdEI7O0FBS0EsY0FBSSxDQUFDLGlCQUFFa0IsT0FBRixDQUFVTCxhQUFWLENBQUwsRUFBK0I7QUFDN0J2QixvQkFBUTZCLE1BQVIsQ0FBZXpCLElBQWYsNkJBQThDbUIsY0FBY08sSUFBZCxDQUFtQixJQUFuQixDQUE5QztBQUNEO0FBQ0YsU0FwQkQ7QUFxQkQ7QUFsRkksS0FBUDtBQW9GRDtBQTdIWSxDIiwiZmlsZSI6Im5vLXVudXNlZC1jbGFzcy5qcyIsInNvdXJjZXNDb250ZW50IjpbIi8qIEBmbG93ICovXG5pbXBvcnQgZnAgZnJvbSAnbG9kYXNoL2ZwJztcbmltcG9ydCBfIGZyb20gJ2xvZGFzaCc7XG5cbmltcG9ydCB7XG4gIGdldFN0eWxlSW1wb3J0Tm9kZURhdGEsXG4gIGdldFN0eWxlQ2xhc3NlcyxcbiAgZ2V0UHJvcGVydHlOYW1lLFxuICBnZXRDbGFzc2VzTWFwLFxuICBnZXRGaWxlUGF0aCxcbn0gZnJvbSAnLi4vY29yZSc7XG5cbmltcG9ydCB0eXBlIHsgSnNOb2RlIH0gZnJvbSAnLi4vdHlwZXMnO1xuXG5leHBvcnQgZGVmYXVsdCB7XG4gIG1ldGE6IHtcbiAgICBkb2NzOiB7XG4gICAgICBkZXNjcmlwdGlvbjogJ0NoZWNrcyB0aGF0IHlvdSBhcmUgdXNpbmcgYWxsIGNzcy9zY3NzL2xlc3MgY2xhc3NlcycsXG4gICAgICByZWNvbW1lbmRlZDogdHJ1ZSxcbiAgICB9LFxuICAgIHNjaGVtYTogW1xuICAgICAge1xuICAgICAgICB0eXBlOiAnb2JqZWN0JyxcbiAgICAgICAgcHJvcGVydGllczoge1xuICAgICAgICAgIGNhbWVsQ2FzZTogeyBlbnVtOiBbdHJ1ZSwgJ2Rhc2hlcycsICdvbmx5JywgJ2Rhc2hlcy1vbmx5J10gfSxcbiAgICAgICAgICBtYXJrQXNVc2VkOiB7IHR5cGU6ICdhcnJheScgfSxcbiAgICAgICAgfSxcbiAgICAgIH1cbiAgICBdLFxuICB9LFxuICBjcmVhdGUgKGNvbnRleHQ6IE9iamVjdCkge1xuICAgIGNvbnN0IG1hcmtBc1VzZWQgPSBfLmdldChjb250ZXh0LCAnb3B0aW9uc1swXS5tYXJrQXNVc2VkJyk7XG4gICAgY29uc3QgY2FtZWxDYXNlID0gXy5nZXQoY29udGV4dCwgJ29wdGlvbnNbMF0uY2FtZWxDYXNlJyk7XG5cbiAgICAvKlxuICAgICAgIG1hcHMgdmFyaWFibGUgbmFtZSB0byBwcm9wZXJ0eSBPYmplY3RcbiAgICAgICBtYXAgPSB7XG4gICAgICAgICBbdmFyaWFibGVOYW1lXToge1xuICAgICAgICAgICBjbGFzc2VzOiB7IGZvbzogZmFsc2UsICdmb28tYmFyJzogZmFsc2UgfSxcbiAgICAgICAgICAgY2xhc3Nlc01hcDogeyBmb286ICdmb28nLCBmb29CYXI6ICdmb28tYmFyJywgJ2Zvby1iYXInOiAnZm9vLWJhcicgfSxcbiAgICAgICAgICAgbm9kZTogey4uLn1cbiAgICAgICAgIH1cbiAgICAgICB9XG5cbiAgICAgICBleGFtcGxlOlxuICAgICAgIGltcG9ydCBzIGZyb20gJy4vZm9vLnNjc3MnO1xuICAgICAgIHMgaXMgdmFyaWFibGUgbmFtZVxuXG4gICAgICAgcHJvcGVydHkgT2JqZWN0IGhhcyB0d28ga2V5c1xuICAgICAgIDEuIGNsYXNzZXM6IGFuIG9iamVjdCB3aXRoIGNsYXNzTmFtZSBhcyBrZXkgYW5kIGEgYm9vbGVhbiBhcyB2YWx1ZS4gVGhlIGJvb2xlYW4gaXMgbWFya2VkIGlmIGl0IGlzIHVzZWQgaW4gZmlsZVxuICAgICAgIDIuIGNsYXNzZXNNYXA6IGFuIG9iamVjdCB3aXRoIHByb3BlcnR5TmFtZSBhcyBrZXkgYW5kIGl0cyBjbGFzc05hbWUgYXMgdmFsdWVcbiAgICAgICAzLiBub2RlOiBub2RlIHRoYXQgY29ycmVzcG9uZCB0byBzIChzZWUgZXhhbXBsZSBhYm92ZSlcbiAgICAgKi9cbiAgICBjb25zdCBtYXAgPSB7fTtcblxuICAgIHJldHVybiB7XG4gICAgICBJbXBvcnREZWNsYXJhdGlvbiAobm9kZTogSnNOb2RlKSB7XG4gICAgICAgIGNvbnN0IHN0eWxlSW1wb3J0Tm9kZURhdGEgPSBnZXRTdHlsZUltcG9ydE5vZGVEYXRhKG5vZGUpO1xuXG4gICAgICAgIGlmICghc3R5bGVJbXBvcnROb2RlRGF0YSkge1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IHtcbiAgICAgICAgICBpbXBvcnROYW1lLFxuICAgICAgICAgIHN0eWxlRmlsZVBhdGgsXG4gICAgICAgICAgaW1wb3J0Tm9kZSxcbiAgICAgICAgfSA9IHN0eWxlSW1wb3J0Tm9kZURhdGE7XG5cbiAgICAgICAgY29uc3Qgc3R5bGVGaWxlQWJzb2x1dGVQYXRoID0gZ2V0RmlsZVBhdGgoY29udGV4dCwgc3R5bGVGaWxlUGF0aCk7XG5cbiAgICAgICAgLy8gdGhpcyB3aWxsIGJlIHVzZWQgdG8gbWFyayBzLmZvbyBhcyB1c2VkIGluIE1lbWJlckV4cHJlc3Npb25cbiAgICAgICAgY29uc3QgY2xhc3NlcyA9IGdldFN0eWxlQ2xhc3NlcyhzdHlsZUZpbGVBYnNvbHV0ZVBhdGgpO1xuICAgICAgICBjb25zdCBjbGFzc2VzTWFwID0gY2xhc3NlcyAmJiBnZXRDbGFzc2VzTWFwKGNsYXNzZXMsIGNhbWVsQ2FzZSk7XG5cbiAgICAgICAgXy5zZXQobWFwLCBgJHtpbXBvcnROYW1lfS5jbGFzc2VzYCwgY2xhc3Nlcyk7XG4gICAgICAgIF8uc2V0KG1hcCwgYCR7aW1wb3J0TmFtZX0uY2xhc3Nlc01hcGAsIGNsYXNzZXNNYXApO1xuXG4gICAgICAgIC8vIHNhdmUgbm9kZSBmb3IgcmVwb3J0aW5nIHVudXNlZCBzdHlsZXNcbiAgICAgICAgXy5zZXQobWFwLCBgJHtpbXBvcnROYW1lfS5ub2RlYCwgaW1wb3J0Tm9kZSk7XG4gICAgICB9LFxuICAgICAgTWVtYmVyRXhwcmVzc2lvbjogKG5vZGU6IEpzTm9kZSkgPT4ge1xuICAgICAgICAvKlxuICAgICAgICAgICBDaGVjayBpZiBwcm9wZXJ0eSBleGlzdHMgaW4gY3NzL3Njc3MgZmlsZSBhcyBjbGFzc1xuICAgICAgICAgKi9cblxuICAgICAgICBjb25zdCBvYmplY3ROYW1lID0gbm9kZS5vYmplY3QubmFtZTtcbiAgICAgICAgY29uc3QgcHJvcGVydHlOYW1lID0gZ2V0UHJvcGVydHlOYW1lKG5vZGUsIGNhbWVsQ2FzZSk7XG5cbiAgICAgICAgaWYgKCFwcm9wZXJ0eU5hbWUpIHtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCBjbGFzc05hbWUgPSBfLmdldChtYXAsIGAke29iamVjdE5hbWV9LmNsYXNzZXNNYXAuJHtwcm9wZXJ0eU5hbWV9YCk7XG5cbiAgICAgICAgaWYgKGNsYXNzTmFtZSA9PSBudWxsKSB7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gbWFyayB0aGlzIHByb3BlcnR5IGhhcyB1c2VkXG4gICAgICAgIF8uc2V0KG1hcCwgYCR7b2JqZWN0TmFtZX0uY2xhc3Nlcy4ke2NsYXNzTmFtZX1gLCB0cnVlKTtcbiAgICAgIH0sXG4gICAgICAnUHJvZ3JhbTpleGl0JyAoKSB7XG4gICAgICAgIC8qXG4gICAgICAgICAgIENoZWNrIGlmIGFsbCBjbGFzc2VzIGRlZmluZWQgaW4gY3NzL3Njc3MgZmlsZSBhcmUgdXNlZFxuICAgICAgICAgKi9cblxuICAgICAgICAvKlxuICAgICAgICAgICB3ZSBhcmUgbG9vcGluZyBvdmVyIGVhY2ggaW1wb3J0IHN0eWxlIG5vZGUgaW4gcHJvZ3JhbVxuICAgICAgICAgICBleGFtcGxlOlxuICAgICAgICAgICBgYGBcbiAgICAgICAgICAgICBpbXBvcnQgcyBmcm9tICcuL2Zvby5jc3MnO1xuICAgICAgICAgICAgIGltcG9ydCB4IGZyb20gJy4vYmFyLnNjc3MnO1xuICAgICAgICAgICBgYGBcbiAgICAgICAgICAgdGhlbiB0aGUgbG9vcCB3aWxsIGJlIHJ1biAyIHRpbWVzXG4gICAgICAgICAqL1xuICAgICAgICBfLmZvckluKG1hcCwgKG8pID0+IHtcbiAgICAgICAgICBjb25zdCB7IGNsYXNzZXMsIG5vZGUgfSA9IG87XG5cbiAgICAgICAgICAvKlxuICAgICAgICAgICAgIGlmIG9wdGlvbiBpcyBwYXNzZWQgdG8gbWFyayBhIGNsYXNzIGFzIHVzZWQsIGV4YW1wbGU6XG4gICAgICAgICAgICAgZXNsaW50IGNzcy1tb2R1bGVzL25vLXVudXNlZC1jbGFzczogWzIsIHsgbWFya0FzVXNlZDogWydjb250YWluZXInXSB9XVxuICAgICAgICAgICAqL1xuICAgICAgICAgIF8uZm9yRWFjaChtYXJrQXNVc2VkLCAodXNlZENsYXNzKSA9PiB7XG4gICAgICAgICAgICBjbGFzc2VzW3VzZWRDbGFzc10gPSB0cnVlO1xuICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgLy8gY2xhc3NOYW1lcyBub3QgbWFya2VkIGFzIHRydWUgYXJlIHVudXNlZFxuICAgICAgICAgIGNvbnN0IHVudXNlZENsYXNzZXMgPSBmcC5jb21wb3NlKFxuICAgICAgICAgICAgZnAua2V5cyxcbiAgICAgICAgICAgIGZwLm9taXRCeShmcC5pZGVudGl0eSksIC8vIG9taXQgdHJ1dGh5IHZhbHVlc1xuICAgICAgICAgICkoY2xhc3Nlcyk7XG5cbiAgICAgICAgICBpZiAoIV8uaXNFbXB0eSh1bnVzZWRDbGFzc2VzKSkge1xuICAgICAgICAgICAgY29udGV4dC5yZXBvcnQobm9kZSwgYFVudXNlZCBjbGFzc2VzIGZvdW5kOiAke3VudXNlZENsYXNzZXMuam9pbignLCAnKX1gKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH07XG4gIH1cbn07XG4iXX0=