Detect Plugin

Automatically detect the minimum required ES version for your code

Overview

The Detect plugin automatically determines the minimum ECMAScript version required to run your JavaScript code. It analyzes all ES features in your code and returns the lowest ES version that supports all detected features.

Installation & Import

import detectPlugin from 'fast-brake/src/plugins/detect';

// Use with detector
const detector = new Detector({ plugins: [detectPlugin] });

How It Works

The Detect plugin:

  1. Scans your code for all ES features
  2. Identifies the highest ES version feature used
  3. Returns the minimum ES version required
  4. Provides a complete feature inventory

Usage Examples

Basic Minimum Version Detection

import { getMinimumESVersion } from 'fast-brake';

const code = `
  const arrow = () => {};
  const template = \`Hello \${name}\`;
  class MyClass {}
`;

const minVersion = await getMinimumESVersion(code);
console.log(minVersion); // 'es2015'

Detailed Feature Analysis

import { Detector } from 'fast-brake';
import detectPlugin from 'fast-brake/src/plugins/detect';

const detector = new Detector({ plugins: [detectPlugin] });
await detector.initialize();

const code = `
  // ES2015 features
  const arrow = () => {};
  
  // ES2017 features  
  async function getData() {
    await fetch('/api');
  }
  
  // ES2020 features
  const value = obj?.prop ?? 'default';
`;

const analysis = await detector.detect(code);

// Group features by version
const featuresByVersion = {};
analysis.forEach(feature => {
  if (!featuresByVersion[feature.version]) {
    featuresByVersion[feature.version] = [];
  }
  featuresByVersion[feature.version].push(feature.name);
});

console.log('Features by ES version:', featuresByVersion);
// {
//   es2015: ['arrow_functions', 'const'],
//   es2017: ['async_functions', 'await'],
//   es2020: ['optional_chaining', 'nullish_coalescing']
// }

console.log('Minimum required version:', 'es2020');

Common Use Cases

Dynamic Transpilation

import { getMinimumESVersion } from 'fast-brake';
import * as babel from '@babel/core';

async function smartTranspile(code, targetVersion = 'es5') {
  const minVersion = await getMinimumESVersion(code);
  
  // Skip transpilation if already compatible
  if (compareVersions(minVersion, targetVersion) <= 0) {
    console.log(`Code is already ${targetVersion} compatible`);
    return code;
  }
  
  // Only transpile what's needed
  const result = babel.transformSync(code, {
    presets: [
      ['@babel/preset-env', {
        targets: { esmodules: targetVersion === 'es2015' }
      }]
    ]
  });
  
  return result.code;
}

Build Optimization

// webpack.config.js
const { getMinimumESVersion } = require('fast-brake');

module.exports = async (env, argv) => {
  // Analyze entry point
  const entryCode = fs.readFileSync('./src/index.js', 'utf-8');
  const minVersion = await getMinimumESVersion(entryCode);
  
  console.log(`Project requires minimum ${minVersion}`);
  
  return {
    entry: './src/index.js',
    module: {
      rules: [{
        test: /\.js$/,
        exclude: /node_modules/,
        use: minVersion === 'es5' ? [] : ['babel-loader']
      }]
    },
    optimization: {
      // Skip optimization for modern code
      minimize: minVersion !== 'es2020'
    }
  };
};

Polyfill Detection

import { getMinimumESVersion } from 'fast-brake';

const polyfillMap = {
  es2015: ['promise-polyfill'],
  es2017: ['async-polyfill'],
  es2018: ['async-iteration-polyfill'],
  es2019: ['array-flat-polyfill'],
  es2020: ['optional-chaining-polyfill'],
  es2021: ['string-replaceall-polyfill'],
  es2022: ['array-at-polyfill']
};

async function getRequiredPolyfills(code) {
  const minVersion = await getMinimumESVersion(code);
  const polyfills = [];
  
  // Collect all polyfills needed up to the min version
  for (const [version, versionPolyfills] of Object.entries(polyfillMap)) {
    if (compareVersions(version, minVersion) <= 0) {
      polyfills.push(...versionPolyfills);
    }
  }
  
  return polyfills;
}

// Usage
const code = readFileSync('app.js', 'utf-8');
const polyfills = await getRequiredPolyfills(code);
console.log('Required polyfills:', polyfills);

Version Comparison

