@seanhogg/builderforce-webcontainers-core 2026.10.0

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.
Files changed (78) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +20 -0
  3. package/dist/attribution.d.ts +22 -0
  4. package/dist/attribution.d.ts.map +1 -0
  5. package/dist/attribution.js +50 -0
  6. package/dist/attribution.js.map +1 -0
  7. package/dist/compileScript.d.ts +31 -0
  8. package/dist/compileScript.d.ts.map +1 -0
  9. package/dist/compileScript.js +75 -0
  10. package/dist/compileScript.js.map +1 -0
  11. package/dist/detectProject.d.ts +16 -0
  12. package/dist/detectProject.d.ts.map +1 -0
  13. package/dist/detectProject.js +49 -0
  14. package/dist/detectProject.js.map +1 -0
  15. package/dist/devServer.d.ts +46 -0
  16. package/dist/devServer.d.ts.map +1 -0
  17. package/dist/devServer.js +136 -0
  18. package/dist/devServer.js.map +1 -0
  19. package/dist/html.d.ts +17 -0
  20. package/dist/html.d.ts.map +1 -0
  21. package/dist/html.js +46 -0
  22. package/dist/html.js.map +1 -0
  23. package/dist/index.d.ts +21 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +14 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/mime.d.ts +3 -0
  28. package/dist/mime.d.ts.map +1 -0
  29. package/dist/mime.js +35 -0
  30. package/dist/mime.js.map +1 -0
  31. package/dist/packageCdn.d.ts +19 -0
  32. package/dist/packageCdn.d.ts.map +1 -0
  33. package/dist/packageCdn.js +29 -0
  34. package/dist/packageCdn.js.map +1 -0
  35. package/dist/paths.d.ts +12 -0
  36. package/dist/paths.d.ts.map +1 -0
  37. package/dist/paths.js +36 -0
  38. package/dist/paths.js.map +1 -0
  39. package/dist/projectConfig.d.ts +36 -0
  40. package/dist/projectConfig.d.ts.map +1 -0
  41. package/dist/projectConfig.js +155 -0
  42. package/dist/projectConfig.js.map +1 -0
  43. package/dist/resolve.d.ts +19 -0
  44. package/dist/resolve.d.ts.map +1 -0
  45. package/dist/resolve.js +62 -0
  46. package/dist/resolve.js.map +1 -0
  47. package/dist/rewriteImports.d.ts +11 -0
  48. package/dist/rewriteImports.d.ts.map +1 -0
  49. package/dist/rewriteImports.js +28 -0
  50. package/dist/rewriteImports.js.map +1 -0
  51. package/dist/styleModules.d.ts +12 -0
  52. package/dist/styleModules.d.ts.map +1 -0
  53. package/dist/styleModules.js +53 -0
  54. package/dist/styleModules.js.map +1 -0
  55. package/dist/transformer.d.ts +42 -0
  56. package/dist/transformer.d.ts.map +1 -0
  57. package/dist/transformer.js +39 -0
  58. package/dist/transformer.js.map +1 -0
  59. package/dist/vfs.d.ts +57 -0
  60. package/dist/vfs.d.ts.map +1 -0
  61. package/dist/vfs.js +114 -0
  62. package/dist/vfs.js.map +1 -0
  63. package/package.json +67 -0
  64. package/src/attribution.ts +57 -0
  65. package/src/compileScript.ts +87 -0
  66. package/src/detectProject.ts +66 -0
  67. package/src/devServer.ts +156 -0
  68. package/src/html.ts +58 -0
  69. package/src/index.ts +20 -0
  70. package/src/mime.ts +36 -0
  71. package/src/packageCdn.ts +48 -0
  72. package/src/paths.ts +37 -0
  73. package/src/projectConfig.ts +157 -0
  74. package/src/resolve.ts +60 -0
  75. package/src/rewriteImports.ts +26 -0
  76. package/src/styleModules.ts +57 -0
  77. package/src/transformer.ts +76 -0
  78. package/src/vfs.ts +135 -0
