UNPKG

@eagleoutice/flowr-dev

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

76 lines 3.83 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.resolveClassifyOptions = resolveClassifyOptions; exports.isShinyAppSignals = isShinyAppSignals; exports.classifyProjectKind = classifyProjectKind; const project_kind_1 = require("./project-kind"); /** default DESCRIPTION `Type:` values marking a shiny app */ const shinyDescriptionTypes = new Set(['shiny', 'shiny-app', 'shinyapp']); /** default (lower-cased) file names a shiny app is assembled from */ const shinyEntryFiles = new Set(['app.r', 'ui.r', 'server.r', 'global.r']); /** default evidence that a file is part of a shiny app (a load of the shiny stack or a call to one of its entry points) */ const shinyUsage = /(?:library|require|requireNamespace|loadNamespace)\s*\(\s*['"]?(?:shiny|bslib|shinydashboard|shinyMobile|golem|flexdashboard)\b|\b(?:shinyApp|shinyServer|shinyUI|runApp|fluidPage|fluidRow|navbarPage|bootstrapPage|fillPage|dashboardPage|page_fluid|page_sidebar|page_navbar|pageWithSidebar)\s*\(/; /** default extensions marking a notebook / literate document */ const notebookExtension = /\.(ipynb|rmd|rmarkdown|qmd|rnw)$/; /** Resolve {@link ClassificationConfig} into {@link ResolvedClassifyOptions}, using the built-in defaults for unset fields. */ function resolveClassifyOptions(cfg) { return { shinyDescriptionTypes: cfg?.shinyDescriptionTypes ? new Set(cfg.shinyDescriptionTypes.map(s => s.toLowerCase())) : shinyDescriptionTypes, shinyEntryFiles: cfg?.shinyEntryFiles ? new Set(cfg.shinyEntryFiles.map(s => s.toLowerCase())) : shinyEntryFiles, shinyUsage: cfg?.shinyUsagePattern ? new RegExp(cfg.shinyUsagePattern) : shinyUsage, notebookExtension: cfg?.notebookExtensions ? new RegExp(`\\.(${cfg.notebookExtensions.join('|')})$`, 'i') : notebookExtension }; } const defaultClassifyOptions = resolveClassifyOptions(); /** * Whether the project is a shiny app: it has the canonical entry files (a single `app.R`, or the `ui.R`+`server.R` * pair) and one of them uses the shiny stack. When no entry file can be read we trust the file names alone. */ function isShinyAppSignals(names, entries, shinyUsagePattern = shinyUsage) { if (!names.has('app.r') && !(names.has('ui.r') && names.has('server.r'))) { return false; } let couldRead = false; for (const read of entries.values()) { const content = read(); if (content === undefined) { continue; } couldRead = true; if (shinyUsagePattern.test(content)) { return true; } } return !couldRead; } /** * Classify the {@link ProjectKind} from the given {@link ProjectKindSignals}. Shiny apps are detected first (they * often ship a `DESCRIPTION` too), then packages, then notebooks; the rest is a single-file `Script` or a `Project`. */ function classifyProjectKind(signals, opts = defaultClassifyOptions) { if (signals.descriptionTypes.some(t => opts.shinyDescriptionTypes.has(t))) { return project_kind_1.ProjectKind.ShinyApp; } if (isShinyAppSignals(signals.names, signals.entries, opts.shinyUsage)) { return project_kind_1.ProjectKind.ShinyApp; } if (signals.descriptionCount > 0) { return project_kind_1.ProjectKind.Package; } if (signals.names.size === 0) { return project_kind_1.ProjectKind.Unknown; } for (const name of signals.names) { if (opts.notebookExtension.test(name)) { return project_kind_1.ProjectKind.Notebook; } } let rSources = 0; for (const name of signals.names) { if (name.endsWith('.r')) { rSources++; } } return rSources <= 1 ? project_kind_1.ProjectKind.Script : project_kind_1.ProjectKind.Project; } //# sourceMappingURL=classify-project-kind.js.map