@vizhub/runtime 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +173 -0
  2. package/package.json +37 -0
  3. package/src/computeSrcDoc.ts +68 -0
  4. package/src/index.ts +7 -0
  5. package/src/useRuntime.ts +394 -0
  6. package/src/v2Runtime/bundle/bubleJSXOnly.ts +34 -0
  7. package/src/v2Runtime/bundle/getLibraries.js +31 -0
  8. package/src/v2Runtime/bundle/hypothetical.js +232 -0
  9. package/src/v2Runtime/bundle/index.js +88 -0
  10. package/src/v2Runtime/bundle/packageJson.ts +49 -0
  11. package/src/v2Runtime/bundle/rollup.browser.js +28414 -0
  12. package/src/v2Runtime/bundle.test.js +151 -0
  13. package/src/v2Runtime/computeSrcDocV2.test.ts +163 -0
  14. package/src/v2Runtime/computeSrcDocV2.ts +34 -0
  15. package/src/v2Runtime/getComputedIndexHtml.test.ts +33 -0
  16. package/src/v2Runtime/getComputedIndexHtml.ts +106 -0
  17. package/src/v2Runtime/getText.ts +19 -0
  18. package/src/v2Runtime/magicSandbox.js +291 -0
  19. package/src/v2Runtime/packageJson.js +42 -0
  20. package/src/v2Runtime/transformFiles.test.js +18 -0
  21. package/src/v2Runtime/transformFiles.ts +15 -0
  22. package/src/v2Runtime/v3FilesToV2Files.test.ts +20 -0
  23. package/src/v2Runtime/v3FilesToV2Files.ts +14 -0
  24. package/src/v3Runtime/build.test.ts +474 -0
  25. package/src/v3Runtime/build.ts +270 -0
  26. package/src/v3Runtime/cleanRollupErrorMessage.ts +15 -0
  27. package/src/v3Runtime/computeSrcDocV3.ts +151 -0
  28. package/src/v3Runtime/extractVizImport.test.ts +41 -0
  29. package/src/v3Runtime/extractVizImport.ts +34 -0
  30. package/src/v3Runtime/generateRollupErrorMessage.ts +84 -0
  31. package/src/v3Runtime/importFromViz.ts +36 -0
  32. package/src/v3Runtime/index.ts +1 -0
  33. package/src/v3Runtime/parseId.ts +14 -0
  34. package/src/v3Runtime/setupV3Runtime.ts +478 -0
  35. package/src/v3Runtime/transformDSV/d3-dsv-custom-build/bundle-modified-src.js +121 -0
  36. package/src/v3Runtime/transformDSV/d3-dsv-custom-build/bundle-modified.js +121 -0
  37. package/src/v3Runtime/transformDSV/d3-dsv-custom-build/bundle.js +239 -0
  38. package/src/v3Runtime/transformDSV/d3-dsv-custom-build/index.js +1 -0
  39. package/src/v3Runtime/transformDSV/d3-dsv-custom-build/package-lock.json +475 -0
  40. package/src/v3Runtime/transformDSV/d3-dsv-custom-build/package.json +19 -0
  41. package/src/v3Runtime/transformDSV/d3-dsv-custom-build/rollup.config.js +9 -0
  42. package/src/v3Runtime/transformDSV/index.ts +71 -0
  43. package/src/v3Runtime/transformSvelte.ts +111 -0
  44. package/src/v3Runtime/types.ts +158 -0
  45. package/src/v3Runtime/urlLoad.ts +33 -0
  46. package/src/v3Runtime/virtual.ts +27 -0
  47. package/src/v3Runtime/vizCache.test.ts +126 -0
  48. package/src/v3Runtime/vizCache.ts +60 -0
  49. package/src/v3Runtime/vizLoad.ts +68 -0
  50. package/src/v3Runtime/vizLoadSvelte.ts +46 -0
  51. package/src/v3Runtime/vizResolve.ts +100 -0
  52. package/src/v3Runtime/worker.ts +231 -0
