@ttsc/playground 0.18.4 → 0.19.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/README.md +1 -1
- package/lib/src/compiler/createWorkerCompiler.js +7 -191
- package/lib/src/compiler/createWorkerCompiler.js.map +1 -1
- package/lib/src/compiler/internal/createWorkerCompilerService.d.ts +18 -0
- package/lib/src/compiler/internal/createWorkerCompilerService.js +272 -0
- package/lib/src/compiler/internal/createWorkerCompilerService.js.map +1 -0
- package/lib/src/npm/collectExternalPackageNames.js +253 -9
- package/lib/src/npm/collectExternalPackageNames.js.map +1 -1
- package/lib/src/react/ConsoleViewer.js +17 -17
- package/lib/src/react/ConsoleViewer.js.map +1 -1
- package/lib/src/react/DependencyProgressModal.js +1 -1
- package/lib/src/react/DependencyProgressModal.js.map +1 -1
- package/lib/src/react/DiagnosticsPanel.js +2 -2
- package/lib/src/react/DiagnosticsPanel.js.map +1 -1
- package/lib/src/react/ExamplePicker.js +2 -2
- package/lib/src/react/ExamplePicker.js.map +1 -1
- package/lib/src/react/LintPane.js +3 -3
- package/lib/src/react/LintPane.js.map +1 -1
- package/lib/src/react/OptionsPanel.js +1 -1
- package/lib/src/react/OptionsPanel.js.map +1 -1
- package/lib/src/react/PlaygroundShell.js +14 -13
- package/lib/src/react/PlaygroundShell.js.map +1 -1
- package/lib/src/react/ResultViewer.d.ts +2 -2
- package/lib/src/react/ResultViewer.js +3 -3
- package/lib/src/react/ResultViewer.js.map +1 -1
- package/lib/src/react/SourceEditor.js +1 -1
- package/lib/src/react/SourceEditor.js.map +1 -1
- package/lib/src/sandbox/createSandboxRequire.js +34 -9
- package/lib/src/sandbox/createSandboxRequire.js.map +1 -1
- package/package.json +3 -3
- package/src/compiler/createWorkerCompiler.ts +8 -236
- package/src/compiler/internal/createWorkerCompilerService.ts +358 -0
- package/src/npm/collectExternalPackageNames.ts +262 -9
- package/src/react/ConsoleViewer.tsx +18 -18
- package/src/react/DependencyProgressModal.tsx +11 -11
- package/src/react/DiagnosticsPanel.tsx +11 -11
- package/src/react/ExamplePicker.tsx +7 -7
- package/src/react/LintPane.tsx +5 -5
- package/src/react/OptionsPanel.tsx +10 -10
- package/src/react/PlaygroundShell.tsx +64 -74
- package/src/react/ResultViewer.tsx +4 -4
- package/src/react/SourceEditor.tsx +2 -3
- package/src/sandbox/createSandboxRequire.ts +32 -9
|
@@ -15,7 +15,6 @@ import type { IPlaygroundDependencyProgress } from "../structures/IPlaygroundDep
|
|
|
15
15
|
import type { IPlaygroundShellProps } from "../structures/IPlaygroundShellProps";
|
|
16
16
|
import type { ITransformOptions } from "../structures/ITransformOptions";
|
|
17
17
|
import { ConsoleViewer } from "./ConsoleViewer";
|
|
18
|
-
import { createCompilerClient } from "./createCompilerClient";
|
|
19
18
|
import { DEFAULT_OPTION_TOGGLES } from "./DEFAULT_OPTION_TOGGLES";
|
|
20
19
|
import { DependencyProgressModal } from "./DependencyProgressModal";
|
|
21
20
|
import { DiagnosticsPanel } from "./DiagnosticsPanel";
|
|
@@ -24,6 +23,7 @@ import { LintPane } from "./LintPane";
|
|
|
24
23
|
import { OptionsPanel } from "./OptionsPanel";
|
|
25
24
|
import { ResultViewer } from "./ResultViewer";
|
|
26
25
|
import { SourceEditor } from "./SourceEditor";
|
|
26
|
+
import { createCompilerClient } from "./createCompilerClient";
|
|
27
27
|
|
|
28
28
|
const DEFAULT_OPTIONS: ITransformOptions = {
|
|
29
29
|
typia: true,
|
|
@@ -64,9 +64,7 @@ export function PlaygroundShell({
|
|
|
64
64
|
const [optionsOpen, setOptionsOpen] = useState(false);
|
|
65
65
|
const [running, setRunning] = useState(false);
|
|
66
66
|
const [shareToast, setShareToast] = useState(false);
|
|
67
|
-
const [consoleMessages, setConsoleMessages] = useState<IConsoleMessage[]>(
|
|
68
|
-
[],
|
|
69
|
-
);
|
|
67
|
+
const [consoleMessages, setConsoleMessages] = useState<IConsoleMessage[]>([]);
|
|
70
68
|
const [executing, setExecuting] = useState(false);
|
|
71
69
|
const [bootError, setBootError] = useState<unknown>(null);
|
|
72
70
|
const [bootPhase, setBootPhase] = useState<"booting" | "ready" | "failed">(
|
|
@@ -284,8 +282,7 @@ export function PlaygroundShell({
|
|
|
284
282
|
}, 2400);
|
|
285
283
|
return error;
|
|
286
284
|
} finally {
|
|
287
|
-
if (dependencyAbort.current === abort)
|
|
288
|
-
dependencyAbort.current = null;
|
|
285
|
+
if (dependencyAbort.current === abort) dependencyAbort.current = null;
|
|
289
286
|
}
|
|
290
287
|
});
|
|
291
288
|
dependencyInstallChain.current = task.then(() => {});
|
|
@@ -302,11 +299,7 @@ export function PlaygroundShell({
|
|
|
302
299
|
// pane is rendered. Re-running the wasm-heavy pipeline on every tab
|
|
303
300
|
// click would burn multiple seconds of work per click.
|
|
304
301
|
const run = useCallback(
|
|
305
|
-
async (
|
|
306
|
-
input: string,
|
|
307
|
-
opts: ITransformOptions,
|
|
308
|
-
version: number,
|
|
309
|
-
) => {
|
|
302
|
+
async (input: string, opts: ITransformOptions, version: number) => {
|
|
310
303
|
const epoch = ++compileEpoch.current;
|
|
311
304
|
setRunning(true);
|
|
312
305
|
try {
|
|
@@ -544,7 +537,12 @@ export function PlaygroundShell({
|
|
|
544
537
|
// React state, so including `source` here would re-create the
|
|
545
538
|
// callback per keystroke and propagate into the global keydown
|
|
546
539
|
// useEffect, churning event-listener add/remove every character.
|
|
547
|
-
}, [
|
|
540
|
+
}, [
|
|
541
|
+
createCompilerService,
|
|
542
|
+
executeBundle,
|
|
543
|
+
installDependenciesForSource,
|
|
544
|
+
options,
|
|
545
|
+
]);
|
|
548
546
|
|
|
549
547
|
const allDiagnostics = useMemo(() => {
|
|
550
548
|
const fromCompile: ICompilerService.IDiagnostic[] = [];
|
|
@@ -602,10 +600,10 @@ export function PlaygroundShell({
|
|
|
602
600
|
|
|
603
601
|
if (bootPhase === "failed") {
|
|
604
602
|
return (
|
|
605
|
-
<div className="flex
|
|
603
|
+
<div className="flex h-screen w-full flex-col items-center justify-center gap-5 bg-[#f7fbff] px-6 text-center text-[#102a43]">
|
|
606
604
|
<span className="text-red-400 text-3xl">⚠</span>
|
|
607
605
|
<h1 className="text-lg font-mono">Playground failed to boot.</h1>
|
|
608
|
-
<pre className="max-w-xl
|
|
606
|
+
<pre className="max-w-xl whitespace-pre-wrap break-words font-mono text-[12px] text-slate-500">
|
|
609
607
|
{(() => {
|
|
610
608
|
const e = bootError;
|
|
611
609
|
if (e instanceof Error) return `${e.name}: ${e.message}`;
|
|
@@ -639,7 +637,7 @@ export function PlaygroundShell({
|
|
|
639
637
|
}
|
|
640
638
|
})();
|
|
641
639
|
}}
|
|
642
|
-
className="px-5 py-2
|
|
640
|
+
className="rounded-md bg-[#3178c6] px-5 py-2 font-mono text-xs text-white shadow-[0_8px_22px_rgba(49,120,198,0.24)] transition-colors hover:bg-[#235a97]"
|
|
643
641
|
>
|
|
644
642
|
Retry
|
|
645
643
|
</button>
|
|
@@ -648,54 +646,28 @@ export function PlaygroundShell({
|
|
|
648
646
|
}
|
|
649
647
|
|
|
650
648
|
return (
|
|
651
|
-
<div className="flex
|
|
652
|
-
{/* ── Toolbar ── */}
|
|
653
|
-
<div className="flex items-center gap-3 px-4 py-2.5 border-b border-neutral-800/70 bg-neutral-950 shrink-0">
|
|
654
|
-
{brand}
|
|
655
|
-
<span className="text-neutral-700">/</span>
|
|
656
|
-
<span className="text-sm text-neutral-400">Playground</span>
|
|
657
|
-
|
|
658
|
-
<div className="ml-auto flex items-center gap-2">
|
|
659
|
-
<ExamplePicker
|
|
660
|
-
examples={examples}
|
|
661
|
-
onPick={onPickExample}
|
|
662
|
-
groupLabels={exampleGroupLabels}
|
|
663
|
-
/>
|
|
664
|
-
<button
|
|
665
|
-
onClick={() => setOptionsOpen((v) => !v)}
|
|
666
|
-
className="px-3 py-1.5 text-xs font-mono text-neutral-300 border border-neutral-800 rounded-md hover:border-neutral-600 hover:bg-neutral-900 transition-colors"
|
|
667
|
-
>
|
|
668
|
-
Options
|
|
669
|
-
</button>
|
|
670
|
-
<button
|
|
671
|
-
onClick={onReset}
|
|
672
|
-
className="px-3 py-1.5 text-xs font-mono text-neutral-400 hover:text-neutral-200 transition-colors"
|
|
673
|
-
>
|
|
674
|
-
Reset
|
|
675
|
-
</button>
|
|
676
|
-
<button
|
|
677
|
-
onClick={onShare}
|
|
678
|
-
className="px-3 py-1.5 text-xs font-mono text-neutral-900 bg-white rounded-md hover:shadow-[0_0_30px_rgba(255,255,255,0.2)] transition-shadow"
|
|
679
|
-
>
|
|
680
|
-
{shareToast ? "Copied ✓" : "Share"}
|
|
681
|
-
</button>
|
|
682
|
-
</div>
|
|
683
|
-
</div>
|
|
684
|
-
|
|
649
|
+
<div className="flex h-screen w-full flex-col overflow-hidden bg-white text-[#102a43]">
|
|
685
650
|
{sourceFromURL && (
|
|
686
|
-
<div className="shrink-0 px-4 py-1.5 text-[11px]
|
|
651
|
+
<div className="shrink-0 border-b border-amber-300 bg-amber-50 px-4 py-1.5 font-mono text-[11px] text-amber-800">
|
|
687
652
|
Source loaded from share URL. Hit Reset to return to the default
|
|
688
653
|
example.
|
|
689
654
|
</div>
|
|
690
655
|
)}
|
|
691
656
|
|
|
692
657
|
{shareWarn && (
|
|
693
|
-
<div className="shrink-0 px-4 py-1.5 text-[11px]
|
|
658
|
+
<div className="shrink-0 border-b border-amber-300 bg-amber-50 px-4 py-1.5 font-mono text-[11px] text-amber-800">
|
|
694
659
|
{shareWarn}
|
|
695
660
|
</div>
|
|
696
661
|
)}
|
|
697
662
|
|
|
698
|
-
<div className="flex items-center gap-0 border-b border-
|
|
663
|
+
<div className="flex shrink-0 flex-wrap items-center gap-0 border-b border-[#c7dff4] bg-white">
|
|
664
|
+
{brand ? (
|
|
665
|
+
<div className="flex items-center gap-2 border-r border-[#c7dff4] px-4 py-2">
|
|
666
|
+
{brand}
|
|
667
|
+
<span className="text-[#9db6cb]">/</span>
|
|
668
|
+
<span className="text-sm text-[#526b82]">Playground</span>
|
|
669
|
+
</div>
|
|
670
|
+
) : null}
|
|
699
671
|
{(
|
|
700
672
|
[
|
|
701
673
|
{ id: "javascript", label: "Compiled JS" },
|
|
@@ -707,29 +679,47 @@ export function PlaygroundShell({
|
|
|
707
679
|
onClick={() => setTarget(tab.id)}
|
|
708
680
|
className={`px-4 py-2 text-[12px] font-mono border-b-2 transition-colors ${
|
|
709
681
|
target === tab.id
|
|
710
|
-
? "
|
|
711
|
-
: "text-
|
|
682
|
+
? "border-[#3178c6] text-[#235a97]"
|
|
683
|
+
: "border-transparent text-slate-400 hover:text-[#3178c6]"
|
|
712
684
|
}`}
|
|
713
685
|
>
|
|
714
686
|
{tab.label}
|
|
715
687
|
</button>
|
|
716
688
|
))}
|
|
717
|
-
<div className="ml-auto
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
689
|
+
<div className="ml-auto flex w-full items-center justify-end gap-2 border-t border-[#d8e7f4] px-4 py-1.5 sm:w-auto sm:border-t-0 sm:pl-0">
|
|
690
|
+
<ExamplePicker
|
|
691
|
+
examples={examples}
|
|
692
|
+
onPick={onPickExample}
|
|
693
|
+
groupLabels={exampleGroupLabels}
|
|
694
|
+
/>
|
|
695
|
+
<button
|
|
696
|
+
onClick={() => setOptionsOpen((v) => !v)}
|
|
697
|
+
className="rounded-md border border-[#b9d5ee] bg-white px-3 py-1.5 font-mono text-xs text-[#235a97] transition-colors hover:border-[#3178c6] hover:bg-[#eaf4ff]"
|
|
698
|
+
>
|
|
699
|
+
Options
|
|
700
|
+
</button>
|
|
701
|
+
<button
|
|
702
|
+
onClick={onReset}
|
|
703
|
+
className="px-3 py-1.5 font-mono text-xs text-slate-500 transition-colors hover:text-[#235a97]"
|
|
704
|
+
>
|
|
705
|
+
Reset
|
|
706
|
+
</button>
|
|
707
|
+
<button
|
|
708
|
+
onClick={onShare}
|
|
709
|
+
className="rounded-md bg-[#3178c6] px-3 py-1.5 font-mono text-xs text-white shadow-[0_6px_16px_rgba(49,120,198,0.22)] transition-colors hover:bg-[#235a97]"
|
|
710
|
+
>
|
|
711
|
+
{shareToast ? "Copied ✓" : "Share"}
|
|
712
|
+
</button>
|
|
723
713
|
</div>
|
|
724
714
|
</div>
|
|
725
715
|
|
|
726
716
|
<div className="flex flex-1 min-h-0 flex-col md:flex-row">
|
|
727
|
-
<div className="flex-1 min-w-0
|
|
728
|
-
<div className="flex items-center justify-between
|
|
729
|
-
<span className="text-[11px]
|
|
717
|
+
<div className="flex h-1/2 min-w-0 flex-1 flex-col border-[#c7dff4] md:h-full md:border-r">
|
|
718
|
+
<div className="flex items-center justify-between border-b border-[#c7dff4] bg-[#eef6ff] px-4 py-1.5">
|
|
719
|
+
<span className="font-mono text-[11px] text-slate-500">
|
|
730
720
|
src/playground.ts
|
|
731
721
|
</span>
|
|
732
|
-
<span className="text-[10px]
|
|
722
|
+
<span className="font-mono text-[10px] text-slate-400">
|
|
733
723
|
{source.split("\n").length} lines
|
|
734
724
|
</span>
|
|
735
725
|
</div>
|
|
@@ -742,14 +732,14 @@ export function PlaygroundShell({
|
|
|
742
732
|
</div>
|
|
743
733
|
</div>
|
|
744
734
|
|
|
745
|
-
<div className="flex-1 min-w-0 flex flex-col
|
|
746
|
-
<div className="flex items-center justify-between
|
|
747
|
-
<span className="text-[11px]
|
|
735
|
+
<div className="flex h-1/2 min-w-0 flex-1 flex-col border-t border-[#c7dff4] md:h-full md:border-t-0">
|
|
736
|
+
<div className="flex items-center justify-between border-b border-[#c7dff4] bg-[#eef6ff] px-4 py-1.5">
|
|
737
|
+
<span className="font-mono text-[11px] text-slate-500">
|
|
748
738
|
{target === "javascript"
|
|
749
739
|
? resultCaption(options)
|
|
750
740
|
: "lint diagnostics"}
|
|
751
741
|
</span>
|
|
752
|
-
<span className="text-[10px]
|
|
742
|
+
<span className="font-mono text-[10px] text-slate-400">
|
|
753
743
|
{result?.type === "error" ? "error" : ""}
|
|
754
744
|
</span>
|
|
755
745
|
</div>
|
|
@@ -774,22 +764,22 @@ export function PlaygroundShell({
|
|
|
774
764
|
</div>
|
|
775
765
|
|
|
776
766
|
{bundleError && (
|
|
777
|
-
<div className="shrink-0 px-4 py-1.5 text-[11px]
|
|
767
|
+
<div className="shrink-0 border-t border-red-300 bg-red-50 px-4 py-1.5 font-mono text-[11px] text-red-700">
|
|
778
768
|
Bundle failed — {bundleError}
|
|
779
769
|
</div>
|
|
780
770
|
)}
|
|
781
771
|
|
|
782
772
|
{executeBundle && (
|
|
783
|
-
<div className="shrink-0 border-t border-
|
|
784
|
-
<div className="flex items-center justify-between px-4 py-1.5
|
|
785
|
-
<span className="text-[11px]
|
|
773
|
+
<div className="flex h-48 shrink-0 flex-col border-t border-[#c7dff4] bg-[#f7fbff]">
|
|
774
|
+
<div className="flex items-center justify-between border-b border-[#d8e7f4] px-4 py-1.5">
|
|
775
|
+
<span className="font-mono text-[11px] text-slate-500">
|
|
786
776
|
console output
|
|
787
777
|
</span>
|
|
788
778
|
<div className="flex items-center gap-2">
|
|
789
779
|
{consoleMessages.length > 0 && (
|
|
790
780
|
<button
|
|
791
781
|
onClick={() => setConsoleMessages([])}
|
|
792
|
-
className="px-2 py-1 text-[10px]
|
|
782
|
+
className="px-2 py-1 font-mono text-[10px] text-slate-500 transition-colors hover:text-[#235a97]"
|
|
793
783
|
>
|
|
794
784
|
Clear
|
|
795
785
|
</button>
|
|
@@ -797,7 +787,7 @@ export function PlaygroundShell({
|
|
|
797
787
|
<button
|
|
798
788
|
onClick={onExecute}
|
|
799
789
|
disabled={executing}
|
|
800
|
-
className="px-3 py-1 text-[11px]
|
|
790
|
+
className="rounded-md bg-[#3178c6] px-3 py-1 font-mono text-[11px] text-white transition-colors hover:bg-[#235a97] disabled:opacity-50"
|
|
801
791
|
title="Cmd/Ctrl+Enter"
|
|
802
792
|
>
|
|
803
793
|
▶ {executing ? "Executing…" : "Execute"}
|
|
@@ -9,8 +9,8 @@ interface ResultViewerProps {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
* Read-only Monaco pane used to render the compiled / transformed output
|
|
13
|
-
*
|
|
12
|
+
* Read-only Monaco pane used to render the compiled / transformed output with a
|
|
13
|
+
* copy button. Wraps `<Editor readOnly>` and adds the toast UI.
|
|
14
14
|
*/
|
|
15
15
|
export function ResultViewer({ language, value }: ResultViewerProps) {
|
|
16
16
|
const [copied, setCopied] = useState(false);
|
|
@@ -39,7 +39,7 @@ export function ResultViewer({ language, value }: ResultViewerProps) {
|
|
|
39
39
|
{value && (
|
|
40
40
|
<button
|
|
41
41
|
onClick={onCopy}
|
|
42
|
-
className="absolute top-2
|
|
42
|
+
className="absolute right-3 top-2 z-10 rounded-md border border-[#b9d5ee] bg-white/90 px-2 py-1 font-mono text-[10px] text-[#235a97] shadow-sm transition-colors hover:bg-[#eaf4ff]"
|
|
43
43
|
>
|
|
44
44
|
{copied ? "Copied ✓" : "Copy"}
|
|
45
45
|
</button>
|
|
@@ -47,7 +47,7 @@ export function ResultViewer({ language, value }: ResultViewerProps) {
|
|
|
47
47
|
<Editor
|
|
48
48
|
height="100%"
|
|
49
49
|
language={language}
|
|
50
|
-
theme="vs
|
|
50
|
+
theme="vs"
|
|
51
51
|
value={value}
|
|
52
52
|
path={`output.${
|
|
53
53
|
language === "typescript"
|
|
@@ -45,8 +45,7 @@ export function SourceEditor({
|
|
|
45
45
|
tsd.setCompilerOptions({
|
|
46
46
|
target: monaco.languages.typescript.ScriptTarget.ESNext,
|
|
47
47
|
module: monaco.languages.typescript.ModuleKind.ESNext,
|
|
48
|
-
moduleResolution:
|
|
49
|
-
monaco.languages.typescript.ModuleResolutionKind.NodeJs,
|
|
48
|
+
moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
|
|
50
49
|
esModuleInterop: DEFAULT_PLAYGROUND_COMPILER_OPTIONS.esModuleInterop,
|
|
51
50
|
strict: DEFAULT_PLAYGROUND_COMPILER_OPTIONS.strict,
|
|
52
51
|
experimentalDecorators:
|
|
@@ -73,7 +72,7 @@ export function SourceEditor({
|
|
|
73
72
|
<Editor
|
|
74
73
|
height="100%"
|
|
75
74
|
defaultLanguage="typescript"
|
|
76
|
-
theme="vs
|
|
75
|
+
theme="vs"
|
|
77
76
|
value={value}
|
|
78
77
|
onChange={(v) => onChange(v ?? "")}
|
|
79
78
|
onMount={handleMount}
|
|
@@ -64,15 +64,13 @@ export function createSandboxRequire(
|
|
|
64
64
|
return null;
|
|
65
65
|
}
|
|
66
66
|
if (subpath === null) {
|
|
67
|
-
// bare "name": honor exports
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
return has(resolved) ? resolved : null;
|
|
75
|
-
}
|
|
67
|
+
// bare "name": honor the root `exports` entry (string, subpath table
|
|
68
|
+
// "." key, or bare condition map) → CJS target, else main, else index.
|
|
69
|
+
const root = rootExportTarget(pj.exports);
|
|
70
|
+
const r = pickConditionalExport(root);
|
|
71
|
+
if (r) {
|
|
72
|
+
const resolved = `${pkg}/${stripDotSlash(r)}`;
|
|
73
|
+
return has(resolved) ? resolved : null;
|
|
76
74
|
}
|
|
77
75
|
if (typeof pj.main === "string") {
|
|
78
76
|
return tryPaths(
|
|
@@ -220,6 +218,31 @@ export function createSandboxRequire(
|
|
|
220
218
|
};
|
|
221
219
|
}
|
|
222
220
|
|
|
221
|
+
/**
|
|
222
|
+
* Reduce a `package.json` `exports` field to the value describing its ROOT
|
|
223
|
+
* (".") entry, ready for {@link pickConditionalExport}. Node accepts three valid
|
|
224
|
+
* root shapes and they must all resolve consistently:
|
|
225
|
+
*
|
|
226
|
+
* - A bare string target — `"exports": "./index.cjs"`;
|
|
227
|
+
* - A subpath table keyed by "." — `{ ".": <target>, "./sub": ... }`;
|
|
228
|
+
* - A bare condition map whose keys are all conditions, not subpaths — `{
|
|
229
|
+
* "require": "./index.cjs", "default": "./index.cjs" }`.
|
|
230
|
+
*
|
|
231
|
+
* Node forbids mixing subpath keys with condition keys, so the presence of any
|
|
232
|
+
* "."-prefixed key decides the interpretation: a subpath table exposes its root
|
|
233
|
+
* as `exports["."]`, while a condition map is itself the root target. Returns
|
|
234
|
+
* null when `exports` is absent, empty, or describes no root entry.
|
|
235
|
+
*/
|
|
236
|
+
function rootExportTarget(exports: unknown): unknown {
|
|
237
|
+
if (typeof exports === "string") return exports;
|
|
238
|
+
if (!exports || typeof exports !== "object") return null;
|
|
239
|
+
const obj = exports as Record<string, unknown>;
|
|
240
|
+
const keys = Object.keys(obj);
|
|
241
|
+
if (keys.length === 0) return null;
|
|
242
|
+
const hasSubpathKey = keys.some((k) => k === "." || k.startsWith("./"));
|
|
243
|
+
return hasSubpathKey ? obj["."] : obj;
|
|
244
|
+
}
|
|
245
|
+
|
|
223
246
|
function pickConditionalExport(value: unknown): string | null {
|
|
224
247
|
if (typeof value === "string") return value;
|
|
225
248
|
if (value && typeof value === "object") {
|