File
result
|
Type
|
PluginFolderStats[]
|
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')}`);
}