Repository navigation
@unlayer/migrate: MJML and Beefree importers (--from mjml|beefree) - #70
Open
ivoIturrieta wants to merge 6 commits into
Open
ivoIturrieta wants to merge 6 commits into
ivoIturrieta wants to merge 6 commits into
Conversation
…urpose Some content changes are known and accepted, such as the editor's Social block writing an icon's alt text as "Facebook icon", or Beefree's phone hamburger menu having no equivalent. Without a way to say so, the content check reported them as a converter bug. applyDeclared(originalHtml, declarations) applies only the named substitutions (an alt or src on an exact image, a glyph inside a named class, an element shown or hidden) to the reference before the unchanged content check runs, and describeDeclared lists each one as a difference. Anything undeclared still fails; --strict still fails declared ones. Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <noreply@anthropic.com>
Same flow as React Email: convert, check against the original, report, and write with --write or --out under the same git-safety, collision and exit-code rules. Both converters are pure source → Unlayer design functions; @unlayer/react-elements/codegen writes the design as code and --design writes the design itself. - MJML (mjml pinned at 5.4.1): mj-attributes, mj-class and mj-include resolved; sections, columns, text, buttons, images, dividers, spacers, social, navbar and simple tables become editable blocks; hero overlays, carousels and accordions stay as MJML's own HTML, counted separately as not editable. MJML 3 files fail with a pointer to mjml-migrate. Checked against MJML's own output. - Beefree: classic JSON in its known wrappers and versions (unknown versions fail loudly); rows, columns and modules map to blocks. Checked against Beefree's HTML export when a <name>.html sits beside the JSON, otherwise reported as unverified (--strict fails it). - Social alt text and Beefree's hamburger menu are declared differences. On real templates: MJML 112 of 114 ✓, 0 ✗, 96.2% of blocks editable (the other 2 don't compile in MJML either); Beefree 107 of 107 converted, 33 ✓ and 0 ✗ against its exports. Committed fixtures are permissively licensed, with LICENSE and NOTICE files. Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <noreply@anthropic.com>
…porters New sources are a feature, so a minor: the release job publishes a committed version ahead of npm instead of its default patch. Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <noreply@anthropic.com>
An mj-include outside the folder given (or one that fails to read) is left out of the conversion, and the reference is compiled from the same document, so the check couldn't see the missing content and passed it. The template now fails, naming what wasn't read, in the run and the report; it's still written, flagged, as other content failures are. Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <noreply@anthropic.com>
The file name went into a // comment of the code that's written and loaded to check the template, so a name with a line break ended the comment and ran what followed. Line breaks become spaces. Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <noreply@anthropic.com>
ivoIturrieta
marked this pull request as ready for review
October 16, 2026 05:16
A template that fails for an mj-include it couldn't read now says it failed the check, not that its content differs, and the summary no longer asks to report it as a converter bug. Co-Authored-By: Claude Code (Claude Opus 5.5 xhigh) <noreply@anthropic.com>
This branch has not been deployed
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on the 0.3.0 library PR (#69), since it uses
@unlayer/react-elements/codegen: it shows only the importer changes. Merge it after #69, with its base changed tomainfirst (GitHub does that itself when #69's branch is deleted on merge).The repo's tests only run on pull requests into
main, so CI starts here once the base ismain(a push after that triggers it). Until then, the validation below was run locally on the branch.What
npx @unlayer/migrate <dir> --from mjmland--from beefree, with the same flow as--from react-email:--write/--out/--design/--report/--strict, the same git-safety and collision rules, the same ✓/✗ output and exit codes.The source files stay in place, and
<name>.tsxis written beside each one.Both converters are pure source → Unlayer design functions (
src/from-mjml,src/from-beefree). The library's codegen writes the design as Elements code, and--designwrites the design itself. MJML and Beefree templates carry no program logic, so nothing is lost by going through the design. The same functions could later power editor imports.MJML (
mjmlpinned at 5.4.1)mj-attributes,mj-classandmj-includeare resolved. An include it can't read (outside the folder given) fails the template, naming it: its check can't see what's missing.mjml-migrate.Beefree
<name>.htmlsits beside the JSON. Otherwise the template is reported as unverified, and--strictfails it.Declared differences (
convert-core)A converter can declare specific changes it knows it makes. The editor's Social block writes alt text as "Facebook icon", and Beefree's phone hamburger menu has no equivalent. The check expects these and lists them under "looks different" instead of flagging a converter bug. Anything undeclared still fails, and
--strictstill fails declared ones.Results on real templates
Committed fixtures are permissively licensed (MIT, Apache-2.0), with LICENSE and NOTICE files. The rest of the corpus was used locally only.
Validation
Release
@unlayer/migratepublishes as 0.2.0 (committed: new sources are a minor), with the Elements range^0.3.0set in #69.