@lvce-editor/iframe-worker 1.0.0 → 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.
@@ -2,11 +2,327 @@ const createUrl = (protocol, host) => {
2
2
  return protocol + '//' + host;
3
3
  };
4
4
 
5
+ const createLocalHostUrl = (locationProtocol, locationHost, isGitpod, webViewPort) => {
6
+ if (isGitpod) {
7
+ return createUrl(locationProtocol, locationHost.replace('3000', `${webViewPort}`));
8
+ }
9
+ return `http://localhost:${webViewPort}`;
10
+ };
11
+
12
+ const Web = 1;
13
+ const Electron = 2;
14
+ const Remote = 3;
15
+ const Test = 4;
16
+
17
+ /* istanbul ignore file */
18
+
19
+ // TODO this should always be completely tree shaken out during build, maybe need to be marked as @__Pure for terser to work
20
+
21
+ // TODO treeshake this function out when targeting electron
22
+
23
+ /**
24
+ * @returns {number}
25
+ */
26
+ const getPlatform = () => {
27
+ // @ts-ignore
28
+ if (typeof PLATFORM !== 'undefined') {
29
+ // @ts-ignore
30
+ return PLATFORM;
31
+ }
32
+ // @ts-ignore
33
+ if (typeof process !== 'undefined' && process.env.NODE_ENV === 'test') {
34
+ return Test;
35
+ }
36
+ // TODO find a better way to pass runtime environment
37
+ if (typeof name !== 'undefined' && name.endsWith('(Electron)')) {
38
+ return Electron;
39
+ }
40
+ if (typeof name !== 'undefined' && name.endsWith('(Web)')) {
41
+ return Web;
42
+ }
43
+ return Remote;
44
+ };
45
+ const platform = getPlatform();
46
+
47
+ const getAssetDir = () => {
48
+ // @ts-ignore
49
+ if (typeof ASSET_DIR !== 'undefined') {
50
+ // @ts-ignore
51
+ return ASSET_DIR;
52
+ }
53
+ if (platform === Electron) {
54
+ return '';
55
+ }
56
+ return '';
57
+ };
58
+ const assetDir = getAssetDir();
59
+
60
+ const getWebViewHtml = (baseUrl, locationOrigin, elements) => {
61
+ if (!elements) {
62
+ return '';
63
+ }
64
+ const middle = [];
65
+ middle.push('<meta charset="utf-8">');
66
+ for (const element of elements) {
67
+ if (element.type === 'title') {
68
+ middle.push(`<title>${element.value}</title>`);
69
+ } else if (element.type === 'script') {
70
+ middle.push(`<script type="module" src="${locationOrigin}${assetDir}/js/preview-injected.js"></script>`);
71
+ middle.push(`<script type="module" src="${locationOrigin}${baseUrl}/${element.path}"></script>`);
72
+ } else if (element.type === 'css') {
73
+ middle.push(`<link rel="stylesheet" href="${locationOrigin}${baseUrl}/${element.path}" />`);
74
+ }
75
+ }
76
+ const middleHtml = middle.join('\n ');
77
+ let html = `<!DOCTYPE html>
78
+ <html>
79
+ <head>
80
+ ${middleHtml}
81
+ </head>
82
+ </html>
83
+ `;
84
+ return html;
85
+ };
86
+
87
+ const WebView = 'lvce-oss-webview';
88
+
89
+ const getWebView$1 = (webViews, webViewId) => {
90
+ for (const webView of webViews) {
91
+ if (webView.id === webViewId) {
92
+ return webView;
93
+ }
94
+ }
95
+ return undefined;
96
+ };
97
+ const getWebViewPath = (webViews, webViewId) => {
98
+ const webView = getWebView$1(webViews, webViewId);
99
+ if (!webView) {
100
+ return '';
101
+ }
102
+ return webView.path;
103
+ };
104
+ const getWebViewUri = (webViews, webViewId) => {
105
+ const webViewPath = getWebViewPath(webViews, webViewId);
106
+ if (!webViewPath) {
107
+ return '';
108
+ }
109
+ if (webViewPath.startsWith('/')) {
110
+ // TODO make it work on windows also
111
+ return `file://${webViewPath}`;
112
+ }
113
+ return webViewPath;
114
+ };
115
+ const getIframeSrcRemote = (webViews, webViewPort, webViewId, locationProtocol, locationHost, isGitpod, root) => {
116
+ const webView = getWebView$1(webViews, webViewId);
117
+ const webViewUri = getWebViewUri(webViews, webViewId);
118
+ if (!webViewUri) {
119
+ return undefined;
120
+ }
121
+ let iframeSrc = webViewUri;
122
+ let webViewRoot = webViewUri;
123
+ if (platform === Electron) {
124
+ const relativePath = new URL(webViewUri).pathname.replace('/index.html', '');
125
+ iframeSrc = `${WebView}://-${relativePath}/`;
126
+ // TODO
127
+ } else if (platform === Remote) {
128
+ const relativePath = new URL(webViewUri).pathname.replace('/index.html', '');
129
+ if (webViewUri.startsWith('file://')) {
130
+ // ignore
131
+ webViewRoot = webViewUri.slice('file://'.length).replace('/index.html', '');
132
+ } else {
133
+ webViewRoot = root + relativePath;
134
+ }
135
+ iframeSrc = createLocalHostUrl(locationProtocol, locationHost, isGitpod, webViewPort);
136
+ }
137
+ let iframeContent = getWebViewHtml('', '', webView.elements);
138
+ // TODO either
139
+ // - load webviews the same as in web using blob urls
140
+ // - load webviews from a pattern like /webviews/:id/:fileName
141
+ iframeContent = iframeContent.replaceAll('/media/', '/').replaceAll('//', '/');
142
+ return {
143
+ srcDoc: '',
144
+ iframeSrc,
145
+ webViewRoot,
146
+ iframeContent
147
+ };
148
+ };
149
+
150
+ const getBlobUrl = (content, contentType) => {
151
+ const blob = new Blob([content], {
152
+ type: contentType
153
+ });
154
+ const url = URL.createObjectURL(blob); // TODO dispose
155
+ return url;
156
+ };
157
+
158
+ const getDefaultBaseUrl = webView => {
159
+ const {
160
+ remotePath,
161
+ path
162
+ } = webView;
163
+ if (remotePath) {
164
+ if (remotePath.endsWith('/index.html')) {
165
+ return remotePath.slice(0, -'/index.html'.length);
166
+ }
167
+ return remotePath;
168
+ }
169
+ if (path) {
170
+ if (path.endsWith('/index.html')) {
171
+ return path.slice(0, -'/index.html'.length);
172
+ }
173
+ return path;
174
+ }
175
+ return '';
176
+ };
177
+ const getWebViewBaseUrl = webView => {
178
+ const defaultBaseUrl = getDefaultBaseUrl(webView);
179
+ return defaultBaseUrl;
180
+ };
181
+
182
+ const getIframeSrc$1 = (webView, locationOrigin) => {
183
+ const baseUrl = getWebViewBaseUrl(webView);
184
+ const srcHtml = getWebViewHtml(baseUrl, locationOrigin, webView.elements);
185
+ if (srcHtml) {
186
+ const blobUrl = getBlobUrl(srcHtml, 'text/html');
187
+ return {
188
+ srcDoc: '',
189
+ iframeSrc: blobUrl,
190
+ webViewRoot: '',
191
+ iframeContent: ''
192
+ };
193
+ }
194
+ return undefined;
195
+ };
196
+
197
+ const getWebView = (webViews, webViewId) => {
198
+ for (const webView of webViews) {
199
+ if (webView.id === webViewId) {
200
+ return webView;
201
+ }
202
+ }
203
+ return undefined;
204
+ };
205
+
206
+ const normalizeLine$1 = line => {
207
+ if (line.startsWith('Error: ')) {
208
+ return line.slice(`Error: `.length);
209
+ }
210
+ if (line.startsWith('VError: ')) {
211
+ return line.slice(`VError: `.length);
212
+ }
213
+ return line;
214
+ };
215
+ const getCombinedMessage$1 = (error, message) => {
216
+ const stringifiedError = normalizeLine$1(`${error}`);
217
+ if (message) {
218
+ return `${message}: ${stringifiedError}`;
219
+ }
220
+ return stringifiedError;
221
+ };
222
+ const NewLine$2 = '\n';
223
+ const getNewLineIndex$1 = (string, startIndex = undefined) => {
224
+ return string.indexOf(NewLine$2, startIndex);
225
+ };
226
+ const mergeStacks$1 = (parent, child) => {
227
+ if (!child) {
228
+ return parent;
229
+ }
230
+ const parentNewLineIndex = getNewLineIndex$1(parent);
231
+ const childNewLineIndex = getNewLineIndex$1(child);
232
+ if (childNewLineIndex === -1) {
233
+ return parent;
234
+ }
235
+ const parentFirstLine = parent.slice(0, parentNewLineIndex);
236
+ const childRest = child.slice(childNewLineIndex);
237
+ const childFirstLine = normalizeLine$1(child.slice(0, childNewLineIndex));
238
+ if (parentFirstLine.includes(childFirstLine)) {
239
+ return parentFirstLine + childRest;
240
+ }
241
+ return child;
242
+ };
243
+ let VError$1 = class VError extends Error {
244
+ constructor(error, message) {
245
+ const combinedMessage = getCombinedMessage$1(error, message);
246
+ super(combinedMessage);
247
+ this.name = 'VError';
248
+ if (error instanceof Error) {
249
+ this.stack = mergeStacks$1(this.stack, error.stack);
250
+ }
251
+ if (error.codeFrame) {
252
+ // @ts-ignore
253
+ this.codeFrame = error.codeFrame;
254
+ }
255
+ if (error.code) {
256
+ // @ts-ignore
257
+ this.code = error.code;
258
+ }
259
+ }
260
+ };
261
+
262
+ const getIframeSrc = (webViews, webViewId, webViewPort, root, isGitpod, locationProtocol, locationHost, locationOrigin) => {
263
+ try {
264
+ const webView = getWebView(webViews, webViewId);
265
+ if (platform === Web) {
266
+ return getIframeSrc$1(webView, locationOrigin);
267
+ }
268
+ return getIframeSrcRemote(webViews, webViewPort, webViewId, locationProtocol, locationHost, isGitpod, root);
269
+ } catch (error) {
270
+ throw new VError$1(error, `Failed to construct webview iframe src`);
271
+ }
272
+ };
273
+
274
+ const SemiColon = ';';
275
+ const Space = ' ';
276
+
277
+ const addSemicolon = line => {
278
+ return line + SemiColon;
279
+ };
280
+
281
+ const getContentSecurityPolicy = items => {
282
+ return items.map(addSemicolon).join(Space);
283
+ };
284
+
285
+ const getWebViewCsp = webView => {
286
+ if (webView && webView.contentSecurityPolicy) {
287
+ return getContentSecurityPolicy(webView.contentSecurityPolicy);
288
+ }
289
+ const csp = getContentSecurityPolicy([`default-src 'none'`, `script-src 'self'`, `style-src 'self'`, `img-src 'self'`, `media-src 'self'`]);
290
+ return csp;
291
+ };
292
+
5
293
  const getWebViewFrameAncestors = (locationProtocol, locationHost) => {
6
294
  const frameAncestors = createUrl(locationProtocol, locationHost);
7
295
  return frameAncestors;
8
296
  };
