zmod-ember
v0.3.0
Published
zmod parser adapter for Ember's .gjs and .gts files via ember-estree
Maintainers
Readme
zmod-ember
This repo provides an adapter for zmod for ember's gjs and gts files via ember-estree as zmod's default parser is oxc-parser, which is ESTree-compatible.
Installation
pnpm add zmod-ember zmodUsage
With z.withParser()
import { z } from "zmod";
import { emberParser } from "zmod-ember";
const j = z.withParser(emberParser);
const source = `import Component from '@glimmer/component';
export default class OldComponent extends Component {
<template>
<h1>Hello {{@name}}</h1>
</template>
}
`;
const root = j(source, { filePath: "my-component.gjs" });
root.find(j.Identifier, { name: "OldComponent" }).replaceWith("NewComponent");
console.log(root.toSource());As a transform module
import type { Transform } from "zmod";
import { emberParser } from "zmod-ember";
// Export the parser so zmod's `run()` uses it for all files
export const parser = emberParser;
const transform: Transform = ({ source, path }, { z }) => {
const root = z(source, { filePath: path });
root.find(z.Identifier, { name: "OldName" }).replaceWith("NewName");
return root.toSource();
};
export default transform;Operating on Glimmer nodes
Glimmer template nodes are exposed as Glimmer*-prefixed types and can be found using string-based type queries:
import { z } from "zmod";
import { emberParser } from "zmod-ember";
const j = z.withParser(emberParser);
const source = `<template>
<OldComponent @oldArg={{this.value}}>
<:header>Header</:header>
</OldComponent>
</template>
`;
const root = j(source, { filePath: "component.gjs" });
// Rename a component
root.find("GlimmerElementNode", { tag: "OldComponent" }).forEach((path) => {
path.node.tag = "NewComponent";
});
// Rename an argument
root.find("GlimmerAttrNode", { name: "@oldArg" }).replaceWith("@newArg={{this.value}}");
// Rename a named block
root.find("GlimmerElementNode", { tag: ":header" }).replaceWith("<:title>Header</:title>");
console.log(root.toSource());Other Glimmer node types you can query include GlimmerMustacheStatement, GlimmerBlockStatement, GlimmerPathExpression, GlimmerTextNode, GlimmerElementModifierStatement, GlimmerSubExpression, GlimmerHashPair, GlimmerStringLiteral, GlimmerNumberLiteral, and GlimmerBlockParam.
Running transforms
import { run } from "zmod";
import transform from "./my-transform.js";
const result = await run(transform, {
include: ["src/**/*.gjs", "src/**/*.gts"],
});
console.log(result.files);How it works
The adapter wraps ember-estree's toTree() to implement zmod's Parser interface:
parse(source, options)— CallstoTreeand returns an ESTree-compatible AST with embedded Glimmer template nodes. All nodes are guaranteed to havestart/endbyte-offset properties required by zmod's span-based patching. Handles both top-level and class body<template>tags.print(node)— Serializes AST nodes back to source code. Handles standard ESTree nodes and Glimmer template nodes (e.g.,GlimmerElementNode,GlimmerMustacheStatement).
Pass { filePath: 'name.gjs' } or { filePath: 'name.gts' } in the parse options to control the file type.
Note: Use zmod's default parser for plain
.jsand.tsfiles —zmod-emberis only needed for.gjsand.gtsfiles that contain<template>tags. For codemods that target both standard JS/TS and Ember template files, usezmod-emberonly for the.gjs/.gtsfiles:import { z, run } from "zmod"; import { emberParser } from "zmod-ember"; // For .gjs/.gts files, use the ember parser const gjsTransform = ({ source, path }, { z }) => { return z(source, { filePath: path }) .find(z.Identifier, { name: "OldName" }) .replaceWith("NewName") .toSource(); }; gjsTransform.parser = emberParser; // For .js/.ts files, use zmod's default parser (no parser export needed) const jsTransform = ({ source }, { z }) => { return z(source).find(z.Identifier, { name: "OldName" }).replaceWith("NewName").toSource(); };
Peer dependencies
| Package | Required | Notes |
| ------- | -------- | -------------------- |
| zmod | Yes | Core codemod toolkit |
