diff --git a/lib/utils/filename.js b/lib/utils/filename.js index 906820a..49d5c47 100644 --- a/lib/utils/filename.js +++ b/lib/utils/filename.js @@ -24,11 +24,29 @@ export const getFolderPath = (p) => posix.join(posix.dirname(p), posix.sep); * @param {string} filename filename without path * @param {boolean} [ignoreMiddleExtensions] flag to ignore middle extensions */ -export const getBasename = (filename, ignoreMiddleExtensions = false) => - filename.substring( - 0, - ignoreMiddleExtensions ? filename.indexOf('.') : filename.lastIndexOf('.') - ); +export const getBasename = (filename, ignoreMiddleExtensions = false) => { + const findDotsOutsideBrackets = (str) => { + const positions = []; + let depth = 0; + + for (let i = 0; i < str.length; i++) { + if (str[i] === '[') depth++; + else if (str[i] === ']') depth--; + else if (str[i] === '.' && depth === 0) { + positions.push(i); + } + } + return positions; + }; + + const dotPositions = findDotsOutsideBrackets(filename); + + const curPosition = ignoreMiddleExtensions + ? dotPositions[0] + : dotPositions[dotPositions.length - 1]; + + return filename.substring(0, curPosition); +}; /** * @returns {string[]} all folders diff --git a/tests/lib/rules/filename-naming-convention.posix.js b/tests/lib/rules/filename-naming-convention.posix.js index b9babe3..2ce1580 100644 --- a/tests/lib/rules/filename-naming-convention.posix.js +++ b/tests/lib/rules/filename-naming-convention.posix.js @@ -804,6 +804,82 @@ ruleTester.run( } ); +ruleTester.run( + "filename-naming-convention with Next.js dynamic routes and option: [{ '**/*.{js,jsx,ts,tsx}': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE' }, { ignoreMiddleExtensions: true }]", + rule, + { + valid: [ + { + code: "var foo = 'bar';", + filename: 'src/pages/blog/[[...userId]].tsx', + options: [ + { '**/*.{js,jsx,ts,tsx}': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE' }, + { ignoreMiddleExtensions: true }, + ], + }, + { + code: "var foo = 'bar';", + filename: 'src/pages/blog/[[...slug]].tsx', + options: [ + { '**/*.{js,jsx,ts,tsx}': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE' }, + { ignoreMiddleExtensions: true }, + ], + }, + { + code: "var foo = 'bar';", + filename: 'src/pages/blog/[...params].tsx', + options: [ + { '**/*.{js,jsx,ts,tsx}': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE' }, + { ignoreMiddleExtensions: true }, + ], + }, + { + code: "var foo = 'bar';", + filename: 'src/pages/user/[id].tsx', + options: [ + { '**/*.{js,jsx,ts,tsx}': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE' }, + { ignoreMiddleExtensions: true }, + ], + }, + { + code: "var foo = 'bar';", + filename: 'src/pages/post/[postId].test.tsx', + options: [ + { '**/*.{js,jsx,ts,tsx}': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE' }, + { ignoreMiddleExtensions: true }, + ], + }, + { + code: "var foo = 'bar';", + filename: 'src/pages/api/user.test.ts', + options: [ + { '**/*.{js,jsx,ts,tsx}': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE' }, + { ignoreMiddleExtensions: true }, + ], + }, + ], + + invalid: [ + { + code: "var foo = 'bar';", + filename: 'src/pages/blog/Invalid_Name.tsx', + options: [ + { '**/*.{js,jsx,ts,tsx}': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE' }, + { ignoreMiddleExtensions: true }, + ], + errors: [ + { + message: + 'The filename "Invalid_Name.tsx" does not match the "NEXT_JS_PAGE_ROUTER_FILENAME_CASE" pattern', + column: 1, + line: 1, + }, + ], + }, + ], + } +); + ruleTester.run( "filename-naming-convention with option: [{ '**/*.js': '__+([a-z])', '**/*.jsx': '__+([a-z])' }]", rule, diff --git a/tests/lib/rules/filename-naming-convention.windows.js b/tests/lib/rules/filename-naming-convention.windows.js index 524a4d2..8bde869 100644 --- a/tests/lib/rules/filename-naming-convention.windows.js +++ b/tests/lib/rules/filename-naming-convention.windows.js @@ -826,6 +826,82 @@ ruleTester.run( } ); +ruleTester.run( + "filename-naming-convention with Next.js dynamic routes and option on Windows: [{ '**/*.{js,jsx,ts,tsx}': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE' }, { ignoreMiddleExtensions: true }]", + rule, + { + valid: [ + { + code: "var foo = 'bar';", + filename: 'src\\pages\\blog\\[[...userId]].tsx', + options: [ + { '**/*.{js,jsx,ts,tsx}': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE' }, + { ignoreMiddleExtensions: true }, + ], + }, + { + code: "var foo = 'bar';", + filename: 'src\\pages\\blog\\[[...slug]].tsx', + options: [ + { '**/*.{js,jsx,ts,tsx}': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE' }, + { ignoreMiddleExtensions: true }, + ], + }, + { + code: "var foo = 'bar';", + filename: 'src\\pages\\blog\\[...params].tsx', + options: [ + { '**/*.{js,jsx,ts,tsx}': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE' }, + { ignoreMiddleExtensions: true }, + ], + }, + { + code: "var foo = 'bar';", + filename: 'src\\pages\\user\\[id].tsx', + options: [ + { '**/*.{js,jsx,ts,tsx}': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE' }, + { ignoreMiddleExtensions: true }, + ], + }, + { + code: "var foo = 'bar';", + filename: 'src\\pages\\post\\[postId].test.tsx', + options: [ + { '**/*.{js,jsx,ts,tsx}': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE' }, + { ignoreMiddleExtensions: true }, + ], + }, + { + code: "var foo = 'bar';", + filename: 'src\\pages\\api\\user.test.ts', + options: [ + { '**/*.{js,jsx,ts,tsx}': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE' }, + { ignoreMiddleExtensions: true }, + ], + }, + ], + + invalid: [ + { + code: "var foo = 'bar';", + filename: 'src\\pages\\blog\\Invalid_Name.tsx', + options: [ + { '**/*.{js,jsx,ts,tsx}': 'NEXT_JS_PAGE_ROUTER_FILENAME_CASE' }, + { ignoreMiddleExtensions: true }, + ], + errors: [ + { + message: + 'The filename "Invalid_Name.tsx" does not match the "NEXT_JS_PAGE_ROUTER_FILENAME_CASE" pattern', + column: 1, + line: 1, + }, + ], + }, + ], + } +); + ruleTester.run( "filename-naming-convention with option on Windows: [{ '**/*.js': 'CAMEL_CASE' }, { ignoreMiddleExtensions: true }]", rule,