@eagleoutice/flowr 2.7.4 → 2.7.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +14 -14
  2. package/cli/repl/commands/repl-commands.d.ts +2 -0
  3. package/cli/repl/commands/repl-commands.js +2 -0
  4. package/cli/repl/commands/repl-dataflow.d.ts +2 -0
  5. package/cli/repl/commands/repl-dataflow.js +38 -1
  6. package/cli/repl/core.js +22 -0
  7. package/config.d.ts +5 -0
  8. package/config.js +6 -0
  9. package/dataflow/graph/graph.js +2 -0
  10. package/documentation/wiki-analyzer.js +12 -0
  11. package/documentation/wiki-interface.js +3 -0
  12. package/documentation/wiki-query.js +1 -1
  13. package/package.json +3 -1
  14. package/project/context/flowr-analyzer-dependencies-context.d.ts +5 -1
  15. package/project/context/flowr-analyzer-functions-context.d.ts +16 -0
  16. package/project/context/flowr-analyzer-functions-context.js +6 -0
  17. package/project/context/flowr-analyzer-loading-order-context.d.ts +4 -4
  18. package/project/context/flowr-analyzer-loading-order-context.js +4 -0
  19. package/project/plugins/file-plugins/files/flowr-description-file.d.ts +18 -1
  20. package/project/plugins/file-plugins/files/flowr-description-file.js +47 -13
  21. package/project/plugins/loading-order-plugins/flowr-analyzer-loading-order-description-file-plugin.js +8 -3
  22. package/project/plugins/package-version-plugins/flowr-analyzer-package-versions-description-file-plugin.js +5 -2
  23. package/project/plugins/package-version-plugins/flowr-analyzer-package-versions-namespace-file-plugin.js +6 -1
  24. package/project/plugins/package-version-plugins/package.js +1 -1
  25. package/project/plugins/project-discovery/flowr-analyzer-project-discovery-plugin.js +12 -2
  26. package/queries/catalog/project-query/project-query-executor.js +12 -2
  27. package/queries/catalog/project-query/project-query-format.d.ts +13 -0
  28. package/queries/catalog/project-query/project-query-format.js +25 -2
  29. package/queries/query-print.js +8 -3
  30. package/util/mermaid/cfg.d.ts +3 -0
  31. package/util/mermaid/cfg.js +25 -0
  32. package/util/r-author.d.ts +39 -0
  33. package/util/r-author.js +194 -0
  34. package/util/r-license.d.ts +23 -0
  35. package/util/r-license.js +196 -0
  36. package/util/simple-df/dfg-ascii.d.ts +5 -0
  37. package/util/simple-df/dfg-ascii.js +272 -0
  38. package/util/version.js +1 -1
package/README.md CHANGED
@@ -24,7 +24,7 @@ It offers a wide variety of features, for example:
24
24
 
25
25
  ```shell
26
26
  $ docker run -it --rm eagleoutice/flowr # or npm run flowr
27
- flowR repl using flowR v2.7.3, R grammar v14 (tree-sitter engine)
27
+ flowR repl using flowR v2.7.5, R grammar v14 (tree-sitter engine)
28
28
  R> :query @linter "read.csv(\"/root/x.txt\")"
29
29
  ```
30
30
 
@@ -33,21 +33,21 @@ It offers a wide variety of features, for example:
33
33
 
34
34
 
35
35
  ```text
36
- Query: linter (2 ms)
36
+ Query: linter (1 ms)
37
37
  ╰ **Deprecated Functions** (deprecated-functions):
38
38
  ╰ _Metadata_: <code>{"totalCalls":0,"totalFunctionDefinitions":0,"searchTimeMs":0,"processTimeMs":0}</code>
39
39
  ╰ **File Path Validity** (file-path-validity):
40
40
  ╰ certain:
41
41
  ╰ Path `/root/x.txt` at 1.1-23
42
- ╰ _Metadata_: <code>{"totalReads":1,"totalUnknown":0,"totalWritesBeforeAlways":0,"totalValid":0,"searchTimeMs":1,"processTimeMs":0}</code>
42
+ ╰ _Metadata_: <code>{"totalReads":1,"totalUnknown":0,"totalWritesBeforeAlways":0,"totalValid":0,"searchTimeMs":0,"processTimeMs":0}</code>
43
43
  ╰ **Seeded Randomness** (seeded-randomness):
44
44
  ╰ _Metadata_: <code>{"consumerCalls":0,"callsWithFunctionProducers":0,"callsWithAssignmentProducers":0,"callsWithNonConstantProducers":0,"callsWithOtherBranchProducers":0,"searchTimeMs":0,"processTimeMs":0}</code>
45
45
  ╰ **Absolute Paths** (absolute-file-paths):
46
46
  ╰ certain:
47
47
  ╰ Path `/root/x.txt` at 1.1-23
48
- ╰ _Metadata_: <code>{"totalConsidered":1,"totalUnknown":0,"searchTimeMs":0,"processTimeMs":0}</code>
48
+ ╰ _Metadata_: <code>{"totalConsidered":1,"totalUnknown":0,"searchTimeMs":1,"processTimeMs":0}</code>
49
49
  ╰ **Unused Definitions** (unused-definitions):
50
- ╰ _Metadata_: <code>{"totalConsidered":0,"searchTimeMs":0,"processTimeMs":1}</code>
50
+ ╰ _Metadata_: <code>{"totalConsidered":0,"searchTimeMs":0,"processTimeMs":0}</code>
51
51
  ╰ **Naming Convention** (naming-convention):
52
52
  ╰ _Metadata_: <code>{"numMatches":0,"numBreak":0,"searchTimeMs":0,"processTimeMs":0}</code>
53
53
  ╰ **Network Functions** (network-functions):
@@ -58,7 +58,7 @@ It offers a wide variety of features, for example:
58
58
  ╰ _Metadata_: <code>{"consideredNodes":5,"searchTimeMs":0,"processTimeMs":0}</code>
59
59
  ╰ **Useless Loops** (useless-loop):
60
60
  ╰ _Metadata_: <code>{"numOfUselessLoops":0,"searchTimeMs":0,"processTimeMs":0}</code>
61
- All queries together required ≈2 ms (1ms accuracy, total 3 ms)
61
+ All queries together required ≈1 ms (1ms accuracy, total 2 ms)
62
62
  ```
