@hpcc-js/observablehq-compiler 3.7.6 → 3.7.8
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 +67 -67
- package/dist/index.js.map +1 -1
- package/dist/node/index.cjs.map +1 -1
- package/dist/node/index.js.map +1 -1
- package/dist/runtime.js.map +1 -1
- package/package.json +3 -3
- package/src/__package__.ts +3 -3
- package/src/compiler.md +234 -234
- package/src/compiler.ts +340 -340
- package/src/cst.ts +181 -181
- package/src/index.node.ts +7 -7
- package/src/index.ts +6 -6
- package/src/kit/compiler.ts +60 -60
- package/src/kit/index.ts +2 -2
- package/src/kit/runtime.ts +85 -85
- package/src/kit/util.ts +157 -157
- package/src/observable-shim.ts +2 -2
- package/src/parse.ts +136 -136
- package/src/types.ts +179 -179
- package/src/util.md +113 -113
- package/src/util.ts +155 -155
- package/src/writer.ts +83 -83
package/src/kit/runtime.ts
CHANGED
|
@@ -1,86 +1,86 @@
|
|
|
1
|
-
import { type Definition, type DefineState, NotebookRuntime as NotebookRuntimeBase } from "@observablehq/notebook-kit/runtime";
|
|
2
|
-
|
|
3
|
-
import "@observablehq/notebook-kit/index.css";
|
|
4
|
-
import "@observablehq/notebook-kit/theme-air.css";
|
|
5
|
-
|
|
6
|
-
export { Definition, DefineState };
|
|
7
|
-
|
|
8
|
-
export class NotebookRuntime extends NotebookRuntimeBase {
|
|
9
|
-
|
|
10
|
-
stateById = new Map<number, DefineState>();
|
|
11
|
-
|
|
12
|
-
constructor() {
|
|
13
|
-
super();
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
has(cellId: number): boolean {
|
|
17
|
-
return this.stateById.has(cellId);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
async add(definition: Definition): Promise<HTMLDivElement> {
|
|
21
|
-
if (this.stateById.has(definition.id)) {
|
|
22
|
-
throw new Error(`Cell with id ${definition.id} already exists`);
|
|
23
|
-
}
|
|
24
|
-
const placeholderDiv = document.createElement("div");
|
|
25
|
-
placeholderDiv.className = "observablehq observablehq--cell";
|
|
26
|
-
const state = { root: placeholderDiv, expanded: [], variables: [] };
|
|
27
|
-
this.stateById.set(definition.id, state);
|
|
28
|
-
this.define(state, definition);
|
|
29
|
-
await this.runtime._computeNow();
|
|
30
|
-
return state.root;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async update(definition: Definition): Promise<HTMLDivElement> {
|
|
34
|
-
const state = this.stateById.get(definition.id);
|
|
35
|
-
if (!state) {
|
|
36
|
-
throw new Error(`Cell with id ${definition.id} does not exist`);
|
|
37
|
-
}
|
|
38
|
-
await this.clear(definition.id);
|
|
39
|
-
this.define(state, definition);
|
|
40
|
-
await this.runtime._computeNow();
|
|
41
|
-
return state.root;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async clear(cellId: number): Promise<void> {
|
|
45
|
-
const state = this.stateById.get(cellId);
|
|
46
|
-
if (!state) {
|
|
47
|
-
throw new Error(`Cell with id ${cellId} does not exist`);
|
|
48
|
-
}
|
|
49
|
-
[...state.variables].forEach(v => v.delete());
|
|
50
|
-
await this.runtime._computeNow();
|
|
51
|
-
state.variables = [];
|
|
52
|
-
state.expanded = [];
|
|
53
|
-
state.root.innerHTML = "";
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async remove(cellId: number): Promise<void> {
|
|
57
|
-
const state = this.stateById.get(cellId);
|
|
58
|
-
if (!state) {
|
|
59
|
-
throw new Error(`Cell with id ${cellId} does not exist`);
|
|
60
|
-
}
|
|
61
|
-
void this.clear(cellId);
|
|
62
|
-
this.stateById.delete(cellId);
|
|
63
|
-
state.root.remove();
|
|
64
|
-
await this.runtime._computeNow();
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
async removeAll(): Promise<void> {
|
|
68
|
-
const keys = [...this.stateById.keys()];
|
|
69
|
-
for (const key of keys) {
|
|
70
|
-
this.remove(key);
|
|
71
|
-
}
|
|
72
|
-
await this.runtime._computeNow();
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
async render(definitions: Definition[], target: HTMLElement) {
|
|
76
|
-
for (const definition of definitions) {
|
|
77
|
-
let observableDiv: HTMLDivElement;
|
|
78
|
-
if (this.stateById.has(definition.id)) {
|
|
79
|
-
observableDiv = await this.update(definition);
|
|
80
|
-
} else {
|
|
81
|
-
observableDiv = await this.add(definition);
|
|
82
|
-
}
|
|
83
|
-
target.appendChild(observableDiv);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
1
|
+
import { type Definition, type DefineState, NotebookRuntime as NotebookRuntimeBase } from "@observablehq/notebook-kit/runtime";
|
|
2
|
+
|
|
3
|
+
import "@observablehq/notebook-kit/index.css";
|
|
4
|
+
import "@observablehq/notebook-kit/theme-air.css";
|
|
5
|
+
|
|
6
|
+
export { Definition, DefineState };
|
|
7
|
+
|
|
8
|
+
export class NotebookRuntime extends NotebookRuntimeBase {
|
|
9
|
+
|
|
10
|
+
stateById = new Map<number, DefineState>();
|
|
11
|
+
|
|
12
|
+
constructor() {
|
|
13
|
+
super();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
has(cellId: number): boolean {
|
|
17
|
+
return this.stateById.has(cellId);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async add(definition: Definition): Promise<HTMLDivElement> {
|
|
21
|
+
if (this.stateById.has(definition.id)) {
|
|
22
|
+
throw new Error(`Cell with id ${definition.id} already exists`);
|
|
23
|
+
}
|
|
24
|
+
const placeholderDiv = document.createElement("div");
|
|
25
|
+
placeholderDiv.className = "observablehq observablehq--cell";
|
|
26
|
+
const state = { root: placeholderDiv, expanded: [], variables: [] };
|
|
27
|
+
this.stateById.set(definition.id, state);
|
|
28
|
+
this.define(state, definition);
|
|
29
|
+
await this.runtime._computeNow();
|
|
30
|
+
return state.root;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async update(definition: Definition): Promise<HTMLDivElement> {
|
|
34
|
+
const state = this.stateById.get(definition.id);
|
|
35
|
+
if (!state) {
|
|
36
|
+
throw new Error(`Cell with id ${definition.id} does not exist`);
|
|
37
|
+
}
|
|
38
|
+
await this.clear(definition.id);
|
|
39
|
+
this.define(state, definition);
|
|
40
|
+
await this.runtime._computeNow();
|
|
41
|
+
return state.root;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async clear(cellId: number): Promise<void> {
|
|
45
|
+
const state = this.stateById.get(cellId);
|
|
46
|
+
if (!state) {
|
|
47
|
+
throw new Error(`Cell with id ${cellId} does not exist`);
|
|
48
|
+
}
|
|
49
|
+
[...state.variables].forEach(v => v.delete());
|
|
50
|
+
await this.runtime._computeNow();
|
|
51
|
+
state.variables = [];
|
|
52
|
+
state.expanded = [];
|
|
53
|
+
state.root.innerHTML = "";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async remove(cellId: number): Promise<void> {
|
|
57
|
+
const state = this.stateById.get(cellId);
|
|
58
|
+
if (!state) {
|
|
59
|
+
throw new Error(`Cell with id ${cellId} does not exist`);
|
|
60
|
+
}
|
|
61
|
+
void this.clear(cellId);
|
|
62
|
+
this.stateById.delete(cellId);
|
|
63
|
+
state.root.remove();
|
|
64
|
+
await this.runtime._computeNow();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async removeAll(): Promise<void> {
|
|
68
|
+
const keys = [...this.stateById.keys()];
|
|
69
|
+
for (const key of keys) {
|
|
70
|
+
this.remove(key);
|
|
71
|
+
}
|
|
72
|
+
await this.runtime._computeNow();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async render(definitions: Definition[], target: HTMLElement) {
|
|
76
|
+
for (const definition of definitions) {
|
|
77
|
+
let observableDiv: HTMLDivElement;
|
|
78
|
+
if (this.stateById.has(definition.id)) {
|
|
79
|
+
observableDiv = await this.update(definition);
|
|
80
|
+
} else {
|
|
81
|
+
observableDiv = await this.add(definition);
|
|
82
|
+
}
|
|
83
|
+
target.appendChild(observableDiv);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
86
|
}
|
package/src/kit/util.ts
CHANGED
|
@@ -1,157 +1,157 @@
|
|
|
1
|
-
import { type Notebook, type Cell, parseJavaScript, serialize, deserialize, toNotebook, toCell } from "@observablehq/notebook-kit";
|
|
2
|
-
import { type Definition } from "@observablehq/notebook-kit/runtime";
|
|
3
|
-
|
|
4
|
-
export type { Notebook, Cell, Definition };
|
|
5
|
-
export { toNotebook, toCell };
|
|
6
|
-
|
|
7
|
-
// Shared function constructor utilities to avoid duplication between util modules.
|
|
8
|
-
|
|
9
|
-
export type RegularFunction = (...args: any[]) => any;
|
|
10
|
-
interface RegularFunctionConstructor {
|
|
11
|
-
new(...args: string[]): RegularFunction;
|
|
12
|
-
(...args: string[]): RegularFunction;
|
|
13
|
-
readonly prototype: RegularFunction;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export type AsyncFunction = (...args: any[]) => Promise<any>;
|
|
17
|
-
interface AsyncFunctionConstructor {
|
|
18
|
-
new(...args: string[]): AsyncFunction;
|
|
19
|
-
(...args: string[]): AsyncFunction;
|
|
20
|
-
readonly prototype: AsyncFunction;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export type GeneratorFunction = (...args: any[]) => Generator<any, any, any>;
|
|
24
|
-
interface GeneratorFunctionConstructor {
|
|
25
|
-
new(...args: string[]): GeneratorFunction;
|
|
26
|
-
(...args: string[]): GeneratorFunction;
|
|
27
|
-
readonly prototype: GeneratorFunction;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export type AsyncGeneratorFunction = (...args: any[]) => AsyncGenerator<any, any, any>;
|
|
31
|
-
interface AsyncGeneratorFunctionConstructor {
|
|
32
|
-
new(...args: string[]): AsyncGeneratorFunction;
|
|
33
|
-
(...args: string[]): AsyncGeneratorFunction;
|
|
34
|
-
readonly prototype: AsyncGeneratorFunction;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export type AnyFunction = RegularFunction | AsyncFunction | GeneratorFunction | AsyncGeneratorFunction;
|
|
38
|
-
|
|
39
|
-
export const FunctionConstructors: {
|
|
40
|
-
regular: RegularFunctionConstructor;
|
|
41
|
-
async: AsyncFunctionConstructor;
|
|
42
|
-
generator: GeneratorFunctionConstructor;
|
|
43
|
-
asyncGenerator: AsyncGeneratorFunctionConstructor;
|
|
44
|
-
} = {
|
|
45
|
-
regular: Object.getPrototypeOf(function () { }).constructor as RegularFunctionConstructor,
|
|
46
|
-
async: Object.getPrototypeOf(async function () { }).constructor as AsyncFunctionConstructor,
|
|
47
|
-
generator: Object.getPrototypeOf(function* () { }).constructor as GeneratorFunctionConstructor,
|
|
48
|
-
asyncGenerator: Object.getPrototypeOf(async function* () { }).constructor as AsyncGeneratorFunctionConstructor,
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
function funcType(async: boolean = false, generator: boolean = false) {
|
|
52
|
-
if (!async && !generator) return FunctionConstructors.regular;
|
|
53
|
-
if (async && !generator) return FunctionConstructors.async;
|
|
54
|
-
if (!async && generator) return FunctionConstructors.generator;
|
|
55
|
-
return FunctionConstructors.asyncGenerator;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
interface Ref {
|
|
59
|
-
start: number,
|
|
60
|
-
end: number,
|
|
61
|
-
newText: string
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export interface Refs {
|
|
65
|
-
inputs: string[];
|
|
66
|
-
args: string[];
|
|
67
|
-
patches: Ref[];
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export function createFunction(refs: Refs, async = false, generator = false, blockStatement = false, body?: string) {
|
|
71
|
-
if (body === undefined) {
|
|
72
|
-
return undefined;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
refs.patches.sort((l, r) => r.start - l.start);
|
|
76
|
-
refs.patches.forEach(r => {
|
|
77
|
-
body = body!.substring(0, r.start) + r.newText + body!.substring(r.end);
|
|
78
|
-
});
|
|
79
|
-
return new (funcType(async, generator))(...refs.args, blockStatement ?
|
|
80
|
-
body.substring(1, body.length - 1).trim() :
|
|
81
|
-
`return (\n${body}\n);`);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function join(baseURL: string, relativeURL: string) {
|
|
85
|
-
return relativeURL
|
|
86
|
-
? baseURL.replace(/\/+$/, "") + "/" + relativeURL.replace(/^\/+/, "")
|
|
87
|
-
: baseURL;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export const isRelativePath = (path: string) => path[0] === ".";
|
|
91
|
-
export const fixRelativeUrl = (path: string, basePath: string) => {
|
|
92
|
-
if (isRelativePath(path)) {
|
|
93
|
-
return join(basePath, path);
|
|
94
|
-
}
|
|
95
|
-
return path;
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
// Hide "import" from bundlers as they have a habit of replacing "import" with "require"
|
|
99
|
-
const obfuscatedImportFunction = new FunctionConstructors.async("url", "return import(url)");
|
|
100
|
-
export async function obfuscatedImport(url: string) {
|
|
101
|
-
return obfuscatedImportFunction(url);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function _constructFunction(body: any, bodyStr: string, name?: string) {
|
|
105
|
-
if (body.type !== "FunctionExpression" && body.type !== "FunctionDeclaration" && body.type !== "ArrowFunctionExpression") {
|
|
106
|
-
throw new Error(`Unsupported function type: ${body.type}`);
|
|
107
|
-
}
|
|
108
|
-
const func = body.async && body.generator ?
|
|
109
|
-
FunctionConstructors.asyncGenerator :
|
|
110
|
-
body.async ?
|
|
111
|
-
FunctionConstructors.async :
|
|
112
|
-
body.generator ?
|
|
113
|
-
FunctionConstructors.generator :
|
|
114
|
-
FunctionConstructors.regular;
|
|
115
|
-
|
|
116
|
-
const params = body.params?.map((param) => bodyStr.slice(param.start, param.end)).join(", ") ?? "";
|
|
117
|
-
const isBlock = body.body.type === "BlockStatement";
|
|
118
|
-
const { start, end } = body.body;
|
|
119
|
-
const inner = isBlock
|
|
120
|
-
? bodyStr.slice(start + 1, end - 1)
|
|
121
|
-
: `return ${bodyStr.slice(start, end)}`;
|
|
122
|
-
// If a name is provided, build a true named function expression for reliable naming.
|
|
123
|
-
if (name) {
|
|
124
|
-
// Sanitize to a valid identifier; fallback to underscored name when necessary.
|
|
125
|
-
const sanitize = (n: string): string => {
|
|
126
|
-
let s = n.replace(/[^A-Za-z0-9_$]/g, "_");
|
|
127
|
-
if (!/^[A-Za-z_$]/.test(s)) s = "_" + s;
|
|
128
|
-
return s;
|
|
129
|
-
};
|
|
130
|
-
const id = sanitize(name);
|
|
131
|
-
const asyncKw = body.async ? "async " : "";
|
|
132
|
-
const genMark = body.generator ? "*" : "";
|
|
133
|
-
const src = `return ${asyncKw}function${genMark} ${id}(${params}) {\n${inner}\n}`;
|
|
134
|
-
// Use a plain Function wrapper to evaluate the named function expression.
|
|
135
|
-
const builtNamed = (new Function(src)()) as AnyFunction;
|
|
136
|
-
// Expose the intended display name even if sanitization changed the identifier.
|
|
137
|
-
try { (builtNamed as any).displayName = name; } catch { /* noop */ }
|
|
138
|
-
return builtNamed;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const built = func(params, inner) as AnyFunction;
|
|
142
|
-
return built;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
export function constructFunction(bodyStr: string, name?: string) {
|
|
146
|
-
const { body } = parseJavaScript(bodyStr);
|
|
147
|
-
if (body.type === "Program") {
|
|
148
|
-
if (body.body.length !== 1) {
|
|
149
|
-
throw new Error(`Expected a single function, but found ${body.body.length} statements`);
|
|
150
|
-
}
|
|
151
|
-
return _constructFunction(body.body[0], bodyStr, name);
|
|
152
|
-
}
|
|
153
|
-
return _constructFunction(body, bodyStr, name);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
export const html2notebook = (html: string): Notebook => deserialize(html);
|
|
157
|
-
export const notebook2html = (notebook: Notebook): string => serialize(notebook);
|
|
1
|
+
import { type Notebook, type Cell, parseJavaScript, serialize, deserialize, toNotebook, toCell } from "@observablehq/notebook-kit";
|
|
2
|
+
import { type Definition } from "@observablehq/notebook-kit/runtime";
|
|
3
|
+
|
|
4
|
+
export type { Notebook, Cell, Definition };
|
|
5
|
+
export { toNotebook, toCell };
|
|
6
|
+
|
|
7
|
+
// Shared function constructor utilities to avoid duplication between util modules.
|
|
8
|
+
|
|
9
|
+
export type RegularFunction = (...args: any[]) => any;
|
|
10
|
+
interface RegularFunctionConstructor {
|
|
11
|
+
new(...args: string[]): RegularFunction;
|
|
12
|
+
(...args: string[]): RegularFunction;
|
|
13
|
+
readonly prototype: RegularFunction;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type AsyncFunction = (...args: any[]) => Promise<any>;
|
|
17
|
+
interface AsyncFunctionConstructor {
|
|
18
|
+
new(...args: string[]): AsyncFunction;
|
|
19
|
+
(...args: string[]): AsyncFunction;
|
|
20
|
+
readonly prototype: AsyncFunction;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type GeneratorFunction = (...args: any[]) => Generator<any, any, any>;
|
|
24
|
+
interface GeneratorFunctionConstructor {
|
|
25
|
+
new(...args: string[]): GeneratorFunction;
|
|
26
|
+
(...args: string[]): GeneratorFunction;
|
|
27
|
+
readonly prototype: GeneratorFunction;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type AsyncGeneratorFunction = (...args: any[]) => AsyncGenerator<any, any, any>;
|
|
31
|
+
interface AsyncGeneratorFunctionConstructor {
|
|
32
|
+
new(...args: string[]): AsyncGeneratorFunction;
|
|
33
|
+
(...args: string[]): AsyncGeneratorFunction;
|
|
34
|
+
readonly prototype: AsyncGeneratorFunction;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type AnyFunction = RegularFunction | AsyncFunction | GeneratorFunction | AsyncGeneratorFunction;
|
|
38
|
+
|
|
39
|
+
export const FunctionConstructors: {
|
|
40
|
+
regular: RegularFunctionConstructor;
|
|
41
|
+
async: AsyncFunctionConstructor;
|
|
42
|
+
generator: GeneratorFunctionConstructor;
|
|
43
|
+
asyncGenerator: AsyncGeneratorFunctionConstructor;
|
|
44
|
+
} = {
|
|
45
|
+
regular: Object.getPrototypeOf(function () { }).constructor as RegularFunctionConstructor,
|
|
46
|
+
async: Object.getPrototypeOf(async function () { }).constructor as AsyncFunctionConstructor,
|
|
47
|
+
generator: Object.getPrototypeOf(function* () { }).constructor as GeneratorFunctionConstructor,
|
|
48
|
+
asyncGenerator: Object.getPrototypeOf(async function* () { }).constructor as AsyncGeneratorFunctionConstructor,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
function funcType(async: boolean = false, generator: boolean = false) {
|
|
52
|
+
if (!async && !generator) return FunctionConstructors.regular;
|
|
53
|
+
if (async && !generator) return FunctionConstructors.async;
|
|
54
|
+
if (!async && generator) return FunctionConstructors.generator;
|
|
55
|
+
return FunctionConstructors.asyncGenerator;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface Ref {
|
|
59
|
+
start: number,
|
|
60
|
+
end: number,
|
|
61
|
+
newText: string
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface Refs {
|
|
65
|
+
inputs: string[];
|
|
66
|
+
args: string[];
|
|
67
|
+
patches: Ref[];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function createFunction(refs: Refs, async = false, generator = false, blockStatement = false, body?: string) {
|
|
71
|
+
if (body === undefined) {
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
refs.patches.sort((l, r) => r.start - l.start);
|
|
76
|
+
refs.patches.forEach(r => {
|
|
77
|
+
body = body!.substring(0, r.start) + r.newText + body!.substring(r.end);
|
|
78
|
+
});
|
|
79
|
+
return new (funcType(async, generator))(...refs.args, blockStatement ?
|
|
80
|
+
body.substring(1, body.length - 1).trim() :
|
|
81
|
+
`return (\n${body}\n);`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function join(baseURL: string, relativeURL: string) {
|
|
85
|
+
return relativeURL
|
|
86
|
+
? baseURL.replace(/\/+$/, "") + "/" + relativeURL.replace(/^\/+/, "")
|
|
87
|
+
: baseURL;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export const isRelativePath = (path: string) => path[0] === ".";
|
|
91
|
+
export const fixRelativeUrl = (path: string, basePath: string) => {
|
|
92
|
+
if (isRelativePath(path)) {
|
|
93
|
+
return join(basePath, path);
|
|
94
|
+
}
|
|
95
|
+
return path;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// Hide "import" from bundlers as they have a habit of replacing "import" with "require"
|
|
99
|
+
const obfuscatedImportFunction = new FunctionConstructors.async("url", "return import(url)");
|
|
100
|
+
export async function obfuscatedImport(url: string) {
|
|
101
|
+
return obfuscatedImportFunction(url);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function _constructFunction(body: any, bodyStr: string, name?: string) {
|
|
105
|
+
if (body.type !== "FunctionExpression" && body.type !== "FunctionDeclaration" && body.type !== "ArrowFunctionExpression") {
|
|
106
|
+
throw new Error(`Unsupported function type: ${body.type}`);
|
|
107
|
+
}
|
|
108
|
+
const func = body.async && body.generator ?
|
|
109
|
+
FunctionConstructors.asyncGenerator :
|
|
110
|
+
body.async ?
|
|
111
|
+
FunctionConstructors.async :
|
|
112
|
+
body.generator ?
|
|
113
|
+
FunctionConstructors.generator :
|
|
114
|
+
FunctionConstructors.regular;
|
|
115
|
+
|
|
116
|
+
const params = body.params?.map((param) => bodyStr.slice(param.start, param.end)).join(", ") ?? "";
|
|
117
|
+
const isBlock = body.body.type === "BlockStatement";
|
|
118
|
+
const { start, end } = body.body;
|
|
119
|
+
const inner = isBlock
|
|
120
|
+
? bodyStr.slice(start + 1, end - 1)
|
|
121
|
+
: `return ${bodyStr.slice(start, end)}`;
|
|
122
|
+
// If a name is provided, build a true named function expression for reliable naming.
|
|
123
|
+
if (name) {
|
|
124
|
+
// Sanitize to a valid identifier; fallback to underscored name when necessary.
|
|
125
|
+
const sanitize = (n: string): string => {
|
|
126
|
+
let s = n.replace(/[^A-Za-z0-9_$]/g, "_");
|
|
127
|
+
if (!/^[A-Za-z_$]/.test(s)) s = "_" + s;
|
|
128
|
+
return s;
|
|
129
|
+
};
|
|
130
|
+
const id = sanitize(name);
|
|
131
|
+
const asyncKw = body.async ? "async " : "";
|
|
132
|
+
const genMark = body.generator ? "*" : "";
|
|
133
|
+
const src = `return ${asyncKw}function${genMark} ${id}(${params}) {\n${inner}\n}`;
|
|
134
|
+
// Use a plain Function wrapper to evaluate the named function expression.
|
|
135
|
+
const builtNamed = (new Function(src)()) as AnyFunction;
|
|
136
|
+
// Expose the intended display name even if sanitization changed the identifier.
|
|
137
|
+
try { (builtNamed as any).displayName = name; } catch { /* noop */ }
|
|
138
|
+
return builtNamed;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const built = func(params, inner) as AnyFunction;
|
|
142
|
+
return built;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function constructFunction(bodyStr: string, name?: string) {
|
|
146
|
+
const { body } = parseJavaScript(bodyStr);
|
|
147
|
+
if (body.type === "Program") {
|
|
148
|
+
if (body.body.length !== 1) {
|
|
149
|
+
throw new Error(`Expected a single function, but found ${body.body.length} statements`);
|
|
150
|
+
}
|
|
151
|
+
return _constructFunction(body.body[0], bodyStr, name);
|
|
152
|
+
}
|
|
153
|
+
return _constructFunction(body, bodyStr, name);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export const html2notebook = (html: string): Notebook => deserialize(html);
|
|
157
|
+
export const notebook2html = (notebook: Notebook): string => serialize(notebook);
|
package/src/observable-shim.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export type { ohq } from "./types.ts";
|
|
2
|
-
export * from "./parse.ts";
|
|
1
|
+
export type { ohq } from "./types.ts";
|
|
2
|
+
export * from "./parse.ts";
|