Files
crowd-funder-from-jason/docs/migration/migration-templates/eslint-rules.md
Matthew Raymer db5da0cdfc docs: reorganize documentation structure with 7-item folder limits
- Create logical sub-folder classification for all documentation
- Organize 91 migration files into component-specific folders
- Separate user guides, build system, migration, and development docs
- Maintain maximum 7 items per folder for easy navigation
- Add comprehensive README and reorganization summary
- Ensure all changes tracked in git with proper versioning

Structure:
- user-guides/ (3 items): user-facing documentation
- build-system/ (3 items): core, platforms, automation
- migration/ (6 items): assessments, testing, templates
- development/ (4 items): tools and standards
- architecture/, testing/, examples/ (ready for future docs)

Total: 24 folders created, all within 7-item limits
2025-07-22 09:18:30 +00:00

8.4 KiB

ESLint Rules for PlatformServiceMixin Migration

Overview

Custom ESLint rules to enforce PlatformServiceMixin patterns and prevent regression to legacy patterns.

Rules Configuration

Add to .eslintrc.js:

module.exports = {
  // ... existing config
  rules: {
    // ... existing rules
    
    // Custom rules for PlatformServiceMixin migration
    'timesafari/no-direct-database-util': 'error',
    'timesafari/no-legacy-logging': 'error',
    'timesafari/require-mixin-for-database': 'error',
    'timesafari/no-direct-platform-service': 'warn',
    'timesafari/prefer-mixin-methods': 'warn',
  },
  
  // Custom rules plugin
  plugins: ['timesafari'],
}

Custom Rules Implementation

Create eslint-plugin-timesafari/index.js:

module.exports = {
  rules: {
    'no-direct-database-util': {
      meta: {
        type: 'problem',
        docs: {
          description: 'Disallow direct imports from databaseUtil',
          category: 'Migration',
          recommended: true,
        },
        schema: [],
      },
      create(context) {
        return {
          ImportDeclaration(node) {
            if (node.source.value.includes('databaseUtil')) {
              context.report({
                node,
                message: 'Direct databaseUtil imports are deprecated. Use PlatformServiceMixin instead.',
              });
            }
          },
        };
      },
    },
    
    'no-legacy-logging': {
      meta: {
        type: 'problem',
        docs: {
          description: 'Disallow legacy logging methods',
          category: 'Migration',
          recommended: true,
        },
        schema: [],
      },
      create(context) {
        return {
          ImportDeclaration(node) {
            if (node.specifiers.some(spec => spec.imported?.name === 'logConsoleAndDb')) {
              context.report({
                node,
                message: 'logConsoleAndDb is deprecated. Use PlatformServiceMixin $log methods instead.',
              });
            }
          },
          CallExpression(node) {
            if (node.callee.name === 'logConsoleAndDb') {
              context.report({
                node,
                message: 'logConsoleAndDb is deprecated. Use this.$logAndConsole() instead.',
              });
            }
          },
        };
      },
    },
    
    'require-mixin-for-database': {
      meta: {
        type: 'suggestion',
        docs: {
          description: 'Require PlatformServiceMixin for components using database operations',
          category: 'Migration',
          recommended: true,
        },
        schema: [],
      },
      create(context) {
        let hasDbOperations = false;
        let hasMixin = false;
        
        return {
          CallExpression(node) {
            // Check for database operations
            if (node.callee.property && 
                ['dbQuery', 'dbExec', 'dbGetOneRow'].includes(node.callee.property.name)) {
              hasDbOperations = true;
            }
          },
          Property(node) {
            // Check for mixin usage
            if (node.key.name === 'mixins' && 
                node.value.elements?.some(el => el.name === 'PlatformServiceMixin')) {
              hasMixin = true;
            }
          },
          'Program:exit'() {
            if (hasDbOperations && !hasMixin) {
              context.report({
                node: context.getSourceCode().ast,
                message: 'Components using database operations should include PlatformServiceMixin.',
              });
            }
          },
        };
      },
    },
    
    'no-direct-platform-service': {
      meta: {
        type: 'suggestion',
        docs: {
          description: 'Discourage direct PlatformServiceFactory usage',
          category: 'Migration',
          recommended: false,
        },
        schema: [],
      },
      create(context) {
        return {
          CallExpression(node) {
            if (node.callee.object?.name === 'PlatformServiceFactory' &&
                node.callee.property?.name === 'getInstance') {
              context.report({
                node,
                message: 'Consider using PlatformServiceMixin methods instead of direct PlatformServiceFactory.',
              });
            }
          },
        };
      },
    },
    
    'prefer-mixin-methods': {
      meta: {
        type: 'suggestion',
        docs: {
          description: 'Prefer mixin convenience methods over direct database calls',
          category: 'Migration',
          recommended: false,
        },
        schema: [],
      },
      create(context) {
        return {
          CallExpression(node) {
            // Check for patterns that could use mixin methods
            if (node.callee.property?.name === 'dbQuery') {
              const arg = node.arguments[0];
              if (arg && arg.type === 'Literal') {
                const sql = arg.value.toLowerCase();
                if (sql.includes('select * from contacts')) {
                  context.report({
                    node,
                    message: 'Consider using this.$getAllContacts() instead of direct SQL.',
                  });
                }
                if (sql.includes('select * from settings')) {
                  context.report({
                    node,
                    message: 'Consider using this.$settings() instead of direct SQL.',
                  });
                }
              }
            }
          },
        };
      },
    },
  },
};

