@eagleoutice/flowr 2.8.0 → 2.8.2

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.
@@ -383,6 +383,7 @@ export declare const DefaultBuiltinConfig: [{
383
383
  readonly processor: "builtin:assignment";
384
384
  readonly config: {
385
385
  readonly targetVariable: true;
386
+ readonly mayHaveMoreArgs: true;
386
387
  };
387
388
  readonly assumePrimitive: true;
388
389
  }, {
@@ -247,7 +247,7 @@ exports.DefaultBuiltinConfig = [
247
247
  { type: 'function', names: ['library', 'require'], processor: 'builtin:library', config: {}, assumePrimitive: false },
248
248
  { type: 'function', names: ['<-', '='], processor: 'builtin:assignment', config: { canBeReplacement: true }, assumePrimitive: true },
249
249
  { type: 'function', names: [':='], processor: 'builtin:assignment', config: {}, assumePrimitive: true },
250
- { type: 'function', names: ['assign', 'setGeneric', 'setValidity'], processor: 'builtin:assignment', config: { targetVariable: true }, assumePrimitive: true },
250
+ { type: 'function', names: ['assign', 'setGeneric', 'setValidity'], processor: 'builtin:assignment', config: { targetVariable: true, mayHaveMoreArgs: true }, assumePrimitive: true },
251
251
  { type: 'function', names: ['setMethod'], processor: 'builtin:assignment-like', config: { targetVariable: true, canBeReplacement: false, target: { idx: 0, name: 'f' }, source: { idx: 2, name: 'definition' } }, assumePrimitive: true },
252
252
  { type: 'function', names: ['delayedAssign'], processor: 'builtin:assignment', config: { quoteSource: true, targetVariable: true }, assumePrimitive: true },
253
253
  { type: 'function', names: ['<<-'], processor: 'builtin:assignment', config: { superAssignment: true, canBeReplacement: true }, assumePrimitive: true },
@@ -1,9 +1,16 @@
1
1
  import { DataflowGraph } from './graph';
2
2
  import type { DataflowGraphVertexFunctionCall, DataflowGraphVertexFunctionDefinition } from './vertex';
3
+ import type { NodeId } from '../../r-bridge/lang-4.x/ast/model/processing/node-id';
3
4
  /**
4
5
  * A call graph is a dataflow graph where all vertices are function calls.
6
+ * You can create a call graph from a dataflow graph using {@link computeCallGraph}.
7
+ * If you want to extract a sub call graph, use {@link getSubCallGraph}.
5
8
  */
6
9
  export type CallGraph = DataflowGraph<Required<DataflowGraphVertexFunctionCall | DataflowGraphVertexFunctionDefinition>>;
10
+ /**
11
+ * Extracts the sub call graph from the given call graph, starting from the given entry points.
12
+ */
13
+ export declare function getSubCallGraph(graph: CallGraph, entryPoints: Set<NodeId>): CallGraph;
7
14
  /**
8
15
  * Computes the call graph from the given dataflow graph.
9
16
  */
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getSubCallGraph = getSubCallGraph;
3
4
  exports.computeCallGraph = computeCallGraph;
4
5
  const graph_1 = require("./graph");
5
6
  const vertex_1 = require("./vertex");
@@ -7,6 +8,33 @@ const linker_1 = require("../internal/linker");
7
8
  const edge_1 = require("./edge");
8
9
  const built_in_1 = require("../environments/built-in");
9
10
  const defaultmap_1 = require("../../util/collections/defaultmap");
11
+ /**
12
+ * Extracts the sub call graph from the given call graph, starting from the given entry points.
13
+ */
14
+ function getSubCallGraph(graph, entryPoints) {
15
+ const result = new graph_1.DataflowGraph(graph.idMap);
16
+ const toVisit = Array.from(entryPoints);
17
+ const visited = new Set();
18
+ while (toVisit.length > 0) {
19
+ const currentId = toVisit.pop();
20
+ if (visited.has(currentId)) {
21
+ continue;
22
+ }
23
+ visited.add(currentId);
24
+ const currentVtx = graph.getVertex(currentId, true);
25
+ if (!currentVtx) {
26
+ continue;
27
+ }
28
+ result.addVertex(currentVtx, undefined, true);
29
+ for (const [tar, { types }] of graph.outgoingEdges(currentId) ?? []) {
30
+ if ((0, edge_1.edgeIncludesType)(types, edge_1.EdgeType.Calls)) {
31
+ result.addEdge(currentId, tar, edge_1.EdgeType.Calls);
32
+ toVisit.push(tar);
33
+ }
34
+ }
35
+ }
36
+ return result;
37
+ }
10
38
  function reaches(from, to, graph, knownReachability) {
11
39
  const visited = new Set();
12
40
  const toVisit = [from];
@@ -41,10 +69,10 @@ function computeCallGraph(graph) {
41
69
  knownReachability: new defaultmap_1.DefaultMap(() => new Set())
42
70
  };
43
71
  for (const [, vert] of graph.vertices(false)) {
44
- if (vert?.tag === vertex_1.VertexType.FunctionCall) {
72
+ if (vert.tag === vertex_1.VertexType.FunctionCall) {
45
73
  processCall(vert, undefined, graph, result, state);
46
74
  }
47
- else if (vert?.tag === vertex_1.VertexType.FunctionDefinition) {
75
+ else if (vert.tag === vertex_1.VertexType.FunctionDefinition) {
48
76
  processFunctionDefinition(vert, undefined, graph, result, state);
49
77
  }
50
78
  }
@@ -74,6 +102,8 @@ function processCds(vtx, graph, result, state) {
74
102
  }
75
103
  }
76
104
  }
105
+ const UntargetedCallFollow = edge_1.EdgeType.Reads | edge_1.EdgeType.DefinedByOnCall | edge_1.EdgeType.DefinedBy | edge_1.EdgeType.Returns;
106
+ const UntargetedCallAvoid = edge_1.EdgeType.NonStandardEvaluation | edge_1.EdgeType.Argument;
77
107
  /**
78
108
  * This tracks the known symbol origins for a function call for which we know that flowr found no targets!
79
109
  */
@@ -94,7 +124,7 @@ function fallbackUntargetedCall(vtx, graph) {
94
124
  }
95
125
  let addedNew = false;
96
126
  for (const [tar, { types }] of graph.outgoingEdges(currentId) ?? []) {
97
- if ((0, edge_1.edgeIncludesType)(types, edge_1.EdgeType.Reads | edge_1.EdgeType.DefinedByOnCall | edge_1.EdgeType.DefinedBy | edge_1.EdgeType.Returns) && (0, edge_1.edgeDoesNotIncludeType)(types, edge_1.EdgeType.NonStandardEvaluation | edge_1.EdgeType.Argument)) {
127
+ if ((0, edge_1.edgeIncludesType)(types, UntargetedCallFollow) && (0, edge_1.edgeDoesNotIncludeType)(types, UntargetedCallAvoid)) {
98
128
  addedNew = true;
99
129
  toVisit.push(tar);
100
130
  }
@@ -107,21 +137,22 @@ function fallbackUntargetedCall(vtx, graph) {
107
137
  return collected;
108
138
  }
109
139
  function processCall(vtx, from, graph, result, state) {
110
- if (from && !reaches(from, vtx.id, result, state.knownReachability)) {
111
- result.addEdge(from, vtx.id, edge_1.EdgeType.Calls);
140
+ const vid = vtx.id;
141
+ if (from && !reaches(from, vid, result, state.knownReachability)) {
142
+ result.addEdge(from, vid, edge_1.EdgeType.Calls);
112
143
  }
113
- if (state.visited.has(vtx.id)) {
144
+ if (state.visited.has(vid)) {
114
145
  return;
115
146
  }
147
+ state.visited.add(vid);
116
148
  result.addVertex(vtx, undefined, true);
117
149
  processCds(vtx, graph, result, state);
118
- state.visited.add(vtx.id);
119
150
  // for each call, resolve the targets
120
- const tars = (0, linker_1.getAllFunctionCallTargets)(vtx.id, graph, vtx.environment);
151
+ const tars = (0, linker_1.getAllFunctionCallTargets)(vid, graph, vtx.environment);
121
152
  let addedTarget = false;
122
153
  for (const tar of tars) {
123
154
  if ((0, built_in_1.isBuiltIn)(tar)) {
124
- result.addEdge(vtx.id, tar, edge_1.EdgeType.Calls);
155
+ result.addEdge(vid, tar, edge_1.EdgeType.Calls);
125
156
  addedTarget = true;
126
157
  continue;
127
158
  }
@@ -130,13 +161,13 @@ function processCall(vtx, from, graph, result, state) {
130
161
  continue;
131
162
  }
132
163
  addedTarget = true;
133
- processFunctionDefinition(targetVtx, vtx.id, graph, result, state);
164
+ processFunctionDefinition(targetVtx, vid, graph, result, state);
134
165
  }
135
166
  if (vtx.origin !== 'unnamed') {
136
167
  for (const origs of vtx.origin) {
137
168
  if (origs.startsWith('builtin:')) {
138
169
  addedTarget = true;
139
- result.addEdge(vtx.id, (0, built_in_1.builtInId)(origs.substring('builtin:'.length)), edge_1.EdgeType.Calls);
170
+ result.addEdge(vid, (0, built_in_1.builtInId)(origs.substring('builtin:'.length)), edge_1.EdgeType.Calls);
140
171
  }
141
172
  }
142
173
  }
@@ -147,7 +178,7 @@ function processCall(vtx, from, graph, result, state) {
147
178
  if (!oriVtx) {
148
179
  continue;
149
180
  }
150
- result.addEdge(vtx.id, ori, edge_1.EdgeType.Calls);
181
+ result.addEdge(vid, ori, edge_1.EdgeType.Calls);
151
182
  const name = graph.idMap?.get(ori);
152
183
  if (name?.lexeme && oriVtx.tag === vertex_1.VertexType.Use) {
153
184
  result.addVertex({
@@ -198,12 +229,12 @@ function processFunctionDefinition(vtx, from, graph, result, state) {
198
229
  result.addVertex(vtx, undefined, true);
199
230
  processCds(vtx, graph, result, state);
200
231
  const exits = new Set(vtx.exitPoints);
232
+ state.potentials.push([vtx.id, vtx.subflow.graph.difference(exits)]);
201
233
  for (const { nodeId } of exits) {
202
234
  const v = graph.getVertex(nodeId, true);
203
235
  if (v) {
204
236
  processUnknown(v, vtx.id, graph, result, state);
205
237
  }
206
238
  }
207
- state.potentials.push([vtx.id, vtx.subflow.graph.difference(exits)]);
208
239
  }
209
240
  //# sourceMappingURL=call-graph.js.map
@@ -34,33 +34,21 @@ function findNonLocalReads(graph, ignore) {
34
34
  const ids = new Set(graph.vertexIdsOfType(vertex_1.VertexType.Use).concat(graph.vertexIdsOfType(vertex_1.VertexType.FunctionCall)));
35
35
  /* find all variable use ids which do not link to a given id */
36
36
  const nonLocalReads = [];
37
- for (const id of ids) {
38
- if (ignores.has(id)) {
37
+ for (const nodeId of ids) {
38
+ if (ignores.has(nodeId)) {
39
39
  continue;
40
40
  }
41
- const outgoing = graph.outgoingEdges(id);
42
- const name = (0, node_id_1.recoverName)(id, graph.idMap);
43
- const origin = graph.getVertex(id, true);
41
+ const outgoing = graph.outgoingEdges(nodeId);
42
+ const name = (0, node_id_1.recoverName)(nodeId, graph.idMap);
43
+ const origin = graph.getVertex(nodeId, true);
44
+ const type = origin?.tag === vertex_1.VertexType.FunctionCall ? identifier_1.ReferenceType.Function : identifier_1.ReferenceType.Variable;
44
45
  if (outgoing === undefined) {
45
- nonLocalReads.push({
46
- name: (0, node_id_1.recoverName)(id, graph.idMap),
47
- nodeId: id,
48
- controlDependencies: undefined,
49
- type: origin?.tag === vertex_1.VertexType.FunctionCall ? identifier_1.ReferenceType.Function : identifier_1.ReferenceType.Variable
50
- });
46
+ nonLocalReads.push({ name, nodeId, type });
51
47
  continue;
52
48
  }
53
49
  for (const [target, { types }] of outgoing) {
54
50
  if ((0, edge_1.edgeIncludesType)(types, edge_1.EdgeType.Reads) && !ids.has(target)) {
55
- if (!name) {
56
- logger_1.dataflowLogger.warn('found non-local read without name for id ' + id);
57
- }
58
- nonLocalReads.push({
59
- name: (0, node_id_1.recoverName)(id, graph.idMap),
60
- nodeId: id,
61
- controlDependencies: undefined,
62
- type: origin?.tag === vertex_1.VertexType.FunctionCall ? identifier_1.ReferenceType.Function : identifier_1.ReferenceType.Variable
63
- });
51
+ nonLocalReads.push({ name, nodeId, type });
64
52
  break;
65
53
  }
66
54
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eagleoutice/flowr",
3
- "version": "2.8.0",
3
+ "version": "2.8.2",
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": {
@@ -75,7 +75,7 @@ class FlowrAnalyzerPlugin {
75
75
  }
76
76
  catch (error) {
77
77
  const duration = Date.now() - now;
78
- generalPluginLog.error(`Plugin ${this.name} (v${this.version.format()}, {this.type}) failed after ${duration}ms. Error: ${error.message}`);
78
+ generalPluginLog.error(`Plugin ${this.name} (v${this.version.format()}, ${this.type}) failed after ${duration}ms. Error: ${error.message}`);
79
79
  throw error;
80
80
  }
81
81
  }
@@ -1,4 +1,7 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.FlowrAnalyzerProjectDiscoveryPlugin = void 0;
4
7
  const flowr_analyzer_plugin_1 = require("../flowr-analyzer-plugin");
@@ -6,6 +9,7 @@ const semver_1 = require("semver");
6
9
  const flowr_file_1 = require("../../context/flowr-file");
7
10
  const files_1 = require("../../../util/files");
8
11
  const built_in_source_1 = require("../../../dataflow/internal/process/functions/call/built-in/built-in-source");
12
+ const path_1 = __importDefault(require("path"));
9
13
  /**
10
14
  * This is the base class for all plugins that discover files in a project for analysis.
11
15
  * These plugins interplay with the {@link FlowrAnalyzerFilesContext} to gather information about the files in the project.
@@ -52,11 +56,9 @@ class DefaultFlowrAnalyzerProjectDiscoveryPlugin extends FlowrAnalyzerProjectDis
52
56
  process(_context, args) {
53
57
  const requests = [];
54
58
  /* the dummy approach of collecting all files, group R and Rmd files, and be done with it */
55
- for (const file of (0, files_1.getAllFilesSync)(args.content)) {
56
- if (this.ignorePathsRegex.test(file)) {
57
- continue;
58
- }
59
- if (this.supportedExtensions.test(file) && (!this.onlyTraversePaths || this.onlyTraversePaths.test(file)) && !this.excludePathsRegex.test((0, built_in_source_1.platformDirname)(file))) {
59
+ for (const file of (0, files_1.getAllFilesSync)(args.content, /.*/, this.ignorePathsRegex)) {
60
+ const relativePath = path_1.default.relative(args.content, file);
61
+ if (this.supportedExtensions.test(relativePath) && (!this.onlyTraversePaths || this.onlyTraversePaths.test(relativePath)) && !this.excludePathsRegex.test((0, built_in_source_1.platformDirname)(relativePath))) {
60
62
  requests.push({ content: file, request: 'file' });
61
63
  }
62
64
  else {
@@ -2,16 +2,41 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.executeHigherOrderQuery = executeHigherOrderQuery;
4
4
  const higher_order_function_1 = require("../../../dataflow/fn/higher-order-function");
5
- const inspect_exception_query_executor_1 = require("../inspect-exceptions-query/inspect-exception-query-executor");
5
+ const parse_1 = require("../../../slicing/criterion/parse");
6
+ const vertex_1 = require("../../../dataflow/graph/vertex");
6
7
  /**
7
8
  * Execute higher-order function inspection queries on the given analyzer.
8
9
  */
9
10
  async function executeHigherOrderQuery({ analyzer }, queries) {
10
11
  const start = Date.now();
11
- const { cg, fns } = await (0, inspect_exception_query_executor_1.getFunctionsToConsiderInCallGraph)(queries, analyzer);
12
+ let filters = undefined;
13
+ // filter will remain undefined if at least one of the queries wants all functions
14
+ for (const q of queries) {
15
+ if (q.filter === undefined) {
16
+ filters = undefined;
17
+ break;
18
+ }
19
+ else {
20
+ filters ??= [];
21
+ filters = filters.concat(q.filter);
22
+ }
23
+ }
24
+ const ast = await analyzer.normalize();
25
+ const filterFor = new Set();
26
+ if (filters) {
27
+ for (const f of filters) {
28
+ const i = (0, parse_1.tryResolveSliceCriterionToId)(f, ast.idMap);
29
+ if (i !== undefined) {
30
+ filterFor.add(i);
31
+ }
32
+ }
33
+ }
34
+ const graph = (await analyzer.dataflow()).graph;
35
+ const fns = graph.verticesOfType(vertex_1.VertexType.FunctionDefinition)
36
+ .filter(([, v]) => filterFor.size === 0 || filterFor.has(v.id));
12
37
  const result = {};
13
38
  for (const [id,] of fns) {
14
- result[id] = (0, higher_order_function_1.isFunctionHigherOrder)(id, cg, analyzer.inspectContext());
39
+ result[id] = (0, higher_order_function_1.isFunctionHigherOrder)(id, graph, analyzer.inspectContext());
15
40
  }
16
41
  return {
17
42
  '.meta': {
package/queries/query.js CHANGED
@@ -169,7 +169,7 @@ async function genericWrapReplFailIfNoRequest(fn, output, analyzer) {
169
169
  output.stderr(output.formatter.format('No requests to analyze were found.', { color: 1 /* Colors.Red */, style: 1 /* FontStyles.Bold */, effect: ansi_1.ColorEffect.Foreground })
170
170
  + '\nIf you consider this an error, please report a bug: '
171
171
  + (0, assert_1.getGuardIssueUrl)('analyzer found no requests to analyze'));
172
- output.stderr((e instanceof Error) ? e.message : String(e));
172
+ output.stderr('Full error message: ' + ((e instanceof Error) ? e.message : String(e)));
173
173
  }
174
174
  else {
175
175
  throw e;
@@ -12,6 +12,6 @@ import type { ReadOnlyFlowrAnalyzerContext } from '../../project/context/flowr-a
12
12
  */
13
13
  export declare function getAllFunctionCallTargets(dataflowGraph: DataflowGraph, callerInfo: DataflowGraphVertexFunctionCall, baseEnvironment: REnvironmentInformation, queue: VisitingQueue, ctx: ReadOnlyFlowrAnalyzerContext): [Set<DataflowGraphVertexInfo>, REnvironmentInformation];
14
14
  /** returns the new threshold hit count */
15
- export declare function sliceForCall(current: NodeToSlice, callerInfo: DataflowGraphVertexFunctionCall, dataflowInformation: DataflowInformation, queue: VisitingQueue, ctx: ReadOnlyFlowrAnalyzerContext): void;
15
+ export declare function sliceForCall(current: NodeToSlice, callerInfo: DataflowGraphVertexFunctionCall, { graph }: DataflowInformation, queue: VisitingQueue, ctx: ReadOnlyFlowrAnalyzerContext): void;
16
16
  /** Returns true if we found at least one return edge */
17
17
  export declare function handleReturns(from: NodeId, queue: VisitingQueue, currentEdges: OutgoingEdges, baseEnvFingerprint: Fingerprint, baseEnvironment: REnvironmentInformation): boolean;
@@ -59,24 +59,25 @@ function linkCallTargets(onlyForSideEffects, functionCallTargets, activeEnvironm
59
59
  }
60
60
  }
61
61
  /** returns the new threshold hit count */
62
- function sliceForCall(current, callerInfo, dataflowInformation, queue, ctx) {
63
- const [functionCallTargets, activeEnvironment] = getAllFunctionCallTargets(dataflowInformation.graph, callerInfo, current.baseEnvironment, queue, ctx);
64
- const activeEnvironmentFingerprint = (0, fingerprint_1.envFingerprint)(activeEnvironment);
62
+ function sliceForCall(current, callerInfo, { graph }, queue, ctx) {
63
+ const [functionCallTargets, activeEnvironment] = getAllFunctionCallTargets(graph, callerInfo, current.baseEnvironment, queue, ctx);
65
64
  if (functionCallTargets.size === 0) {
66
65
  /*
67
66
  * if we do not have any call to resolve this function, we have to assume that every function passed is actually called!
68
67
  * hence, we add a new flag and add all argument values to the queue causing directly
69
68
  */
70
69
  for (const arg of callerInfo.args) {
71
- includeArgumentFunctionCallClosure(arg, activeEnvironment, queue, dataflowInformation.graph);
70
+ includeArgumentFunctionCallClosure(arg, activeEnvironment, queue, graph);
72
71
  }
73
72
  return;
74
73
  }
74
+ const activeEnvironmentFingerprint = (0, fingerprint_1.envFingerprint)(activeEnvironment);
75
75
  linkCallTargets(current.onlyForSideEffects, functionCallTargets, activeEnvironment, activeEnvironmentFingerprint, queue);
76
76
  }
77
+ const PotentialFollowOnReturn = edge_1.EdgeType.DefinesOnCall | edge_1.EdgeType.DefinedByOnCall | edge_1.EdgeType.Argument;
77
78
  /** Returns true if we found at least one return edge */
78
79
  function handleReturns(from, queue, currentEdges, baseEnvFingerprint, baseEnvironment) {
79
- const e = [...currentEdges.entries()];
80
+ const e = Array.from(currentEdges.entries());
80
81
  const found = e.filter(([_, edge]) => (0, edge_1.edgeIncludesType)(edge.types, edge_1.EdgeType.Returns));
81
82
  if (found.length === 0) {
82
83
  return false;
@@ -88,7 +89,7 @@ function handleReturns(from, queue, currentEdges, baseEnvFingerprint, baseEnviro
88
89
  if ((0, edge_1.edgeIncludesType)(edge.types, edge_1.EdgeType.Reads)) {
89
90
  queue.add(target, baseEnvironment, baseEnvFingerprint, false);
90
91
  }
91
- else if ((0, edge_1.edgeIncludesType)(edge.types, edge_1.EdgeType.DefinesOnCall | edge_1.EdgeType.DefinedByOnCall | edge_1.EdgeType.Argument)) {
92
+ else if ((0, edge_1.edgeIncludesType)(edge.types, PotentialFollowOnReturn)) {
92
93
  (0, static_slicer_1.updatePotentialAddition)(queue, from, target, baseEnvironment, baseEnvFingerprint);
93
94
  }
94
95
  }
package/util/files.d.ts CHANGED
@@ -19,7 +19,7 @@ export declare function getAllFiles(dir: string, suffix?: RegExp): AsyncGenerato
19
19
  * Retrieves all files in the given directory recursively (synchronously)
20
20
  * @see {@link getAllFiles} - for an asynchronous version.
21
21
  */
22
- export declare function getAllFilesSync(dir: string, suffix?: RegExp): Generator<string>;
22
+ export declare function getAllFilesSync(dir: string, suffix?: RegExp, ignoreDirs?: RegExp | undefined): Generator<string>;
23
23
  /**
24
24
  * Retrieves all R files in a given directory (asynchronously)
25
25
  * @param input - directory-path to start the search from, can be a file as well. Will just return the file then.
package/util/files.js CHANGED
@@ -72,12 +72,14 @@ async function* getAllFiles(dir, suffix = /.*/) {
72
72
  * Retrieves all files in the given directory recursively (synchronously)
73
73
  * @see {@link getAllFiles} - for an asynchronous version.
74
74
  */
75
- function* getAllFilesSync(dir, suffix = /.*/) {
75
+ function* getAllFilesSync(dir, suffix = /.*/, ignoreDirs = undefined) {
76
76
  const entries = fs_1.default.readdirSync(dir, { withFileTypes: true, recursive: false });
77
77
  for (const subEntries of entries) {
78
78
  const res = path_1.default.resolve(dir, subEntries.name);
79
79
  if (subEntries.isDirectory()) {
80
- yield* getAllFilesSync(res, suffix);
80
+ if (!ignoreDirs?.test(subEntries.name)) {
81
+ yield* getAllFilesSync(res, suffix);
82
+ }
81
83
  }
82
84
  else if (suffix.test(subEntries.name)) {
83
85
  yield res;
package/util/r-version.js CHANGED
@@ -9,60 +9,47 @@ function makeVersion(version, original) {
9
9
  semver.str = original;
10
10
  return semver;
11
11
  }
12
- /**
13
- * Additionally, this transforms `a-b.c` into `a.0.0-b.c` and `a.b-c.d` into `a.b.0-c.d` to
14
- * ensure valid SemVer format.
15
- */
16
- function normalizeTooShortVersions(versions) {
17
- const TooShortVersionRegex = /(^|(?<=[^\d.-]))(?<major>\d+)(\.(?<minor>\d+))?\.?-(?<prerelease>[0-9A-Za-z-.]+)(\s|$)/g;
18
- let newVersions = '';
19
- let match;
20
- while ((match = TooShortVersionRegex.exec(versions)) !== null) {
21
- const { major, minor, prerelease } = match.groups;
22
- const prefix = versions.slice(0, match.index);
23
- let newVersion = '';
24
- if (minor === undefined) {
25
- // only major version present
26
- newVersion = `${major}.0.0-${prerelease}`;
27
- }
28
- else {
29
- // major and minor version present, but dot before prerelease
30
- newVersion = `${major}.${minor}.0-${prerelease}`;
12
+ function normalizeVersion(version) {
13
+ version = version.trim();
14
+ let comparator = '';
15
+ // extract comparator if present
16
+ const comparatorMatch = version.match(/^([<>=~^]+)/);
17
+ if (comparatorMatch) {
18
+ comparator = comparatorMatch[1].replace('==', '=');
19
+ version = version.slice(comparatorMatch[1].length).trim();
20
+ }
21
+ const [mainVersion, ...preReleaseParts] = version.split('-');
22
+ const mainVersionParts = mainVersion.split('.');
23
+ if (mainVersionParts.length > 3) {
24
+ const newPreReleasePart = mainVersionParts.splice(3).join('.');
25
+ preReleaseParts.unshift(newPreReleasePart);
26
+ }
27
+ else {
28
+ while (mainVersionParts.length < 3) {
29
+ mainVersionParts.push('0');
31
30
  }
32
- newVersions += `${prefix}${newVersion}`;
33
- versions = versions.slice(match.index + match[0].length);
34
31
  }
35
- newVersions += versions; // append any remaining part
36
- return newVersions;
32
+ return comparator + mainVersionParts.map(n => String(Number(n))).join('.') + (preReleaseParts.length > 0 ? `-${preReleaseParts.join('-')}` : '');
37
33
  }
38
- /**
39
- * For every version `a.b.c.d.e...` converts it to `a.b.c-d.e...`
40
- * If the version already contains a prerelease part, it is appended:
41
- * `a.b.c.d.e-prerelease...` becomes `a.b.c-d.e-prerelease...`
42
- */
43
- function normalizeAdditionalVersionsToPrerelease(version) {
44
- version = normalizeTooShortVersions(version);
45
- const AdditionalVersionRegex = /(\d+\.\d+\.\d+)(?<additional>(\.\d+)+)(-(?<prerelease>[0-9A-Za-z-.]+))?/g;
34
+ const AnyVerWithMaybeRangeRegex = /(\s*[<>=~^]*\s*\d+(\.\d*)*(-[0-9A-Za-z-.]+)?)/g;
35
+ function normalizeVersions(versions) {
36
+ const parts = [];
37
+ // extract all version-like parts and normalize them individually
46
38
  let match;
47
- let newVersion = '';
48
- // repeat until no more matches
49
- while ((match = AdditionalVersionRegex.exec(version)) !== null) {
50
- // append to new version, first take over anything before the match
51
- const prefix = version.slice(0, match.index);
52
- const { additional, prerelease } = match.groups;
53
- const additionalParts = additional.slice(1).split('.'); // remove leading dot and split
54
- if (additionalParts.length === 0) {
55
- continue; // nothing to do
39
+ let index = 0;
40
+ while ((match = AnyVerWithMaybeRangeRegex.exec(versions)) !== null) {
41
+ const prefix = versions.slice(index, match.index);
42
+ if (prefix.length > 0) {
43
+ parts.push(prefix);
56
44
  }
57
- let newPrerelease = additionalParts.join('.');
58
- if (prerelease) {
59
- newPrerelease = `${newPrerelease}-${prerelease}`;
60
- }
61
- newVersion = `${prefix}${match[1]}-${newPrerelease}`;
62
- version = version.slice(match.index + match[0].length);
45
+ const versionPart = match[1];
46
+ parts.push(normalizeVersion(versionPart));
47
+ index = match.index + versionPart.length;
48
+ }
49
+ if (index < versions.length) {
50
+ parts.push(versions.slice(index));
63
51
  }
64
- newVersion += version; // append any remaining part
65
- return newVersion;
52
+ return parts.join('');
66
53
  }
67
54
  /**
68
55
  * This parses an R version string and returns a SemVer object.
@@ -76,7 +63,7 @@ function parseRVersion(version) {
76
63
  }
77
64
  catch { /* do nothing */ }
78
65
  try {
79
- const normalized = normalizeAdditionalVersionsToPrerelease(version);
66
+ const normalized = normalizeVersions(version);
80
67
  return makeVersion(normalized, version);
81
68
  }
82
69
  catch { /* do nothing */ }
@@ -100,7 +87,7 @@ function parseRRange(range) {
100
87
  return makeRange(range, range);
101
88
  }
102
89
  catch { /* try to normalize R range to SemVer */ }
103
- const normalized = normalizeAdditionalVersionsToPrerelease(range);
104
- return makeRange(normalized.replace(/==/g, '='), range);
90
+ const normalized = normalizeVersions(range);
91
+ return makeRange(normalized, range);
105
92
  }
106
93
  //# sourceMappingURL=r-version.js.map
package/util/version.js CHANGED
@@ -6,7 +6,7 @@ exports.printVersionInformation = printVersionInformation;
6
6
  const semver_1 = require("semver");
7
7
  const assert_1 = require("./assert");
8
8
  // this is automatically replaced with the current version by release-it
9
- const version = '2.8.0';
9
+ const version = '2.8.2';
10
10
  /**
11
11
  * Retrieves the current flowR version as a new {@link SemVer} object.
12
12
  */