Helper Functions

// Compare ES versions
function compareVersions(a, b) {
  const versions = ['es5', 'es2015', 'es2016', 'es2017', 'es2018', 
                    'es2019', 'es2020', 'es2021', 'es2022', 'es2023', 
                    'es2024', 'es2025'];
  return versions.indexOf(a) - versions.indexOf(b);
}

// Check if code requires transpilation
async function needsTranspilation(code, target) {
  const minVersion = await getMinimumESVersion(code);
  return compareVersions(minVersion, target) > 0;
}

// Get transpilation preset
async function getTranspilationPreset(code) {
  const minVersion = await getMinimumESVersion(code);
  
  const presets = {
    es5: '@babel/preset-env',
    es2015: '@babel/preset-es2015',
    es2017: '@babel/preset-es2017',
    // ... etc
  };
  
  return presets[minVersion] || '@babel/preset-env';
}

Integration Examples

With ESLint

// .eslintrc.js
const { getMinimumESVersion } = require('fast-brake');

module.exports = async () => {
  // Detect project's ES version
  const files = glob.sync('src/**/*.js');
  let projectMinVersion = 'es5';
  
  for (const file of files) {
    const code = fs.readFileSync(file, 'utf-8');
    const fileVersion = await getMinimumESVersion(code);
    if (compareVersions(fileVersion, projectMinVersion) > 0) {
      projectMinVersion = fileVersion;
    }
  }
  
  // Configure ESLint based on detected version
  return {
    parserOptions: {
      ecmaVersion: getEcmaVersion(projectMinVersion)
    },
    env: {
      es6: projectMinVersion !== 'es5'
    }
  };
};

With Package.json

// update-engines.js
import { getMinimumESVersion } from 'fast-brake';
import { glob } from 'glob';
import fs from 'fs';

async function updateEngines() {
  const files = glob.sync('src/**/*.js');
  let maxVersion = 'es5';
  
  for (const file of files) {
    const code = fs.readFileSync(file, 'utf-8');
    const version = await getMinimumESVersion(code);
    if (compareVersions(version, maxVersion) > 0) {
      maxVersion = version;
    }
  }
  
  // Map ES version to Node.js version
  const nodeVersionMap = {
    es5: '0.10',
    es2015: '6.0',
    es2017: '8.0',
    es2018: '10.0',
    es2019: '12.0',
    es2020: '14.0',
    es2021: '15.0',
    es2022: '16.0'
  };
  
  // Update package.json
  const pkg = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
  pkg.engines = {
    node: `>=${nodeVersionMap[maxVersion] || '14.0'}`
  };
  
  fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2));
  console.log(`Updated engines to Node.js ${nodeVersionMap[maxVersion]}+ for ${maxVersion}`);
}

Performance Considerations

Caching Results

const versionCache = new Map();

async function getCachedMinVersion(code) {
  const hash = createHash('md5').update(code).digest('hex');
  
  if (versionCache.has(hash)) {
    return versionCache.get(hash);
  }
  
  const version = await getMinimumESVersion(code);
  versionCache.set(hash, version);
  
  return version;
}

Batch Processing

async function analyzeCodebase(directory) {
  const files = glob.sync(`${directory}/**/*.js`);
  const results = new Map();
  
  // Process in parallel for performance
  const analyses = await Promise.all(
    files.map(async file => {
      const code = fs.readFileSync(file, 'utf-8');
      const version = await getMinimumESVersion(code);
      return { file, version };
    })
  );
  
  // Aggregate results
  analyses.forEach(({ file, version }) => {
    if (!results.has(version)) {
      results.set(version, []);
    }
    results.get(version).push(file);
  });
  
  return results;
}

Output Format

The Detect plugin returns the minimum ES version as a string:

// Possible return values
'es5'      // No modern features detected
'es2015'   // ES6 features (arrow functions, classes, etc.)
'es2016'   // Exponentiation operator
'es2017'   // Async/await
'es2018'   // Rest/spread properties
'es2019'   // Array.flat, Array.flatMap
'es2020'   // Optional chaining, nullish coalescing
'es2021'   // Logical assignment operators
'es2022'   // Private fields, top-level await
'es2023'   // Array methods (findLast, toSorted, etc.)
'es2024'   // Promise.withResolvers, Object.groupBy
'es2025'   // Temporal API, Set methods