@ttsc/playground 0.14.0-dev.20250530.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 +191 -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 +5 -0
- package/lib/src/compiler/internal/joinUnder.js +13 -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 +21 -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 +84 -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 +18 -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 +5 -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 +70 -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 +273 -0
- package/src/compiler/installDependenciesIntoMemFS.ts +28 -0
- package/src/compiler/installTypiaSourcePack.ts +24 -0
- package/src/compiler/internal/joinUnder.ts +8 -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 +18 -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 +292 -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 +80 -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 +96 -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 +18 -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 +12 -0
- package/src/structures/IPlaygroundExample.ts +16 -0
- package/src/structures/IPlaygroundShellProps.ts +83 -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
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ttsc/playground",
|
|
3
|
+
"version": "0.14.0-dev.20250530.1",
|
|
4
|
+
"description": "Reusable React + Web Worker scaffolding for in-browser ttsc playgrounds built on @ttsc/wasm.",
|
|
5
|
+
"main": "lib/src/index.js",
|
|
6
|
+
"types": "lib/src/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"lib",
|
|
9
|
+
"src",
|
|
10
|
+
"README.md"
|
|
11
|
+
],
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./lib/src/index.d.ts",
|
|
18
|
+
"default": "./lib/src/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./package.json": "./package.json"
|
|
21
|
+
},
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "https://github.com/samchon/ttsc"
|
|
25
|
+
},
|
|
26
|
+
"author": "Jeongho Nam",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/samchon/ttsc/issues"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://ttsc.dev",
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"lz-string": "^1.5.0"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"@monaco-editor/react": "^4.6.0",
|
|
37
|
+
"monaco-editor": "^0.52.0",
|
|
38
|
+
"react": "^18.0.0",
|
|
39
|
+
"react-dom": "^18.0.0",
|
|
40
|
+
"tgrid": "^1.0.3",
|
|
41
|
+
"@ttsc/wasm": "^0.14.0-dev.20250530.1"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@monaco-editor/react": "^4.6.0",
|
|
45
|
+
"@types/node": "^25.3.0",
|
|
46
|
+
"@types/react": "^18.3.1",
|
|
47
|
+
"@typescript/native-preview": "7.0.0-dev.20260527.2",
|
|
48
|
+
"monaco-editor": "^0.52.0",
|
|
49
|
+
"react": "^18.3.1",
|
|
50
|
+
"react-dom": "^18.3.1",
|
|
51
|
+
"rimraf": "^6.1.2",
|
|
52
|
+
"tgrid": "^1.0.3",
|
|
53
|
+
"@ttsc/wasm": "^0.14.0-dev.20250530.1"
|
|
54
|
+
},
|
|
55
|
+
"keywords": [
|
|
56
|
+
"ttsc",
|
|
57
|
+
"playground",
|
|
58
|
+
"typescript",
|
|
59
|
+
"browser",
|
|
60
|
+
"wasm",
|
|
61
|
+
"react"
|
|
62
|
+
],
|
|
63
|
+
"scripts": {
|
|
64
|
+
"build": "rimraf lib && tsgo -p ."
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The compiler options every playground tsconfig opts into by default. Sites
|
|
3
|
+
* pass these straight to `buildTsconfigJSON`; consumer code that needs a tweak
|
|
4
|
+
* spreads the constant and overrides individual fields.
|
|
5
|
+
*
|
|
6
|
+
* `target` and `module` are TypeScript's numeric enum values (99 = ESNext, 199
|
|
7
|
+
* = NodeNext, …). They are spelled numerically here because the wasm doesn't
|
|
8
|
+
* run the TS configuration parser through a string→enum step before Go reads
|
|
9
|
+
* the JSON; passing the canonical numbers avoids version-drift.
|
|
10
|
+
*/
|
|
11
|
+
export const DEFAULT_PLAYGROUND_COMPILER_OPTIONS = {
|
|
12
|
+
target: 99,
|
|
13
|
+
esModuleInterop: true,
|
|
14
|
+
forceConsistentCasingInFileNames: true,
|
|
15
|
+
moduleResolution: 100,
|
|
16
|
+
strict: true,
|
|
17
|
+
skipLibCheck: true,
|
|
18
|
+
experimentalDecorators: true,
|
|
19
|
+
} as const;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { IBuildTsconfigOptions } from "../structures/IBuildTsconfigOptions";
|
|
2
|
+
import { DEFAULT_PLAYGROUND_COMPILER_OPTIONS } from "./DEFAULT_PLAYGROUND_COMPILER_OPTIONS";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Serialize a tsconfig JSON document the wasm-side compiler can consume.
|
|
6
|
+
*
|
|
7
|
+
* `buildTsconfigJSON` is deliberately conservative — it returns a string the
|
|
8
|
+
* caller writes to MemFS as-is, so there's no schema validation here. Bad
|
|
9
|
+
* compiler options surface as wasm-side errors on the next build call.
|
|
10
|
+
*/
|
|
11
|
+
export function buildTsconfigJSON(options: IBuildTsconfigOptions): string {
|
|
12
|
+
return JSON.stringify({
|
|
13
|
+
compilerOptions: {
|
|
14
|
+
...DEFAULT_PLAYGROUND_COMPILER_OPTIONS,
|
|
15
|
+
module: options.module,
|
|
16
|
+
outDir: options.outDir ?? "dist",
|
|
17
|
+
rootDir: options.rootDir ?? "src",
|
|
18
|
+
...(options.compilerOptions ?? {}),
|
|
19
|
+
},
|
|
20
|
+
include: options.include ?? ["src"],
|
|
21
|
+
});
|
|
22
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { IMemFSHost } from "@ttsc/wasm";
|
|
2
|
+
|
|
3
|
+
import type { IInstallTypiaSourcePackOptions } from "../structures/IInstallTypiaSourcePackOptions";
|
|
4
|
+
import { installTypiaSourcePack } from "./installTypiaSourcePack";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Build a `mount` callback for the `typiaPlugin` config of
|
|
8
|
+
* {@link createWorkerCompiler}.
|
|
9
|
+
*
|
|
10
|
+
* The returned function fetches the pack once (cached per URL by
|
|
11
|
+
* {@link loadTypiaSourcePack}) and writes every entry to the MemFS the first
|
|
12
|
+
* time it is invoked on a given host.
|
|
13
|
+
*/
|
|
14
|
+
export function createTypiaSourcePackMount(
|
|
15
|
+
options: IInstallTypiaSourcePackOptions,
|
|
16
|
+
): (host: IMemFSHost, workDir?: string) => Promise<void> {
|
|
17
|
+
return async (host: IMemFSHost, workDir?: string) => {
|
|
18
|
+
// Honor the caller's workDir when the site did not pin mountRoot
|
|
19
|
+
// explicitly. Otherwise a `createWorkerCompiler({workDir: '/foo'})`
|
|
20
|
+
// would still mount typia under `/work/node_modules/` and tsgo would
|
|
21
|
+
// never resolve `typia` from the project root.
|
|
22
|
+
//
|
|
23
|
+
// `workDir` is intentionally optional so callers built against the
|
|
24
|
+
// previous single-arg signature (`mount(host)`) keep working — when
|
|
25
|
+
// both mountRoot and workDir are absent, installTypiaSourcePack falls
|
|
26
|
+
// back to its own default (`/work/node_modules`).
|
|
27
|
+
const mountRoot =
|
|
28
|
+
options.mountRoot ??
|
|
29
|
+
(workDir ? `${workDir.replace(/\/+$/, "")}/node_modules` : undefined);
|
|
30
|
+
await installTypiaSourcePack(host, { ...options, mountRoot });
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
// Worker-side playground compiler factory.
|
|
2
|
+
//
|
|
3
|
+
// This module ships a ready-to-bind `ICompilerService` implementation. The
|
|
4
|
+
// consumer's worker entry boots the wasm once, builds tsconfig variants for
|
|
5
|
+
// the site's chosen module shape, registers the typia/lint plugin verbs (or
|
|
6
|
+
// the site-provided overrides), and serializes every MemFS-mutating call onto
|
|
7
|
+
// a single chain so concurrent compiles never corrupt each other.
|
|
8
|
+
import {
|
|
9
|
+
type IBootResult,
|
|
10
|
+
type ITtscCompileResult,
|
|
11
|
+
bootTtsc,
|
|
12
|
+
parseResult,
|
|
13
|
+
} from "@ttsc/wasm";
|
|
14
|
+
|
|
15
|
+
import type { ICompilerService } from "../structures/ICompilerService";
|
|
16
|
+
import type { ICreateWorkerCompilerOptions } from "../structures/ICreateWorkerCompilerOptions";
|
|
17
|
+
import type { ITransformOptions } from "../structures/ITransformOptions";
|
|
18
|
+
import { DEFAULT_ENTRY_FILE } from "./DEFAULT_ENTRY_FILE";
|
|
19
|
+
import { DEFAULT_LINT_PLUGIN_NAME } from "./DEFAULT_LINT_PLUGIN_NAME";
|
|
20
|
+
import { DEFAULT_TSCONFIG_PATH } from "./DEFAULT_TSCONFIG_PATH";
|
|
21
|
+
import { DEFAULT_TYPIA_PLUGIN_NAME } from "./DEFAULT_TYPIA_PLUGIN_NAME";
|
|
22
|
+
import { DEFAULT_WORK_DIR } from "./DEFAULT_WORK_DIR";
|
|
23
|
+
import { buildTsconfigJSON } from "./buildTsconfigJSON";
|
|
24
|
+
import { installDependenciesIntoMemFS } from "./installDependenciesIntoMemFS";
|
|
25
|
+
import { joinUnder } from "./internal/joinUnder";
|
|
26
|
+
import { parseLintDiagnostics } from "./internal/parseLintDiagnostics";
|
|
27
|
+
import { safeParseTypiaTransform } from "./internal/safeParseTypiaTransform";
|
|
28
|
+
import { mapDiagnostic } from "./mapDiagnostic";
|
|
29
|
+
import { normalizeError } from "./normalizeError";
|
|
30
|
+
import { pickEmittedJS } from "./pickEmittedJS";
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Build an `ICompilerService` ready to register with tgrid's `WorkerServer`.
|
|
34
|
+
*
|
|
35
|
+
* Usage in the worker entry:
|
|
36
|
+
*
|
|
37
|
+
* ```ts
|
|
38
|
+
* import { createWorkerCompiler } from "@ttsc/playground";
|
|
39
|
+
* import { WorkerServer } from "tgrid";
|
|
40
|
+
*
|
|
41
|
+
* const service = createWorkerCompiler({
|
|
42
|
+
* wasmUrl: "/compiler/playground.wasm",
|
|
43
|
+
* apiName: "ttscPlayground",
|
|
44
|
+
* typiaPlugin: { mount: installTypiaPack },
|
|
45
|
+
* });
|
|
46
|
+
*
|
|
47
|
+
* const main = async () => {
|
|
48
|
+
* const worker = new WorkerServer();
|
|
49
|
+
* await worker.open(service);
|
|
50
|
+
* };
|
|
51
|
+
* void main();
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export function createWorkerCompiler(
|
|
55
|
+
options: ICreateWorkerCompilerOptions,
|
|
56
|
+
): ICompilerService {
|
|
57
|
+
const workDir = options.workDir ?? DEFAULT_WORK_DIR;
|
|
58
|
+
const tsconfigPath = options.tsconfigPath ?? DEFAULT_TSCONFIG_PATH;
|
|
59
|
+
const entryFile = options.entryFile ?? DEFAULT_ENTRY_FILE;
|
|
60
|
+
|
|
61
|
+
const typiaPlugin =
|
|
62
|
+
options.typiaPlugin === false ? null : (options.typiaPlugin ?? {});
|
|
63
|
+
const typiaPluginName = typiaPlugin?.name ?? DEFAULT_TYPIA_PLUGIN_NAME;
|
|
64
|
+
const typiaTransformModule =
|
|
65
|
+
typiaPlugin?.transformModule ?? "typia/lib/transform";
|
|
66
|
+
|
|
67
|
+
const lintPlugin =
|
|
68
|
+
options.lintPlugin === false ? null : (options.lintPlugin ?? {});
|
|
69
|
+
const lintPluginName = lintPlugin?.name ?? DEFAULT_LINT_PLUGIN_NAME;
|
|
70
|
+
|
|
71
|
+
// tsconfig variants. ESM for the "Compiled JS" preview lane, CommonJS for
|
|
72
|
+
// the bundle/Execute lane (whose `new Function` driver expects CJS).
|
|
73
|
+
const tsconfigPlugins = typiaPlugin
|
|
74
|
+
? [{ transform: typiaTransformModule }]
|
|
75
|
+
: [];
|
|
76
|
+
const extraCompilerOptions = {
|
|
77
|
+
...(options.extraCompilerOptions ?? {}),
|
|
78
|
+
...(tsconfigPlugins.length > 0 ? { plugins: tsconfigPlugins } : {}),
|
|
79
|
+
};
|
|
80
|
+
const tsconfigESM = buildTsconfigJSON({
|
|
81
|
+
module: "ESNext",
|
|
82
|
+
compilerOptions: extraCompilerOptions,
|
|
83
|
+
});
|
|
84
|
+
const tsconfigCJS = buildTsconfigJSON({
|
|
85
|
+
module: "CommonJS",
|
|
86
|
+
compilerOptions: extraCompilerOptions,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Cache the boot promise across calls. If the boot rejects we clear the
|
|
90
|
+
// cache so the next call retries — otherwise every later compile/bundle/
|
|
91
|
+
// lint would replay the same rejection forever (page reload required).
|
|
92
|
+
// Mirrors the createCompilerClient (UI-side) pattern.
|
|
93
|
+
let boot: Promise<IBootResult> | null = null;
|
|
94
|
+
function getBoot(): Promise<IBootResult> {
|
|
95
|
+
if (boot) return boot;
|
|
96
|
+
boot = (async () => {
|
|
97
|
+
const result = await bootTtsc({
|
|
98
|
+
wasmUrl: options.wasmUrl,
|
|
99
|
+
wasmExecUrl: options.wasmExecUrl,
|
|
100
|
+
apiName: options.apiName,
|
|
101
|
+
});
|
|
102
|
+
if (typiaPlugin?.mount) await typiaPlugin.mount(result.host, workDir);
|
|
103
|
+
return result;
|
|
104
|
+
})().catch((err) => {
|
|
105
|
+
boot = null;
|
|
106
|
+
throw err;
|
|
107
|
+
});
|
|
108
|
+
return boot;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Serialize every MemFS-mutating call. tgrid queues incoming messages, but
|
|
112
|
+
// the wasm-side host runs them concurrently — a fast keystroke could
|
|
113
|
+
// interleave a bundle's tsconfig rewrite with a lint pass and corrupt
|
|
114
|
+
// either result. The chain keeps compile/bundle/lint linear.
|
|
115
|
+
let busy: Promise<unknown> = Promise.resolve();
|
|
116
|
+
const enqueue = <T>(fn: () => Promise<T>): Promise<T> => {
|
|
117
|
+
const next = busy.then(fn, fn);
|
|
118
|
+
busy = next.catch(() => {});
|
|
119
|
+
return next;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const projectFiles = (
|
|
123
|
+
source: string,
|
|
124
|
+
tsconfigText: string,
|
|
125
|
+
): Record<string, string> => ({
|
|
126
|
+
[`${workDir}/${tsconfigPath}`]: tsconfigText,
|
|
127
|
+
[`${workDir}/${entryFile}`]: source,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const writeProject = (
|
|
131
|
+
host: IBootResult["host"],
|
|
132
|
+
files: Record<string, string>,
|
|
133
|
+
): void => {
|
|
134
|
+
for (const [path, text] of Object.entries(files)) {
|
|
135
|
+
host.writeFile(path, text);
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
// Typia transform — run the registered typia plugin's `transform` verb,
|
|
140
|
+
// parse its JSON stdout, and write the rewritten TS back into MemFS so the
|
|
141
|
+
// subsequent `api.build` sees the post-transform text.
|
|
142
|
+
const applyTypiaTransform = async (
|
|
143
|
+
api: IBootResult["api"],
|
|
144
|
+
host: IBootResult["host"],
|
|
145
|
+
): Promise<void> => {
|
|
146
|
+
if (!typiaPlugin) return;
|
|
147
|
+
try {
|
|
148
|
+
const raw = await api.plugin({
|
|
149
|
+
name: typiaPluginName,
|
|
150
|
+
command: "transform",
|
|
151
|
+
cwd: workDir,
|
|
152
|
+
tsconfig: tsconfigPath,
|
|
153
|
+
output: "ts",
|
|
154
|
+
});
|
|
155
|
+
if (!raw.stdout) return;
|
|
156
|
+
const transformed = safeParseTypiaTransform(raw.stdout);
|
|
157
|
+
if (!transformed) return;
|
|
158
|
+
for (const [rel, text] of Object.entries(transformed.typescript)) {
|
|
159
|
+
host.writeFile(joinUnder(workDir, rel), text);
|
|
160
|
+
}
|
|
161
|
+
} catch {
|
|
162
|
+
// Swallow plugin spawn / JSON parse failures so the user still sees the
|
|
163
|
+
// tsgo compile diagnostics from the subsequent build call. Real
|
|
164
|
+
// user-facing errors (e.g. typia rule violations) flow through the
|
|
165
|
+
// wasm-side diagnostics path.
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const runBuildPipeline = async (
|
|
170
|
+
source: string,
|
|
171
|
+
runTypia: boolean,
|
|
172
|
+
tsconfigText: string,
|
|
173
|
+
): Promise<ICompilerService.IResult> => {
|
|
174
|
+
try {
|
|
175
|
+
const { api, host } = await getBoot();
|
|
176
|
+
writeProject(host, projectFiles(source, tsconfigText));
|
|
177
|
+
if (runTypia) await applyTypiaTransform(api, host);
|
|
178
|
+
const raw = await api.build({
|
|
179
|
+
cwd: workDir,
|
|
180
|
+
tsconfig: tsconfigPath,
|
|
181
|
+
});
|
|
182
|
+
if (raw.code !== 0 && !raw.result) {
|
|
183
|
+
return {
|
|
184
|
+
type: "error",
|
|
185
|
+
target: "javascript",
|
|
186
|
+
value: {
|
|
187
|
+
message:
|
|
188
|
+
raw.stderr || "ttsc: build failed without a result payload",
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
const parsed = parseResult<ITtscCompileResult>(raw);
|
|
193
|
+
if (!parsed) {
|
|
194
|
+
return {
|
|
195
|
+
type: "error",
|
|
196
|
+
target: "javascript",
|
|
197
|
+
value: { message: "ttsc: result JSON could not be parsed" },
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
const diagnostics = (parsed.diagnostics ?? []).map((d) =>
|
|
201
|
+
mapDiagnostic(d, source),
|
|
202
|
+
);
|
|
203
|
+
const js = pickEmittedJS(parsed.output ?? {}, entryFile);
|
|
204
|
+
const errors = diagnostics.filter((d) => d.severity === "error");
|
|
205
|
+
if (errors.length > 0) {
|
|
206
|
+
return {
|
|
207
|
+
type: "failure",
|
|
208
|
+
target: "javascript",
|
|
209
|
+
value: js ?? "",
|
|
210
|
+
diagnostics,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
return { type: "success", target: "javascript", value: js ?? "" };
|
|
214
|
+
} catch (error) {
|
|
215
|
+
return {
|
|
216
|
+
type: "error",
|
|
217
|
+
target: "javascript",
|
|
218
|
+
value: normalizeError(error),
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
// Lint pipeline. The lint plugin emits tsgo-style pretty diagnostics on
|
|
224
|
+
// stderr; we parse those lines back into IDiagnostic so the UI can render
|
|
225
|
+
// them in the Lint tab.
|
|
226
|
+
const runLintPipeline = async (
|
|
227
|
+
source: string,
|
|
228
|
+
): Promise<ICompilerService.ILintResult> => {
|
|
229
|
+
if (!lintPlugin) return { diagnostics: [] };
|
|
230
|
+
try {
|
|
231
|
+
const { api, host } = await getBoot();
|
|
232
|
+
writeProject(host, projectFiles(source, tsconfigESM));
|
|
233
|
+
const raw = await api.plugin({
|
|
234
|
+
name: lintPluginName,
|
|
235
|
+
command: "check",
|
|
236
|
+
cwd: workDir,
|
|
237
|
+
tsconfig: tsconfigPath,
|
|
238
|
+
});
|
|
239
|
+
return { diagnostics: parseLintDiagnostics(raw.stderr, source) };
|
|
240
|
+
} catch {
|
|
241
|
+
return { diagnostics: [] };
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
return {
|
|
246
|
+
installDependencies: (props) =>
|
|
247
|
+
enqueue(async () => {
|
|
248
|
+
const { host } = await getBoot();
|
|
249
|
+
return installDependenciesIntoMemFS(host, workDir, props);
|
|
250
|
+
}),
|
|
251
|
+
compile: (props) =>
|
|
252
|
+
enqueue(() =>
|
|
253
|
+
runBuildPipeline(
|
|
254
|
+
props.source,
|
|
255
|
+
shouldRunTypia(props.options),
|
|
256
|
+
tsconfigESM,
|
|
257
|
+
),
|
|
258
|
+
),
|
|
259
|
+
bundle: (props) =>
|
|
260
|
+
enqueue(() =>
|
|
261
|
+
runBuildPipeline(
|
|
262
|
+
props.source,
|
|
263
|
+
shouldRunTypia(props.options),
|
|
264
|
+
tsconfigCJS,
|
|
265
|
+
),
|
|
266
|
+
),
|
|
267
|
+
lint: (props) => enqueue(() => runLintPipeline(props.source)),
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function shouldRunTypia(options?: ITransformOptions): boolean {
|
|
272
|
+
return options?.typia !== false;
|
|
273
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { IMemFSHost } from "@ttsc/wasm";
|
|
2
|
+
|
|
3
|
+
import type { ICompilerService } from "../structures/ICompilerService";
|
|
4
|
+
import { normalizeNodeModulePath } from "./normalizeNodeModulePath";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Mount external npm package files into the worker's MemFS under
|
|
8
|
+
* `<workDir>/node_modules/...`.
|
|
9
|
+
*
|
|
10
|
+
* Used by `createWorkerCompiler` to implement the `installDependencies` verb of
|
|
11
|
+
* `ICompilerService`. The dependency installer (UI side) feeds keys like
|
|
12
|
+
* `node_modules/uuid/dist/index.js`; we normalize, sanity-check, and copy each
|
|
13
|
+
* entry under the project root.
|
|
14
|
+
*/
|
|
15
|
+
export function installDependenciesIntoMemFS(
|
|
16
|
+
host: IMemFSHost,
|
|
17
|
+
workDir: string,
|
|
18
|
+
props: ICompilerService.IInstallDependenciesProps,
|
|
19
|
+
): ICompilerService.IInstallDependenciesResult {
|
|
20
|
+
let fileCount = 0;
|
|
21
|
+
for (const [rel, text] of Object.entries(props.files)) {
|
|
22
|
+
const normalized = normalizeNodeModulePath(rel);
|
|
23
|
+
if (!normalized) continue;
|
|
24
|
+
host.writeFile(`${workDir}/${normalized}`, text);
|
|
25
|
+
fileCount++;
|
|
26
|
+
}
|
|
27
|
+
return { installed: props.packages, fileCount };
|
|
28
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { IMemFSHost } from "@ttsc/wasm";
|
|
2
|
+
|
|
3
|
+
import type { IInstallTypiaSourcePackOptions } from "../structures/IInstallTypiaSourcePackOptions";
|
|
4
|
+
import { loadTypiaSourcePack } from "./loadTypiaSourcePack";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Mounts typia + @typia/* source trees into the in-browser MemFS so the
|
|
8
|
+
* wasm-side compiler can resolve `import typia, { tags } from "typia"` against
|
|
9
|
+
* the same code the published package uses.
|
|
10
|
+
*
|
|
11
|
+
* Idempotent on the same host (re-writing the same paths is a no-op for the
|
|
12
|
+
* MemFS implementation).
|
|
13
|
+
*/
|
|
14
|
+
export async function installTypiaSourcePack(
|
|
15
|
+
host: IMemFSHost,
|
|
16
|
+
options: IInstallTypiaSourcePackOptions,
|
|
17
|
+
): Promise<void> {
|
|
18
|
+
const mountRoot = options.mountRoot ?? "/work/node_modules";
|
|
19
|
+
const pack = await loadTypiaSourcePack(options);
|
|
20
|
+
host.mkdirp(mountRoot);
|
|
21
|
+
for (const [rel, content] of Object.entries(pack)) {
|
|
22
|
+
host.writeFile(`${mountRoot}/${rel}`, content);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { ICompilerService } from "../../structures/ICompilerService";
|
|
2
|
+
|
|
3
|
+
// Match `file:line:col - severity TS<code>: [rule-name] message` lines from
|
|
4
|
+
// tsgo's pretty diagnostic renderer. ANSI color escapes are stripped first
|
|
5
|
+
// so the matcher works on the exact text rendered in the Lint tab.
|
|
6
|
+
const LINT_LINE_REGEXP =
|
|
7
|
+
/([^\s:]+):(\d+):(\d+)\s+-\s+(error|warning)\s+TS(\d+):\s+(?:\[([^\]]+)\]\s+)?(.*)$/;
|
|
8
|
+
// Strip real ANSI SGR sequences, which always start with ESC (0x1b). The
|
|
9
|
+
// leading ESC byte is required — `/\[[0-9;]*m/g` would also chew innocuous
|
|
10
|
+
// substrings like "[m" or "[0;1m" inside diagnostic messages, and would
|
|
11
|
+
// fail to actually strip the ESC bytes tsgo writes, leaving the
|
|
12
|
+
// LINT_LINE_REGEXP unable to anchor on the filename column.
|
|
13
|
+
const ANSI_REGEXP = /\x1b\[[0-9;]*m/g;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Parse the lint plugin's stderr (tsgo-style pretty diagnostics) into the
|
|
17
|
+
* playground's normalized diagnostic shape.
|
|
18
|
+
*/
|
|
19
|
+
export function parseLintDiagnostics(
|
|
20
|
+
stderr: string,
|
|
21
|
+
source: string,
|
|
22
|
+
): ICompilerService.IDiagnostic[] {
|
|
23
|
+
const stripped = stderr.replace(ANSI_REGEXP, "");
|
|
24
|
+
const lines = stripped.split(/\r?\n/);
|
|
25
|
+
const out: ICompilerService.IDiagnostic[] = [];
|
|
26
|
+
for (const line of lines) {
|
|
27
|
+
const m = line.match(LINT_LINE_REGEXP);
|
|
28
|
+
if (!m) continue;
|
|
29
|
+
const [, , lineStr, colStr, sev, codeStr, rule, message] = m;
|
|
30
|
+
const lineNum = Number(lineStr);
|
|
31
|
+
const colNum = Number(colStr);
|
|
32
|
+
if (!Number.isFinite(lineNum) || !Number.isFinite(colNum)) continue;
|
|
33
|
+
out.push({
|
|
34
|
+
line: lineNum,
|
|
35
|
+
column: colNum,
|
|
36
|
+
length: lengthOfTokenAt(source, lineNum, colNum) ?? 1,
|
|
37
|
+
severity: sev === "warning" ? "warning" : "error",
|
|
38
|
+
message: rule ? `[${rule}] ${message ?? ""}` : (message ?? ""),
|
|
39
|
+
code: `TS${codeStr ?? ""}`,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return out;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Best-effort length of the identifier-like token starting at (`line`,
|
|
47
|
+
* `column`) in `source`. Falls back to 1 when no token is found.
|
|
48
|
+
*/
|
|
49
|
+
function lengthOfTokenAt(
|
|
50
|
+
source: string,
|
|
51
|
+
line: number,
|
|
52
|
+
column: number,
|
|
53
|
+
): number | null {
|
|
54
|
+
const lines = source.split(/\r?\n/);
|
|
55
|
+
if (line < 1 || line > lines.length) return null;
|
|
56
|
+
const text = lines[line - 1] ?? "";
|
|
57
|
+
const start = Math.max(0, column - 1);
|
|
58
|
+
const match = text.slice(start).match(/^[\w$]+/);
|
|
59
|
+
if (!match) return null;
|
|
60
|
+
return match[0].length;
|
|
61
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Shape of the typia transform plugin's stdout JSON payload. */
|
|
2
|
+
export interface ITypiaTransformOutput {
|
|
3
|
+
diagnostics?: unknown;
|
|
4
|
+
typescript: Record<string, string>;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Parse the typia transform plugin's stdout into the expected shape. Returns
|
|
9
|
+
* null on any parse / shape mismatch — the caller treats null as "transform
|
|
10
|
+
* produced no usable output".
|
|
11
|
+
*/
|
|
12
|
+
export function safeParseTypiaTransform(
|
|
13
|
+
text: string,
|
|
14
|
+
): ITypiaTransformOutput | null {
|
|
15
|
+
try {
|
|
16
|
+
const parsed = JSON.parse(text) as ITypiaTransformOutput;
|
|
17
|
+
if (parsed && typeof parsed === "object" && parsed.typescript) {
|
|
18
|
+
return parsed;
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
} catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Compute 1-based line/column for a 0-based char offset into `source`. */
|
|
2
|
+
export function lineColumnOf(
|
|
3
|
+
source: string,
|
|
4
|
+
start: number | undefined,
|
|
5
|
+
): { line: number; column: number } {
|
|
6
|
+
if (typeof start !== "number" || start < 0) return { line: 1, column: 1 };
|
|
7
|
+
const slice = source.slice(0, Math.min(start, source.length));
|
|
8
|
+
const newlines = slice.match(/\n/g);
|
|
9
|
+
const line = newlines ? newlines.length + 1 : 1;
|
|
10
|
+
const lastNewline = slice.lastIndexOf("\n");
|
|
11
|
+
const column =
|
|
12
|
+
lastNewline === -1 ? slice.length + 1 : slice.length - lastNewline;
|
|
13
|
+
return { line, column };
|
|
14
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { IInstallTypiaSourcePackOptions } from "../structures/IInstallTypiaSourcePackOptions";
|
|
2
|
+
|
|
3
|
+
const packCache = new Map<string, Promise<Record<string, string>>>();
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Fetch the typia source pack JSON. Cached per URL across calls. On rejection
|
|
7
|
+
* the cache entry is cleared so the next call retries — otherwise a transient
|
|
8
|
+
* fetch failure during the first boot would wedge every later boot through
|
|
9
|
+
* `getBoot`'s typiaPlugin.mount path.
|
|
10
|
+
*/
|
|
11
|
+
export function loadTypiaSourcePack(
|
|
12
|
+
options: IInstallTypiaSourcePackOptions,
|
|
13
|
+
): Promise<Record<string, string>> {
|
|
14
|
+
const cached = packCache.get(options.url);
|
|
15
|
+
if (cached) return cached;
|
|
16
|
+
const fetchImpl = options.fetch ?? globalThis.fetch?.bind(globalThis);
|
|
17
|
+
if (!fetchImpl) {
|
|
18
|
+
throw new Error(
|
|
19
|
+
"loadTypiaSourcePack: no fetch implementation available in this environment.",
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
const url = options.url;
|
|
23
|
+
const promise = (async () => {
|
|
24
|
+
const response = await fetchImpl(url);
|
|
25
|
+
if (!response.ok) {
|
|
26
|
+
throw new Error(
|
|
27
|
+
`loadTypiaSourcePack: failed to fetch ${url}: ${response.status}`,
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
return (await response.json()) as Record<string, string>;
|
|
31
|
+
})().catch((err) => {
|
|
32
|
+
packCache.delete(url);
|
|
33
|
+
throw err;
|
|
34
|
+
});
|
|
35
|
+
packCache.set(url, promise);
|
|
36
|
+
return promise;
|
|
37
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ITtscCompileResult } from "@ttsc/wasm";
|
|
2
|
+
|
|
3
|
+
import type { ICompilerService } from "../structures/ICompilerService";
|
|
4
|
+
import { lineColumnOf } from "./lineColumnOf";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Convert a `@ttsc/wasm` diagnostic into the playground's normalized shape.
|
|
8
|
+
*
|
|
9
|
+
* Falls back to computing line/column from the source text when the wasm
|
|
10
|
+
* diagnostic did not carry them (some plugin emitters drop them).
|
|
11
|
+
*/
|
|
12
|
+
export function mapDiagnostic(
|
|
13
|
+
diag: NonNullable<ITtscCompileResult["diagnostics"]>[number],
|
|
14
|
+
source: string,
|
|
15
|
+
): ICompilerService.IDiagnostic {
|
|
16
|
+
const fallback = lineColumnOf(source, diag.start);
|
|
17
|
+
return {
|
|
18
|
+
line: diag.line && diag.line > 0 ? diag.line : fallback.line,
|
|
19
|
+
column:
|
|
20
|
+
diag.character && diag.character > 0 ? diag.character : fallback.column,
|
|
21
|
+
length: typeof diag.length === "number" ? diag.length : 1,
|
|
22
|
+
severity: diag.category === "warning" ? "warning" : "error",
|
|
23
|
+
message: diag.messageText,
|
|
24
|
+
code: typeof diag.code === "number" ? `TS${diag.code}` : String(diag.code),
|
|
25
|
+
};
|
|
26
|
+
}
|