Skip to content

Babel internal dep#3073

Merged
DylanPiercey merged 2 commits intomainfrom
babel-internal-dep
Feb 5, 2026
Merged

Babel internal dep#3073
DylanPiercey merged 2 commits intomainfrom
babel-internal-dep

Conversation

@DylanPiercey
Copy link
Contributor

Description

Removes the direct dependencies on babel modules in the @marko/compiler.
Instead the compiler now uses a pre-patched and pre-build copy of babel.

Since Marko patches some internals of babel there have been several times where a change in babel breaks Marko itself.

With this new setup we can adopt new versions of babel during our regular dependencies upgrades in the Marko project.

An added bonus is that the existing patch strategy we had for babel was execution order dependent, and also broke down when multiple copies of babel were at play. With this change that is also resolved.

@changeset-bot
Copy link

changeset-bot bot commented Feb 5, 2026

🦋 Changeset detected

Latest commit: 57a0d78

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
marko Patch
@marko/runtime-tags Patch
@marko/compiler Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@codecov
Copy link

codecov bot commented Feb 5, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.01%. Comparing base (b31b5b7) to head (57a0d78).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3073      +/-   ##
==========================================
- Coverage   89.03%   89.01%   -0.03%     
==========================================
  Files         375      370       -5     
  Lines       47566    46705     -861     
  Branches     4155     4155              
==========================================
- Hits        42351    41575     -776     
+ Misses       5163     5078      -85     
  Partials       52       52              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai
Copy link

coderabbitai bot commented Feb 5, 2026

Walkthrough

Creates a centralized internal Babel façade at packages/compiler/internal/babel that re-exports Babel parsers, generators, plugins, traverse, and types. Removes in-repo Marko-specific Babel type/generator/traverse patches and instead adds patch files under patches/ for @babel/types, @babel/generator, and @babel/traverse. Updates package.json scripts, build tooling (new build-babel script and build-babel-types), internal imports to use the new façade, and removes @babel/runtime from runtime-class. Test snapshots include formatting and minor wiring changes.

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Babel internal dep' is vague and does not clearly convey the main objective of removing direct Babel dependencies and using an internal pre-patched copy instead. Consider a more descriptive title such as 'Replace direct Babel dependencies with internal pre-patched copy' to clearly communicate the primary change.
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The description is well-related to the changeset, explaining the rationale for moving to an internal pre-patched Babel copy and the benefits of this approach.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch babel-internal-dep

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Fix all issues with AI agents
In `@babel.config.js`:
- Around line 8-10: The Babel config's targets.node is set to "20" which
prevents transpilation compatibility for the minimum supported Node version;
update the babel.config.js targets node property (the targets object, node key)
to "18" to match package.json's minimum "18 || 20 || >=22" (or alternatively
remove Node 18 from package.json engines if you intend to drop support) so Babel
emits code compatible with Node 18.

In `@package.json`:
- Line 21: The package.json uses caret ranges for Babel packages so
patch-package may skip applying version-specific patch files (e.g.,
patches/@babel+traverse+7.29.0.patch); update package.json to pin
`@babel/traverse`, `@babel/types`, and `@babel/generator` to the exact versions that
match the patch filenames (replace ^7.x ranges with the exact 7.29.0-style
versions referenced by your patches) and keep the existing "prepare" script
(prepare) and any other dependency entries in sync so patch-package will always
find and apply the correct patches.

In `@packages/compiler/internal/babel/index.ts`:
- Around line 1-22: Remove the module augmentation declaring "declare module
\"@babel/core\" { export const File: any; }" and remove "File" from the export
list so the file no longer re-exports a non-existent symbol; update any import
sites expecting File to use supported APIs (e.g., parser/transform functions or
documented helpers) or import Babel internals explicitly if intentionally
needed, and run tests to ensure no runtime module resolution errors remain.