63
63
 
64
64
 
@@ -100,16 +100,16 @@ It offers a wide variety of features, for example:
100
100
  &nbsp;&nbsp;&nbsp;╰ **Network Functions** (network-functions):\
101
101
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>{"totalCalls":0,"totalFunctionDefinitions":0,"searchTimeMs":0,"processTimeMs":0}</code>\
102
102
  &nbsp;&nbsp;&nbsp;╰ **Dataframe Access Validation** (dataframe-access-validation):\
103
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>{"numOperations":0,"numAccesses":0,"totalAccessed":0,"searchTimeMs":0,"processTimeMs":1}</code>\
103
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>{"numOperations":0,"numAccesses":0,"totalAccessed":0,"searchTimeMs":0,"processTimeMs":0}</code>\
104
104
  &nbsp;&nbsp;&nbsp;╰ **Dead Code** (dead-code):\
105
105
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>{"consideredNodes":5,"searchTimeMs":0,"processTimeMs":0}</code>\
106
106
  &nbsp;&nbsp;&nbsp;╰ **Useless Loops** (useless-loop):\
107
107
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>{"numOfUselessLoops":0,"searchTimeMs":0,"processTimeMs":0}</code>\
108
- _All queries together required ≈3 ms (1ms accuracy, total 4 ms)_
108
+ _All queries together required ≈3 ms (1ms accuracy, total 3 ms)_
109
109
 
110
110
  <details> <summary style="color:gray">Show Detailed Results as Json</summary>
111
111
 
112
- The analysis required _3.5 ms_ (including parsing and normalization and the query) within the generation environment.
112
+ The analysis required _3.2 ms_ (including parsing and normalization and the query) within the generation environment.
113
113
 
114
114
  In general, the JSON contains the Ids of the nodes in question as they are present in the normalized AST or the dataflow graph of flowR.
115
115
  Please consult the [Interface](https://github.com/flowr-analysis/flowr/wiki/Interface) wiki page for more information on how to get those.
@@ -217,7 +217,7 @@ It offers a wide variety of features, for example:
217
217
  "numAccesses": 0,
218
218
  "totalAccessed": 0,
219
219
  "searchTimeMs": 0,
220
- "processTimeMs": 1
220
+ "processTimeMs": 0
221
221
  }
222
222
  },
