Skip to content

Commit

Permalink
fix(Static Template): Ensure valid HTML formatting for static template (
Browse files Browse the repository at this point in the history
#894)

Co-authored-by: Danilo Woznica <danilowoz@gmail.com>
  • Loading branch information
joshwcomeau and danilowoz committed Apr 10, 2023
1 parent fd40625 commit af68579
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 21 deletions.
22 changes: 2 additions & 20 deletions sandpack-client/src/clients/static/index.ts
Expand Up @@ -13,7 +13,7 @@ import { EventEmitter } from "../event-emitter";
import { fromBundlerFilesToFS } from "../node/client.utils";
import type { SandpackNodeMessage } from "../node/types";

import { insertHtmlAfterRegex, readBuffer } from "./utils";
import { insertHtmlAfterRegex, readBuffer, validateHtml } from "./utils";

export class SandpackStatic extends SandpackClient {
private emitter: EventEmitter;
Expand Down Expand Up @@ -44,7 +44,7 @@ export class SandpackStatic extends SandpackClient {
}
if (filepath.endsWith(".html") || filepath.endsWith(".htm")) {
try {
content = this.injectDocType(content);
content = validateHtml(content);
content = this.injectProtocolScript(content);
content = this.injectExternalResources(
content,
Expand Down Expand Up @@ -81,19 +81,6 @@ export class SandpackStatic extends SandpackClient {
}
}

private injectDocType(content: FileContent): FileContent {
// Make it a string
let contentString = readBuffer(content);

// Add the DOCTYPE tag
const docTypeRegex = /<!DOCTYPE.*>/gi;
if (!docTypeRegex.test(contentString)) {
contentString = `<!DOCTYPE html>\n${contentString}`;
}

return contentString;
}

private injectContentIntoHead(
content: FileContent,
contentToInsert: string
Expand All @@ -104,11 +91,6 @@ export class SandpackStatic extends SandpackClient {
// Inject script
content =
insertHtmlAfterRegex(/<head[^<>]*>/g, content, "\n" + contentToInsert) ??
insertHtmlAfterRegex(
/<html[^<>]*>/g,
content,
"<head>\n" + contentToInsert + "</head>\n"
) ??
contentToInsert + "\n" + content;

return content;
Expand Down
68 changes: 68 additions & 0 deletions sandpack-client/src/clients/static/utils.test.ts
@@ -0,0 +1,68 @@
/**
* @jest-environment jsdom
*/

import { validateHtml } from "./utils";

describe(validateHtml, () => {
it("returns a valid HTML string", () => {
const content =
"<html><head><title>Test</title></head><body><p>Hello world!</p></body></html>";
const validatedContent = validateHtml(content);

expect(validatedContent).toContain("<!DOCTYPE html>");
expect(validatedContent).toContain(
'<html lang="en"><head><title>Test</title></head><body><p>Hello world!</p></body></html>'
);
});

it("adds html/head/body tags to an empty string", () => {
const content = "";
const validatedContent = validateHtml(content);

expect(validatedContent).toContain("<!DOCTYPE html>");
expect(validatedContent).toContain(
'<html lang="en"><head></head><body></body></html>'
);
});

it("shouldn't change anything if a valid document is provided ", () => {
const content = `<!DOCTYPE html>
<html lang="en"><head></head>
<body>
<h1>Hello World</h1>
</body></html>`;

expect(validateHtml(content)).toBe(content);
});

it("should add the lang attribute to the html if not provided", () => {
const content = `<!DOCTYPE html>
<html>
<head></head>
<body>
<h1>Hello World</h1>
</body>
</html>`;

const validatedContent = validateHtml(content);

expect(validatedContent).toContain("<!DOCTYPE html>");
expect(validatedContent).toContain('<html lang="en">');
});

it("shouldn't change the land attr if a custom value is provided", () => {
const content = `<!DOCTYPE html>
<html lang="pt-BR">
<head></head>
<body>
<h1>Hello World</h1>
</body>
</html>`;

const validatedContent = validateHtml(content);

expect(validatedContent).toContain("<!DOCTYPE html>");
expect(validatedContent).toContain('<html lang="pt-BR">');
});
});
18 changes: 18 additions & 0 deletions sandpack-client/src/clients/static/utils.ts
@@ -1,3 +1,5 @@
import type { FileContent } from "static-browser-server";

export const insertHtmlAfterRegex = (
regex: RegExp,
content: string,
Expand Down Expand Up @@ -27,3 +29,19 @@ export const readBuffer = (content: string | Uint8Array): string => {
return new TextDecoder().decode(content);
}
};

export const validateHtml = (content: FileContent): FileContent => {
// Make it a string
const contentString = readBuffer(content);

const domParser = new DOMParser();
const doc = domParser.parseFromString(contentString, "text/html");

if (!doc.documentElement.getAttribute("lang")) {
doc.documentElement.setAttribute("lang", "en");
}

const html = doc.documentElement.outerHTML;

return `<!DOCTYPE html>\n${html}`;
};
2 changes: 1 addition & 1 deletion sandpack-react/src/utils/sandpackUtils.test.ts
Expand Up @@ -361,7 +361,7 @@ describe(getSandpackStateFromProps, () => {
foo: "*",
react: "^18.0.0",
"react-dom": "^18.0.0",
"react-scripts": "^4.0.0",
"react-scripts": "^5.0.0",
});
});

Expand Down

1 comment on commit af68579

@vercel
Copy link

@vercel vercel bot commented on af68579 Apr 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.