@eagleoutice/flowr 2.4.8 → 2.5.0
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.
- package/README.md +28 -22
- package/benchmark/slicer.js +1 -1
- package/benchmark/summarizer/second-phase/graph.js +2 -2
- package/cli/slicer-app.js +7 -2
- package/documentation/print-linting-and-testing-wiki.js +4 -0
- package/documentation/print-readme.js +6 -0
- package/package.json +4 -1
- package/r-bridge/lang-4.x/tree-sitter/tree-sitter-executor.js +1 -1
- package/r-bridge/retriever.d.ts +14 -2
- package/r-bridge/retriever.js +10 -4
- package/util/formats/adapter-format.d.ts +6 -0
- package/util/formats/adapter-format.js +3 -0
- package/util/formats/adapter.d.ts +16 -0
- package/util/formats/adapter.js +42 -0
- package/util/formats/adapters/r-adapter.d.ts +4 -0
- package/util/formats/adapters/r-adapter.js +7 -0
- package/util/formats/adapters/rmd-adapter.d.ts +26 -0
- package/util/formats/adapters/rmd-adapter.js +91 -0
- 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.4.
|
|
27
|
+
flowR repl using flowR v2.4.8, R v4.5.0 (r-shell engine)
|
|
28
28
|
R> :query @linter "read.csv(\"/root/x.txt\")"
|
|
29
29
|
```
|
|
30
30
|
|
|
@@ -78,34 +78,34 @@ It offers a wide variety of features, for example:
|
|
|
78
78
|
|
|
79
79
|
_Results (prettified and summarized):_
|
|
80
80
|
|
|
81
|
-
Query: **linter** (
|
|
81
|
+
Query: **linter** (15 ms)\
|
|
82
82
|
╰ **Deprecated Functions** (deprecated-functions):\
|
|
83
83
|
╰ _Metadata_: <code>{"totalDeprecatedCalls":0,"totalDeprecatedFunctionDefinitions":0,"searchTimeMs":2,"processTimeMs":0}</code>\
|
|
84
84
|
╰ **File Path Validity** (file-path-validity):\
|
|
85
85
|
╰ certain:\
|
|
86
86
|
╰ Path `/root/x.txt` at 1.1-23\
|
|
87
|
-
╰ _Metadata_: <code>{"totalReads":1,"totalUnknown":0,"totalWritesBeforeAlways":0,"totalValid":0,"searchTimeMs":
|
|
87
|
+
╰ _Metadata_: <code>{"totalReads":1,"totalUnknown":0,"totalWritesBeforeAlways":0,"totalValid":0,"searchTimeMs":5,"processTimeMs":0}</code>\
|
|
88
88
|
╰ **Seeded Randomness** (seeded-randomness):\
|
|
89
|
-
╰ _Metadata_: <code>{"consumerCalls":0,"callsWithFunctionProducers":0,"callsWithAssignmentProducers":0,"callsWithNonConstantProducers":0,"searchTimeMs":
|
|
89
|
+
╰ _Metadata_: <code>{"consumerCalls":0,"callsWithFunctionProducers":0,"callsWithAssignmentProducers":0,"callsWithNonConstantProducers":0,"searchTimeMs":1,"processTimeMs":0}</code>\
|
|
90
90
|
╰ **Absolute Paths** (absolute-file-paths):\
|
|
91
91
|
╰ certain:\
|
|
92
92
|
╰ Path `/root/x.txt` at 1.1-23\
|
|
93
|
-
╰ _Metadata_: <code>{"totalConsidered":1,"totalUnknown":0,"searchTimeMs":
|
|
93
|
+
╰ _Metadata_: <code>{"totalConsidered":1,"totalUnknown":0,"searchTimeMs":2,"processTimeMs":0}</code>\
|
|
94
94
|
╰ **Unused Definitions** (unused-definitions):\
|
|
95
95
|
╰ _Metadata_: <code>{"totalConsidered":0,"searchTimeMs":0,"processTimeMs":0}</code>\
|
|
96
96
|
╰ **Naming Convention** (naming-convention):\
|
|
97
97
|
╰ _Metadata_: <code>{"numMatches":0,"numBreak":0,"searchTimeMs":0,"processTimeMs":0}</code>\
|
|
98
98
|
╰ **Dataframe Access Validation** (dataframe-access-validation):\
|
|
99
|
-
╰ _Metadata_: <code>{"numOperations":0,"numAccesses":0,"totalAccessed":0,"searchTimeMs":0,"processTimeMs":
|
|
99
|
+
╰ _Metadata_: <code>{"numOperations":0,"numAccesses":0,"totalAccessed":0,"searchTimeMs":0,"processTimeMs":4}</code>\
|
|
100
100
|
╰ **Dead Code** (dead-code):\
|
|
101
101
|
╰ _Metadata_: <code>{"consideredNodes":5,"searchTimeMs":1,"processTimeMs":0}</code>\
|
|
102
102
|
╰ **Useless Loops** (useless-loop):\
|
|
103
|
-
╰ _Metadata_: <code>{"numOfUselessLoops":0,"searchTimeMs":0,"processTimeMs":
|
|
104
|
-
_All queries together required ≈
|
|
103
|
+
╰ _Metadata_: <code>{"numOfUselessLoops":0,"searchTimeMs":0,"processTimeMs":0}</code>\
|
|
104
|
+
_All queries together required ≈16 ms (1ms accuracy, total 209 ms)_
|
|
105
105
|
|
|
106
106
|
<details> <summary style="color:gray">Show Detailed Results as Json</summary>
|
|
107
107
|
|
|
108
|
-
The analysis required
|
|
108
|
+
The analysis required _209.2 ms_ (including parsing and normalization and the query) within the generation environment.
|
|
109
109
|
|
|
110
110
|
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.
|
|
111
111
|
Please consult the [Interface](https://github.com/flowr-analysis/flowr/wiki/Interface) wiki page for more information on how to get those.
|
|
@@ -144,8 +144,8 @@ It offers a wide variety of features, for example:
|
|
|
144
144
|
"totalUnknown": 0,
|
|
145
145
|
"totalWritesBeforeAlways": 0,
|
|
146
146
|
"totalValid": 0,
|
|
147
|
-
"searchTimeMs":
|
|
148
|
-
"processTimeMs":
|
|
147
|
+
"searchTimeMs": 5,
|
|
148
|
+
"processTimeMs": 0
|
|
149
149
|
}
|
|
150
150
|
},
|
|
151
151
|
"seeded-randomness": {
|
|
@@ -155,8 +155,8 @@ It offers a wide variety of features, for example:
|
|
|
155
155
|
"callsWithFunctionProducers": 0,
|
|
156
156
|
"callsWithAssignmentProducers": 0,
|
|
157
157
|
"callsWithNonConstantProducers": 0,
|
|
158
|
-
"searchTimeMs":
|
|
159
|
-
"processTimeMs":
|
|
158
|
+
"searchTimeMs": 1,
|
|
159
|
+
"processTimeMs": 0
|
|
160
160
|
}
|
|
161
161
|
},
|
|
162
162
|
"absolute-file-paths": {
|
|
@@ -175,8 +175,8 @@ It offers a wide variety of features, for example:
|
|
|
175
175
|
".meta": {
|
|
176
176
|
"totalConsidered": 1,
|
|
177
177
|
"totalUnknown": 0,
|
|
178
|
-
"searchTimeMs":
|
|
179
|
-
"processTimeMs":
|
|
178
|
+
"searchTimeMs": 2,
|
|
179
|
+
"processTimeMs": 0
|
|
180
180
|
}
|
|
181
181
|
},
|
|
182
182
|
"unused-definitions": {
|
|
@@ -203,7 +203,7 @@ It offers a wide variety of features, for example:
|
|
|
203
203
|
"numAccesses": 0,
|
|
204
204
|
"totalAccessed": 0,
|
|
205
205
|
"searchTimeMs": 0,
|
|
206
|
-
"processTimeMs":
|
|
206
|
+
"processTimeMs": 4
|
|
207
207
|
}
|
|
208
208
|
},
|
|
209
209
|
"dead-code": {
|
|
@@ -219,16 +219,16 @@ It offers a wide variety of features, for example:
|
|
|
219
219
|
".meta": {
|
|
220
220
|
"numOfUselessLoops": 0,
|
|
221
221
|
"searchTimeMs": 0,
|
|
222
|
-
"processTimeMs":
|
|
222
|
+
"processTimeMs": 0
|
|
223
223
|
}
|
|
224
224
|
}
|
|
225
225
|
},
|
|
226
226
|
".meta": {
|
|
227
|
-
"timing":
|
|
227
|
+
"timing": 15
|
|
228
228
|
}
|
|
229
229
|
},
|
|
230
230
|
".meta": {
|
|
231
|
-
"timing":
|
|
231
|
+
"timing": 16
|
|
232
232
|
}
|
|
233
233
|
}
|
|
234
234
|
```
|
|
@@ -295,7 +295,7 @@ It offers a wide variety of features, for example:
|
|
|
295
295
|
|
|
296
296
|
```shell
|
|
297
297
|
$ docker run -it --rm eagleoutice/flowr # or npm run flowr
|
|
298
|
-
flowR repl using flowR v2.4.
|
|
298
|
+
flowR repl using flowR v2.4.8, R v4.5.0 (r-shell engine)
|
|
299
299
|
R> :slicer test/testfiles/example.R --criterion "11@sum"
|
|
300
300
|
```
|
|
301
301
|
|
|
@@ -378,7 +378,7 @@ It offers a wide variety of features, for example:
|
|
|
378
378
|
|
|
379
379
|
```shell
|
|
380
380
|
$ docker run -it --rm eagleoutice/flowr # or npm run flowr
|
|
381
|
-
flowR repl using flowR v2.4.
|
|
381
|
+
flowR repl using flowR v2.4.8, R v4.5.0 (r-shell engine)
|
|
382
382
|
R> :dataflow* test/testfiles/example.R
|
|
383
383
|
```
|
|
384
384
|
|
|
@@ -683,7 +683,7 @@ It offers a wide variety of features, for example:
|
|
|
683
683
|
```
|
|
684
684
|
|
|
685
685
|
|
|
686
|
-
(The analysis required _14.
|
|
686
|
+
(The analysis required _14.2 ms_ (including parse and normalize, using the [r-shell](https://github.com/flowr-analysis/flowr/wiki/Engines) engine) within the generation environment.)
|
|
687
687
|
|
|
688
688
|
|
|
689
689
|
|
|
@@ -728,6 +728,12 @@ You can enter <span title="Description (Repl Command): Show help information (al
|
|
|
728
728
|
|
|
729
729
|

|
|
730
730
|
|
|
731
|
+
If you want to use the same commands:
|
|
732
|
+
|
|
733
|
+
1. First this runs `docker run -it --rm eagleoutice/flowr` in a terminal to start the REPL.
|
|
734
|
+
2. In the REPL, it runs `:slicer -c '11@prod' demo.R --diff` to slice the example file `demo.R` for the print statement in line 11.
|
|
735
|
+
Please note that the `11` refers to the 11th line number to slice for!
|
|
736
|
+
|
|
731
737
|
</details>
|
|
732
738
|
|
|
733
739
|
## 📜 More Information
|
package/benchmark/slicer.js
CHANGED
|
@@ -9,7 +9,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
10
|
exports.BenchmarkSlicer = exports.benchmarkLogger = void 0;
|
|
11
11
|
const stopwatch_1 = require("./stopwatch");
|
|
12
|
-
const fs_1 = __importDefault(require("fs"));
|
|
13
12
|
const seedrandom_1 = __importDefault(require("seedrandom"));
|
|
14
13
|
const log_1 = require("../util/log");
|
|
15
14
|
const assert_1 = require("../util/assert");
|
|
@@ -30,6 +29,7 @@ const extract_cfg_1 = require("../control-flow/extract-cfg");
|
|
|
30
29
|
const absint_info_1 = require("../abstract-interpretation/data-frame/absint-info");
|
|
31
30
|
const domain_1 = require("../abstract-interpretation/data-frame/domain");
|
|
32
31
|
const shape_inference_1 = require("../abstract-interpretation/data-frame/shape-inference");
|
|
32
|
+
const fs_1 = __importDefault(require("fs"));
|
|
33
33
|
/**
|
|
34
34
|
* The logger to be used for benchmarking as a global object.
|
|
35
35
|
*/
|
|
@@ -19,7 +19,7 @@ function writeGraphOutput(ultimate, outputGraphPath) {
|
|
|
19
19
|
name: pointName[0].toUpperCase() + pointName.slice(1),
|
|
20
20
|
unit: 'ms',
|
|
21
21
|
value: Number(measurement.mean / 1e6),
|
|
22
|
-
range: Number(measurement.std / 1e6),
|
|
22
|
+
range: String(Number(measurement.std / 1e6)),
|
|
23
23
|
extra: `median: ${(measurement.median / 1e6).toFixed(2)}ms`
|
|
24
24
|
});
|
|
25
25
|
}
|
|
@@ -51,7 +51,7 @@ function writeGraphOutput(ultimate, outputGraphPath) {
|
|
|
51
51
|
name: 'memory (df-graph)',
|
|
52
52
|
unit: 'KiB',
|
|
53
53
|
value: ultimate.dataflow.sizeOfObject.mean / 1024,
|
|
54
|
-
range: ultimate.dataflow.sizeOfObject.std / 1024,
|
|
54
|
+
range: String(ultimate.dataflow.sizeOfObject.std / 1024),
|
|
55
55
|
extra: `median: ${(ultimate.dataflow.sizeOfObject.median / 1024).toFixed(2)}`
|
|
56
56
|
});
|
|
57
57
|
// write the output file
|
package/cli/slicer-app.js
CHANGED
|
@@ -15,6 +15,7 @@ const print_1 = require("../benchmark/stats/print");
|
|
|
15
15
|
const magic_comments_1 = require("../reconstruct/auto-select/magic-comments");
|
|
16
16
|
const auto_select_defaults_1 = require("../reconstruct/auto-select/auto-select-defaults");
|
|
17
17
|
const config_1 = require("../config");
|
|
18
|
+
const adapter_1 = require("../util/formats/adapter");
|
|
18
19
|
const options = (0, script_1.processCommandLineArgs)('slicer', ['input', 'criterion'], {
|
|
19
20
|
subtitle: 'Slice R code based on a given slicing criterion',
|
|
20
21
|
examples: [
|
|
@@ -32,7 +33,7 @@ async function getSlice() {
|
|
|
32
33
|
const config = (0, config_1.getConfig)();
|
|
33
34
|
await slicer.init(options['input-is-text']
|
|
34
35
|
? { request: 'text', content: options.input.replaceAll('\\n', '\n') }
|
|
35
|
-
:
|
|
36
|
+
: (0, adapter_1.requestFromFile)(options.input), config, options['no-magic-comments'] ? auto_select_defaults_1.doNotAutoSelect : (0, magic_comments_1.makeMagicCommentHandler)(auto_select_defaults_1.doNotAutoSelect));
|
|
36
37
|
let mappedSlices = [];
|
|
37
38
|
let reconstruct = undefined;
|
|
38
39
|
const doSlicing = options.criterion.trim() !== '';
|
|
@@ -74,7 +75,11 @@ async function getSlice() {
|
|
|
74
75
|
}
|
|
75
76
|
else {
|
|
76
77
|
if (doSlicing && options.diff) {
|
|
77
|
-
|
|
78
|
+
let originalCode = options.input;
|
|
79
|
+
if (!options['input-is-text']) {
|
|
80
|
+
const request = (0, adapter_1.requestFromFile)(options.input);
|
|
81
|
+
originalCode = request.request === 'text' ? request.content : fs_1.default.readFileSync(request.content).toString();
|
|
82
|
+
}
|
|
78
83
|
console.log((0, slice_diff_ansi_1.sliceDiffAnsi)(slice.result, normalize, new Set(mappedSlices.map(({ id }) => id)), originalCode));
|
|
79
84
|
}
|
|
80
85
|
if (options.stats) {
|
|
@@ -201,6 +201,10 @@ To get started, install the [vitest Extension](https://marketplace.visualstudio.
|
|
|
201
201
|
#### Webstorm
|
|
202
202
|
|
|
203
203
|
Please follow the official guide [here](https://www.jetbrains.com/help/webstorm/vitest.html).
|
|
204
|
+
Note that the working directory has to be set to the project root directory, not the test subdirectory!
|
|
205
|
+
Otherwise, the tests will not be instantiated.
|
|
206
|
+
|
|
207
|
+

|
|
204
208
|
|
|
205
209
|
<a id='ci-pipeline'></a>
|
|
206
210
|
## 🪈 CI Pipeline
|
|
@@ -142,6 +142,12 @@ You can enter ${(0, doc_cli_option_1.getReplCommand)('help')} to gain more infor
|
|
|
142
142
|
|
|
143
143
|

|
|
144
144
|
|
|
145
|
+
If you want to use the same commands:
|
|
146
|
+
|
|
147
|
+
1. First this runs \`docker run -it --rm eagleoutice/flowr\` in a terminal to start the REPL.
|
|
148
|
+
2. In the REPL, it runs \`:slicer -c '11@prod' demo.R --diff\` to slice the example file \`demo.R\` for the print statement in line 11.
|
|
149
|
+
Please note that the \`11\` refers to the 11th line number to slice for!
|
|
150
|
+
|
|
145
151
|
</details>
|
|
146
152
|
|
|
147
153
|
## 📜 More Information
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eagleoutice/flowr",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0",
|
|
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": {
|
|
@@ -183,6 +183,7 @@
|
|
|
183
183
|
"@j-ulrich/release-it-regex-bumper": "^5.3.0",
|
|
184
184
|
"@types/command-line-args": "^5.2.3",
|
|
185
185
|
"@types/command-line-usage": "^5.0.4",
|
|
186
|
+
"@types/commonmark": "^0.27.10",
|
|
186
187
|
"@types/n-readlines": "^1.0.6",
|
|
187
188
|
"@types/n3": "^1.26.0",
|
|
188
189
|
"@types/object-hash": "^3.0.6",
|
|
@@ -212,6 +213,8 @@
|
|
|
212
213
|
"clipboardy": "^4.0.0",
|
|
213
214
|
"command-line-args": "^6.0.1",
|
|
214
215
|
"command-line-usage": "^7.0.3",
|
|
216
|
+
"commonmark": "^0.31.2",
|
|
217
|
+
"gray-matter": "^4.0.3",
|
|
215
218
|
"joi": "^18.0.1",
|
|
216
219
|
"lz-string": "^1.5.0",
|
|
217
220
|
"n-readlines": "^1.0.1",
|
|
@@ -5,8 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.TreeSitterExecutor = exports.DEFAULT_TREE_SITTER_WASM_PATH = exports.DEFAULT_TREE_SITTER_R_WASM_PATH = void 0;
|
|
7
7
|
const web_tree_sitter_1 = __importDefault(require("web-tree-sitter"));
|
|
8
|
-
const fs_1 = __importDefault(require("fs"));
|
|
9
8
|
const log_1 = require("../../../util/log");
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
10
|
exports.DEFAULT_TREE_SITTER_R_WASM_PATH = './node_modules/@eagleoutice/tree-sitter-r/tree-sitter-r.wasm';
|
|
11
11
|
exports.DEFAULT_TREE_SITTER_WASM_PATH = './node_modules/web-tree-sitter/tree-sitter.wasm';
|
|
12
12
|
const wasmLog = log_1.log.getSubLogger({ name: 'tree-sitter-wasm' });
|
package/r-bridge/retriever.d.ts
CHANGED
|
@@ -2,16 +2,24 @@ import { type RShell } from './shell';
|
|
|
2
2
|
import type { AsyncOrSync } from 'ts-essentials';
|
|
3
3
|
import { RShellExecutor } from './shell-executor';
|
|
4
4
|
import type { NormalizedAst } from './lang-4.x/ast/model/processing/decorate';
|
|
5
|
+
import type { SupportedFormats } from '../util/formats/adapter-format';
|
|
5
6
|
export declare const fileProtocol = "file://";
|
|
6
|
-
export interface
|
|
7
|
+
export interface PraseRequestAdditionalInfoBase {
|
|
8
|
+
type: SupportedFormats;
|
|
9
|
+
}
|
|
10
|
+
export interface RParseRequestFromFile<AdditionalInfo extends PraseRequestAdditionalInfoBase = PraseRequestAdditionalInfoBase> {
|
|
7
11
|
readonly request: 'file';
|
|
8
12
|
/**
|
|
9
13
|
* The path to the file (an absolute path is probably best here).
|
|
10
14
|
* See {@link RParseRequests} for multiple files.
|
|
11
15
|
*/
|
|
12
16
|
readonly content: string;
|
|
17
|
+
/**
|
|
18
|
+
* Aditional info from different file formates like .Rmd
|
|
19
|
+
*/
|
|
20
|
+
readonly info?: AdditionalInfo;
|
|
13
21
|
}
|
|
14
|
-
export interface RParseRequestFromText {
|
|
22
|
+
export interface RParseRequestFromText<AdditionalInfo extends PraseRequestAdditionalInfoBase = PraseRequestAdditionalInfoBase> {
|
|
15
23
|
readonly request: 'text';
|
|
16
24
|
/**
|
|
17
25
|
* Source code to parse (not a file path).
|
|
@@ -20,6 +28,10 @@ export interface RParseRequestFromText {
|
|
|
20
28
|
* or concatenate their contents to pass them with this request.
|
|
21
29
|
*/
|
|
22
30
|
readonly content: string;
|
|
31
|
+
/**
|
|
32
|
+
* Aditional info from different file formates like .Rmd
|
|
33
|
+
*/
|
|
34
|
+
readonly info?: AdditionalInfo;
|
|
23
35
|
}
|
|
24
36
|
/**
|
|
25
37
|
* A provider for an {@link RParseRequests} that can be used, for example, to override source file parsing behavior in tests
|
package/r-bridge/retriever.js
CHANGED
|
@@ -22,6 +22,7 @@ const decorate_1 = require("./lang-4.x/ast/model/processing/decorate");
|
|
|
22
22
|
const type_1 = require("./lang-4.x/ast/model/type");
|
|
23
23
|
const fs_1 = __importDefault(require("fs"));
|
|
24
24
|
const path_1 = __importDefault(require("path"));
|
|
25
|
+
const adapter_1 = require("../util/formats/adapter");
|
|
25
26
|
exports.fileProtocol = 'file://';
|
|
26
27
|
/**
|
|
27
28
|
* Creates a {@link RParseRequests} from a given input.
|
|
@@ -35,10 +36,15 @@ function requestFromInput(input) {
|
|
|
35
36
|
}
|
|
36
37
|
const content = input;
|
|
37
38
|
const file = content.startsWith(exports.fileProtocol);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
if (file) {
|
|
40
|
+
return (0, adapter_1.requestFromFile)(content.slice(7));
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
return {
|
|
44
|
+
request: 'text',
|
|
45
|
+
content: content
|
|
46
|
+
};
|
|
47
|
+
}
|
|
42
48
|
}
|
|
43
49
|
function requestProviderFromFile() {
|
|
44
50
|
return {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { RParseRequest } from '../../r-bridge/retriever';
|
|
2
|
+
export declare const FileAdapters: {
|
|
3
|
+
readonly R: {
|
|
4
|
+
convertRequest: (request: RParseRequest) => RParseRequest;
|
|
5
|
+
};
|
|
6
|
+
readonly Rmd: {
|
|
7
|
+
convertRequest: (request: RParseRequest) => import("../../r-bridge/retriever").RParseRequestFromText<import("./adapters/rmd-adapter").RmdInfo>;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
export declare const DocumentTypeToFormat: {
|
|
11
|
+
readonly '.r': "R";
|
|
12
|
+
readonly '.rmd': "Rmd";
|
|
13
|
+
};
|
|
14
|
+
export type AdapterReturnTypes = ReturnType<typeof FileAdapters[keyof typeof FileAdapters]['convertRequest']>;
|
|
15
|
+
export declare function requestFromFile(path: string): AdapterReturnTypes;
|
|
16
|
+
export declare function inferFileType(request: RParseRequest): keyof typeof FileAdapters;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DocumentTypeToFormat = exports.FileAdapters = void 0;
|
|
7
|
+
exports.requestFromFile = requestFromFile;
|
|
8
|
+
exports.inferFileType = inferFileType;
|
|
9
|
+
const r_adapter_1 = require("./adapters/r-adapter");
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const rmd_adapter_1 = require("./adapters/rmd-adapter");
|
|
12
|
+
exports.FileAdapters = {
|
|
13
|
+
'R': r_adapter_1.RAdapter,
|
|
14
|
+
'Rmd': rmd_adapter_1.RmdAdapter
|
|
15
|
+
};
|
|
16
|
+
exports.DocumentTypeToFormat = {
|
|
17
|
+
'.r': 'R',
|
|
18
|
+
'.rmd': 'Rmd'
|
|
19
|
+
};
|
|
20
|
+
function requestFromFile(path) {
|
|
21
|
+
const baseRequest = {
|
|
22
|
+
request: 'file',
|
|
23
|
+
content: path
|
|
24
|
+
};
|
|
25
|
+
const type = inferFileType(baseRequest);
|
|
26
|
+
return exports.FileAdapters[type].convertRequest(baseRequest);
|
|
27
|
+
}
|
|
28
|
+
function inferFileType(request) {
|
|
29
|
+
if (request.request === 'text') {
|
|
30
|
+
// For now we don't know what type the request is
|
|
31
|
+
// and have to assume it is normal R Code
|
|
32
|
+
// In the future we could add a heuristic to guess the type
|
|
33
|
+
return 'R';
|
|
34
|
+
}
|
|
35
|
+
const type = path_1.default.extname(request.content).toLowerCase();
|
|
36
|
+
// Fallback to default if unknown
|
|
37
|
+
if (!Object.hasOwn(exports.DocumentTypeToFormat, type)) {
|
|
38
|
+
return 'R';
|
|
39
|
+
}
|
|
40
|
+
return exports.DocumentTypeToFormat[type];
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=adapter.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Node } from 'commonmark';
|
|
2
|
+
import type { RParseRequest, RParseRequestFromText } from '../../../r-bridge/retriever';
|
|
3
|
+
export interface CodeBlock {
|
|
4
|
+
options: string;
|
|
5
|
+
code: string;
|
|
6
|
+
}
|
|
7
|
+
export type CodeBlockEx = CodeBlock & {
|
|
8
|
+
startpos: {
|
|
9
|
+
line: number;
|
|
10
|
+
col: number;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
export interface RmdInfo {
|
|
14
|
+
type: 'Rmd';
|
|
15
|
+
blocks: CodeBlock[];
|
|
16
|
+
options: object;
|
|
17
|
+
}
|
|
18
|
+
export declare const RmdAdapter: {
|
|
19
|
+
convertRequest: (request: RParseRequest) => RParseRequestFromText<RmdInfo>;
|
|
20
|
+
};
|
|
21
|
+
export declare function isRCodeBlock(node: Node): node is Node & {
|
|
22
|
+
literal: string;
|
|
23
|
+
info: string;
|
|
24
|
+
};
|
|
25
|
+
export declare function restoreBlocksWithoutMd(blocks: CodeBlockEx[], totalLines: number): string;
|
|
26
|
+
export declare function parseCodeBlockOptions(header: string, content: string): string;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.RmdAdapter = void 0;
|
|
7
|
+
exports.isRCodeBlock = isRCodeBlock;
|
|
8
|
+
exports.restoreBlocksWithoutMd = restoreBlocksWithoutMd;
|
|
9
|
+
exports.parseCodeBlockOptions = parseCodeBlockOptions;
|
|
10
|
+
const fs_1 = __importDefault(require("fs"));
|
|
11
|
+
const commonmark_1 = require("commonmark");
|
|
12
|
+
const gray_matter_1 = __importDefault(require("gray-matter"));
|
|
13
|
+
const assert_1 = require("../../assert");
|
|
14
|
+
exports.RmdAdapter = {
|
|
15
|
+
convertRequest: (request) => {
|
|
16
|
+
// Read and Parse Markdown
|
|
17
|
+
const raw = request.request === 'text'
|
|
18
|
+
? request.content
|
|
19
|
+
: fs_1.default.readFileSync(request.content, 'utf-8').toString();
|
|
20
|
+
const parser = new commonmark_1.Parser();
|
|
21
|
+
const ast = parser.parse(raw);
|
|
22
|
+
// Parse Frontmatter
|
|
23
|
+
const frontmatter = (0, gray_matter_1.default)(raw);
|
|
24
|
+
// Parse Codeblocks
|
|
25
|
+
const walker = ast.walker();
|
|
26
|
+
const blocks = [];
|
|
27
|
+
let e;
|
|
28
|
+
while ((e = walker.next())) {
|
|
29
|
+
const node = e.node;
|
|
30
|
+
if (!isRCodeBlock(node)) {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
blocks.push({
|
|
34
|
+
code: node.literal,
|
|
35
|
+
options: parseCodeBlockOptions(node.info, node.literal),
|
|
36
|
+
startpos: { line: node.sourcepos[0][0] + 1, col: 0 }
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
request: 'text',
|
|
41
|
+
content: restoreBlocksWithoutMd(blocks, countNewlines(raw)),
|
|
42
|
+
info: {
|
|
43
|
+
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
44
|
+
blocks: blocks.map(({ startpos, ...block }) => block),
|
|
45
|
+
options: frontmatter.data,
|
|
46
|
+
type: 'Rmd'
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
const RTagRegex = /{[rR](?:[\s,][^}]*)?}/;
|
|
52
|
+
function isRCodeBlock(node) {
|
|
53
|
+
return node.type === 'code_block' && node.literal !== null && node.info !== null && RTagRegex.test(node.info);
|
|
54
|
+
}
|
|
55
|
+
const LineRegex = /\r\n|\r|\n/;
|
|
56
|
+
function countNewlines(str) {
|
|
57
|
+
return str.split(LineRegex).length - 1;
|
|
58
|
+
}
|
|
59
|
+
function restoreBlocksWithoutMd(blocks, totalLines) {
|
|
60
|
+
let line = 1;
|
|
61
|
+
let output = '';
|
|
62
|
+
const goToLine = (n) => {
|
|
63
|
+
const diff = n - line;
|
|
64
|
+
(0, assert_1.guard)(diff >= 0);
|
|
65
|
+
line += diff;
|
|
66
|
+
output += '\n'.repeat(diff);
|
|
67
|
+
};
|
|
68
|
+
for (const block of blocks) {
|
|
69
|
+
goToLine(block.startpos.line);
|
|
70
|
+
output += block.code;
|
|
71
|
+
line += countNewlines(block.code);
|
|
72
|
+
}
|
|
73
|
+
// Add remainder of file
|
|
74
|
+
goToLine(totalLines + 1);
|
|
75
|
+
return output;
|
|
76
|
+
}
|
|
77
|
+
function parseCodeBlockOptions(header, content) {
|
|
78
|
+
let opts = header.length === 3 // '{r}' => header.length=3 (no options in header)
|
|
79
|
+
? ''
|
|
80
|
+
: header.substring(3, header.length - 1).trim();
|
|
81
|
+
const lines = content.split('\n');
|
|
82
|
+
for (const line of lines) {
|
|
83
|
+
if (!line.trim().startsWith('#|')) {
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
const opt = line.substring(3);
|
|
87
|
+
opts += opts.length === 0 ? opt : `, ${opt}`;
|
|
88
|
+
}
|
|
89
|
+
return opts;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=rmd-adapter.js.map
|
package/util/version.js
CHANGED
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.flowrVersion = flowrVersion;
|
|
4
4
|
const semver_1 = require("semver");
|
|
5
5
|
// this is automatically replaced with the current version by release-it
|
|
6
|
-
const version = '2.
|
|
6
|
+
const version = '2.5.0';
|
|
7
7
|
function flowrVersion() {
|
|
8
8
|
return new semver_1.SemVer(version);
|
|
9
9
|
}
|