Skip to content

Instantly share code, notes, and snippets.

@maxlath
Last active March 15, 2024 01:27
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save maxlath/35c3a3bd06eb64e078397206cb86268e to your computer and use it in GitHub Desktop.
Save maxlath/35c3a3bd06eb64e078397206cb86268e to your computer and use it in GitHub Desktop.
Convert a Handlebars template to Svelte
#!/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