@ttsc/playground 0.14.0-dev.20260528.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 +21 -0
- package/README.md +230 -0
- package/lib/src/compiler/DEFAULT_ENTRY_FILE.d.ts +2 -0
- package/lib/src/compiler/DEFAULT_ENTRY_FILE.js +6 -0
- package/lib/src/compiler/DEFAULT_ENTRY_FILE.js.map +1 -0
- package/lib/src/compiler/DEFAULT_LINT_PLUGIN_NAME.d.ts +5 -0
- package/lib/src/compiler/DEFAULT_LINT_PLUGIN_NAME.js +9 -0
- package/lib/src/compiler/DEFAULT_LINT_PLUGIN_NAME.js.map +1 -0
- package/lib/src/compiler/DEFAULT_PLAYGROUND_COMPILER_OPTIONS.d.ts +19 -0
- package/lib/src/compiler/DEFAULT_PLAYGROUND_COMPILER_OPTIONS.js +23 -0
- package/lib/src/compiler/DEFAULT_PLAYGROUND_COMPILER_OPTIONS.js.map +1 -0
- package/lib/src/compiler/DEFAULT_TSCONFIG_PATH.d.ts +2 -0
- package/lib/src/compiler/DEFAULT_TSCONFIG_PATH.js +6 -0
- package/lib/src/compiler/DEFAULT_TSCONFIG_PATH.js.map +1 -0
- package/lib/src/compiler/DEFAULT_TYPIA_PLUGIN_NAME.d.ts +6 -0
- package/lib/src/compiler/DEFAULT_TYPIA_PLUGIN_NAME.js +10 -0
- package/lib/src/compiler/DEFAULT_TYPIA_PLUGIN_NAME.js.map +1 -0
- package/lib/src/compiler/DEFAULT_WORK_DIR.d.ts +2 -0
- package/lib/src/compiler/DEFAULT_WORK_DIR.js +6 -0
- package/lib/src/compiler/DEFAULT_WORK_DIR.js.map +1 -0
- package/lib/src/compiler/buildTsconfigJSON.d.ts +9 -0
- package/lib/src/compiler/buildTsconfigJSON.js +24 -0
- package/lib/src/compiler/buildTsconfigJSON.js.map +1 -0
- package/lib/src/compiler/createTypiaSourcePackMount.d.ts +11 -0
- package/lib/src/compiler/createTypiaSourcePackMount.js +29 -0
- package/lib/src/compiler/createTypiaSourcePackMount.js.map +1 -0
- package/lib/src/compiler/createWorkerCompiler.d.ts +25 -0
- package/lib/src/compiler/createWorkerCompiler.js +227 -0
- package/lib/src/compiler/createWorkerCompiler.js.map +1 -0
- package/lib/src/compiler/installDependenciesIntoMemFS.d.ts +12 -0
- package/lib/src/compiler/installDependenciesIntoMemFS.js +25 -0
- package/lib/src/compiler/installDependenciesIntoMemFS.js.map +1 -0
- package/lib/src/compiler/installTypiaSourcePack.d.ts +11 -0
- package/lib/src/compiler/installTypiaSourcePack.js +21 -0
- package/lib/src/compiler/installTypiaSourcePack.js.map +1 -0
- package/lib/src/compiler/internal/joinUnder.d.ts +2 -0
- package/lib/src/compiler/internal/joinUnder.js +10 -0
- package/lib/src/compiler/internal/joinUnder.js.map +1 -0
- package/lib/src/compiler/internal/parseLintDiagnostics.d.ts +6 -0
- package/lib/src/compiler/internal/parseLintDiagnostics.js +57 -0
- package/lib/src/compiler/internal/parseLintDiagnostics.js.map +1 -0
- package/lib/src/compiler/internal/safeParseTypiaTransform.d.ts +11 -0
- package/lib/src/compiler/internal/safeParseTypiaTransform.js +21 -0
- package/lib/src/compiler/internal/safeParseTypiaTransform.js.map +1 -0
- package/lib/src/compiler/lineColumnOf.d.ts +5 -0
- package/lib/src/compiler/lineColumnOf.js +15 -0
- package/lib/src/compiler/lineColumnOf.js.map +1 -0
- package/lib/src/compiler/loadTypiaSourcePack.d.ts +8 -0
- package/lib/src/compiler/loadTypiaSourcePack.js +33 -0
- package/lib/src/compiler/loadTypiaSourcePack.js.map +1 -0
- package/lib/src/compiler/mapDiagnostic.d.ts +9 -0
- package/lib/src/compiler/mapDiagnostic.js +22 -0
- package/lib/src/compiler/mapDiagnostic.js.map +1 -0
- package/lib/src/compiler/normalizeError.d.ts +2 -0
- package/lib/src/compiler/normalizeError.js +14 -0
- package/lib/src/compiler/normalizeError.js.map +1 -0
- package/lib/src/compiler/normalizeNodeModulePath.d.ts +8 -0
- package/lib/src/compiler/normalizeNodeModulePath.js +19 -0
- package/lib/src/compiler/normalizeNodeModulePath.js.map +1 -0
- package/lib/src/compiler/pickEmittedJS.d.ts +6 -0
- package/lib/src/compiler/pickEmittedJS.js +26 -0
- package/lib/src/compiler/pickEmittedJS.js.map +1 -0
- package/lib/src/index.d.ts +47 -0
- package/lib/src/index.js +104 -0
- package/lib/src/index.js.map +1 -0
- package/lib/src/npm/BUILT_IN_PLAYGROUND_PACKAGES.d.ts +6 -0
- package/lib/src/npm/BUILT_IN_PLAYGROUND_PACKAGES.js +15 -0
- package/lib/src/npm/BUILT_IN_PLAYGROUND_PACKAGES.js.map +1 -0
- package/lib/src/npm/collectExternalPackageNames.d.ts +5 -0
- package/lib/src/npm/collectExternalPackageNames.js +34 -0
- package/lib/src/npm/collectExternalPackageNames.js.map +1 -0
- package/lib/src/npm/installPlaygroundDependencies.d.ts +13 -0
- package/lib/src/npm/installPlaygroundDependencies.js +121 -0
- package/lib/src/npm/installPlaygroundDependencies.js.map +1 -0
- package/lib/src/npm/internal/npmRegistry.d.ts +57 -0
- package/lib/src/npm/internal/npmRegistry.js +216 -0
- package/lib/src/npm/internal/npmRegistry.js.map +1 -0
- package/lib/src/npm/packageNameFromSpecifier.d.ts +7 -0
- package/lib/src/npm/packageNameFromSpecifier.js +66 -0
- package/lib/src/npm/packageNameFromSpecifier.js.map +1 -0
- package/lib/src/react/ConsoleViewer.d.ts +7 -0
- package/lib/src/react/ConsoleViewer.js +57 -0
- package/lib/src/react/ConsoleViewer.js.map +1 -0
- package/lib/src/react/DEFAULT_OPTION_TOGGLES.d.ts +3 -0
- package/lib/src/react/DEFAULT_OPTION_TOGGLES.js +17 -0
- package/lib/src/react/DEFAULT_OPTION_TOGGLES.js.map +1 -0
- package/lib/src/react/DependencyProgressModal.d.ts +7 -0
- package/lib/src/react/DependencyProgressModal.js +16 -0
- package/lib/src/react/DependencyProgressModal.js.map +1 -0
- package/lib/src/react/DiagnosticsPanel.d.ts +4 -0
- package/lib/src/react/DiagnosticsPanel.js +15 -0
- package/lib/src/react/DiagnosticsPanel.js.map +1 -0
- package/lib/src/react/ExamplePicker.d.ts +9 -0
- package/lib/src/react/ExamplePicker.js +43 -0
- package/lib/src/react/ExamplePicker.js.map +1 -0
- package/lib/src/react/LintPane.d.ts +9 -0
- package/lib/src/react/LintPane.js +17 -0
- package/lib/src/react/LintPane.js.map +1 -0
- package/lib/src/react/OptionsPanel.d.ts +13 -0
- package/lib/src/react/OptionsPanel.js +43 -0
- package/lib/src/react/OptionsPanel.js.map +1 -0
- package/lib/src/react/PlaygroundShell.d.ts +2 -0
- package/lib/src/react/PlaygroundShell.js +609 -0
- package/lib/src/react/PlaygroundShell.js.map +1 -0
- package/lib/src/react/ResultViewer.d.ts +10 -0
- package/lib/src/react/ResultViewer.js +49 -0
- package/lib/src/react/ResultViewer.js.map +1 -0
- package/lib/src/react/SourceEditor.d.ts +2 -0
- package/lib/src/react/SourceEditor.js +74 -0
- package/lib/src/react/SourceEditor.js.map +1 -0
- package/lib/src/react/createCompilerClient.d.ts +14 -0
- package/lib/src/react/createCompilerClient.js +77 -0
- package/lib/src/react/createCompilerClient.js.map +1 -0
- package/lib/src/sandbox/createSandboxRequire.d.ts +11 -0
- package/lib/src/sandbox/createSandboxRequire.js +211 -0
- package/lib/src/sandbox/createSandboxRequire.js.map +1 -0
- package/lib/src/sandbox/loadTypiaRuntimePack.d.ts +7 -0
- package/lib/src/sandbox/loadTypiaRuntimePack.js +35 -0
- package/lib/src/sandbox/loadTypiaRuntimePack.js.map +1 -0
- package/lib/src/structures/IBuildTsconfigOptions.d.ts +19 -0
- package/lib/src/structures/IBuildTsconfigOptions.js +3 -0
- package/lib/src/structures/IBuildTsconfigOptions.js.map +1 -0
- package/lib/src/structures/ICompilerService.d.ts +83 -0
- package/lib/src/structures/ICompilerService.js +3 -0
- package/lib/src/structures/ICompilerService.js.map +1 -0
- package/lib/src/structures/IConsoleMessage.d.ts +10 -0
- package/lib/src/structures/IConsoleMessage.js +3 -0
- package/lib/src/structures/IConsoleMessage.js.map +1 -0
- package/lib/src/structures/ICreateCompilerClientOptions.d.ts +13 -0
- package/lib/src/structures/ICreateCompilerClientOptions.js +3 -0
- package/lib/src/structures/ICreateCompilerClientOptions.js.map +1 -0
- package/lib/src/structures/ICreateWorkerCompilerOptions.d.ts +35 -0
- package/lib/src/structures/ICreateWorkerCompilerOptions.js +3 -0
- package/lib/src/structures/ICreateWorkerCompilerOptions.js.map +1 -0
- package/lib/src/structures/IInstallTypiaSourcePackOptions.d.ts +15 -0
- package/lib/src/structures/IInstallTypiaSourcePackOptions.js +3 -0
- package/lib/src/structures/IInstallTypiaSourcePackOptions.js.map +1 -0
- package/lib/src/structures/ILintPluginConfig.d.ts +5 -0
- package/lib/src/structures/ILintPluginConfig.js +3 -0
- package/lib/src/structures/ILintPluginConfig.js.map +1 -0
- package/lib/src/structures/IOptionToggle.d.ts +12 -0
- package/lib/src/structures/IOptionToggle.js +3 -0
- package/lib/src/structures/IOptionToggle.js.map +1 -0
- package/lib/src/structures/IPlaygroundDependencyInstallOptions.d.ts +16 -0
- package/lib/src/structures/IPlaygroundDependencyInstallOptions.js +3 -0
- package/lib/src/structures/IPlaygroundDependencyInstallOptions.js.map +1 -0
- package/lib/src/structures/IPlaygroundDependencyInstallResult.d.ts +20 -0
- package/lib/src/structures/IPlaygroundDependencyInstallResult.js +3 -0
- package/lib/src/structures/IPlaygroundDependencyInstallResult.js.map +1 -0
- package/lib/src/structures/IPlaygroundDependencyPackage.d.ts +8 -0
- package/lib/src/structures/IPlaygroundDependencyPackage.js +3 -0
- package/lib/src/structures/IPlaygroundDependencyPackage.js.map +1 -0
- package/lib/src/structures/IPlaygroundDependencyProgress.d.ts +10 -0
- package/lib/src/structures/IPlaygroundDependencyProgress.js +3 -0
- package/lib/src/structures/IPlaygroundDependencyProgress.js.map +1 -0
- package/lib/src/structures/IPlaygroundDependencyProgressPhase.d.ts +2 -0
- package/lib/src/structures/IPlaygroundDependencyProgressPhase.js +3 -0
- package/lib/src/structures/IPlaygroundDependencyProgressPhase.js.map +1 -0
- package/lib/src/structures/IPlaygroundExample.d.ts +16 -0
- package/lib/src/structures/IPlaygroundExample.js +3 -0
- package/lib/src/structures/IPlaygroundExample.js.map +1 -0
- package/lib/src/structures/IPlaygroundShellProps.d.ts +64 -0
- package/lib/src/structures/IPlaygroundShellProps.js +3 -0
- package/lib/src/structures/IPlaygroundShellProps.js.map +1 -0
- package/lib/src/structures/ISourceEditorProps.d.ts +18 -0
- package/lib/src/structures/ISourceEditorProps.js +3 -0
- package/lib/src/structures/ISourceEditorProps.js.map +1 -0
- package/lib/src/structures/ITransformOptions.d.ts +15 -0
- package/lib/src/structures/ITransformOptions.js +3 -0
- package/lib/src/structures/ITransformOptions.js.map +1 -0
- package/lib/src/structures/ITypiaPluginConfig.d.ts +19 -0
- package/lib/src/structures/ITypiaPluginConfig.js +3 -0
- package/lib/src/structures/ITypiaPluginConfig.js.map +1 -0
- package/lib/src/structures/index.d.ts +18 -0
- package/lib/src/structures/index.js +35 -0
- package/lib/src/structures/index.js.map +1 -0
- package/package.json +66 -0
- package/src/compiler/DEFAULT_ENTRY_FILE.ts +2 -0
- package/src/compiler/DEFAULT_LINT_PLUGIN_NAME.ts +5 -0
- package/src/compiler/DEFAULT_PLAYGROUND_COMPILER_OPTIONS.ts +19 -0
- package/src/compiler/DEFAULT_TSCONFIG_PATH.ts +2 -0
- package/src/compiler/DEFAULT_TYPIA_PLUGIN_NAME.ts +6 -0
- package/src/compiler/DEFAULT_WORK_DIR.ts +2 -0
- package/src/compiler/buildTsconfigJSON.ts +22 -0
- package/src/compiler/createTypiaSourcePackMount.ts +32 -0
- package/src/compiler/createWorkerCompiler.ts +274 -0
- package/src/compiler/installDependenciesIntoMemFS.ts +28 -0
- package/src/compiler/installTypiaSourcePack.ts +24 -0
- package/src/compiler/internal/joinUnder.ts +5 -0
- package/src/compiler/internal/parseLintDiagnostics.ts +61 -0
- package/src/compiler/internal/safeParseTypiaTransform.ts +24 -0
- package/src/compiler/lineColumnOf.ts +14 -0
- package/src/compiler/loadTypiaSourcePack.ts +37 -0
- package/src/compiler/mapDiagnostic.ts +26 -0
- package/src/compiler/normalizeError.ts +12 -0
- package/src/compiler/normalizeNodeModulePath.ts +13 -0
- package/src/compiler/pickEmittedJS.ts +23 -0
- package/src/index.ts +57 -0
- package/src/npm/BUILT_IN_PLAYGROUND_PACKAGES.ts +11 -0
- package/src/npm/collectExternalPackageNames.ts +34 -0
- package/src/npm/installPlaygroundDependencies.ts +163 -0
- package/src/npm/internal/npmRegistry.ts +288 -0
- package/src/npm/packageNameFromSpecifier.ts +61 -0
- package/src/react/ConsoleViewer.tsx +121 -0
- package/src/react/DEFAULT_OPTION_TOGGLES.ts +15 -0
- package/src/react/DependencyProgressModal.tsx +73 -0
- package/src/react/DiagnosticsPanel.tsx +74 -0
- package/src/react/ExamplePicker.tsx +93 -0
- package/src/react/LintPane.tsx +58 -0
- package/src/react/OptionsPanel.tsx +132 -0
- package/src/react/PlaygroundShell.tsx +864 -0
- package/src/react/ResultViewer.tsx +75 -0
- package/src/react/SourceEditor.tsx +96 -0
- package/src/react/createCompilerClient.ts +79 -0
- package/src/sandbox/createSandboxRequire.ts +275 -0
- package/src/sandbox/loadTypiaRuntimePack.ts +36 -0
- package/src/structures/IBuildTsconfigOptions.ts +19 -0
- package/src/structures/ICompilerService.ts +95 -0
- package/src/structures/IConsoleMessage.ts +10 -0
- package/src/structures/ICreateCompilerClientOptions.ts +13 -0
- package/src/structures/ICreateWorkerCompilerOptions.ts +40 -0
- package/src/structures/IInstallTypiaSourcePackOptions.ts +15 -0
- package/src/structures/ILintPluginConfig.ts +5 -0
- package/src/structures/IOptionToggle.ts +12 -0
- package/src/structures/IPlaygroundDependencyInstallOptions.ts +17 -0
- package/src/structures/IPlaygroundDependencyInstallResult.ts +21 -0
- package/src/structures/IPlaygroundDependencyPackage.ts +8 -0
- package/src/structures/IPlaygroundDependencyProgress.ts +11 -0
- package/src/structures/IPlaygroundDependencyProgressPhase.ts +9 -0
- package/src/structures/IPlaygroundExample.ts +16 -0
- package/src/structures/IPlaygroundShellProps.ts +77 -0
- package/src/structures/ISourceEditorProps.ts +18 -0
- package/src/structures/ITransformOptions.ts +15 -0
- package/src/structures/ITypiaPluginConfig.ts +20 -0
- package/src/structures/index.ts +18 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Best-effort error normalization for tgrid transport. */
|
|
2
|
+
export function normalizeError(error: unknown): unknown {
|
|
3
|
+
if (error instanceof Error)
|
|
4
|
+
return { name: error.name, message: error.message, stack: error.stack };
|
|
5
|
+
if (
|
|
6
|
+
error &&
|
|
7
|
+
typeof error === "object" &&
|
|
8
|
+
"message" in (error as Record<string, unknown>)
|
|
9
|
+
)
|
|
10
|
+
return error;
|
|
11
|
+
return { name: "Error", message: String(error) };
|
|
12
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Accept either `node_modules/...` or `/node_modules/...` (or backslash
|
|
3
|
+
* variants); reject anything that escapes the node_modules root via `..`.
|
|
4
|
+
*
|
|
5
|
+
* Returns `null` for paths that don't match the expected shape — the caller
|
|
6
|
+
* skips those entries instead of writing them to the MemFS.
|
|
7
|
+
*/
|
|
8
|
+
export function normalizeNodeModulePath(path: string): string | null {
|
|
9
|
+
const normalized = path.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
10
|
+
if (!normalized.startsWith("node_modules/")) return null;
|
|
11
|
+
if (normalized.split("/").some((segment) => segment === "..")) return null;
|
|
12
|
+
return normalized;
|
|
13
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pick the most likely emitted JavaScript file from a compile result's output
|
|
3
|
+
* map. Tries common paths first, then falls back to the first `.js` entry.
|
|
4
|
+
* Returns null when no `.js` was emitted.
|
|
5
|
+
*/
|
|
6
|
+
export function pickEmittedJS(
|
|
7
|
+
output: Record<string, string>,
|
|
8
|
+
entryFile: string,
|
|
9
|
+
): string | null {
|
|
10
|
+
const base = entryFile.replace(/\.[cm]?tsx?$/i, ".js");
|
|
11
|
+
const candidates = [
|
|
12
|
+
`dist/${base}`,
|
|
13
|
+
`dist/src/${base}`,
|
|
14
|
+
`src/${base}`,
|
|
15
|
+
base,
|
|
16
|
+
];
|
|
17
|
+
for (const key of candidates) {
|
|
18
|
+
if (output[key] !== undefined) return output[key];
|
|
19
|
+
}
|
|
20
|
+
const jsKeys = Object.keys(output).filter((k) => k.endsWith(".js"));
|
|
21
|
+
if (jsKeys.length > 0) return output[jsKeys[0]!] ?? null;
|
|
22
|
+
return null;
|
|
23
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@ttsc/playground` — reusable Web Worker + React scaffolding for in-browser
|
|
3
|
+
* ttsc playgrounds built on `@ttsc/wasm`.
|
|
4
|
+
*
|
|
5
|
+
* The package is organized by concern: the `compiler/*` modules implement the
|
|
6
|
+
* worker-side `ICompilerService` contract; the `npm/*` modules install
|
|
7
|
+
* on-the-fly npm dependencies into the wasm MemFS; the `sandbox/*` modules
|
|
8
|
+
* drive an in-page `new Function` execute sandbox; the `react/*` modules
|
|
9
|
+
* render the shell, editor, and result panes.
|
|
10
|
+
*
|
|
11
|
+
* The package root re-exports everything; sub-path imports are not needed.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
// ── compiler / worker core ────────────────────────────────────────────────
|
|
15
|
+
export { buildTsconfigJSON } from "./compiler/buildTsconfigJSON";
|
|
16
|
+
export { createTypiaSourcePackMount } from "./compiler/createTypiaSourcePackMount";
|
|
17
|
+
export { createWorkerCompiler } from "./compiler/createWorkerCompiler";
|
|
18
|
+
export { DEFAULT_ENTRY_FILE } from "./compiler/DEFAULT_ENTRY_FILE";
|
|
19
|
+
export { DEFAULT_LINT_PLUGIN_NAME } from "./compiler/DEFAULT_LINT_PLUGIN_NAME";
|
|
20
|
+
export { DEFAULT_PLAYGROUND_COMPILER_OPTIONS } from "./compiler/DEFAULT_PLAYGROUND_COMPILER_OPTIONS";
|
|
21
|
+
export { DEFAULT_TSCONFIG_PATH } from "./compiler/DEFAULT_TSCONFIG_PATH";
|
|
22
|
+
export { DEFAULT_TYPIA_PLUGIN_NAME } from "./compiler/DEFAULT_TYPIA_PLUGIN_NAME";
|
|
23
|
+
export { DEFAULT_WORK_DIR } from "./compiler/DEFAULT_WORK_DIR";
|
|
24
|
+
export { installDependenciesIntoMemFS } from "./compiler/installDependenciesIntoMemFS";
|
|
25
|
+
export { installTypiaSourcePack } from "./compiler/installTypiaSourcePack";
|
|
26
|
+
export { lineColumnOf } from "./compiler/lineColumnOf";
|
|
27
|
+
export { loadTypiaSourcePack } from "./compiler/loadTypiaSourcePack";
|
|
28
|
+
export { mapDiagnostic } from "./compiler/mapDiagnostic";
|
|
29
|
+
export { normalizeError } from "./compiler/normalizeError";
|
|
30
|
+
export { normalizeNodeModulePath } from "./compiler/normalizeNodeModulePath";
|
|
31
|
+
export { pickEmittedJS } from "./compiler/pickEmittedJS";
|
|
32
|
+
|
|
33
|
+
// ── npm-from-the-browser dependency installer ─────────────────────────────
|
|
34
|
+
export { BUILT_IN_PLAYGROUND_PACKAGES } from "./npm/BUILT_IN_PLAYGROUND_PACKAGES";
|
|
35
|
+
export { collectExternalPackageNames } from "./npm/collectExternalPackageNames";
|
|
36
|
+
export { installPlaygroundDependencies } from "./npm/installPlaygroundDependencies";
|
|
37
|
+
export { packageNameFromSpecifier } from "./npm/packageNameFromSpecifier";
|
|
38
|
+
|
|
39
|
+
// ── typia runtime sandbox (CJS-flavoured require over a pack JSON) ────────
|
|
40
|
+
export { createSandboxRequire } from "./sandbox/createSandboxRequire";
|
|
41
|
+
export { loadTypiaRuntimePack } from "./sandbox/loadTypiaRuntimePack";
|
|
42
|
+
|
|
43
|
+
// ── React UI surface ──────────────────────────────────────────────────────
|
|
44
|
+
export { ConsoleViewer } from "./react/ConsoleViewer";
|
|
45
|
+
export { createCompilerClient } from "./react/createCompilerClient";
|
|
46
|
+
export { DEFAULT_OPTION_TOGGLES } from "./react/DEFAULT_OPTION_TOGGLES";
|
|
47
|
+
export { DependencyProgressModal } from "./react/DependencyProgressModal";
|
|
48
|
+
export { DiagnosticsPanel } from "./react/DiagnosticsPanel";
|
|
49
|
+
export { ExamplePicker } from "./react/ExamplePicker";
|
|
50
|
+
export { LintPane } from "./react/LintPane";
|
|
51
|
+
export { OptionsPanel } from "./react/OptionsPanel";
|
|
52
|
+
export { PlaygroundShell } from "./react/PlaygroundShell";
|
|
53
|
+
export { ResultViewer } from "./react/ResultViewer";
|
|
54
|
+
export { SourceEditor } from "./react/SourceEditor";
|
|
55
|
+
|
|
56
|
+
// ── structured types ──────────────────────────────────────────────────────
|
|
57
|
+
export * from "./structures/index";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Packages the typia source pack already mounts inside the wasm. The npm
|
|
3
|
+
* installer skips these so it doesn't try to fetch typia / @typia/* from
|
|
4
|
+
* the registry on top of the mounted source tree.
|
|
5
|
+
*/
|
|
6
|
+
export const BUILT_IN_PLAYGROUND_PACKAGES = [
|
|
7
|
+
"typia",
|
|
8
|
+
"@typia/interface",
|
|
9
|
+
"@typia/utils",
|
|
10
|
+
"@standard-schema/spec",
|
|
11
|
+
] as const;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { BUILT_IN_PLAYGROUND_PACKAGES } from "./BUILT_IN_PLAYGROUND_PACKAGES";
|
|
2
|
+
import { packageNameFromSpecifier } from "./packageNameFromSpecifier";
|
|
3
|
+
|
|
4
|
+
const MODULE_SPECIFIER_REGEXP =
|
|
5
|
+
/\b(?:import|export)\s+(?:type\s+)?(?:[^"'()]*?\s+from\s*)?["']([^"']+)["']|import\s*\(\s*["']([^"']+)["']\s*\)|require\s*\(\s*["']([^"']+)["']\s*\)/g;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Scan `source` for `import` / `require` specifiers and return the unique
|
|
9
|
+
* sorted list of bare npm package names that are not in `ignoredPackages`.
|
|
10
|
+
*/
|
|
11
|
+
export function collectExternalPackageNames(
|
|
12
|
+
source: string,
|
|
13
|
+
ignoredPackages: Iterable<string> = BUILT_IN_PLAYGROUND_PACKAGES,
|
|
14
|
+
): string[] {
|
|
15
|
+
const ignored = new Set(ignoredPackages);
|
|
16
|
+
const found = new Set<string>();
|
|
17
|
+
for (const specifier of collectModuleSpecifiers(source)) {
|
|
18
|
+
const packageName = packageNameFromSpecifier(specifier);
|
|
19
|
+
if (packageName && !ignored.has(packageName)) found.add(packageName);
|
|
20
|
+
}
|
|
21
|
+
return [...found].sort();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function collectModuleSpecifiers(source: string): string[] {
|
|
25
|
+
const out: string[] = [];
|
|
26
|
+
MODULE_SPECIFIER_REGEXP.lastIndex = 0;
|
|
27
|
+
for (;;) {
|
|
28
|
+
const match = MODULE_SPECIFIER_REGEXP.exec(source);
|
|
29
|
+
if (!match) break;
|
|
30
|
+
const specifier = match[1] ?? match[2] ?? match[3];
|
|
31
|
+
if (specifier) out.push(specifier);
|
|
32
|
+
}
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import type { IPlaygroundDependencyInstallOptions } from "../structures/IPlaygroundDependencyInstallOptions";
|
|
2
|
+
import type { IPlaygroundDependencyInstallResult } from "../structures/IPlaygroundDependencyInstallResult";
|
|
3
|
+
import type { IPlaygroundDependencyProgressPhase } from "../structures/IPlaygroundDependencyProgressPhase";
|
|
4
|
+
import { BUILT_IN_PLAYGROUND_PACKAGES } from "./BUILT_IN_PLAYGROUND_PACKAGES";
|
|
5
|
+
import {
|
|
6
|
+
DECLARATION_FILE_REGEXP,
|
|
7
|
+
downloadTarball,
|
|
8
|
+
enqueuePackageDependencies,
|
|
9
|
+
fetchNpmMetadata,
|
|
10
|
+
type IQueueItem,
|
|
11
|
+
mountPackageFiles,
|
|
12
|
+
selectVersion,
|
|
13
|
+
throwIfAborted,
|
|
14
|
+
toTypesPackageName,
|
|
15
|
+
unpackNpmTarball,
|
|
16
|
+
} from "./internal/npmRegistry";
|
|
17
|
+
|
|
18
|
+
const DEFAULT_MAX_PACKAGES = 48;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Resolve, download, and unpack a set of npm packages directly inside the
|
|
22
|
+
* browser, returning their files keyed for the wasm-side MemFS, Monaco's
|
|
23
|
+
* extra-libs registry, and the in-page execute sandbox `require`.
|
|
24
|
+
*
|
|
25
|
+
* Transitive dependencies are followed via the resolved `package.json`'s
|
|
26
|
+
* `dependencies` and (non-optional) `peerDependencies` fields. The walk is
|
|
27
|
+
* bounded by `maxPackages` to keep a single keystroke from exhausting the
|
|
28
|
+
* tab's network/memory budget.
|
|
29
|
+
*/
|
|
30
|
+
export async function installPlaygroundDependencies(
|
|
31
|
+
packageNames: Iterable<string>,
|
|
32
|
+
options: IPlaygroundDependencyInstallOptions = {},
|
|
33
|
+
): Promise<IPlaygroundDependencyInstallResult> {
|
|
34
|
+
const fetchImpl = options.fetch ?? globalThis.fetch?.bind(globalThis);
|
|
35
|
+
if (!fetchImpl) {
|
|
36
|
+
throw new Error("installPlaygroundDependencies requires fetch.");
|
|
37
|
+
}
|
|
38
|
+
throwIfAborted(options.signal);
|
|
39
|
+
|
|
40
|
+
const ignored = new Set(
|
|
41
|
+
options.ignoredPackages ?? BUILT_IN_PLAYGROUND_PACKAGES,
|
|
42
|
+
);
|
|
43
|
+
const installed = new Set(options.installedPackages ?? []);
|
|
44
|
+
const maxPackages = options.maxPackages ?? DEFAULT_MAX_PACKAGES;
|
|
45
|
+
const queue: IQueueItem[] = [];
|
|
46
|
+
const queued = new Set<string>();
|
|
47
|
+
const done = new Set<string>();
|
|
48
|
+
const result: IPlaygroundDependencyInstallResult = {
|
|
49
|
+
packages: [],
|
|
50
|
+
compilerFiles: {},
|
|
51
|
+
editorLibs: {},
|
|
52
|
+
runtimeFiles: {},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const enqueue = (item: IQueueItem): void => {
|
|
56
|
+
if (ignored.has(item.name) || installed.has(item.name)) return;
|
|
57
|
+
if (queued.has(item.name) || done.has(item.name)) return;
|
|
58
|
+
queued.add(item.name);
|
|
59
|
+
queue.push(item);
|
|
60
|
+
report("queued", item, `Queued ${item.name}`);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const report = (
|
|
64
|
+
phase: IPlaygroundDependencyProgressPhase,
|
|
65
|
+
item: IQueueItem | null,
|
|
66
|
+
message: string,
|
|
67
|
+
version?: string,
|
|
68
|
+
): void => {
|
|
69
|
+
options.onProgress?.({
|
|
70
|
+
phase,
|
|
71
|
+
packageName: item?.name,
|
|
72
|
+
version,
|
|
73
|
+
completed: done.size,
|
|
74
|
+
total: Math.max(queue.length, done.size + (item ? 1 : 0)),
|
|
75
|
+
message,
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
for (const name of packageNames) {
|
|
80
|
+
enqueue({ name, range: "latest", optional: false });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
for (let index = 0; index < queue.length; index++) {
|
|
84
|
+
if (done.size >= maxPackages) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
`Dependency install stopped after ${maxPackages} packages. Narrow the imports or raise maxPackages.`,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const item = queue[index]!;
|
|
91
|
+
if (installed.has(item.name) || done.has(item.name)) continue;
|
|
92
|
+
throwIfAborted(options.signal);
|
|
93
|
+
|
|
94
|
+
report("resolve", item, `Resolving ${item.name}`);
|
|
95
|
+
const metadata = await fetchNpmMetadata(
|
|
96
|
+
fetchImpl,
|
|
97
|
+
item.name,
|
|
98
|
+
item.optional,
|
|
99
|
+
options.signal,
|
|
100
|
+
);
|
|
101
|
+
throwIfAborted(options.signal);
|
|
102
|
+
if (!metadata) {
|
|
103
|
+
done.add(item.name);
|
|
104
|
+
report("skip", item, `Skipped optional ${item.name}`);
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const version = selectVersion(metadata, item.range);
|
|
109
|
+
const versionMetadata = metadata.versions[version];
|
|
110
|
+
const tarball = versionMetadata?.dist?.tarball;
|
|
111
|
+
if (!versionMetadata || !tarball) {
|
|
112
|
+
if (item.optional) {
|
|
113
|
+
done.add(item.name);
|
|
114
|
+
report("skip", item, `Skipped optional ${item.name}`);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
throw new Error(`No tarball found for ${item.name}@${version}.`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
report("download", item, `Downloading ${item.name}@${version}`, version);
|
|
121
|
+
const tgz = await downloadTarball(fetchImpl, tarball, options.signal);
|
|
122
|
+
throwIfAborted(options.signal);
|
|
123
|
+
report("extract", item, `Extracting ${item.name}@${version}`, version);
|
|
124
|
+
const unpacked = await unpackNpmTarball(tgz, options.signal);
|
|
125
|
+
throwIfAborted(options.signal);
|
|
126
|
+
const packageJson = {
|
|
127
|
+
...versionMetadata,
|
|
128
|
+
...unpacked.packageJson,
|
|
129
|
+
};
|
|
130
|
+
const mounted = mountPackageFiles(item.name, unpacked.files);
|
|
131
|
+
|
|
132
|
+
Object.assign(result.compilerFiles, mounted.compilerFiles);
|
|
133
|
+
Object.assign(result.editorLibs, mounted.editorLibs);
|
|
134
|
+
Object.assign(result.runtimeFiles, mounted.runtimeFiles);
|
|
135
|
+
|
|
136
|
+
const declarationCount = Object.keys(mounted.editorLibs).filter((key) =>
|
|
137
|
+
DECLARATION_FILE_REGEXP.test(key),
|
|
138
|
+
).length;
|
|
139
|
+
result.packages.push({
|
|
140
|
+
name: item.name,
|
|
141
|
+
version,
|
|
142
|
+
tarball,
|
|
143
|
+
fileCount: Object.keys(mounted.compilerFiles).length,
|
|
144
|
+
declarationCount,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
done.add(item.name);
|
|
148
|
+
installed.add(item.name);
|
|
149
|
+
report("done", item, `Installed ${item.name}@${version}`, version);
|
|
150
|
+
|
|
151
|
+
enqueuePackageDependencies(packageJson, enqueue);
|
|
152
|
+
if (declarationCount === 0 && !item.name.startsWith("@types/")) {
|
|
153
|
+
enqueue({
|
|
154
|
+
name: toTypesPackageName(item.name),
|
|
155
|
+
range: "latest",
|
|
156
|
+
optional: true,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
report("done", null, "Dependency install complete");
|
|
162
|
+
return result;
|
|
163
|
+
}
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
// Internal npm-registry helpers used by `installPlaygroundDependencies`.
|
|
2
|
+
// Grouped in one file because every helper is privately coupled to the tar
|
|
3
|
+
// + version-resolve + tarball-extract flow; splitting them per the public
|
|
4
|
+
// "one symbol per file" rule would make the install path harder to follow.
|
|
5
|
+
|
|
6
|
+
interface IPackageJson {
|
|
7
|
+
name?: string;
|
|
8
|
+
version?: string;
|
|
9
|
+
dependencies?: Record<string, string>;
|
|
10
|
+
peerDependencies?: Record<string, string>;
|
|
11
|
+
peerDependenciesMeta?: Record<string, { optional?: boolean }>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface INpmVersionMetadata {
|
|
15
|
+
name: string;
|
|
16
|
+
version: string;
|
|
17
|
+
dependencies?: Record<string, string>;
|
|
18
|
+
peerDependencies?: Record<string, string>;
|
|
19
|
+
peerDependenciesMeta?: Record<string, { optional?: boolean }>;
|
|
20
|
+
dist?: {
|
|
21
|
+
tarball?: string;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface INpmMetadata {
|
|
26
|
+
name: string;
|
|
27
|
+
"dist-tags"?: Record<string, string>;
|
|
28
|
+
versions: Record<string, INpmVersionMetadata | undefined>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface IUnpackedPackage {
|
|
32
|
+
files: Record<string, string>;
|
|
33
|
+
packageJson: IPackageJson;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface IQueueItem {
|
|
37
|
+
name: string;
|
|
38
|
+
range: string;
|
|
39
|
+
optional: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export type FetchLike = (
|
|
43
|
+
input: string,
|
|
44
|
+
init?: RequestInit,
|
|
45
|
+
) => Promise<Response>;
|
|
46
|
+
|
|
47
|
+
const TEXT_FILE_REGEXP =
|
|
48
|
+
/(^package\.json$|\.([cm]?js|jsx|[cm]?ts|tsx|json)$|\.d\.[cm]?ts$)/i;
|
|
49
|
+
export const DECLARATION_FILE_REGEXP = /\.d\.[cm]?ts$/i;
|
|
50
|
+
const RUNTIME_FILE_REGEXP = /(^package\.json$|\.([cm]?js|json)$)/i;
|
|
51
|
+
|
|
52
|
+
export function throwIfAborted(signal: AbortSignal | undefined): void {
|
|
53
|
+
if (!signal?.aborted) return;
|
|
54
|
+
if (signal.reason !== undefined) throw signal.reason;
|
|
55
|
+
throw new DOMException("The operation was aborted.", "AbortError");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export async function fetchNpmMetadata(
|
|
59
|
+
fetchImpl: FetchLike,
|
|
60
|
+
packageName: string,
|
|
61
|
+
optional: boolean,
|
|
62
|
+
signal: AbortSignal | undefined,
|
|
63
|
+
): Promise<INpmMetadata | null> {
|
|
64
|
+
const response = await fetchImpl(
|
|
65
|
+
`https://registry.npmjs.org/${encodeURIComponent(packageName)}`,
|
|
66
|
+
{
|
|
67
|
+
headers: {
|
|
68
|
+
Accept: "application/vnd.npm.install-v1+json, application/json",
|
|
69
|
+
},
|
|
70
|
+
signal,
|
|
71
|
+
},
|
|
72
|
+
);
|
|
73
|
+
if (response.status === 404 && optional) return null;
|
|
74
|
+
if (!response.ok) {
|
|
75
|
+
throw new Error(
|
|
76
|
+
`npm registry returned ${response.status} while resolving ${packageName}.`,
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
return (await response.json()) as INpmMetadata;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function selectVersion(metadata: INpmMetadata, range: string): string {
|
|
83
|
+
const versions = metadata.versions;
|
|
84
|
+
if (versions[range]) return range;
|
|
85
|
+
const tag = metadata["dist-tags"]?.[range];
|
|
86
|
+
if (tag && versions[tag]) return tag;
|
|
87
|
+
const normalized = range.replace(/^[~^<>= ]+/, "").trim();
|
|
88
|
+
if (normalized && versions[normalized]) return normalized;
|
|
89
|
+
const latest = metadata["dist-tags"]?.latest;
|
|
90
|
+
if (latest && versions[latest]) return latest;
|
|
91
|
+
const all = Object.keys(versions).sort(compareVersionDesc);
|
|
92
|
+
const fallback = all[0];
|
|
93
|
+
if (!fallback) throw new Error(`No versions found for ${metadata.name}.`);
|
|
94
|
+
return fallback;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export async function downloadTarball(
|
|
98
|
+
fetchImpl: FetchLike,
|
|
99
|
+
tarball: string,
|
|
100
|
+
signal: AbortSignal | undefined,
|
|
101
|
+
): Promise<ArrayBuffer> {
|
|
102
|
+
const response = await fetchImpl(tarball, { signal });
|
|
103
|
+
if (!response.ok) {
|
|
104
|
+
throw new Error(`tarball download failed with HTTP ${response.status}.`);
|
|
105
|
+
}
|
|
106
|
+
return response.arrayBuffer();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export async function unpackNpmTarball(
|
|
110
|
+
tgz: ArrayBuffer,
|
|
111
|
+
signal: AbortSignal | undefined,
|
|
112
|
+
): Promise<IUnpackedPackage> {
|
|
113
|
+
throwIfAborted(signal);
|
|
114
|
+
const tar = await gunzip(tgz);
|
|
115
|
+
throwIfAborted(signal);
|
|
116
|
+
const decoder = new TextDecoder();
|
|
117
|
+
const files: Record<string, string> = {};
|
|
118
|
+
let packageJson: IPackageJson = {};
|
|
119
|
+
let offset = 0;
|
|
120
|
+
let longPath: string | null = null;
|
|
121
|
+
let paxPath: string | null = null;
|
|
122
|
+
|
|
123
|
+
while (offset + 512 <= tar.length) {
|
|
124
|
+
throwIfAborted(signal);
|
|
125
|
+
const header = tar.subarray(offset, offset + 512);
|
|
126
|
+
offset += 512;
|
|
127
|
+
if (header.every((value) => value === 0)) break;
|
|
128
|
+
|
|
129
|
+
const type = String.fromCharCode(header[156] ?? 0);
|
|
130
|
+
const size = parseOctal(header.subarray(124, 136));
|
|
131
|
+
const body = tar.subarray(offset, offset + size);
|
|
132
|
+
offset += Math.ceil(size / 512) * 512;
|
|
133
|
+
|
|
134
|
+
if (type === "L") {
|
|
135
|
+
longPath = trimNull(decoder.decode(body));
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (type === "x") {
|
|
139
|
+
paxPath = parsePaxPath(decoder.decode(body));
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (type !== "0" && type !== "\0") {
|
|
143
|
+
longPath = null;
|
|
144
|
+
paxPath = null;
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const rawPath =
|
|
149
|
+
paxPath ?? longPath ?? readTarString(header.subarray(0, 100), decoder);
|
|
150
|
+
longPath = null;
|
|
151
|
+
paxPath = null;
|
|
152
|
+
const rel = stripTarRoot(rawPath);
|
|
153
|
+
if (!rel || !TEXT_FILE_REGEXP.test(rel)) continue;
|
|
154
|
+
|
|
155
|
+
const text = decoder.decode(body);
|
|
156
|
+
files[rel] = text;
|
|
157
|
+
if (rel === "package.json") {
|
|
158
|
+
try {
|
|
159
|
+
packageJson = JSON.parse(text) as IPackageJson;
|
|
160
|
+
} catch {
|
|
161
|
+
packageJson = {};
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return { files, packageJson };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async function gunzip(input: ArrayBuffer): Promise<Uint8Array> {
|
|
170
|
+
if (!("DecompressionStream" in globalThis)) {
|
|
171
|
+
throw new Error(
|
|
172
|
+
"This browser cannot unpack npm tgz files because DecompressionStream is unavailable.",
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
const stream = new Blob([input])
|
|
176
|
+
.stream()
|
|
177
|
+
.pipeThrough(new DecompressionStream("gzip"));
|
|
178
|
+
return new Uint8Array(await new Response(stream).arrayBuffer());
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export interface IMountedFiles {
|
|
182
|
+
compilerFiles: Record<string, string>;
|
|
183
|
+
editorLibs: Record<string, string>;
|
|
184
|
+
runtimeFiles: Record<string, string>;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function mountPackageFiles(
|
|
188
|
+
packageName: string,
|
|
189
|
+
files: Record<string, string>,
|
|
190
|
+
): IMountedFiles {
|
|
191
|
+
const compilerFiles: Record<string, string> = {};
|
|
192
|
+
const editorLibs: Record<string, string> = {};
|
|
193
|
+
const runtimeFiles: Record<string, string> = {};
|
|
194
|
+
|
|
195
|
+
for (const [rel, text] of Object.entries(files)) {
|
|
196
|
+
const packageRel = `${packageName}/${rel}`;
|
|
197
|
+
compilerFiles[`node_modules/${packageRel}`] = text;
|
|
198
|
+
if (rel === "package.json" || DECLARATION_FILE_REGEXP.test(rel)) {
|
|
199
|
+
editorLibs[`file:///node_modules/${packageRel}`] = text;
|
|
200
|
+
}
|
|
201
|
+
if (RUNTIME_FILE_REGEXP.test(rel)) {
|
|
202
|
+
runtimeFiles[packageRel] = text;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return { compilerFiles, editorLibs, runtimeFiles };
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function enqueuePackageDependencies(
|
|
210
|
+
packageJson: { dependencies?: Record<string, string>; peerDependencies?: Record<string, string>; peerDependenciesMeta?: Record<string, { optional?: boolean }> },
|
|
211
|
+
enqueue: (item: IQueueItem) => void,
|
|
212
|
+
): void {
|
|
213
|
+
for (const [name, range] of Object.entries(packageJson.dependencies ?? {})) {
|
|
214
|
+
if (isRegistryRange(range)) enqueue({ name, range, optional: false });
|
|
215
|
+
}
|
|
216
|
+
for (const [name, range] of Object.entries(
|
|
217
|
+
packageJson.peerDependencies ?? {},
|
|
218
|
+
)) {
|
|
219
|
+
const optional =
|
|
220
|
+
packageJson.peerDependenciesMeta?.[name]?.optional === true;
|
|
221
|
+
// Truly optional peers (peerDependenciesMeta[name].optional === true)
|
|
222
|
+
// are never enqueued. Required peers MUST propagate optional: false so
|
|
223
|
+
// a 404 on the registry surfaces as an install failure instead of a
|
|
224
|
+
// silent skip that leaves the wasm-side compile reporting a generic
|
|
225
|
+
// "Cannot find module" with no breadcrumb back to the dep installer.
|
|
226
|
+
if (!optional && isRegistryRange(range))
|
|
227
|
+
enqueue({ name, range, optional: false });
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function isRegistryRange(range: string): boolean {
|
|
232
|
+
return !/^(file:|link:|workspace:|portal:|git\+|github:|https?:)/.test(range);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export function toTypesPackageName(packageName: string): string {
|
|
236
|
+
if (!packageName.startsWith("@")) return `@types/${packageName}`;
|
|
237
|
+
const [scope, name] = packageName.slice(1).split("/");
|
|
238
|
+
return scope && name ? `@types/${scope}__${name}` : `@types/${packageName}`;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function readTarString(bytes: Uint8Array, decoder: TextDecoder): string {
|
|
242
|
+
return trimNull(decoder.decode(bytes));
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function trimNull(text: string): string {
|
|
246
|
+
const index = text.indexOf("\0");
|
|
247
|
+
return (index < 0 ? text : text.slice(0, index)).trim();
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function parseOctal(bytes: Uint8Array): number {
|
|
251
|
+
const text = trimNull(new TextDecoder().decode(bytes)).trim();
|
|
252
|
+
return text ? Number.parseInt(text, 8) : 0;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function parsePaxPath(text: string): string | null {
|
|
256
|
+
let rest = text;
|
|
257
|
+
while (rest.length > 0) {
|
|
258
|
+
const space = rest.indexOf(" ");
|
|
259
|
+
if (space < 0) return null;
|
|
260
|
+
const length = Number(rest.slice(0, space));
|
|
261
|
+
if (!Number.isFinite(length) || length <= space) return null;
|
|
262
|
+
const record = rest.slice(space + 1, length - 1);
|
|
263
|
+
const eq = record.indexOf("=");
|
|
264
|
+
if (eq > 0 && record.slice(0, eq) === "path") return record.slice(eq + 1);
|
|
265
|
+
rest = rest.slice(length);
|
|
266
|
+
}
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function stripTarRoot(path: string): string {
|
|
271
|
+
const normalized = path.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
272
|
+
if (!normalized) return "";
|
|
273
|
+
if (normalized.startsWith("package/"))
|
|
274
|
+
return normalized.slice("package/".length);
|
|
275
|
+
const slash = normalized.indexOf("/");
|
|
276
|
+
return slash < 0 ? normalized : normalized.slice(slash + 1);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function compareVersionDesc(a: string, b: string): number {
|
|
280
|
+
const pa = a.split(/[.-]/).map((part) => Number.parseInt(part, 10));
|
|
281
|
+
const pb = b.split(/[.-]/).map((part) => Number.parseInt(part, 10));
|
|
282
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
283
|
+
const av = Number.isFinite(pa[i]) ? pa[i]! : 0;
|
|
284
|
+
const bv = Number.isFinite(pb[i]) ? pb[i]! : 0;
|
|
285
|
+
if (av !== bv) return bv - av;
|
|
286
|
+
}
|
|
287
|
+
return b.localeCompare(a);
|
|
288
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
const BUILTIN_MODULES = new Set([
|
|
2
|
+
"assert",
|
|
3
|
+
"buffer",
|
|
4
|
+
"child_process",
|
|
5
|
+
"console",
|
|
6
|
+
"constants",
|
|
7
|
+
"crypto",
|
|
8
|
+
"dns",
|
|
9
|
+
"events",
|
|
10
|
+
"fs",
|
|
11
|
+
"http",
|
|
12
|
+
"https",
|
|
13
|
+
"module",
|
|
14
|
+
"net",
|
|
15
|
+
"os",
|
|
16
|
+
"path",
|
|
17
|
+
"perf_hooks",
|
|
18
|
+
"process",
|
|
19
|
+
"punycode",
|
|
20
|
+
"querystring",
|
|
21
|
+
"readline",
|
|
22
|
+
"stream",
|
|
23
|
+
"string_decoder",
|
|
24
|
+
"timers",
|
|
25
|
+
"tls",
|
|
26
|
+
"tty",
|
|
27
|
+
"url",
|
|
28
|
+
"util",
|
|
29
|
+
"vm",
|
|
30
|
+
"worker_threads",
|
|
31
|
+
"zlib",
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Extract the package name from a module specifier.
|
|
36
|
+
*
|
|
37
|
+
* Returns `null` for relative paths, hash imports, URL specifiers, and
|
|
38
|
+
* Node built-in modules — the caller doesn't install those from npm.
|
|
39
|
+
*/
|
|
40
|
+
export function packageNameFromSpecifier(specifier: string): string | null {
|
|
41
|
+
if (
|
|
42
|
+
specifier.startsWith("#") ||
|
|
43
|
+
specifier.startsWith(".") ||
|
|
44
|
+
specifier.startsWith("/") ||
|
|
45
|
+
/^[a-z][a-z0-9+.-]*:/i.test(specifier)
|
|
46
|
+
)
|
|
47
|
+
return null;
|
|
48
|
+
const bare = specifier.startsWith("node:") ? specifier.slice(5) : specifier;
|
|
49
|
+
const first = bare.split("/")[0];
|
|
50
|
+
if (first && BUILTIN_MODULES.has(first)) return null;
|
|
51
|
+
if (bare.startsWith("@")) {
|
|
52
|
+
const firstSlash = bare.indexOf("/");
|
|
53
|
+
if (firstSlash < 0) return null;
|
|
54
|
+
if (firstSlash === 1) return null;
|
|
55
|
+
const secondSlash = bare.indexOf("/", firstSlash + 1);
|
|
56
|
+
if (secondSlash === firstSlash + 1) return null;
|
|
57
|
+
return secondSlash < 0 ? bare : bare.slice(0, secondSlash);
|
|
58
|
+
}
|
|
59
|
+
const slash = bare.indexOf("/");
|
|
60
|
+
return slash < 0 ? bare : bare.slice(0, slash);
|
|
61
|
+
}
|