9
297
 
298
+ const getWebViewOrigin = webViewPort => {
299
+ // TODO don't hardcode protocol
300
+ let origin = '';
301
+ if (platform === Electron) {
302
+ origin = `${WebView}://-/`;
303
+ } else if (platform === Remote) {
304
+ origin = `http://localhost:${webViewPort}`;
305
+ } else {
306
+ origin = '*'; // TODO
307
+ }
308
+ return origin;
309
+ };
310
+
311
+ const AllowScripts = 'allow-scripts';
312
+ const AllowSameOrigin = 'allow-same-origin';
313
+
314
+ const getIframeSandbox = (webView, platform) => {
315
+ const extensionSandbox = webView.sandbox || [];
316
+ if (platform === Remote) {
317
+ return [AllowScripts, AllowSameOrigin, ...extensionSandbox]; // TODO maybe disallow same origin
318
+ }
319
+ if (platform === Web) {
320
+ return [AllowScripts, ...extensionSandbox];
321
+ }
322
+ // TODO set something for electron
323
+ return [...extensionSandbox];
324
+ };
325
+
10
326
  const getOrigin = () => {
11
327
  return location.origin;
12
328
  };
@@ -18,10 +334,15 @@ const getProtocol = () => {
18
334
  };
19
335
 
20
336
  const commandMap = {
21
- 'Location.getProtocol': getProtocol,
22
337
  'Location.getHost': getHost,
23
338
  'Location.getOrigin': getOrigin,
24
- 'WebView.getFrameAncestors': getWebViewFrameAncestors
339
+ 'Location.getProtocol': getProtocol,
340
+ 'WebView.getBaseUrl': getWebViewBaseUrl,
341
+ 'WebView.getFrameAncestors': getWebViewFrameAncestors,
342
+ 'WebView.getIframeSrc': getIframeSrc,
343
+ 'WebView.getOrigin': getWebViewOrigin,
344
+ 'WebView.getSandbox': getIframeSandbox,
345
+ 'WebView.getWebViewCsp': getWebViewCsp
25
346
  };
26
347
 
27
348
  const state = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/iframe-worker",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "",
5
5
  "main": "dist/iframeWorkerMain.js",
6
6
  "type": "module",