Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 72 additions & 10 deletions src/download.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,7 @@ async function createZipWithSingleFile ({ htmlContent, cssContent, jsContent })
async function createZipWithMultipleFiles ({ htmlContent, cssContent, jsContent }) {
const zip = await getZip()

const indexHtml = `<!DOCTYPE html>
<html lang="en">
<head>
<link type="text/css" rel="stylesheet" href="style.css"/>
</head>
<body>
${htmlContent}
<script type="module" src="script.js"></script>
</body>
</html>`
const indexHtml = buildIndexHtml(htmlContent)

return await zip([
{ name: 'style.css', input: cssContent },
Expand All @@ -49,6 +40,77 @@ async function createZipWithMultipleFiles ({ htmlContent, cssContent, jsContent
]).blob()
}

function buildIndexHtml (html) {
const hasDoctype = /<!doctype\s+html[^>]*>/i.test(html)
const hasHtml = /<html[\s>]/i.test(html)
const hasHead = /<head[\s>]/i.test(html)
const hasBody = /<body[\s>]/i.test(html)
const hasStyleLink = /<link[^>]*href=["']style\.css["'][^>]*>/i.test(html)
const hasScript = /<script[^>]*src=["']script\.js["'][^>]*>/i.test(html)

let result = html

// Add script before </body> or at the end
if (!hasScript) {
if (hasBody) {
result = result.replace(/<\/body>/i, ' <script type="module" src="script.js"></script>\n </body>')
} else {
result = result + '\n <script type="module" src="script.js"></script>'
}
}

// Add stylesheet link inside <head> or store for later
if (!hasStyleLink) {
if (hasHead) {
result = result.replace(/<head([^>]*)>/i, '<head$1>\n <link type="text/css" rel="stylesheet" href="style.css"/>')
}
// If no <head>, it will be added below with the link included
}

// Add <head> if missing
if (!hasHead) {
const headBlock = ' <head>\n <link type="text/css" rel="stylesheet" href="style.css"/>\n </head>'
if (hasBody) {
result = result.replace(/<body/i, headBlock + '\n <body')
} else if (hasHtml) {
result = result.replace(/<html([^>]*)>/i, '<html$1>\n' + headBlock)
} else {
result = headBlock + '\n' + result
}
}

// Wrap in <body> if missing
if (!hasBody) {
if (hasHtml) {
// Find content after </head> and before </html>, wrap it in <body>
result = result.replace(/(<\/head>)([\s\S]*?)(<\/html>)/i, '$1\n <body>\n $2\n </body>\n$3')
} else {
// Find content after </head> and wrap the rest in <body>
const headEnd = result.indexOf('</head>')
if (headEnd !== -1) {
const afterHead = headEnd + '</head>'.length
const before = result.slice(0, afterHead)
const after = result.slice(afterHead)
result = before + '\n <body>' + after + '\n </body>'
} else {
result = ' <body>\n' + result + '\n </body>'
}
}
}

// Wrap in <html> if missing
if (!hasHtml) {
result = '<html lang="en">\n' + result + '\n</html>'
}

// Add DOCTYPE if missing
if (!hasDoctype) {
result = '<!DOCTYPE html>\n' + result
}

return result
}

function generateZip ({ zipBlob, zipFileName }) {
console.log({ zipBlob, zipFileName })
const element = window.document.createElement('a')
Expand Down