duckfn-docs-kit 0.2.1 → 0.4.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/AGENTS.md +294 -224
- package/README.md +10 -6
- package/bin/sql-verify.mjs +12 -12
- package/dist/IconButton.d.ts +22 -0
- package/dist/codemirror.d.ts +29 -0
- package/dist/index.d.ts +28 -7
- package/dist/index.js +2 -2
- package/dist/mermaid/DfkMermaid.d.ts +7 -0
- package/dist/mermaid/config.d.ts +48 -0
- package/dist/mermaid/remark.d.ts +40 -0
- package/dist/mermaid/remark.js +32 -0
- package/dist/mermaid/render.d.ts +94 -0
- package/dist/mermaid/styles.d.ts +6 -0
- package/dist/mermaid/title.d.ts +23 -0
- package/dist/{register-DKLiYs-F.js → register-Dev_kc3Z.js} +921 -579
- package/dist/remark.d.ts +1 -1
- package/dist/sql/browserRunner.d.ts +41 -0
- package/dist/sql/browserRunner.js +186 -0
- package/dist/sql/client.js +1 -1
- package/dist/sql/harness.d.ts +59 -0
- package/dist/sql/harness.js +8328 -0
- package/dist/sql/remark.d.ts +6 -1
- package/dist/sql/renderers.d.ts +2 -0
- package/dist/sql/runtime.d.ts +20 -0
- package/dist/sql/verify.d.ts +4 -5
- package/dist/sql/verify.js +36 -49
- package/package.json +6 -2
- package/src/IconButton.ts +50 -0
- package/src/codemirror.ts +88 -0
- package/src/index.ts +31 -7
- package/src/mermaid/DfkMermaid.css +289 -0
- package/src/mermaid/DfkMermaid.ts +557 -0
- package/src/mermaid/config.ts +74 -0
- package/src/mermaid/remark.ts +98 -0
- package/src/mermaid/render.ts +178 -0
- package/src/mermaid/styles.ts +24 -0
- package/src/mermaid/title.ts +127 -0
- package/src/register.ts +3 -0
- package/src/remark.ts +1 -1
- package/src/sql/DfkSql.css +13 -10
- package/src/sql/DfkSql.ts +11 -46
- package/src/sql/browserRunner.ts +402 -0
- package/src/sql/harness.ts +134 -0
- package/src/sql/remark.ts +6 -1
- package/src/sql/renderers.ts +24 -3
- package/src/sql/runtime.ts +44 -0
- package/src/sql/sql.css +13 -11
- package/src/sql/verify.ts +23 -41
- package/dist/sql/editor.d.ts +0 -16
- package/dist/sql/nodeRunner.d.ts +0 -51
- package/dist/sql/nodeRunner.js +0 -115
- package/src/sql/editor.ts +0 -75
- package/src/sql/nodeRunner.ts +0 -298
package/src/sql/sql.css
CHANGED
|
@@ -237,11 +237,13 @@ dfk-sql:not(:defined) {
|
|
|
237
237
|
|
|
238
238
|
/* --- Icon buttons and their tooltips ------------------------------------- */
|
|
239
239
|
|
|
240
|
-
/* The
|
|
241
|
-
`
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
240
|
+
/* The class names are written by the shared `IconButton` widget
|
|
241
|
+
(`src/IconButton.ts`). The same rules (and the `[data-tip]` tooltip below) are
|
|
242
|
+
declared in `DfkSql.css` and `DfkMermaid.css`, because the buttons are split
|
|
243
|
+
across shadow boundaries: the code-block actions live in `<dfk-sql>`'s tree,
|
|
244
|
+
a diagram's cluster in `<dfk-mermaid>`'s, and the fullscreen toggle in this
|
|
245
|
+
light-DOM tab strip. Keep the copies in sync. */
|
|
246
|
+
.dfk-icon-button {
|
|
245
247
|
position: relative;
|
|
246
248
|
display: inline-flex;
|
|
247
249
|
align-items: center;
|
|
@@ -257,27 +259,27 @@ dfk-sql:not(:defined) {
|
|
|
257
259
|
transition: background 0.12s ease, color 0.12s ease;
|
|
258
260
|
}
|
|
259
261
|
|
|
260
|
-
.dfk-
|
|
261
|
-
.dfk-
|
|
262
|
+
.dfk-icon-button:hover:not(:disabled),
|
|
263
|
+
.dfk-icon-button:focus-visible {
|
|
262
264
|
background: var(--ifm-color-emphasis-200, #e6e6e6);
|
|
263
265
|
color: var(--ifm-color-primary, #14459b);
|
|
264
266
|
}
|
|
265
267
|
|
|
266
|
-
.dfk-
|
|
268
|
+
.dfk-icon-button:disabled {
|
|
267
269
|
opacity: 0.45;
|
|
268
270
|
cursor: progress;
|
|
269
271
|
}
|
|
270
272
|
|
|
271
|
-
.dfk-
|
|
273
|
+
.dfk-icon-button[hidden] {
|
|
272
274
|
display: none;
|
|
273
275
|
}
|
|
274
276
|
|
|
275
277
|
/* A sticky state, e.g. the wrap toggle while wrapping is on. */
|
|
276
|
-
.dfk-
|
|
278
|
+
.dfk-icon-on {
|
|
277
279
|
color: var(--ifm-color-primary, #14459b);
|
|
278
280
|
}
|
|
279
281
|
|
|
280
|
-
.dfk-
|
|
282
|
+
.dfk-icon {
|
|
281
283
|
font-size: 1rem;
|
|
282
284
|
}
|
|
283
285
|
|
package/src/sql/verify.ts
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
* publishes, so a broken example is caught by CI instead of by a reader
|
|
4
4
|
* clicking **Run**.
|
|
5
5
|
*
|
|
6
|
-
* It is the same environment the site gives a block: DuckDB-Wasm in a
|
|
6
|
+
* It is the same environment the site gives a block: DuckDB-Wasm in a browser,
|
|
7
7
|
* the site's extension preloaded, one instance per page and the page's blocks
|
|
8
|
-
* sharing a connection (see `sql/
|
|
9
|
-
* not the
|
|
8
|
+
* sharing a connection (see `sql/browserRunner.ts` for why this is a real
|
|
9
|
+
* browser and not the Node worker the kit used to run).
|
|
10
10
|
*
|
|
11
11
|
* A block may fail *on purpose* — half the guide ends on a statement that
|
|
12
12
|
* demonstrates an error. Such a block says so in its own metadata
|
|
@@ -15,12 +15,11 @@
|
|
|
15
15
|
* on has to be data rather than a string match on a comment. Only blocks that
|
|
16
16
|
* did not behave as declared make the command exit non-zero.
|
|
17
17
|
*/
|
|
18
|
-
import {
|
|
19
|
-
import {tmpdir} from 'node:os';
|
|
18
|
+
import {readdirSync, statSync, writeFileSync} from 'node:fs';
|
|
20
19
|
import {join, resolve} from 'node:path';
|
|
21
20
|
|
|
22
21
|
import {collectRunnableSql, expectsError, type RunnableSqlBlock} from './collect';
|
|
23
|
-
import {
|
|
22
|
+
import {BrowserSqlRunner, type WasmPlatform} from './browserRunner';
|
|
24
23
|
|
|
25
24
|
export interface VerifyOptions {
|
|
26
25
|
/** Docs site root; defaults to the working directory. */
|
|
@@ -39,11 +38,10 @@ export interface VerifyOptions {
|
|
|
39
38
|
/** Per-block timeout in milliseconds; a hang is reported instead of blocking CI. */
|
|
40
39
|
timeoutMs?: number;
|
|
41
40
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* DuckDB's file system is the real one, relative to the working directory.
|
|
41
|
+
* The browser executable that runs the blocks. Defaults to a detected
|
|
42
|
+
* Chrome/Edge; the `DFK_BROWSER` environment variable is the same override.
|
|
45
43
|
*/
|
|
46
|
-
|
|
44
|
+
browser?: string;
|
|
47
45
|
/** Write the full result list here as JSON. */
|
|
48
46
|
reportFile?: string;
|
|
49
47
|
}
|
|
@@ -79,8 +77,6 @@ const DEFAULT_TIMEOUT_MS = 30_000;
|
|
|
79
77
|
|
|
80
78
|
/** Runs every runnable block of the site and returns the outcome of each. */
|
|
81
79
|
export async function verifySqlDocs(options: VerifyOptions = {}): Promise<VerifyReport> {
|
|
82
|
-
// Everything is resolved to absolute paths first: the run changes the working
|
|
83
|
-
// directory (see below).
|
|
84
80
|
const siteDir = resolve(options.siteDir ?? process.cwd());
|
|
85
81
|
const blocks = collectRunnableSql({
|
|
86
82
|
siteDir,
|
|
@@ -94,26 +90,10 @@ export async function verifySqlDocs(options: VerifyOptions = {}): Promise<Verify
|
|
|
94
90
|
: defaultExtension(siteDir);
|
|
95
91
|
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
96
92
|
|
|
97
|
-
//
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
|
|
101
|
-
const scratch = options.workingDir
|
|
102
|
-
? resolve(options.workingDir)
|
|
103
|
-
: mkdtempSync(join(tmpdir(), 'duckfn-sql-verify-'));
|
|
104
|
-
mkdirSync(scratch, {recursive: true});
|
|
105
|
-
const previousCwd = process.cwd();
|
|
106
|
-
process.chdir(scratch);
|
|
107
|
-
|
|
108
|
-
let results: BlockResult[];
|
|
109
|
-
try {
|
|
110
|
-
results = await runPages(blocks, extension, options, timeoutMs);
|
|
111
|
-
} finally {
|
|
112
|
-
process.chdir(previousCwd);
|
|
113
|
-
if (!options.workingDir) {
|
|
114
|
-
rmSync(scratch, {recursive: true, force: true});
|
|
115
|
-
}
|
|
116
|
-
}
|
|
93
|
+
// No working directory: in a browser DuckDB's file system is the instance's
|
|
94
|
+
// own memory, so `COPY … TO` / `dfn_file_write_*` never touch the docs tree —
|
|
95
|
+
// they land in the page and vanish on the next `newPage()`.
|
|
96
|
+
const results = await runPages(blocks, extension, options, timeoutMs);
|
|
117
97
|
|
|
118
98
|
const report: VerifyReport = {
|
|
119
99
|
blocks: results,
|
|
@@ -135,10 +115,11 @@ async function runPages(
|
|
|
135
115
|
options: VerifyOptions,
|
|
136
116
|
timeoutMs: number,
|
|
137
117
|
): Promise<BlockResult[]> {
|
|
138
|
-
const runner = await
|
|
118
|
+
const runner = await BrowserSqlRunner.create({
|
|
139
119
|
extension,
|
|
140
120
|
platform: options.platform,
|
|
141
121
|
engine: options.engine,
|
|
122
|
+
browser: options.browser,
|
|
142
123
|
});
|
|
143
124
|
const results: BlockResult[] = [];
|
|
144
125
|
try {
|
|
@@ -158,7 +139,7 @@ async function runPages(
|
|
|
158
139
|
|
|
159
140
|
/** Runs one block and judges the result against the block's own declaration. */
|
|
160
141
|
async function runBlock(
|
|
161
|
-
runner:
|
|
142
|
+
runner: BrowserSqlRunner,
|
|
162
143
|
block: RunnableSqlBlock,
|
|
163
144
|
timeoutMs: number,
|
|
164
145
|
): Promise<BlockResult> {
|
|
@@ -278,9 +259,9 @@ export async function cliMain(argv: readonly string[]): Promise<void> {
|
|
|
278
259
|
|
|
279
260
|
const USAGE = `Usage: duckfn-sql-verify [options]
|
|
280
261
|
|
|
281
|
-
Runs every runnable SQL block of a duckfn docs site in
|
|
282
|
-
that each one behaves as its own metadata declares
|
|
283
|
-
block that demonstrates a failure).
|
|
262
|
+
Runs every runnable SQL block of a duckfn docs site in a headless browser
|
|
263
|
+
(DuckDB-Wasm), and checks that each one behaves as its own metadata declares
|
|
264
|
+
("expect": "error" for a block that demonstrates a failure).
|
|
284
265
|
|
|
285
266
|
--site <dir> Docs site root (default: the working directory)
|
|
286
267
|
--content <dir> Content directory, relative to the site root (repeatable;
|
|
@@ -291,8 +272,9 @@ block that demonstrates a failure).
|
|
|
291
272
|
--platform <eh|mvp> DuckDB-Wasm bundle, which must match the extension build
|
|
292
273
|
(default: eh)
|
|
293
274
|
--engine <path> Engine wasm override
|
|
275
|
+
--browser <path> Browser executable (default: a detected Chrome/Edge,
|
|
276
|
+
or the DFK_BROWSER environment variable)
|
|
294
277
|
--timeout <ms> Per-block timeout (default: 30000)
|
|
295
|
-
--working-dir <dir> Directory the blocks run in (default: a temporary one)
|
|
296
278
|
--report <file> Write the full result list as JSON
|
|
297
279
|
--quiet Only report unexpected behaviour
|
|
298
280
|
--help Show this help
|
|
@@ -330,12 +312,12 @@ function parseArgs(argv: readonly string[]): ParsedArgs {
|
|
|
330
312
|
case '--engine':
|
|
331
313
|
options.engine = next();
|
|
332
314
|
break;
|
|
315
|
+
case '--browser':
|
|
316
|
+
options.browser = next();
|
|
317
|
+
break;
|
|
333
318
|
case '--timeout':
|
|
334
319
|
options.timeoutMs = Number(next());
|
|
335
320
|
break;
|
|
336
|
-
case '--working-dir':
|
|
337
|
-
options.workingDir = next();
|
|
338
|
-
break;
|
|
339
321
|
case '--report':
|
|
340
322
|
options.reportFile = next();
|
|
341
323
|
break;
|
package/dist/sql/editor.d.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The CodeMirror 6 editor that *is* the code view of a runnable SQL block.
|
|
3
|
-
*
|
|
4
|
-
* Every CodeMirror module arrives through dynamic `import()` inside
|
|
5
|
-
* {@link mountSqlEditor}: a page full of SQL examples pays nothing for the
|
|
6
|
-
* editor on its critical path, and Docusaurus' Node prerender never evaluates
|
|
7
|
-
* any of it.
|
|
8
|
-
*/
|
|
9
|
-
export interface SqlEditor {
|
|
10
|
-
getValue(): string;
|
|
11
|
-
setValue(value: string): void;
|
|
12
|
-
/** Soft-wraps long lines, or stops wrapping them (the block's wrap toggle). */
|
|
13
|
-
setWrap(wrapped: boolean): void;
|
|
14
|
-
destroy(): void;
|
|
15
|
-
}
|
|
16
|
-
export declare function mountSqlEditor(container: HTMLElement, value: string, onChange: (value: string) => void): Promise<SqlEditor>;
|
package/dist/sql/nodeRunner.d.ts
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
export interface DuckdbQueryResult {
|
|
2
|
-
numRows: number;
|
|
3
|
-
schema: {
|
|
4
|
-
fields: {
|
|
5
|
-
name: string;
|
|
6
|
-
}[];
|
|
7
|
-
};
|
|
8
|
-
}
|
|
9
|
-
export interface DuckdbConnection {
|
|
10
|
-
query(sql: string): Promise<DuckdbQueryResult>;
|
|
11
|
-
}
|
|
12
|
-
export type WasmPlatform = 'eh' | 'mvp';
|
|
13
|
-
export interface RunnerOptions {
|
|
14
|
-
/**
|
|
15
|
-
* The extension to `LOAD`: a local `.duckdb_extension.wasm` path (served to
|
|
16
|
-
* the worker over a loopback http server) or an absolute `http(s)` URL.
|
|
17
|
-
*/
|
|
18
|
-
extension: string;
|
|
19
|
-
/**
|
|
20
|
-
* DuckDB-Wasm platform. Must match how the extension was built — a site
|
|
21
|
-
* serving `duckfn-wasm_eh.duckdb_extension.wasm` runs the `eh` bundle.
|
|
22
|
-
*/
|
|
23
|
-
platform?: WasmPlatform;
|
|
24
|
-
/** The engine wasm; defaults to the one shipped beside the worker bundle. */
|
|
25
|
-
engine?: string;
|
|
26
|
-
}
|
|
27
|
-
export interface RunResult {
|
|
28
|
-
rows: number;
|
|
29
|
-
columns: number;
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* One DuckDB-Wasm instance at a time, driven from Node.
|
|
33
|
-
*
|
|
34
|
-
* `newPage()` is what a docs site does per page load: a fresh instance, a fresh
|
|
35
|
-
* connection, the extension loaded again. Blocks of one page then share state
|
|
36
|
-
* (a table created in one block is visible to the next), while pages stay
|
|
37
|
-
* isolated — which is why the runner is used one page at a time rather than
|
|
38
|
-
* over a single long-lived connection.
|
|
39
|
-
*/
|
|
40
|
-
export declare class WasmSqlRunner {
|
|
41
|
-
#private;
|
|
42
|
-
private constructor();
|
|
43
|
-
static create(options: RunnerOptions): Promise<WasmSqlRunner>;
|
|
44
|
-
/** Drops the current instance and starts a fresh page: new instance, new connection. */
|
|
45
|
-
newPage(): Promise<void>;
|
|
46
|
-
/** Runs one block; the caller decides whether a failure is expected. */
|
|
47
|
-
run(sql: string): Promise<RunResult>;
|
|
48
|
-
/** Where the fetched extension was staged, for diagnostics. */
|
|
49
|
-
get stagingDir(): string | null;
|
|
50
|
-
close(): Promise<void>;
|
|
51
|
-
}
|
package/dist/sql/nodeRunner.js
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import { createRequire as e } from "node:module";
|
|
2
|
-
import { basename as t, dirname as n, join as r } from "node:path";
|
|
3
|
-
import { mkdirSync as i, readFileSync as a, rmSync as o } from "node:fs";
|
|
4
|
-
import { createServer as s } from "node:http";
|
|
5
|
-
import { Worker as c } from "node:worker_threads";
|
|
6
|
-
import { homedir as l } from "node:os";
|
|
7
|
-
//#region src/sql/nodeRunner.ts
|
|
8
|
-
var u = e(import.meta.url), d = "@duckdb/duckdb-wasm/dist/duckdb-node.cjs", f = class {
|
|
9
|
-
#e;
|
|
10
|
-
#t = /* @__PURE__ */ new Map();
|
|
11
|
-
onmessage = null;
|
|
12
|
-
onerror = null;
|
|
13
|
-
onclose = null;
|
|
14
|
-
constructor(e) {
|
|
15
|
-
this.#e = new c(u.resolve(d), { workerData: {
|
|
16
|
-
mod: e,
|
|
17
|
-
name: "",
|
|
18
|
-
type: ""
|
|
19
|
-
} }), this.#e.on("message", (e) => this.#n("message", e)), this.#e.on("error", (e) => this.#n("error", e)), this.#e.on("exit", () => this.#n("close"));
|
|
20
|
-
}
|
|
21
|
-
#n(e, t) {
|
|
22
|
-
let n = {
|
|
23
|
-
type: e,
|
|
24
|
-
data: t,
|
|
25
|
-
target: this,
|
|
26
|
-
currentTarget: this
|
|
27
|
-
}, r = this[`on${e}`];
|
|
28
|
-
typeof r == "function" && r(n);
|
|
29
|
-
for (let t of this.#t.get(e) ?? []) t(n);
|
|
30
|
-
}
|
|
31
|
-
addEventListener(e, t) {
|
|
32
|
-
this.#t.set(e, [...this.#t.get(e) ?? [], t]);
|
|
33
|
-
}
|
|
34
|
-
removeEventListener(e, t) {
|
|
35
|
-
this.#t.set(e, (this.#t.get(e) ?? []).filter((e) => e !== t));
|
|
36
|
-
}
|
|
37
|
-
postMessage(e, t) {
|
|
38
|
-
this.#e.postMessage(e, t ?? []);
|
|
39
|
-
}
|
|
40
|
-
terminate() {
|
|
41
|
-
return this.#e.terminate();
|
|
42
|
-
}
|
|
43
|
-
}, p = class e {
|
|
44
|
-
#e = null;
|
|
45
|
-
#t = null;
|
|
46
|
-
#n = null;
|
|
47
|
-
#r = null;
|
|
48
|
-
#i;
|
|
49
|
-
#a;
|
|
50
|
-
#o;
|
|
51
|
-
constructor(e, t, n) {
|
|
52
|
-
this.#i = e, this.#a = t, this.#o = n;
|
|
53
|
-
}
|
|
54
|
-
static async create(t) {
|
|
55
|
-
let i = t.platform ?? "eh", a = u.resolve(`@duckdb/duckdb-wasm/dist/duckdb-node-${i}.worker.cjs`), o = t.engine ?? r(n(a), `duckdb-${i}.wasm`), s = /^https?:\/\//i.test(t.extension) ? null : await m(t.extension), c = s ? s.url : t.extension, l = new e(c, o, a);
|
|
56
|
-
return l.#n = s ? s.server : null, l.#r = s ? h(s.stagingHost, s.stagingSegment) : null, l;
|
|
57
|
-
}
|
|
58
|
-
async newPage() {
|
|
59
|
-
this.#e && (await this.#e.terminate(), this.#e = null, this.#t = null);
|
|
60
|
-
let e = await import(
|
|
61
|
-
/* @vite-ignore */
|
|
62
|
-
d
|
|
63
|
-
), t = new e.AsyncDuckDB(new e.ConsoleLogger(), new f(this.#o));
|
|
64
|
-
await t.instantiate(this.#a, null), await t.open({ allowUnsignedExtensions: !0 });
|
|
65
|
-
let n = await t.connect();
|
|
66
|
-
await n.query(`LOAD '${this.#i}'`), this.#e = t, this.#t = n;
|
|
67
|
-
}
|
|
68
|
-
async run(e) {
|
|
69
|
-
let t = this.#t;
|
|
70
|
-
if (!t) throw Error("sql/verify: no page is open — call newPage() first");
|
|
71
|
-
let n = await t.query(e);
|
|
72
|
-
return {
|
|
73
|
-
rows: n.numRows,
|
|
74
|
-
columns: n.schema.fields.length
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
get stagingDir() {
|
|
78
|
-
return this.#r;
|
|
79
|
-
}
|
|
80
|
-
async close() {
|
|
81
|
-
this.#e && (await this.#e.terminate(), this.#e = null, this.#t = null), this.#n &&= (await new Promise((e) => this.#n?.close(() => e())), null);
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
async function m(e) {
|
|
85
|
-
let n = a(e), r = t(e), i = g(e), o = s((e, t) => {
|
|
86
|
-
t.writeHead(200, {
|
|
87
|
-
"content-type": "application/octet-stream",
|
|
88
|
-
"content-length": n.length
|
|
89
|
-
}), t.end(n);
|
|
90
|
-
}), c = process.platform === "win32" ? 80 : 0;
|
|
91
|
-
await new Promise((e, t) => {
|
|
92
|
-
o.once("error", (e) => {
|
|
93
|
-
t(/* @__PURE__ */ Error(`sql/verify: cannot serve the extension on port ${c} (${e.code}): a port in the URL would put a colon into DuckDB's staging path, which is not a legal Windows path — free port 80, or load the extension from an http(s) URL with --extension`));
|
|
94
|
-
}), o.listen(c, "localhost", e);
|
|
95
|
-
});
|
|
96
|
-
let l = o.address(), u = typeof l == "object" && l ? l.port : c, d = u === 80 ? "localhost" : `localhost:${u}`;
|
|
97
|
-
return {
|
|
98
|
-
server: o,
|
|
99
|
-
url: `http://${d}/${i}/${r}`,
|
|
100
|
-
stagingHost: d,
|
|
101
|
-
stagingSegment: i
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
function h(e, t) {
|
|
105
|
-
let n = r(l(), ".duckdb", "extensions", e, t);
|
|
106
|
-
return o(n, {
|
|
107
|
-
recursive: !0,
|
|
108
|
-
force: !0
|
|
109
|
-
}), i(n, { recursive: !0 }), n;
|
|
110
|
-
}
|
|
111
|
-
function g(e) {
|
|
112
|
-
return t(e).split(".")[0] ?? "";
|
|
113
|
-
}
|
|
114
|
-
//#endregion
|
|
115
|
-
export { p as WasmSqlRunner };
|
package/src/sql/editor.ts
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The CodeMirror 6 editor that *is* the code view of a runnable SQL block.
|
|
3
|
-
*
|
|
4
|
-
* Every CodeMirror module arrives through dynamic `import()` inside
|
|
5
|
-
* {@link mountSqlEditor}: a page full of SQL examples pays nothing for the
|
|
6
|
-
* editor on its critical path, and Docusaurus' Node prerender never evaluates
|
|
7
|
-
* any of it.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
export interface SqlEditor {
|
|
11
|
-
getValue(): string;
|
|
12
|
-
setValue(value: string): void;
|
|
13
|
-
/** Soft-wraps long lines, or stops wrapping them (the block's wrap toggle). */
|
|
14
|
-
setWrap(wrapped: boolean): void;
|
|
15
|
-
destroy(): void;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export async function mountSqlEditor(
|
|
19
|
-
container: HTMLElement,
|
|
20
|
-
value: string,
|
|
21
|
-
onChange: (value: string) => void,
|
|
22
|
-
): Promise<SqlEditor> {
|
|
23
|
-
const [
|
|
24
|
-
{basicSetup},
|
|
25
|
-
{sql},
|
|
26
|
-
{EditorView, keymap},
|
|
27
|
-
{defaultKeymap, historyKeymap},
|
|
28
|
-
{Compartment},
|
|
29
|
-
] = await Promise.all([
|
|
30
|
-
import('codemirror'),
|
|
31
|
-
import('@codemirror/lang-sql'),
|
|
32
|
-
import('@codemirror/view'),
|
|
33
|
-
import('@codemirror/commands'),
|
|
34
|
-
import('@codemirror/state'),
|
|
35
|
-
]);
|
|
36
|
-
|
|
37
|
-
// Wrapping is toggled from the outside, and reconfiguring it must not disturb
|
|
38
|
-
// the document or the undo history — that is exactly what a compartment is
|
|
39
|
-
// for, so the extension is swapped in place rather than rebuilt.
|
|
40
|
-
const wrap = new Compartment();
|
|
41
|
-
|
|
42
|
-
const view = new EditorView({
|
|
43
|
-
doc: value,
|
|
44
|
-
extensions: [
|
|
45
|
-
basicSetup,
|
|
46
|
-
sql(),
|
|
47
|
-
keymap.of([...defaultKeymap, ...historyKeymap]),
|
|
48
|
-
wrap.of([]),
|
|
49
|
-
EditorView.updateListener.of((update) => {
|
|
50
|
-
if (update.docChanged) {
|
|
51
|
-
onChange(update.state.doc.toString());
|
|
52
|
-
}
|
|
53
|
-
}),
|
|
54
|
-
],
|
|
55
|
-
parent: container,
|
|
56
|
-
// `root` is left to CodeMirror's own `getRoot(container)`. The container sits
|
|
57
|
-
// in `<dfk-sql>`'s shadow root, so style-mod mounts the base theme into that
|
|
58
|
-
// same shadow root — exactly where the `.cm-*` rules are needed. Pinning it
|
|
59
|
-
// to `document` would put them outside the editor's tree instead, where a
|
|
60
|
-
// shadow boundary stops them.
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
return {
|
|
64
|
-
getValue: () => view.state.doc.toString(),
|
|
65
|
-
setValue: (next: string) =>
|
|
66
|
-
view.dispatch({
|
|
67
|
-
changes: {from: 0, to: view.state.doc.length, insert: next},
|
|
68
|
-
}),
|
|
69
|
-
setWrap: (wrapped: boolean) =>
|
|
70
|
-
view.dispatch({
|
|
71
|
-
effects: wrap.reconfigure(wrapped ? EditorView.lineWrapping : []),
|
|
72
|
-
}),
|
|
73
|
-
destroy: () => view.destroy(),
|
|
74
|
-
};
|
|
75
|
-
}
|