@@ -16,6 +16,44 @@ interface AnalysisResult {
1616 recommendations : string [ ] ;
1717}
1818
19+ interface ModuleCheckExceptions {
20+ skipCodeOfConductCheck ?: boolean ;
21+ skipDependabotCheck ?: boolean ;
22+ skipEslintChecks ?: boolean ;
23+ skipReadmeChecks ?: boolean ;
24+ }
25+
26+ const MODULE_CHECK_EXCEPTIONS : Record < string , ModuleCheckExceptions > = {
27+ // mmpm is a standalone management tool, not a classic MM module runtime package.
28+ // Some mirror-module README/community checks are not meaningful for it.
29+ "Bee-Mar/mmpm" : {
30+ skipReadmeChecks : true ,
31+ skipDependabotCheck : true ,
32+ skipEslintChecks : true
33+ }
34+ } ;
35+
36+ function getRepositoryId ( moduleUrl : string ) : string | null {
37+ try {
38+ const url = new URL ( moduleUrl ) ;
39+ const segments = url . pathname . split ( "/" ) . filter ( Boolean ) ;
40+ if ( segments . length < 2 ) {
41+ return null ;
42+ }
43+
44+ const owner = segments [ 0 ] ;
45+ const repo = segments [ 1 ] . replace ( / \. g i t $ / u, "" ) ;
46+ if ( ! owner || ! repo ) {
47+ return null ;
48+ }
49+
50+ return `${ owner } /${ repo } ` ;
51+ }
52+ catch {
53+ return null ;
54+ }
55+ }
56+
1957// Comprehensive TEXT_RULES with all deprecated APIs, typos, and recommendations
2058const TEXT_RULES : Record < string , TextRule > = {
2159 "new Buffer(" : {
@@ -330,6 +368,8 @@ export async function analyzeModule(
330368 files : string [ ]
331369) : Promise < AnalysisResult > {
332370 const issues : string [ ] = [ ] ;
371+ const moduleRepoId = getRepositoryId ( moduleUrl ) ;
372+ const moduleExceptions = moduleRepoId ? MODULE_CHECK_EXCEPTIONS [ moduleRepoId ] ?? { } : { } ;
333373
334374 // Filter out files in node_modules and .git directories.
335375 // Use path segments instead of substring matching so ".github" files are not excluded.
@@ -399,7 +439,7 @@ export async function analyzeModule(
399439 const relativePath = filePath . startsWith ( modulePath )
400440 ? filePath . slice ( modulePath . length ) . replace ( / ^ \/ + / , "" )
401441 : filePath ;
402- if ( relativePath === "README.md" ) {
442+ if ( relativePath === "README.md" && ! moduleExceptions . skipReadmeChecks ) {
403443 // Check for update section
404444 if ( ! content . includes ( "## Updat" ) ) {
405445 issues . push (
@@ -471,69 +511,71 @@ export async function analyzeModule(
471511 ) ;
472512 }
473513
474- if ( ! filenames . has ( "CODE_OF_CONDUCT" ) && ! filenames . has ( "CODE_OF_CONDUCT.MD" ) ) {
514+ if ( ! moduleExceptions . skipCodeOfConductCheck && ! filenames . has ( "CODE_OF_CONDUCT" ) && ! filenames . has ( "CODE_OF_CONDUCT.MD" ) ) {
475515 issues . push (
476516 "Recommendation: There is no CODE_OF_CONDUCT file. It is recommended to add one ([example CODE_OF_CONDUCT file](https://github.com/KristjanESPERANTO/MMM-ApothekenNotdienst/blob/main/CODE_OF_CONDUCT.md))."
477517 ) ;
478518 }
479519
480- if ( ! filenames . has ( "DEPENDABOT.YAML" ) && ! filenames . has ( "DEPENDABOT.YML" ) ) {
520+ if ( ! moduleExceptions . skipDependabotCheck && ! filenames . has ( "DEPENDABOT.YAML" ) && ! filenames . has ( "DEPENDABOT.YML" ) ) {
481521 issues . push (
482522 "Recommendation: There is no dependabot configuration file. It is recommended to add one ([example dependabot file](https://github.com/KristjanESPERANTO/MMM-ApothekenNotdienst/blob/main/.github/dependabot.yaml))."
483523 ) ;
484524 }
485525
486526 // ESLint checks
487- const hasOldEslintrc = filenames . has ( "ESLINTRC" ) || filenames . has ( "ESLINTRC.JSON" ) || filenames . has ( "ESLINTRC.JS" ) || filenames . has ( "ESLINTRC.YML" ) || filenames . has ( "ESLINTRC.YAML" ) ;
488- const hasNewEslint = filenames . has ( "ESLINT.CONFIG.JS" ) || filenames . has ( "ESLINT.CONFIG.MJS" ) ;
489-
490- if ( hasOldEslintrc ) {
491- issues . push ( "Recommendation: Replace eslintrc by new flat config." ) ;
492- } else if ( ! hasNewEslint ) {
493- issues . push (
494- "Recommendation: No ESLint configuration was found. ESLint is very helpful, it is worth using it even for small projects ([basic instructions](https://github.com/MagicMirrorOrg/MagicMirror-3rd-Party-Modules/blob/main/guides/eslint.md))."
495- ) ;
496- } else {
497- // Check if ESLint is in package.json dependencies
498- const packageJsonFiles = relevantFiles . filter ( ( f ) => f . endsWith ( "package.json" ) ) ;
499- for ( const pkgFile of packageJsonFiles ) {
500- const pkgContent = await readFile ( pkgFile , "utf-8" ) . catch ( ( ) => "{}" ) ;
501- try {
502- const pkg = JSON . parse ( pkgContent ) ;
503- if (
504- ! pkg . dependencies ?. eslint &&
505- ! pkg . devDependencies ?. eslint
506- ) {
507- issues . push (
508- "Recommendation: ESLint is not in the dependencies or devDependencies. It is recommended to add it to one of them."
509- ) ;
510- }
527+ if ( ! moduleExceptions . skipEslintChecks ) {
528+ const hasOldEslintrc = filenames . has ( "ESLINTRC" ) || filenames . has ( "ESLINTRC.JSON" ) || filenames . has ( "ESLINTRC.JS" ) || filenames . has ( "ESLINTRC.YML" ) || filenames . has ( "ESLINTRC.YAML" ) ;
529+ const hasNewEslint = filenames . has ( "ESLINT.CONFIG.JS" ) || filenames . has ( "ESLINT.CONFIG.MJS" ) ;
511530
512- // Check lint script
513- if ( pkg . scripts ) {
514- if ( ! pkg . scripts . lint ) {
515- issues . push ( "Recommendation: No lint script found in package.json. It is recommended to add one." ) ;
516- } else if ( ! pkg . scripts . lint . includes ( "eslint" ) ) {
531+ if ( hasOldEslintrc ) {
532+ issues . push ( "Recommendation: Replace eslintrc by new flat config." ) ;
533+ } else if ( ! hasNewEslint ) {
534+ issues . push (
535+ "Recommendation: No ESLint configuration was found. ESLint is very helpful, it is worth using it even for small projects ([basic instructions](https://github.com/MagicMirrorOrg/MagicMirror-3rd-Party-Modules/blob/main/guides/eslint.md))."
536+ ) ;
537+ } else {
538+ // Check if ESLint is in package.json dependencies
539+ const packageJsonFiles = relevantFiles . filter ( ( f ) => f . endsWith ( "package.json" ) ) ;
540+ for ( const pkgFile of packageJsonFiles ) {
541+ const pkgContent = await readFile ( pkgFile , "utf-8" ) . catch ( ( ) => "{}" ) ;
542+ try {
543+ const pkg = JSON . parse ( pkgContent ) ;
544+ if (
545+ ! pkg . dependencies ?. eslint &&
546+ ! pkg . devDependencies ?. eslint
547+ ) {
517548 issues . push (
518- "Recommendation: The lint script in package.json does not contain `eslint` . It is recommended to add it."
549+ "Recommendation: ESLint is not in the dependencies or devDependencies . It is recommended to add it to one of them ."
519550 ) ;
520551 }
552+
553+ // Check lint script
554+ if ( pkg . scripts ) {
555+ if ( ! pkg . scripts . lint ) {
556+ issues . push ( "Recommendation: No lint script found in package.json. It is recommended to add one." ) ;
557+ } else if ( ! pkg . scripts . lint . includes ( "eslint" ) ) {
558+ issues . push (
559+ "Recommendation: The lint script in package.json does not contain `eslint`. It is recommended to add it."
560+ ) ;
561+ }
562+ }
563+ } catch {
564+ // Silently ignore JSON parse errors
521565 }
522- } catch {
523- // Silently ignore JSON parse errors
524566 }
525- }
526567
527- // Check for defineConfig in eslint.config.js
528- const eslintConfigFiles = relevantFiles . filter (
529- ( f ) => f . endsWith ( "eslint.config.js" ) || f . endsWith ( "eslint.config.mjs" )
530- ) ;
531- for ( const configFile of eslintConfigFiles ) {
532- const configContent = await readFile ( configFile , "utf-8" ) . catch ( ( ) => "" ) ;
533- if ( ! configContent . includes ( "defineConfig" ) ) {
534- issues . push (
535- `Recommendation: The ESLint configuration file \`${ configFile . split ( "/" ) . pop ( ) } \` does not contain \`defineConfig\`. It is recommended to use it.`
536- ) ;
568+ // Check for defineConfig in eslint.config.js
569+ const eslintConfigFiles = relevantFiles . filter (
570+ ( f ) => f . endsWith ( "eslint.config.js" ) || f . endsWith ( "eslint.config.mjs" )
571+ ) ;
572+ for ( const configFile of eslintConfigFiles ) {
573+ const configContent = await readFile ( configFile , "utf-8" ) . catch ( ( ) => "" ) ;
574+ if ( ! configContent . includes ( "defineConfig" ) ) {
575+ issues . push (
576+ `Recommendation: The ESLint configuration file \`${ configFile . split ( "/" ) . pop ( ) } \` does not contain \`defineConfig\`. It is recommended to use it.`
577+ ) ;
578+ }
537579 }
538580 }
539581 }
0 commit comments