@@ -0,0 +1,76 @@
1
+ /**
2
+ * The compiler, as a PORT. The core never imports esbuild: the browser runtime
3
+ * plugs in esbuild-wasm, tests plug in native esbuild, and a fork can plug in SWC
4
+ * or anything else that turns TS/JSX into an ES module.
5
+ */
6
+ export type Loader = 'js' | 'jsx' | 'ts' | 'tsx';
7
+
8
+ export interface TransformRequest {
9
+ loader: Loader;
10
+ /** The VFS path — shows up in error messages and source maps. */
11
+ sourcefile: string;
12
+ /** Compile-time replacements, esbuild `define` semantics (values are JS source). */
13
+ define: Record<string, string>;
14
+ jsx: { runtime: 'automatic' | 'classic'; importSource: string };
15
+ }
16
+
17
+ export interface Transformer {
18
+ transform(code: string, request: TransformRequest): Promise<{ code: string }>;
19
+ }
20
+
21
+ /** The subset of esbuild's (and esbuild-wasm's) API the adapter uses. */
22
+ export interface EsbuildLike {
23
+ transform(
24
+ code: string,
25
+ options: {
26
+ loader: Loader;
27
+ sourcefile: string;
28
+ format: 'esm';
29
+ target: string;
30
+ define: Record<string, string>;
31
+ jsx: 'automatic' | 'transform';
32
+ jsxImportSource?: string;
33
+ sourcemap: 'inline';
34
+ },
35
+ ): Promise<{ code: string }>;
36
+ }
37
+
38
+ /** Adapt esbuild or esbuild-wasm to {@link Transformer}. */
39
+ export function createEsbuildTransformer(esbuild: EsbuildLike): Transformer {
40
+ return {
41
+ transform(code, request) {
42
+ return esbuild.transform(code, {
43
+ loader: request.loader,
44
+ sourcefile: request.sourcefile,
45
+ format: 'esm',
46
+ target: 'es2020',
47
+ define: request.define,
48
+ jsx: request.jsx.runtime === 'automatic' ? 'automatic' : 'transform',
49
+ ...(request.jsx.runtime === 'automatic' ? { jsxImportSource: request.jsx.importSource } : {}),
50
+ sourcemap: 'inline',
51
+ });
52
+ },
53
+ };
54
+ }
55
+
56
+ /** The loader for a source extension, or undefined when the file is not compiled. */
57
+ export function loaderFor(ext: string): Loader | undefined {
58
+ switch (ext) {
59
+ case '.ts':
60
+ case '.mts':
61
+ case '.cts':
62
+ return 'ts';
63
+ case '.tsx':
64
+ return 'tsx';
65
+ // `.js` gets the JSX loader: Create React App puts JSX in `.js` files, and the
66
+ // JSX loader accepts plain JavaScript unchanged.
67
+ case '.js':
68
+ case '.jsx':
69
+ return 'jsx';
70
+ case '.mjs':
71
+ case '.cjs':
72
+ return 'js';
73
+ default:
74
+ return undefined;
75
+ }
76
+ }
package/src/vfs.ts ADDED
@@ -0,0 +1,135 @@
1
+ import { normalizePath, join } from './paths.js';
2
+
3
+ export type FileContent = string | Uint8Array;
4
+
5
+ /** The nested tree shape `@webcontainer/api`'s `mount()` takes — accepted as-is. */
6
+ export interface FileSystemTree {
7
+ [name: string]: { file: { contents: FileContent } } | { directory: FileSystemTree };
8
+ }
9
+
10
+ /** A flat `{ 'src/main.tsx': '...' }` map, the other shape callers commonly hold. */
11
+ export type FlatFiles = Record<string, FileContent>;
12
+
13
+ export interface FsChange {
14
+ type: 'write' | 'remove';
15
+ path: string;
16
+ /** For a write: the file did not exist before. A new file can change how imports resolve. */
17
+ created?: boolean;
18
+ }
19
+
20
+ const decoder = new TextDecoder();
21
+
22
+ /**
23
+ * An in-memory file system. Files only — a directory exists while it contains a
24
+ * file, which keeps the store a single map and makes "is this a directory?" a
25
+ * prefix check rather than a second structure to keep consistent.
26
+ *
27
+ * Every write bumps a per-file version, so a transform cache can key on
28
+ * (path, version) instead of hashing contents.
29
+ */
30
+ export class VirtualFileSystem {
31
+ private readonly files = new Map<string, FileContent>();
32
+ private readonly versions = new Map<string, number>();
33
+ private readonly listeners = new Set<(change: FsChange) => void>();
34
+ private clock = 0;
35
+
36
+ writeFile(path: string, contents: FileContent): void {
37
+ const key = normalizePath(path);
38
+ const created = !this.files.has(key);
39
+ this.files.set(key, contents);
40
+ this.versions.set(key, ++this.clock);
41
+ this.emit({ type: 'write', path: key, created });
42
+ }
43
+
44
+ readFile(path: string): FileContent | undefined {
45
+ return this.files.get(normalizePath(path));
46
+ }
47
+
48
+ readText(path: string): string | undefined {
49
+ const contents = this.readFile(path);
50
+ if (contents === undefined) return undefined;
51
+ return typeof contents === 'string' ? contents : decoder.decode(contents);
52
+ }
53
+
54
+ isFile(path: string): boolean {
55
+ return this.files.has(normalizePath(path));
56
+ }
57
+
58
+ isDirectory(path: string): boolean {
59
+ const key = normalizePath(path);
60
+ if (key === '/') return true;
61
+ const prefix = key + '/';
62
+ for (const file of this.files.keys()) if (file.startsWith(prefix)) return true;
63
+ return false;
64
+ }
65
+
66
+ exists(path: string): boolean {
67
+ return this.isFile(path) || this.isDirectory(path);
68
+ }
69
+
70
+ /** Direct children of a directory (files and sub-directories), sorted. */
71
+ readdir(path: string): string[] {
72
+ const key = normalizePath(path);
73
+ const prefix = key === '/' ? '/' : key + '/';
74
+ const names = new Set<string>();
75
+ for (const file of this.files.keys()) {
76
+ if (!file.startsWith(prefix)) continue;
77
+ const rest = file.slice(prefix.length);
78
+ const name = rest.split('/')[0];
79
+ if (name) names.add(name);
80
+ }
81
+ return [...names].sort();
82
+ }
83
+
84
+ /** Every file path, sorted. */
85
+ list(): string[] {
86
+ return [...this.files.keys()].sort();
87
+ }
88
+
89
+ /** Remove a file, or a directory and everything under it. */
90
+ rm(path: string): void {
91
+ const key = normalizePath(path);
92
+ const prefix = key === '/' ? '/' : key + '/';
93
+ for (const file of [...this.files.keys()]) {
94
+ if (file === key || file.startsWith(prefix)) {
95
+ this.files.delete(file);
96
+ this.versions.delete(file);
97
+ this.emit({ type: 'remove', path: file });
98
+ }
99
+ }
100
+ }
101
+
102
+ /** The file's version (bumped on every write), or undefined when absent. */
103
+ version(path: string): number | undefined {
104
+ return this.versions.get(normalizePath(path));
105
+ }
106
+
107
+ /**
108
+ * Write many files at once. Takes either the nested WebContainer tree or a flat
109
+ * path→contents map — or a mix, since each entry is judged on its own.
110
+ */
111
+ mount(files: FileSystemTree | FlatFiles, at = '/'): void {
112
+ for (const [name, entry] of Object.entries(files)) {
113
+ const path = join(at, name);
114
+ if (typeof entry === 'string' || entry instanceof Uint8Array) this.writeFile(path, entry);
115
+ else if ('file' in entry) this.writeFile(path, entry.file.contents);
116
+ else this.mount(entry.directory, path);
117
+ }
118
+ }
119
+
120
+ /** Flat snapshot keyed without the leading slash (`src/main.tsx`). */
121
+ toFlat(): FlatFiles {
122
+ const out: FlatFiles = {};
123
+ for (const [path, contents] of this.files) out[path.slice(1)] = contents;
124
+ return out;
125
+ }
126
+
127
+ watch(listener: (change: FsChange) => void): () => void {
128
+ this.listeners.add(listener);
129
+ return () => this.listeners.delete(listener);
130
+ }
131
+
132
+ private emit(change: FsChange): void {
133
+ for (const listener of this.listeners) listener(change);
134
+ }
135
+ }