@utoo/web 1.0.0 → 1.0.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.
@@ -0,0 +1,29 @@
1
+ import { PackFile, ProjectEndpoint, ProjectOptions } from "./type";
2
+ import initWasm, { Project as ProjectInternal } from "./utoo";
3
+ declare class InternalEndpoint implements ProjectEndpoint {
4
+ projectInternal?: ProjectInternal;
5
+ wasmInit?: ReturnType<typeof initWasm>;
6
+ options?: Omit<ProjectOptions, "workerUrl" | "serviceWorker">;
7
+ mount(opt: Omit<ProjectOptions, "workerUrl" | "serviceWorker">): Promise<void>;
8
+ install(packageLock: string, maxConcurrentDownloads?: number): Promise<void>;
9
+ build(): Promise<any>;
10
+ readFile(path: string, encoding?: "utf8"): Promise<any>;
11
+ writeFile(path: string, content: string | Uint8Array, _encoding?: "utf8"): Promise<void>;
12
+ copyFile(src: string, dst: string): Promise<void>;
13
+ readdir(path: string, options?: {
14
+ recursive?: boolean;
15
+ }): Promise<any>;
16
+ mkdir(path: string, options?: {
17
+ recursive?: boolean;
18
+ }): Promise<void>;
19
+ rm(path: string, options?: {
20
+ recursive?: boolean;
21
+ }): Promise<void>;
22
+ rmdir(path: string, options?: {
23
+ recursive?: boolean;
24
+ }): Promise<void>;
25
+ gzip(files: PackFile[]): Promise<Uint8Array<ArrayBufferLike>>;
26
+ sigMd5(content: Uint8Array): Promise<string>;
27
+ }
28
+ declare const internalEndpoint: InternalEndpoint;
29
+ export { internalEndpoint };
@@ -0,0 +1,184 @@
1
+ import initWasm, { init_log_filter, Project as ProjectInternal, recvPoolRequest, recvWorkerTermination, } from "./utoo";
2
+ // @ts-ignore
3
+ import webpackLoadersCode from "./webpackLoaders/workerContent";
4
+ let nextWorkerId = 0;
5
+ const loaderWorkers = {};
6
+ async function getTurbopackLoaderAssets() {
7
+ const turbopackLoaderAssets = {};
8
+ const dirEntries = await internalEndpoint.projectInternal.readDir(".turbopack");
9
+ await Promise.all(dirEntries.map(async (entry) => {
10
+ // only collect .js files
11
+ if (entry.type === "file" && entry.name.endsWith(".js")) {
12
+ turbopackLoaderAssets[`./${entry.name}`] =
13
+ await internalEndpoint.projectInternal.readToString(`.turbopack/${entry.name}`);
14
+ }
15
+ }));
16
+ return turbopackLoaderAssets;
17
+ }
18
+ const createOrScalePool = async (binding, loadersImportMap) => {
19
+ while (true) {
20
+ try {
21
+ let poolOptions = await recvPoolRequest();
22
+ const { filename: entrypoint, maxConcurrency } = poolOptions;
23
+ const workers = loaderWorkers[entrypoint] || (loaderWorkers[entrypoint] = []);
24
+ if (workers.length < maxConcurrency) {
25
+ for (let i = workers.length; i < maxConcurrency; i++) {
26
+ nextWorkerId += 1;
27
+ const turbopackLoaderAssets = await getTurbopackLoaderAssets();
28
+ const blob = new Blob([webpackLoadersCode], {
29
+ type: "text/javascript",
30
+ });
31
+ const workerUrl = URL.createObjectURL(blob);
32
+ const worker = new Worker(workerUrl);
33
+ worker.postMessage([
34
+ // @ts-ignore
35
+ initWasm.__wbindgen_wasm_module,
36
+ binding.memory,
37
+ {
38
+ workerData: {
39
+ poolId: entrypoint,
40
+ workerId: nextWorkerId,
41
+ },
42
+ loaderAssets: {
43
+ importMaps: { ...turbopackLoaderAssets, ...loadersImportMap },
44
+ entrypoint: entrypoint.replace(".turbopack/", ""),
45
+ },
46
+ },
47
+ ]);
48
+ // @ts-ignore
49
+ worker.workerId = nextWorkerId;
50
+ // @ts-ignore
51
+ workers.push(worker);
52
+ }
53
+ }
54
+ else if (workers.length > maxConcurrency) {
55
+ const workersToStop = workers.splice(0, workers.length - maxConcurrency);
56
+ workersToStop.forEach((worker) => worker.terminate());
57
+ }
58
+ }
59
+ catch (_e) {
60
+ // rust channel closed. do nothing
61
+ return;
62
+ }
63
+ }
64
+ };
65
+ const waitingForWorkerTermination = async () => {
66
+ while (true) {
67
+ try {
68
+ const { filename, workerId } = await recvWorkerTermination();
69
+ const workers = loaderWorkers[filename];
70
+ const workerIdx = workers.findIndex((worker) => worker.workerId === workerId);
71
+ if (workerIdx > -1) {
72
+ const worker = workers.splice(workerIdx, 1);
73
+ worker[0].terminate();
74
+ }
75
+ }
76
+ catch (_e) {
77
+ // rust channel closed. do nothing
78
+ return;
79
+ }
80
+ }
81
+ };
82
+ class InternalEndpoint {
83
+ // This should be called only once
84
+ async mount(opt) {
85
+ var _a;
86
+ this.options = opt;
87
+ const { cwd, wasmUrl, threadWorkerUrl, logFilter } = opt;
88
+ (_a = this.wasmInit) !== null && _a !== void 0 ? _a : (this.wasmInit = initWasm(wasmUrl));
89
+ await this.wasmInit;
90
+ // Initialize log filter after wasm init
91
+ const filter = logFilter || "pack_core=info,pack_api=info,utoo_wasm=info";
92
+ init_log_filter(filter);
93
+ this.projectInternal = new ProjectInternal(cwd, threadWorkerUrl);
94
+ return;
95
+ }
96
+ async install(packageLock, maxConcurrentDownloads) {
97
+ await this.wasmInit;
98
+ await this.projectInternal.install(packageLock, maxConcurrentDownloads);
99
+ return;
100
+ }
101
+ async build() {
102
+ var _a;
103
+ const binding = await this.wasmInit;
104
+ createOrScalePool(binding, (_a = this.options) === null || _a === void 0 ? void 0 : _a.loadersImportMap);
105
+ waitingForWorkerTermination();
106
+ return await this.projectInternal.build();
107
+ }
108
+ async readFile(path, encoding) {
109
+ await this.wasmInit;
110
+ let ret;
111
+ if (encoding === "utf8") {
112
+ ret = await this.projectInternal.readToString(path);
113
+ }
114
+ else {
115
+ ret = await this.projectInternal.read(path);
116
+ }
117
+ return ret;
118
+ }
119
+ async writeFile(path, content, _encoding) {
120
+ await this.wasmInit;
121
+ if (typeof content === "string") {
122
+ return await this.projectInternal.writeString(path, content);
123
+ }
124
+ else {
125
+ return await this.projectInternal.write(path, content);
126
+ }
127
+ }
128
+ async copyFile(src, dst) {
129
+ await this.wasmInit;
130
+ return await this.projectInternal.copyFile(src, dst);
131
+ }
132
+ async readdir(path, options) {
133
+ await this.wasmInit;
134
+ const dirEntries = (options === null || options === void 0 ? void 0 : options.recursive)
135
+ ? await this.projectInternal.readDir(path)
136
+ : // TODO: support recursive readDirAll
137
+ await this.projectInternal.readDir(path);
138
+ const rawDirents = dirEntries.map((e) => {
139
+ const dir = e.toJSON();
140
+ return {
141
+ name: dir.name,
142
+ type: dir.type,
143
+ };
144
+ });
145
+ // WARN: This is a hack, functions can not be structurally cloned
146
+ return rawDirents;
147
+ }
148
+ async mkdir(path, options) {
149
+ await this.wasmInit;
150
+ if (options === null || options === void 0 ? void 0 : options.recursive) {
151
+ return await this.projectInternal.createDirAll(path);
152
+ }
153
+ else {
154
+ return await this.projectInternal.createDir(path);
155
+ }
156
+ }
157
+ async rm(path, options) {
158
+ await this.wasmInit;
159
+ let metadata = (await this.projectInternal.metadata(path)).toJSON();
160
+ switch (metadata.type) {
161
+ case "file":
162
+ return await this.projectInternal.removeFile(path);
163
+ case "directory":
164
+ return await this.projectInternal.removeDir(path, !!(options === null || options === void 0 ? void 0 : options.recursive));
165
+ default:
166
+ // nothing to remove now
167
+ break;
168
+ }
169
+ }
170
+ async rmdir(path, options) {
171
+ await this.wasmInit;
172
+ return await this.projectInternal.removeDir(path, !!(options === null || options === void 0 ? void 0 : options.recursive));
173
+ }
174
+ async gzip(files) {
175
+ await this.wasmInit;
176
+ return await this.projectInternal.gzip(files);
177
+ }
178
+ async sigMd5(content) {
179
+ await this.wasmInit;
180
+ return await this.projectInternal.sigMd5(content);
181
+ }
182
+ }
183
+ const internalEndpoint = new InternalEndpoint();
184
+ export { internalEndpoint };
package/esm/project.d.ts CHANGED
@@ -1,7 +1,8 @@
1
- import { BuildOutput, Dirent, PackFile, ProjectEndpoint, ProjectOptions } from "./type";
1
+ import { BuildOutput, Dirent, PackFile, ProjectEndpoint, ProjectOptions, ServiceWorkerOptions } from "./type";
2
2
  export declare class Project implements ProjectEndpoint {
3
3
  #private;
4
- private serviceWorkerOptions?;
4
+ readonly cwd: string;
5
+ readonly serviceWorkerOptions?: ServiceWorkerOptions;
5
6
  private remote;
6
7
  constructor(options: ProjectOptions);
7
8
  private connectWorker;
package/esm/project.js CHANGED
@@ -22,14 +22,13 @@ export class Project {
22
22
  constructor(options) {
23
23
  var _a, _b;
24
24
  _Project_mount.set(this, void 0);
25
- const { cwd, workerUrl, wasmUrl, threadWorkerUrl, serviceWorker, logFilter, } = options;
25
+ const { cwd, workerUrl, wasmUrl, threadWorkerUrl, serviceWorker, logFilter, loadersImportMap, } = options;
26
+ this.cwd = cwd;
26
27
  this.serviceWorkerOptions = serviceWorker;
27
28
  const { port1, port2 } = new MessageChannel();
28
29
  (_a = this.remote) !== null && _a !== void 0 ? _a : (this.remote = comlink.wrap(port1));
29
30
  if (!ProjectWorker) {
30
- ProjectWorker = workerUrl
31
- ? new Worker(workerUrl)
32
- : new Worker(new URL("./worker", import.meta.url));
31
+ ProjectWorker = new Worker(workerUrl);
33
32
  window.addEventListener("message", (e) => {
34
33
  this.connectWorker(e);
35
34
  });
@@ -44,6 +43,7 @@ export class Project {
44
43
  cwd,
45
44
  wasmUrl,
46
45
  threadWorkerUrl,
46
+ loadersImportMap,
47
47
  logFilter,
48
48
  }), "f");
49
49
  }
@@ -1,10 +1,4 @@
1
1
  import initWasm from "./utoo";
2
- // Read logFilter from URL query string and set to globalThis
3
- const urlParams = new URLSearchParams(self.location.search);
4
- const logFilter = urlParams.get("logFilter");
5
- if (logFilter) {
6
- globalThis.__UTOO_LOG_FILTER__ = logFilter;
7
- }
8
2
  // this is for wasm_thread to spawn new worker thread.
9
3
  // see: https://github.com/utooland/wasm_thread/blob/94438ff771ee0a6a55d79e49a655707970acb615/src/wasm32/js/web_worker.js#L10
10
4
  self.wasm_bindgen = initWasm;
package/esm/type.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Issue } from "@utoo/pack-shared";
2
- import { DirEntryType } from "./utoo";
2
+ import initWasm, { DirEntryType } from "./utoo";
3
3
  export interface RawDirent {
4
4
  name: string;
5
5
  type: DirEntryType;
@@ -42,13 +42,15 @@ export interface ProjectEndpoint {
42
42
  }
43
43
  export interface ProjectOptions {
44
44
  cwd: string;
45
- workerUrl?: string;
45
+ workerUrl: string;
46
46
  threadWorkerUrl: string;
47
47
  wasmUrl?: string;
48
48
  serviceWorker?: ServiceWorkerOptions;
49
49
  logFilter?: string;
50
+ loadersImportMap?: Record<string, string>;
50
51
  }
51
52
  export interface ServiceWorkerOptions {
52
53
  url: string;
53
54
  scope: string;
54
55
  }
56
+ export type Binding = Awaited<ReturnType<typeof initWasm>>;
@@ -1,3 +1,37 @@
1
+ /**
2
+ * @returns {Promise<WorkerTermination>}
3
+ */
4
+ export function recvWorkerTermination(): Promise<WorkerTermination>;
5
+ /**
6
+ * @param {number} task_id
7
+ * @param {number} worker_id
8
+ * @returns {Promise<void>}
9
+ */
10
+ export function notifyWorkerAck(task_id: number, worker_id: number): Promise<void>;
11
+ /**
12
+ * @param {number} task_id
13
+ * @param {string} message
14
+ * @returns {Promise<void>}
15
+ */
16
+ export function sendTaskMessage(task_id: number, message: string): Promise<void>;
17
+ /**
18
+ * @param {number} worker_id
19
+ * @returns {Promise<string>}
20
+ */
21
+ export function recvMessageInWorker(worker_id: number): Promise<string>;
22
+ /**
23
+ * @param {string} pool_id
24
+ * @returns {Promise<number>}
25
+ */
26
+ export function recvWorkerRequest(pool_id: string): Promise<number>;
27
+ /**
28
+ * @returns {Promise<PoolOptions>}
29
+ */
30
+ export function recvPoolRequest(): Promise<PoolOptions>;
31
+ /**
32
+ * @param {string} filter
33
+ */
34
+ export function init_log_filter(filter: string): void;
1
35
  export function init_pack(): void;
2
36
  /**
3
37
  * Entry point for web workers
@@ -45,6 +79,28 @@ export class Metadata {
45
79
  __wbg_ptr: number | undefined;
46
80
  free(): void;
47
81
  }
82
+ export class PoolOptions {
83
+ static __wrap(ptr: any): any;
84
+ __destroy_into_raw(): number | undefined;
85
+ __wbg_ptr: number | undefined;
86
+ free(): void;
87
+ /**
88
+ * @param {string} arg0
89
+ */
90
+ set filename(arg0: string);
91
+ /**
92
+ * @returns {string}
93
+ */
94
+ get filename(): string;
95
+ /**
96
+ * @param {number} arg0
97
+ */
98
+ set maxConcurrency(arg0: number);
99
+ /**
100
+ * @returns {number}
101
+ */
102
+ get maxConcurrency(): number;
103
+ }
48
104
  export class Project {
49
105
  /**
50
106
  * @param {string} cwd
@@ -141,6 +197,28 @@ export class Project {
141
197
  */
142
198
  copyFile(src: string, dst: string): Promise<void>;
143
199
  }
200
+ export class WorkerTermination {
201
+ static __wrap(ptr: any): any;
202
+ __destroy_into_raw(): number | undefined;
203
+ __wbg_ptr: number | undefined;
204
+ free(): void;
205
+ /**
206
+ * @param {string} arg0
207
+ */
208
+ set filename(arg0: string);
209
+ /**
210
+ * @returns {string}
211
+ */
212
+ get filename(): string;
213
+ /**
214
+ * @param {number} arg0
215
+ */
216
+ set workerId(arg0: number);
217
+ /**
218
+ * @returns {number}
219
+ */
220
+ get workerId(): number;
221
+ }
144
222
  export default __wbg_init;
145
223
  export function initSync(module: any, memory: any): any;
146
224
  declare function __wbg_init(module_or_path: any, memory: any): Promise<any>;