@roq/eslint-plugin
Advanced tools
Comparing version 1.0.2 to 1.0.3
@@ -8,3 +8,3 @@ const path = require('path'); | ||
const resourceIdentifiers = { | ||
backend: ['decorators', 'dtos', 'entities', 'enums', 'guards', 'interfaces', 'loaders', 'mappers', 'models', 'repositories', 'resolvers', 'services', 'strategies', 'scalars', 'schemas'], | ||
backend: ['decorators', 'dtos', 'entities', 'enums', 'guards', 'interfaces', 'loaders', 'mappers', 'models', 'repositories', 'resolvers', 'services', 'strategies', 'scalars', 'schemas', 'config'], | ||
frontend: ['components', 'configuration', 'layouts', 'pages', 'shared', 'slices', 'styles', 'utils', 'views'], | ||
@@ -11,0 +11,0 @@ frontendCommon: ['interfaces', 'roq-hooks', 'roq-ui'], |
@@ -8,3 +8,3 @@ const path = require('path'); | ||
const getPathPatterns = (backendBasePattern, frontendBasePattern, backendModuleList) => { | ||
const getPathPatterns = (backendBasePattern, frontendBasePattern) => { | ||
const pathPatterns = { backend: {}, frontend: {} }; | ||
@@ -14,5 +14,2 @@ for (const resource of resourceIdentifiers.backend) { | ||
} | ||
for (const resource of backendModuleList) { | ||
pathPatterns.backend[resource] = [backendBasePattern, resource].join(escapedSep); | ||
} | ||
@@ -35,3 +32,2 @@ for (const resource of resourceIdentifiers.frontend) { | ||
let frontendTestsPattern = ''; | ||
const backendModuleList = []; | ||
@@ -58,22 +54,4 @@ if (pluginSettings) { | ||
} | ||
const backendPath = backendBasePath || 'backend/src'; | ||
try { | ||
const [backendPathInitToken] = backendPath.split('/').filter((e) => e !== ''); | ||
const rootPath = path.resolve('./').replace(backendPathInitToken, ''); | ||
const dirContents = fs.readdirSync(path.resolve(rootPath, backendPath), { withFileTypes: true }); | ||
dirContents.forEach((e) => { | ||
if (e.isDirectory()) { | ||
const nestedDirContents = fs.readdirSync(path.resolve(rootPath, backendPath, e.name), | ||
{ withFileTypes: true }); | ||
if (nestedDirContents.some((el) => el.isFile())) { | ||
backendModuleList.push(e.name); | ||
} else { | ||
nestedDirContents.forEach(({ name: moduleName }) => { | ||
backendModuleList.push([e.name, moduleName].join(escapedSep)); | ||
}); | ||
} | ||
} | ||
}); | ||
} catch (_) { /* */ } | ||
const pathPatterns = getPathPatterns(backendBasePattern, frontendBasePattern, backendModuleList); | ||
const pathPatterns = getPathPatterns(backendBasePattern, frontendBasePattern); | ||
return { | ||
@@ -85,3 +63,2 @@ backendBasePattern, | ||
frontendTestsPattern, | ||
backendModuleList, | ||
}; | ||
@@ -92,4 +69,10 @@ } | ||
const isBackendModule = (dirPath) => { | ||
const filesInDir = fs.readdirSync(dirPath); | ||
return filesInDir.includes('services') || filesInDir.includes('resolvers') || filesInDir.includes('models'); | ||
}; | ||
module.exports = { | ||
get, | ||
isBackendModule, | ||
}; |
@@ -67,3 +67,2 @@ const { | ||
backendTestsPattern, | ||
backendModuleList, | ||
} = executionContext.get(context); | ||
@@ -101,6 +100,6 @@ const errorReportObject = { | ||
5) in dtos allow both "arg-type" and "dto" as suffix | ||
6) in frontend components should have a suffix "component". | ||
*/ | ||
const backendModuleRootPaths = backendModuleList.map((e) => new RegExp(`${pathPatterns.backend[e]}$`)); | ||
const isBackendModuleRoot = backendModuleRootPaths.some((e) => e.test(dirPath)); | ||
const isBackendModuleRoot = executionContext.isBackendModule(dirPath); | ||
const isDTO = (new RegExp(pathPatterns.backend.dtos, 'g')).test(dirPath); | ||
@@ -110,4 +109,13 @@ const isReactHook = new RegExp(`${[pathPatterns.frontend['roq-hooks'], allowedNamingPattern].join(escapedSep)}$`).test(dirPath); | ||
const isView = (new RegExp(`${[pathPatterns.frontend.views, allowedNamingPattern].join(escapedSep)}$`)).test(dirPath); | ||
const isComponent = (new RegExp(`${[pathPatterns.frontend.components, allowedNamingPattern].join(escapedSep)}$`)).test(dirPath); | ||
if (isBackendModuleRoot) { // type has to be module | ||
if (isComponent) { | ||
parentType = 'component'; | ||
const suffixCheck = checkSuffixMismatch(file, parentType); | ||
mismatchType = suffixCheck.mismatchType; | ||
if (mismatchType) { | ||
expectedFileName = suffixCheck.expectedFileName; | ||
isExceptionalMismatch = true; | ||
} | ||
} else if (isBackendModuleRoot) { // type has to be module | ||
parentType = 'module'; | ||
@@ -114,0 +122,0 @@ const suffixCheck = checkSuffixMismatch(file, parentType); |
@@ -9,3 +9,9 @@ const { fileContext } = require('../../helper'); | ||
if (isUnsuitableDirectory) { | ||
const functionWithDecoratorCheck = directoryType === 'entity' | ||
&& node.parent | ||
&& node.parent.parent | ||
&& node.parent.parent.type === 'FunctionExpression' | ||
&& node.parent.parent.parent.decorators; | ||
if (!functionWithDecoratorCheck && isUnsuitableDirectory) { | ||
context.report({ | ||
@@ -12,0 +18,0 @@ loc: node.loc, |
@@ -5,3 +5,3 @@ const { | ||
const { fileContext } = require('../../helper'); | ||
const { fileContext, executionContext } = require('../../helper'); | ||
@@ -51,3 +51,3 @@ const dirChecked = []; | ||
const dirName = dirPath.split(sep).slice(-1)[0]; | ||
let dirName = dirPath.split(sep).slice(-1)[0]; | ||
let desiredCasing = 'lowerCased'; | ||
@@ -58,2 +58,9 @@ let desiredSeparators = 'any'; | ||
const { frontendBasePattern } = executionContext.get(context); | ||
const frontendCheck = new RegExp(frontendBasePattern).test(dirPath); | ||
if (frontendCheck && dirName.startsWith('[') && dirName.endsWith(']')) { | ||
dirName = dirName.slice(1, -1); | ||
} | ||
if (ruleOptions && ruleOptions[0]) { | ||
@@ -60,0 +67,0 @@ desiredCasing = ruleOptions[0].casing || 'lowerCased'; |
const { | ||
escapedSep, | ||
sep, | ||
} = require('../../constants'); | ||
@@ -20,2 +21,3 @@ const { fileContext, executionContext } = require('../../helper'); | ||
invalidPageResource: 'Default Export name should match parent directory name (when pascalCased). Expected {{expectedPageName}}', | ||
invalidNestedPageResource: 'Default Export name in case of nested pages, can only include directory names in order (when pascalCased, ignoring directory names enclosed in []).', | ||
}, | ||
@@ -31,2 +33,4 @@ schema: [], | ||
const dirPath = parentDir.absolutePath; | ||
const dirPathParts = dirPath.split(sep); | ||
const dirName = dirPathParts.slice(-1)[0]; | ||
@@ -40,2 +44,13 @@ const isPage = new RegExp(pathPatterns.frontend.pages, 'g').test(dirPath); | ||
if (ruleExecutionRequired) { | ||
const checkPagesDirIndex = (name) => name === 'pages'; | ||
const pagesDirIndex = dirPathParts.findIndex(checkPagesDirIndex); | ||
let pagesPathArr = dirPathParts.slice(pagesDirIndex + 1); | ||
if (pagesPathArr[0] === 'components') { | ||
pagesPathArr = pagesPathArr.slice(1); | ||
} | ||
const checkDirNameIndex = (element) => element === dirName; | ||
const isNestedPageResource = pagesPathArr.findIndex(checkDirNameIndex) >= 1; | ||
reportingObj.Program = (node) => { | ||
@@ -51,2 +66,27 @@ const defaultDeclarations = node.body.filter((e) => e.type === 'ExportDefaultDeclaration'); | ||
}); | ||
} else if (isNestedPageResource) { | ||
const orderedDirectorySequence = []; | ||
pagesPathArr.forEach((dir) => { | ||
if (!dir.startsWith('[') && !dir.endsWith(']')) { | ||
orderedDirectorySequence.push( | ||
...(fileContext.getWordsFromCaseDifferedStrings(dir).map( | ||
(word) => word.charAt(0).toUpperCase() + word.slice(1), | ||
)), | ||
); | ||
} | ||
}); | ||
orderedDirectorySequence.push('Page'); // this is for efficient name matching | ||
const exportedPageName = defaultDeclarations[0].declaration.name; | ||
const wordsInExportedName = exportedPageName.split(/(?=[A-Z])/); | ||
let previousWordAtIndex = 0; | ||
const isInvalidName = !exportedPageName.endsWith('Page') || wordsInExportedName.length <= 1 || wordsInExportedName.some((word) => { | ||
previousWordAtIndex = orderedDirectorySequence.indexOf(word, previousWordAtIndex); | ||
return previousWordAtIndex === -1; | ||
}); | ||
if (isInvalidName) { | ||
context.report({ | ||
node: defaultDeclarations[0], | ||
messageId: 'invalidNestedPageResource', | ||
}); | ||
} | ||
} else if (defaultDeclarations[0].declaration.name !== expectedPageName) { | ||
@@ -53,0 +93,0 @@ context.report({ |
{ | ||
"name": "@roq/eslint-plugin", | ||
"version": "1.0.2", | ||
"version": "1.0.3", | ||
"main": "index.js", | ||
@@ -5,0 +5,0 @@ "scripts": { |
@@ -20,2 +20,12 @@ const { RuleTester } = require('eslint'); | ||
}, | ||
{ | ||
code: '@Entity()\n' | ||
+ 'export class anyEntity {\n' | ||
+ ' @BeforeInsert()\n' | ||
+ ' convertToJson(): void {\n' | ||
+ ' if (this.parameters) this.parameters = JSON.parse(this.parameters);\n' | ||
+ ' }\n' | ||
+ '}', | ||
filename: 'backend/src/auth/entities/refresh-token.entity.ts', | ||
}, | ||
], | ||
@@ -22,0 +32,0 @@ invalid: [ |
const { RuleTester } = require('eslint'); | ||
const ruleUnderTest = require('../../../lib/rules/no-invalid-dirname'); | ||
const ruleTesterInstance = new RuleTester({ parserOptions: { ecmaVersion: 2021 } }); | ||
const ruleTesterInstance = new RuleTester({ | ||
parserOptions: { ecmaVersion: 2021 }, | ||
settings: { | ||
'roq-linter': { | ||
backendBasePath: 'backend/src', | ||
frontendBasePath: 'frontend/src', | ||
backendTestsBasePath: 'backend/tests', | ||
}, | ||
}, | ||
}); | ||
@@ -9,2 +18,6 @@ ruleTesterInstance.run('no-invalid-dirname', ruleUnderTest, { | ||
{ | ||
code: '// File Path : frontend/src/pages/users/edit/[name]/index.tsx', | ||
filename: 'frontend/src/pages/users/edit/[name]/index.tsx', | ||
}, | ||
{ | ||
code: '// File Path : backend/src/auth/auth.module.ts', | ||
@@ -104,3 +117,21 @@ filename: 'backend/src/auth/auth.module.ts', | ||
}, | ||
{ | ||
code: '// File Path : frontend/src/pages/users/edit/test[name]/index.tsx', | ||
errors: [ | ||
{ | ||
messageId: 'invalidDirName', | ||
data: { | ||
dirName: 'test[name]', | ||
expectedDescription: 'It should be a lowerCased string without numbers and with hyphen separator(s)', | ||
}, | ||
line: 1, | ||
column: 1, | ||
endLine: 1, | ||
endColumn: 66, | ||
}, | ||
], | ||
filename: 'frontend/src/pages/users/edit/test[name]/index.tsx', | ||
options: [{ casing: 'lowerCased', allowedSeparator: 'hyphen', noNumerics: true }], | ||
}, | ||
], | ||
}); |
@@ -32,2 +32,12 @@ const { RuleTester } = require('eslint'); | ||
}, | ||
{ | ||
code: `/* UsersEditPage definition */ | ||
export default UsersEditPage;`, | ||
filename: 'frontend/src/pages/users/edit/[id]/index.tsx', | ||
}, | ||
{ | ||
code: `/* UsersCreateNewTestPage definition */ | ||
export default UsersCreateNewTestPage;`, | ||
filename: 'frontend/src/pages/users/create/new/test/index.tsx', | ||
}, | ||
], | ||
@@ -86,3 +96,31 @@ invalid: [ | ||
}, | ||
{ | ||
code: `/* AnyOtherDirNamePage definition */ | ||
export default AnyOtherDirNamePage;`, | ||
errors: [ | ||
{ | ||
messageId: 'invalidNestedPageResource', | ||
line: 2, | ||
column: 7, | ||
endLine: 2, | ||
endColumn: 42, | ||
}, | ||
], | ||
filename: 'frontend/src/pages/users/edit/[id]/index.tsx', | ||
}, | ||
{ | ||
code: `/* TestUsersPage definition */ | ||
export default TestUsersPage;`, | ||
errors: [ | ||
{ | ||
messageId: 'invalidNestedPageResource', | ||
line: 2, | ||
column: 7, | ||
endLine: 2, | ||
endColumn: 36, | ||
}, | ||
], | ||
filename: 'frontend/src/pages/users/edit/test/[id]/index.tsx', | ||
}, | ||
], | ||
}); |
164715
96
4562