@@ -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
7393function 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 = """ ;
103+ result + = """ ;
90104 break ;
91105 case 38 : // &
92- escape = "&" ;
106+ result + = "&" ;
93107 break ;
94108 case 39 : // '
95- escape = "'" ;
109+ result + = "'" ;
96110 break ;
97111 case 60 : // <
98- escape = "<" ;
112+ result + = "<" ;
99113 break ;
100114 case 62 : // >
101- escape = ">" ;
115+ result + = ">" ;
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