File

libs/nx/src/executors/extract-plugin-metadata/compat/utils/collect-plugin-folders.ts

Extends

FolderStats

Index

Properties

Properties

hasPluginTypeInMarkdown
isPlugin
markdownFilePath
Type string
moduleFilePath
Type string
import { LoggerApi } from '@angular-devkit/core/src/logger';
import { existsSync, readdirSync, statSync } from 'fs';
import { basename, join } from 'path';
import { readPackageJsonFile } from '../../../shared/metadata-utils/extract-package-json-data';
import { isTypeAttributeSetToPlugin } from '../utils/markdown/extract-md-file-data';
import { TypeInMarkdown } from '../utils/markdown/markdown-validators';

export interface PluginFoldersCollection {
  secondaryEntryPoint: string | undefined;
  result: PluginFolderStats[];
  skipped: FolderStats[];
}

export interface FolderStats {
  folderPath: string;
  isPlugin: boolean;
  moduleFilePath: string | undefined;
  injectableFilePath: string | undefined;
  markdownFilePath: string | undefined;
  hasPluginTypeInMarkdown: boolean;
}

export interface PluginFolderStats extends FolderStats {
  isPlugin: true;
  moduleFilePath: string;
  markdownFilePath: string;
  hasPluginTypeInMarkdown: true;
}

enum FolderFileType {
  Module = 'module',
  Injectable = 'injectable',
  Markdown = 'markdown'
}

/**
 * Find all suitable folders that contain a Plugin.
 * A Plugin needs to be an Injectable, with a Markdown Files and Module.
 * All with the same name
 */
export async function collectPluginFolders(source: string): Promise<PluginFoldersCollection[]> {
  const result: PluginFoldersCollection[] = [];

  const mainEntryPointResult: PluginFoldersCollection = await processEntrypoint(source);
  result.push(mainEntryPointResult);

  const secondaryEntries = collectSecondaryEntrypoints(source);
  for (const entry of secondaryEntries) {
    const secondaryEntryPointResult: PluginFoldersCollection = await processEntrypoint(entry, true);
    result.push(secondaryEntryPointResult);
  }

  return result;
}

function collectSecondaryEntrypoints(source: string) {
  const files = readdirSync(source);
  const secondaryEntries: string[] = [];
  for (const folderName of files) {
    const currentFolderPath = join(source, folderName);
    const stats = statSync(currentFolderPath);
    if (stats.isDirectory() && isSecondaryEntryPoint(currentFolderPath)) {
      secondaryEntries.push(currentFolderPath);
    }
  }
  return secondaryEntries;
}

function isSecondaryEntryPoint(projectFolder: string) {
  const packageJsonExists = existsSync(join(projectFolder, 'package.json'));
  const ngPackageJsonExists = existsSync(join(projectFolder, 'ng-package.json'));

  if (ngPackageJsonExists) {
    return true;
  }

  if (packageJsonExists) {
    const packageJson = readPackageJsonFile(projectFolder);
    const isSecondaryEntryPoint = Boolean(packageJson.ngPackage?.lib?.entryFile);
    return isSecondaryEntryPoint;
  }

  return false;
}

async function processEntrypoint(
  entryPoint: string,
  secondary = false
): Promise<PluginFoldersCollection> {
  const sourceFolder = join(entryPoint, 'src', 'lib');

  const result: PluginFolderStats[] = [];
  const skipped: FolderStats[] = [];

  if (existsSync(sourceFolder)) {
    const pluginFolders = readdirSync(sourceFolder);
    for (const folderName of pluginFolders) {
      const currentFolderPath = join(sourceFolder, folderName);
      const stats = statSync(currentFolderPath);

      if (stats.isDirectory()) {
        const stats: FolderStats = await getFolderStats(currentFolderPath);

        if (stats.isPlugin) {
          result.push(stats as PluginFolderStats);
        } else {
          skipped.push(stats);
        }
      }
    }
  }

  return {
    secondaryEntryPoint: secondary ? basename(entryPoint) : undefined,
    result,
    skipped
  };
}

function getExpectedFileNames(type: FolderFileType, folderPath = '') {
  const folderName = basename(folderPath);

  switch (type) {
    case FolderFileType.Injectable:
      return [`${folderName}.service.ts`, `${folderName}.ts`];
    case FolderFileType.Module:
      return [`${folderName}.module.ts`];
    case FolderFileType.Markdown:
      return [`${folderName}.md`];
    default:
      return [];
  }
}

function getFilePath(type: FolderFileType, folderPath = '') {
  return getExpectedFileNames(type, folderPath)
    .map((name) => join(folderPath, name))
    .find((name) => existsSync(name));
}

/**
 * Create a small stat report about the given folder.
 * Check if all required files for a Plugin do exist
 * and return details of the check for a later analysis/logging
 * to provide some verbose output.
 */
async function getFolderStats(folderPath: string): Promise<FolderStats> {
  const injectableFilePath = getFilePath(FolderFileType.Injectable, folderPath);
  const moduleFilePath = getFilePath(FolderFileType.Module, folderPath);
  const markdownFilePath = getFilePath(FolderFileType.Markdown, folderPath);

  let hasPluginTypeInMarkdown = false;
  if (markdownFilePath) {
    hasPluginTypeInMarkdown = await isTypeAttributeSetToPlugin(markdownFilePath);
  }

  const isPlugin = Boolean(moduleFilePath && markdownFilePath && hasPluginTypeInMarkdown);

  return {
    folderPath,
    isPlugin,
    moduleFilePath,
    markdownFilePath,
    injectableFilePath,
    hasPluginTypeInMarkdown
  };
}

export function logPluginFolderWarnings(stats: FolderStats, logger: LoggerApi) {
  if (!logger) return;

  const folderName = basename(stats.folderPath);
  const warnings: string[] = [];

  if (!stats.moduleFilePath) {
    const expectedModuleFileName = getExpectedFileNames(FolderFileType.Module, folderName);
    warnings.push(`no module file, expected ${expectedModuleFileName}`);
  }

  if (!stats.markdownFilePath) {
    const expectedMarkdownFileName = getExpectedFileNames(FolderFileType.Markdown, folderName);
    warnings.push(`no markdown file, expected ${expectedMarkdownFileName}`);
  }

  if (stats.markdownFilePath && !stats.hasPluginTypeInMarkdown) {
    warnings.push(
      `no "type" attribute set to ${TypeInMarkdown.PLUGIN} in markdown file ${basename(
        stats.markdownFilePath
      )}`
    );
  }

  logger.warn(`└── ${folderName}`);
  logger.warn(`    Not a Plugin Module, check the following reasons`);
  logger.warn(`${warnings.map((v) => `        '${v}'`).join('\n')}`);
}

results matching ""

    No results matching ""