zero-com 0.0.8 → 1.2.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.
- package/README.md +57 -91
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/lib/common.d.ts +38 -6
- package/lib/common.js +251 -4
- package/lib/rollup.d.ts +1 -1
- package/lib/rollup.js +28 -137
- package/lib/runtime.d.ts +16 -0
- package/lib/runtime.js +30 -0
- package/lib/webpack.d.ts +2 -8
- package/lib/webpack.js +29 -134
- package/package.json +5 -2
- package/tsconfig.json +16 -17
- package/lib/index.d.ts +0 -11
- package/lib/index.js +0 -18
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Zero-com
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A ~420 Bytes utility for transparently communicating client and server in full-stack projects through compile-time code transformation, with end-to-end static type checking.
|
|
4
4
|
|
|
5
5
|
## Table of Contents
|
|
6
6
|
|
|
@@ -27,10 +27,6 @@ module.exports = {
|
|
|
27
27
|
plugins: [
|
|
28
28
|
new ZeroComWebpackPlugin({
|
|
29
29
|
development: true,
|
|
30
|
-
patterns: {
|
|
31
|
-
client: 'src/client/**',
|
|
32
|
-
server: 'src/server/api/**',
|
|
33
|
-
},
|
|
34
30
|
}),
|
|
35
31
|
],
|
|
36
32
|
};
|
|
@@ -49,10 +45,6 @@ export default {
|
|
|
49
45
|
plugins: [
|
|
50
46
|
zeroComRollupPlugin({
|
|
51
47
|
development: true,
|
|
52
|
-
patterns: {
|
|
53
|
-
client: 'src/client/**',
|
|
54
|
-
server: 'src/server/api/**',
|
|
55
|
-
},
|
|
56
48
|
}),
|
|
57
49
|
],
|
|
58
50
|
};
|
|
@@ -61,16 +53,17 @@ export default {
|
|
|
61
53
|
The above code will identify all the references from client-side code to the server-side files and will tranform the modules to comunicate through your defined transport layer. The only callable functions in the server-side modules will be the exported async functions. See the example below.
|
|
62
54
|
|
|
63
55
|
Server side
|
|
64
|
-
```
|
|
56
|
+
```ts
|
|
65
57
|
// server/phones.ts
|
|
66
|
-
|
|
58
|
+
import { func } from 'zero-com';
|
|
67
59
|
|
|
68
|
-
|
|
69
|
-
|
|
60
|
+
export const getPhones = func(async () => {
|
|
61
|
+
// ...
|
|
62
|
+
})
|
|
70
63
|
```
|
|
71
64
|
|
|
72
65
|
Client side
|
|
73
|
-
```
|
|
66
|
+
```tsx
|
|
74
67
|
// client/phones.tsx
|
|
75
68
|
import { getPhones } '../server/phones'
|
|
76
69
|
```
|
|
@@ -79,28 +72,15 @@ import { getPhones } '../server/phones'
|
|
|
79
72
|
|
|
80
73
|
Zero-com does not define any transport layer, it is up to you to create one or reuse your own. This means you have complete control over how data is sent between the client and server.
|
|
81
74
|
|
|
82
|
-
### Communication Flow
|
|
83
|
-
|
|
84
|
-
The following diagram illustrates the communication flow between the client and server:
|
|
85
|
-
|
|
86
|
-
```
|
|
87
|
-
+--------+ +-----------------------------+ +-------------+
|
|
88
|
-
| Client |----->| window.ZERO_COM_CLIENT_SEND |----->| Your Server |
|
|
89
|
-
+--------+ +-----------------------------+ +-------------+
|
|
90
|
-
|
|
|
91
|
-
v
|
|
92
|
-
+--------+ +-------------------------+ +-------------------+
|
|
93
|
-
| Client |<-----| (Your custom transport) |<-----| someCustomHandler |
|
|
94
|
-
+--------+ +-------------------------+ +-------------------+
|
|
95
|
-
```
|
|
96
|
-
|
|
97
75
|
### Client-side
|
|
98
76
|
|
|
99
|
-
All messages from the client-side will be sent using the
|
|
77
|
+
All messages from the client-side will be sent using the transport function you define. Import `call` from `zero-com` and pass your transport function.
|
|
100
78
|
|
|
101
79
|
```javascript
|
|
102
80
|
// client/transport.js
|
|
103
|
-
|
|
81
|
+
import { call } from 'zero-com';
|
|
82
|
+
|
|
83
|
+
call(async (funcId, params) => {
|
|
104
84
|
const response = await fetch('http://localhost:8000/api', {
|
|
105
85
|
method: 'POST',
|
|
106
86
|
headers: {
|
|
@@ -109,24 +89,19 @@ window.ZERO_COM_CLIENT_SEND = async ({ funcId, params }) => {
|
|
|
109
89
|
body: JSON.stringify({ funcId, params }),
|
|
110
90
|
});
|
|
111
91
|
return await response.json();
|
|
112
|
-
};
|
|
92
|
+
});
|
|
113
93
|
```
|
|
114
94
|
|
|
115
95
|
### Server-side
|
|
116
96
|
|
|
117
|
-
On the server-side, you need to create a handler that receives messages from the client, executes the corresponding function, and returns the result.
|
|
97
|
+
On the server-side, you need to create a handler that receives messages from the client, executes the corresponding function, and returns the result. Import `handle` from `zero-com` and call it with the function ID, context, and arguments.
|
|
118
98
|
|
|
119
99
|
```javascript
|
|
120
100
|
// server/api.js
|
|
121
|
-
import {
|
|
101
|
+
import { handle } from 'zero-com';
|
|
122
102
|
|
|
123
103
|
const someCustomHandler = async (message) => {
|
|
124
|
-
|
|
125
|
-
if (func) {
|
|
126
|
-
return await execServerFn(func, message.params);
|
|
127
|
-
} else {
|
|
128
|
-
throw new Error(`Function with id ${message.funcId} not found.`);
|
|
129
|
-
}
|
|
104
|
+
return await handle(message.funcId, null, message.params);
|
|
130
105
|
};
|
|
131
106
|
|
|
132
107
|
// Example of how to use the handler with an Express server
|
|
@@ -155,13 +130,17 @@ Often you want to pass a context-related object to the server functions to have
|
|
|
155
130
|
|
|
156
131
|
### Passing Context to Server Functions
|
|
157
132
|
|
|
158
|
-
To pass context to a server function, you need to wrap the function in `
|
|
133
|
+
To pass context to a server function, you need to wrap the function in `func` and type the first parameter as `context`. The plugin detects this type and handles it accordingly.
|
|
159
134
|
|
|
160
|
-
```
|
|
161
|
-
// server/api/phones.
|
|
162
|
-
import {
|
|
135
|
+
```typescript
|
|
136
|
+
// server/api/phones.ts
|
|
137
|
+
import { func, context } from 'zero-com';
|
|
138
|
+
|
|
139
|
+
type MyContext = {
|
|
140
|
+
request: any
|
|
141
|
+
}
|
|
163
142
|
|
|
164
|
-
export const getPhones =
|
|
143
|
+
export const getPhones = func(async (ctx: context<MyContext>, name: string) => {
|
|
165
144
|
// ctx is the context object passed from the server
|
|
166
145
|
console.log(ctx.request.headers);
|
|
167
146
|
// ... your code
|
|
@@ -170,17 +149,16 @@ export const getPhones = serverFn(async (ctx, name) => {
|
|
|
170
149
|
|
|
171
150
|
### Providing Context on the Server
|
|
172
151
|
|
|
173
|
-
You can pass the context to `
|
|
152
|
+
You can pass the context to `handle` when you execute the server function.
|
|
174
153
|
|
|
175
154
|
```javascript
|
|
176
155
|
// server/api.js
|
|
177
|
-
import {
|
|
156
|
+
import { handle } from 'zero-com';
|
|
178
157
|
|
|
179
158
|
const myHandler = (request, response, message) => {
|
|
180
159
|
const ctx = { request, response };
|
|
181
|
-
const func = global.ZERO_COM_SERVER_REGISTRY[message.funcId];
|
|
182
160
|
// pass context on exec
|
|
183
|
-
return
|
|
161
|
+
return handle(message.funcId, ctx, message.params);
|
|
184
162
|
};
|
|
185
163
|
```
|
|
186
164
|
|
|
@@ -189,9 +167,6 @@ const myHandler = (request, response, message) => {
|
|
|
189
167
|
| Option | Type | Description |
|
|
190
168
|
|-------------|-----------|-----------------------------------------------------------------------------|
|
|
191
169
|
| `development` | `boolean` | If `false`, will add internal variable renaming to the final bundle. |
|
|
192
|
-
| `patterns` | `object` | |
|
|
193
|
-
| `patterns.client` | `string` | A glob pattern to identify client-side files. |
|
|
194
|
-
| `patterns.server` | `string` | A glob pattern to identify server-side files. |
|
|
195
170
|
|
|
196
171
|
## Complete Example
|
|
197
172
|
|
|
@@ -206,17 +181,17 @@ Here's a complete example of how to use Zero-com in a project.
|
|
|
206
181
|
├── rollup.config.js
|
|
207
182
|
└── src
|
|
208
183
|
├── client
|
|
209
|
-
│ ├── index.
|
|
210
|
-
│ └── transport.
|
|
184
|
+
│ ├── index.ts
|
|
185
|
+
│ └── transport.ts
|
|
211
186
|
└── server
|
|
212
187
|
└── api
|
|
213
|
-
└── phones.
|
|
188
|
+
└── phones.ts
|
|
214
189
|
```
|
|
215
190
|
|
|
216
191
|
### Client-side
|
|
217
192
|
|
|
218
|
-
```
|
|
219
|
-
// src/client/index.
|
|
193
|
+
```typescript
|
|
194
|
+
// src/client/index.ts
|
|
220
195
|
import { getPhones } from '../../server/api/phones';
|
|
221
196
|
|
|
222
197
|
async function main() {
|
|
@@ -227,9 +202,11 @@ async function main() {
|
|
|
227
202
|
main();
|
|
228
203
|
```
|
|
229
204
|
|
|
230
|
-
```
|
|
231
|
-
// src/client/transport.
|
|
232
|
-
|
|
205
|
+
```typescript
|
|
206
|
+
// src/client/transport.ts
|
|
207
|
+
import { call } from 'zero-com';
|
|
208
|
+
|
|
209
|
+
call(async (funcId, params) => {
|
|
233
210
|
const response = await fetch('http://localhost:8000/api', {
|
|
234
211
|
method: 'POST',
|
|
235
212
|
headers: {
|
|
@@ -238,16 +215,21 @@ window.ZERO_COM_CLIENT_SEND = async ({ funcId, params }) => {
|
|
|
238
215
|
body: JSON.stringify({ funcId, params }),
|
|
239
216
|
});
|
|
240
217
|
return await response.json();
|
|
241
|
-
};
|
|
218
|
+
});
|
|
242
219
|
```
|
|
243
220
|
|
|
244
221
|
### Server-side
|
|
245
222
|
|
|
246
|
-
```
|
|
247
|
-
// src/server/api/phones.
|
|
248
|
-
import {
|
|
223
|
+
```typescript
|
|
224
|
+
// src/server/api/phones.ts
|
|
225
|
+
import { func, context } from 'zero-com';
|
|
249
226
|
|
|
250
|
-
|
|
227
|
+
type Context = {
|
|
228
|
+
req: any,
|
|
229
|
+
res: any
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export const getPhones = func(async (ctx: context<Context>, name: string) => {
|
|
251
233
|
// In a real application, you would fetch this from a database
|
|
252
234
|
const allPhones = [
|
|
253
235
|
{ name: 'iPhone 13', brand: 'Apple' },
|
|
@@ -260,10 +242,10 @@ export const getPhones = serverFn(async (ctx, name) => {
|
|
|
260
242
|
|
|
261
243
|
### Server
|
|
262
244
|
|
|
263
|
-
```
|
|
264
|
-
// server.
|
|
245
|
+
```typescript
|
|
246
|
+
// server.ts
|
|
265
247
|
import express from 'express';
|
|
266
|
-
import {
|
|
248
|
+
import { handle } from 'zero-com';
|
|
267
249
|
import './src/server/api/phones.js'; // Make sure to import the server-side modules
|
|
268
250
|
|
|
269
251
|
const app = express();
|
|
@@ -271,17 +253,11 @@ app.use(express.json());
|
|
|
271
253
|
|
|
272
254
|
app.post('/api', async (req, res) => {
|
|
273
255
|
const { funcId, params } = req.body;
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
res.json(result);
|
|
280
|
-
} catch (error) {
|
|
281
|
-
res.status(500).json({ error: error.message });
|
|
282
|
-
}
|
|
283
|
-
} else {
|
|
284
|
-
res.status(404).json({ error: `Function with id ${funcId} not found.` });
|
|
256
|
+
try {
|
|
257
|
+
const result = await handle(funcId, { req, res }, params);
|
|
258
|
+
res.json(result);
|
|
259
|
+
} catch (error) {
|
|
260
|
+
res.status(500).json({ error: error.message });
|
|
285
261
|
}
|
|
286
262
|
});
|
|
287
263
|
|
|
@@ -304,12 +280,7 @@ module.exports = {
|
|
|
304
280
|
path: __dirname + '/dist',
|
|
305
281
|
},
|
|
306
282
|
plugins: [
|
|
307
|
-
new ZeroComWebpackPlugin(
|
|
308
|
-
patterns: {
|
|
309
|
-
client: 'src/client/**',
|
|
310
|
-
server: 'src/server/api/**',
|
|
311
|
-
},
|
|
312
|
-
}),
|
|
283
|
+
new ZeroComWebpackPlugin(),
|
|
313
284
|
],
|
|
314
285
|
};
|
|
315
286
|
```
|
|
@@ -327,12 +298,7 @@ export default {
|
|
|
327
298
|
format: 'cjs',
|
|
328
299
|
},
|
|
329
300
|
plugins: [
|
|
330
|
-
zeroComRollupPlugin(
|
|
331
|
-
patterns: {
|
|
332
|
-
client: 'src/client/**',
|
|
333
|
-
server: 'src/server/api/**',
|
|
334
|
-
},
|
|
335
|
-
}),
|
|
301
|
+
zeroComRollupPlugin(),
|
|
336
302
|
],
|
|
337
303
|
};
|
|
338
304
|
```
|
package/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from './lib/
|
|
1
|
+
export * from './lib/runtime';
|
package/index.js
CHANGED
|
@@ -14,4 +14,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./lib/
|
|
17
|
+
__exportStar(require("./lib/runtime"), exports);
|
package/lib/common.d.ts
CHANGED
|
@@ -1,10 +1,42 @@
|
|
|
1
|
+
import { CallExpression, Project, SourceFile } from 'ts-morph';
|
|
1
2
|
export type Options = {
|
|
2
3
|
development?: boolean;
|
|
3
|
-
patterns: {
|
|
4
|
-
client: string;
|
|
5
|
-
server: string;
|
|
6
|
-
};
|
|
7
4
|
};
|
|
8
|
-
export
|
|
5
|
+
export type ServerFuncInfo = {
|
|
6
|
+
funcId: string;
|
|
7
|
+
filePath: string;
|
|
8
|
+
exportName: string;
|
|
9
|
+
requireContext: boolean;
|
|
10
|
+
};
|
|
11
|
+
export type ServerFuncRegistry = Map<string, Map<string, ServerFuncInfo>>;
|
|
12
|
+
export declare const ZERO_COM_CLIENT_CALL = "ZERO_COM_CLIENT_CALL";
|
|
9
13
|
export declare const ZERO_COM_SERVER_REGISTRY = "ZERO_COM_SERVER_REGISTRY";
|
|
10
|
-
export declare const
|
|
14
|
+
export declare const SERVER_FUNCTION_WRAPPER_NAME = "func";
|
|
15
|
+
export declare const HANDLE_NAME = "handle";
|
|
16
|
+
export declare const CALL_NAME = "call";
|
|
17
|
+
export declare const EXEC_FUNC_NAME = "execFunc";
|
|
18
|
+
export declare const CONTEXT_TYPE_NAME = "context";
|
|
19
|
+
export declare const LIBRARY_NAME = "zero-com";
|
|
20
|
+
export declare const FILE_EXTENSIONS: string[];
|
|
21
|
+
export declare const formatFuncIdName: (funcName: string, filePath: string, line: number) => string;
|
|
22
|
+
export declare const generateCompilationId: () => string;
|
|
23
|
+
export declare const getReplacements: (compilationId: string) => {
|
|
24
|
+
target: string;
|
|
25
|
+
replacement: string;
|
|
26
|
+
}[];
|
|
27
|
+
export declare const isFromLibrary: (callExpr: CallExpression, libraryName: string) => boolean;
|
|
28
|
+
export declare const resolveFilePath: (basePath: string) => string;
|
|
29
|
+
export declare const createProject: () => Project;
|
|
30
|
+
export declare const buildRegistry: (contextDir: string, registry: ServerFuncRegistry) => void;
|
|
31
|
+
export declare const getImportedServerFunctions: (sourceFile: SourceFile, registry: ServerFuncRegistry) => Map<string, ServerFuncInfo>;
|
|
32
|
+
export declare const transformCallSites: (sourceFile: SourceFile, importedFuncs: Map<string, ServerFuncInfo>) => boolean;
|
|
33
|
+
export declare const transformHandleCalls: (sourceFile: SourceFile) => boolean;
|
|
34
|
+
export declare const transformSendCalls: (sourceFile: SourceFile) => boolean;
|
|
35
|
+
export declare const appendRegistryCode: (sourceFile: SourceFile, fileRegistry: Map<string, ServerFuncInfo>) => string;
|
|
36
|
+
export type TransformResult = {
|
|
37
|
+
content: string;
|
|
38
|
+
transformed: boolean;
|
|
39
|
+
};
|
|
40
|
+
export declare const transformSourceFile: (filePath: string, content: string, registry: ServerFuncRegistry) => TransformResult;
|
|
41
|
+
export declare const emitToJs: (filePath: string, content: string) => string;
|
|
42
|
+
export declare const applyReplacements: (code: string, compilationId: string) => string;
|
package/lib/common.js
CHANGED
|
@@ -1,9 +1,256 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.formatFuncIdName = exports.ZERO_COM_SERVER_REGISTRY = exports.
|
|
4
|
-
|
|
6
|
+
exports.applyReplacements = exports.emitToJs = exports.transformSourceFile = exports.appendRegistryCode = exports.transformSendCalls = exports.transformHandleCalls = exports.transformCallSites = exports.getImportedServerFunctions = exports.buildRegistry = exports.createProject = exports.resolveFilePath = exports.isFromLibrary = exports.getReplacements = exports.generateCompilationId = exports.formatFuncIdName = exports.FILE_EXTENSIONS = exports.LIBRARY_NAME = exports.CONTEXT_TYPE_NAME = exports.EXEC_FUNC_NAME = exports.CALL_NAME = exports.HANDLE_NAME = exports.SERVER_FUNCTION_WRAPPER_NAME = exports.ZERO_COM_SERVER_REGISTRY = exports.ZERO_COM_CLIENT_CALL = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const ts_morph_1 = require("ts-morph");
|
|
10
|
+
// Constants
|
|
11
|
+
exports.ZERO_COM_CLIENT_CALL = 'ZERO_COM_CLIENT_CALL';
|
|
5
12
|
exports.ZERO_COM_SERVER_REGISTRY = 'ZERO_COM_SERVER_REGISTRY';
|
|
6
|
-
|
|
7
|
-
|
|
13
|
+
exports.SERVER_FUNCTION_WRAPPER_NAME = 'func';
|
|
14
|
+
exports.HANDLE_NAME = 'handle';
|
|
15
|
+
exports.CALL_NAME = 'call';
|
|
16
|
+
exports.EXEC_FUNC_NAME = 'execFunc';
|
|
17
|
+
exports.CONTEXT_TYPE_NAME = 'context';
|
|
18
|
+
exports.LIBRARY_NAME = 'zero-com';
|
|
19
|
+
exports.FILE_EXTENSIONS = ['', '.ts', '.tsx', '.js', '.jsx', '.mjs'];
|
|
20
|
+
const formatFuncIdName = (funcName, filePath, line) => {
|
|
21
|
+
return `${funcName}@${filePath}:${line}`;
|
|
8
22
|
};
|
|
9
23
|
exports.formatFuncIdName = formatFuncIdName;
|
|
24
|
+
const generateCompilationId = () => String(Math.floor(Math.random() * 1000000));
|
|
25
|
+
exports.generateCompilationId = generateCompilationId;
|
|
26
|
+
const getReplacements = (compilationId) => [
|
|
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}` }
|
|
29
|
+
];
|
|
30
|
+
exports.getReplacements = getReplacements;
|
|
31
|
+
// Utilities
|
|
32
|
+
const isFromLibrary = (callExpr, libraryName) => {
|
|
33
|
+
const symbol = callExpr.getExpression().getSymbol();
|
|
34
|
+
if (!symbol)
|
|
35
|
+
return false;
|
|
36
|
+
for (const decl of symbol.getDeclarations()) {
|
|
37
|
+
const kind = decl.getKind();
|
|
38
|
+
if (kind === ts_morph_1.ts.SyntaxKind.ImportSpecifier || kind === ts_morph_1.ts.SyntaxKind.NamespaceImport) {
|
|
39
|
+
const importDecl = decl.getFirstAncestorByKind(ts_morph_1.ts.SyntaxKind.ImportDeclaration);
|
|
40
|
+
if ((importDecl === null || importDecl === void 0 ? void 0 : importDecl.getModuleSpecifierValue()) === libraryName)
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
};
|
|
46
|
+
exports.isFromLibrary = isFromLibrary;
|
|
47
|
+
const resolveFilePath = (basePath) => {
|
|
48
|
+
for (const ext of exports.FILE_EXTENSIONS) {
|
|
49
|
+
const tryPath = basePath + ext;
|
|
50
|
+
if (fs_1.default.existsSync(tryPath))
|
|
51
|
+
return tryPath;
|
|
52
|
+
}
|
|
53
|
+
for (const ext of exports.FILE_EXTENSIONS.slice(1)) {
|
|
54
|
+
const tryPath = path_1.default.join(basePath, 'index' + ext);
|
|
55
|
+
if (fs_1.default.existsSync(tryPath))
|
|
56
|
+
return tryPath;
|
|
57
|
+
}
|
|
58
|
+
return '';
|
|
59
|
+
};
|
|
60
|
+
exports.resolveFilePath = resolveFilePath;
|
|
61
|
+
const createProject = () => new ts_morph_1.Project({
|
|
62
|
+
compilerOptions: { target: ts_morph_1.ts.ScriptTarget.ES2017, module: ts_morph_1.ts.ModuleKind.ESNext }
|
|
63
|
+
});
|
|
64
|
+
exports.createProject = createProject;
|
|
65
|
+
const getCalleeName = (callExpr) => {
|
|
66
|
+
const expr = callExpr.getExpression();
|
|
67
|
+
const kind = expr.getKind();
|
|
68
|
+
if (kind === ts_morph_1.SyntaxKind.Identifier)
|
|
69
|
+
return expr.getText();
|
|
70
|
+
if (kind === ts_morph_1.SyntaxKind.PropertyAccessExpression)
|
|
71
|
+
return expr.getName();
|
|
72
|
+
return '';
|
|
73
|
+
};
|
|
74
|
+
const getCalleeFullName = (callExpr) => {
|
|
75
|
+
const expr = callExpr.getExpression();
|
|
76
|
+
const kind = expr.getKind();
|
|
77
|
+
if (kind === ts_morph_1.SyntaxKind.Identifier)
|
|
78
|
+
return expr.getText();
|
|
79
|
+
if (kind === ts_morph_1.SyntaxKind.PropertyAccessExpression)
|
|
80
|
+
return expr.getText();
|
|
81
|
+
return '';
|
|
82
|
+
};
|
|
83
|
+
// Registry building
|
|
84
|
+
const buildRegistry = (contextDir, registry) => {
|
|
85
|
+
registry.clear();
|
|
86
|
+
const scanDirectory = (dir) => {
|
|
87
|
+
for (const entry of fs_1.default.readdirSync(dir, { withFileTypes: true })) {
|
|
88
|
+
const fullPath = path_1.default.join(dir, entry.name);
|
|
89
|
+
if (entry.isDirectory() && entry.name !== 'node_modules' && !entry.name.startsWith('.')) {
|
|
90
|
+
scanDirectory(fullPath);
|
|
91
|
+
}
|
|
92
|
+
else if (entry.isFile() && exports.FILE_EXTENSIONS.slice(1).includes(path_1.default.extname(entry.name))) {
|
|
93
|
+
scanFileForServerFunctions(fullPath, contextDir, registry);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
scanDirectory(contextDir);
|
|
98
|
+
};
|
|
99
|
+
exports.buildRegistry = buildRegistry;
|
|
100
|
+
const scanFileForServerFunctions = (filePath, contextDir, registry) => {
|
|
101
|
+
var _a;
|
|
102
|
+
const sourceFile = (0, exports.createProject)().createSourceFile(filePath, fs_1.default.readFileSync(filePath, 'utf8'), { overwrite: true });
|
|
103
|
+
const fileRegistry = new Map();
|
|
104
|
+
for (const decl of sourceFile.getVariableDeclarations()) {
|
|
105
|
+
if (!decl.isExported())
|
|
106
|
+
continue;
|
|
107
|
+
const initializer = decl.getInitializer();
|
|
108
|
+
if (!initializer || initializer.getKind() !== ts_morph_1.SyntaxKind.CallExpression)
|
|
109
|
+
continue;
|
|
110
|
+
const callExpr = initializer;
|
|
111
|
+
if (!(0, exports.isFromLibrary)(callExpr, exports.LIBRARY_NAME))
|
|
112
|
+
continue;
|
|
113
|
+
const funcExprText = callExpr.getExpression().getText();
|
|
114
|
+
if (funcExprText !== exports.SERVER_FUNCTION_WRAPPER_NAME && !funcExprText.endsWith(`.${exports.SERVER_FUNCTION_WRAPPER_NAME}`))
|
|
115
|
+
continue;
|
|
116
|
+
const args = callExpr.getArguments();
|
|
117
|
+
if (args.length !== 1 || args[0].getKind() !== ts_morph_1.SyntaxKind.ArrowFunction)
|
|
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
|
+
const exportName = decl.getName();
|
|
122
|
+
fileRegistry.set(exportName, {
|
|
123
|
+
funcId: (0, exports.formatFuncIdName)(exportName, path_1.default.relative(contextDir, filePath), decl.getStartLineNumber()),
|
|
124
|
+
filePath,
|
|
125
|
+
exportName,
|
|
126
|
+
requireContext,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
if (fileRegistry.size > 0)
|
|
130
|
+
registry.set(filePath, fileRegistry);
|
|
131
|
+
};
|
|
132
|
+
// Imported functions resolution
|
|
133
|
+
const getImportedServerFunctions = (sourceFile, registry) => {
|
|
134
|
+
var _a;
|
|
135
|
+
const importedFuncs = new Map();
|
|
136
|
+
for (const importDecl of sourceFile.getImportDeclarations()) {
|
|
137
|
+
const moduleSpecifier = importDecl.getModuleSpecifierValue();
|
|
138
|
+
if (!moduleSpecifier.startsWith('.'))
|
|
139
|
+
continue;
|
|
140
|
+
const resolvedPath = (0, exports.resolveFilePath)(path_1.default.resolve(path_1.default.dirname(sourceFile.getFilePath()), moduleSpecifier));
|
|
141
|
+
const fileRegistry = registry.get(resolvedPath);
|
|
142
|
+
if (!fileRegistry)
|
|
143
|
+
continue;
|
|
144
|
+
for (const namedImport of importDecl.getNamedImports()) {
|
|
145
|
+
const funcInfo = fileRegistry.get(namedImport.getName());
|
|
146
|
+
if (funcInfo)
|
|
147
|
+
importedFuncs.set(((_a = namedImport.getAliasNode()) === null || _a === void 0 ? void 0 : _a.getText()) || namedImport.getName(), funcInfo);
|
|
148
|
+
}
|
|
149
|
+
const nsImport = importDecl.getNamespaceImport();
|
|
150
|
+
if (nsImport) {
|
|
151
|
+
fileRegistry.forEach((info, name) => importedFuncs.set(`${nsImport.getText()}.${name}`, info));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return importedFuncs;
|
|
155
|
+
};
|
|
156
|
+
exports.getImportedServerFunctions = getImportedServerFunctions;
|
|
157
|
+
// Transformations
|
|
158
|
+
const transformCallSites = (sourceFile, importedFuncs) => {
|
|
159
|
+
if (importedFuncs.size === 0)
|
|
160
|
+
return false;
|
|
161
|
+
let modified = false;
|
|
162
|
+
sourceFile.forEachDescendant((node) => {
|
|
163
|
+
if (node.getKind() !== ts_morph_1.SyntaxKind.CallExpression)
|
|
164
|
+
return;
|
|
165
|
+
const callExpr = node;
|
|
166
|
+
const calleeName = getCalleeFullName(callExpr);
|
|
167
|
+
const funcInfo = importedFuncs.get(calleeName);
|
|
168
|
+
if (!funcInfo)
|
|
169
|
+
return;
|
|
170
|
+
const args = callExpr.getArguments().map(a => a.getText()).join(', ');
|
|
171
|
+
callExpr.replaceWithText(`globalThis.${exports.ZERO_COM_CLIENT_CALL}('${funcInfo.funcId}', [${args}])`);
|
|
172
|
+
modified = true;
|
|
173
|
+
});
|
|
174
|
+
return modified;
|
|
175
|
+
};
|
|
176
|
+
exports.transformCallSites = transformCallSites;
|
|
177
|
+
const transformHandleCalls = (sourceFile) => {
|
|
178
|
+
let modified = false;
|
|
179
|
+
sourceFile.forEachDescendant((node) => {
|
|
180
|
+
if (node.getKind() !== ts_morph_1.SyntaxKind.CallExpression)
|
|
181
|
+
return;
|
|
182
|
+
const callExpr = node;
|
|
183
|
+
if (!(0, exports.isFromLibrary)(callExpr, exports.LIBRARY_NAME) || getCalleeName(callExpr) !== exports.HANDLE_NAME)
|
|
184
|
+
return;
|
|
185
|
+
const args = callExpr.getArguments();
|
|
186
|
+
if (args.length < 3)
|
|
187
|
+
return;
|
|
188
|
+
callExpr.replaceWithText(`${exports.EXEC_FUNC_NAME}(globalThis.${exports.ZERO_COM_SERVER_REGISTRY}[${args[0].getText()}], ${args[1].getText()}, ${args[2].getText()})`);
|
|
189
|
+
modified = true;
|
|
190
|
+
});
|
|
191
|
+
return modified;
|
|
192
|
+
};
|
|
193
|
+
exports.transformHandleCalls = transformHandleCalls;
|
|
194
|
+
const transformSendCalls = (sourceFile) => {
|
|
195
|
+
let modified = false;
|
|
196
|
+
sourceFile.forEachDescendant((node) => {
|
|
197
|
+
if (node.getKind() !== ts_morph_1.SyntaxKind.CallExpression)
|
|
198
|
+
return;
|
|
199
|
+
const callExpr = node;
|
|
200
|
+
if (!(0, exports.isFromLibrary)(callExpr, exports.LIBRARY_NAME) || getCalleeName(callExpr) !== exports.CALL_NAME)
|
|
201
|
+
return;
|
|
202
|
+
const args = callExpr.getArguments();
|
|
203
|
+
if (args.length < 1)
|
|
204
|
+
return;
|
|
205
|
+
callExpr.replaceWithText(`globalThis.${exports.ZERO_COM_CLIENT_CALL} = ${args[0].getText()}`);
|
|
206
|
+
modified = true;
|
|
207
|
+
});
|
|
208
|
+
return modified;
|
|
209
|
+
};
|
|
210
|
+
exports.transformSendCalls = transformSendCalls;
|
|
211
|
+
const appendRegistryCode = (sourceFile, fileRegistry) => {
|
|
212
|
+
const registrations = Array.from(fileRegistry.values())
|
|
213
|
+
.map(info => `globalThis.${exports.ZERO_COM_SERVER_REGISTRY}['${info.funcId}'] = ${info.exportName}`)
|
|
214
|
+
.join(';\n');
|
|
215
|
+
return `${sourceFile.getFullText()}
|
|
216
|
+
if (!globalThis.${exports.ZERO_COM_SERVER_REGISTRY}) globalThis.${exports.ZERO_COM_SERVER_REGISTRY} = Object.create(null);
|
|
217
|
+
${registrations};`;
|
|
218
|
+
};
|
|
219
|
+
exports.appendRegistryCode = appendRegistryCode;
|
|
220
|
+
const transformSourceFile = (filePath, content, registry) => {
|
|
221
|
+
const project = (0, exports.createProject)();
|
|
222
|
+
const sourceFile = project.createSourceFile(filePath, content, { overwrite: true });
|
|
223
|
+
const fileRegistry = registry.get(filePath);
|
|
224
|
+
const isServerFunctionFile = fileRegistry && fileRegistry.size > 0;
|
|
225
|
+
const importedFuncs = (0, exports.getImportedServerFunctions)(sourceFile, registry);
|
|
226
|
+
// Don't transform calls to functions defined in the same file
|
|
227
|
+
if (isServerFunctionFile) {
|
|
228
|
+
fileRegistry.forEach((_, name) => importedFuncs.delete(name));
|
|
229
|
+
}
|
|
230
|
+
const callsTransformed = (0, exports.transformCallSites)(sourceFile, importedFuncs);
|
|
231
|
+
const handleTransformed = (0, exports.transformHandleCalls)(sourceFile);
|
|
232
|
+
const sendTransformed = (0, exports.transformSendCalls)(sourceFile);
|
|
233
|
+
if (isServerFunctionFile) {
|
|
234
|
+
return { content: (0, exports.appendRegistryCode)(sourceFile, fileRegistry), transformed: true };
|
|
235
|
+
}
|
|
236
|
+
if (callsTransformed || handleTransformed || sendTransformed) {
|
|
237
|
+
return { content: sourceFile.getFullText(), transformed: true };
|
|
238
|
+
}
|
|
239
|
+
return { content, transformed: false };
|
|
240
|
+
};
|
|
241
|
+
exports.transformSourceFile = transformSourceFile;
|
|
242
|
+
const emitToJs = (filePath, content) => {
|
|
243
|
+
const project = (0, exports.createProject)();
|
|
244
|
+
project.createSourceFile(filePath, content, { overwrite: true });
|
|
245
|
+
const files = project.emitToMemory().getFiles();
|
|
246
|
+
return files.length > 0 ? files[0].text : content;
|
|
247
|
+
};
|
|
248
|
+
exports.emitToJs = emitToJs;
|
|
249
|
+
const applyReplacements = (code, compilationId) => {
|
|
250
|
+
let result = code;
|
|
251
|
+
for (const { target, replacement } of (0, exports.getReplacements)(compilationId)) {
|
|
252
|
+
result = result.replaceAll(target, replacement);
|
|
253
|
+
}
|
|
254
|
+
return result;
|
|
255
|
+
};
|
|
256
|
+
exports.applyReplacements = applyReplacements;
|
package/lib/rollup.d.ts
CHANGED
package/lib/rollup.js
CHANGED
|
@@ -14,158 +14,49 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.zeroComRollupPlugin = zeroComRollupPlugin;
|
|
16
16
|
const fs_1 = __importDefault(require("fs"));
|
|
17
|
-
const minimatch_1 = require("minimatch");
|
|
18
17
|
const path_1 = __importDefault(require("path"));
|
|
19
|
-
const ts_morph_1 = require("ts-morph");
|
|
20
|
-
const typescript_1 = __importDefault(require("typescript"));
|
|
21
18
|
const common_1 = require("./common");
|
|
22
|
-
function zeroComRollupPlugin(options) {
|
|
23
|
-
const { development = true
|
|
24
|
-
const compilationId =
|
|
25
|
-
const
|
|
26
|
-
const serverPattern = patterns.server;
|
|
27
|
-
const replacements = [
|
|
28
|
-
{ target: common_1.ZERO_COM_CLIENT_SEND, replacement: `__ZERO_COM_CLIENT_SEND_${compilationId}` },
|
|
29
|
-
{ target: common_1.ZERO_COM_SERVER_REGISTRY, replacement: `__ZERO_COM_SERVER_REGISTRY_${compilationId}` }
|
|
30
|
-
];
|
|
19
|
+
function zeroComRollupPlugin(options = {}) {
|
|
20
|
+
const { development = true } = options;
|
|
21
|
+
const compilationId = (0, common_1.generateCompilationId)();
|
|
22
|
+
const registry = new Map();
|
|
31
23
|
return {
|
|
32
24
|
name: 'zero-com-rollup-plugin',
|
|
33
|
-
|
|
25
|
+
buildStart() {
|
|
26
|
+
(0, common_1.buildRegistry)(process.cwd(), registry);
|
|
27
|
+
console.log(`[ZeroComRollupPlugin] Found ${registry.size} files with server functions`);
|
|
28
|
+
},
|
|
29
|
+
resolveId(source, importer, opts) {
|
|
34
30
|
return __awaiter(this, void 0, void 0, function* () {
|
|
35
|
-
if (!importer)
|
|
36
|
-
return null;
|
|
37
|
-
const resolveResult = yield this.resolve(source, importer, Object.assign(Object.assign({}, options), { skipSelf: true }));
|
|
38
|
-
if (!resolveResult)
|
|
31
|
+
if (!importer || (!source.startsWith('.') && !source.startsWith('/')))
|
|
39
32
|
return null;
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
if (!isServerFile)
|
|
33
|
+
const result = yield this.resolve(source, importer, Object.assign(Object.assign({}, opts), { skipSelf: true }));
|
|
34
|
+
if (!result)
|
|
43
35
|
return null;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
let resolvedPath = '';
|
|
49
|
-
if (fs_1.default.existsSync(tsPath)) {
|
|
50
|
-
resolvedPath = tsPath;
|
|
51
|
-
}
|
|
52
|
-
else if (fs_1.default.existsSync(jsPath)) {
|
|
53
|
-
resolvedPath = jsPath;
|
|
54
|
-
}
|
|
55
|
-
else if (fs_1.default.existsSync(mjsPath)) {
|
|
56
|
-
resolvedPath = mjsPath;
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
36
|
+
let resolvedPath = (0, common_1.resolveFilePath)(result.id);
|
|
37
|
+
if (!resolvedPath && fs_1.default.existsSync(result.id))
|
|
38
|
+
resolvedPath = result.id;
|
|
39
|
+
if (!resolvedPath)
|
|
59
40
|
return null;
|
|
60
|
-
}
|
|
61
|
-
return {
|
|
62
|
-
id: resolvedPath,
|
|
63
|
-
meta: {
|
|
64
|
-
isClient: requestedFromClient,
|
|
65
|
-
}
|
|
66
|
-
};
|
|
41
|
+
return { id: resolvedPath, meta: { needsTransform: true } };
|
|
67
42
|
});
|
|
68
43
|
},
|
|
69
44
|
load(id) {
|
|
70
|
-
var _a;
|
|
71
|
-
|
|
72
|
-
if (meta === undefined || meta.isClient === undefined)
|
|
45
|
+
var _a, _b;
|
|
46
|
+
if (!((_b = (_a = this.getModuleInfo(id)) === null || _a === void 0 ? void 0 : _a.meta) === null || _b === void 0 ? void 0 : _b.needsTransform) || !fs_1.default.existsSync(id))
|
|
73
47
|
return null;
|
|
74
|
-
const
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
});
|
|
81
|
-
const sourceFile = project.createSourceFile(id, originalContent, { overwrite: true });
|
|
82
|
-
if (meta.isClient) {
|
|
83
|
-
sourceFile.getFunctions().forEach(func => {
|
|
84
|
-
if (func.isExported() && func.isAsync()) {
|
|
85
|
-
const funcName = String(func.getName());
|
|
86
|
-
const lineNumber = func.getStartLineNumber();
|
|
87
|
-
const funcParams = func.getParameters().map(p => p.getName()).join(', ');
|
|
88
|
-
const funcId = (0, common_1.formatFuncIdName)(funcName, path_1.default.relative(process.cwd(), id), lineNumber);
|
|
89
|
-
const newFunctionBody = `return window.${common_1.ZERO_COM_CLIENT_SEND}({funcId: '${funcId}', params: [${funcParams}]})`;
|
|
90
|
-
func.setBodyText(newFunctionBody);
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
sourceFile.getVariableDeclarations().forEach(decl => {
|
|
94
|
-
const initializer = decl.getInitializer();
|
|
95
|
-
if (!initializer || !decl.isExported())
|
|
96
|
-
return;
|
|
97
|
-
if (initializer instanceof ts_morph_1.ArrowFunction && initializer.isAsync()) {
|
|
98
|
-
const funcName = decl.getName();
|
|
99
|
-
const lineNumber = decl.getStartLineNumber();
|
|
100
|
-
const funcParams = initializer.getParameters().map(p => p.getName()).join(', ');
|
|
101
|
-
const funcId = (0, common_1.formatFuncIdName)(funcName, path_1.default.relative(process.cwd(), id), lineNumber);
|
|
102
|
-
const newFunctionBody = `return window.${common_1.ZERO_COM_CLIENT_SEND}({funcId: '${funcId}', params: [${funcParams}]})`;
|
|
103
|
-
initializer.setBodyText(newFunctionBody);
|
|
104
|
-
}
|
|
105
|
-
else if (initializer.getKind() === typescript_1.default.SyntaxKind.CallExpression && initializer.getExpression().getText() === 'serverFn') {
|
|
106
|
-
const call = initializer;
|
|
107
|
-
const arg = call.getArguments()[0];
|
|
108
|
-
if (arg && arg instanceof ts_morph_1.ArrowFunction) {
|
|
109
|
-
const funcName = decl.getName();
|
|
110
|
-
const lineNumber = decl.getStartLineNumber();
|
|
111
|
-
const funcParams = arg.getParameters().map(p => p.getName()).join(', ');
|
|
112
|
-
const funcId = (0, common_1.formatFuncIdName)(funcName, path_1.default.relative(process.cwd(), id), lineNumber);
|
|
113
|
-
const newFunctionBody = `return window.${common_1.ZERO_COM_CLIENT_SEND}({funcId: '${funcId}', params: [${funcParams}]})`;
|
|
114
|
-
// Create a new arrow function string
|
|
115
|
-
const newArrowFunc = `(${funcParams}) => { ${newFunctionBody} }`;
|
|
116
|
-
// Replace the serverFn call with the new arrow function
|
|
117
|
-
decl.setInitializer(newArrowFunc);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
else {
|
|
123
|
-
const chunks = [];
|
|
124
|
-
sourceFile.getFunctions().forEach(func => {
|
|
125
|
-
if (func.isExported() && func.isAsync()) {
|
|
126
|
-
const funcName = String(func.getName());
|
|
127
|
-
const lineNumber = func.getStartLineNumber();
|
|
128
|
-
const funcId = (0, common_1.formatFuncIdName)(funcName, path_1.default.relative(process.cwd(), id), lineNumber);
|
|
129
|
-
chunks.push(`global.${common_1.ZERO_COM_SERVER_REGISTRY}['${funcId}'] = ${funcName}`);
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
sourceFile.getVariableDeclarations().forEach(decl => {
|
|
133
|
-
const initializer = decl.getInitializer();
|
|
134
|
-
if (initializer) {
|
|
135
|
-
const isServerFn = initializer.getKind() === typescript_1.default.SyntaxKind.CallExpression && initializer.getExpression().getText() === 'serverFn';
|
|
136
|
-
if ((initializer instanceof ts_morph_1.ArrowFunction && decl.isExported() && initializer.isAsync()) ||
|
|
137
|
-
(isServerFn && decl.isExported())) {
|
|
138
|
-
const funcName = decl.getName();
|
|
139
|
-
const lineNumber = decl.getStartLineNumber();
|
|
140
|
-
const funcId = (0, common_1.formatFuncIdName)(funcName, path_1.default.relative(process.cwd(), id), lineNumber);
|
|
141
|
-
chunks.push(`global.${common_1.ZERO_COM_SERVER_REGISTRY}['${funcId}'] = ${funcName}`);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
});
|
|
145
|
-
if (chunks.length > 0) {
|
|
146
|
-
const textToAdd = `\nif (!global.${common_1.ZERO_COM_SERVER_REGISTRY}) global.${common_1.ZERO_COM_SERVER_REGISTRY} = Object.create(null); ${chunks.join(',')}`;
|
|
147
|
-
sourceFile.insertText(sourceFile.getEnd(), textToAdd);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
const result = project.emitToMemory();
|
|
151
|
-
const newContent = result.getFiles()[0].text;
|
|
152
|
-
return newContent;
|
|
48
|
+
const content = fs_1.default.readFileSync(id, 'utf8');
|
|
49
|
+
const result = (0, common_1.transformSourceFile)(id, content, registry);
|
|
50
|
+
if (!result.transformed)
|
|
51
|
+
return null;
|
|
52
|
+
console.log(`[ZeroComRollupPlugin] Transformed: ${path_1.default.relative(process.cwd(), id)}`);
|
|
53
|
+
return (0, common_1.emitToJs)(id, result.content);
|
|
153
54
|
},
|
|
154
|
-
renderChunk(code
|
|
55
|
+
renderChunk(code) {
|
|
155
56
|
if (development)
|
|
156
57
|
return null;
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
replacements.forEach(({ target, replacement }) => {
|
|
160
|
-
if (newCode.includes(target)) {
|
|
161
|
-
newCode = newCode.replaceAll(target, replacement);
|
|
162
|
-
modified = true;
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
|
-
if (modified) {
|
|
166
|
-
return { code: newCode, map: null };
|
|
167
|
-
}
|
|
168
|
-
return null;
|
|
58
|
+
const newCode = (0, common_1.applyReplacements)(code, compilationId);
|
|
59
|
+
return newCode !== code ? { code: newCode, map: null } : null;
|
|
169
60
|
}
|
|
170
61
|
};
|
|
171
62
|
}
|
package/lib/runtime.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
declare global {
|
|
2
|
+
var ZERO_COM_SERVER_REGISTRY: {
|
|
3
|
+
[funcId: string]: (...args: any[]) => any;
|
|
4
|
+
};
|
|
5
|
+
var ZERO_COM_CLIENT_CALL: (funcId: string, args: any[]) => Promise<any>;
|
|
6
|
+
}
|
|
7
|
+
declare const contextBrand: unique symbol;
|
|
8
|
+
export type context<T = unknown> = T & {
|
|
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>;
|
|
13
|
+
export declare const execFunc: (sfn: ReturnType<typeof func>, ctx: any, args: any[]) => ReturnType<typeof sfn>;
|
|
14
|
+
export declare const handle: (_funcId: string, _ctx: any, _args: any[]) => any;
|
|
15
|
+
export declare const call: (_fn: (funcId: string, args: any[]) => Promise<any>) => void;
|
|
16
|
+
export {};
|
package/lib/runtime.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.call = exports.handle = exports.execFunc = void 0;
|
|
4
|
+
exports.func = func;
|
|
5
|
+
// Implementation
|
|
6
|
+
function func(fn) {
|
|
7
|
+
fn.requireContext = true;
|
|
8
|
+
return fn;
|
|
9
|
+
}
|
|
10
|
+
// Internal implementation - receives the actual function from registry
|
|
11
|
+
const execFunc = (sfn, ctx, args) => {
|
|
12
|
+
const fn = sfn;
|
|
13
|
+
if (fn.requireContext) {
|
|
14
|
+
return fn(ctx, ...args);
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
return fn(...args);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
exports.execFunc = execFunc;
|
|
21
|
+
// User-facing function - transformed by plugin to execFunc(globalThis.ZERO_COM_SERVER_REGISTRY[funcId], ctx, args)
|
|
22
|
+
const handle = (_funcId, _ctx, _args) => {
|
|
23
|
+
throw new Error('handle() was not transformed. Ensure the zero-com plugin is configured.');
|
|
24
|
+
};
|
|
25
|
+
exports.handle = handle;
|
|
26
|
+
// User-facing function - transformed by plugin to globalThis.ZERO_COM_CLIENT_CALL = fn
|
|
27
|
+
const call = (_fn) => {
|
|
28
|
+
throw new Error('call() was not transformed. Ensure the zero-com plugin is configured.');
|
|
29
|
+
};
|
|
30
|
+
exports.call = call;
|
package/lib/webpack.d.ts
CHANGED
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
import { Compiler } from 'webpack';
|
|
2
2
|
import { Options } from './common';
|
|
3
|
-
export type Message = {
|
|
4
|
-
funcId: string;
|
|
5
|
-
params: any[];
|
|
6
|
-
[key: string]: any;
|
|
7
|
-
};
|
|
8
3
|
export declare class ZeroComWebpackPlugin {
|
|
9
4
|
private options;
|
|
10
5
|
private compilationId;
|
|
11
|
-
private
|
|
12
|
-
|
|
13
|
-
constructor(options: Options);
|
|
6
|
+
private registry;
|
|
7
|
+
constructor(options?: Options);
|
|
14
8
|
apply(compiler: Compiler): void;
|
|
15
9
|
}
|
package/lib/webpack.js
CHANGED
|
@@ -5,157 +5,52 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.ZeroComWebpackPlugin = void 0;
|
|
7
7
|
const fs_1 = __importDefault(require("fs"));
|
|
8
|
-
const minimatch_1 = require("minimatch");
|
|
9
8
|
const path_1 = __importDefault(require("path"));
|
|
10
|
-
const ts_morph_1 = require("ts-morph");
|
|
11
|
-
const typescript_1 = __importDefault(require("typescript"));
|
|
12
9
|
const common_1 = require("./common");
|
|
13
10
|
class ZeroComWebpackPlugin {
|
|
14
|
-
constructor(options) {
|
|
15
|
-
this.
|
|
16
|
-
this.
|
|
17
|
-
this.
|
|
18
|
-
this.serverPattern = this.options.patterns.server;
|
|
11
|
+
constructor(options = {}) {
|
|
12
|
+
this.registry = new Map();
|
|
13
|
+
this.options = Object.assign({ development: true }, options);
|
|
14
|
+
this.compilationId = (0, common_1.generateCompilationId)();
|
|
19
15
|
}
|
|
20
16
|
apply(compiler) {
|
|
21
17
|
const pluginName = ZeroComWebpackPlugin.name;
|
|
22
18
|
const { webpack } = compiler;
|
|
23
|
-
|
|
19
|
+
// Build registry before compilation
|
|
20
|
+
compiler.hooks.beforeCompile.tap(pluginName, () => {
|
|
21
|
+
(0, common_1.buildRegistry)(compiler.context, this.registry);
|
|
22
|
+
console.log(`[ZeroComWebpackPlugin] Found ${this.registry.size} files with server functions`);
|
|
23
|
+
});
|
|
24
|
+
// Transform files during module resolution
|
|
24
25
|
compiler.hooks.normalModuleFactory.tap(pluginName, (nmf) => {
|
|
25
26
|
nmf.hooks.beforeResolve.tap(pluginName, (resolveData) => {
|
|
26
|
-
|
|
27
|
-
const isServerFile = (0, minimatch_1.minimatch)(absolutePath, path_1.default.join(compiler.context, this.serverPattern));
|
|
28
|
-
if (!isServerFile)
|
|
27
|
+
if (!resolveData.request.startsWith('.') && !resolveData.request.startsWith('/'))
|
|
29
28
|
return;
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
if (
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
else if (fs_1.default.existsSync(mjsPath)) {
|
|
42
|
-
resolvedPath = mjsPath;
|
|
43
|
-
}
|
|
44
|
-
else {
|
|
45
|
-
throw new Error('Unable to resolve: ' + absolutePath);
|
|
46
|
-
}
|
|
47
|
-
const originalContent = fs_1.default.readFileSync(resolvedPath, 'utf8');
|
|
48
|
-
const project = new ts_morph_1.Project({
|
|
49
|
-
compilerOptions: {
|
|
50
|
-
target: typescript_1.default.ScriptTarget.ES2017,
|
|
51
|
-
module: typescript_1.default.ModuleKind.ESNext,
|
|
52
|
-
},
|
|
53
|
-
});
|
|
54
|
-
const sourceFile = project.createSourceFile(absolutePath, originalContent, { overwrite: true });
|
|
55
|
-
let newModuleContent = '';
|
|
56
|
-
if (requestedFromClient) {
|
|
57
|
-
const generatedFunctions = [];
|
|
58
|
-
sourceFile.getFunctions().forEach(func => {
|
|
59
|
-
if (func.isExported() && func.isAsync()) {
|
|
60
|
-
const funcName = String(func.getName());
|
|
61
|
-
const lineNumber = func.getStartLineNumber();
|
|
62
|
-
const funcParams = func.getParameters().map(p => p.getName()).join(', ');
|
|
63
|
-
const funcId = (0, common_1.formatFuncIdName)(funcName, path_1.default.relative(compiler.context, absolutePath), lineNumber);
|
|
64
|
-
const newFunctionBody = `return window.${common_1.ZERO_COM_CLIENT_SEND}({funcId: '${funcId}', params: [${funcParams}]})`;
|
|
65
|
-
func.setBodyText(newFunctionBody);
|
|
66
|
-
generatedFunctions.push(func.getText());
|
|
67
|
-
console.log('client:', funcId);
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
sourceFile.getVariableDeclarations().forEach(decl => {
|
|
71
|
-
const initializer = decl.getInitializer();
|
|
72
|
-
if (!initializer || !decl.isExported())
|
|
73
|
-
return;
|
|
74
|
-
if (initializer instanceof ts_morph_1.ArrowFunction && initializer.isAsync()) {
|
|
75
|
-
const funcName = decl.getName();
|
|
76
|
-
const lineNumber = decl.getStartLineNumber();
|
|
77
|
-
const funcParams = initializer.getParameters().map(p => p.getName()).join(', ');
|
|
78
|
-
const funcId = (0, common_1.formatFuncIdName)(funcName, path_1.default.relative(compiler.context, absolutePath), lineNumber);
|
|
79
|
-
const newFunctionBody = `return window.${common_1.ZERO_COM_CLIENT_SEND}({funcId: '${funcId}', params: [${funcParams}]})`;
|
|
80
|
-
initializer.setBodyText(newFunctionBody);
|
|
81
|
-
generatedFunctions.push(decl.getVariableStatementOrThrow().getText());
|
|
82
|
-
console.log('client:', funcId);
|
|
83
|
-
}
|
|
84
|
-
else if (initializer.getKind() === typescript_1.default.SyntaxKind.CallExpression && initializer.getExpression().getText() === 'serverFn') {
|
|
85
|
-
const call = initializer;
|
|
86
|
-
const arg = call.getArguments()[0];
|
|
87
|
-
if (arg && arg instanceof ts_morph_1.ArrowFunction) {
|
|
88
|
-
const funcName = decl.getName();
|
|
89
|
-
const lineNumber = decl.getStartLineNumber();
|
|
90
|
-
const funcParams = arg.getParameters().map(p => p.getName()).join(', ');
|
|
91
|
-
const funcId = (0, common_1.formatFuncIdName)(funcName, path_1.default.relative(compiler.context, absolutePath), lineNumber);
|
|
92
|
-
const newFunctionBody = `return window.${common_1.ZERO_COM_CLIENT_SEND}({funcId: '${funcId}', params: [${funcParams}]})`;
|
|
93
|
-
// Create a new arrow function string
|
|
94
|
-
const newArrowFunc = `(${funcParams}) => { ${newFunctionBody} }`;
|
|
95
|
-
// Replace the serverFn call with the new arrow function
|
|
96
|
-
decl.setInitializer(newArrowFunc);
|
|
97
|
-
generatedFunctions.push(decl.getVariableStatementOrThrow().getText());
|
|
98
|
-
console.log('client:', funcId);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
newModuleContent = generatedFunctions.join('\n\n');
|
|
103
|
-
}
|
|
104
|
-
else {
|
|
105
|
-
const chunks = [];
|
|
106
|
-
sourceFile.getFunctions().forEach(func => {
|
|
107
|
-
if (func.isExported() && func.isAsync()) {
|
|
108
|
-
const funcName = String(func.getName());
|
|
109
|
-
const lineNumber = func.getStartLineNumber();
|
|
110
|
-
const funcId = (0, common_1.formatFuncIdName)(funcName, path_1.default.relative(compiler.context, absolutePath), lineNumber);
|
|
111
|
-
chunks.push(`global.${common_1.ZERO_COM_SERVER_REGISTRY}['${funcId}'] = ${funcName}`);
|
|
112
|
-
console.log('server:', funcId);
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
sourceFile.getVariableDeclarations().forEach(decl => {
|
|
116
|
-
const initializer = decl.getInitializer();
|
|
117
|
-
if (initializer) {
|
|
118
|
-
const isServerFn = initializer.getKind() === typescript_1.default.SyntaxKind.CallExpression && initializer.getExpression().getText() === 'serverFn';
|
|
119
|
-
if ((initializer instanceof ts_morph_1.ArrowFunction && decl.isExported() && initializer.isAsync()) ||
|
|
120
|
-
(isServerFn && decl.isExported())) {
|
|
121
|
-
const funcName = decl.getName();
|
|
122
|
-
const lineNumber = decl.getStartLineNumber();
|
|
123
|
-
const funcId = (0, common_1.formatFuncIdName)(funcName, path_1.default.relative(compiler.context, absolutePath), lineNumber);
|
|
124
|
-
chunks.push(`global.${common_1.ZERO_COM_SERVER_REGISTRY}['${funcId}'] = ${funcName}`);
|
|
125
|
-
console.log('server:', funcId);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
newModuleContent = `${originalContent} if (!global.${common_1.ZERO_COM_SERVER_REGISTRY}) global.${common_1.ZERO_COM_SERVER_REGISTRY} = Object.create(null); ${chunks.join(',')}`;
|
|
130
|
-
}
|
|
131
|
-
project.createSourceFile(absolutePath + '.ts', newModuleContent, { overwrite: true });
|
|
132
|
-
const result = project.emitToMemory();
|
|
133
|
-
const newContent = result.getFiles()[0].text;
|
|
134
|
-
const inlineLoader = `data:text/javascript,${encodeURIComponent(newContent)}`;
|
|
135
|
-
resolveData.request = inlineLoader;
|
|
29
|
+
const resolvedPath = (0, common_1.resolveFilePath)(path_1.default.resolve(resolveData.context, resolveData.request));
|
|
30
|
+
if (!resolvedPath || !fs_1.default.existsSync(resolvedPath))
|
|
31
|
+
return;
|
|
32
|
+
const content = fs_1.default.readFileSync(resolvedPath, 'utf8');
|
|
33
|
+
const result = (0, common_1.transformSourceFile)(resolvedPath, content, this.registry);
|
|
34
|
+
if (!result.transformed)
|
|
35
|
+
return;
|
|
36
|
+
const jsContent = (0, common_1.emitToJs)(resolvedPath, result.content);
|
|
37
|
+
resolveData.request = `data:text/javascript,${encodeURIComponent(jsContent)}`;
|
|
38
|
+
console.log(`[ZeroComWebpackPlugin] Transformed: ${path_1.default.relative(compiler.context, resolvedPath)}`);
|
|
136
39
|
});
|
|
137
40
|
});
|
|
41
|
+
// Production: minify global names
|
|
138
42
|
if (this.options.development)
|
|
139
43
|
return;
|
|
140
|
-
const
|
|
141
|
-
{ target: common_1.ZERO_COM_CLIENT_SEND, replacement: `__ZERO_COM_CLIENT_SEND_${this.compilationId}` },
|
|
142
|
-
{ target: common_1.ZERO_COM_SERVER_REGISTRY, replacement: `__ZERO_COM_SERVER_REGISTRY_${this.compilationId}` }
|
|
143
|
-
];
|
|
44
|
+
const { RawSource } = webpack.sources;
|
|
144
45
|
compiler.hooks.thisCompilation.tap(pluginName, (compilation) => {
|
|
145
46
|
compilation.hooks.processAssets.tap({ name: pluginName, stage: webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_INLINE }, (assets) => {
|
|
146
47
|
for (const assetName in assets) {
|
|
147
|
-
if (assetName.endsWith('.js'))
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
modified = true;
|
|
154
|
-
}
|
|
155
|
-
});
|
|
156
|
-
if (modified) {
|
|
157
|
-
compilation.updateAsset(assetName, new RawSource(assetSource));
|
|
158
|
-
}
|
|
48
|
+
if (!assetName.endsWith('.js'))
|
|
49
|
+
continue;
|
|
50
|
+
const source = String(assets[assetName].source());
|
|
51
|
+
const newSource = (0, common_1.applyReplacements)(source, this.compilationId);
|
|
52
|
+
if (newSource !== source) {
|
|
53
|
+
compilation.updateAsset(assetName, new RawSource(newSource));
|
|
159
54
|
}
|
|
160
55
|
}
|
|
161
56
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zero-com",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"repository": "https://github.com/yosbelms/zero-com",
|
|
6
6
|
"keywords": [
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
],
|
|
14
14
|
"scripts": {
|
|
15
15
|
"build": "npx tsc -d",
|
|
16
|
+
"test": "vitest run",
|
|
17
|
+
"test:watch": "vitest",
|
|
16
18
|
"clean": "find . -type f \\( -name \"*.js\" -o -name \"*.js.map\" -o -name \"*.d.ts\" -o -name \"*.d.ts.map\" \\) | grep -v \"./node_modules\" | xargs rm",
|
|
17
19
|
"prepublishOnly": "npm run clean && npm run build"
|
|
18
20
|
},
|
|
@@ -26,7 +28,8 @@
|
|
|
26
28
|
"devDependencies": {
|
|
27
29
|
"@types/webpack": "^5.28.5",
|
|
28
30
|
"rollup": "^4.52.5",
|
|
29
|
-
"typescript": "^5.8.3"
|
|
31
|
+
"typescript": "^5.8.3",
|
|
32
|
+
"vitest": "^4.0.18"
|
|
30
33
|
},
|
|
31
34
|
"peerDependencies": {
|
|
32
35
|
"typescript": "^5.0.0"
|
package/tsconfig.json
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
3
|
/* Visit https://aka.ms/tsconfig to read more about this file */
|
|
4
|
-
|
|
5
4
|
/* Projects */
|
|
6
5
|
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
|
7
6
|
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
|
@@ -9,10 +8,11 @@
|
|
|
9
8
|
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
|
10
9
|
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
|
11
10
|
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
|
12
|
-
|
|
13
11
|
/* Language and Environment */
|
|
14
|
-
"target": "es2016",
|
|
15
|
-
"lib": [
|
|
12
|
+
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
|
13
|
+
"lib": [
|
|
14
|
+
"es2021"
|
|
15
|
+
], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
|
16
16
|
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
|
17
17
|
// "libReplacement": true, /* Enable lib replacement. */
|
|
18
18
|
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
|
|
@@ -24,9 +24,8 @@
|
|
|
24
24
|
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
|
25
25
|
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
|
26
26
|
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
|
27
|
-
|
|
28
27
|
/* Modules */
|
|
29
|
-
"module": "commonjs",
|
|
28
|
+
"module": "commonjs", /* Specify what module code is generated. */
|
|
30
29
|
// "rootDir": "./", /* Specify the root folder within your source files. */
|
|
31
30
|
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
|
|
32
31
|
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
|
@@ -42,15 +41,13 @@
|
|
|
42
41
|
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
|
|
43
42
|
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
|
|
44
43
|
// "noUncheckedSideEffectImports": true, /* Check side effect imports. */
|
|
45
|
-
|
|
44
|
+
"resolveJsonModule": true, /* Enable importing .json files. */
|
|
46
45
|
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
|
|
47
46
|
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
|
48
|
-
|
|
49
47
|
/* JavaScript Support */
|
|
50
48
|
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
|
51
49
|
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
|
52
50
|
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
|
53
|
-
|
|
54
51
|
/* Emit */
|
|
55
52
|
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
|
56
53
|
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
|
@@ -73,19 +70,17 @@
|
|
|
73
70
|
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
|
74
71
|
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
|
|
75
72
|
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
|
76
|
-
|
|
77
73
|
/* Interop Constraints */
|
|
78
74
|
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
|
79
75
|
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
|
|
80
76
|
// "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */
|
|
81
77
|
// "erasableSyntaxOnly": true, /* Do not allow runtime constructs that are not part of ECMAScript. */
|
|
82
78
|
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
|
83
|
-
"esModuleInterop": true,
|
|
79
|
+
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
|
|
84
80
|
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
|
85
|
-
"forceConsistentCasingInFileNames": true,
|
|
86
|
-
|
|
81
|
+
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
|
87
82
|
/* Type Checking */
|
|
88
|
-
"strict": true,
|
|
83
|
+
"strict": true, /* Enable all strict type-checking options. */
|
|
89
84
|
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
|
90
85
|
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
|
91
86
|
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
|
@@ -105,9 +100,13 @@
|
|
|
105
100
|
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
|
|
106
101
|
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
|
107
102
|
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
|
108
|
-
|
|
109
103
|
/* Completeness */
|
|
110
104
|
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
|
111
|
-
"skipLibCheck": true
|
|
105
|
+
"skipLibCheck": true, /* Skip type checking all .d.ts files. */
|
|
106
|
+
"paths": {
|
|
107
|
+
"zero-com": [
|
|
108
|
+
"./index.ts"
|
|
109
|
+
]
|
|
110
|
+
}
|
|
112
111
|
}
|
|
113
|
-
}
|
|
112
|
+
}
|
package/lib/index.d.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
declare global {
|
|
2
|
-
var ZERO_COM_SERVER_REGISTRY: {
|
|
3
|
-
[funcId: string]: (...args: any[]) => any;
|
|
4
|
-
};
|
|
5
|
-
var ZERO_COM_CLIENT_SEND: (...args: any[]) => Promise<any>;
|
|
6
|
-
}
|
|
7
|
-
export declare const serverFn: <Ctx, Rest extends any[], R>(sfn: (ctx: Ctx, ...rest: Rest) => R) => {
|
|
8
|
-
(...rest: Rest): R;
|
|
9
|
-
serverFn: (ctx: Ctx, ...rest: Rest) => R;
|
|
10
|
-
};
|
|
11
|
-
export declare const execServerFn: (sfn: ReturnType<typeof serverFn>, ctx: any, args: any[]) => ReturnType<typeof sfn>;
|
package/lib/index.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.execServerFn = exports.serverFn = void 0;
|
|
4
|
-
const serverFn = (sfn) => {
|
|
5
|
-
const clonedSfn = (...rest) => sfn(null, ...rest);
|
|
6
|
-
clonedSfn.serverFn = sfn;
|
|
7
|
-
return clonedSfn;
|
|
8
|
-
};
|
|
9
|
-
exports.serverFn = serverFn;
|
|
10
|
-
const execServerFn = (sfn, ctx, args) => {
|
|
11
|
-
if (sfn.serverFn) {
|
|
12
|
-
return sfn.serverFn.call(null, ctx, ...args);
|
|
13
|
-
}
|
|
14
|
-
else {
|
|
15
|
-
return sfn.call(null, ...args);
|
|
16
|
-
}
|
|
17
|
-
};
|
|
18
|
-
exports.execServerFn = execServerFn;
|