UNPKG

@eagleoutice/flowr-dev

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

225 lines (224 loc) 11.8 kB
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; }