UNPKG

8.25 kBJavaScriptView Raw
1import { getTemplate } from "codesandbox-import-utils/lib/create-sandbox/templates";
2import isEqual from "lodash.isequal";
3// Muhahaha
4// eslint-disable-next-line
5// @ts-ignore
6import { version } from "../package.json";
7import Protocol from "./file-resolver-protocol";
8import { IFrameProtocol } from "./iframe-protocol";
9import { createPackageJSON, addPackageJSONIfNeeded, extractErrorDetails, } from "./utils";
10const BUNDLER_URL = process.env.CODESANDBOX_ENV === "development"
11 ? "http://localhost:3000/"
12 : `https://${version.replace(/\./g, "-")}-sandpack.codesandbox.io/`;
13export class SandpackClient {
14 constructor(selector, sandboxInfo, options = {}) {
15 this.getTranspilerContext = () => new Promise((resolve) => {
16 const unsubscribe = this.listen((message) => {
17 if (message.type === "transpiler-context") {
18 resolve(message.data);
19 unsubscribe();
20 }
21 });
22 this.dispatch({ type: "get-transpiler-context" });
23 });
24 this.options = options;
25 this.sandboxInfo = sandboxInfo;
26 this.bundlerURL = options.bundlerURL || BUNDLER_URL;
27 this.bundlerState = undefined;
28 this.errors = [];
29 this.status = "initializing";
30 if (typeof selector === "string") {
31 this.selector = selector;
32 const element = document.querySelector(selector);
33 if (!element) {
34 throw new Error(`No element found for selector '${selector}'`);
35 }
36 this.element = element;
37 this.iframe = document.createElement("iframe");
38 this.initializeElement();
39 }
40 else {
41 this.element = selector;
42 this.iframe = selector;
43 }
44 if (!this.iframe.getAttribute("sandbox")) {
45 this.iframe.setAttribute("sandbox", "allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts");
46 }
47 if (!this.iframe.getAttribute("allow")) {
48 this.iframe.setAttribute("allow", "accelerometer; ambient-light-sensor; autoplay; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking");
49 }
50 this.iframe.src = options.startRoute
51 ? new URL(options.startRoute, this.bundlerURL).toString()
52 : this.bundlerURL;
53 this.iframeProtocol = new IFrameProtocol(this.iframe, this.bundlerURL);
54 this.unsubscribeGlobalListener = this.iframeProtocol.globalListen((mes) => {
55 if (mes.type !== "initialized" || !this.iframe.contentWindow) {
56 return;
57 }
58 this.iframeProtocol.register();
59 if (this.options.fileResolver) {
60 // TODO: Find a common place for the Protocol to be implemented for both sandpack-core and sandpack-client
61 this.fileResolverProtocol = new Protocol("file-resolver", async (data) => {
62 if (data.m === "isFile") {
63 return this.options.fileResolver.isFile(data.p);
64 }
65 return this.options.fileResolver.readFile(data.p);
66 }, this.iframe.contentWindow);
67 }
68 this.updatePreview(this.sandboxInfo, true);
69 });
70 this.unsubscribeChannelListener = this.iframeProtocol.channelListen((mes) => {
71 switch (mes.type) {
72 case "start": {
73 this.errors = [];
74 break;
75 }
76 case "status": {
77 this.status = mes.status;
78 break;
79 }
80 case "action": {
81 if (mes.action === "show-error") {
82 this.errors = [...this.errors, extractErrorDetails(mes)];
83 }
84 break;
85 }
86 case "state": {
87 this.bundlerState = mes.state;
88 break;
89 }
90 }
91 });
92 }
93 cleanup() {
94 this.unsubscribeChannelListener();
95 this.unsubscribeGlobalListener();
96 this.iframeProtocol.cleanup();
97 }
98 updateOptions(options) {
99 if (!isEqual(this.options, options)) {
100 this.options = options;
101 this.updatePreview();
102 }
103 }
104 updatePreview(sandboxInfo = this.sandboxInfo, isInitializationCompile) {
105 var _a, _b, _c;
106 this.sandboxInfo = sandboxInfo;
107 const files = this.getFiles();
108 const modules = Object.keys(files).reduce((prev, next) => ({
109 ...prev,
110 [next]: {
111 code: files[next].code,
112 path: next,
113 },
114 }), {});
115 let packageJSON = JSON.parse(createPackageJSON(this.sandboxInfo.dependencies, this.sandboxInfo.entry));
116 try {
117 packageJSON = JSON.parse(files["/package.json"].code);
118 }
119 catch (e) {
120 console.error("Could not parse package.json file: " + e.message);
121 }
122 // TODO move this to a common format
123 const normalizedModules = Object.keys(files).reduce((prev, next) => ({
124 ...prev,
125 [next]: {
126 content: files[next].code,
127 path: next,
128 },
129 }), {});
130 this.dispatch({
131 type: "compile",
132 codesandbox: true,
133 version: 3,
134 isInitializationCompile,
135 modules,
136 externalResources: [],
137 hasFileResolver: Boolean(this.options.fileResolver),
138 disableDependencyPreprocessing: this.sandboxInfo
139 .disableDependencyPreprocessing,
140 template: this.sandboxInfo.template ||
141 getTemplate(packageJSON, normalizedModules),
142 showOpenInCodeSandbox: (_a = this.options.showOpenInCodeSandbox) !== null && _a !== void 0 ? _a : true,
143 showErrorScreen: (_b = this.options.showErrorScreen) !== null && _b !== void 0 ? _b : true,
144 showLoadingScreen: (_c = this.options.showLoadingScreen) !== null && _c !== void 0 ? _c : true,
145 skipEval: this.options.skipEval || false,
146 clearConsoleDisabled: !this.options.clearConsoleOnFirstCompile,
147 });
148 }
149 dispatch(message) {
150 this.iframeProtocol.dispatch(message);
151 }
152 listen(listener) {
153 return this.iframeProtocol.channelListen(listener);
154 }
155 /**
156 * Get the URL of the contents of the current sandbox
157 */
158 getCodeSandboxURL() {
159 const files = this.getFiles();
160 const paramFiles = Object.keys(files).reduce((prev, next) => ({
161 ...prev,
162 [next.replace("/", "")]: {
163 content: files[next].code,
164 isBinary: false,
165 },
166 }), {});
167 return fetch("https://codesandbox.io/api/v1/sandboxes/define?json=1", {
168 method: "POST",
169 body: JSON.stringify({ files: paramFiles }),
170 headers: {
171 Accept: "application/json",
172 "Content-Type": "application/json",
173 },
174 })
175 .then((x) => x.json())
176 .then((res) => ({
177 sandboxId: res.sandbox_id,
178 editorUrl: `https://codesandbox.io/s/${res.sandbox_id}`,
179 embedUrl: `https://codesandbox.io/embed/${res.sandbox_id}`,
180 }));
181 }
182 getFiles() {
183 const { sandboxInfo } = this;
184 if (sandboxInfo.files["/package.json"] === undefined) {
185 return addPackageJSONIfNeeded(sandboxInfo.files, sandboxInfo.dependencies, sandboxInfo.entry);
186 }
187 return this.sandboxInfo.files;
188 }
189 initializeElement() {
190 this.iframe.style.border = "0";
191 this.iframe.style.width = this.options.width || "100%";
192 this.iframe.style.height = this.options.height || "100%";
193 this.iframe.style.overflow = "hidden";
194 if (!this.element.parentNode) {
195 // This should never happen
196 throw new Error("Given element does not have a parent.");
197 }
198 this.element.parentNode.replaceChild(this.iframe, this.element);
199 }
200}
201//# sourceMappingURL=client.js.map
\No newline at end of file