Pre-commit Hook

Create .pre-commit-config.yaml:

repos:
  - repo: local
    hooks:
      - id: eslint-migration-check
        name: ESLint Migration Check
        entry: npx eslint --ext .vue --rule 'timesafari/no-direct-database-util: error'
        language: system
        files: \.vue$
        
      - id: no-legacy-logging
        name: No Legacy Logging
        entry: bash -c 'if grep -r "logConsoleAndDb" src/ --include="*.vue" --include="*.ts"; then echo "Found legacy logging imports"; exit 1; fi'
        language: system
        pass_filenames: false

Migration Validation Script

Create scripts/validate-migration.sh:

#!/bin/bash

echo "🔍 Validating PlatformServiceMixin migration..."

# Check for legacy patterns
echo "Checking for legacy databaseUtil imports..."
LEGACY_DB_IMPORTS=$(grep -r "import.*databaseUtil" src/ --include="*.vue" --include="*.ts" | wc -l)
echo "Found $LEGACY_DB_IMPORTS legacy databaseUtil imports"

echo "Checking for legacy logging imports..."
LEGACY_LOG_IMPORTS=$(grep -r "logConsoleAndDb" src/ --include="*.vue" --include="*.ts" | wc -l)
echo "Found $LEGACY_LOG_IMPORTS legacy logging imports"

# Check for mixin usage
echo "Checking for PlatformServiceMixin usage..."
MIXIN_USAGE=$(grep -r "PlatformServiceMixin" src/ --include="*.vue" | wc -l)
echo "Found $MIXIN_USAGE files using PlatformServiceMixin"

# Check for direct PlatformService usage
echo "Checking for direct PlatformService usage..."
DIRECT_PLATFORM=$(grep -r "PlatformServiceFactory.getInstance" src/ --include="*.vue" --include="*.ts" | wc -l)
echo "Found $DIRECT_PLATFORM direct PlatformService usages"

# Summary
echo ""
echo "📊 Migration Status Summary:"
echo "- Legacy databaseUtil imports: $LEGACY_DB_IMPORTS (should be 0)"
echo "- Legacy logging imports: $LEGACY_LOG_IMPORTS (should be 0)"
echo "- Mixin usage: $MIXIN_USAGE (should be high)"
echo "- Direct PlatformService usage: $DIRECT_PLATFORM (should be low)"

# Set exit code based on legacy usage
if [ $LEGACY_DB_IMPORTS -gt 0 ] || [ $LEGACY_LOG_IMPORTS -gt 0 ]; then
    echo "❌ Migration validation failed - legacy patterns found"
    exit 1
else
    echo "✅ Migration validation passed - no legacy patterns found"
    exit 0
fi

Usage

  1. Install ESLint rules:

    npm install --save-dev eslint-plugin-timesafari
    
  2. Run validation:

    npm run lint
    ./scripts/validate-migration.sh
    
  3. Fix issues automatically:

    npm run lint -- --fix
    

IDE Integration

VS Code Settings

Add to .vscode/settings.json:

{
  "eslint.validate": [
    "javascript",
    "typescript",
    "vue"
  ],
  "eslint.options": {
    "extensions": [".js", ".ts", ".vue"]
  }
}

WebStorm Settings

  1. Go to Settings → Languages & Frameworks → JavaScript → Code Quality Tools → ESLint
  2. Enable ESLint
  3. Set configuration file to .eslintrc.js
  4. Add .vue to file extensions