223
223
  "dead-code": {
@@ -307,7 +307,7 @@ It offers a wide variety of features, for example:
307
307
 
308
308
  ```shell
309
309
  $ docker run -it --rm eagleoutice/flowr # or npm run flowr
310
- flowR repl using flowR v2.7.3, R grammar v14 (tree-sitter engine)
310
+ flowR repl using flowR v2.7.5, R grammar v14 (tree-sitter engine)
311
311
  R> :query @static-slice (11@sum) file://test/testfiles/example.R
312
312
  ```
313
313
 
@@ -355,7 +355,7 @@ It offers a wide variety of features, for example:
355
355
 
356
356
 
357
357
  * 🚀 **fast data- and control-flow graphs**\
358
- Within just <i><span title="This measurement is automatically fetched from the latest benchmark!">120.3 ms</span></i> (as of Dec 21, 2025),
358
+ Within just <i><span title="This measurement is automatically fetched from the latest benchmark!">122.3 ms</span></i> (as of Dec 23, 2025),
359
359
  _flowR_ can analyze the data- and control-flow of the average real-world R script. See the [benchmarks](https://flowr-analysis.github.io/flowr/wiki/stats/benchmark) for more information,
360
360
  and consult the [wiki pages](https://github.com/flowr-analysis/flowr/wiki/Dataflow-Graph) for more details on the dataflow graph.
361
361
 
@@ -391,7 +391,7 @@ It offers a wide variety of features, for example:
391
391
 
392
392
  ```shell
393
393
  $ docker run -it --rm eagleoutice/flowr # or npm run flowr
394
- flowR repl using flowR v2.7.3, R grammar v14 (tree-sitter engine)
394
+ flowR repl using flowR v2.7.5, R grammar v14 (tree-sitter engine)
395
395
  R> :dataflow* test/testfiles/example.R
396
396
  ```
397
397
 
@@ -696,7 +696,7 @@ It offers a wide variety of features, for example:
696
696
  ```
697
697
 
698
698
 
699
- (The analysis required _2.9 ms_ (including parse and normalize, using the [tree-sitter](https://github.com/flowr-analysis/flowr/wiki/Engines) engine) within the generation environment.)
699
+ (The analysis required _2.1 ms_ (including parse and normalize, using the [tree-sitter](https://github.com/flowr-analysis/flowr/wiki/Engines) engine) within the generation environment.)
700
700
 
701
701
 
702
702
 
@@ -16,6 +16,8 @@ declare const _commands: {
16
16
  readonly 'dataflow*': ReplCodeCommand;
17
17
  readonly dataflowsimple: ReplCodeCommand;
18
18
  readonly 'dataflowsimple*': ReplCodeCommand;
19
+ readonly dataflowascii: ReplCodeCommand;
20
+ readonly dataflowsilent: ReplCodeCommand;
19
21
  readonly controlflow: ReplCodeCommand;
20
22
  readonly 'controlflow*': ReplCodeCommand;
21
23
  readonly controlflowbb: ReplCodeCommand;
@@ -83,6 +83,8 @@ const _commands = {
83
83
  'dataflow*': repl_dataflow_1.dataflowStarCommand,
84
84
  'dataflowsimple': repl_dataflow_1.dataflowSimplifiedCommand,
85
85
  'dataflowsimple*': repl_dataflow_1.dataflowSimpleStarCommand,
86
+ 'dataflowascii': repl_dataflow_1.dataflowAsciiCommand,
87
+ 'dataflowsilent': repl_dataflow_1.dataflowSilentCommand,
86
88
  'controlflow': repl_cfg_1.controlflowCommand,
87
89
  'controlflow*': repl_cfg_1.controlflowStarCommand,
88
90
  'controlflowbb': repl_cfg_1.controlflowBbCommand,
@@ -1,5 +1,7 @@
1
1
  import type { ReplCodeCommand } from './repl-main';
2
2
  export declare const dataflowCommand: ReplCodeCommand;
3
3
  export declare const dataflowStarCommand: ReplCodeCommand;
4
+ export declare const dataflowAsciiCommand: ReplCodeCommand;
5
+ export declare const dataflowSilentCommand: ReplCodeCommand;
4
6
  export declare const dataflowSimplifiedCommand: ReplCodeCommand;
5
7
  export declare const dataflowSimpleStarCommand: ReplCodeCommand;
@@ -33,11 +33,13 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.dataflowSimpleStarCommand = exports.dataflowSimplifiedCommand = exports.dataflowStarCommand = exports.dataflowCommand = void 0;
36
+ exports.dataflowSimpleStarCommand = exports.dataflowSimplifiedCommand = exports.dataflowSilentCommand = exports.dataflowAsciiCommand = exports.dataflowStarCommand = exports.dataflowCommand = void 0;
37
37
  const retriever_1 = require("../../../r-bridge/retriever");
38
38
  const dfg_1 = require("../../../util/mermaid/dfg");
39
39
  const ansi_1 = require("../../../util/text/ansi");
40
40
  const core_1 = require("../core");
41
+ const vertex_1 = require("../../../dataflow/graph/vertex");
42
+ const dfg_ascii_1 = require("../../../util/simple-df/dfg-ascii");
41
43
  function formatInfo(out, type, meta) {
42
44
  return out.formatter.format(`Copied ${type} to clipboard (dataflow: ${meta['.meta'].timing + 'ms'}).`, { color: 7 /* Colors.White */, effect: ansi_1.ColorEffect.Foreground, style: 3 /* FontStyles.Italic */ });
43
45
  }
@@ -80,6 +82,41 @@ exports.dataflowStarCommand = {
80
82
  catch { /* do nothing this is a service thing */ }
81
83
  }
82
84
  };
85
+ exports.dataflowAsciiCommand = {
86
+ description: 'Returns an ASCII representation of the dataflow graph',
87
+ isCodeCommand: true,
88
+ usageExample: ':dataflowascii',
89
+ aliases: ['df!'],
90
+ script: false,
91
+ argsParser: (args) => (0, core_1.handleString)(args),
92
+ fn: async ({ output, analyzer }) => {
93
+ const result = await analyzer.dataflow();
94
+ output.stdout((0, dfg_ascii_1.dfgToAscii)(result.graph));
95
+ }
96
+ };
97
+ exports.dataflowSilentCommand = {
98
+ description: 'Just calculates the DFG, but only prints summary info',
99
+ isCodeCommand: true,
100
+ usageExample: ':dataflowsilent',
101
+ aliases: ['d#', 'df#'],
102
+ script: false,
103
+ argsParser: (args) => (0, core_1.handleString)(args),
104
+ fn: async ({ output, analyzer }) => {
105
+ const result = await analyzer.dataflow();
106
+ const numOfEdges = Array.from(result.graph.edges().flatMap(e => e[1].entries())).length;
107
+ const numOfVertices = Array.from(result.graph.vertices(true)).length;
108
+ output.stdout(output.formatter.format(`Dataflow calculated in ${result['.meta'].timing}ms.`, { color: 7 /* Colors.White */, effect: ansi_1.ColorEffect.Foreground, style: 3 /* FontStyles.Italic */ }) + '\n' +
109
+ 'Edges: ' + output.formatter.format(`${String(numOfEdges).padStart(12)}`, { color: 6 /* Colors.Cyan */, effect: ansi_1.ColorEffect.Foreground }) + '\n' +
110
+ // number of vertices and edges
111
+ 'Vertices: ' + output.formatter.format(`${String(numOfVertices).padStart(12)}`, { color: 6 /* Colors.Cyan */, effect: ansi_1.ColorEffect.Foreground }));
112
+ const longestVertexType = Math.max(...Object.keys(vertex_1.VertexType).map(vt => vt.length));
113
+ for (const vertType of Object.values(vertex_1.VertexType)) {
114
+ const vertsOfType = Array.from(result.graph.verticesOfType(vertType));
115
+ const longVertexName = Object.entries(vertex_1.VertexType).find(([, v]) => v === vertType)?.[0] ?? vertType;
116
+ output.stdout(` - ${(longVertexName + ':').padEnd(longestVertexType + 1)} ` + output.formatter.format(`${String(vertsOfType.length).padStart(8)}`, { color: 6 /* Colors.Cyan */, effect: ansi_1.ColorEffect.Foreground }).padStart(9, ' '));
117
+ }
118
+ }
119
+ };
83
120
  exports.dataflowSimplifiedCommand = {
84
121
  description: `Get mermaid code for the simplified dataflow graph, start with '${retriever_1.fileProtocol}' to indicate a file`,
85
122
  isCodeCommand: true,
package/cli/repl/core.js CHANGED
@@ -53,6 +53,7 @@ const os_1 = __importDefault(require("os"));
53
53
  const path_1 = __importDefault(require("path"));
54
54
  const fs_1 = __importDefault(require("fs"));
55
55
  const args_1 = require("../../util/text/args");
56
+ const ansi_1 = require("../../util/text/ansi");
56
57
  const repl_commands_1 = require("./commands/repl-commands");
57
58
  const scripts_info_1 = require("../common/scripts-info");
58
59
  const retriever_1 = require("../../r-bridge/retriever");
@@ -144,6 +145,7 @@ function handleString(code) {
144
145
  };
145
146
  }
146
147
  async function replProcessStatement(output, statement, analyzer, allowRSessionAccess) {
148
+ const time = Date.now();
147
149
  if (statement.startsWith(':')) {
148
150
  const command = statement.slice(1).split(' ')[0].toLowerCase();
149
151
  const processor = (0, repl_commands_1.getCommand)(command);
@@ -179,6 +181,26 @@ async function replProcessStatement(output, statement, analyzer, allowRSessionAc
179
181
  else {
180
182
  await (0, repl_execute_1.tryExecuteRShellCommand)({ output, analyzer, remainingLine: statement, allowRSessionAccess });
181
183
  }
184
+ if (analyzer.inspectContext().config.repl.quickStats) {
185
+ try {
186
+ const duration = Date.now() - time;
187
+ console.log(output.formatter.format(`[REPL Stats] Processed in ${duration}ms`, {
188
+ style: 3 /* FontStyles.Italic */,
189
+ effect: ansi_1.ColorEffect.Foreground,
190
+ color: 7 /* Colors.White */
191
+ }));
192
+ const memoryUsage = process.memoryUsage();
193
+ const memoryUsageStr = Object.entries(memoryUsage).map(([key, value]) => `${key}: ${(value / 1024 / 1024).toFixed(2)} MB`).join(', ');
194
+ console.log(output.formatter.format(`[REPL Stats] Memory Usage: ${memoryUsageStr}`, {
195
+ style: 3 /* FontStyles.Italic */,
196
+ effect: ansi_1.ColorEffect.Foreground,
197
+ color: 7 /* Colors.White */
198
+ }));
199
+ }
200
+ catch {
201
+ // do nothing, this is just a nice-to-have
202
+ }
203
+ }
182
204
  }
183
205
  /**
184
206
  * This function interprets the given `expr` as a REPL command (see {@link repl} for more on the semantics).
package/config.d.ts CHANGED
@@ -98,6 +98,11 @@ export interface FlowrConfigOptions extends MergeableRecord {
98
98
  };
99
99
  };
100
100
  };
101
+ /** Configuration options for the REPL */
102
+ readonly repl: {
103
+ /** Whether to show quick stats in the REPL after each evaluation */
104
+ quickStats: boolean;
105
+ };
101
106
  readonly project: {
102
107
  /** Whether to resolve unknown paths loaded by the r project disk when trying to source/analyze files */
103
108
  resolveUnknownPathsOnDisk: boolean;
package/config.js CHANGED
@@ -65,6 +65,9 @@ exports.defaultConfigOptions = {
65
65
  }
66
66
  }
67
67
  },
68
+ repl: {
69
+ quickStats: false
70
+ },
68
71
  project: {
69
72
  resolveUnknownPathsOnDisk: true
70
73
  },
@@ -106,6 +109,9 @@ exports.flowrConfigFileSchema = joi_1.default.object({
106
109
  }).optional().description('Do you want to overwrite (parts) of the builtin definition?')
107
110
  }).optional().description('Semantics regarding how to handle the R environment.')
108
111
  }).description('Configure language semantics and how flowR handles them.'),
112
+ repl: joi_1.default.object({
113
+ quickStats: joi_1.default.boolean().optional().description('Whether to show quick stats in the REPL after each evaluation.')
114
+ }).description('Configuration options for the REPL.'),
109
115
  project: joi_1.default.object({
110
116
  resolveUnknownPathsOnDisk: joi_1.default.boolean().optional().description('Whether to resolve unknown paths loaded by the r project disk when trying to source/analyze files.')
111
117
  }).description('Project specific configuration options.'),
@@ -308,6 +308,8 @@ class DataflowGraph {
308
308
  }
309
309
  else {
310
310
  this.vertexInformation.set(reference.nodeId, { ...vertex, tag: vertex_1.VertexType.VariableDefinition });
311
+ this.types.set(vertex.tag, (this.types.get(vertex.tag) ?? []).filter(id => id !== reference.nodeId));
312
+ this.types.set(vertex_1.VertexType.VariableDefinition, (this.types.get(vertex_1.VertexType.VariableDefinition) ?? []).concat([reference.nodeId]));
311
313
  }
312
314
  }
313
315
  /**
@@ -27,6 +27,7 @@ const doc_maker_1 = require("./wiki-mk/doc-maker");
27
27
  const flowr_analyzer_rmd_file_plugin_1 = require("../project/plugins/file-plugins/notebooks/flowr-analyzer-rmd-file-plugin");
28
28
  const flowr_analyzer_plugin_1 = require("../project/plugins/flowr-analyzer-plugin");
29
29
  const flowr_analyzer_environment_context_1 = require("../project/context/flowr-analyzer-environment-context");
30
+ const flowr_analyzer_functions_context_1 = require("../project/context/flowr-analyzer-functions-context");
30
31
  async function analyzerQuickExample() {
31
32
  const analyzer = await new flowr_analyzer_builder_1.FlowrAnalyzerBuilder()
32
33
  .setEngine('tree-sitter')
@@ -398,6 +399,17 @@ Probably the most important method is
398
399
  ${ctx.linkM(flowr_analyzer_dependencies_context_1.FlowrAnalyzerDependenciesContext, 'getDependency', { codeFont: true, realNameWrapper: 'i' })}
399
400
  that allows you to query for a specific dependency by name.
400
401
 
402
+ ${(0, doc_structure_1.section)('Functions Context', 3)}
403
+
404
+ The ${ctx.link(flowr_analyzer_dependencies_context_1.FlowrAnalyzerDependenciesContext)} also provides access to the associated
405
+ ${ctx.link(flowr_analyzer_functions_context_1.FlowrAnalyzerFunctionsContext)} via its \`functionsContext\` attribute.
406
+
407
+ ${ctx.hierarchy(flowr_analyzer_functions_context_1.FlowrAnalyzerFunctionsContext, { showImplSnippet: false })}
408
+
409
+ Probably the most important method is
410
+ ${ctx.linkM(flowr_analyzer_functions_context_1.FlowrAnalyzerFunctionsContext, 'getFunctionInfo', { codeFont: true, realNameWrapper: 'i' })}
411
+ that allows you to query for a specific function by name.
412
+
401
413
  ${(0, doc_structure_1.section)('Environment Context', 3)}
402
414
 
403
415
  Here is the structure of the ${ctx.link(flowr_analyzer_environment_context_1.FlowrAnalyzerEnvironmentContext)} that provides access to the built-in environment:
@@ -213,6 +213,9 @@ ${(0, doc_code_1.codeBlock)('json', JSON.stringify({
213
213
  }
214
214
  }
215
215
  },
216
+ repl: {
217
+ quickStats: false
218
+ },
216
219
  project: {
217
220
  resolveUnknownPathsOnDisk: true
218
221
  },
@@ -149,7 +149,7 @@ ${await (0, doc_query_1.showQuery)(shell, '', [{
149
149
  const exampleCode = 'x + 1';
150
150
  return `
151
151
  This query returns the information about the analyzed project.
152
- Currently, this is only the list of file paths included.
152
+ If present, it will incorporate plugins to, e.g., extract author and license information from R package DESCRIPTION files.
153
153
 
154
154
  ${await (0, doc_query_1.showQuery)(shell, exampleCode, [{
155
155
  type: 'project'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eagleoutice/flowr",
3
- "version": "2.7.4",
3
+ "version": "2.7.6",
4
4
  "description": "Static Dataflow Analyzer and Program Slicer for the R Programming Language",
5
5
  "types": "dist/src/index.d.ts",
6
6
  "repository": {
@@ -172,6 +172,7 @@
172
172
  "@types/command-line-args": "^5.2.3",
173
173
  "@types/command-line-usage": "^5.0.4",
174
174
  "@types/commonmark": "^0.27.10",
175
+ "@types/dagre": "^0.7.53",
175
176
  "@types/n-readlines": "^1.0.6",
176
177
  "@types/n3": "^1.26.0",
177
178
  "@types/object-hash": "^3.0.6",
@@ -203,6 +204,7 @@
203
204
  "command-line-args": "^6.0.1",
204
205
  "command-line-usage": "^7.0.3",
205
206
  "commonmark": "^0.31.2",
207
+ "dagre": "^0.8.5",
206
208
  "gray-matter": "^4.0.3",
207
209
  "joi": "^18.0.1",
208
210
  "lz-string": "^1.5.0",
@@ -1,7 +1,7 @@
1
1
  import { AbstractFlowrAnalyzerContext } from './abstract-flowr-analyzer-context';
2
2
  import { FlowrAnalyzerPackageVersionsPlugin } from '../plugins/package-version-plugins/flowr-analyzer-package-versions-plugin';
3
3
  import type { Package } from '../plugins/package-version-plugins/package';
4
- import type { FlowrAnalyzerFunctionsContext } from './flowr-analyzer-functions-context';
4
+ import type { FlowrAnalyzerFunctionsContext, ReadOnlyFlowrAnalyzerFunctionsContext } from './flowr-analyzer-functions-context';
5
5
  /**
6
6
  * This is a read-only interface to the {@link FlowrAnalyzerDependenciesContext}.
7
7
  * It prevents you from modifying the dependencies, but allows you to inspect them (which is probably what you want when using the {@link FlowrAnalyzer}).
@@ -12,6 +12,10 @@ export interface ReadOnlyFlowrAnalyzerDependenciesContext {
12
12
  * The name of this context.
13
13
  */
14
14
  readonly name: string;
15
+ /**
16
+ * The functions context associated with this dependencies-context.
17
+ */
18
+ readonly functionsContext: ReadOnlyFlowrAnalyzerFunctionsContext;
15
19
  /**
16
20
  * Get the dependency with the given name, if it exists.
17
21
  *
@@ -14,10 +14,26 @@ export interface FunctionInfo {
14
14
  s3TypeDispatch?: string;
15
15
  inferredType?: string;
16
16
  }
17
+ /**
18
+ * This is a read-only interface to the {@link FlowrAnalyzerFunctionsContext}.
19
+ * It prevents you from modifying the functions, but allows you to inspect them (which is probably what you want when using the {@link FlowrAnalyzer}).
20
+ * If you are a {@link FlowrAnalyzerPackageVersionsPlugin} and want to modify the functions, you can use the {@link FlowrAnalyzerFunctionsContext} directly.
21
+ */
17
22
  export interface ReadOnlyFlowrAnalyzerFunctionsContext {
18
23
  readonly name: string;
24
+ /**
25
+ * Get the function information for the given function name and optional class name.
26
+ * @param name - The name of the function to get information for.
27
+ * @param className - The optional class name (e.g., for S3 generics).
28
+ */
19
29
  getFunctionInfo(name: string, className?: string): FunctionInfo | FunctionInfo[] | undefined;
20
30
  }
31
+ /**
32
+ * This context is responsible for managing the functions identified in the project, including their origins, types, and other metadata.
33
+ * It works in conjunction with {@link FlowrAnalyzerPackageVersionsPlugin}s to gather and maintain this information.
34
+ *
35
+ * If you are interested in inspecting these functions, refer to {@link ReadOnlyFlowrAnalyzerFunctionsContext}.
36
+ */
21
37
  export declare class FlowrAnalyzerFunctionsContext extends AbstractFlowrAnalyzerContext<undefined, void, FlowrAnalyzerPackageVersionsPlugin> implements ReadOnlyFlowrAnalyzerFunctionsContext {
22
38
  readonly name = "flowr-analyzer-functions-context";
23
39
  private functionInfo;
@@ -10,6 +10,12 @@ var FunctionTypes;
10
10
  FunctionTypes["ExportTypes"] = "exportTypes";
11
11
  FunctionTypes["S3"] = "S3";
12
12
  })(FunctionTypes || (exports.FunctionTypes = FunctionTypes = {}));
13
+ /**
14
+ * This context is responsible for managing the functions identified in the project, including their origins, types, and other metadata.
15
+ * It works in conjunction with {@link FlowrAnalyzerPackageVersionsPlugin}s to gather and maintain this information.
16
+ *
17
+ * If you are interested in inspecting these functions, refer to {@link ReadOnlyFlowrAnalyzerFunctionsContext}.
18
+ */
13
19
  class FlowrAnalyzerFunctionsContext extends abstract_flowr_analyzer_context_1.AbstractFlowrAnalyzerContext {
14
20
  name = 'flowr-analyzer-functions-context';
15
21
  functionInfo = new Map();
@@ -29,7 +29,7 @@ export interface ReadOnlyFlowrAnalyzerLoadingOrderContext {
29
29
  /**
30
30
  * Get the current guesses for the loading order, if any. These are populated by {@link FlowrAnalyzerLoadingOrderPlugin}s.
31
31
  */
32
- currentGuesses(): readonly RParseRequest[][];
32
+ currentGuesses(): readonly (readonly RParseRequest[])[];
33
33
  /**
34
34
  * Get the current known loading order, if any. This is populated by {@link FlowrAnalyzerLoadingOrderPlugin}s if they have a source of identifying the order definitively.
35
35
  */
@@ -46,9 +46,9 @@ export declare class FlowrAnalyzerLoadingOrderContext extends AbstractFlowrAnaly
46
46
  private rerunRequired;
47
47
  constructor(ctx: FlowrAnalyzerContext, plugins: readonly FlowrAnalyzerLoadingOrderPlugin[] | undefined);
48
48
  private knownOrder?;
49
- private guesses;
49
+ private readonly guesses;
50
50
  /** just the base collection of requests we know nothing about the order! */
51
- private unordered;
51
+ private readonly unordered;
52
52
  reset(): void;
53
53
  /**
54
54
  * Add one or multiple requests to the context.
@@ -69,7 +69,7 @@ export declare class FlowrAnalyzerLoadingOrderContext extends AbstractFlowrAnaly
69
69
  * In case you have a certain order, use the `certain` flag to indicate this -- but please take care of *really* being certain!
70
70
  */
71
71
  addGuess(guess: readonly RParseRequest[], certain?: boolean): void;
72
- currentGuesses(): readonly RParseRequest[][];
72
+ currentGuesses(): readonly (readonly RParseRequest[])[];
73
73
  currentKnownOrder(): readonly RParseRequest[] | undefined;
74
74
  peekLoadingOrder(): readonly RParseRequest[] | undefined;
75
75
  getUnorderedRequests(): readonly RParseRequest[];
@@ -65,12 +65,16 @@ class FlowrAnalyzerLoadingOrderContext extends abstract_flowr_analyzer_context_1
65
65
  loadingOrderLog.warn(`Adding certain guess ${guess.map(g => g.request).join(', ')} after known order!`);
66
66
  if (!(0, arrays_1.arrayEqual)(this.knownOrder, guess)) {
67
67
  loadingOrderLog.error(`Certain guess ${guess.map(g => g.request).join(', ')} does not match known order ${this.knownOrder.map(g => g.request).join(', ')}`);
68
+ this.guesses.push(guess);
68
69
  }
69
70
  }
70
71
  else {
71
72
  this.knownOrder = guess;
72
73
  }
73
74
  }
75
+ else {
76
+ this.guesses.push(guess);
77
+ }
74
78
  }
75
79
  currentGuesses() {
76
80
  return this.guesses;
@@ -1,9 +1,12 @@
1
1
  import { type FlowrFileProvider, type FileRole, FlowrFile } from '../../../context/flowr-file';
2
+ import type { RAuthorInfo } from '../../../../util/r-author';
3
+ import type { DeepReadonly } from 'ts-essentials';
4
+ import type { RLicenseElementInfo } from '../../../../util/r-license';
2
5
  export type DCF = Map<string, string[]>;
3
6
  /**
4
7
  * This decorates a text file and provides access to its content as a DCF (Debian Control File)-like structure.
5
8
  */
6
- export declare class FlowrDescriptionFile extends FlowrFile<DCF> {
9
+ export declare class FlowrDescriptionFile extends FlowrFile<DeepReadonly<DCF>> {
7
10
  private readonly wrapped;
8
11
  /**
9
12
  * Prefer the static {@link FlowrDescriptionFile.from} method to create instances of this class as it will not re-create if already a description file
@@ -19,4 +22,18 @@ export declare class FlowrDescriptionFile extends FlowrFile<DCF> {
19
22
  * Description file lifter, this does not re-create if already a description file
20
23
  */
21
24
  static from(file: FlowrFileProvider | FlowrDescriptionFile, role?: FileRole): FlowrDescriptionFile;
25
+ /**
26
+ * Returns the parsed license information from the 'License' field in the DESCRIPTION file.
27
+ */
28
+ license(): RLicenseElementInfo[] | undefined;
29
+ /**
30
+ * Returns the parsed authors from the `Authors@R` field in the DESCRIPTION file.
31
+ */
32
+ authors(): RAuthorInfo[] | undefined;
22
33
  }
34
+ /**
35
+ * Parses the 'License' field from an R DESCRIPTION file into SPDX license expressions.
36
+ * @param licenseField - The 'License' field from the DESCRIPTION file as an array of strings.
37
+ * @returns An array of SPDX license information objects if parsing was successful.
38
+ */
39
+ export declare function parseRLicenseField(...licenseField: string[]): RLicenseElementInfo[];
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.FlowrDescriptionFile = void 0;
4
+ exports.parseRLicenseField = parseRLicenseField;
4
5
  const flowr_file_1 = require("../../../context/flowr-file");
6
+ const r_author_1 = require("../../../../util/r-author");
7
+ const args_1 = require("../../../../util/text/args");
8
+ const r_license_1 = require("../../../../util/r-license");
5
9
  /**
6
10
  * This decorates a text file and provides access to its content as a DCF (Debian Control File)-like structure.
7
11
  */
@@ -31,8 +35,46 @@ class FlowrDescriptionFile extends flowr_file_1.FlowrFile {
31
35
  }
32
36
  return file instanceof FlowrDescriptionFile ? file : new FlowrDescriptionFile(file);
33
37
  }
38
+ /**
39
+ * Returns the parsed license information from the 'License' field in the DESCRIPTION file.
40
+ */
41
+ license() {
42
+ const licenses = this.content().get('License');
43
+ if (!licenses) {
44
+ return undefined;
45
+ }
46
+ return parseRLicenseField(...licenses);
47
+ }
48
+ /**
49
+ * Returns the parsed authors from the `Authors@R` field in the DESCRIPTION file.
50
+ */
51
+ authors() {
52
+ const authors = this.content().get('Authors@R');
53
+ return authors ? authors.flatMap(r_author_1.parseRAuthorString) : undefined;
54
+ }
34
55
  }
35
56
  exports.FlowrDescriptionFile = FlowrDescriptionFile;
57
+ /**
58
+ * Parses the 'License' field from an R DESCRIPTION file into SPDX license expressions.
59
+ * @param licenseField - The 'License' field from the DESCRIPTION file as an array of strings.
60
+ * @returns An array of SPDX license information objects if parsing was successful.
61
+ */
62
+ function parseRLicenseField(...licenseField) {
63
+ return licenseField.map(r_license_1.parseRLicense);
64
+ }
65
+ function emplaceDCF(key, val, result) {
66
+ if (!key) {
67
+ return;
68
+ }
69
+ let values = [];
70
+ if (key.includes('@')) {
71
+ values = [val.trim()];
72
+ }
73
+ else {
74
+ values = val ? cleanValues(val) : [];
75
+ }
76
+ result.set(key, values);
77
+ }
36
78
  /**
37
79
  * Parses the given file in the 'Debian Control Format'.
38
80
  * @param file - The file to parse
@@ -49,27 +91,19 @@ function parseDCF(file) {
49
91
  currentValue += '\n' + line.trim();
50
92
  }
51
93
  else {
52
- if (currentKey) {
53
- const values = currentValue ? cleanValues(currentValue) : [];
54
- result.set(currentKey, values);
55
- }
94
+ emplaceDCF(currentKey, currentValue, result);
56
95
  const [key, rest] = line.split(firstColonRegex).map(s => s.trim());
57
96
  currentKey = key?.trim() ?? '';
58
97
  currentValue = rest?.trim() ?? '';
59
98
  }
60
99
  }
61
- if (currentKey) {
62
- const values = currentValue ? cleanValues(currentValue) : [];
63
- result.set(currentKey, values);
64
- }
100
+ emplaceDCF(currentKey, currentValue, result);
65
101
  return result;
66
102
  }
67
- const cleanSplitRegex = /[\n,]+/;
68
- const cleanQuotesRegex = /'/g;
103
+ const splitRegex = /[\n\r]+/g;
69
104
  function cleanValues(values) {
70
- return values
71
- .split(cleanSplitRegex)
72
- .map(s => s.trim().replace(cleanQuotesRegex, ''))
105
+ return values.split(splitRegex).flatMap(l => (0, args_1.splitAtEscapeSensitive)(l, false, ','))
106
+ .map(s => s.trim())
73
107
  .filter(s => s.length > 0);
74
108
  }
75
109
  //# sourceMappingURL=flowr-description-file.js.map
@@ -5,6 +5,7 @@ const flowr_analyzer_description_file_plugin_1 = require("../file-plugins/flowr-
5
5
  const semver_1 = require("semver");
6
6
  const flowr_analyzer_loading_order_plugin_1 = require("./flowr-analyzer-loading-order-plugin");
7
7
  const flowr_file_1 = require("../../context/flowr-file");
8
+ const retriever_1 = require("../../../r-bridge/retriever");
8
9
  /**
9
10
  * This plugin extracts loading order information from R `DESCRIPTION` files.
10
11
  * It looks at the `Collate` field to determine the order in which files should be loaded.
@@ -16,14 +17,18 @@ class FlowrAnalyzerLoadingOrderDescriptionFilePlugin extends flowr_analyzer_load
16
17
  version = new semver_1.SemVer('0.1.0');
17
18
  process(ctx) {
18
19
  const descFiles = ctx.files.getFilesByRole(flowr_file_1.FileRole.Description);
19
- if (descFiles.length !== 1) {
20
- flowr_analyzer_description_file_plugin_1.descriptionFileLog.warn(`Supporting only exactly one DESCRIPTION file, found ${descFiles.length}`);
20
+ if (descFiles.length === 0) {
21
+ flowr_analyzer_description_file_plugin_1.descriptionFileLog.warn('No description file found, cannot determine loading order from Collate field.');
21
22
  return;
22
23
  }
24
+ else if (descFiles.length > 1) {
25
+ flowr_analyzer_description_file_plugin_1.descriptionFileLog.warn(`Found ${descFiles.length} description files, expected exactly one.`);
26
+ }
23
27
  /** this will do the caching etc. for me */
24
28
  const deps = descFiles[0].content();
25
29
  if (deps.has('Collate')) {
26
- const collate = deps.get('Collate') ?? [];
30
+ const collate = deps.get('Collate')?.map(f => (0, retriever_1.removeRQuotes)(f))
31
+ ?? [];
27
32
  /* we probably have to do some more guesswork here */
28
33
  const unordered = ctx.files.loadingOrder.getUnorderedRequests();
29
34
  // sort them by their path index in the Collate field
@@ -17,10 +17,13 @@ class FlowrAnalyzerPackageVersionsDescriptionFilePlugin extends flowr_analyzer_p
17
17
  version = new semver_1.SemVer('0.1.0');
18
18
  process(ctx) {
19
19
  const descFiles = ctx.files.getFilesByRole(flowr_file_1.FileRole.Description);
20
- if (descFiles.length !== 1) {
21
- flowr_analyzer_description_file_plugin_1.descriptionFileLog.warn(`Supporting only exactly one DESCRIPTION file, found ${descFiles.length}`);
20
+ if (descFiles.length === 0) {
21
+ flowr_analyzer_description_file_plugin_1.descriptionFileLog.warn('No description file found, cannot extract package versions.');
22
22
  return;
23
23
  }
24
+ else if (descFiles.length > 1) {
25
+ flowr_analyzer_description_file_plugin_1.descriptionFileLog.warn(`Found ${descFiles.length} description files, expected exactly one.`);
26
+ }
24
27
  /** this will do the caching etc. for me */
25
28
  const deps = descFiles[0].content();
26
29
  this.retrieveVersionsFromField(ctx, deps, 'Depends', 'r');