In `@packages/compiler/package.json`:
- Around line 43-44: The package export for "./internal/babel" currently points
to TS source and the dist artifacts (dist/babel.js, dist/babel.d.ts and
dist/types/dist/traverse) are missing; update the build to produce those files
and change the export to a conditional export. Run and fix
scripts/build-babel.ts and scripts/types.js so they generate dist/babel.js and
dist/babel.d.ts (and the referenced dist/types and dist/traverse), then replace
the "./internal/babel": "./internal/babel/index.ts" export with a conditional
object that provides a "types" entry pointing to "./dist/babel.d.ts" and a
"default" (or "node" / "require" / "import" as appropriate) pointing to
"./dist/babel.js" similar to the existing "./babel-utils" export pattern; also
update babel-types.d.ts references to point at generated ./dist paths. Ensure
multiple imports from `@marko/compiler/internal/babel` (e.g., src/index.js,
src/babel-utils/parse.js, src/babel-plugin/index.js) resolve to the generated
dist artifacts.

In `@packages/compiler/scripts/types/babel-traverse.js`:
- Line 29: The import of MARKO_TYPES from "@babel/types" is invalid at runtime;
update the import in packages/compiler/scripts/types/babel-traverse.js to import
MARKO_TYPES from the module that actually exports it (or else create and export
a runtime constant named MARKO_TYPES from a local module and import that).
Locate the use of MARKO_TYPES in this file and replace the source string to the
correct module that provides the runtime export, or add a new export for
MARKO_TYPES (ensuring the export name matches) and import that instead so the
symbol is available at runtime.

In `@patches/`@babel+traverse+7.29.0.patch:
- Around line 59-61: The code registers Program parameter bindings with an
incorrect binding kind "params", so Scope analysis ignores them; update the call
to use the singular binding kind "param" instead (i.e., change
registerBinding("params", path) to registerBinding("param", path)) when checking
path.type === "Program" and path.node.params?.length to ensure parameters are
correctly registered in scope; reference the registerBinding function and the
path/node params check to locate the change.

In `@patches/`@babel+types+7.29.0.patch:
- Around line 183-191: The function markoScriptlet uses the reserved word static
as a parameter name which breaks in strict/ESM contexts; rename the parameter
(e.g., isStatic or staticFlag) in the function signature and update the returned
object to set the "static" property from that new parameter (e.g., "static":
isStatic) so the external shape remains unchanged; update any local references
to that parameter and keep exports.markoScriptlet/exports.MarkoScriptlet
pointing to the renamed function.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@patches/`@babel+generator+7.29.1.patch:
- Around line 288-300: The type-check in expressionCouldHaveUnenclosedWhitespace
erroneously uses the non-existent AST type "Function"; update the switch to
check the correct function node types (e.g., "FunctionExpression",
"ArrowFunctionExpression", and/or "FunctionDeclaration" as appropriate) instead
of "Function" so the condition can match real Babel AST nodes, keeping the other
cases ("AssignmentExpression", "BinaryExpression", "ConditionalExpression",
"LogicalExpression", "NewExpression") unchanged.
🧹 Nitpick comments (1)
patches/@babel+generator+7.29.1.patch (1)

368-382: Consider using native Array.prototype.toSpliced.

The toSpliced helper is a polyfill, but Array.prototype.toSpliced is natively available in Node 18+ (and the engines field in packages/compiler/package.json requires Node 18+). You could use the native method directly for simplicity, though the current implementation is correct.

@DylanPiercey DylanPiercey merged commit 910167c into main Feb 5, 2026
10 of 11 checks passed
@DylanPiercey DylanPiercey deleted the babel-internal-dep branch February 5, 2026 23:13
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@patches/`@babel+generator+7.29.1.patch:
- Around line 342-348: The loop using while (i < bodyLen && j <
attributeTagsLen) contains a branch where the condition j < attributeTagsLen is
always true, making the final else (which pushes bodyNode and increments i)
unreachable; in the block that handles merging attributeTag and bodyNode
(variables attributeTag, bodyNode, i, j, result), remove the unreachable else
branch and simplify the logic to always push attributeTag and increment j when
location data is missing (i.e., replace the if/else chain with the single branch
that handles j < attributeTagsLen), or if the intended behavior was to allow
exhausting one side, change the loop condition to while (i < bodyLen || j <
attributeTagsLen) and keep appropriate boundary checks — update the code around
the while loop accordingly (references: i, j, bodyLen, attributeTagsLen,
attributeTag, bodyNode, result).
🧹 Nitpick comments (3)
patches/@babel+types+7.29.0.patch (1)

