@hpcc-js/observablehq-compiler 1.5.0 → 1.5.1
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/LICENSE +43 -43
- package/README.md +105 -105
- package/bin/ojscc.mjs +73 -73
- package/dist/index.esm.js +49 -49
- package/dist/index.esm.js.map +1 -1
- package/dist/index.esm.min.js.map +1 -1
- package/dist/index.js +49 -49
- package/dist/index.js.map +1 -1
- package/dist/index.min.js.map +1 -1
- package/package.json +3 -3
- package/src/__package__.ts +3 -3
- package/src/__tests__/File Attachments.ts +894 -894
- package/src/__tests__/Introduction to Imports.ts +748 -748
- package/src/__tests__/Observable TimeChart.ts +771 -771
- package/src/__tests__/index.ts +13 -13
- package/src/__tests__/m1.mjs +3 -3
- package/src/__tests__/node.ts +199 -199
- package/src/compiler.md +234 -234
- package/src/compiler.ts +311 -311
- package/src/cst.ts +178 -178
- package/src/index.css +459 -459
- package/src/index.ts +6 -6
- package/src/util.md +113 -113
- package/src/util.ts +175 -175
- package/src/writer.ts +80 -80
package/src/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export type { ohq } from "@hpcc-js/observable-shim";
|
|
2
|
-
|
|
3
|
-
export * from "./compiler";
|
|
4
|
-
export { ojs2notebook, omd2notebook, download } from "./util";
|
|
5
|
-
|
|
6
|
-
import "../src/index.css";
|
|
1
|
+
export type { ohq } from "@hpcc-js/observable-shim";
|
|
2
|
+
|
|
3
|
+
export * from "./compiler";
|
|
4
|
+
export { ojs2notebook, omd2notebook, download } from "./util";
|
|
5
|
+
|
|
6
|
+
import "../src/index.css";
|
package/src/util.md
CHANGED
|
@@ -1,113 +1,113 @@
|
|
|
1
|
-
# Utilities
|
|
2
|
-
|
|
3
|
-
Various utilities and helpers.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
<a name="download" href="#download">#</a> **download**(_impUrl_) => Promise\<ohq.Module\> · [<>](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/observablehq/compiler/src/util.ts "Source")
|
|
8
|
-
|
|
9
|
-
* _impUrl_: Full url to Obaservable HQ notebook as a string.
|
|
10
|
-
* _returns_: Returns a Promise of a notebook (a JSON object).
|
|
11
|
-
|
|
12
|
-
Downloads a notebook directly from [Observable HQ](https://observablehq.com/) as a JSON object. The following example downloads the [@observablehq/plot](https://observablehq.com/@observablehq/plot) notebook as JSON:
|
|
13
|
-
|
|
14
|
-
<ClientOnly>
|
|
15
|
-
<hpcc-vitepress style="width:100%;height:600px">
|
|
16
|
-
<hpcc-codemirror id="placeholder" mode="json" theme="dark" style="width:100%;height:100%">
|
|
17
|
-
</hpcc-codemirror>
|
|
18
|
-
<script type="module">
|
|
19
|
-
import "@hpcc-js/wc-editor";
|
|
20
|
-
import { download } from "@hpcc-js/observablehq-compiler";
|
|
21
|
-
|
|
22
|
-
const notebook = await download("https://observablehq.com/@observablehq/plot");
|
|
23
|
-
const placeholder = document.getElementById("placeholder");
|
|
24
|
-
placeholder.text = JSON.stringify(notebook, undefined, 4);
|
|
25
|
-
</script>
|
|
26
|
-
</hpcc-vitepress>
|
|
27
|
-
</ClientOnly>
|
|
28
|
-
|
|
29
|
-
---
|
|
30
|
-
|
|
31
|
-
<a name="ojs2notebook" href="#ojs2notebook">#</a> **ojs2notebook**(_ojs_) => ohq.Module · [<>](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/observablehq/compiler/src/util.ts "Source")
|
|
32
|
-
|
|
33
|
-
* _ojs_: String containing Observable JavaScript.
|
|
34
|
-
* _returns_: Returns the notebook as a JSON object.
|
|
35
|
-
|
|
36
|
-
Transforms Observable JavaScript to a JSON notebook.
|
|
37
|
-
|
|
38
|
-
<ClientOnly>
|
|
39
|
-
<hpcc-vitepress style="width:100%;height:600px">
|
|
40
|
-
<hpcc-codemirror id="placeholder" mode="json" theme="dark" style="width:100%;height:100%">
|
|
41
|
-
</hpcc-codemirror>
|
|
42
|
-
<script type="module">
|
|
43
|
-
import "@hpcc-js/wc-editor";
|
|
44
|
-
import { ojs2notebook } from "@hpcc-js/observablehq-compiler";
|
|
45
|
-
|
|
46
|
-
const notebook = ojs2notebook(`
|
|
47
|
-
md\`# Simple Notebook\`
|
|
48
|
-
a = 1
|
|
49
|
-
b = 2
|
|
50
|
-
c = a + b
|
|
51
|
-
d = {
|
|
52
|
-
yield 1;
|
|
53
|
-
yield 2;
|
|
54
|
-
yield 3;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
viewof e = {
|
|
58
|
-
let output = {};
|
|
59
|
-
let listeners = [];
|
|
60
|
-
output.value = 10;
|
|
61
|
-
output.addEventListener = (listener) => listeners.push(listener);;
|
|
62
|
-
output.removeEventListener = (listener) => {
|
|
63
|
-
listeners = listeners.filter(l => l !== listener);
|
|
64
|
-
};
|
|
65
|
-
return output;
|
|
66
|
-
}
|
|
67
|
-
`);
|
|
68
|
-
const placeholder = document.getElementById("placeholder");
|
|
69
|
-
placeholder.text = JSON.stringify(notebook, undefined, 4);
|
|
70
|
-
</script>
|
|
71
|
-
</hpcc-vitepress>
|
|
72
|
-
</ClientOnly>
|
|
73
|
-
|
|
74
|
-
<a name="omd2notebook" href="#omd2notebook">#</a> **omd2notebook**(_omd_) => ohq.Module · [<>](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/observablehq/compiler/src/util.ts "Source")
|
|
75
|
-
|
|
76
|
-
* _omd_: String containing Observable Markdown.
|
|
77
|
-
* _returns_: Returns the notebook as a JSON object.
|
|
78
|
-
|
|
79
|
-
Transforms Observable Markdown to a JSON notebook.
|
|
80
|
-
|
|
81
|
-
<ClientOnly>
|
|
82
|
-
<hpcc-vitepress style="width:100%;height:600px">
|
|
83
|
-
<hpcc-codemirror id="placeholder" mode="json" theme="dark" style="width:100%;height:100%">
|
|
84
|
-
</hpcc-codemirror>
|
|
85
|
-
<script type="module">
|
|
86
|
-
import "@hpcc-js/wc-editor";
|
|
87
|
-
import { omd2notebook } from "@hpcc-js/observablehq-compiler";
|
|
88
|
-
|
|
89
|
-
const notebook = omd2notebook(`\
|
|
90
|
-
# Simple Notebook
|
|
91
|
-
|
|
92
|
-
* Set A
|
|
93
|
-
\`\`\`
|
|
94
|
-
a = 1
|
|
95
|
-
\`\`\`
|
|
96
|
-
|
|
97
|
-
* Set B
|
|
98
|
-
\`\`\`
|
|
99
|
-
b = 2
|
|
100
|
-
\`\`\`
|
|
101
|
-
|
|
102
|
-
* Calculate c
|
|
103
|
-
|
|
104
|
-
\`\`\`
|
|
105
|
-
c = a + b
|
|
106
|
-
\`\`\`
|
|
107
|
-
`);
|
|
108
|
-
const placeholder = document.getElementById("placeholder");
|
|
109
|
-
placeholder.text = JSON.stringify(notebook, undefined, 4);
|
|
110
|
-
</script>
|
|
111
|
-
</hpcc-vitepress>
|
|
112
|
-
</ClientOnly>
|
|
113
|
-
|
|
1
|
+
# Utilities
|
|
2
|
+
|
|
3
|
+
Various utilities and helpers.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<a name="download" href="#download">#</a> **download**(_impUrl_) => Promise\<ohq.Module\> · [<>](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/observablehq/compiler/src/util.ts "Source")
|
|
8
|
+
|
|
9
|
+
* _impUrl_: Full url to Obaservable HQ notebook as a string.
|
|
10
|
+
* _returns_: Returns a Promise of a notebook (a JSON object).
|
|
11
|
+
|
|
12
|
+
Downloads a notebook directly from [Observable HQ](https://observablehq.com/) as a JSON object. The following example downloads the [@observablehq/plot](https://observablehq.com/@observablehq/plot) notebook as JSON:
|
|
13
|
+
|
|
14
|
+
<ClientOnly>
|
|
15
|
+
<hpcc-vitepress style="width:100%;height:600px">
|
|
16
|
+
<hpcc-codemirror id="placeholder" mode="json" theme="dark" style="width:100%;height:100%">
|
|
17
|
+
</hpcc-codemirror>
|
|
18
|
+
<script type="module">
|
|
19
|
+
import "@hpcc-js/wc-editor";
|
|
20
|
+
import { download } from "@hpcc-js/observablehq-compiler";
|
|
21
|
+
|
|
22
|
+
const notebook = await download("https://observablehq.com/@observablehq/plot");
|
|
23
|
+
const placeholder = document.getElementById("placeholder");
|
|
24
|
+
placeholder.text = JSON.stringify(notebook, undefined, 4);
|
|
25
|
+
</script>
|
|
26
|
+
</hpcc-vitepress>
|
|
27
|
+
</ClientOnly>
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
<a name="ojs2notebook" href="#ojs2notebook">#</a> **ojs2notebook**(_ojs_) => ohq.Module · [<>](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/observablehq/compiler/src/util.ts "Source")
|
|
32
|
+
|
|
33
|
+
* _ojs_: String containing Observable JavaScript.
|
|
34
|
+
* _returns_: Returns the notebook as a JSON object.
|
|
35
|
+
|
|
36
|
+
Transforms Observable JavaScript to a JSON notebook.
|
|
37
|
+
|
|
38
|
+
<ClientOnly>
|
|
39
|
+
<hpcc-vitepress style="width:100%;height:600px">
|
|
40
|
+
<hpcc-codemirror id="placeholder" mode="json" theme="dark" style="width:100%;height:100%">
|
|
41
|
+
</hpcc-codemirror>
|
|
42
|
+
<script type="module">
|
|
43
|
+
import "@hpcc-js/wc-editor";
|
|
44
|
+
import { ojs2notebook } from "@hpcc-js/observablehq-compiler";
|
|
45
|
+
|
|
46
|
+
const notebook = ojs2notebook(`
|
|
47
|
+
md\`# Simple Notebook\`
|
|
48
|
+
a = 1
|
|
49
|
+
b = 2
|
|
50
|
+
c = a + b
|
|
51
|
+
d = {
|
|
52
|
+
yield 1;
|
|
53
|
+
yield 2;
|
|
54
|
+
yield 3;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
viewof e = {
|
|
58
|
+
let output = {};
|
|
59
|
+
let listeners = [];
|
|
60
|
+
output.value = 10;
|
|
61
|
+
output.addEventListener = (listener) => listeners.push(listener);;
|
|
62
|
+
output.removeEventListener = (listener) => {
|
|
63
|
+
listeners = listeners.filter(l => l !== listener);
|
|
64
|
+
};
|
|
65
|
+
return output;
|
|
66
|
+
}
|
|
67
|
+
`);
|
|
68
|
+
const placeholder = document.getElementById("placeholder");
|
|
69
|
+
placeholder.text = JSON.stringify(notebook, undefined, 4);
|
|
70
|
+
</script>
|
|
71
|
+
</hpcc-vitepress>
|
|
72
|
+
</ClientOnly>
|
|
73
|
+
|
|
74
|
+
<a name="omd2notebook" href="#omd2notebook">#</a> **omd2notebook**(_omd_) => ohq.Module · [<>](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/observablehq/compiler/src/util.ts "Source")
|
|
75
|
+
|
|
76
|
+
* _omd_: String containing Observable Markdown.
|
|
77
|
+
* _returns_: Returns the notebook as a JSON object.
|
|
78
|
+
|
|
79
|
+
Transforms Observable Markdown to a JSON notebook.
|
|
80
|
+
|
|
81
|
+
<ClientOnly>
|
|
82
|
+
<hpcc-vitepress style="width:100%;height:600px">
|
|
83
|
+
<hpcc-codemirror id="placeholder" mode="json" theme="dark" style="width:100%;height:100%">
|
|
84
|
+
</hpcc-codemirror>
|
|
85
|
+
<script type="module">
|
|
86
|
+
import "@hpcc-js/wc-editor";
|
|
87
|
+
import { omd2notebook } from "@hpcc-js/observablehq-compiler";
|
|
88
|
+
|
|
89
|
+
const notebook = omd2notebook(`\
|
|
90
|
+
# Simple Notebook
|
|
91
|
+
|
|
92
|
+
* Set A
|
|
93
|
+
\`\`\`
|
|
94
|
+
a = 1
|
|
95
|
+
\`\`\`
|
|
96
|
+
|
|
97
|
+
* Set B
|
|
98
|
+
\`\`\`
|
|
99
|
+
b = 2
|
|
100
|
+
\`\`\`
|
|
101
|
+
|
|
102
|
+
* Calculate c
|
|
103
|
+
|
|
104
|
+
\`\`\`
|
|
105
|
+
c = a + b
|
|
106
|
+
\`\`\`
|
|
107
|
+
`);
|
|
108
|
+
const placeholder = document.getElementById("placeholder");
|
|
109
|
+
placeholder.text = JSON.stringify(notebook, undefined, 4);
|
|
110
|
+
</script>
|
|
111
|
+
</hpcc-vitepress>
|
|
112
|
+
</ClientOnly>
|
|
113
|
+
|
package/src/util.ts
CHANGED
|
@@ -1,176 +1,176 @@
|
|
|
1
|
-
import type { ohq } from "@hpcc-js/observable-shim";
|
|
2
|
-
import { parseCell, splitModule } from "@hpcc-js/observable-shim";
|
|
3
|
-
|
|
4
|
-
const FuncTypes = {
|
|
5
|
-
functionType: Object.getPrototypeOf(function () { }).constructor,
|
|
6
|
-
asyncFunctionType: Object.getPrototypeOf(async function () { }).constructor,
|
|
7
|
-
generatorFunctionType: Object.getPrototypeOf(function* () { }).constructor,
|
|
8
|
-
asyncGeneratorFunctionType: Object.getPrototypeOf(async function* () { }).constructor
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
function funcType(async: boolean = false, generator: boolean = false) {
|
|
12
|
-
if (!async && !generator) return FuncTypes.functionType;
|
|
13
|
-
if (async && !generator) return FuncTypes.asyncFunctionType;
|
|
14
|
-
if (!async && generator) return FuncTypes.generatorFunctionType;
|
|
15
|
-
return FuncTypes.asyncGeneratorFunctionType;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
interface Ref {
|
|
19
|
-
start: number,
|
|
20
|
-
end: number,
|
|
21
|
-
newText: string
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface Refs {
|
|
25
|
-
inputs: string[];
|
|
26
|
-
args: string[];
|
|
27
|
-
patches: Ref[];
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function createFunction(refs: Refs, async = false, generator = false, blockStatement = false, body?: string) {
|
|
31
|
-
if (body === undefined) {
|
|
32
|
-
return undefined;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
refs.patches.sort((l, r) => r.start - l.start);
|
|
36
|
-
refs.patches.forEach(r => {
|
|
37
|
-
body = body!.substring(0, r.start) + r.newText + body!.substring(r.end);
|
|
38
|
-
});
|
|
39
|
-
return new (funcType(async, generator))(...refs.args, blockStatement ?
|
|
40
|
-
body.substring(1, body.length - 1).trim() :
|
|
41
|
-
`return (\n${body}\n);`);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function join(baseURL, relativeURL) {
|
|
45
|
-
return relativeURL
|
|
46
|
-
? baseURL.replace(/\/+$/, "") + "/" + relativeURL.replace(/^\/+/, "")
|
|
47
|
-
: baseURL;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export const isRelativePath = (path: string) => path[0] === ".";
|
|
51
|
-
export const fixRelativeUrl = (path: string, basePath: string) => {
|
|
52
|
-
if (isRelativePath(path)) {
|
|
53
|
-
return join(basePath, path);
|
|
54
|
-
}
|
|
55
|
-
return path;
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
// Hide "import" from bundlers as they have a habit of replacing "import" with "require"
|
|
59
|
-
export async function obfuscatedImport(url: string) {
|
|
60
|
-
return new FuncTypes.asyncFunctionType("url", "return import(url)")(url);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
interface ParsedOJS {
|
|
64
|
-
ojs: string;
|
|
65
|
-
offset: number;
|
|
66
|
-
inlineMD: boolean;
|
|
67
|
-
cell: any;
|
|
68
|
-
error: any;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export function encodeBacktick(str: string) {
|
|
72
|
-
return str
|
|
73
|
-
.split("`").join("\\`")
|
|
74
|
-
;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function createParsedOJS(ojs: string, offset: number, inlineMD: boolean): ParsedOJS {
|
|
78
|
-
let cell;
|
|
79
|
-
let error;
|
|
80
|
-
try {
|
|
81
|
-
cell = parseCell(ojs);
|
|
82
|
-
} catch (e) {
|
|
83
|
-
error = e;
|
|
84
|
-
}
|
|
85
|
-
return {
|
|
86
|
-
ojs,
|
|
87
|
-
offset,
|
|
88
|
-
inlineMD,
|
|
89
|
-
cell,
|
|
90
|
-
error
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function splitOmd(_: string): ParsedOJS[] {
|
|
95
|
-
const retVal: ParsedOJS[] = [];
|
|
96
|
-
// Load Markdown ---
|
|
97
|
-
const re = /(```(?:\s|\S)[\s\S]*?```)/g;
|
|
98
|
-
let prevOffset = 0;
|
|
99
|
-
let match = re.exec(_);
|
|
100
|
-
while (match !== null) {
|
|
101
|
-
if (match.index > prevOffset) {
|
|
102
|
-
retVal.push(createParsedOJS(_.substring(prevOffset, match.index), prevOffset, true));
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const outer = match[0];
|
|
106
|
-
if (outer.indexOf("``` ") === 0 || outer.indexOf("```\n") === 0 || outer.indexOf("```\r\n") === 0) {
|
|
107
|
-
const prefixLen = 3;
|
|
108
|
-
const inner = outer.substring(prefixLen, outer.length - prefixLen);
|
|
109
|
-
retVal.push(createParsedOJS(inner, match.index + prefixLen, false));
|
|
110
|
-
} else {
|
|
111
|
-
retVal.push(createParsedOJS(outer, match.index, true));
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
prevOffset = match.index + match[0].length;
|
|
115
|
-
match = re.exec(_);
|
|
116
|
-
}
|
|
117
|
-
if (_.length > prevOffset) {
|
|
118
|
-
retVal.push(createParsedOJS(_.substring(prevOffset, _.length), prevOffset, true));
|
|
119
|
-
}
|
|
120
|
-
return retVal;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export function notebook2ojs(_: string): ParsedOJS[] {
|
|
124
|
-
const parsed: ohq.Notebook = JSON.parse(_);
|
|
125
|
-
return parsed.nodes.map(node => createParsedOJS(node.value, 0, node.mode === "md"));
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export function ojs2notebook(ojs: string): ohq.Notebook {
|
|
129
|
-
const cells = splitModule(ojs);
|
|
130
|
-
return {
|
|
131
|
-
files: [],
|
|
132
|
-
nodes: cells.map((cell, idx) => {
|
|
133
|
-
return {
|
|
134
|
-
id: idx,
|
|
135
|
-
mode: "js",
|
|
136
|
-
value: cell.text,
|
|
137
|
-
start: cell.start,
|
|
138
|
-
end: cell.end
|
|
139
|
-
};
|
|
140
|
-
})
|
|
141
|
-
} as ohq.Notebook;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
export function omd2notebook(omd: string): ohq.Notebook {
|
|
145
|
-
const cells = splitOmd(omd);
|
|
146
|
-
return {
|
|
147
|
-
files: [],
|
|
148
|
-
nodes: cells.map((cell, idx) => {
|
|
149
|
-
return {
|
|
150
|
-
id: idx,
|
|
151
|
-
mode: cell.inlineMD ? "md" : "js",
|
|
152
|
-
value: cell.ojs,
|
|
153
|
-
start: cell.offset,
|
|
154
|
-
end: cell.offset + cell.ojs.length
|
|
155
|
-
};
|
|
156
|
-
})
|
|
157
|
-
} as ohq.Notebook;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
export function fetchEx(url: string, proxyPrefix = "https://api.codetabs.com/v1/proxy/?quest=", proxyPostfix = "") {
|
|
161
|
-
const matches = url.match(/^(?:https?:\/\/)?(?:[^@\n]+@)?(?:www\.)?([^:\/\n?]+)/img);
|
|
162
|
-
return fetch(url, { headers: { origin: matches[0], referer: url } }).then(response => {
|
|
163
|
-
if (response.ok) return response;
|
|
164
|
-
throw new Error("CORS?");
|
|
165
|
-
}).catch(e => {
|
|
166
|
-
url = `${proxyPrefix}${url}${proxyPostfix}`;
|
|
167
|
-
return fetch(url, { headers: { origin: matches[0], referer: url } });
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
export function download(impUrl: string, proxyPrefix?: string, proxyPostfix?: string): Promise<ohq.Notebook> {
|
|
172
|
-
const isShared = impUrl.indexOf("https://observablehq.com/d") === 0;
|
|
173
|
-
return fetchEx(impUrl.replace(`https://observablehq.com/${isShared ? "d/" : ""}`, "https://api.observablehq.com/document/"), proxyPrefix, proxyPostfix)
|
|
174
|
-
.then(r => r.json())
|
|
175
|
-
;
|
|
1
|
+
import type { ohq } from "@hpcc-js/observable-shim";
|
|
2
|
+
import { parseCell, splitModule } from "@hpcc-js/observable-shim";
|
|
3
|
+
|
|
4
|
+
const FuncTypes = {
|
|
5
|
+
functionType: Object.getPrototypeOf(function () { }).constructor,
|
|
6
|
+
asyncFunctionType: Object.getPrototypeOf(async function () { }).constructor,
|
|
7
|
+
generatorFunctionType: Object.getPrototypeOf(function* () { }).constructor,
|
|
8
|
+
asyncGeneratorFunctionType: Object.getPrototypeOf(async function* () { }).constructor
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
function funcType(async: boolean = false, generator: boolean = false) {
|
|
12
|
+
if (!async && !generator) return FuncTypes.functionType;
|
|
13
|
+
if (async && !generator) return FuncTypes.asyncFunctionType;
|
|
14
|
+
if (!async && generator) return FuncTypes.generatorFunctionType;
|
|
15
|
+
return FuncTypes.asyncGeneratorFunctionType;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface Ref {
|
|
19
|
+
start: number,
|
|
20
|
+
end: number,
|
|
21
|
+
newText: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface Refs {
|
|
25
|
+
inputs: string[];
|
|
26
|
+
args: string[];
|
|
27
|
+
patches: Ref[];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function createFunction(refs: Refs, async = false, generator = false, blockStatement = false, body?: string) {
|
|
31
|
+
if (body === undefined) {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
refs.patches.sort((l, r) => r.start - l.start);
|
|
36
|
+
refs.patches.forEach(r => {
|
|
37
|
+
body = body!.substring(0, r.start) + r.newText + body!.substring(r.end);
|
|
38
|
+
});
|
|
39
|
+
return new (funcType(async, generator))(...refs.args, blockStatement ?
|
|
40
|
+
body.substring(1, body.length - 1).trim() :
|
|
41
|
+
`return (\n${body}\n);`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function join(baseURL, relativeURL) {
|
|
45
|
+
return relativeURL
|
|
46
|
+
? baseURL.replace(/\/+$/, "") + "/" + relativeURL.replace(/^\/+/, "")
|
|
47
|
+
: baseURL;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const isRelativePath = (path: string) => path[0] === ".";
|
|
51
|
+
export const fixRelativeUrl = (path: string, basePath: string) => {
|
|
52
|
+
if (isRelativePath(path)) {
|
|
53
|
+
return join(basePath, path);
|
|
54
|
+
}
|
|
55
|
+
return path;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// Hide "import" from bundlers as they have a habit of replacing "import" with "require"
|
|
59
|
+
export async function obfuscatedImport(url: string) {
|
|
60
|
+
return new FuncTypes.asyncFunctionType("url", "return import(url)")(url);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
interface ParsedOJS {
|
|
64
|
+
ojs: string;
|
|
65
|
+
offset: number;
|
|
66
|
+
inlineMD: boolean;
|
|
67
|
+
cell: any;
|
|
68
|
+
error: any;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function encodeBacktick(str: string) {
|
|
72
|
+
return str
|
|
73
|
+
.split("`").join("\\`")
|
|
74
|
+
;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function createParsedOJS(ojs: string, offset: number, inlineMD: boolean): ParsedOJS {
|
|
78
|
+
let cell;
|
|
79
|
+
let error;
|
|
80
|
+
try {
|
|
81
|
+
cell = parseCell(ojs);
|
|
82
|
+
} catch (e) {
|
|
83
|
+
error = e;
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
ojs,
|
|
87
|
+
offset,
|
|
88
|
+
inlineMD,
|
|
89
|
+
cell,
|
|
90
|
+
error
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function splitOmd(_: string): ParsedOJS[] {
|
|
95
|
+
const retVal: ParsedOJS[] = [];
|
|
96
|
+
// Load Markdown ---
|
|
97
|
+
const re = /(```(?:\s|\S)[\s\S]*?```)/g;
|
|
98
|
+
let prevOffset = 0;
|
|
99
|
+
let match = re.exec(_);
|
|
100
|
+
while (match !== null) {
|
|
101
|
+
if (match.index > prevOffset) {
|
|
102
|
+
retVal.push(createParsedOJS(_.substring(prevOffset, match.index), prevOffset, true));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const outer = match[0];
|
|
106
|
+
if (outer.indexOf("``` ") === 0 || outer.indexOf("```\n") === 0 || outer.indexOf("```\r\n") === 0) {
|
|
107
|
+
const prefixLen = 3;
|
|
108
|
+
const inner = outer.substring(prefixLen, outer.length - prefixLen);
|
|
109
|
+
retVal.push(createParsedOJS(inner, match.index + prefixLen, false));
|
|
110
|
+
} else {
|
|
111
|
+
retVal.push(createParsedOJS(outer, match.index, true));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
prevOffset = match.index + match[0].length;
|
|
115
|
+
match = re.exec(_);
|
|
116
|
+
}
|
|
117
|
+
if (_.length > prevOffset) {
|
|
118
|
+
retVal.push(createParsedOJS(_.substring(prevOffset, _.length), prevOffset, true));
|
|
119
|
+
}
|
|
120
|
+
return retVal;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function notebook2ojs(_: string): ParsedOJS[] {
|
|
124
|
+
const parsed: ohq.Notebook = JSON.parse(_);
|
|
125
|
+
return parsed.nodes.map(node => createParsedOJS(node.value, 0, node.mode === "md"));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function ojs2notebook(ojs: string): ohq.Notebook {
|
|
129
|
+
const cells = splitModule(ojs);
|
|
130
|
+
return {
|
|
131
|
+
files: [],
|
|
132
|
+
nodes: cells.map((cell, idx) => {
|
|
133
|
+
return {
|
|
134
|
+
id: idx,
|
|
135
|
+
mode: "js",
|
|
136
|
+
value: cell.text,
|
|
137
|
+
start: cell.start,
|
|
138
|
+
end: cell.end
|
|
139
|
+
};
|
|
140
|
+
})
|
|
141
|
+
} as ohq.Notebook;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function omd2notebook(omd: string): ohq.Notebook {
|
|
145
|
+
const cells = splitOmd(omd);
|
|
146
|
+
return {
|
|
147
|
+
files: [],
|
|
148
|
+
nodes: cells.map((cell, idx) => {
|
|
149
|
+
return {
|
|
150
|
+
id: idx,
|
|
151
|
+
mode: cell.inlineMD ? "md" : "js",
|
|
152
|
+
value: cell.ojs,
|
|
153
|
+
start: cell.offset,
|
|
154
|
+
end: cell.offset + cell.ojs.length
|
|
155
|
+
};
|
|
156
|
+
})
|
|
157
|
+
} as ohq.Notebook;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function fetchEx(url: string, proxyPrefix = "https://api.codetabs.com/v1/proxy/?quest=", proxyPostfix = "") {
|
|
161
|
+
const matches = url.match(/^(?:https?:\/\/)?(?:[^@\n]+@)?(?:www\.)?([^:\/\n?]+)/img);
|
|
162
|
+
return fetch(url, { headers: { origin: matches[0], referer: url } }).then(response => {
|
|
163
|
+
if (response.ok) return response;
|
|
164
|
+
throw new Error("CORS?");
|
|
165
|
+
}).catch(e => {
|
|
166
|
+
url = `${proxyPrefix}${url}${proxyPostfix}`;
|
|
167
|
+
return fetch(url, { headers: { origin: matches[0], referer: url } });
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function download(impUrl: string, proxyPrefix?: string, proxyPostfix?: string): Promise<ohq.Notebook> {
|
|
172
|
+
const isShared = impUrl.indexOf("https://observablehq.com/d") === 0;
|
|
173
|
+
return fetchEx(impUrl.replace(`https://observablehq.com/${isShared ? "d/" : ""}`, "https://api.observablehq.com/document/"), proxyPrefix, proxyPostfix)
|
|
174
|
+
.then(r => r.json())
|
|
175
|
+
;
|
|
176
176
|
}
|