Last active
September 12, 2024 07:54
-
-
Save maxlath/35c3a3bd06eb64e078397206cb86268e to your computer and use it in GitHub Desktop.
Convert a Handlebars template to Svelte
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env node | |
// Handlebars and Svelte templates are quite similar (that is, looking a lot like plain HTML), | |
// so converting from Handlebars to Svelte is mostly a matter of converting handlebars helpers argument syntax | |
// into Svelte JS-based syntax, and importing the helper functions. | |
// WARNING: if you have been using named arguments in Handlebars (a.k.a. 'hash' arguments), | |
// the helper function interface will need to be updated (or the arguments passed as `{ hash: argsObject }`) | |
// STATUS: WIP. | |
// - Most of the conversion work around special keywords remains to be done manually | |
// - Helpers with a mix of inline and hash arguments aren't parsed correctly | |
// HOW TO: | |
// chmod +x handlebars2svelte.js | |
// cat some_template.hbs | handlebars2svelte.js > some_template.svelte | |
const helpersNames = new Set() | |
const keywords = { | |
'#if': '#if ', | |
'/if': '/if ', | |
'/unless': '/if ', | |
'#unless': '#if !', | |
'else': ':else ', | |
'this': 'THIS ', | |
'>': 'PARTIAL ', | |
'#each': '#each ADD_AS ', | |
'/each': '/each ', | |
} | |
const parseHelperFunction = content => { | |
if (!content.match(/\s/)) return content | |
let [ , helperName, args ] = content.match(/^(>?#?\/?\w*)(.*)$/) | |
if (keywords[helperName]) { | |
helperName = keywords[helperName] | |
let argsText = parseObjectArgs(args) | |
if (argsText === '()') return helperName | |
else return `${helperName}${parseObjectArgs(args)}` | |
} else { | |
helpersNames.add(helperName) | |
return `${helperName}(${parseObjectArgs(args)})` | |
} | |
} | |
const parseObjectArgs = args => { | |
args = args.trim() | |
if (args.match(/\w+=/)) { | |
return args | |
.replace(/(\w+)=/g, ', $1: ') | |
.replace(/ , /g, ', ') | |
.replace(/^, /, '{ ') | |
.replace(/$/, ' }') | |
} else { | |
return args | |
} | |
} | |
const handlebars2svelte = hbsText => { | |
const svelteText = hbsText | |
.split('{{{') | |
.map((part, i) => { | |
if (i === 0) { | |
return part | |
} else { | |
const [ content, after ] = part.split('}}}') | |
return `{@html ${parseHelperFunction(content)}}${after}` | |
} | |
}) | |
.join('') | |
.split('{{') | |
.map((part, i) => { | |
if (i === 0) { | |
return part | |
} else { | |
const [ content, after ] = part.split('}}') | |
return `{${parseHelperFunction(content).trim()}}${after}` | |
} | |
}) | |
.join('') | |
const importText = `import { ${Array.from(helpersNames).join(', ')} } from './helpers_path'` | |
return `<script>\n ${importText}\n</script>\n\n${svelteText}` | |
} | |
let text = '' | |
process.stdin | |
.on('data', buf => text += buf.toString()) | |
.on('close', () => process.stdout.write(handlebars2svelte(text))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment