@eagleoutice/flowr-dev
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
225 lines (224 loc) • 11.8 kB
TypeScript
import { AbstractFlowrAnalyzerContext } from './abstract-flowr-analyzer-context';
import type { RParseRequestFromText, RParseRequest, RParseRequestFromFile } from '../../r-bridge/retriever';
import type { FlowrAnalyzerLoadingOrderContext, ReadOnlyFlowrAnalyzerLoadingOrderContext } from './flowr-analyzer-loading-order-context';
import { FlowrAnalyzerProjectDiscoveryPlugin } from '../plugins/project-discovery/flowr-analyzer-project-discovery-plugin';
import { FlowrAnalyzerFilePlugin } from '../plugins/file-plugins/flowr-analyzer-file-plugin';
import { FlowrFile, type FlowrFileProvider, FileRole } from './flowr-file';
import type { FlowrDescriptionFile } from '../plugins/file-plugins/files/flowr-description-file';
import type { FlowrNewsFile } from '../plugins/file-plugins/files/flowr-news-file';
import type { FlowrNamespaceFile } from '../plugins/file-plugins/files/flowr-namespace-file';
import type { FlowrManifestFile } from '../plugins/file-plugins/files/flowr-manifest-files';
import type { ProjectKind } from './project-kind';
import type { FlowrAnalyzerContext } from './flowr-analyzer-context';
import type { InvalidationEvent, InvalidationEventReceiver } from '../cache/flowr-cache';
/**
* This is a request to process a folder as a project, which will be expanded by the registered {@link FlowrAnalyzerProjectDiscoveryPlugin}s.
*/
export interface RProjectAnalysisRequest {
readonly request: 'project';
/**
* The path to the root folder (an absolute path is probably best here).
*/
readonly content: string;
}
export type RAnalysisRequest = RParseRequest | RProjectAnalysisRequest;
export type RoleBasedFiles = {
[FileRole.Description]: FlowrDescriptionFile[];
[FileRole.News]: FlowrNewsFile[];
[FileRole.Namespace]: FlowrNamespaceFile[];
[FileRole.Manifest]: FlowrManifestFile[];
[FileRole.Vignette]: FlowrFileProvider[];
[FileRole.Test]: FlowrFileProvider[];
[FileRole.Install]: FlowrFileProvider[];
[FileRole.License]: FlowrFileProvider[];
[FileRole.VirtualEnv]: FlowrFileProvider[];
[FileRole.Startup]: FlowrFileProvider[];
[FileRole.Environment]: FlowrFileProvider[];
[FileRole.Source]: FlowrFileProvider[];
[FileRole.Data]: FlowrFileProvider[];
[FileRole.Other]: FlowrFileProvider[];
};
/**
* This is the read-only interface for the files context, which is used to manage all files known to the {@link FlowrAnalyzer}.
* It prevents you from modifying the available files, but allows you to inspect them (which is probably what you want when using the {@link FlowrAnalyzer}).
* If you are a {@link FlowrAnalyzerProjectDiscoveryPlugin} and want to modify the available files, you can use the {@link FlowrAnalyzerFilesContext} directly.
*/
export interface ReadOnlyFlowrAnalyzerFilesContext {
/**
* The name of this context.
*/
readonly name: string;
/**
* The loading order context provides access to the loading order of script files in the project.
*/
readonly loadingOrder: ReadOnlyFlowrAnalyzerLoadingOrderContext;
/**
* Get all requests that have been added to this context.
* @example If you want to obtain all description files, use
* ```ts
* getFilesByRole(SpecialFileRole.Description)
* ```
*/
getFilesByRole<Role extends FileRole>(role: Role): RoleBasedFiles[Role];
/**
* Get all files known to this context.
* @returns An array of all files.
*/
getAllFiles(): FlowrFileProvider[];
/**
* Get a file by its path.
* Checks both disk-backed files and inline files.
* However, this will not load new files that have not yet been requested by flowR.
* @param path - The exact path of the file.
* @returns The file if found, otherwise `undefined`.
*/
getFileByPath(path: string): FlowrFileProvider | undefined;
/**
* Check if the context has a cached file with the given path.
* @param path - The path to the file.
*/
hasCached(path: string): boolean;
/**
* Check if the context has a file with the given path.
* Please note, that this may also check the file system, depending on the configuration
* (see {@link FlowrConfig.project.resolveUnknownPathsOnDisk}).
* @param path - The path to the file.
*
* If you do not know the exact path or, e.g., casing of the file, use {@link exists} instead.
*/
hasFile(path: string): boolean;
/**
* Check if a file exists at the given path, optionally ignoring case.
* @param path - The path to the file.
* @param ignoreCase - Whether to ignore case when checking for the file.
*
* Please note that this method checks the file system based on the configuration (see {@link FlowrConfig.project.resolveUnknownPathsOnDisk}).
* @returns The actual path of the file if it exists, otherwise `undefined`.
*/
exists(path: string, ignoreCase: boolean): string | undefined;
/** The project root folder (common directory of the requested roots), or `undefined` if none was requested. */
root(): string | undefined;
/**
* Until parsers support multiple request types from the virtual context system,
* we resolve their contents.
*/
resolveRequest(r: RParseRequest): {
r: RParseRequestFromText;
path?: string;
};
/**
* Get all files that have been considered during dataflow analysis.
*/
consideredFilesList(): readonly string[];
/**
* Classify the {@link ProjectKind} of the project from its files. A {@link ProjectKind.ShinyApp | shiny app}
* is detected first, as apps commonly ship a `DESCRIPTION` too. The finer distinctions rely on the source
* files, which are only known once the dataflow ran; before that a non-package project reports
* {@link ProjectKind.Unknown}. The result is cached and invalidated whenever the files change.
*/
projectKind(): ProjectKind;
/**
* The root paths that were requested for analysis: the folder for a project request, or the containing
* folder for a single-file request. Useful to report back which inputs did not resolve to anything.
*/
getRequestedRoots(): readonly string[];
/**
* The total number of files known to this context (every file added via {@link addFile}, both disk-backed and inline).
*
* This is unrelated to {@link getFilesByRole}: A file counted here may have no role, one role, or several
* (summing `getFilesByRole(role).length` over all roles can both over-count (multi-role files) and under-count (roleless files) relative to this method).
*
* Files that were merely considered during dataflow analysis (see {@link consideredFilesList}) but never actually added to the context are not included.
* @returns The number of files currently held by this context.
*/
getFileCount(): number;
}
/**
* This is the analyzer file context to be modified by all plugins that affect the files.
* If you are interested in inspecting these files, refer to {@link ReadOnlyFlowrAnalyzerFilesContext}.
* Plugins, however, can use this context directly to modify files.
*/
export declare class FlowrAnalyzerFilesContext extends AbstractFlowrAnalyzerContext<RProjectAnalysisRequest, (RParseRequest | FlowrFile<string>)[], FlowrAnalyzerProjectDiscoveryPlugin> implements ReadOnlyFlowrAnalyzerFilesContext, InvalidationEventReceiver {
readonly name = "flowr-analyzer-files-context";
readonly loadingOrder: FlowrAnalyzerLoadingOrderContext;
private files;
private inlineFiles;
private readonly fileLoaders;
private readonly context;
/** these are all the paths of files that have been considered by the dataflow graph (even if not added) */
private readonly consideredFiles;
/** User-registered project discovery plugins; if non-empty, they replace the default. */
private readonly discoveryPlugins;
private byRole;
/** cached {@link projectKind}, invalidated whenever the files change (added or reset) */
private projectKindCache;
private requestedRoots;
/** directories already scanned by {@link discoverImplicitSources}, so a sibling implicit source is not re-triggered for every file added from it */
private implicitSourceDirs;
/** cached {@link root}, fixed on first use as the ids built from it have to stay stable */
private rootCache;
private rootResolved;
constructor(context: FlowrAnalyzerContext, loadingOrder: FlowrAnalyzerLoadingOrderContext, plugins: readonly FlowrAnalyzerProjectDiscoveryPlugin[], fileLoaders: readonly FlowrAnalyzerFilePlugin[]);
reset(): void;
/** The directory the analysis was asked about: a `project`'s folder, or the one holding the requested file(s). */
root(): string | undefined;
/** The path of `filePath` seen from the {@link root}, see {@link relativeTo}. */
relativePath(filePath: string): string;
receive(event: InvalidationEvent): void;
/**
* Record that a file has been considered during dataflow analysis.
*/
addConsideredFile(path: string): void;
/**
* Get all files that have been considered during dataflow analysis.
*/
consideredFilesList(): readonly string[];
projectKind(): ProjectKind;
getRequestedRoots(): readonly string[];
private classifyProject;
/**
* Add multiple requests to the context. This is just a convenience method that calls {@link addRequest} for each request.
*/
addRequests(requests: readonly RAnalysisRequest[]): void;
/**
* Add a request to the context. If the request is of type `project`, it will be expanded using the registered {@link FlowrAnalyzerProjectDiscoveryPlugin}s.
* User-registered discovery plugins replace the built-in default; if none are registered, the default runs.
*/
private addRequest;
/**
* A single-file request otherwise skips project discovery entirely, so a sibling `project.implicitSources`
* entry (e.g. a shiny app's `s.R` next to the analyzed `t.R`) would never be found. If `implicitSources` is
* configured, scan `fileContent`'s directory once and add whatever matches.
*/
private discoverImplicitSources;
/**
* Add multiple files to the context. This is just a convenience method that calls {@link addFile} for each file.
*/
addFiles(files: (string | FlowrFileProvider | RParseRequestFromFile)[]): void;
/**
* Add a file to the context. If the file has a special role, it will be added to the corresponding list of special files.
* This method also applies any registered {@link FlowrAnalyzerFilePlugin}s to the file before adding it to the context.
*/
addFile(file: string | FlowrFileProvider | RParseRequestFromFile, roles?: readonly FileRole[]): FlowrFileProvider<{
toString(): string;
}>;
hasCached(path: string): boolean;
hasFile(path: string): boolean;
exists(p: string, ignoreCase: boolean): string | undefined;
private fileLoadPlugins;
/** Resolve the file at `path`, loading it from disk through the {@link FlowrAnalyzerFilePlugin}s if unknown. */
resolveFile(path: string): FlowrFileProvider | undefined;
resolveRequest(r: RParseRequest): {
r: RParseRequestFromText;
path?: string;
};
/**
* Get all requests that have been added to this context.
* This is a convenience method that calls {@link FlowrAnalyzerLoadingOrderContext.getLoadingOrder}.
*/
computeLoadingOrder(): readonly RParseRequest[];
getFilesByRole<Role extends FileRole>(role: Role): RoleBasedFiles[Role];
getAllFiles(): FlowrFileProvider[];
getFileByPath(path: string): FlowrFileProvider | undefined;
getFileCount(): number;
}