Skip to content

Commit 1a473e3

Browse files
SSHaridanilowoz
andauthored
feat: add console hook to static template (#909)
Co-authored-by: Danilo Woznica <danilowoz@gmail.com>
1 parent 349c68d commit 1a473e3

File tree

10 files changed

+208
-7
lines changed

10 files changed

+208
-7
lines changed

sandpack-client/rollup.config.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import pkg from "./package.json";
99

1010
const configs = [
1111
{
12-
input: "src/clients/node/inject-scripts/consoleHook.ts",
12+
input: "src/inject-scripts/consoleHook.ts",
1313
output: {
14-
file: "src/clients/node/inject-scripts/dist/consoleHook.js",
14+
file: "src/inject-scripts/dist/consoleHook.js",
1515
format: "es",
1616
},
1717
plugins: [

sandpack-client/src/clients/node/inject-scripts/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import { INJECT_MESSAGE_TYPE } from "@codesandbox/nodebox";
55

66
// get the bundled file, which contains all dependencies
77
// @ts-ignore
8-
import consoleHook from "./dist/consoleHook.js";
8+
import consoleHook from "../../../inject-scripts/dist/consoleHook.js";
9+
910
import { setupHistoryListeners } from "./historyListener";
1011

1112
const scripts = [

sandpack-client/src/clients/node/taskManager.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,14 @@ type Token =
4040
| { type: TokenType.OR | TokenType.AND | TokenType.PIPE }
4141
| {
4242
type: TokenType.Command | TokenType.Argument | TokenType.String;
43-
value: string;
43+
value?: string;
4444
}
4545
| {
4646
type: TokenType.EnvVar;
4747
value: Record<string, string>;
4848
};
4949

50-
const operators = new Map<string, { type: TokenType }>([
50+
const operators = new Map<string, Token>([
5151
["&&", { type: TokenType.AND }],
5252
["||", { type: TokenType.OR }],
5353
["|", { type: TokenType.PIPE }],

sandpack-client/src/clients/static/index.ts

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable @typescript-eslint/ban-ts-comment */
12
import type { FilesMap } from "@codesandbox/nodebox";
23
import type { FileContent } from "static-browser-server";
34
import { PreviewController } from "static-browser-server";
@@ -8,9 +9,12 @@ import type {
89
SandboxSetup,
910
UnsubscribeFunction,
1011
} from "../..";
12+
// get the bundled file, which contains all dependencies
13+
// @ts-ignore
14+
import consoleHook from "../../inject-scripts/dist/consoleHook.js";
1115
import { SandpackClient } from "../base";
1216
import { EventEmitter } from "../event-emitter";
13-
import { fromBundlerFilesToFS } from "../node/client.utils";
17+
import { fromBundlerFilesToFS, generateRandomId } from "../node/client.utils";
1418
import type { SandpackNodeMessage } from "../node/types";
1519

1620
import { insertHtmlAfterRegex, readBuffer, validateHtml } from "./utils";
@@ -52,6 +56,10 @@ export class SandpackStatic extends SandpackClient {
5256
content,
5357
options.externalResources
5458
);
59+
content = this.injectScriptIntoHead(content, {
60+
script: consoleHook,
61+
scope: { channelId: generateRandomId() },
62+
});
5563
} catch (err) {
5664
console.error("Runtime injection failed", err);
5765
}
@@ -82,6 +90,11 @@ export class SandpackStatic extends SandpackClient {
8290
);
8391
}
8492

93+
this.eventListener = this.eventListener.bind(this);
94+
if (typeof window !== "undefined") {
95+
window.addEventListener("message", this.eventListener);
96+
}
97+
8598
// Dispatch very first compile action
8699
this.updateSandbox();
87100
}
@@ -139,6 +152,25 @@ export class SandpackStatic extends SandpackClient {
139152
return this.injectContentIntoHead(content, tagsToInsert);
140153
}
141154

155+
private injectScriptIntoHead(
156+
content: FileContent,
157+
opts: {
158+
script: string;
159+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
160+
scope?: { channelId: string } & Record<string, any>;
161+
}
162+
): FileContent {
163+
const { script, scope = {} } = opts;
164+
const scriptToInsert = `
165+
<script>
166+
const scope = ${JSON.stringify(scope)};
167+
${script}
168+
</script>
169+
`.trim();
170+
171+
return this.injectContentIntoHead(content, scriptToInsert);
172+
}
173+
142174
public updateSandbox(
143175
setup = this.sandboxSetup,
144176
_isInitializationCompile?: boolean
@@ -172,6 +204,21 @@ export class SandpackStatic extends SandpackClient {
172204
});
173205
}
174206

207+
// Handles message windows coming from iframes
208+
private eventListener(evt: MessageEvent): void {
209+
// skip events originating from different iframes
210+
if (evt.source !== this.iframe.contentWindow) {
211+
return;
212+
}
213+
214+
const message = evt.data;
215+
if (!message.codesandbox) {
216+
return;
217+
}
218+
219+
this.dispatch(message);
220+
}
221+
175222
/**
176223
* Bundler communication
177224
*/
@@ -193,5 +240,8 @@ export class SandpackStatic extends SandpackClient {
193240

194241
public destroy(): void {
195242
this.emitter.cleanup();
243+
if (typeof window !== "undefined") {
244+
window.removeEventListener("message", this.eventListener);
245+
}
196246
}
197247
}

sandpack-react/src/components/Console/Console.stories.tsx

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,3 +235,57 @@ export const MaxMessageCount = () => {
235235
</>
236236
);
237237
};
238+
239+
export const StaticTemplate: React.FC = () => {
240+
return (
241+
<Sandpack
242+
files={{
243+
"index.html": `<!DOCTYPE html>
244+
<html>
245+
246+
<head>
247+
<title>Parcel Sandbox</title>
248+
<meta charset="UTF-8" />
249+
<link rel="stylesheet" href="/styles.css" />
250+
<script>
251+
console.log("fooo")
252+
</script>
253+
</head>
254+
255+
<body>
256+
<h1>Hello world</h1>
257+
<button onclick="console.log(document.querySelectorAll('button'))">Log</button>
258+
<button onclick="console.log(document.querySelectorAll('button'))">Log</button>
259+
</body>
260+
261+
</html>`,
262+
}}
263+
options={{ showConsole: true }}
264+
template="static"
265+
/>
266+
);
267+
};
268+
269+
export const NodeTemplate: React.FC = () => {
270+
return <Sandpack options={{ showConsole: true }} template="node" />;
271+
};
272+
273+
export const ReactTemplate: React.FC = () => {
274+
return (
275+
<Sandpack
276+
options={{ showConsole: true }}
277+
files={{
278+
"App.js": `import { useState } from "react"
279+
export default function App() {
280+
const foo = useState("")
281+
console.log(foo)
282+
return (
283+
<>
284+
</>
285+
)
286+
}`,
287+
}}
288+
template="react"
289+
/>
290+
);
291+
};

sandpack-react/src/components/Console/ConsoleList.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ export const ConsoleList: React.FC<{ data: SandpackConsoleData }> = ({
1212
data,
1313
}) => {
1414
const classNames = useClassNames();
15-
1615
return (
1716
<>
1817
{data.map(({ data, id, method }, logIndex, references) => {

sandpack-react/src/components/Console/utils/constraints.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export const CLEAR_LOG = {
77
};
88

99
export const TRANSFORMED_TYPE_KEY = "@t";
10+
export const TRANSFORMED_TYPE_KEY_ALTERNATE = "#@t";
1011
export const CIRCULAR_REF_KEY = "@r";
1112

1213
export const MAX_LENGTH_STRING = 10000;

sandpack-react/src/components/Console/utils/fromConsoleToString.test.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,73 @@ const cases: Array<[Message, string]> = [
4040
{ constructor: { name: "CustomThing" } },
4141
'CustomThing { constructor: { name: "CustomThing" } }',
4242
],
43+
[
44+
{
45+
"0": {
46+
"#@t": "HTMLElement",
47+
data: {
48+
tagName: "button",
49+
attributes: {},
50+
innerHTML: "Test",
51+
},
52+
},
53+
"1": {
54+
"#@t": "HTMLElement",
55+
data: {
56+
tagName: "button",
57+
attributes: {
58+
onclick: "console.log(document.querySelectorAll('button'))",
59+
},
60+
innerHTML: "Log",
61+
},
62+
},
63+
entries: {
64+
"#@t": "Function",
65+
data: {
66+
name: "entries",
67+
body: "",
68+
proto: "Function",
69+
},
70+
},
71+
keys: {
72+
"#@t": "Function",
73+
data: {
74+
name: "keys",
75+
body: "",
76+
proto: "Function",
77+
},
78+
},
79+
values: {
80+
"#@t": "Function",
81+
data: {
82+
name: "values",
83+
body: "",
84+
proto: "Function",
85+
},
86+
},
87+
forEach: {
88+
"#@t": "Function",
89+
data: {
90+
name: "forEach",
91+
body: "",
92+
proto: "Function",
93+
},
94+
},
95+
length: 2,
96+
item: {
97+
"#@t": "Function",
98+
data: {
99+
name: "item",
100+
body: "",
101+
proto: "Function",
102+
},
103+
},
104+
constructor: {
105+
name: "NodeList",
106+
},
107+
},
108+
`NodeList(2)[<button>Test</button>,<button onclick="console.log(document.querySelectorAll('button'))">Log</button>]`,
109+
],
43110

44111
/**
45112
* Function

sandpack-react/src/components/Console/utils/fromConsoleToString.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import {
99
TRANSFORMED_TYPE_KEY,
10+
TRANSFORMED_TYPE_KEY_ALTERNATE,
1011
MAX_NEST_LEVEL,
1112
MAX_KEYS,
1213
MAX_LENGTH_STRING,
@@ -48,6 +49,25 @@ const formatSymbols = (message: Message): any => {
4849
const transform = transformers[type];
4950

5051
return transform(message.data);
52+
} else if (
53+
typeof message == "object" &&
54+
TRANSFORMED_TYPE_KEY_ALTERNATE in message
55+
) {
56+
const type = message[TRANSFORMED_TYPE_KEY_ALTERNATE] as TransformsTypes;
57+
const transform = transformers[type];
58+
59+
return transform(message.data);
60+
} else if (
61+
typeof message == "object" &&
62+
message.constructor?.name === "NodeList"
63+
) {
64+
const NodeList = {};
65+
Object.entries(message).forEach(([key, value]) => {
66+
// @ts-ignore
67+
NodeList[key] = formatSymbols(value);
68+
});
69+
70+
return NodeList;
5171
}
5272

5373
return message;
@@ -163,6 +183,15 @@ export const fromConsoleToString = (
163183
return fromConsoleToString(newMessage, references, level + 1);
164184
}
165185

186+
if (output.constructor?.name === "NodeList") {
187+
const length = output.length;
188+
const nodes = new Array(length).fill(null).map((_, index) => {
189+
return fromConsoleToString(output[index], references);
190+
});
191+
192+
return `NodeList(${output.length})[${nodes}]`;
193+
}
194+
166195
return objectToString(output, references, level + 1);
167196
}
168197
} catch {

0 commit comments

Comments
 (0)