├── .nvmrc
├── .husky
└── pre-commit
├── CHANGELOG.md
├── .npmignore
├── .lintstagedrc.json
├── .prettierignore
├── src
├── vite-env.d.ts
├── dev
│ ├── main.tsx
│ ├── App.tsx
│ └── theme.utils.ts
├── index.ts
├── components
│ ├── icons
│ │ ├── Check.tsx
│ │ └── Copy.tsx
│ └── CodeBlockWithCopy.tsx
├── index.css
├── utils
│ └── utils.ts
└── TiptapParser.tsx
├── .github
├── ISSUE_TEMPLATE
│ ├── config.yml
│ ├── feature_request.md
│ └── bug_report.md
└── workflows
│ ├── automerge.yml
│ ├── publish.yml
│ ├── add-badges.yml
│ └── release.yml
├── example
├── .prettierignore
├── src
│ ├── vite-env.d.ts
│ ├── index.css
│ ├── main.tsx
│ └── App.tsx
├── public
│ └── favicon.ico
├── .yarn
│ └── install-state.gz
├── postcss.config
├── tailwind.config
├── docs
│ └── Note.md
├── tsconfig.node.json
├── .prettierrc.cjs
├── .gitignore
├── vite.config.ts
├── tsconfig.json
├── index.html
├── README.md
├── package.json
└── eslint.config.mjs
├── public
└── favicon.ico
├── screenshots
└── screenshot.png
├── .yarnrc.yml
├── tsconfig.build.json
├── .markdown-link-check-config.json
├── tsconfig.build-esm.json
├── .vscode
├── extensions.json
├── launch.json
└── settings.json
├── tsconfig.node.json
├── vite.config.ts
├── .cspell.json
├── .prettierrc.cjs
├── tsconfig.json
├── index.html
├── CONTRIBUTING.md
├── .gitignore
├── package.json
├── README.md
└── eslint.config.mjs
/.nvmrc:
--------------------------------------------------------------------------------
1 | v20
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | yarn lint-staged
2 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 0.2.5 (2024-10-12)
2 |
3 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .nyc_output/
3 | coverage/
--------------------------------------------------------------------------------
/.lintstagedrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "*.{ts,tsx}": "eslint --fix"
3 | }
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 | .next
4 | build
--------------------------------------------------------------------------------
/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | /// This is a stringified html with code This is a stringified html with code
4 | HTML parser for Tiptap editor build on the of html-react-parser with code syntax highlighting.
5 | {children}
39 | Here is an exemple of code
5 |
7 |
39 | `;
40 |
41 | const App = () => {
42 | return (
43 | import Link from 'next/link';
8 |
9 | import Title from '@/components/typography/Title';
10 |
11 | // some comment here
12 |
13 | const NotFound = () => (
14 | <html lang="en">
15 | <body className="">
16 | <div className="flex min-h-screen flex-col items-center justify-center space-y-8">
17 | <Title className="text-4xl font-semibold">404 - Page Not Found</Title>
18 |
19 | <div className="space-x-4">
20 | <Link
21 | className="text-blue-600 underline duration-300 hover:text-red-500"
22 | href="/"
23 | >
24 | Homepage
25 | </Link>
26 | <Link
27 | className="text-blue-600 underline duration-300 hover:text-red-500"
28 | href="/contact"
29 | >
30 | Contact Us
31 | </Link>
32 | </div>
33 | </div>
34 | </body>
35 | </html>
36 | );
37 | export default NotFound;
38 | Here is an exemple of code
5 |
7 |
39 | `;
40 |
41 | const App = () => {
42 | return (
43 | import Link from 'next/link';
8 |
9 | import Title from '@/components/typography/Title';
10 |
11 | // some comment here
12 |
13 | const NotFound = () => (
14 | <html lang="en">
15 | <body className="">
16 | <div className="flex min-h-screen flex-col items-center justify-center space-y-8">
17 | <Title className="text-4xl font-semibold">404 - Page Not Found</Title>
18 |
19 | <div className="space-x-4">
20 | <Link
21 | className="text-blue-600 underline duration-300 hover:text-red-500"
22 | href="/"
23 | >
24 | Homepage
25 | </Link>
26 | <Link
27 | className="text-blue-600 underline duration-300 hover:text-red-500"
28 | href="/contact"
29 | >
30 | Contact Us
31 | </Link>
32 | </div>
33 | </div>
34 | </body>
35 | </html>
36 | );
37 | export default NotFound;
38 |
22 |
23 | ## Installation
24 |
25 | ```shell
26 |
27 | npm install tiptap-parser
28 |
29 | ```
30 | or
31 | ```shell
32 |
33 | yarn add tiptap-parser
34 | ```
35 |
36 |
37 | ## Get started
38 |
39 | #### Simple usage
40 |
41 | ```tsx
42 | import TiptapParser from "tiptap-parser";
43 |
44 | const html = `Hello world
`;
45 |
46 | function App() {
47 | return (
48 | Hello there
>`;
57 |
58 | console.log("Log something here")Hello there
console.log('Hello, World!')`
44 | */
45 | text: string,
46 | classNames?: ClassNamesProps,
47 | language = 'javascript',
48 | options?: HTMLReactParserOptions
49 | ) => {
50 | const { codeClassName } = classNames || {};
51 | /**
52 | * Parse the html string content and highlight the code snippets
53 | */
54 |
55 | const defaultOptions = {
56 | /**
57 | * Replace the `` with the highlighted code snippet
58 | * the string content of the `` tag is transformed to stringified html using the lowlight library
59 | * the stringified html is then highlighted using the `highlight` theme
60 | * for that to work with tailwindcss, we are use tailwindcss plugin `tailwind-highlightjs`
61 | * hast trees as returned by lowlight can be serialized to HTML using `hast-util-to-html`
62 | * finally the highlighted code snippet is parsed to react component using the `parse` function of `html-react-parser`
63 | * @param domProps
64 | * @returns
65 | */
66 | replace: (domProps: DOMNode) => {
67 | const { name, children, attribs } = domProps as Element;
68 | const Component = name as ElementType;
69 |
70 | if (name) {
71 | const props = attributesToProps(attribs);
72 |
73 | /*
74 | * do not replace the `` tag
75 | * if (name === 'pre') return
76 | */
77 | if (name === 'pre') {
78 | return (
79 | // pre with the copy button
80 |
81 | {domToReact(children as DOMNode[], {
82 | replace: (codeNode: any) => {
83 | /**
84 | * Replace the `` tag with the highlighted code snippet
85 | * with the `hljs` class name to apply the highlight theme (see: https://highlightjs.org/examples/)
86 | */
87 | if (codeNode.name === 'code') {
88 | const codeStr = domToReact(codeNode.children as DOMNode[]) as string;
89 | const tree = lowlight.highlight(language, codeStr);
90 |
91 | return (
92 |
93 | {parse(toHtml(tree))}
94 |
95 | );
96 | }
97 | },
98 | })}
99 |
100 | );
101 | }
102 |
103 | if (name === 'image') {
104 | return ;
105 | }
106 |
107 | if (['br', 'hr', 'img'].find((currentName: string) => currentName === name)) {
108 | return ;
109 | }
110 |
111 | return (
112 |
113 | {domToReact(children as DOMNode[])}
114 |
115 | );
116 | }
117 | },
118 | };
119 |
120 | /*
121 | * If the `` tag is not found in the html string content
122 | * it means that there are no code snippets to be highlighted.
123 | */
124 | return parse(text, { ...defaultOptions, ...options });
125 | };
126 |
127 | /*
128 | * ------------------------------ //
129 | * ---------- main props -------- //
130 | * ------------------------------ //
131 | */
132 | export type TiptapProps = {
133 | /**
134 | * the stringified html content to be parsed
135 | */
136 | content: string;
137 | /**
138 | * object that contains the class names for the html tags
139 | */
140 | classNames?: ClassNamesProps;
141 | /**
142 | * the programming language of the code snippets to be highlighted
143 | * default: `javascript`
144 | * see all available languages here: https://highlightjs.org/examples
145 | */
146 | language?: string;
147 | /**
148 | * the class name of the container div
149 | */
150 | containerClassName?: string;
151 | /**
152 | * HTMLReactParserOptions: the options of the `html-react-parser` library
153 | */
154 | } & HTMLReactParserOptions;
155 | const TiptapParser = ({
156 | classNames, containerClassName, language, content, ...rest
157 | }: TiptapProps) => {
158 | return (
159 |
160 | {parseHtml(content, classNames, language, rest)}
161 |
162 | );
163 | };
164 |
165 | export default TiptapParser;
166 |
--------------------------------------------------------------------------------
/src/dev/theme.utils.ts:
--------------------------------------------------------------------------------
1 | import {
2 | BadgeProps, PaletteMode, PaletteOptions, Theme, createTheme,
3 | } from '@mui/material';
4 | import { grey, teal } from '@mui/material/colors';
5 |
6 | export const DASHBOARD_BACKGROUND_COLOR = '#FAFBFB';
7 | export const DEFAULT_THEME_COLOR = 'green';
8 |
9 |
10 | const textDarkColor = {
11 | color: '#fff',
12 | };
13 |
14 | const defaultTheme = {
15 | palette: {
16 | secondary: {
17 | light: '#ff7961',
18 | main: '#f44336',
19 | dark: '#ba000d',
20 | contrastText: '#000',
21 | },
22 | success: {
23 | main: '#00C292',
24 | },
25 | info: {
26 | main: '#0BB2FB',
27 | },
28 | error: {
29 | main: '#E46A76',
30 | },
31 | },
32 | typography: {
33 | fontFamily: [
34 | 'DM Sans',
35 | '-apple-system',
36 | 'BlinkMacSystemFont',
37 | '"Segoe UI"',
38 | 'Roboto',
39 | '"Helvetica Neue"',
40 | 'Arial',
41 | 'sans-serif',
42 | '"Apple Color Emoji"',
43 | '"Segoe UI Emoji"',
44 | '"Segoe UI Symbol"',
45 | ].join(','),
46 | },
47 | components: {
48 | MuiSwitch: {
49 | styleOverrides: {
50 | root: ({ theme }: { theme: Theme; ownerState: BadgeProps }) => ({
51 | width: 38,
52 | height: 20,
53 | padding: 0,
54 | display: 'flex',
55 | '&:active': {
56 | '& .MuiSwitch-thumb': {
57 | width: 19,
58 | },
59 | '& .MuiSwitch-switchBase.Mui-checked': {
60 | transform: 'translateX(9px)',
61 | },
62 | },
63 | '& .MuiSwitch-switchBase': {
64 | padding: 1,
65 | '&.Mui-checked': {
66 | transform: 'translateX(16px)',
67 | color: '#fff',
68 | '& + .MuiSwitch-track': {
69 | opacity: 1,
70 | backgroundColor: theme.palette.mode === 'dark' ? '#177ddc' : '#1890ff',
71 | },
72 | },
73 | },
74 | '& .MuiSwitch-thumb': {
75 | boxShadow: '0 2px 4px 0 rgb(0 35 11 / 20%)',
76 | width: 18,
77 | height: 18,
78 | borderRadius: 20 / 2,
79 | transition: theme.transitions.create(['width'], {
80 | duration: 200,
81 | }),
82 | },
83 | '& .MuiSwitch-track': {
84 | borderRadius: 20 / 2,
85 | opacity: 1,
86 | backgroundColor:
87 | theme.palette.mode === 'dark' ? 'rgba(255,255,255,.35)' : 'rgba(0,0,0,.25)',
88 | boxSizing: 'border-box',
89 | },
90 | }),
91 | },
92 | },
93 | MuiChip: {
94 | styleOverrides: {
95 | root: {
96 | padding: '0px 3px',
97 | fontWeight: 500,
98 | fontSize: 12,
99 | },
100 | filled: {
101 | borderRadius: 6,
102 | color: '#fff',
103 | },
104 | label: {
105 | paddingLeft: 8,
106 | paddingRight: 8,
107 | },
108 | },
109 | },
110 | MuiBadge: {
111 | styleOverrides: {
112 | badge: ({ theme, ownerState }: { theme: Theme; ownerState: BadgeProps }) => {
113 | if (ownerState.overlap === 'rectangular') {
114 | return {
115 | color: '#fff',
116 | top: -2,
117 | left: 0,
118 | border: `1px solid ${theme.palette.background.paper}`,
119 | };
120 | }
121 | },
122 | },
123 | },
124 | MuiButton: {
125 | styleOverrides: {
126 | root: ({ theme }: { theme: Theme }) => ({
127 | fontWeight: 400,
128 | fontStyle: 'normal',
129 | fontSize: 16,
130 | textTransform: 'initial',
131 | padding: '12px 24px',
132 | borderRadius: 60,
133 | // border: 'none',
134 | '&.Mui-disabled': {
135 | color: '#fff',
136 | backgroundColor: theme.palette.grey[300],
137 | },
138 | }),
139 | contained: ({ theme }: { theme: Theme }) => ({
140 | backgroundColor: theme.palette.primary.main,
141 | color: 'white',
142 | boxShadow: '0px 0px 4px rgba(0, 0, 0, 0.25)',
143 | }),
144 | },
145 | },
146 | MuiStack: {
147 | defaultProps: {
148 | useFlexGap: true,
149 | },
150 | /*
151 | * mui stack has no, so overrides in the variants instead
152 | * ISSUE: https://stackoverflow.com/questions/72382224/styleoverrides-not-being-applied-with-styled-components-in-mui
153 | */
154 | variants: [
155 | {
156 | props: {},
157 | style: {
158 | flexWrap: 'wrap',
159 | },
160 | },
161 | ],
162 | },
163 | MuiDialog: {
164 | styleOverrides: {
165 | paper: {
166 | paddingTop: 4,
167 | paddingBottom: 4,
168 | },
169 | },
170 | },
171 | MuiCard: {
172 | styleOverrides: {
173 | root: ({ theme }: { theme: Theme }) => ({
174 | [theme.breakpoints.up('sm')]: {
175 | boxShadow: 'rgba(0, 0, 0, 0.1) 0px 4px 12px',
176 | },
177 | [theme.breakpoints.down('sm')]: {
178 | boxShadow: 'none',
179 | },
180 | }),
181 | },
182 | },
183 | MuiTextField: {
184 | styleOverrides: {
185 | root: {
186 | '& .MuiFormHelperText-root': {
187 | marginLeft: 0,
188 | },
189 | },
190 | },
191 | },
192 | MuiAutocomplete: {
193 | styleOverrides: {
194 | popper: ({ theme }: { theme: Theme }) => ({
195 | '& .MuiAutocomplete-listbox': {
196 | '& li': {
197 | fontSize: '16px !important',
198 | paddingTop: 14 + ' !important',
199 | paddingBottom: 14 + ' !important',
200 | fontFamily: theme.typography.fontFamily + ' !important',
201 | '&:hover': {
202 | fontWeight: 500,
203 | },
204 | '&:not(:last-child)': {
205 | borderBottom: '1px solid ' + theme.palette.grey[100] + ' !important',
206 | },
207 | },
208 | },
209 | }),
210 | },
211 | },
212 | },
213 | };
214 |
215 | export const websitePalette = {
216 | primary: {
217 | light: teal[50],
218 | main: '#03C9D7',
219 | dark: teal[900],
220 | contrastText: '#fff',
221 | },
222 | secondary: {
223 | light: '#ff7961',
224 | main: '#222222',
225 | dark: '#ba000d',
226 | contrastText: '#000',
227 | },
228 | success: {
229 | main: '#00C292',
230 | },
231 | info: {
232 | main: '#0BB2FB',
233 | },
234 | error: {
235 | main: '#E46A76',
236 | },
237 | };
238 |
239 | export const boPalette: PaletteOptions = {
240 | primary: {
241 | light: teal[50],
242 | main: '#03C9D7',
243 | dark: teal[900],
244 | contrastText: '#fff',
245 | },
246 | ...defaultTheme.palette,
247 | };
248 |
249 | const lightTheme = {
250 | ...defaultTheme,
251 | palette: {
252 | ...defaultTheme.palette,
253 | background: {
254 | default: '#FAFBFB',
255 | },
256 | },
257 | components: {
258 | ...defaultTheme.components,
259 | MuiListSubheader: {
260 | styleOverrides: {
261 | root: { color: grey[700] },
262 | },
263 | },
264 | },
265 | };
266 |
267 | const DEFAULT_BG = '#20232A';
268 | const darkTheme = {
269 | ...defaultTheme,
270 | palette: {
271 | ...defaultTheme.palette,
272 | background: {
273 | paper: '#282C34',
274 | default: DEFAULT_BG,
275 | },
276 | },
277 | components: {
278 | ...defaultTheme.components,
279 | MuiTypography: {
280 | styleOverrides: {
281 | root: textDarkColor,
282 | },
283 | },
284 | MuiListItemText: {
285 | styleOverrides: {
286 | primary: textDarkColor,
287 | },
288 | },
289 | MuiListSubheader: {
290 | styleOverrides: {
291 | root: textDarkColor,
292 | },
293 | },
294 | MuiFormControl: {
295 | styleOverrides: {
296 | root: { backgroundColor: 'transparent !important' },
297 | },
298 | },
299 | MuiInputBase: {
300 | styleOverrides: {
301 | input: {
302 | backgroundColor: DEFAULT_BG,
303 | },
304 | inputMultiline: {
305 | backgroundColor: DEFAULT_BG,
306 | },
307 | root: {
308 | backgroundColor: DEFAULT_BG,
309 | },
310 | },
311 | },
312 | MuiLink: {
313 | styleOverrides: {
314 | root: { color: '#fff !important' },
315 | },
316 | },
317 | },
318 | };
319 |
320 | export const getTheme = (mode: PaletteMode = 'light'): Theme => {
321 | const defaultTheme = mode === 'light' ? lightTheme : darkTheme;
322 |
323 | // @ts-expect-error - unknown type
324 | const theme = createTheme({
325 | ...defaultTheme,
326 | palette: {
327 | mode,
328 | primary: {
329 | light: teal[50],
330 | main: '#03C9D7',
331 | dark: teal[900],
332 | contrastText: '#fff',
333 | },
334 | ...defaultTheme.palette,
335 | },
336 | });
337 |
338 | theme.typography.h6 = {
339 | [theme.breakpoints.down('md')]: {
340 | fontSize: 14,
341 | },
342 | };
343 |
344 | return theme;
345 | };
346 |
--------------------------------------------------------------------------------
/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | import js from '@eslint/js'
2 | import globals from 'globals'
3 | import reactHooks from 'eslint-plugin-react-hooks'
4 | import stylistic from '@stylistic/eslint-plugin'
5 | import reactRefresh from 'eslint-plugin-react-refresh'
6 | import tseslint from 'typescript-eslint'
7 | import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
8 | import preferArrowFunctions from "eslint-plugin-prefer-arrow-functions";
9 | import eslint from '@eslint/js';
10 | import comments from "@eslint-community/eslint-plugin-eslint-comments/configs";
11 | import { fixupPluginRules } from "@eslint/compat";
12 | import jsxA11y from "eslint-plugin-jsx-a11y";
13 |
14 | export default tseslint.config(
15 | {
16 | ignores: [
17 | "**/dist",
18 | "**/node_modules/",
19 | "**/build",
20 | "**/vite.config.ts",
21 | "**/.prettierrc.cjs",
22 | "**/example/",
23 | ]
24 | },
25 | eslint.configs.recommended,
26 | ...tseslint.configs.recommendedTypeChecked,
27 | ...tseslint.configs.stylisticTypeChecked,
28 | {
29 | languageOptions: {
30 | globals: {
31 | ...globals.browser,
32 | Parse: "readonly",
33 | // ...globals.node,
34 | },
35 | parserOptions: {
36 | // projectService: true,
37 | projectService: {
38 | allowDefaultProject: ['*.mjs', '*.js'],
39 | defaultProject: 'tsconfig.json',
40 | },
41 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
42 | tsconfigRootDir: import.meta.dirname,
43 | },
44 | sourceType: "module",
45 | ecmaVersion: 2023,
46 | },
47 | },
48 | jsxA11y.flatConfigs.recommended,
49 | {
50 | extends: [
51 | js.configs.recommended,
52 | eslintPluginPrettierRecommended,
53 | comments.recommended,
54 | ],
55 | files: ['**/*.ts', '**/*.tsx'],
56 | plugins: {
57 | "react-hooks": fixupPluginRules(reactHooks),
58 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
59 | 'react-refresh': reactRefresh,
60 | "prefer-arrow-functions": preferArrowFunctions,
61 | '@stylistic': stylistic
62 | },
63 | rules: {
64 | ...reactHooks.configs.recommended.rules,
65 | ...stylistic.configs["recommended-flat"].rules,
66 | "@typescript-eslint/no-empty-object-type": "off",
67 | 'react-refresh/only-export-components': [
68 | 'warn',
69 | { allowConstantExport: true },
70 | ],
71 | "prettier/prettier": ["off", {
72 | singleQuote: true,
73 | }],
74 | "@typescript-eslint/no-explicit-any": "off",
75 | "import/no-extraneous-dependencies": "off",
76 | "import/extensions": "off",
77 | "no-await-in-loop": "off",
78 | "import/no-cycle": "off",
79 | "no-plusplus": "off",
80 | "no-param-reassign": "off",
81 | "prefer-template": "off",
82 | "react/react-in-jsx-scope": "off",
83 | "no-console": "off",
84 | "import/prefer-default-export": "off",
85 | "global-require": "off",
86 | "react/require-default-props": "off",
87 | "react/jsx-props-no-spreading": "off",
88 | "jsx-a11y/label-has-associated-control": "off",
89 | "react/no-unescaped-entities": "off",
90 | "jsx-a11y/control-has-associated-label": "off",
91 | "react/function-component-definition": "off",
92 | "react/prop-types": "off",
93 | "max-len": "off",
94 | "consistent-return": "off",
95 | "react/no-array-index-key": "off",
96 | "no-restricted-syntax": "off",
97 | "arrow-body-style": "off",
98 | "prefer-arrow-callback": "off",
99 | "no-unsafe-optional-chaining": "error",
100 | "prefer-arrow-functions/prefer-arrow-functions": ["warn", {
101 | allowNamedFunctions: false,
102 | classPropertiesAllowed: false,
103 | disallowPrototype: false,
104 | returnStyle: "unchanged",
105 | singleReturnOnly: false,
106 | }],
107 | "require-await": "error",
108 | "no-use-before-define": "error",
109 | "array-bracket-spacing": ["error", "never"],
110 | "block-spacing": "error",
111 | "no-unused-vars": "off",
112 | "@eslint-community/eslint-comments/disable-enable-pair": "off",
113 | // ------------------------------------ //
114 | // ------------ @stylistic ------------ //
115 | // ------------------------------------ //
116 | "@stylistic/eol-last": "error",
117 | '@stylistic/semi': 'error',
118 | "no-useless-return": "error",
119 | "@stylistic/indent": ["error", 2],
120 | "@stylistic/keyword-spacing": ["error", { "after": true, "before": true }],
121 | "@stylistic/key-spacing": ["error", { "beforeColon": false, "afterColon": true }],
122 | "@stylistic/lines-around-comment": ["error", { "beforeBlockComment": false }],
123 | "@stylistic/multiline-comment-style": ["error", "starred-block"],
124 | "@stylistic/multiline-ternary": "off",
125 | "@stylistic/no-extra-semi": "error",
126 | "@stylistic/no-floating-decimal": "error",
127 | "@stylistic/no-mixed-operators": "error",
128 | "@stylistic/no-mixed-spaces-and-tabs": "error",
129 | "@stylistic/no-multi-spaces": "error",
130 | "@stylistic/no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 0 }],
131 | "@stylistic/no-trailing-spaces": "error",
132 |
133 | "@stylistic/max-len": ["error", {
134 | "code": 110,
135 | // Ignore objects when enforcing line length (to avoid conflicts with object-curly-newline)
136 | "ignorePattern": "ImportDeclaration",
137 | "ignoreUrls": true, // Optionally, you can ignore long URLs
138 | "ignoreStrings": true, // Ignore long strings (optional)
139 | "ignoreComments": true,
140 | "ignoreTrailingComments": true,
141 | }],
142 | "@stylistic/object-curly-newline": ["error", {
143 | // e.g: const a = {};
144 | "ObjectExpression": { "consistent": true, "multiline": true, "minProperties": 0 },
145 | // "ObjectExpression": { "consistent": true, "multiline": true, "minProperties": 3 },
146 | // e.g: const { a } = obj;
147 | "ObjectPattern": { "consistent": true, "multiline": true, "minProperties": 4 },
148 | // e.g: import { a } from 'module';
149 | "ImportDeclaration": { "consistent": true, "minProperties": 4 },
150 | // e.g: export { a } from 'module';
151 | "ExportDeclaration": { "consistent": true, "multiline": true, "minProperties": 3 }
152 | }],
153 | "@stylistic/object-curly-spacing": ["error", "always"],
154 | "@stylistic/quote-props": ["error", "as-needed"],
155 | "@stylistic/quotes": ["error", "single"],
156 | "@stylistic/rest-spread-spacing": ["error", "never"],
157 | "@stylistic/semi-spacing": "error",
158 | "@stylistic/space-before-blocks": "error",
159 | "@stylistic/space-in-parens": ["error", "never"],
160 | "@stylistic/space-unary-ops": "error",
161 | "@stylistic/spaced-comment": ["error", "always"],
162 | "@stylistic/template-curly-spacing": "error",
163 | "@stylistic/member-delimiter-style": "error",
164 | "@stylistic/padding-line-between-statements": [
165 | "error",
166 | { blankLine: "always", prev: ["const", "let", "var"], next: "*"},
167 | { blankLine: "any", prev: ["const", "let", "var"], next: ["const", "let", "var"]}
168 | ],
169 | "@typescript-eslint/no-unused-vars": [
170 | "error",
171 | {
172 | "args": "all",
173 | "argsIgnorePattern": "^_",
174 | "caughtErrors": "all",
175 | "caughtErrorsIgnorePattern": "^_",
176 | "destructuredArrayIgnorePattern": "^_",
177 | "varsIgnorePattern": "^_",
178 | "ignoreRestSiblings": true
179 | }
180 | ],
181 | "@stylistic/comma-dangle": ["error", {
182 | "arrays": "always-multiline", // Trailing commas for arrays with multiple lines
183 | "objects": "always-multiline", // Trailing commas for objects with multiple lines
184 | "imports": "always-multiline", // Trailing commas in multi-line import statements
185 | "exports": "always-multiline", // Trailing commas in multi-line export statements
186 | "functions": "never" // No trailing commas for function parameters
187 | }],
188 | "@stylistic/operator-linebreak": [
189 | "error",
190 | "after",
191 | {
192 | "overrides": {
193 | "?": "before", // Example: Optional for other operators
194 | ":": "before"
195 | }
196 | }
197 | ],
198 | // ------------------------------------ //
199 | // ------------ typescript ------------ //
200 | // ------------------------------------ //
201 | "default-param-last": "off",
202 | "@typescript-eslint/default-param-last": "error",
203 | // Note: you must disable the base rule as it can report incorrect errors
204 | "max-params": "off",
205 | "@typescript-eslint/no-unsafe-assignment": "off",
206 | "@typescript-eslint/max-params": ["error", { "max": 4 }],
207 | "@typescript-eslint/method-signature-style": ["error", "property"],
208 | "@typescript-eslint/no-array-delete": "error",
209 | "@typescript-eslint/no-duplicate-enum-values": "error",
210 | "@typescript-eslint/no-duplicate-type-constituents": "error",
211 | "@typescript-eslint/no-mixed-enums": "error",
212 | "@typescript-eslint/no-require-imports": "error",
213 | "@typescript-eslint/no-unnecessary-boolean-literal-compare": ["error", { "allowComparingNullableBooleansToTrue": false }],
214 | "@typescript-eslint/no-unnecessary-template-expression": "error",
215 | "@typescript-eslint/no-unnecessary-type-arguments": "error",
216 | "@typescript-eslint/no-unsafe-function-type": "error",
217 | "@typescript-eslint/no-useless-empty-export": "error",
218 | // Note: you must disable the base rule as it can report incorrect errors
219 | "no-throw-literal": "off",
220 | "@typescript-eslint/only-throw-error": "error",
221 | "@typescript-eslint/prefer-find": "error",
222 | "@typescript-eslint/prefer-for-of": "error",
223 | "@typescript-eslint/prefer-optional-chain": "error",
224 | "@typescript-eslint/array-type": "error",
225 | "@typescript-eslint/explicit-function-return-type": "off",
226 | // these 2 following rules cause performance issue
227 | "@typescript-eslint/await-thenable": "off",
228 | "@typescript-eslint/no-floating-promises": "off",
229 | "@typescript-eslint/no-unsafe-call": "off",
230 | "@typescript-eslint/no-unsafe-member-access": "off",
231 | "@typescript-eslint/no-unsafe-argument": "off",
232 | "@typescript-eslint/no-unsafe-return": "off",
233 | "@typescript-eslint/no-unsafe-enum-comparison": "off",
234 | "@typescript-eslint/no-redundant-type-constituents": "off",
235 | // This rule will not work as expected if strictNullChecks is not enabled, so it is disabled by default.
236 | "@typescript-eslint/prefer-nullish-coalescing": "off",
237 | "@typescript-eslint/no-misused-promises": "off",
238 | "@typescript-eslint/no-non-null-asserted-optional-chain": "off",
239 | "@typescript-eslint/consistent-type-definitions": "off",
240 | "@stylistic/type-generic-spacing": ["error"],
241 | // --------- naming-convention --------- //
242 | "camelcase": "off",
243 | "@typescript-eslint/naming-convention": [
244 | "error",
245 | {
246 | "selector": "variable",
247 | "modifiers": ["const"],
248 | "format": ["camelCase", "UPPER_CASE", "PascalCase"]
249 | },
250 | {
251 | "selector": "import",
252 | "format": ["camelCase", "PascalCase", "UPPER_CASE"]
253 | },
254 | {
255 | "selector": "variable",
256 | "types": ["boolean"],
257 | "format": ["PascalCase"],
258 | // e.g: isReady, hasError, shouldFetch, ...
259 | "prefix": ["is", "should", "has", "can", "did", "will", "open"],
260 | "filter": {
261 | // exception. e.g: loadingProjects, openDialog, LOCAL
262 | "regex": "^(?:[A-Z_]+|.*loading.*|open.*)$",
263 | "match": false
264 | }
265 | },
266 | { "selector": "typeLike", "format": ["PascalCase"] },
267 | // e.g: IProject, IUser, IProjectData, ...
268 | { "selector": "interface", "format": ["PascalCase"], "prefix": ["I"] },
269 | { "selector": "typeAlias", "format": ["PascalCase"] },
270 | { "selector": "enumMember", "format": ["UPPER_CASE"] },
271 | { "selector": "class", "format": ["PascalCase"] },
272 | { "selector": "classProperty", "format": ["camelCase", "UPPER_CASE"] },
273 | { "selector": "classMethod", "format": ["camelCase"] },
274 | { "selector": "parameter", "format": ["camelCase"], "leadingUnderscore": "allow" },
275 | { "selector": "function", "format": ["camelCase"] },
276 | { "selector": "enum", "format": ["PascalCase"] },
277 | ],
278 |
279 | // ------------------------------------ //
280 | // ---------------- jsx --------------- //
281 | // ------------------------------------ //
282 | // Enforce closing bracket location in JSX
283 | '@stylistic/jsx-closing-bracket-location': [1, 'line-aligned'],
284 | '@stylistic/jsx-one-expression-per-line': [1, { allow: 'non-jsx' }],
285 | "@stylistic/jsx-pascal-case": [1, { allowAllCaps: false, allowNamespace: true, allowLeadingUnderscore: false }],
286 |
287 | "@stylistic/jsx-equals-spacing": ["error", "never"],
288 |
289 | "@stylistic/jsx-max-props-per-line": [
290 | "error",
291 | {
292 | // Maximum number of props per line
293 | "maximum": { 'single': 3, 'multi': 1 },
294 | }
295 | ],
296 | // Force new line after opening tag if multiline
297 | "@stylistic/jsx-first-prop-new-line": [2, "multiline-multiprop"],
298 | "@stylistic/jsx-props-no-multi-spaces": "error",
299 | "@stylistic/jsx-tag-spacing": ["error", {
300 | "closingSlash": "never",
301 | "beforeSelfClosing": "always",
302 | "afterOpening": "never",
303 | "beforeClosing": "never"
304 | }],
305 | /**
306 | * Sort props in JSX
307 | * NOTE: comments between props (line) is not supported
308 | * solution: name="John" // User name
309 | */
310 | "@stylistic/jsx-sort-props": ["error", {
311 | "callbacksLast": true,
312 | "shorthandFirst": true,
313 | "shorthandLast": false,
314 | "ignoreCase": true,
315 | "reservedFirst": true,
316 | "multiline": "last",
317 | }],
318 | "@stylistic/dot-location": ["error", "property"],
319 | "@stylistic/curly-newline": ["error", { "minElements": 1 }],
320 | // Wrap multiline JSX expressions in parentheses, e.g: (...)
321 | "@stylistic/jsx-wrap-multilines": ["error", {
322 | "declaration": "parens-new-line",
323 | "assignment": "parens-new-line",
324 | "return": "parens-new-line",
325 | "arrow": "parens-new-line",
326 | "condition": "parens-new-line",
327 | "logical": "parens-new-line",
328 | "prop": "parens-new-line",
329 | "propertyValue": "parens-new-line"
330 | }],
331 | },
332 | },
333 | )
334 |
--------------------------------------------------------------------------------
/example/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | import js from '@eslint/js'
2 | import globals from 'globals'
3 | import reactHooks from 'eslint-plugin-react-hooks'
4 | import stylistic from '@stylistic/eslint-plugin'
5 | import reactRefresh from 'eslint-plugin-react-refresh'
6 | import tseslint from 'typescript-eslint'
7 | import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
8 | import preferArrowFunctions from "eslint-plugin-prefer-arrow-functions";
9 | import eslint from '@eslint/js';
10 | import comments from "@eslint-community/eslint-plugin-eslint-comments/configs";
11 | import { fixupPluginRules } from "@eslint/compat";
12 | import jsxA11y from "eslint-plugin-jsx-a11y";
13 |
14 | export default tseslint.config(
15 | {
16 | ignores: [
17 | "**/dist",
18 | "**/node_modules/",
19 | "**/build",
20 | "**/vite.config.ts",
21 | "**/.prettierrc.cjs",
22 | "**/example/",
23 | ]
24 | },
25 | eslint.configs.recommended,
26 | ...tseslint.configs.recommendedTypeChecked,
27 | ...tseslint.configs.stylisticTypeChecked,
28 | {
29 | languageOptions: {
30 | globals: {
31 | ...globals.browser,
32 | Parse: "readonly",
33 | // ...globals.node,
34 | },
35 | parserOptions: {
36 | // projectService: true,
37 | projectService: {
38 | allowDefaultProject: ['*.mjs', '*.js'],
39 | defaultProject: 'tsconfig.json',
40 | },
41 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
42 | tsconfigRootDir: import.meta.dirname,
43 | },
44 | sourceType: "module",
45 | ecmaVersion: 2023,
46 | },
47 | },
48 | jsxA11y.flatConfigs.recommended,
49 | {
50 | extends: [
51 | js.configs.recommended,
52 | eslintPluginPrettierRecommended,
53 | comments.recommended,
54 | ],
55 | files: ['**/*.ts', '**/*.tsx'],
56 | plugins: {
57 | "react-hooks": fixupPluginRules(reactHooks),
58 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
59 | 'react-refresh': reactRefresh,
60 | "prefer-arrow-functions": preferArrowFunctions,
61 | '@stylistic': stylistic
62 | },
63 | rules: {
64 | ...reactHooks.configs.recommended.rules,
65 | ...stylistic.configs["recommended-flat"].rules,
66 | "@typescript-eslint/no-empty-object-type": "off",
67 | 'react-refresh/only-export-components': [
68 | 'warn',
69 | { allowConstantExport: true },
70 | ],
71 | "prettier/prettier": ["off", {
72 | singleQuote: true,
73 | }],
74 | "@typescript-eslint/no-explicit-any": "off",
75 | "import/no-extraneous-dependencies": "off",
76 | "import/extensions": "off",
77 | "no-await-in-loop": "off",
78 | "import/no-cycle": "off",
79 | "no-plusplus": "off",
80 | "no-param-reassign": "off",
81 | "prefer-template": "off",
82 | "react/react-in-jsx-scope": "off",
83 | "no-console": "off",
84 | "import/prefer-default-export": "off",
85 | "global-require": "off",
86 | "react/require-default-props": "off",
87 | "react/jsx-props-no-spreading": "off",
88 | "jsx-a11y/label-has-associated-control": "off",
89 | "react/no-unescaped-entities": "off",
90 | "jsx-a11y/control-has-associated-label": "off",
91 | "react/function-component-definition": "off",
92 | "react/prop-types": "off",
93 | "max-len": "off",
94 | "consistent-return": "off",
95 | "react/no-array-index-key": "off",
96 | "no-restricted-syntax": "off",
97 | "arrow-body-style": "off",
98 | "prefer-arrow-callback": "off",
99 | "no-unsafe-optional-chaining": "error",
100 | "prefer-arrow-functions/prefer-arrow-functions": ["warn", {
101 | allowNamedFunctions: false,
102 | classPropertiesAllowed: false,
103 | disallowPrototype: false,
104 | returnStyle: "unchanged",
105 | singleReturnOnly: false,
106 | }],
107 | "require-await": "error",
108 | "no-use-before-define": "error",
109 | "array-bracket-spacing": ["error", "never"],
110 | "block-spacing": "error",
111 | "no-unused-vars": "off",
112 | "@eslint-community/eslint-comments/disable-enable-pair": "off",
113 | // ------------------------------------ //
114 | // ------------ @stylistic ------------ //
115 | // ------------------------------------ //
116 | "@stylistic/eol-last": "error",
117 | '@stylistic/semi': 'error',
118 | "no-useless-return": "error",
119 | "@stylistic/indent": ["error", 2],
120 | "@stylistic/keyword-spacing": ["error", { "after": true, "before": true }],
121 | "@stylistic/key-spacing": ["error", { "beforeColon": false, "afterColon": true }],
122 | "@stylistic/lines-around-comment": ["error", { "beforeBlockComment": false }],
123 | "@stylistic/multiline-comment-style": ["error", "starred-block"],
124 | "@stylistic/multiline-ternary": "off",
125 | "@stylistic/no-extra-semi": "error",
126 | "@stylistic/no-floating-decimal": "error",
127 | "@stylistic/no-mixed-operators": "error",
128 | "@stylistic/no-mixed-spaces-and-tabs": "error",
129 | "@stylistic/no-multi-spaces": "error",
130 | "@stylistic/no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 0 }],
131 | "@stylistic/no-trailing-spaces": "error",
132 |
133 | "@stylistic/max-len": ["error", {
134 | "code": 110,
135 | // Ignore objects when enforcing line length (to avoid conflicts with object-curly-newline)
136 | "ignorePattern": "ImportDeclaration",
137 | "ignoreUrls": true, // Optionally, you can ignore long URLs
138 | "ignoreStrings": true, // Ignore long strings (optional)
139 | "ignoreComments": true,
140 | "ignoreTrailingComments": true,
141 | }],
142 | "@stylistic/object-curly-newline": ["error", {
143 | // e.g: const a = {};
144 | "ObjectExpression": { "consistent": true, "multiline": true, "minProperties": 0 },
145 | // "ObjectExpression": { "consistent": true, "multiline": true, "minProperties": 3 },
146 | // e.g: const { a } = obj;
147 | "ObjectPattern": { "consistent": true, "multiline": true, "minProperties": 4 },
148 | // e.g: import { a } from 'module';
149 | "ImportDeclaration": { "consistent": true, "minProperties": 4 },
150 | // e.g: export { a } from 'module';
151 | "ExportDeclaration": { "consistent": true, "multiline": true, "minProperties": 3 }
152 | }],
153 | "@stylistic/object-curly-spacing": ["error", "always"],
154 | "@stylistic/quote-props": ["error", "as-needed"],
155 | "@stylistic/quotes": ["error", "single"],
156 | "@stylistic/rest-spread-spacing": ["error", "never"],
157 | "@stylistic/semi-spacing": "error",
158 | "@stylistic/space-before-blocks": "error",
159 | "@stylistic/space-in-parens": ["error", "never"],
160 | "@stylistic/space-unary-ops": "error",
161 | "@stylistic/spaced-comment": ["error", "always"],
162 | "@stylistic/template-curly-spacing": "error",
163 | "@stylistic/member-delimiter-style": "error",
164 | "@stylistic/padding-line-between-statements": [
165 | "error",
166 | { blankLine: "always", prev: ["const", "let", "var"], next: "*"},
167 | { blankLine: "any", prev: ["const", "let", "var"], next: ["const", "let", "var"]}
168 | ],
169 | "@typescript-eslint/no-unused-vars": [
170 | "error",
171 | {
172 | "args": "all",
173 | "argsIgnorePattern": "^_",
174 | "caughtErrors": "all",
175 | "caughtErrorsIgnorePattern": "^_",
176 | "destructuredArrayIgnorePattern": "^_",
177 | "varsIgnorePattern": "^_",
178 | "ignoreRestSiblings": true
179 | }
180 | ],
181 | "@stylistic/comma-dangle": ["error", {
182 | "arrays": "always-multiline", // Trailing commas for arrays with multiple lines
183 | "objects": "always-multiline", // Trailing commas for objects with multiple lines
184 | "imports": "always-multiline", // Trailing commas in multi-line import statements
185 | "exports": "always-multiline", // Trailing commas in multi-line export statements
186 | "functions": "never" // No trailing commas for function parameters
187 | }],
188 | "@stylistic/operator-linebreak": [
189 | "error",
190 | "after",
191 | {
192 | "overrides": {
193 | "?": "before", // Example: Optional for other operators
194 | ":": "before"
195 | }
196 | }
197 | ],
198 | // ------------------------------------ //
199 | // ------------ typescript ------------ //
200 | // ------------------------------------ //
201 | "default-param-last": "off",
202 | "@typescript-eslint/default-param-last": "error",
203 | // Note: you must disable the base rule as it can report incorrect errors
204 | "max-params": "off",
205 | "@typescript-eslint/no-unsafe-assignment": "off",
206 | "@typescript-eslint/max-params": ["error", { "max": 4 }],
207 | "@typescript-eslint/method-signature-style": ["error", "property"],
208 | "@typescript-eslint/no-array-delete": "error",
209 | "@typescript-eslint/no-duplicate-enum-values": "error",
210 | "@typescript-eslint/no-duplicate-type-constituents": "error",
211 | "@typescript-eslint/no-mixed-enums": "error",
212 | "@typescript-eslint/no-require-imports": "error",
213 | "@typescript-eslint/no-unnecessary-boolean-literal-compare": ["error", { "allowComparingNullableBooleansToTrue": false }],
214 | "@typescript-eslint/no-unnecessary-template-expression": "error",
215 | "@typescript-eslint/no-unnecessary-type-arguments": "error",
216 | "@typescript-eslint/no-unsafe-function-type": "error",
217 | "@typescript-eslint/no-useless-empty-export": "error",
218 | // Note: you must disable the base rule as it can report incorrect errors
219 | "no-throw-literal": "off",
220 | "@typescript-eslint/only-throw-error": "error",
221 | "@typescript-eslint/prefer-find": "error",
222 | "@typescript-eslint/prefer-for-of": "error",
223 | "@typescript-eslint/prefer-optional-chain": "error",
224 | "@typescript-eslint/array-type": "error",
225 | "@typescript-eslint/explicit-function-return-type": "off",
226 | // these 2 following rules cause performance issue
227 | "@typescript-eslint/await-thenable": "off",
228 | "@typescript-eslint/no-floating-promises": "off",
229 | "@typescript-eslint/no-unsafe-call": "off",
230 | "@typescript-eslint/no-unsafe-member-access": "off",
231 | "@typescript-eslint/no-unsafe-argument": "off",
232 | "@typescript-eslint/no-unsafe-return": "off",
233 | "@typescript-eslint/no-unsafe-enum-comparison": "off",
234 | "@typescript-eslint/no-redundant-type-constituents": "off",
235 | // This rule will not work as expected if strictNullChecks is not enabled, so it is disabled by default.
236 | "@typescript-eslint/prefer-nullish-coalescing": "off",
237 | "@typescript-eslint/no-misused-promises": "off",
238 | "@typescript-eslint/no-non-null-asserted-optional-chain": "off",
239 | "@typescript-eslint/consistent-type-definitions": "off",
240 | "@stylistic/type-generic-spacing": ["error"],
241 | // --------- naming-convention --------- //
242 | "camelcase": "off",
243 | "@typescript-eslint/naming-convention": [
244 | "error",
245 | {
246 | "selector": "variable",
247 | "modifiers": ["const"],
248 | "format": ["camelCase", "UPPER_CASE", "PascalCase"]
249 | },
250 | {
251 | "selector": "import",
252 | "format": ["camelCase", "PascalCase", "UPPER_CASE"]
253 | },
254 | {
255 | "selector": "variable",
256 | "types": ["boolean"],
257 | "format": ["PascalCase"],
258 | // e.g: isReady, hasError, shouldFetch, ...
259 | "prefix": ["is", "should", "has", "can", "did", "will", "open"],
260 | "filter": {
261 | // exception. e.g: loadingProjects, openDialog, LOCAL
262 | "regex": "^(?:[A-Z_]+|.*loading.*|open.*)$",
263 | "match": false
264 | }
265 | },
266 | { "selector": "typeLike", "format": ["PascalCase"] },
267 | // e.g: IProject, IUser, IProjectData, ...
268 | { "selector": "interface", "format": ["PascalCase"], "prefix": ["I"] },
269 | { "selector": "typeAlias", "format": ["PascalCase"] },
270 | { "selector": "enumMember", "format": ["UPPER_CASE"] },
271 | { "selector": "class", "format": ["PascalCase"] },
272 | { "selector": "classProperty", "format": ["camelCase", "UPPER_CASE"] },
273 | { "selector": "classMethod", "format": ["camelCase"] },
274 | { "selector": "parameter", "format": ["camelCase"], "leadingUnderscore": "allow" },
275 | { "selector": "function", "format": ["camelCase"] },
276 | { "selector": "enum", "format": ["PascalCase"] },
277 | ],
278 |
279 | // ------------------------------------ //
280 | // ---------------- jsx --------------- //
281 | // ------------------------------------ //
282 | // Enforce closing bracket location in JSX
283 | '@stylistic/jsx-closing-bracket-location': [1, 'line-aligned'],
284 | '@stylistic/jsx-one-expression-per-line': [1, { allow: 'non-jsx' }],
285 | "@stylistic/jsx-pascal-case": [1, { allowAllCaps: false, allowNamespace: true, allowLeadingUnderscore: false }],
286 |
287 | "@stylistic/jsx-equals-spacing": ["error", "never"],
288 |
289 | "@stylistic/jsx-max-props-per-line": [
290 | "error",
291 | {
292 | // Maximum number of props per line
293 | "maximum": { 'single': 3, 'multi': 1 },
294 | }
295 | ],
296 | // Force new line after opening tag if multiline
297 | "@stylistic/jsx-first-prop-new-line": [2, "multiline-multiprop"],
298 | "@stylistic/jsx-props-no-multi-spaces": "error",
299 | "@stylistic/jsx-tag-spacing": ["error", {
300 | "closingSlash": "never",
301 | "beforeSelfClosing": "always",
302 | "afterOpening": "never",
303 | "beforeClosing": "never"
304 | }],
305 | /**
306 | * Sort props in JSX
307 | * NOTE: comments between props (line) is not supported
308 | * solution: name="John" // User name
309 | */
310 | "@stylistic/jsx-sort-props": ["error", {
311 | "callbacksLast": true,
312 | "shorthandFirst": true,
313 | "shorthandLast": false,
314 | "ignoreCase": true,
315 | "reservedFirst": true,
316 | "multiline": "last",
317 | }],
318 | "@stylistic/dot-location": ["error", "property"],
319 | "@stylistic/curly-newline": ["error", { "minElements": 1 }],
320 | // Wrap multiline JSX expressions in parentheses, e.g: (...)
321 | "@stylistic/jsx-wrap-multilines": ["error", {
322 | "declaration": "parens-new-line",
323 | "assignment": "parens-new-line",
324 | "return": "parens-new-line",
325 | "arrow": "parens-new-line",
326 | "condition": "parens-new-line",
327 | "logical": "parens-new-line",
328 | "prop": "parens-new-line",
329 | "propertyValue": "parens-new-line"
330 | }],
331 | },
332 | },
333 | )
334 |
--------------------------------------------------------------------------------