eslint-plugin-react icon indicating copy to clipboard operation
eslint-plugin-react copied to clipboard

[Bug]: `jsx-newline` sometimes breaks with comments

Open happenslol opened this issue 2 years ago • 3 comments

Is there an existing issue for this?

  • [X] I have searched the existing issues and my issue is unique
  • [X] My issue appears in the command-line and not only in the text editor

Description Overview

In the following minimal example, the react/jsx-newline rule fails to parse the file:

function Test() {
  return (
    <div>
      <div />
      {/* a comment */}
    </div>
  );
}

The following error is printed on running eslint:

Oops! Something went wrong! :(

ESLint: 8.40.0

TypeError: Cannot read properties of undefined (reading 'loc')
Occurred while linting /home/happens/tmp/jsx-newline-test/test.jsx:1
Rule: "react/jsx-newline"
    at isMultilined (/home/happens/tmp/jsx-newline-test/node_modules/eslint-plugin-react/lib/rules/jsx-newline.js:23:15)
    at /home/happens/tmp/jsx-newline-test/node_modules/eslint-plugin-react/lib/rules/jsx-newline.js:111:22
    at Array.forEach (<anonymous>)
    at /home/happens/tmp/jsx-newline-test/node_modules/eslint-plugin-react/lib/rules/jsx-newline.js:87:27
    at Set.forEach (<anonymous>)
    at Program:exit (/home/happens/tmp/jsx-newline-test/node_modules/eslint-plugin-react/lib/rules/jsx-newline.js:86:27)
    at ruleErrorHandler (/home/happens/tmp/jsx-newline-test/node_modules/eslint/lib/linter/linter.js:1049:28)
    at /home/happens/tmp/jsx-newline-test/node_modules/eslint/lib/linter/safe-emitter.js:45:58
    at Array.forEach (<anonymous>)
    at Object.emit (/home/happens/tmp/jsx-newline-test/node_modules/eslint/lib/linter/safe-emitter.js:45:38)

Moving the comment up a row or removing the sibling fixes the problem.

Expected Behavior

The rule should be able to correctly parse the file.

eslint-plugin-react version

v7.32.2

eslint version

v8.40.0

node version

v19.9.0

happenslol avatar May 09 '23 07:05 happenslol

This cannot be reproduced with the default parser, can you show what parser you are using? For example running npx eslint --print-config src/index.js

chiawendt avatar May 13 '23 14:05 chiawendt

I'm having the same issue. Package versions and my eslint config file is below. Thanks!

Packages
"dependencies": {
    "@emotion/react": "^11.11.1",
    "@emotion/styled": "^11.11.0",
    "@mui/icons-material": "^5.14.18",
    "@mui/material": "^5.14.18",
    "axios": "^1.6.2",
    "fuse.js": "^7.0.0",
    "immer": "^10.0.3",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-router-dom": "^6.19.0",
    "zustand": "^4.4.6"
},
"devDependencies": {
    "@types/node": "^20.9.2",
    "@types/react": "^18.2.37",
    "@types/react-dom": "^18.2.15",
    "@typescript-eslint/eslint-plugin": "^6.11.0",
    "@typescript-eslint/parser": "^6.11.0",
    "@vitejs/plugin-react-swc": "^3.5.0",
    "dotenv": "^16.3.1",
    "eslint": "^8.54.0",
    "eslint-plugin-import": "^2.29.0",
    "eslint-plugin-json": "^3.1.0",
    "eslint-plugin-react": "^7.33.2",
    "eslint-plugin-react-hooks": "^4.6.0",
    "eslint-plugin-react-refresh": "^0.4.4",
    "typescript": "^5.2.2",
    "vite": "^5.0.0"
}
ESLint Config
module.exports = {
	root: true,
	env: { browser: true, es2020: true },
	settings: {
		react: {
			version: "detect"
		}
	},
	extends: [
		"eslint:recommended",
		"plugin:import/typescript",
		"plugin:react/recommended",
		"plugin:react-hooks/recommended",
		"plugin:@typescript-eslint/recommended",
		"plugin:json/recommended"
	],
	parserOptions: {
		project: ["./tsconfig.json"]
	},
	parser: "@typescript-eslint/parser",
	ignorePatterns: ["node_modules", "dist", "build", "dist", "package.json", "tsconfig.json", "tsconfig.node.json", ".eslintrc.cjs", "vite.config.ts", "**/components_old/**/*.tsx", "**/oldStores/**/*.tsx"],
	plugins: ["import", "react", "react-hooks", "react-refresh", "@typescript-eslint", "json"],
	rules: {
		"json/*": ["error", "allowComments"],
	"import/named": "off",
	"import/newline-after-import": ["error", { count: 2 }],
	"import/no-default-export": "error",
	"import/no-unresolved": ["error", { ignore: ['\\.woff$'] }],
	"import/no-self-import": "error",
	"import/no-duplicates": "error",
	"import/order": [
		"error",
		{
			"newlines-between": "always",
			alphabetize: { "order": "asc", "caseInsensitive": true },
			groups: ["builtin", "external", "internal", ["parent", "sibling", "index", "object"], "type", "unknown"],
			pathGroups: [
				{
					pattern: "**/*.+(css|sass|less|scss|pcss|styl|woff)",
					patternOptions: { dot: true, nocomment: true },
					group: "unknown",
					position: "after"
				},
				{
					pattern: "{.,..}/**/*.+(css|sass|less|scss|pcss|styl|woff)",
					patternOptions: { dot: true, nocomment: true },
					group: "unknown",
					position: "after"
				}
			],
			warnOnUnassignedImports: true
		}
	],

	"eqeqeq": "error",
	"no-debugger": "warn",
	"eol-last": ["error", "always"],
	"prefer-const": "error",
	"no-mixed-spaces-and-tabs": "error",
	"linebreak-style": "off",
	"operator-linebreak": ["error", "before"],
	"quotes": ["error", "double"],
	"semi": ["error", "always"],
	"indent": "off",
	"no-empty": "off",
	"no-empty-function": "off",
	"no-unused-vars": "off",
	"comma-dangle": "off",
	"no-multi-spaces": ["error"],
	"no-multiple-empty-lines": "error",
	"no-multiple-empty-lines": ["error", { max: 2, maxBOF: 0 }],
	"no-restricted-imports": ["error", {
		"paths": [
			{ name: "react", importNames: ["default"], message: "Please do not import React." },
		]
	}],

	"@typescript-eslint/comma-dangle": "error",
	"@typescript-eslint/indent": ["error", "tab"],
	"@typescript-eslint/no-explicit-any": "error",
	"@typescript-eslint/no-inferrable-types": "error",
	"@typescript-eslint/no-empty-function": "error",
	"@typescript-eslint/no-empty-interface": "error",
	"@typescript-eslint/no-unsafe-declaration-merging": "error",
	"@typescript-eslint/no-unsafe-enum-comparison": "error",
	"@typescript-eslint/explicit-module-boundary-types": "error",
	"@typescript-eslint/no-duplicate-enum-values": "error",
	"@typescript-eslint/no-unused-vars": ["warn", { vars: "all", args: "after-used" }],
	"@typescript-eslint/naming-convention": [
		"error",
		{ selector: "default", format: ["camelCase"] },

		{ selector: "variable", modifiers: ["exported"], filter: { regex: "^use", match: true }, format: ["camelCase"] },
		{ selector: "variable", modifiers: ["global"], format: ["PascalCase"] },
		{ selector: "variable", format: ["camelCase"] },

		{ selector: "function", modifiers: ["exported"], filter: { regex: "^use", match: true }, format: ["camelCase"] },
		{ selector: "function", modifiers: ["global"], format: ["PascalCase"] },
		{ selector: "function", format: ["camelCase"] },

		{ selector: "parameter", format: ["camelCase"], format: null },

		{ selector: "property", format: null },
		{ selector: "property", types: ["array", "boolean", "function", "string"], format: ["camelCase", "snake_case", "PascalCase"] },

		{ selector: "classProperty", types: ["number"], format: ["camelCase"], leadingUnderscore: "allow" },

		{ selector: "property", types: ["string"], filter: { regex: "^Access-Control-Allow-Origin$", match: true }, format: null },

		{ selector: "method", format: ["camelCase"] },

		{ selector: "enumMember", format: ["PascalCase"] },
		{ selector: "typeLike", format: ["PascalCase"] },

		{ selector: "import", format: null }
	],

	"react/jsx-uses-react": "off",
	"react/react-in-jsx-scope": "off",
	"react/jsx-fragments": ["error", "element"],
	"react/jsx-filename-extension": ["warn", { extensions: [".ts", ".tsx"] }],
	"react/no-arrow-function-lifecycle": "error",
	"react/no-danger": "error",
	"react/no-deprecated": "error",
	"react/no-did-mount-set-state": "error",
	"react/no-did-update-set-state": "error",
	"react/no-direct-mutation-state": "error",
	"react/no-is-mounted": "error",
	"react/no-unused-state": "warn",
	"react/no-multi-comp": ["error", { "ignoreStateless": true }],
	"react/no-unescaped-entities": "off",
	"react/jsx-newline": ["error", { prevent: true, allowMultilines: true }],
	"react/hook-use-state": ["off", { allowDestructuredState: true }],
	"react/jsx-curly-newline": ["error", { multiline: "consistent", singleline: "consistent" }],
	"react/self-closing-comp": ["error", { component: true, html: true }],
	"react/jsx-wrap-multilines": ["error", {
		"declaration": "parens-new-line",
		"assignment": "parens-new-line",
		"return": "parens-new-line",
		"arrow": "parens-new-line",
		"condition": "ignore",
		"logical": "ignore",
		"prop": "ignore"
	}],
	"react-hooks/rules-of-hooks": "error",
	"react-hooks/exhaustive-deps": "error",
	"react-refresh/only-export-components": ["warn", { allowConstantExport: true }]
},
overrides: [
	{
		files: ["./src/store/store.ts"],
		rules: {
			"no-restricted-imports": ["error", { "paths": [{ name: "react", importNames: ["default"], message: "Please do not import React." }] }]
		}
	}
]

};

yigitlevent avatar Nov 19 '23 12:11 yigitlevent

Same issue here. It seems the problem is that

function isMultilined(node) {
  return node.loc.start.line !== node.loc.end.line;
}

is called with node being undefined.

I see that this simple change

function isMultilined(node) {
  return node?.loc.start.line !== node?.loc.end.line;
}

seems to fix the bug. Not sure if the output is correct/as expected, but at least the execution does not crash

chripi avatar Dec 02 '23 04:12 chripi