zero-com 1.6.5 → 1.7.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/.claude/settings.local.json +2 -1
- package/README.md +74 -20
- package/lib/common.d.ts +1 -2
- package/lib/common.js +8 -14
- package/lib/runtime.d.ts +8 -9
- package/lib/runtime.js +60 -14
- package/lib/turbopack-loader.d.ts +6 -0
- package/lib/turbopack-loader.js +32 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -7,6 +7,7 @@ The 0 bytes utility for transparently communicating client and server in full-st
|
|
|
7
7
|
- [Usage](#usage)
|
|
8
8
|
- [Transport layer](#transport-layer)
|
|
9
9
|
- [Context](#context)
|
|
10
|
+
- [Server-to-Server Calls](#server-to-server-calls)
|
|
10
11
|
- [Plugin options](#plugin-options)
|
|
11
12
|
- [Complete Example](#complete-example)
|
|
12
13
|
|
|
@@ -57,7 +58,7 @@ Server side
|
|
|
57
58
|
// server/phones.ts
|
|
58
59
|
import { func } from 'zero-com';
|
|
59
60
|
|
|
60
|
-
export const getPhones = func(async () => {
|
|
61
|
+
export const getPhones = func(async () => {
|
|
61
62
|
// ...
|
|
62
63
|
})
|
|
63
64
|
```
|
|
@@ -100,8 +101,8 @@ On the server-side, you need to create a handler that receives messages from the
|
|
|
100
101
|
// server/api.js
|
|
101
102
|
import { handle } from 'zero-com';
|
|
102
103
|
|
|
103
|
-
const someCustomHandler = async (message) => {
|
|
104
|
-
return await handle(message.funcId,
|
|
104
|
+
const someCustomHandler = async (message, ctx) => {
|
|
105
|
+
return await handle(message.funcId, ctx, message.params);
|
|
105
106
|
};
|
|
106
107
|
|
|
107
108
|
// Example of how to use the handler with an Express server
|
|
@@ -112,7 +113,8 @@ app.use(express.json());
|
|
|
112
113
|
|
|
113
114
|
app.post('/api', async (req, res) => {
|
|
114
115
|
try {
|
|
115
|
-
const
|
|
116
|
+
const ctx = { req, res };
|
|
117
|
+
const result = await someCustomHandler(req.body, ctx);
|
|
116
118
|
res.json(result);
|
|
117
119
|
} catch (error) {
|
|
118
120
|
res.status(500).json({ error: error.message });
|
|
@@ -126,42 +128,90 @@ app.listen(8000, () => {
|
|
|
126
128
|
|
|
127
129
|
## Context
|
|
128
130
|
|
|
129
|
-
Often you want to
|
|
131
|
+
Often you want to access context-related data in your server functions, such as the request, response, session, etc. Zero-com provides a simple way to do this using the `context()` function.
|
|
130
132
|
|
|
131
|
-
###
|
|
133
|
+
### Accessing Context in Server Functions
|
|
132
134
|
|
|
133
|
-
To
|
|
135
|
+
To access context in a server function, call the `context<T>()` function inside your function body. The context is automatically available when the function is called via `handle()`.
|
|
134
136
|
|
|
135
137
|
```typescript
|
|
136
138
|
// server/api/phones.ts
|
|
137
139
|
import { func, context } from 'zero-com';
|
|
138
140
|
|
|
139
141
|
type MyContext = {
|
|
140
|
-
|
|
142
|
+
req: any;
|
|
143
|
+
res: any;
|
|
144
|
+
userId: string;
|
|
141
145
|
}
|
|
142
146
|
|
|
143
|
-
export const getPhones = func(async (
|
|
144
|
-
//
|
|
145
|
-
|
|
147
|
+
export const getPhones = func(async (name: string) => {
|
|
148
|
+
// Get the context inside the function
|
|
149
|
+
const ctx = context<MyContext>();
|
|
150
|
+
|
|
151
|
+
console.log('User:', ctx.userId);
|
|
152
|
+
console.log('Headers:', ctx.req.headers);
|
|
153
|
+
|
|
146
154
|
// ... your code
|
|
147
155
|
});
|
|
148
156
|
```
|
|
149
157
|
|
|
150
158
|
### Providing Context on the Server
|
|
151
159
|
|
|
152
|
-
|
|
160
|
+
Pass the context as the second argument to `handle()`. The context will be available to the function and any nested server function calls.
|
|
153
161
|
|
|
154
162
|
```javascript
|
|
155
163
|
// server/api.js
|
|
156
164
|
import { handle } from 'zero-com';
|
|
157
165
|
|
|
158
|
-
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
166
|
+
app.post('/api', async (req, res) => {
|
|
167
|
+
const { funcId, params } = req.body;
|
|
168
|
+
|
|
169
|
+
// Create context with request data
|
|
170
|
+
const ctx = {
|
|
171
|
+
req,
|
|
172
|
+
res,
|
|
173
|
+
userId: req.headers['x-user-id']
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
// Pass context to handle - it will be available via context()
|
|
177
|
+
const result = await handle(funcId, ctx, params);
|
|
178
|
+
res.json(result);
|
|
179
|
+
});
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Server-to-Server Calls
|
|
183
|
+
|
|
184
|
+
When one server function calls another server function, the call bypasses the transport layer and executes directly. Context is automatically propagated to nested calls.
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
// server/api/user.ts
|
|
188
|
+
import { func, context } from 'zero-com';
|
|
189
|
+
|
|
190
|
+
export const getFirstName = func(async () => {
|
|
191
|
+
const ctx = context<{ userId: string }>();
|
|
192
|
+
// ... fetch first name from database
|
|
193
|
+
return 'John';
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// server/api/profile.ts
|
|
197
|
+
import { func, context } from 'zero-com';
|
|
198
|
+
import { getFirstName } from './user';
|
|
199
|
+
|
|
200
|
+
export const getFullName = func(async (lastName: string) => {
|
|
201
|
+
// This calls getFirstName directly (no transport layer)
|
|
202
|
+
// Context is automatically propagated
|
|
203
|
+
const firstName = await getFirstName();
|
|
204
|
+
return `${firstName} ${lastName}`;
|
|
205
|
+
});
|
|
163
206
|
```
|
|
164
207
|
|
|
208
|
+
When `getFullName` is called from the client:
|
|
209
|
+
1. The call goes through the transport layer to the server
|
|
210
|
+
2. `handle()` sets up the context
|
|
211
|
+
3. `getFullName` executes and calls `getFirstName`
|
|
212
|
+
4. `getFirstName` executes directly (no transport) with the same context
|
|
213
|
+
5. Both functions can access `context()` with the same data
|
|
214
|
+
|
|
165
215
|
## Plugin options
|
|
166
216
|
|
|
167
217
|
| Option | Type | Description |
|
|
@@ -225,11 +275,15 @@ call(async (funcId, params) => {
|
|
|
225
275
|
import { func, context } from 'zero-com';
|
|
226
276
|
|
|
227
277
|
type Context = {
|
|
228
|
-
req: any
|
|
229
|
-
res: any
|
|
278
|
+
req: any;
|
|
279
|
+
res: any;
|
|
230
280
|
}
|
|
231
281
|
|
|
232
|
-
export const getPhones = func(async (
|
|
282
|
+
export const getPhones = func(async (name: string) => {
|
|
283
|
+
// Access context when needed
|
|
284
|
+
const ctx = context<Context>();
|
|
285
|
+
console.log('Request from:', ctx.req.ip);
|
|
286
|
+
|
|
233
287
|
// In a real application, you would fetch this from a database
|
|
234
288
|
const allPhones = [
|
|
235
289
|
{ name: 'iPhone 13', brand: 'Apple' },
|
package/lib/common.d.ts
CHANGED
|
@@ -12,15 +12,14 @@ export type ServerFuncInfo = {
|
|
|
12
12
|
funcId: string;
|
|
13
13
|
filePath: string;
|
|
14
14
|
exportName: string;
|
|
15
|
-
requireContext: boolean;
|
|
16
15
|
};
|
|
17
16
|
export type ServerFuncRegistry = Map<string, Map<string, ServerFuncInfo>>;
|
|
18
17
|
export declare const ZERO_COM_CLIENT_CALL = "ZERO_COM_CLIENT_CALL";
|
|
19
18
|
export declare const ZERO_COM_SERVER_REGISTRY = "ZERO_COM_SERVER_REGISTRY";
|
|
19
|
+
export declare const ZERO_COM_CONTEXT_STORAGE = "ZERO_COM_CONTEXT_STORAGE";
|
|
20
20
|
export declare const SERVER_FUNCTION_WRAPPER_NAME = "func";
|
|
21
21
|
export declare const HANDLE_NAME = "handle";
|
|
22
22
|
export declare const CALL_NAME = "call";
|
|
23
|
-
export declare const CONTEXT_TYPE_NAME = "context";
|
|
24
23
|
export declare const LIBRARY_NAME = "zero-com";
|
|
25
24
|
export declare const FILE_EXTENSIONS: string[];
|
|
26
25
|
export declare const formatFuncIdName: (funcName: string, filePath: string, line: number) => string;
|
package/lib/common.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.applyReplacements = exports.emitToJs = exports.transformSourceFile = exports.appendRegistryCode = exports.applyReplacementsWithMap = exports.collectFuncCallReplacements = exports.collectSendCallReplacements = exports.collectHandleCallReplacements = exports.collectCallSiteReplacements = exports.getImportedServerFunctions = exports.buildRegistry = exports.createProject = exports.resolveFilePath = exports.isFromLibrary = exports.getReplacements = exports.generateCompilationId = exports.formatFuncIdName = exports.FILE_EXTENSIONS = exports.LIBRARY_NAME = exports.
|
|
6
|
+
exports.applyReplacements = exports.emitToJs = exports.transformSourceFile = exports.appendRegistryCode = exports.applyReplacementsWithMap = exports.collectFuncCallReplacements = exports.collectSendCallReplacements = exports.collectHandleCallReplacements = exports.collectCallSiteReplacements = exports.getImportedServerFunctions = exports.buildRegistry = exports.createProject = exports.resolveFilePath = exports.isFromLibrary = exports.getReplacements = exports.generateCompilationId = exports.formatFuncIdName = exports.FILE_EXTENSIONS = exports.LIBRARY_NAME = exports.CALL_NAME = exports.HANDLE_NAME = exports.SERVER_FUNCTION_WRAPPER_NAME = exports.ZERO_COM_CONTEXT_STORAGE = exports.ZERO_COM_SERVER_REGISTRY = exports.ZERO_COM_CLIENT_CALL = void 0;
|
|
7
7
|
const fs_1 = __importDefault(require("fs"));
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
9
|
const magic_string_1 = __importDefault(require("magic-string"));
|
|
@@ -11,10 +11,10 @@ const ts_morph_1 = require("ts-morph");
|
|
|
11
11
|
// Constants
|
|
12
12
|
exports.ZERO_COM_CLIENT_CALL = 'ZERO_COM_CLIENT_CALL';
|
|
13
13
|
exports.ZERO_COM_SERVER_REGISTRY = 'ZERO_COM_SERVER_REGISTRY';
|
|
14
|
+
exports.ZERO_COM_CONTEXT_STORAGE = 'ZERO_COM_CONTEXT_STORAGE';
|
|
14
15
|
exports.SERVER_FUNCTION_WRAPPER_NAME = 'func';
|
|
15
16
|
exports.HANDLE_NAME = 'handle';
|
|
16
17
|
exports.CALL_NAME = 'call';
|
|
17
|
-
exports.CONTEXT_TYPE_NAME = 'context';
|
|
18
18
|
exports.LIBRARY_NAME = 'zero-com';
|
|
19
19
|
exports.FILE_EXTENSIONS = ['', '.ts', '.tsx', '.js', '.jsx', '.mjs'];
|
|
20
20
|
const formatFuncIdName = (funcName, filePath, line) => {
|
|
@@ -25,7 +25,8 @@ const generateCompilationId = () => String(Math.floor(Math.random() * 1000000));
|
|
|
25
25
|
exports.generateCompilationId = generateCompilationId;
|
|
26
26
|
const getReplacements = (compilationId) => [
|
|
27
27
|
{ target: exports.ZERO_COM_CLIENT_CALL, replacement: `__ZERO_COM_CLIENT_CALL_${compilationId}` },
|
|
28
|
-
{ target: exports.ZERO_COM_SERVER_REGISTRY, replacement: `__ZERO_COM_SERVER_REGISTRY_${compilationId}` }
|
|
28
|
+
{ target: exports.ZERO_COM_SERVER_REGISTRY, replacement: `__ZERO_COM_SERVER_REGISTRY_${compilationId}` },
|
|
29
|
+
{ target: exports.ZERO_COM_CONTEXT_STORAGE, replacement: `__ZERO_COM_CONTEXT_STORAGE_${compilationId}` }
|
|
29
30
|
];
|
|
30
31
|
exports.getReplacements = getReplacements;
|
|
31
32
|
// Utilities
|
|
@@ -98,7 +99,6 @@ const buildRegistry = (contextDir, registry) => {
|
|
|
98
99
|
};
|
|
99
100
|
exports.buildRegistry = buildRegistry;
|
|
100
101
|
const scanFileForServerFunctions = (filePath, contextDir, registry) => {
|
|
101
|
-
var _a;
|
|
102
102
|
const sourceFile = (0, exports.createProject)().createSourceFile(filePath, fs_1.default.readFileSync(filePath, 'utf8'), { overwrite: true });
|
|
103
103
|
const fileRegistry = new Map();
|
|
104
104
|
for (const decl of sourceFile.getVariableDeclarations()) {
|
|
@@ -116,14 +116,11 @@ const scanFileForServerFunctions = (filePath, contextDir, registry) => {
|
|
|
116
116
|
const args = callExpr.getArguments();
|
|
117
117
|
if (args.length !== 1 || args[0].getKind() !== ts_morph_1.SyntaxKind.ArrowFunction)
|
|
118
118
|
continue;
|
|
119
|
-
const params = args[0].getParameters();
|
|
120
|
-
const requireContext = params.length > 0 && ((_a = params[0].getTypeNode()) === null || _a === void 0 ? void 0 : _a.getText().startsWith(exports.CONTEXT_TYPE_NAME)) || false;
|
|
121
119
|
const exportName = decl.getName();
|
|
122
120
|
fileRegistry.set(exportName, {
|
|
123
121
|
funcId: (0, exports.formatFuncIdName)(exportName, path_1.default.relative(contextDir, filePath), decl.getStartLineNumber()),
|
|
124
122
|
filePath,
|
|
125
123
|
exportName,
|
|
126
|
-
requireContext,
|
|
127
124
|
});
|
|
128
125
|
}
|
|
129
126
|
if (fileRegistry.size > 0)
|
|
@@ -191,10 +188,11 @@ const collectHandleCallReplacements = (sourceFile) => {
|
|
|
191
188
|
const funcId = args[0].getText();
|
|
192
189
|
const ctx = args[1].getText();
|
|
193
190
|
const argsArray = args[2].getText();
|
|
191
|
+
// Use contextStorage.run() to make context available via context() calls
|
|
194
192
|
replacements.push({
|
|
195
193
|
start: callExpr.getStart(),
|
|
196
194
|
end: callExpr.getEnd(),
|
|
197
|
-
content: `
|
|
195
|
+
content: `globalThis.${exports.ZERO_COM_CONTEXT_STORAGE}.run(${ctx}, () => globalThis.${exports.ZERO_COM_SERVER_REGISTRY}[${funcId}](...${argsArray}))`
|
|
198
196
|
});
|
|
199
197
|
});
|
|
200
198
|
return replacements;
|
|
@@ -260,13 +258,9 @@ const applyReplacementsWithMap = (source, replacements, filePath) => {
|
|
|
260
258
|
};
|
|
261
259
|
exports.applyReplacementsWithMap = applyReplacementsWithMap;
|
|
262
260
|
const appendRegistryCode = (sourceFile, fileRegistry) => {
|
|
263
|
-
// Generate registration code for each server function
|
|
264
|
-
// We set requireContext based on compile-time analysis of the function's first parameter type.
|
|
265
|
-
// If the first param is context<T>, requireContext = true, otherwise false.
|
|
266
|
-
// This allows handle() at runtime to know whether to inject the context as the first argument.
|
|
261
|
+
// Generate registration code for each server function
|
|
267
262
|
const registrations = Array.from(fileRegistry.values())
|
|
268
|
-
.map(info => `globalThis.${exports.ZERO_COM_SERVER_REGISTRY}['${info.funcId}'] = ${info.exportName}
|
|
269
|
-
`${info.exportName}.requireContext = ${info.requireContext};`)
|
|
263
|
+
.map(info => `globalThis.${exports.ZERO_COM_SERVER_REGISTRY}['${info.funcId}'] = ${info.exportName};`)
|
|
270
264
|
.join('\n');
|
|
271
265
|
return `${sourceFile.getFullText()}
|
|
272
266
|
if (!globalThis.${exports.ZERO_COM_SERVER_REGISTRY}) globalThis.${exports.ZERO_COM_SERVER_REGISTRY} = Object.create(null);
|
package/lib/runtime.d.ts
CHANGED
|
@@ -2,14 +2,13 @@ declare global {
|
|
|
2
2
|
var ZERO_COM_SERVER_REGISTRY: {
|
|
3
3
|
[funcId: string]: (...args: any[]) => any;
|
|
4
4
|
};
|
|
5
|
-
var ZERO_COM_CLIENT_CALL: (funcId: string, args: any[]) =>
|
|
5
|
+
var ZERO_COM_CLIENT_CALL: (funcId: string, args: any[]) => any;
|
|
6
|
+
var ZERO_COM_CONTEXT_STORAGE: {
|
|
7
|
+
run: <T>(ctx: any, fn: () => T) => T;
|
|
8
|
+
getStore: () => any;
|
|
9
|
+
} | undefined;
|
|
6
10
|
}
|
|
7
|
-
declare
|
|
8
|
-
export
|
|
9
|
-
readonly [contextBrand]: never;
|
|
10
|
-
};
|
|
11
|
-
type RemoveContextParam<F> = F extends (ctx: infer C, ...args: infer A) => infer R ? C extends context<unknown> ? (...args: A) => R : F : F;
|
|
12
|
-
export declare function func<F extends (...args: any[]) => any>(fn: F): RemoveContextParam<F>;
|
|
11
|
+
export declare function context<T = unknown>(): T;
|
|
12
|
+
export declare function func<F extends (...args: any[]) => any>(fn: F): F;
|
|
13
13
|
export declare const handle: (funcId: string, ctx: any, args: any[]) => any;
|
|
14
|
-
export declare const call: (fn: (funcId: string, args: any[]) =>
|
|
15
|
-
export {};
|
|
14
|
+
export declare const call: (fn: (funcId: string, args: any[]) => any) => void;
|
package/lib/runtime.js
CHANGED
|
@@ -1,31 +1,77 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.call = exports.handle = void 0;
|
|
4
|
+
exports.context = context;
|
|
4
5
|
exports.func = func;
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
// Context storage - only available on server (Node.js)
|
|
7
|
+
// Lazily initialized to avoid importing async_hooks on client
|
|
8
|
+
function getContextStorage() {
|
|
9
|
+
if (!globalThis.ZERO_COM_CONTEXT_STORAGE) {
|
|
10
|
+
try {
|
|
11
|
+
// Dynamic import to avoid bundling async_hooks for browser
|
|
12
|
+
const { AsyncLocalStorage } = require('async_hooks');
|
|
13
|
+
globalThis.ZERO_COM_CONTEXT_STORAGE = new AsyncLocalStorage();
|
|
14
|
+
}
|
|
15
|
+
catch (_a) {
|
|
16
|
+
// Browser environment - context storage not available
|
|
17
|
+
globalThis.ZERO_COM_CONTEXT_STORAGE = undefined;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return globalThis.ZERO_COM_CONTEXT_STORAGE;
|
|
21
|
+
}
|
|
22
|
+
// Get the current context - call this inside server functions
|
|
23
|
+
function context() {
|
|
24
|
+
const storage = getContextStorage();
|
|
25
|
+
if (!storage) {
|
|
26
|
+
throw new Error('context() is only available on the server');
|
|
27
|
+
}
|
|
28
|
+
const ctx = storage.getStore();
|
|
29
|
+
if (ctx === undefined) {
|
|
30
|
+
throw new Error('context() called outside of a server function');
|
|
31
|
+
}
|
|
32
|
+
return ctx;
|
|
33
|
+
}
|
|
34
|
+
// Default server-side implementation: call directly from registry
|
|
35
|
+
// This enables server functions to call other server functions without transport
|
|
36
|
+
if (typeof globalThis.ZERO_COM_CLIENT_CALL === 'undefined') {
|
|
37
|
+
globalThis.ZERO_COM_CLIENT_CALL = (funcId, args) => {
|
|
38
|
+
var _a;
|
|
39
|
+
const storage = getContextStorage();
|
|
40
|
+
if (!storage) {
|
|
41
|
+
throw new Error('Server function called on client without transport configured. Call call() first.');
|
|
42
|
+
}
|
|
43
|
+
const ctx = storage.getStore();
|
|
44
|
+
if (ctx === undefined) {
|
|
45
|
+
throw new Error('Server function called outside of request context');
|
|
46
|
+
}
|
|
47
|
+
const fn = (_a = globalThis.ZERO_COM_SERVER_REGISTRY) === null || _a === void 0 ? void 0 : _a[funcId];
|
|
48
|
+
if (!fn)
|
|
49
|
+
throw new Error(`Function not found: ${funcId}`);
|
|
50
|
+
return fn(...args);
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
// func() just returns the function as-is
|
|
54
|
+
// In production mode: transformed by plugin to just the inner function
|
|
9
55
|
function func(fn) {
|
|
10
56
|
return fn;
|
|
11
57
|
}
|
|
12
|
-
//
|
|
13
|
-
// In production mode: transformed by plugin to inline code
|
|
58
|
+
// handle() stores context in AsyncLocalStorage and calls the function
|
|
59
|
+
// In production mode: transformed by plugin to inline code
|
|
14
60
|
const handle = (funcId, ctx, args) => {
|
|
15
|
-
|
|
61
|
+
var _a;
|
|
62
|
+
const fn = (_a = globalThis.ZERO_COM_SERVER_REGISTRY) === null || _a === void 0 ? void 0 : _a[funcId];
|
|
16
63
|
if (!fn) {
|
|
17
64
|
throw new Error(`Function not found in registry: ${funcId}`);
|
|
18
65
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
else {
|
|
23
|
-
return fn(...args);
|
|
66
|
+
const storage = getContextStorage();
|
|
67
|
+
if (!storage) {
|
|
68
|
+
throw new Error('handle() is only available on the server');
|
|
24
69
|
}
|
|
70
|
+
return storage.run(ctx, () => fn(...args));
|
|
25
71
|
};
|
|
26
72
|
exports.handle = handle;
|
|
27
|
-
//
|
|
28
|
-
// In production mode: transformed by plugin to assignment
|
|
73
|
+
// Client calls this to set up transport (overrides default server-side behavior)
|
|
74
|
+
// In production mode: transformed by plugin to assignment
|
|
29
75
|
const call = (fn) => {
|
|
30
76
|
globalThis.ZERO_COM_CLIENT_CALL = fn;
|
|
31
77
|
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.default = turbopackLoader;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const common_1 = require("./common");
|
|
9
|
+
// Module-level cache: registry is built once and reused across invocations
|
|
10
|
+
let cachedRegistry = null;
|
|
11
|
+
let cachedRootDir = null;
|
|
12
|
+
function turbopackLoader(source) {
|
|
13
|
+
var _a;
|
|
14
|
+
const options = this.getOptions();
|
|
15
|
+
const filePath = this.resourcePath;
|
|
16
|
+
const rootDir = options.rootDir || this.rootContext || process.cwd();
|
|
17
|
+
const development = (_a = options.development) !== null && _a !== void 0 ? _a : true;
|
|
18
|
+
// Lazily build and cache the registry on first invocation or if rootDir changes
|
|
19
|
+
if (!cachedRegistry || cachedRootDir !== rootDir) {
|
|
20
|
+
cachedRegistry = new Map();
|
|
21
|
+
cachedRootDir = rootDir;
|
|
22
|
+
(0, common_1.buildRegistry)(rootDir, cachedRegistry);
|
|
23
|
+
console.log(`[TurbopackLoader] Found ${cachedRegistry.size} files with server functions`);
|
|
24
|
+
}
|
|
25
|
+
const result = (0, common_1.transformSourceFile)(filePath, source, cachedRegistry, { development });
|
|
26
|
+
if (!result.transformed) {
|
|
27
|
+
this.callback(null, source);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
console.log(`[TurbopackLoader] Transformed: ${path_1.default.basename(filePath)}`);
|
|
31
|
+
this.callback(null, result.content, result.map);
|
|
32
|
+
}
|