module-tsx 0.0.1 → 0.0.3
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 +79 -11
- package/dist/index.d.ts +37 -6
- package/dist/index.js +298 -76
- package/dist/index.mjs +454 -434
- package/dist/index.umd.js +117 -113
- package/package.json +9 -7
package/README.md
CHANGED
|
@@ -1,31 +1,30 @@
|
|
|
1
1
|
# module-tsx
|
|
2
2
|
|
|
3
|
-
Run TypeScript (and React)
|
|
3
|
+
Run TypeScript (and React) modules directly in the browser, without a build step.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Usage
|
|
6
6
|
|
|
7
7
|
```html
|
|
8
|
-
<!-- Load
|
|
8
|
+
<!-- Load this library -->
|
|
9
9
|
<script type="module" src="https://esm.sh/module-tsx"></script>
|
|
10
10
|
|
|
11
11
|
<div id="root"></div>
|
|
12
12
|
|
|
13
|
-
<!-- Write
|
|
13
|
+
<!-- Write your TypeScript/TSX inline -->
|
|
14
14
|
<script type="module-tsx">
|
|
15
|
-
import React from "react"; //
|
|
16
|
-
import { createRoot } from "react-dom/client";
|
|
17
|
-
import App from "./src/App.tsx"; //
|
|
18
|
-
import lib from "https://my.cdn.domain"; // <- Direct http import will NOT be compiled
|
|
15
|
+
import React from "react"; // bare specifier → https://esm.sh/react
|
|
16
|
+
import { createRoot } from "react-dom/client";
|
|
17
|
+
import App from "./src/App.tsx"; // relative imports are fetched and compiled on the fly
|
|
19
18
|
|
|
20
|
-
const root = document.getElementById("root") as HTMLDivElement;
|
|
19
|
+
const root = document.getElementById("root") as HTMLDivElement;
|
|
21
20
|
createRoot(root).render(
|
|
22
21
|
<React.StrictMode>
|
|
23
22
|
<App />
|
|
24
23
|
</React.StrictMode>
|
|
25
|
-
// ^ You can also write JSX/TSX directly
|
|
26
24
|
);
|
|
27
25
|
</script>
|
|
28
|
-
|
|
26
|
+
|
|
27
|
+
<!-- OR load an external file -->
|
|
29
28
|
<script type="module-tsx" src="./main.tsx"></script>
|
|
30
29
|
```
|
|
31
30
|
|
|
@@ -37,3 +36,72 @@ export default function App() {
|
|
|
37
36
|
return <div>Hello, module-tsx!</div>;
|
|
38
37
|
}
|
|
39
38
|
```
|
|
39
|
+
|
|
40
|
+
## Features
|
|
41
|
+
|
|
42
|
+
- **TypeScript & JSX/TSX** — transpiled on the fly using the TypeScript compiler
|
|
43
|
+
- **Bare specifier resolution** — `import "react"` is automatically rewritten to a CDN URL (defaults to `https://esm.sh/`)
|
|
44
|
+
- **Relative imports** — `.ts`/`.tsx` files are fetched and compiled recursively
|
|
45
|
+
- **CSS imports** — `import "./style.css"` injects a `<style>` tag; `import "./style.module.css"` returns a CSS Modules object
|
|
46
|
+
- **Import map support** — respects `<script type="importmap">` on the page for specifier overrides
|
|
47
|
+
- **Auto React import** — JSX is detected and `import React from "react"` is injected automatically if missing
|
|
48
|
+
|
|
49
|
+
## Programmatic API
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import { ModuleTSX } from "module-tsx";
|
|
53
|
+
|
|
54
|
+
const m = new ModuleTSX({
|
|
55
|
+
baseUrl: location.href, // base for resolving relative specifiers
|
|
56
|
+
importMap: { imports: { react: "https://esm.sh/react" } }, // merged with page importmaps
|
|
57
|
+
resolveBareSpecifier: "https://cdn.jsdelivr.net/npm/", // string prefix or function
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Import a module by specifier
|
|
61
|
+
const react = await m.import("react");
|
|
62
|
+
const app = await m.import("./app.tsx");
|
|
63
|
+
|
|
64
|
+
// Import from an in-memory string
|
|
65
|
+
const { x } = await m.importCode(
|
|
66
|
+
document.location.href,
|
|
67
|
+
`export const x: number = 1`,
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
// Events
|
|
71
|
+
m.addEventListener("import", (e) => console.log("loading", e.detail.id));
|
|
72
|
+
m.addEventListener("import:error", (e) =>
|
|
73
|
+
console.error("failed", e.detail.id, e.detail.error),
|
|
74
|
+
);
|
|
75
|
+
m.addEventListener("transform", (e) =>
|
|
76
|
+
console.log("compiling", e.detail.sourceUrl),
|
|
77
|
+
);
|
|
78
|
+
m.addEventListener("transform:error", (e) =>
|
|
79
|
+
console.error("compile error", e.detail.sourceUrl, e.detail.error),
|
|
80
|
+
);
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Use with AI
|
|
84
|
+
|
|
85
|
+
module-tsx is well-suited for AI-generated pages — an AI can produce a single, self-contained HTML file that runs TypeScript and React directly in the browser with no build step or configuration.
|
|
86
|
+
|
|
87
|
+
To use the skill, run
|
|
88
|
+
|
|
89
|
+
```sh
|
|
90
|
+
npx skills add yieldray/module-tsx
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## CDN
|
|
94
|
+
|
|
95
|
+
```html
|
|
96
|
+
<!-- ESM -->
|
|
97
|
+
<script type="module" src="https://esm.sh/module-tsx"></script>
|
|
98
|
+
|
|
99
|
+
<!-- ESM (self-contained, no external dependencies) -->
|
|
100
|
+
<script
|
|
101
|
+
type="module"
|
|
102
|
+
src="https://raw.esm.sh/module-tsx/dist/index.mjs"
|
|
103
|
+
></script>
|
|
104
|
+
|
|
105
|
+
<!-- UMD (exposes window.ModuleTSX) -->
|
|
106
|
+
<script src="https://raw.esm.sh/module-tsx/dist/index.umd.js"></script>
|
|
107
|
+
```
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,28 @@
|
|
|
1
1
|
//#region src/importmap.d.ts
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
type ModuleSpecifierMap = Map<string, URL | null>;
|
|
3
|
+
interface SpecifierResolutionRecord {
|
|
4
|
+
serializedBaseURL: string;
|
|
5
|
+
specifier: string;
|
|
6
|
+
specifierAsURL: URL | null;
|
|
7
|
+
}
|
|
8
|
+
declare class ImportMap {
|
|
9
|
+
imports: ModuleSpecifierMap;
|
|
10
|
+
scopes: Map<string, ModuleSpecifierMap>;
|
|
11
|
+
integrity: Map<string, string>;
|
|
12
|
+
constructor(imports?: ModuleSpecifierMap, scopes?: Map<string, ModuleSpecifierMap>, integrity?: Map<string, string>);
|
|
13
|
+
/** Parse a JSON import map string against a base URL. */
|
|
14
|
+
static parse(input: string, baseURL: URL | string): ImportMap;
|
|
15
|
+
/** Build an ImportMap from a plain object (same shape as the JSON format). */
|
|
16
|
+
static of(json: object, baseURL: URL | string): ImportMap;
|
|
17
|
+
/** Merge newImportMap into oldImportMap in place (spec § "merge existing and new import maps"). */
|
|
18
|
+
static merge(oldImportMap: ImportMap, newImportMap: ImportMap, resolvedModuleSet?: ReadonlyArray<SpecifierResolutionRecord>): void;
|
|
19
|
+
/** Resolve a specifier against an import map (spec § "resolve a module specifier"). */
|
|
20
|
+
static resolve(specifier: string, importMap: ImportMap, baseURL: string | URL): {
|
|
21
|
+
url: string;
|
|
22
|
+
record: SpecifierResolutionRecord;
|
|
23
|
+
} | undefined;
|
|
24
|
+
/** Parse all <script type="importmap"> elements from the DOM. */
|
|
25
|
+
static fromDOM(): ImportMap;
|
|
5
26
|
}
|
|
6
27
|
//#endregion
|
|
7
28
|
//#region src/module-tsx.d.ts
|
|
@@ -12,7 +33,7 @@ interface ModuleTSXConfig {
|
|
|
12
33
|
*/
|
|
13
34
|
baseUrl?: string;
|
|
14
35
|
fetch?: (fullURL: string) => Promise<Response>;
|
|
15
|
-
importMap?:
|
|
36
|
+
importMap?: ImportMap;
|
|
16
37
|
/**
|
|
17
38
|
* Given a bare specifier, return a full URL to load the module.
|
|
18
39
|
* This can be used to convert for example import "react" to import "https://esm.sh/react".
|
|
@@ -22,16 +43,20 @@ interface ModuleTSXConfig {
|
|
|
22
43
|
resolveBareSpecifier?: string | ((specifier: string) => string);
|
|
23
44
|
}
|
|
24
45
|
interface ModuleTSXEventMap {
|
|
46
|
+
/** Fired when an import starts. `id` is the original specifier as passed by the caller. */
|
|
25
47
|
import: CustomEvent<{
|
|
26
48
|
id: string;
|
|
27
49
|
}>;
|
|
50
|
+
/** Fired when an import fails. `id` is the specifier at the point of failure. */
|
|
28
51
|
"import:error": CustomEvent<{
|
|
29
52
|
id: string;
|
|
30
53
|
error: any;
|
|
31
54
|
}>;
|
|
55
|
+
/** Fired when a source file starts being transpiled. */
|
|
32
56
|
transform: CustomEvent<{
|
|
33
57
|
sourceUrl: string;
|
|
34
58
|
}>;
|
|
59
|
+
/** Fired when transpilation fails. */
|
|
35
60
|
"transform:error": CustomEvent<{
|
|
36
61
|
sourceUrl: string;
|
|
37
62
|
error: any;
|
|
@@ -39,15 +64,20 @@ interface ModuleTSXEventMap {
|
|
|
39
64
|
}
|
|
40
65
|
interface IModuleTSX extends EventTarget {
|
|
41
66
|
addEventListener<T extends keyof ModuleTSXEventMap>(type: T, listener: (this: ModuleTSX, ev: ModuleTSXEventMap[T]) => any, options?: boolean | AddEventListenerOptions): void;
|
|
67
|
+
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
|
|
42
68
|
}
|
|
43
69
|
declare class ModuleTSX extends EventTarget implements IModuleTSX {
|
|
44
70
|
readonly baseUrl: string;
|
|
45
|
-
readonly importMap:
|
|
71
|
+
readonly importMap: ImportMap;
|
|
46
72
|
readonly fetch: (url: string) => Promise<Response>;
|
|
47
73
|
readonly resolveBareSpecifier: (specifier: string) => string;
|
|
74
|
+
private readonly resolvedModuleSet;
|
|
48
75
|
private readonly sourceTracker;
|
|
49
76
|
private readonly fetchText;
|
|
50
77
|
constructor(config?: ModuleTSXConfig);
|
|
78
|
+
/** Add a new import map, merging it into the existing one per the spec.
|
|
79
|
+
* Rules that conflict with already-resolved modules are silently dropped. */
|
|
80
|
+
addImportMap(newImportMap: ImportMap): void;
|
|
51
81
|
private emit;
|
|
52
82
|
import(id: string, options?: any): Promise<any>;
|
|
53
83
|
importCode(sourceUrl: string, code: string, options?: any): Promise<any>;
|
|
@@ -55,6 +85,7 @@ declare class ModuleTSX extends EventTarget implements IModuleTSX {
|
|
|
55
85
|
private transformSourceModule;
|
|
56
86
|
private getLoaderByResourceType;
|
|
57
87
|
private tsxLoader;
|
|
88
|
+
private resolveLocalUrl;
|
|
58
89
|
private resolveSpecifier;
|
|
59
90
|
private resolveSpecifiers;
|
|
60
91
|
}
|
|
@@ -71,4 +102,4 @@ declare class ModuleTSXError extends Error {
|
|
|
71
102
|
*/
|
|
72
103
|
declare const instance: ModuleTSX;
|
|
73
104
|
//#endregion
|
|
74
|
-
export { ModuleTSX, ModuleTSXError, instance };
|
|
105
|
+
export { ImportMap, ModuleTSX, ModuleTSXError, instance };
|