@ox-content/code-play 3.0.0-alpha.10 → 3.0.0-alpha.12
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 +16 -0
- package/dist/browser.mjs +81 -2
- package/dist/client.mjs +3 -0
- package/dist/client.mjs.map +1 -1
- package/dist/config.d.mts +33 -1
- package/dist/config.d.mts.map +1 -1
- package/dist/hydrate.d.mts +3 -1
- package/dist/hydrate.d.mts.map +1 -1
- package/dist/hydrate2.mjs +77 -4
- package/dist/hydrate2.mjs.map +1 -1
- package/dist/index.d.mts +29 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +3 -3
- package/dist/plugin.d.mts.map +1 -1
- package/dist/plugin2.mjs +351 -35
- package/dist/plugin2.mjs.map +1 -1
- package/package.json +1 -1
package/dist/plugin2.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { f as resolveCodePlayOptions, h as resolveLanguage, i as escapeAttribute, n as encodePayload, o as DEFAULT_ENDPOINTS, r as decodeHtml, s as DEFAULT_VIEWERS, t as decodePayload, u as DEV_TYPECHECK_PATH } from "./payload.mjs";
|
|
2
2
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
-
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
3
|
+
import { existsSync, readFileSync, readdirSync, realpathSync, statSync } from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
//#region src/authoring.ts
|
|
@@ -48,6 +48,7 @@ function parseCodePlayAttributes(attrs) {
|
|
|
48
48
|
options.viewers = parseViewers(value);
|
|
49
49
|
continue;
|
|
50
50
|
}
|
|
51
|
+
if (applyProjectAttribute(options, name, value)) continue;
|
|
51
52
|
if (name.startsWith("config-")) options.config[configKeyFromAttribute(name.slice(7))] = coerceOptionValue(value);
|
|
52
53
|
}
|
|
53
54
|
return options;
|
|
@@ -118,9 +119,70 @@ function applyPlayOption(options, rawName, value) {
|
|
|
118
119
|
options.viewers = parseViewers(value);
|
|
119
120
|
return;
|
|
120
121
|
}
|
|
122
|
+
if (applyProjectMeta(options, rawName, value)) return;
|
|
121
123
|
const configKey = name.startsWith("play-config:") || name.startsWith("play-config.") ? rawName.slice(12) : name.startsWith("play-") ? rawName.slice(5) : "";
|
|
122
124
|
if (configKey) options.config[configKey] = coerceOptionValue(value);
|
|
123
125
|
}
|
|
126
|
+
function applyProjectMeta(options, rawName, value) {
|
|
127
|
+
switch (rawName.toLowerCase()) {
|
|
128
|
+
case "play-project":
|
|
129
|
+
case "play-sandbox":
|
|
130
|
+
case "play-provider":
|
|
131
|
+
ensureProject(options, value);
|
|
132
|
+
return true;
|
|
133
|
+
case "play-entry":
|
|
134
|
+
ensureProject(options).entry = value;
|
|
135
|
+
return true;
|
|
136
|
+
case "play-file":
|
|
137
|
+
ensureProject(options).file = value;
|
|
138
|
+
return true;
|
|
139
|
+
case "play-files":
|
|
140
|
+
ensureProject(options).files.push(...splitList(value));
|
|
141
|
+
return true;
|
|
142
|
+
case "play-project-url":
|
|
143
|
+
case "play-open-url":
|
|
144
|
+
ensureProject(options).openUrl = value;
|
|
145
|
+
return true;
|
|
146
|
+
case "play-fallback-url":
|
|
147
|
+
ensureProject(options).fallbackUrl = value;
|
|
148
|
+
return true;
|
|
149
|
+
default: return false;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
function applyProjectAttribute(options, name, value) {
|
|
153
|
+
switch (name) {
|
|
154
|
+
case "project":
|
|
155
|
+
case "sandbox":
|
|
156
|
+
case "provider":
|
|
157
|
+
ensureProject(options, value);
|
|
158
|
+
return true;
|
|
159
|
+
case "entry":
|
|
160
|
+
ensureProject(options).entry = value;
|
|
161
|
+
return true;
|
|
162
|
+
case "file":
|
|
163
|
+
ensureProject(options).file = value;
|
|
164
|
+
return true;
|
|
165
|
+
case "files":
|
|
166
|
+
ensureProject(options).files.push(...splitList(value));
|
|
167
|
+
return true;
|
|
168
|
+
case "project-url":
|
|
169
|
+
case "open-url":
|
|
170
|
+
ensureProject(options).openUrl = value;
|
|
171
|
+
return true;
|
|
172
|
+
case "fallback-url":
|
|
173
|
+
ensureProject(options).fallbackUrl = value;
|
|
174
|
+
return true;
|
|
175
|
+
default: return false;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function ensureProject(options, provider = "external") {
|
|
179
|
+
options.project ??= {
|
|
180
|
+
provider,
|
|
181
|
+
files: []
|
|
182
|
+
};
|
|
183
|
+
if (provider && options.project.provider === "external") options.project.provider = provider;
|
|
184
|
+
return options.project;
|
|
185
|
+
}
|
|
124
186
|
function readTokenPair(token) {
|
|
125
187
|
const index = token.indexOf("=");
|
|
126
188
|
if (index === -1) return;
|
|
@@ -155,6 +217,9 @@ function parseViewers(value) {
|
|
|
155
217
|
}
|
|
156
218
|
return Object.keys(viewers).length > 0 ? viewers : void 0;
|
|
157
219
|
}
|
|
220
|
+
function splitList(value) {
|
|
221
|
+
return value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
222
|
+
}
|
|
158
223
|
function coerceOptionValue(value) {
|
|
159
224
|
if (value === "true") return true;
|
|
160
225
|
if (value === "false") return false;
|
|
@@ -231,7 +296,8 @@ function parsePlayFences(source) {
|
|
|
231
296
|
config: options.config,
|
|
232
297
|
ui: options.ui,
|
|
233
298
|
viewers: options.viewers,
|
|
234
|
-
timeoutMs: options.timeoutMs
|
|
299
|
+
timeoutMs: options.timeoutMs,
|
|
300
|
+
project: options.project
|
|
235
301
|
};
|
|
236
302
|
});
|
|
237
303
|
}
|
|
@@ -275,7 +341,8 @@ function parseCodePlayTags(source) {
|
|
|
275
341
|
config: options.config,
|
|
276
342
|
ui: options.ui,
|
|
277
343
|
viewers: options.viewers,
|
|
278
|
-
timeoutMs: options.timeoutMs
|
|
344
|
+
timeoutMs: options.timeoutMs,
|
|
345
|
+
project: options.project
|
|
279
346
|
});
|
|
280
347
|
}
|
|
281
348
|
return tags;
|
|
@@ -300,8 +367,153 @@ function stripIndent(value) {
|
|
|
300
367
|
return lines.map((line) => line.slice(indent)).join("\n");
|
|
301
368
|
}
|
|
302
369
|
//#endregion
|
|
370
|
+
//#region src/project-sandbox.ts
|
|
371
|
+
const PROJECT_SANDBOX_ADAPTERS = {
|
|
372
|
+
stackblitz: adapter("stackblitz", "StackBlitz", "browser"),
|
|
373
|
+
codesandbox: adapter("codesandbox", "CodeSandbox", "browser"),
|
|
374
|
+
webcontainer: adapter("webcontainer", "WebContainer", "node"),
|
|
375
|
+
external: adapter("external", "External sandbox", "external")
|
|
376
|
+
};
|
|
377
|
+
function projectSandboxFromPayloadInput(input) {
|
|
378
|
+
if (!input.project) return;
|
|
379
|
+
const warnings = [...input.warnings ?? []];
|
|
380
|
+
const adapter = resolveProjectSandboxAdapter(input.project.provider, warnings);
|
|
381
|
+
const sourceFile = normalizeProjectPath(input.project.file, warnings, "source file");
|
|
382
|
+
const entry = normalizeProjectPath(input.project.entry, warnings, "entry path");
|
|
383
|
+
const primary = {
|
|
384
|
+
path: sourceFile ?? entry ?? defaultProjectFile(input.language, input.definition),
|
|
385
|
+
code: input.code
|
|
386
|
+
};
|
|
387
|
+
const files = mergeProjectFiles([primary, ...input.files ?? []]);
|
|
388
|
+
const openUrl = safeProjectUrl(input.project.openUrl, adapter.provider, warnings);
|
|
389
|
+
const fallbackUrl = safeProjectUrl(input.project.fallbackUrl, adapter.provider, warnings);
|
|
390
|
+
return adapter.resolve({
|
|
391
|
+
provider: input.project.provider,
|
|
392
|
+
entry: entry ?? primary.path,
|
|
393
|
+
files,
|
|
394
|
+
openUrl,
|
|
395
|
+
fallbackUrl,
|
|
396
|
+
warnings
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
function normalizeProjectPath(value, warnings = [], label = "file path") {
|
|
400
|
+
const trimmed = value?.trim();
|
|
401
|
+
if (!trimmed) return;
|
|
402
|
+
const normalized = trimmed.replace(/^\.\//, "");
|
|
403
|
+
if (normalized.startsWith("/") || normalized.includes("\\") || normalized.includes("\0") || normalized.split("/").some((part) => part === "" || part === "." || part === "..") || /^[a-z]+:/i.test(normalized)) {
|
|
404
|
+
warnings.push(`Skipped unsafe project ${label}: ${trimmed}`);
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
if (normalized.length > 256) {
|
|
408
|
+
warnings.push(`Skipped long project ${label}: ${trimmed}`);
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
return normalized;
|
|
412
|
+
}
|
|
413
|
+
function projectSandboxProviderLabel(provider) {
|
|
414
|
+
return PROJECT_SANDBOX_ADAPTERS[provider].label;
|
|
415
|
+
}
|
|
416
|
+
function adapter(provider, label, target) {
|
|
417
|
+
return {
|
|
418
|
+
provider,
|
|
419
|
+
label,
|
|
420
|
+
target,
|
|
421
|
+
resolve(input) {
|
|
422
|
+
return compactProjectSandbox({
|
|
423
|
+
provider,
|
|
424
|
+
label,
|
|
425
|
+
target,
|
|
426
|
+
entry: input.entry,
|
|
427
|
+
files: input.files,
|
|
428
|
+
openUrl: input.openUrl,
|
|
429
|
+
fallbackUrl: input.fallbackUrl,
|
|
430
|
+
warnings: input.warnings
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
function defaultProjectFile(language, definition) {
|
|
436
|
+
if (definition?.framework === "vue") return "src/App.vue";
|
|
437
|
+
if (definition?.framework === "react" || definition?.framework === "solid") return "src/App.tsx";
|
|
438
|
+
if (definition?.framework === "svelte") return "src/App.svelte";
|
|
439
|
+
switch (language) {
|
|
440
|
+
case "javascript": return "index.js";
|
|
441
|
+
case "typescript": return "index.ts";
|
|
442
|
+
case "go": return "main.go";
|
|
443
|
+
case "rust": return "src/main.rs";
|
|
444
|
+
default: return "snippet.txt";
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
function resolveProjectSandboxAdapter(rawProvider, warnings) {
|
|
448
|
+
switch (rawProvider.trim().toLowerCase()) {
|
|
449
|
+
case "stackblitz":
|
|
450
|
+
case "stack-blitz":
|
|
451
|
+
case "sb": return PROJECT_SANDBOX_ADAPTERS.stackblitz;
|
|
452
|
+
case "codesandbox":
|
|
453
|
+
case "code-sandbox":
|
|
454
|
+
case "csb": return PROJECT_SANDBOX_ADAPTERS.codesandbox;
|
|
455
|
+
case "webcontainer":
|
|
456
|
+
case "web-container": return PROJECT_SANDBOX_ADAPTERS.webcontainer;
|
|
457
|
+
case "external":
|
|
458
|
+
case "": return PROJECT_SANDBOX_ADAPTERS.external;
|
|
459
|
+
default:
|
|
460
|
+
warnings.push(`Unknown project sandbox provider: ${rawProvider}`);
|
|
461
|
+
return PROJECT_SANDBOX_ADAPTERS.external;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
function mergeProjectFiles(files) {
|
|
465
|
+
const merged = /* @__PURE__ */ new Map();
|
|
466
|
+
for (const file of files) merged.set(file.path, file);
|
|
467
|
+
return [...merged.values()];
|
|
468
|
+
}
|
|
469
|
+
function safeProjectUrl(value, provider, warnings) {
|
|
470
|
+
if (!value) return;
|
|
471
|
+
let url;
|
|
472
|
+
try {
|
|
473
|
+
url = new URL(value);
|
|
474
|
+
} catch {
|
|
475
|
+
warnings.push(`Skipped invalid project URL: ${value}`);
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
479
|
+
warnings.push(`Skipped non-http project URL: ${value}`);
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
if (url.username || url.password) {
|
|
483
|
+
warnings.push(`Skipped project URL with credentials: ${url.origin}`);
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
if (!providerAllowsHost(provider, url.hostname)) {
|
|
487
|
+
warnings.push(`Skipped ${provider} project URL on unexpected host: ${url.hostname}`);
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
return url.href;
|
|
491
|
+
}
|
|
492
|
+
function providerAllowsHost(provider, host) {
|
|
493
|
+
if (provider === "external") return true;
|
|
494
|
+
const normalized = host.toLowerCase();
|
|
495
|
+
return {
|
|
496
|
+
stackblitz: ["stackblitz.com"],
|
|
497
|
+
codesandbox: ["codesandbox.io", "csb.app"],
|
|
498
|
+
webcontainer: ["webcontainers.io", "webcontainer.io"]
|
|
499
|
+
}[provider].some((suffix) => normalized === suffix || normalized.endsWith(`.${suffix}`));
|
|
500
|
+
}
|
|
501
|
+
function compactProjectSandbox(project) {
|
|
502
|
+
const next = {
|
|
503
|
+
provider: project.provider,
|
|
504
|
+
label: project.label,
|
|
505
|
+
target: project.target,
|
|
506
|
+
files: project.files
|
|
507
|
+
};
|
|
508
|
+
if (project.entry) next.entry = project.entry;
|
|
509
|
+
if (project.openUrl) next.openUrl = project.openUrl;
|
|
510
|
+
if (project.fallbackUrl) next.fallbackUrl = project.fallbackUrl;
|
|
511
|
+
if (project.warnings?.length) next.warnings = project.warnings;
|
|
512
|
+
return next;
|
|
513
|
+
}
|
|
514
|
+
//#endregion
|
|
303
515
|
//#region src/payload-factory.ts
|
|
304
|
-
function payloadFromFence(fence, options) {
|
|
516
|
+
function payloadFromFence(fence, options, context = {}) {
|
|
305
517
|
const definition = resolveLanguage(fence.language);
|
|
306
518
|
const enabled = definition ? options.languages.get(definition.id) : void 0;
|
|
307
519
|
const payload = {
|
|
@@ -326,6 +538,15 @@ function payloadFromFence(fence, options) {
|
|
|
326
538
|
endpoints: options.endpoints
|
|
327
539
|
};
|
|
328
540
|
if (enabled?.endpoint) payload.endpoint = enabled.endpoint;
|
|
541
|
+
const project = projectSandboxFromPayloadInput({
|
|
542
|
+
language: payload.language,
|
|
543
|
+
code: payload.code,
|
|
544
|
+
definition,
|
|
545
|
+
project: fence.project,
|
|
546
|
+
files: context.files,
|
|
547
|
+
warnings: context.warnings
|
|
548
|
+
});
|
|
549
|
+
if (project) payload.project = project;
|
|
329
550
|
return payload;
|
|
330
551
|
}
|
|
331
552
|
/** TypeScript typecheck in the browser needs a reachable endpoint; hide the dead button otherwise. */
|
|
@@ -369,7 +590,7 @@ function upgradeCodePlayTags(html, options) {
|
|
|
369
590
|
const definition = resolveLanguage(language);
|
|
370
591
|
const endpoints = options.endpoints ?? DEFAULT_ENDPOINTS;
|
|
371
592
|
const playOptions = parseCodePlayAttributes(attrs);
|
|
372
|
-
|
|
593
|
+
const payloadValue = {
|
|
373
594
|
language: definition?.id ?? language,
|
|
374
595
|
code,
|
|
375
596
|
title: playOptions.title,
|
|
@@ -388,7 +609,15 @@ function upgradeCodePlayTags(html, options) {
|
|
|
388
609
|
ui: playOptions.ui ?? "default",
|
|
389
610
|
timeoutMs: playOptions.timeoutMs ?? 1e4,
|
|
390
611
|
endpoints
|
|
391
|
-
}
|
|
612
|
+
};
|
|
613
|
+
const project = projectSandboxFromPayloadInput({
|
|
614
|
+
language: payloadValue.language,
|
|
615
|
+
code,
|
|
616
|
+
definition,
|
|
617
|
+
project: playOptions.project
|
|
618
|
+
});
|
|
619
|
+
if (project) payloadValue.project = project;
|
|
620
|
+
return wrapWidget(options.encodePayload(payloadValue), `<pre><code class="language-${escapeAttribute(language)}">${body}</code></pre>`);
|
|
392
621
|
});
|
|
393
622
|
}
|
|
394
623
|
function wrapMatchingFences(html, matches) {
|
|
@@ -445,6 +674,111 @@ function aliasesEqual(left, right) {
|
|
|
445
674
|
return left.toLowerCase() === right.toLowerCase();
|
|
446
675
|
}
|
|
447
676
|
//#endregion
|
|
677
|
+
//#region src/project-files.ts
|
|
678
|
+
const DEFAULT_MAX_FILES = 32;
|
|
679
|
+
const DEFAULT_MAX_BYTES = 262144;
|
|
680
|
+
function collectProjectFiles(project, context = {}) {
|
|
681
|
+
const warnings = [];
|
|
682
|
+
if (!project?.files.length) return {
|
|
683
|
+
files: [],
|
|
684
|
+
warnings
|
|
685
|
+
};
|
|
686
|
+
if (!context.documentPath) {
|
|
687
|
+
warnings.push("Skipped project files because the Markdown source path is unavailable.");
|
|
688
|
+
return {
|
|
689
|
+
files: [],
|
|
690
|
+
warnings
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
const documentDir = path.dirname(context.documentPath);
|
|
694
|
+
const sourceRoot = path.resolve(context.sourceRoot ?? documentDir);
|
|
695
|
+
const realSourceRoot = realpathIfExists(sourceRoot) ?? sourceRoot;
|
|
696
|
+
const files = [];
|
|
697
|
+
for (const requested of project.files) {
|
|
698
|
+
if (files.length >= (context.maxFiles ?? DEFAULT_MAX_FILES)) {
|
|
699
|
+
warnings.push(`Skipped project file after ${files.length} files: ${requested}`);
|
|
700
|
+
continue;
|
|
701
|
+
}
|
|
702
|
+
const safePath = normalizeProjectPath(requested, warnings);
|
|
703
|
+
if (!safePath) continue;
|
|
704
|
+
const absolute = path.resolve(documentDir, safePath);
|
|
705
|
+
if (!pathInside(absolute, sourceRoot)) {
|
|
706
|
+
warnings.push(`Skipped project file outside source root: ${safePath}`);
|
|
707
|
+
continue;
|
|
708
|
+
}
|
|
709
|
+
const realAbsolute = realpathIfExists(absolute);
|
|
710
|
+
if (!realAbsolute) {
|
|
711
|
+
warnings.push(`Skipped missing project file: ${safePath}`);
|
|
712
|
+
continue;
|
|
713
|
+
}
|
|
714
|
+
if (!pathInside(realAbsolute, realSourceRoot)) {
|
|
715
|
+
warnings.push(`Skipped project file outside real source root: ${safePath}`);
|
|
716
|
+
continue;
|
|
717
|
+
}
|
|
718
|
+
const stat = statSync(realAbsolute);
|
|
719
|
+
if (!stat.isFile()) {
|
|
720
|
+
warnings.push(`Skipped non-file project path: ${safePath}`);
|
|
721
|
+
continue;
|
|
722
|
+
}
|
|
723
|
+
if (stat.size > (context.maxBytes ?? DEFAULT_MAX_BYTES)) {
|
|
724
|
+
warnings.push(`Skipped large project file: ${safePath}`);
|
|
725
|
+
continue;
|
|
726
|
+
}
|
|
727
|
+
files.push({
|
|
728
|
+
path: safePath,
|
|
729
|
+
code: readFileSync(realAbsolute, "utf8")
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
return {
|
|
733
|
+
files,
|
|
734
|
+
warnings
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
function realpathIfExists(file) {
|
|
738
|
+
if (!existsSync(file)) return;
|
|
739
|
+
return realpathSync(file);
|
|
740
|
+
}
|
|
741
|
+
function pathInside(file, root) {
|
|
742
|
+
const relative = path.relative(root, file);
|
|
743
|
+
return relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
744
|
+
}
|
|
745
|
+
//#endregion
|
|
746
|
+
//#region src/plugin-paths.ts
|
|
747
|
+
const MARKDOWN_RE = /\.(?:md|markdown|mdx)(?:$|\?)/i;
|
|
748
|
+
function cleanMarkdownPath(id, root) {
|
|
749
|
+
const file = id.split("?")[0];
|
|
750
|
+
if (!file || file.startsWith("\0")) return;
|
|
751
|
+
return path.isAbsolute(file) ? file : path.resolve(root, file);
|
|
752
|
+
}
|
|
753
|
+
function guessHtmlPath(file, srcDir, outDir) {
|
|
754
|
+
const relative = path.relative(srcDir, file).replace(/\.(?:md|markdown|mdx)$/i, "");
|
|
755
|
+
return [path.join(outDir, `${relative}.html`), path.join(outDir, relative, "index.html")].find((candidate) => existsSync(candidate));
|
|
756
|
+
}
|
|
757
|
+
function normalizeBase(base) {
|
|
758
|
+
if (!base || base === "/") return "/";
|
|
759
|
+
return base.endsWith("/") ? base : `${base}/`;
|
|
760
|
+
}
|
|
761
|
+
function sourceRoot(root, resolved) {
|
|
762
|
+
return path.resolve(root, resolved.srcDir ?? "docs");
|
|
763
|
+
}
|
|
764
|
+
function urlToMarkdown(urlPath, root, srcDir, base) {
|
|
765
|
+
let relative = urlPath;
|
|
766
|
+
if (base !== "/" && relative.startsWith(base)) relative = relative.slice(base.length);
|
|
767
|
+
relative = relative.replace(/^\//, "").replace(/\.html$/, "");
|
|
768
|
+
if (!relative || relative.includes("..")) return;
|
|
769
|
+
return [path.resolve(root, srcDir, `${relative}.md`), path.resolve(root, srcDir, relative, "index.md")].find((candidate) => existsSync(candidate));
|
|
770
|
+
}
|
|
771
|
+
function walkFiles(dir) {
|
|
772
|
+
const entries = readdirSync(dir);
|
|
773
|
+
const files = [];
|
|
774
|
+
for (const entry of entries) {
|
|
775
|
+
const full = path.join(dir, entry);
|
|
776
|
+
if (statSync(full).isDirectory()) files.push(...walkFiles(full));
|
|
777
|
+
else files.push(full);
|
|
778
|
+
}
|
|
779
|
+
return files;
|
|
780
|
+
}
|
|
781
|
+
//#endregion
|
|
448
782
|
//#region src/plugin-client.ts
|
|
449
783
|
const CLIENT_FILE_CANDIDATES = [
|
|
450
784
|
"browser.mjs",
|
|
@@ -555,7 +889,6 @@ function writeJson(res, status, body) {
|
|
|
555
889
|
//#region src/plugin.ts
|
|
556
890
|
const VIRTUAL_ID = "virtual:ox-content/code-play";
|
|
557
891
|
const RESOLVED_VIRTUAL = `\0${VIRTUAL_ID}`;
|
|
558
|
-
const MARKDOWN_RE = /\.(?:md|markdown|mdx)(?:$|\?)/i;
|
|
559
892
|
function codePlay(options = {}) {
|
|
560
893
|
const resolved = resolveCodePlayOptions(options);
|
|
561
894
|
const explicitTypecheck = options.endpoints?.typecheck;
|
|
@@ -610,7 +943,7 @@ function codePlay(options = {}) {
|
|
|
610
943
|
const rewritten = rewritePlayFences(code, (fence) => {
|
|
611
944
|
const definition = resolveLanguage(fence.language);
|
|
612
945
|
if (!definition || !resolved.languages.has(definition.id)) return null;
|
|
613
|
-
return encodePayload(payloadFromFence(fence, resolved));
|
|
946
|
+
return encodePayload(payloadFromFence(fence, resolved, projectContextForFence(fence, cleanMarkdownPath(id, root), sourceRoot(root, resolved))));
|
|
614
947
|
});
|
|
615
948
|
if (rewritten === code) return null;
|
|
616
949
|
needsClientAsset = true;
|
|
@@ -680,6 +1013,7 @@ function enhanceHtmlForUrl(urlPath, html, root, resolved, enhance) {
|
|
|
680
1013
|
const markdownPath = urlToMarkdown(urlPath, root, resolved.srcDir ?? "docs", resolved.base);
|
|
681
1014
|
if (!markdownPath || !existsSync(markdownPath)) return;
|
|
682
1015
|
const source = readFileSync(markdownPath, "utf8");
|
|
1016
|
+
const srcRoot = sourceRoot(root, resolved);
|
|
683
1017
|
const fences = [...parsePlayFences(source), ...parseCodePlayTags(source)].filter((fence) => {
|
|
684
1018
|
const definition = resolveLanguage(fence.language);
|
|
685
1019
|
return Boolean(definition && resolved.languages.has(definition.id));
|
|
@@ -688,16 +1022,9 @@ function enhanceHtmlForUrl(urlPath, html, root, resolved, enhance) {
|
|
|
688
1022
|
return enhancePlayHtml(html, enhance(fences.map((fence) => ({
|
|
689
1023
|
language: fence.language,
|
|
690
1024
|
code: fence.code,
|
|
691
|
-
payload: encodePayload(payloadFromFence(fence, resolved))
|
|
1025
|
+
payload: encodePayload(payloadFromFence(fence, resolved, projectContextForFence(fence, markdownPath, srcRoot)))
|
|
692
1026
|
}))));
|
|
693
1027
|
}
|
|
694
|
-
function urlToMarkdown(urlPath, root, srcDir, base) {
|
|
695
|
-
let relative = urlPath;
|
|
696
|
-
if (base !== "/" && relative.startsWith(base)) relative = relative.slice(base.length);
|
|
697
|
-
relative = relative.replace(/^\//, "").replace(/\.html$/, "");
|
|
698
|
-
if (!relative || relative.includes("..")) return;
|
|
699
|
-
return [path.resolve(root, srcDir, `${relative}.md`), path.resolve(root, srcDir, relative, "index.md")].find((candidate) => existsSync(candidate));
|
|
700
|
-
}
|
|
701
1028
|
function mountProxies(server, rustUrl, goUrl) {
|
|
702
1029
|
server.middlewares.use("/__ox-code-play/rust", (req, res) => {
|
|
703
1030
|
proxy(req, res, rustUrl, "application/json");
|
|
@@ -725,7 +1052,7 @@ async function enhanceWrittenPages(srcDir, outDir, resolved, enhance) {
|
|
|
725
1052
|
const enhanced = enhancePlayHtml(html, enhance(fences.map((fence) => ({
|
|
726
1053
|
language: fence.language,
|
|
727
1054
|
code: fence.code,
|
|
728
|
-
payload: encodePayload(payloadFromFence(fence, resolved))
|
|
1055
|
+
payload: encodePayload(payloadFromFence(fence, resolved, projectContextForFence(fence, file, srcDir)))
|
|
729
1056
|
}))));
|
|
730
1057
|
if (enhanced !== html) {
|
|
731
1058
|
await writeFile(htmlPath, enhanced);
|
|
@@ -734,25 +1061,14 @@ async function enhanceWrittenPages(srcDir, outDir, resolved, enhance) {
|
|
|
734
1061
|
}
|
|
735
1062
|
return enhancedAny;
|
|
736
1063
|
}
|
|
737
|
-
function
|
|
738
|
-
|
|
739
|
-
return
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
const files = [];
|
|
744
|
-
for (const entry of entries) {
|
|
745
|
-
const full = path.join(dir, entry);
|
|
746
|
-
if (statSync(full).isDirectory()) files.push(...walkFiles(full));
|
|
747
|
-
else files.push(full);
|
|
748
|
-
}
|
|
749
|
-
return files;
|
|
750
|
-
}
|
|
751
|
-
function normalizeBase(base) {
|
|
752
|
-
if (!base || base === "/") return "/";
|
|
753
|
-
return base.endsWith("/") ? base : `${base}/`;
|
|
1064
|
+
function projectContextForFence(fence, documentPath, srcRoot) {
|
|
1065
|
+
if (!fence.project) return {};
|
|
1066
|
+
return collectProjectFiles(fence.project, {
|
|
1067
|
+
documentPath,
|
|
1068
|
+
sourceRoot: srcRoot
|
|
1069
|
+
});
|
|
754
1070
|
}
|
|
755
1071
|
//#endregion
|
|
756
|
-
export {
|
|
1072
|
+
export { normalizeProjectPath as a, parseCodePlayTags as c, stripPlayMeta as d, PROJECT_SANDBOX_ADAPTERS as i, parsePlayFences as l, enhanceGeneratedModule as n, projectSandboxFromPayloadInput as o, enhancePlayHtml as r, projectSandboxProviderLabel as s, codePlay as t, rewritePlayFences as u };
|
|
757
1073
|
|
|
758
1074
|
//# sourceMappingURL=plugin2.mjs.map
|