469-471: Consider using "Scopable" alias instead of "Scope" for MarkoTagBody to follow Babel conventions.

The MarkoTagBody type uses the "Scope" alias, but Babel's standard convention is to use "Scopable" for nodes that introduce a scope. Currently, manual validator patches add MarkoTagBody to isScopable, isBlockParent, and isFunctionParent (lines 571, 579, 587). Using "Scopable" instead would eliminate these manual patches and achieve automatic validator registration, aligning with Babel's type system patterns.

packages/compiler/scripts/build-babel.ts (1)

6-17: Consider surfacing esbuild warnings. Optional, but it helps catch bundle issues early.

Suggested tweak
-build({
+build({
   format: "cjs",
   bundle: true,
   absWorkingDir,
   outfile: "dist/babel.js",
   sourcemap: false,
   platform: "node",
   external: ["browserslist"],
   entryPoints: ["internal/babel/index.ts"],
-}).catch((err) => {
+}).then((result) => {
+  if (result.warnings?.length) {
+    console.warn(result.warnings);
+    process.exitCode = 1;
+  }
+}).catch((err) => {
   console.error(err);
   process.exit(1);
 });
patches/@babel+generator+7.29.1.patch (1)

136-136: Remove unnecessary g flag from regex.

The global flag is not needed for .test() which only returns a boolean. While harmless here since the regex is created inline, removing it improves clarity.

Suggested fix
-  const isMultiLine = /[\r\n]/g.test(value);
+  const isMultiLine = /[\r\n]/.test(value);

Comment on lines +342 to +348
+ } else if (j < attributeTagsLen) {
+ result.push(attributeTag);
+ j++;
+ } else {
+ result.push(bodyNode);
+ i++;
+ }
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Unreachable else branch due to loop invariant.

The condition j < attributeTagsLen at line 342 is always true within the while (i < bodyLen && j < attributeTagsLen) loop, making the else branch at lines 345-348 unreachable dead code.

If the intent is to always prioritize attributeTag when location data is missing, simplify by removing the unreachable branch:

Proposed fix
     if (bodyNode.loc != null && attributeTag.loc != null) {
       if (compareStartLoc(bodyNode, attributeTag) < 0) {
         result.push(bodyNode);
         i++;
       } else {
         result.push(attributeTag);
         j++;
       }
-    } else if (j < attributeTagsLen) {
+    } else {
+      // When location data is missing, prioritize attributeTags
       result.push(attributeTag);
       j++;
-    } else {
-      result.push(bodyNode);
-      i++;
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
+ } else if (j < attributeTagsLen) {
+ result.push(attributeTag);
+ j++;
+ } else {
+ result.push(bodyNode);
+ i++;
+ }
} else {
// When location data is missing, prioritize attributeTags
result.push(attributeTag);
j++;
}
🤖 Prompt for AI Agents
In `@patches/`@babel+generator+7.29.1.patch around lines 342 - 348, The loop using
while (i < bodyLen && j < attributeTagsLen) contains a branch where the
condition j < attributeTagsLen is always true, making the final else (which
pushes bodyNode and increments i) unreachable; in the block that handles merging
attributeTag and bodyNode (variables attributeTag, bodyNode, i, j, result),
remove the unreachable else branch and simplify the logic to always push
attributeTag and increment j when location data is missing (i.e., replace the
if/else chain with the single branch that handles j < attributeTagsLen), or if
the intended behavior was to allow exhausting one side, change the loop
condition to while (i < bodyLen || j < attributeTagsLen) and keep appropriate
boundary checks — update the code around the while loop accordingly (references:
i, j, bodyLen, attributeTagsLen, attributeTag, bodyNode, result).

@github-actions github-actions bot mentioned this pull request Feb 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant