Skip to content

Commit 8e99325

Browse files
committed
test
1 parent 5d203a1 commit 8e99325

1 file changed

Lines changed: 38 additions & 31 deletions

File tree

packages/app/server/utils/template.ts

Lines changed: 38 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ export function generateTemplateHtml(
88
99
<form id="mainForm" method="post" action="https://stackblitz.com/run" target="_self">
1010
${Object.entries(files)
11-
.map(
12-
([file, contentOrUrl]) =>
13-
`<input type="hidden" name="project[files][${escapeFilename(file)}]" value="${escapeHtml(contentOrUrl)}">`,
14-
)
11+
.map(([file, contentOrUrl]) => {
12+
const escapedFilename = escapeFilename(file);
13+
let escapedContent = escapeFileContent(file, contentOrUrl);
14+
15+
return `<input type="hidden" name="project[files][${escapedFilename}]" value="${escapedContent}">`;
16+
})
1517
.join("\n")}
1618
<input type="hidden" name="project[description]" value="generated by https://pkg.pr.new">
1719
<input type="hidden" name="project[template]" value="node">
@@ -67,50 +69,55 @@ function escapeFilename(file: string): string {
6769
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
6870
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
6971
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
72+
function escapeFileContent(filename: string, content: string): string {
73+
// Special handling for package.json to preserve formatting
74+
if (filename === "package.json") {
75+
try {
76+
// Parse and re-format the JSON to ensure it's properly formatted
77+
const parsedJson = JSON.parse(content);
78+
const formattedJson = JSON.stringify(parsedJson, null, 2);
79+
80+
// URL-encode the entire formatted JSON content
81+
// This will preserve all whitespace and formatting when sent through an HTML form
82+
// StackBlitz will automatically decode this when loading the file
83+
return encodeURIComponent(formattedJson);
84+
} catch (e) {
85+
console.error("Failed to parse package.json content:", e);
86+
return escapeHtml(content);
87+
}
88+
}
89+
return escapeHtml(content);
90+
}
7091

71-
const matchHtmlRegExp = /["'&<>]/;
72-
92+
// Custom HTML escape function that preserves whitespace and newlines
7393
function escapeHtml(string: string) {
7494
const str = `${string}`;
75-
const match = matchHtmlRegExp.exec(str);
95+
let result = "";
7696

77-
if (!match) {
78-
return str;
79-
}
80-
81-
let escape;
82-
let html = "";
83-
let index = 0;
84-
let lastIndex = 0;
97+
for (let i = 0; i < str.length; i++) {
98+
const char = str[i];
99+
const code = str.charCodeAt(i);
85100

86-
for (index = match.index; index < str.length; index++) {
87-
switch (str.charCodeAt(index)) {
101+
switch (code) {
88102
case 34: // "
89-
escape = "&quot;";
103+
result += "&quot;";
90104
break;
91105
case 38: // &
92-
escape = "&amp;";
106+
result += "&amp;";
93107
break;
94108
case 39: // '
95-
escape = "&#39;";
109+
result += "&#39;";
96110
break;
97111
case 60: // <
98-
escape = "&lt;";
112+
result += "&lt;";
99113
break;
100114
case 62: // >
101-
escape = "&gt;";
115+
result += "&gt;";
102116
break;
103117
default:
104-
continue;
118+
result += char;
105119
}
106-
107-
if (lastIndex !== index) {
108-
html += str.substring(lastIndex, index);
109-
}
110-
111-
lastIndex = index + 1;
112-
html += escape;
113120
}
114121

115-
return lastIndex !== index ? html + str.substring(lastIndex, index) : html;
122+
return result;
116123
}

0 commit comments

Comments
 (0)