1 | import { extname, sep } from "path";
|
2 | import { watch, FSWatcher } from "chokidar";
|
3 | import startsWith = require("lodash/startsWith");
|
4 | import { RpcOptions, RpcChannel } from './rpc/channel';
|
5 | import { RpcClient, ClientOptions } from "./rpc/client";
|
6 | import { RpcServer } from "./rpc/server";
|
7 | import { ModuleProxyBase } from "./proxy";
|
8 | import * as util from './util';
|
9 |
|
10 | export {
|
11 | ModuleProxyBase,
|
12 | RpcOptions,
|
13 | RpcChannel,
|
14 | RpcServer,
|
15 | RpcClient,
|
16 | ClientOptions,
|
17 | FSWatcher,
|
18 | util
|
19 | };
|
20 |
|
21 |
|
22 |
|
23 | declare global {
|
24 | type FunctionPropertyNames<T> = {
|
25 | [K in keyof T]: T[K] extends Function ? K : never
|
26 | }[keyof T];
|
27 | type NonFunctionPropertyNames<T> = {
|
28 | [K in keyof T]: T[K] extends Function ? never : K
|
29 | }[keyof T];
|
30 | type FunctionProperties<T> = Pick<T, FunctionPropertyNames<T>>;
|
31 | type NonFunctionProperties<T> = Pick<T, NonFunctionPropertyNames<T>>;
|
32 | type Voidable<T> = {
|
33 | [K in keyof T]: T[K] | void
|
34 | }
|
35 |
|
36 | interface ModuleConstructor<T> {
|
37 | new(...args: any[]): T;
|
38 | getInstance?(): T;
|
39 | }
|
40 |
|
41 | interface ModuleProxy<T, A1 = any, A2 = any, A3 = any, A4 = any, A5 = any> {
|
42 |
|
43 | readonly name: string;
|
44 |
|
45 | readonly path: string;
|
46 |
|
47 | readonly exports: any;
|
48 |
|
49 | readonly proto: T;
|
50 |
|
51 | readonly ctor: ModuleConstructor<T>;
|
52 |
|
53 |
|
54 | create(): T;
|
55 | create(arg1: A1): T;
|
56 | create(arg1: A1, arg2: A2): T;
|
57 | create(arg1: A1, arg2: A2, arg3: A3): T;
|
58 | create(arg1: A1, arg2: A2, arg3: A3, arg4: A4): T;
|
59 | create(arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5): T;
|
60 |
|
61 | |
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 | instance(local: symbol): T;
|
68 | instance(route?: any): FunctionProperties<T> &
|
69 | Voidable<Readonly<NonFunctionProperties<T>>>;
|
70 |
|
71 | |
72 |
|
73 |
|
74 |
|
75 |
|
76 | noLocal(): this;
|
77 |
|
78 | |
79 |
|
80 |
|
81 |
|
82 | inject(route?: any): PropertyDecorator;
|
83 | }
|
84 | }
|
85 |
|
86 | export interface ModuleLoader {
|
87 | [x: string]: any;
|
88 | |
89 |
|
90 |
|
91 |
|
92 | extension: string | string[],
|
93 |
|
94 | load(filename: string): any;
|
95 |
|
96 | unload(filename: string): void;
|
97 | }
|
98 |
|
99 | export class ModuleProxy extends ModuleProxyBase {
|
100 | |
101 |
|
102 |
|
103 |
|
104 | local = util.local;
|
105 |
|
106 | get exports() {
|
107 | return {};
|
108 | }
|
109 |
|
110 |
|
111 | serve(config: string | RpcOptions): Promise<RpcServer> {
|
112 | return new RpcServer(<any>config).open();
|
113 | }
|
114 |
|
115 |
|
116 | connect(config: string | ClientOptions): Promise<RpcClient> {
|
117 | return new RpcClient(<any>config).open();
|
118 | }
|
119 |
|
120 |
|
121 | resolve(path: string): string {
|
122 | let dir = this.path + sep;
|
123 |
|
124 | if (startsWith(path, dir)) {
|
125 | let modPath = path.slice(dir.length),
|
126 | ext = extname(modPath);
|
127 |
|
128 | if (Array.isArray(this.loader.extension)) {
|
129 | if (this.loader.extension.includes(ext)) {
|
130 | modPath = modPath.slice(0, -ext.length);
|
131 | } else {
|
132 | return;
|
133 | }
|
134 | } else if (ext === this.loader.extension) {
|
135 | modPath = modPath.slice(0, -ext.length);
|
136 | } else if (ext) {
|
137 | return;
|
138 | }
|
139 |
|
140 | return this.name + "." + modPath.replace(/\\|\//g, ".");
|
141 | } else {
|
142 | return;
|
143 | }
|
144 | }
|
145 |
|
146 |
|
147 | watch(listener?: (event: "change" | "unlink", filename: string) => void) {
|
148 | let { path } = this;
|
149 | let clearCache = (event: string, filename: string, cb: Function) => {
|
150 | let name = this.resolve(filename);
|
151 |
|
152 | if (name) {
|
153 | delete this.singletons[name];
|
154 | this.loader.unload(filename);
|
155 | cb && cb(event, filename);
|
156 | }
|
157 | };
|
158 |
|
159 | return watch(path, {
|
160 | awaitWriteFinish: true,
|
161 | followSymlinks: false,
|
162 | ignored: /\.(js\.map|d\.ts)$/
|
163 | }).on("change", (filename) => {
|
164 | clearCache("change", filename, listener);
|
165 | }).on("unlink", (filename) => {
|
166 | clearCache("unlink", filename, listener);
|
167 | }).on("unlinkDir", dirname => {
|
168 | dirname = dirname + sep;
|
169 |
|
170 | for (let filename in require.cache) {
|
171 | if (startsWith(filename, dirname)) {
|
172 | clearCache("unlink", filename, listener);
|
173 | }
|
174 | }
|
175 | });
|
176 | }
|
177 |
|
178 |
|
179 | setLoader(loader: ModuleLoader) {
|
180 | this.loader = loader;
|
181 | }
|
182 | }
|
183 |
|
184 | export default ModuleProxy; |
\ | No newline at end of file |