@drayman/core 1.0.0 → 1.5.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.
@@ -71,6 +71,7 @@ class EventHubClass {
71
71
  #handlers;
72
72
  }
73
73
  const EventHub = new EventHubClass();
74
+ const ComponentInstance = {};
74
75
  // const componentInstanceId = process.argv[2];
75
76
  let browserCallbacks = {};
76
77
  // let tree = [];
@@ -96,15 +97,16 @@ const serializeComponentInstanceOptions = (options) => {
96
97
  return serialized;
97
98
  };
98
99
  const renderedComps = {};
99
- const initializeComponentInstance = async ({ browserCommands = [], extensionsPath, extensionsOptions, componentRootDir, componentName, componentOptions, componentNamePrefix = '' }) => {
100
+ const initializeComponentInstance = async ({ componentInstanceId, browserCommands = [], extensionsPath, extensionsOptions, componentRootDir, componentName, componentOptions, componentNamePrefix = '' }) => {
101
+ ComponentInstance.id = componentInstanceId;
100
102
  if (extensionsPath) {
101
103
  extensions = await require(path_1.default.join(process.cwd(), extensionsPath))(extensionsOptions);
102
104
  }
103
105
  const Browser = {};
104
106
  for (const command of browserCommands) {
105
- Browser[command] = async (data = {}) => new Promise((resolve, reject) => {
107
+ Browser[command] = async (data = {}, element) => new Promise((resolve, reject) => {
106
108
  const newData = {};
107
- for (const key of Object.keys(data)) {
109
+ for (const key of Object.keys(data || {})) {
108
110
  if (typeof data[key] === 'function') {
109
111
  const callbackId = shortid_1.default.generate();
110
112
  newData[key] = callbackId;
@@ -122,7 +124,7 @@ const initializeComponentInstance = async ({ browserCommands = [], extensionsPat
122
124
  callback: (response) => resolve(response),
123
125
  once: true,
124
126
  };
125
- sendMessage({ type: 'browserCommand', payload: { data: newData, callbackId, command } });
127
+ sendMessage({ type: 'browserCommand', payload: { data: newData, callbackId, command, element } });
126
128
  });
127
129
  }
128
130
  props = componentOptions || {};
@@ -161,6 +163,7 @@ const initializeComponentInstance = async ({ browserCommands = [], extensionsPat
161
163
  EventHub,
162
164
  Components,
163
165
  Browser,
166
+ ComponentInstance,
164
167
  ...extensions.importable,
165
168
  });
166
169
  let prevChildProps = {};
@@ -213,6 +216,9 @@ const initializeComponentInstance = async ({ browserCommands = [], extensionsPat
213
216
  // const delta = jsondiff.diff(previouslySerializedTree, result.tree);
214
217
  // const delta = compare(previouslySerializedTree, result.tree);
215
218
  sendMessage({ type: 'view', payload: { view: result.tree, updateId } });
219
+ if (updateId === 1 && ComponentInstance.onInit) {
220
+ await ComponentInstance.onInit();
221
+ }
216
222
  // previouslySerializedTree = { ...result.tree };
217
223
  // previouslySerializedTree = JSON.parse(JSON.stringify(result.tree));
218
224
  // process?.send?.({ type: 'view', payload: { view: serializedView } });
@@ -228,6 +234,7 @@ const initializeComponentInstance = async ({ browserCommands = [], extensionsPat
228
234
  EventHub,
229
235
  Components,
230
236
  Browser,
237
+ ComponentInstance,
231
238
  ...extensions.importable,
232
239
  });
233
240
  await forceUpdate();
@@ -276,12 +283,18 @@ const handleComponentEvent = async ({ requestId, options, files, eventName }) =>
276
283
  const handleEventHubEvent = async ({ type, data, groupId }) => {
277
284
  await EventHub._execute(type, data, groupId);
278
285
  };
286
+ const handleDestroyComponentInstance = async () => {
287
+ if (ComponentInstance.onDestroy) {
288
+ await ComponentInstance.onDestroy();
289
+ }
290
+ };
279
291
  worker_1.expose({
280
292
  initializeComponentInstance,
281
293
  updateComponentInstanceProps,
282
294
  handleBrowserCallback,
283
295
  handleComponentEvent,
284
296
  handleEventHubEvent,
297
+ handleDestroyComponentInstance,
285
298
  events() {
286
299
  return observable_1.Observable.from(subject);
287
300
  }
package/dist/index.d.ts CHANGED
@@ -43,10 +43,10 @@ export declare const onInitializeComponentInstance: ({ namespaceId, extensionsPa
43
43
  }) => Promise<void>;
44
44
  export declare const onDisconnect: ({ connectionId }: {
45
45
  connectionId: any;
46
- }) => void;
46
+ }) => Promise<void>;
47
47
  export declare const onDestroyComponentInstance: ({ componentInstanceId }: {
48
48
  componentInstanceId: any;
49
- }) => void;
49
+ }) => Promise<void>;
50
50
  export declare const componentInstances: {
51
51
  [componentInstanceId: string]: {
52
52
  terminate: () => Promise<void>;
package/dist/index.js CHANGED
@@ -7,11 +7,16 @@ exports.componentInstances = exports.onDestroyComponentInstance = exports.onDisc
7
7
  const fs_extra_1 = __importDefault(require("fs-extra"));
8
8
  const path_1 = __importDefault(require("path"));
9
9
  const shortid_1 = __importDefault(require("shortid"));
10
- const node_find_1 = require("node-find");
11
10
  const typescript_1 = __importDefault(require("typescript"));
12
- // import Piscina from 'piscina';
13
- // import { MessageChannel, MessagePort } from 'worker_threads';
14
11
  const threads_1 = require("threads");
12
+ const child_process_1 = require("child_process");
13
+ const getNpmPackages = (nodeModulesPath) => {
14
+ return new Promise((resolve) => {
15
+ child_process_1.exec(`npm ls -a -p`, { cwd: nodeModulesPath, }, function (error, stdout, stderr) {
16
+ resolve(stdout ? stdout.split('\n').filter(x => path_1.default.isAbsolute(x)) : []);
17
+ });
18
+ });
19
+ };
15
20
  const handleComponentEvent = ({ componentInstanceId, eventName, options, files, onSuccess, onError }) => {
16
21
  options = options || {};
17
22
  if (typeof options === 'string') {
@@ -24,19 +29,19 @@ const handleComponentEvent = ({ componentInstanceId, eventName, options, files,
24
29
  }
25
30
  else {
26
31
  onError({ err: `Component instance was not found.` });
27
- // res.status(500).send(`Component instance was not found.`);
28
32
  }
29
33
  };
30
34
  exports.handleComponentEvent = handleComponentEvent;
31
35
  async function getElementsScriptPaths({ nodeModulesPath = null }) {
32
36
  nodeModulesPath = nodeModulesPath || path_1.default.join(process.cwd(), 'node_modules');
33
- const packages = node_find_1.find(node_find_1.path('*/package.json'), { start: nodeModulesPath });
37
+ const packagePaths = await getNpmPackages(nodeModulesPath);
34
38
  const paths = {};
35
- for await (const packageJsonPath of packages) {
36
- const packageJson = await fs_extra_1.default.readJSON(packageJsonPath.toString('/'), { throws: false });
39
+ for (const packagePath of packagePaths) {
40
+ const packageJsonPath = path_1.default.join(packagePath, 'package.json');
41
+ const packageJson = await fs_extra_1.default.readJSON(packageJsonPath, { throws: false });
37
42
  const elements = packageJson?.drayman?.elements || {};
38
43
  for (const element of Object.keys(elements)) {
39
- paths[element] = path_1.default.join(packageJsonPath.parent.toString('/'), elements[element].script);
44
+ paths[element] = path_1.default.join(packagePath, elements[element].script);
40
45
  }
41
46
  }
42
47
  return paths;
@@ -65,74 +70,22 @@ const handleEventHubEvent = async ({ data, groupId = null, type, namespaceId = n
65
70
  exports.handleEventHubEvent = handleEventHubEvent;
66
71
  const saveComponent = async ({ script, outputFile }) => {
67
72
  const tsConfig = JSON.parse(await fs_extra_1.default.readFile(path_1.default.join(__dirname, process.env.NODE_ENV === 'test' ? `../tests/component-processor.tsconfig.test.json` : `../component-processor.tsconfig.json`), 'utf-8'));
68
- // const componentOutputDir = path.join(rootDir, `./src/components`);
69
- // for (const componentFile of componentFiles) {
70
- // const componentScript = await fs.readFile(path.join(componentInputDir, componentFile), 'utf-8');
71
73
  const transpiledComponentScript = typescript_1.default.transpileModule(script, tsConfig);
72
74
  await fs_extra_1.default.outputFile(outputFile, transpiledComponentScript.outputText);
73
- // }
74
75
  };
75
76
  exports.saveComponent = saveComponent;
76
- // const piscina = new Piscina({
77
- // filename: path.join(__dirname, `./component-processor.js`),
78
- // maxThreads: Infinity,
79
- // // minThreads: 100,
80
- // });
81
77
  const onInitializeComponentInstance = async ({ namespaceId = null, extensionsPath = null, extensionsOptions = null, componentNamePrefix = '', componentName, componentRootDir, componentInstanceId, componentOptions, connectionId, emit, onComponentInstanceConsole, browserCommands, }) => {
82
78
  if (componentOptions && typeof componentOptions === 'string') {
83
79
  componentOptions = JSON.parse(componentOptions);
84
80
  }
85
- // const subprocess = execa.node(
86
- // path.join(__dirname, `./component-processor.js`),
87
- // [
88
- // componentInstanceId
89
- // ],
90
- // { nodeOptions: ['--unhandled-rejections=strict'], serialization: 'advanced', }
91
- // );
92
- // const abortController = new AbortController();
93
- // const { port1, port2 } = new MessageChannel();
94
- // const { signal } = abortController;
95
- // port2.on('message', (message) => console.log('received', message));
96
- // port2.postMessage({ foo: 'bar' });
97
- // // // subprocess.stdout?.on('data', (data) => {
98
- // // // onComponentInstanceConsole?.({ text: data.toString('utf8') });
99
- // // // // console.log(data.toString('utf8'));
100
- // // // // httpClient.debug({ data: data.toString('utf8') });
101
- // // // });
102
- // // // subprocess.stderr?.on('data', (data) => {
103
- // // // onComponentInstanceConsole?.({ text: data.toString('utf8') });
104
- // // // // console.log(data.toString('utf8'));
105
- // // // // httpClient.debug({ data: data.toString('utf8') });
106
- // // // });
107
- /**
108
- * When component instance gets destroyed, we must check for existing user event requests
109
- * and cancel them.
110
- */
111
- // subprocess.on('exit', (x) => {
112
- // // const requests = componentInstances[componentInstanceId].eventRequests;
113
- // // for (const requestId of Object.keys(requests)) {
114
- // // requests[requestId].status(500).send(`Component instance was destroyed.`);
115
- // // }
116
- // delete componentInstances[componentInstanceId];
117
- // emit({ type: 'componentInstanceDestroyed', payload: {}, componentInstanceId });
118
- // // httpClient.sendView({
119
- // // view: null,
120
- // // });
121
- // });
122
- /**
123
- * Handles messages sent by component instance process.
124
- */
125
81
  const worker = await threads_1.spawn(new threads_1.Worker(`./component-processor.js`));
126
82
  exports.componentInstances[componentInstanceId] = {
127
- // abortController,
128
- // messagePort: port2,
129
83
  worker,
130
84
  terminate: async () => {
131
85
  await threads_1.Thread.terminate(worker);
132
86
  delete exports.componentInstances[componentInstanceId];
133
87
  emit({ type: 'componentInstanceDestroyed', payload: {}, componentInstanceId });
134
88
  },
135
- // process: subprocess,
136
89
  /**
137
90
  * Used to store user event requests (on button click, on input, etc.).
138
91
  */
@@ -140,61 +93,66 @@ const onInitializeComponentInstance = async ({ namespaceId = null, extensionsPat
140
93
  connectionId,
141
94
  namespaceId,
142
95
  };
143
- if (toTerminate[componentInstanceId]) {
144
- exports.componentInstances[componentInstanceId].terminate();
145
- delete toTerminate[componentInstanceId];
146
- }
147
- else {
148
- worker.events().subscribe(({ type, payload }) => {
149
- /**
150
- * When there is a response to user event request.
151
- */
152
- if (type === 'response') {
153
- const { requestId, result, err } = payload;
154
- if (err) {
155
- // emit({ type: 'componentEventError', payload: { err, requestId }, componentInstanceId });
156
- exports.componentInstances[componentInstanceId].eventRequests[requestId].onError({ err });
157
- // componentInstances[componentInstanceId].eventRequests[requestId].status(500).send(`${err}`);
158
- }
159
- else {
160
- // emit({ type: 'componentEventSuccess', payload: { result, requestId }, componentInstanceId });
161
- exports.componentInstances[componentInstanceId].eventRequests[requestId].onSuccess({ result });
162
- // componentInstances[componentInstanceId].eventRequests[requestId].json(result || null);
163
- }
164
- delete exports.componentInstances[componentInstanceId].eventRequests[requestId];
165
- }
166
- else if (type === 'eventHubEvent') {
167
- const { eventPayload, groupId, type } = payload;
168
- exports.handleEventHubEvent({ data: eventPayload, groupId, type, namespaceId });
169
- }
170
- else if (type === 'console') {
171
- const { text } = payload;
172
- onComponentInstanceConsole?.({ text });
96
+ worker.events().subscribe(({ type, payload }) => {
97
+ /**
98
+ * When there is a response to user event request.
99
+ */
100
+ if (type === 'response') {
101
+ const { requestId, result, err } = payload;
102
+ if (err) {
103
+ exports.componentInstances[componentInstanceId].eventRequests[requestId].onError({ err });
173
104
  }
174
105
  else {
175
- emit({ type, payload, componentInstanceId });
106
+ exports.componentInstances[componentInstanceId].eventRequests[requestId].onSuccess({ result });
176
107
  }
177
- });
178
- worker.initializeComponentInstance({ browserCommands, componentNamePrefix, componentName, componentRootDir, componentOptions, componentInstanceId, extensionsPath, extensionsOptions });
179
- }
108
+ delete exports.componentInstances[componentInstanceId].eventRequests[requestId];
109
+ }
110
+ else if (type === 'eventHubEvent') {
111
+ const { eventPayload, groupId, type } = payload;
112
+ exports.handleEventHubEvent({ data: eventPayload, groupId, type, namespaceId });
113
+ }
114
+ else if (type === 'console') {
115
+ const { text } = payload;
116
+ onComponentInstanceConsole?.({ text });
117
+ }
118
+ else {
119
+ emit({ type, payload, componentInstanceId });
120
+ }
121
+ });
122
+ worker.initializeComponentInstance({ browserCommands, componentNamePrefix, componentName, componentRootDir, componentOptions, componentInstanceId, extensionsPath, extensionsOptions });
123
+ await clearGarbage();
180
124
  };
181
125
  exports.onInitializeComponentInstance = onInitializeComponentInstance;
182
- const onDisconnect = ({ connectionId }) => {
183
- for (const componentInstanceId of Object.keys(exports.componentInstances).filter(x => exports.componentInstances[x].connectionId === connectionId)) {
184
- exports.onDestroyComponentInstance({ componentInstanceId });
185
- }
126
+ const onDisconnect = async ({ connectionId }) => {
127
+ garbage.connections.push(connectionId);
128
+ await clearGarbage();
186
129
  };
187
130
  exports.onDisconnect = onDisconnect;
188
- const toTerminate = {};
189
- const onDestroyComponentInstance = ({ componentInstanceId }) => {
190
- if (!exports.componentInstances[componentInstanceId]) {
191
- toTerminate[componentInstanceId] = true;
192
- }
193
- else {
194
- exports.componentInstances[componentInstanceId].terminate();
195
- }
196
- // delete componentInstances[componentInstanceId];
197
- // emit({ type: 'componentInstanceDestroyed', payload: {}, componentInstanceId });
131
+ const garbage = {
132
+ connections: [],
133
+ componentInstances: [],
134
+ };
135
+ const onDestroyComponentInstance = async ({ componentInstanceId }) => {
136
+ garbage.componentInstances.push(componentInstanceId);
137
+ await clearGarbage();
198
138
  };
199
139
  exports.onDestroyComponentInstance = onDestroyComponentInstance;
200
140
  exports.componentInstances = {};
141
+ const clearGarbage = async () => {
142
+ const aliveComponentInstances = Object.keys(exports.componentInstances);
143
+ const garbageComponentInstanceIds = [
144
+ ...aliveComponentInstances.filter(x => garbage.connections.includes(exports.componentInstances[x].connectionId)),
145
+ ...garbage.componentInstances,
146
+ ];
147
+ for (const componentInstanceId of garbageComponentInstanceIds) {
148
+ if (exports.componentInstances[componentInstanceId]) {
149
+ try {
150
+ await exports.componentInstances[componentInstanceId].worker.handleDestroyComponentInstance();
151
+ }
152
+ catch (err) {
153
+ console.warn(err);
154
+ }
155
+ exports.componentInstances[componentInstanceId].terminate();
156
+ }
157
+ }
158
+ };
package/dist/utils.js CHANGED
@@ -61,7 +61,7 @@ const render = async (raw, childRenderer = null, renderedArr = [], events = {},
61
61
  }
62
62
  // options[optionKey] = true;
63
63
  }
64
- else if (all_html_attributes_1.svgElementAttributes[type] && (all_html_attributes_1.svgElementAttributes[type].includes(optionKey) || all_html_attributes_1.svgElementAttributes['*'].includes(optionKey))) {
64
+ else if (optionKey === 'ref' || (all_html_attributes_1.svgElementAttributes[type] && (all_html_attributes_1.svgElementAttributes[type].includes(optionKey) || all_html_attributes_1.svgElementAttributes['*'].includes(optionKey)))) {
65
65
  attrs[optionKey] = options[optionKey];
66
66
  }
67
67
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drayman/core",
3
- "version": "1.0.0",
3
+ "version": "1.5.0",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -22,7 +22,7 @@
22
22
  "@babel/preset-env": "^7.13.15",
23
23
  "@babel/preset-react": "^7.13.13",
24
24
  "@babel/preset-typescript": "^7.13.0",
25
- "@drayman/types": "^1.0.0",
25
+ "@drayman/types": "^1.5.0",
26
26
  "@types/jest": "^26.0.22",
27
27
  "@types/node": "^14.14.39",
28
28
  "babel-jest": "^26.6.3",
@@ -39,5 +39,5 @@
39
39
  "ts-node": "^9.1.1",
40
40
  "typescript": "~4.1.5"
41
41
  },
42
- "gitHead": "46398c1c4c9a0d43857174369f214d516a3dfcc2"
42
+ "gitHead": "072618ea36a1d151c16e5233c502ad737eec5785"
43
43
  }