jsx-filename-extension.js 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. /**
  2. * @fileoverview Restrict file extensions that may contain JSX
  3. * @author Joe Lencioni
  4. */
  5. 'use strict';
  6. const path = require('path');
  7. const docsUrl = require('../util/docsUrl');
  8. const report = require('../util/report');
  9. // ------------------------------------------------------------------------------
  10. // Constants
  11. // ------------------------------------------------------------------------------
  12. const DEFAULTS = {
  13. allow: 'always',
  14. extensions: ['.jsx'],
  15. };
  16. // ------------------------------------------------------------------------------
  17. // Rule Definition
  18. // ------------------------------------------------------------------------------
  19. const messages = {
  20. noJSXWithExtension: 'JSX not allowed in files with extension \'{{ext}}\'',
  21. extensionOnlyForJSX: 'Only files containing JSX may use the extension \'{{ext}}\'',
  22. };
  23. module.exports = {
  24. meta: {
  25. docs: {
  26. description: 'Disallow file extensions that may contain JSX',
  27. category: 'Stylistic Issues',
  28. recommended: false,
  29. url: docsUrl('jsx-filename-extension'),
  30. },
  31. messages,
  32. schema: [{
  33. type: 'object',
  34. properties: {
  35. allow: {
  36. enum: ['always', 'as-needed'],
  37. },
  38. extensions: {
  39. type: 'array',
  40. items: {
  41. type: 'string',
  42. },
  43. },
  44. },
  45. additionalProperties: false,
  46. }],
  47. },
  48. create(context) {
  49. const filename = context.getFilename();
  50. let jsxNode;
  51. if (filename === '<text>') {
  52. // No need to traverse any nodes.
  53. return {};
  54. }
  55. const allow = (context.options[0] && context.options[0].allow) || DEFAULTS.allow;
  56. const allowedExtensions = (context.options[0] && context.options[0].extensions) || DEFAULTS.extensions;
  57. const isAllowedExtension = allowedExtensions.some((extension) => filename.slice(-extension.length) === extension);
  58. function handleJSX(node) {
  59. if (!jsxNode) {
  60. jsxNode = node;
  61. }
  62. }
  63. // --------------------------------------------------------------------------
  64. // Public
  65. // --------------------------------------------------------------------------
  66. return {
  67. JSXElement: handleJSX,
  68. JSXFragment: handleJSX,
  69. 'Program:exit'(node) {
  70. if (jsxNode) {
  71. if (!isAllowedExtension) {
  72. report(context, messages.noJSXWithExtension, 'noJSXWithExtension', {
  73. node: jsxNode,
  74. data: {
  75. ext: path.extname(filename),
  76. },
  77. });
  78. }
  79. return;
  80. }
  81. if (isAllowedExtension && allow === 'as-needed') {
  82. report(context, messages.extensionOnlyForJSX, 'extensionOnlyForJSX', {
  83. node,
  84. data: {
  85. ext: path.extname(filename),
  86. },
  87. });
  88. }
  89. },
  90. };
  91. },
  92. };