@@ -0,0 +1,151 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { bundle } from './bundle';
3
+
4
+ // From https://github.com/vizhub-core/vizhub/blob/main/vizhub-v2/packages/presenters/test/test.js
5
+
6
+ // Convenience for testing.
7
+ const removeSourceMap = (files) => {
8
+ files[0].text = files[0].text.split(
9
+ '\n//# sourceMappingURL',
10
+ )[0];
11
+ return files;
12
+ };
13
+
14
+ describe('v2 bundle', () => {
15
+ it('should bundle files using Rollup', async () => {
16
+ const files = [
17
+ {
18
+ name: 'index.js',
19
+ text: 'import { foo } from "./foo.js"; console.log(foo);',
20
+ },
21
+ { name: 'foo.js', text: 'export const foo = "bar";' },
22
+ ];
23
+ expect(removeSourceMap(await bundle(files))).toEqual([
24
+ {
25
+ name: 'bundle.js',
26
+ text: '(function () {\n\t\'use strict\';\n\n\tconst foo = "bar";\n\n\tconsole.log(foo);\n\n}());\n',
27
+ },
28
+ ]);
29
+ });
30
+
31
+ it('should refer to global d3 in bundle for d3 package', async () => {
32
+ const files = [
33
+ {
34
+ name: 'index.js',
35
+ text: 'import { select } from "d3"; console.log(select);',
36
+ },
37
+ ];
38
+ expect(removeSourceMap(await bundle(files))).toEqual([
39
+ {
40
+ name: 'bundle.js',
41
+ text: "(function (d3) {\n\t'use strict';\n\n\tconsole.log(d3.select);\n\n}(d3));\n",
42
+ },
43
+ ]);
44
+ });
45
+
46
+ it('should refer to global React in bundle for React package', async () => {
47
+ const files = [
48
+ {
49
+ name: 'index.js',
50
+ text: 'import React from "react"; console.log(React);',
51
+ },
52
+ ];
53
+ expect(removeSourceMap(await bundle(files))).toEqual([
54
+ {
55
+ name: 'bundle.js',
56
+ text: "(function (React) {\n\t'use strict';\n\n\tReact = React && Object.prototype.hasOwnProperty.call(React, 'default') ? React['default'] : React;\n\n\tconsole.log(React);\n\n}(React));\n",
57
+ },
58
+ ]);
59
+ });
60
+
61
+ it('should refer to global ReactDOM in bundle for ReactDOM package', async () => {
62
+ const files = [
63
+ {
64
+ name: 'index.js',
65
+ text: 'import ReactDOM from "react-dom"; console.log(ReactDOM);',
66
+ },
67
+ ];
68
+ expect(removeSourceMap(await bundle(files))).toEqual([
69
+ {
70
+ name: 'bundle.js',
71
+ text: "(function (ReactDOM) {\n\t'use strict';\n\n\tReactDOM = ReactDOM && Object.prototype.hasOwnProperty.call(ReactDOM, 'default') ? ReactDOM['default'] : ReactDOM;\n\n\tconsole.log(ReactDOM);\n\n}(ReactDOM));\n",
72
+ },
73
+ ]);
74
+ });
75
+
76
+ it('should transpile JSX', async () => {
77
+ const files = [
78
+ { name: 'index.js', text: '<div>Hello JSX!</div>' },
79
+ ];
80
+ expect(removeSourceMap(await bundle(files))).toEqual([
81
+ {
82
+ name: 'bundle.js',
83
+ text: "(function () {\n\t'use strict';\n\n\tReact.createElement( 'div', null, \"Hello JSX!\" );\n\n}());\n",
84
+ },
85
+ ]);
86
+ });
87
+
88
+ it('should not transpile ES6', async () => {
89
+ const files = [
90
+ {
91
+ name: 'index.js',
92
+ text: 'const fn = a => a * a; console.log(fn(4));',
93
+ },
94
+ ];
95
+ expect(removeSourceMap(await bundle(files))).toEqual([
96
+ {
97
+ name: 'bundle.js',
98
+ text: "(function () {\n\t'use strict';\n\n\tconst fn = a => a * a; console.log(fn(4));\n\n}());\n",
99
+ },
100
+ ]);
101
+ });
102
+
103
+ it('should allow generators', async () => {
104
+ const files = [
105
+ {
106
+ name: 'index.js',
107
+ text: 'console.log(function* () { yield 5; }().next().value)',
108
+ },
109
+ ];
110
+ expect(removeSourceMap(await bundle(files))).toEqual([
111
+ {
112
+ name: 'bundle.js',
113
+ text: "(function () {\n\t'use strict';\n\n\tconsole.log(function* () { yield 5; }().next().value);\n\n}());\n",
114
+ },
115
+ ]);
116
+ });
117
+
118
+ it('should allow characters outside of the Latin1 range', async () => {
119
+ const files = [
120
+ { name: 'index.js', text: 'console.log("Привет")' },
121
+ ];
122
+ expect(removeSourceMap(await bundle(files))).toEqual([
123
+ {
124
+ name: 'bundle.js',
125
+ text: '(function () {\n\t\'use strict\';\n\n\tconsole.log("Привет");\n\n}());\n',
126
+ },
127
+ ]);
128
+ });
129
+ });
130
+ // TODO Svelt Support
131
+ // See https://github.com/vizhub-core/vizhub3/issues/185
132
+ // //describe('Svelte', () => {
133
+ // // it('should bundle files using Rollup', async () => {
134
+ // // const files = [
135
+ // // {
136
+ // // name: 'index.js',
137
+ // // text:
138
+ // // "import App from './App.svelte'; const app = new App({ target: document.body, }); export default app;",
139
+ // // },
140
+ // // { name: 'App.svelte', text: '<script>console.log(foo);</script>' },
141
+ // // ];
142
+ // // assert.deepEqual(removeSourceMap(await bundle(files)), [
143
+ // // {
144
+ // // name: 'bundle.js',
145
+ // // text:
146
+ // // 'var bundle = (function () {\n\t\'use strict\';\n\n\tconst globals={};const noop={};const dispatch_dev={};const validate_slots={};const SvelteComponentDev={};const init={};const safe_not_equal={};\n\n\t/* code.svelte generated by Svelte v3.31.0 */\n\n\tconst { console: console_1 } = globals;\n\n\tfunction create_fragment(ctx) {\n\t\tconst block = {\n\t\t\tc: noop,\n\t\t\tl: function claim(nodes) {\n\t\t\t\tthrow new Error("options.hydrate only works if the component was compiled with the `hydratable: true` option");\n\t\t\t},\n\t\t\tm: noop,\n\t\t\tp: noop,\n\t\t\ti: noop,\n\t\t\to: noop,\n\t\t\td: noop\n\t\t};\n\n\t\tdispatch_dev("SvelteRegisterBlock", {\n\t\t\tblock,\n\t\t\tid: create_fragment.name,\n\t\t\ttype: "component",\n\t\t\tsource: "",\n\t\t\tctx\n\t\t});\n\n\t\treturn block;\n\t}\n\n\tfunction instance($$self, $$props) {\n\t\tlet { $$slots: slots = {}, $$scope } = $$props;\n\t\tvalidate_slots("Code", slots, []);\n\t\tconsole.log(foo);\n\t\tconst writable_props = [];\n\n\t\tObject.keys($$props).forEach(key => {\n\t\t\tif (!~writable_props.indexOf(key) && key.slice(0, 2) !== "$$") console_1.warn(`<Code> was created with unknown prop \'${key}\'`);\n\t\t});\n\n\t\treturn [];\n\t}\n\n\tclass Code extends SvelteComponentDev {\n\t\tconstructor(options) {\n\t\t\tsuper(options);\n\t\t\tinit(this, options, instance, create_fragment, safe_not_equal, {});\n\n\t\t\tdispatch_dev("SvelteRegisterComponent", {\n\t\t\t\tcomponent: this,\n\t\t\t\ttagName: "Code",\n\t\t\t\toptions,\n\t\t\t\tid: create_fragment.name\n\t\t\t});\n\t\t}\n\t}\n\n\tconst app = new Code({ target: document.body, });\n\n\treturn app;\n\n}());\n',
147
+ // // },
148
+ // // ]);
149
+ // // });
150
+ // //});
151
+ // });
@@ -0,0 +1,163 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { primordialViz } from 'entities/test/fixtures';
3
+ import { computeSrcDocV2 } from './computeSrcDocV2';
4
+ import { setJSDOM } from './getComputedIndexHtml';
5
+ import { JSDOM } from 'jsdom';
6
+
7
+ setJSDOM(JSDOM);
8
+
9
+ describe('v2 computeSrcDocV2', () => {
10
+ it('TODO should compute correct srcdoc', async () => {
11
+ expect(true).toEqual(true);
12
+ // console.log('`' + computeSrcDocV2(primordialViz.content) + '`');
13
+ expect(await computeSrcDocV2(primordialViz.content))
14
+ .toEqual(`<script>(function() {
15
+ var XHR = window.XMLHttpRequest;
16
+ window.XMLHttpRequest = function() {
17
+ this.xhr = new XHR();
18
+ return this;
19
+ }
20
+ window.XMLHttpRequest.prototype.open = function(method, url, async, user, password) {
21
+ if(__fileNames.indexOf(url) >= 0) {
22
+ this.file = url;
23
+ this.responseText = __files[url];
24
+ if(url.indexOf(".xml") === url.length - 4) {
25
+ try {
26
+ var oParser = new DOMParser();
27
+ var oDOM = oParser.parseFromString(this.responseText, "text/xml");
28
+ this.responseXML = oDOM;
29
+ } catch(e) {}
30
+ }
31
+ // we indicate that the request is done
32
+ this.readyState = 4;
33
+ this.status = 200;
34
+ } else {
35
+ // pass thru to the normal xhr
36
+ this.xhr.open(method, url, async, user, password);
37
+ }
38
+ };
39
+ window.XMLHttpRequest.prototype.setRequestHeader = function(header, value) {
40
+ if(this.file) return;
41
+ return this.xhr.setRequestHeader(header, value);
42
+ }
43
+ window.XMLHttpRequest.prototype.abort = function() {
44
+ return this.xhr.abort()
45
+ }
46
+ window.XMLHttpRequest.prototype.getAllResponseHeaders = function() {
47
+ return this.xhr.getAllResponseHeaders();
48
+ }
49
+ window.XMLHttpRequest.prototype.getResponseHeader = function(header) {
50
+ return this.xhr.getResponseHeader(header);
51
+ }
52
+ window.XMLHttpRequest.prototype.overrideMimeType = function(mime) {
53
+ return this.xhr.overrideMimeType(mime);
54
+ }
55
+ window.XMLHttpRequest.prototype.send = function(data) {
56
+ //we need to remap the fake XHR to the real one inside the onload/onreadystatechange functions
57
+ var that = this;
58
+ // unfortunately we need to do our copying of handlers in the next tick as
59
+ // it seems with normal XHR you can add them after firing off send... which seems
60
+ // unwise to do in the first place, but this is needed to support jQuery...
61
+ setTimeout(function() {
62
+ // we wire up all the listeners to the real XHR
63
+ that.xhr.onerror = this.onerror;
64
+ that.xhr.onprogress = this.onprogress;
65
+ if(that.responseType || that.responseType === '')
66
+ that.xhr.responseType = that.responseType
67
+ // if the onload callback is used we need to copy over
68
+ // the real response data to the fake object
69
+ if(that.onload) {
70
+ var onload = that.onload;
71
+ that.xhr.onload = that.onload = function() {
72
+ try{
73
+ that.response = this.response;
74
+ that.readyState = this.readyState;
75
+ that.status = this.status;
76
+ that.statusText = this.statusText;
77
+ } catch(e) { console.log("onload", e) }
78
+ try {
79
+ if(that.responseType == '') {
80
+ that.responseXML = this.responseXML;
81
+ that.responseText = this.responseText;
82
+ }
83
+ if(that.responseType == 'text') {
84
+ that.responseText = this.responseText;
85
+ }
86
+ } catch(e) { console.log("onload responseText/XML", e) }
87
+ onload();
88
+ }
89
+ }
90
+ // if the readystate change callback is used we need
91
+ // to copy over the real response data to our fake xhr instance
92
+ if(that.onreadystatechange) {
93
+ var ready = that.onreadystatechange;
94
+ that.xhr.onreadystatechange = function() {
95
+ try{
96
+ that.readyState = this.readyState;
97
+ that.responseText = this.responseText;
98
+ that.responseXML = this.responseXML;
99
+ that.responseType = this.responseType;
100
+ that.status = this.status;
101
+ that.statusText = this.statusText;
102
+ } catch(e){
103
+ console.log("e", e)
104
+ }
105
+ ready();
106
+ }
107
+ }
108
+ // if this request is for a local file, we short-circuit and just
109
+ // end the request, since all the data should be on our fake request object
110
+ if(that.file) {
111
+ if(that.onreadystatechange)
112
+ return that.onreadystatechange();
113
+ if(that.onload)
114
+ return that.onload(); //untested
115
+ }
116
+ // if this is a real request, we pass through the send call
117
+ that.xhr.send(data)
118
+ }, 0)
119
+ }
120
+
121
+ var originalFetch = window.fetch;
122
+ window.fetch = function(input, init) {
123
+
124
+ var url = input;
125
+ if (input instanceof Request) {
126
+ url = input.url
127
+ }
128
+
129
+ // This is a hack that seems to fix a problem with the way Mapbox is requesting its TileJSON
130
+ // Not sure what blob:// protocol is anyway...
131
+ url = url.replace('blob://', 'http://')
132
+
133
+ if(__fileNames.indexOf(url) >= 0) {
134
+
135
+ var responseText = __files[url];
136
+ return Promise.resolve({
137
+ ok: true,
138
+ status: 200,
139
+ statusText: 'ok',
140
+ url: url,
141
+ text: function(){ return Promise.resolve(responseText) },
142
+ json: function(){ return Promise.resolve(responseText).then(JSON.parse) },
143
+ blob: function(){ return Promise.resolve(new Blob([responseText])) },
144
+ // Inspired by https://developers.google.com/web/updates/2012/06/How-to-convert-ArrayBuffer-to-and-from-String
145
+ arrayBuffer: function() {
146
+ var buffer = new ArrayBuffer(responseText.length * 2);
147
+ var bufferView = new Uint16Array(buffer);
148
+ for (var i = 0, length = responseText.length; i < length; i++) {
149
+ bufferView[i] = responseText.charCodeAt(i);
150
+ }
151
+ return Promise.resolve(buffer);
152
+ }
153
+ })
154
+ }
155
+
156
+ return originalFetch(input, init)
157
+ }
158
+
159
+ })()</script><meta charset="utf-8"><script>var __filesURI = "%7B%22README.md%22%3A%22Test%20%5BMarkdown%5D(https%3A%2F%2Fwww.markdownguide.org%2F).%5Cn%23%20Introduction%5Cn%5CnThis%20is%20a%20test.%22%7D";
160
+ var __files = JSON.parse(decodeURIComponent(__filesURI));
161
+ var __fileNames = ["README.md"];</script><body style="font-size:26em">Hello</body>`);
162
+ });
163
+ });
@@ -0,0 +1,34 @@
1
+ import { Content, FilesV2, getFileText } from 'entities';
2
+ import magicSandbox from './magicSandbox';
3
+ import { getComputedIndexHtml } from './getComputedIndexHtml';
4
+ import { transformFiles } from './transformFiles';
5
+ import { bundle } from './bundle';
6
+ import { v3FilesToV2Files } from './v3FilesToV2Files';
7
+
8
+ // Inspired by https://github.com/vizhub-core/vizhub/blob/main/vizhub-v2/packages/presenters/src/computeSrcDoc.js
9
+
10
+ export const computeSrcDocV2 = async (content: Content) => {
11
+ // Migrate V3 files to V2 files.
12
+ let filesV2: FilesV2 = v3FilesToV2Files(content.files);
13
+
14
+ // Since we do not include `bundle.js` in the migrated content,
15
+ // we need to add it back in, computed from the files on the fly.
16
+ // ... but only if there is an `index.js` file.
17
+ const indexJS = getFileText(content, 'index.js');
18
+ if (indexJS) {
19
+ const filesV2BundleJSOnly = await bundle(filesV2);
20
+ // Bundle the files using Rollup.
21
+ // This will add a `bundle.js` file to the files.
22
+ // Includes ES module bundling and JSX support.
23
+ filesV2 = [...filesV2, ...filesV2BundleJSOnly];
24
+ }
25
+
26
+ // Compute the index.html file from the files.
27
+ const template = getComputedIndexHtml(filesV2);
28
+
29
+ // Transform files to match what MagicSandbox expects.
30
+ const files = transformFiles(filesV2);
31
+
32
+ // Use MagicSandbox to inject the bundle.js script tag.
33
+ return magicSandbox(template, files);
34
+ };
@@ -0,0 +1,33 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import {
3
+ getComputedIndexHtml,
4
+ setJSDOM,
5
+ } from './getComputedIndexHtml';
6
+ import { JSDOM } from 'jsdom';
7
+
8
+ setJSDOM(JSDOM);
9
+
10
+ describe('v2 getComputedIndexHtml', () => {
11
+ it('should return empty string if missing index.html', async () => {
12
+ const files = [];
13
+ expect(getComputedIndexHtml(files)).toEqual(``);
14
+ });
15
+
16
+ it('should preserve existing index.html if no bundle and no package.json', async () => {
17
+ const text =
18
+ '<html><body><h1>Hello World</h1></body></html>';
19
+ const files = [{ name: 'index.html', text }];
20
+ expect(getComputedIndexHtml(files)).toEqual(text);
21
+ });
22
+
23
+ it('should add bundle.js, no package.json', async () => {
24
+ const text =
25
+ '<html><body><script src="bundle.js"></script></body></html>';
26
+ const files = [
27
+ { name: 'index.html', text },
28
+ { name: 'index.js', text: 'console.log("Hello")' },
29
+ ];
30
+ // console.log(JSON.stringify(getComputedIndexHtml(files), null, 2));
31
+ expect(getComputedIndexHtml(files)).toEqual(text);
32
+ });
33
+ });
@@ -0,0 +1,106 @@
1
+ // From https://github.com/vizhub-core/vizhub/blob/main/vizhub-v2/packages/presenters/src/getComputedIndexHtml.js
2
+
3
+ import {
4
+ dependencies,
5
+ getConfiguredLibraries,
6
+ dependencySource,
7
+ } from './packageJson';
8
+ import { FilesV2 } from 'entities';
9
+ import { getText } from './getText';
10
+
11
+ // const isPackageJSONEnabled = true;
12
+
13
+ let parser;
14
+
15
+ // If we're in the browser, use native DOMParser.
16
+ if (typeof window !== 'undefined') {
17
+ parser = new DOMParser();
18
+ }
19
+
20
+ // Expose a way to inject a DOMParser implementation
21
+ // when we're in a Node environment (tests, API server).
22
+ export const setJSDOM = (JSDOM) => {
23
+ parser = new new JSDOM().window.DOMParser();
24
+ };
25
+
26
+ const injectBundleScript = (htmlTemplate, files) => {
27
+ const doc = parser.parseFromString(
28
+ htmlTemplate,
29
+ 'text/html',
30
+ );
31
+ // console.log('doc', doc);
32
+ if (
33
+ getText(files, 'bundle.js') &&
34
+ !doc.querySelector('[src="bundle.js"]')
35
+ ) {
36
+ const bundleScriptTag = doc.createElement('script');
37
+ // This will be fed through MagicSandbox.
38
+ bundleScriptTag.src = 'bundle.js';
39
+ doc.body.appendChild(bundleScriptTag);
40
+ return `<!DOCTYPE html>${doc.documentElement.outerHTML}`;
41
+ } else {
42
+ return htmlTemplate;
43
+ }
44
+ };
45
+
46
+ const injectDependenciesScript = (htmlTemplate, files) => {
47
+ const deps = Object.entries(dependencies(files) || {});
48
+
49
+ if (deps.length === 0) return htmlTemplate;
50
+
51
+ if (!parser)
52
+ throw new Error(
53
+ 'DOM parser is not defined. Did you forget to call setJSDOM()?',
54
+ );
55
+
56
+ const doc = parser.parseFromString(
57
+ htmlTemplate,
58
+ 'text/html',
59
+ );
60
+ const libraries = getConfiguredLibraries(files);
61
+
62
+ deps
63
+ .map(([name, version]) =>
64
+ dependencySource({ name, version }, libraries),
65
+ )
66
+ .forEach((url) => {
67
+ const scriptTag = doc.createElement('script');
68
+ scriptTag.src = url;
69
+
70
+ doc.head.appendChild(scriptTag);
71
+ });
72
+
73
+ return `<!DOCTYPE html>${doc.documentElement.outerHTML}`;
74
+ };
75
+
76
+ // Compute the index.html file from the files.
77
+ // Includes:
78
+ // - bundle.js script tag
79
+ // - dependencies script tag(s)
80
+ export const getComputedIndexHtml = (files: FilesV2) => {
81
+ try {
82
+ // Isolate the index.html file.
83
+ const htmlTemplate = getText(files, 'index.html');
84
+
85
+ // If there is no index.html file, return an empty string.
86
+ if (!htmlTemplate) {
87
+ return '';
88
+ }
89
+
90
+ // Inject bundle.js script tag if needed.
91
+ const htmlWithBundleScriptTemplate = injectBundleScript(
92
+ htmlTemplate,
93
+ files,
94
+ );
95
+
96
+ // Inject dependencies script tag(s) if needed, based on package.json.
97
+ const indexHtml = injectDependenciesScript(
98
+ htmlWithBundleScriptTemplate,
99
+ files,
100
+ );
101
+
102
+ return indexHtml;
103
+ } catch (err) {
104
+ console.log(err);
105
+ }
106
+ };
@@ -0,0 +1,19 @@
1
+ import { FilesV2 } from 'entities';
2
+
3
+ // From https://github.com/vizhub-core/vizhub/blob/main/vizhub-v2/packages/presenters/src/accessors.js#L23
4
+ const getFileIndex = (files: FilesV2, name: string) => {
5
+ for (let i = 0; i < files.length; i++) {
6
+ if (files[i].name === name) {
7
+ return i;
8
+ }
9
+ }
10
+ return -1;
11
+ };
12
+
13
+ const getFile = (files: FilesV2, name: string) =>
14
+ files[getFileIndex(files, name)];
15
+
16
+ export const getText = (files: FilesV2, name: string) => {
17
+ const file = getFile(files, name);
18
+ return file ? file.text || '' : '';
19
+ };