diff --git a/javascript/packages/rewriter/README.md b/javascript/packages/rewriter/README.md index 587fdd0fe..a61e7ebc7 100644 --- a/javascript/packages/rewriter/README.md +++ b/javascript/packages/rewriter/README.md @@ -59,7 +59,8 @@ await Herb.load() const template = `
` const parseResult = Herb.parse(template, { track_whitespace: true }) -const { output, node } = await rewrite(parseResult.value, [tailwindClassSorter()]) +const sorter = await tailwindClassSorter() +const { output, node } = rewrite(parseResult.value, [sorter]) // output: "
" // node: The rewritten AST node ``` @@ -77,7 +78,8 @@ await Herb.load() const template = `
` -const output = await rewriteString(Herb, template, [tailwindClassSorter()]) +const sorter = await tailwindClassSorter() +const output = rewriteString(Herb, template, [sorter]) // output: "
" ``` @@ -98,7 +100,8 @@ import { tailwindClassSorter } from "@herb-tools/rewriter/loader" await Herb.load() const template = `
` -const output = await rewriteString(Herb, template, [tailwindClassSorter()]) +const sorter = await tailwindClassSorter() +const output = rewriteString(Herb, template, [sorter]) // output: "
" ``` @@ -211,16 +214,16 @@ Which means you can just reference and configure them in `.herb.yml` using their Transform an AST node using the provided rewriters. ```typescript -async function rewrite( +function rewrite( node: T, rewriters: Rewriter[], options?: RewriteOptions -): Promise +): RewriteResult ``` **Parameters:** - `node`: The AST node to transform -- `rewriters`: Array of rewriter instances to apply +- `rewriters`: Array of rewriter instances to apply (must be already initialized) - `options`: Optional configuration - `baseDir`: Base directory for resolving config files (defaults to `process.cwd()`) - `filePath`: Optional file path for context @@ -234,18 +237,18 @@ async function rewrite( Convenience wrapper around `rewrite()` that parses the template string first and returns just the output string. ```typescript -async function rewriteString( +function rewriteString( herb: HerbBackend, template: string, rewriters: Rewriter[], options?: RewriteOptions -): Promise +): string ``` **Parameters:** - `herb`: The Herb backend instance for parsing - `template`: The HTML+ERB template string to rewrite -- `rewriters`: Array of rewriter instances to apply +- `rewriters`: Array of rewriter instances to apply (must be already initialized) - `options`: Optional configuration (same as `rewrite()`) **Returns:** The rewritten template string diff --git a/javascript/packages/rewriter/src/rewrite.ts b/javascript/packages/rewriter/src/rewrite.ts index 4156ec345..5d2e99696 100644 --- a/javascript/packages/rewriter/src/rewrite.ts +++ b/javascript/packages/rewriter/src/rewrite.ts @@ -49,7 +49,7 @@ export interface RewriteResult { * * const template = '
' * const parseResult = Herb.parse(template) - * const { output, document } = await rewrite(parseResult.value, [tailwindClassSorter()]) + * const { output, node } = rewrite(parseResult.value, [tailwindClassSorter()]) * ``` * * @param node - The AST Node to rewrite @@ -57,15 +57,11 @@ export interface RewriteResult { * @param options - Optional configuration for the rewrite operation * @returns Object containing the rewritten string and Node */ -export async function rewrite(node: T, rewriters: Rewriter[], options: RewriteOptions = {}): Promise { +export function rewrite(node: T, rewriters: Rewriter[], options: RewriteOptions = {}): RewriteResult & { node: T } { const { baseDir = process.cwd(), filePath } = options const context: RewriteContext = { baseDir, filePath } - for (const rewriter of rewriters) { - await rewriter.initialize(context) - } - let currentNode = node const astRewriters = rewriters.filter(rewriter => rewriter instanceof ASTRewriter) @@ -109,7 +105,7 @@ export async function rewrite(node: T, rewriters: Rewriter[], op * await Herb.load() * * const template = '
' - * const output = await rewriteString(Herb, template, [tailwindClassSorter()]) + * const output = rewriteString(Herb, template, [tailwindClassSorter()]) * // output: '
' * ``` * @@ -119,14 +115,14 @@ export async function rewrite(node: T, rewriters: Rewriter[], op * @param options - Optional configuration for the rewrite operation * @returns The rewritten template string */ -export async function rewriteString(herb: HerbBackend, template: string, rewriters: Rewriter[], options: RewriteOptions = {}): Promise { +export function rewriteString(herb: HerbBackend, template: string, rewriters: Rewriter[], options: RewriteOptions = {}): string { const parseResult = herb.parse(template, { track_whitespace: true }) if (parseResult.failed) { return template } - const { output } = await rewrite( + const { output } = rewrite( parseResult.value, rewriters, options diff --git a/javascript/packages/rewriter/src/rewriter-factories.ts b/javascript/packages/rewriter/src/rewriter-factories.ts index f299d5264..cdc202ae1 100644 --- a/javascript/packages/rewriter/src/rewriter-factories.ts +++ b/javascript/packages/rewriter/src/rewriter-factories.ts @@ -11,29 +11,27 @@ export interface TailwindClassSorterOptions { /** * Factory function for creating a Tailwind class sorter rewriter * + * Automatically initializes the rewriter before returning it. + * * @example * ```typescript * import { rewrite } from '@herb-tools/rewriter' * import { tailwindClassSorter } from '@herb-tools/rewriter/loader' * * const template = '
' - * const result = await rewrite(template, [tailwindClassSorter()]) + * const sorter = await tailwindClassSorter() + * const result = rewrite(template, [sorter]) * // Result: '
' * ``` * * @param options - Optional configuration for the Tailwind class sorter - * @returns A configured TailwindClassSorterRewriter instance + * @returns A configured and initialized TailwindClassSorterRewriter instance */ -export function tailwindClassSorter(options: TailwindClassSorterOptions = {}): TailwindClassSorterRewriter { +export async function tailwindClassSorter(options: TailwindClassSorterOptions = {}): Promise { const rewriter = new TailwindClassSorterRewriter() + const baseDir = options.baseDir || process.cwd() - if (options.baseDir) { - const originalInitialize = rewriter.initialize.bind(rewriter) - - rewriter.initialize = async (context) => { - return originalInitialize({ ...context, baseDir: options.baseDir || context.baseDir }) - } - } + await rewriter.initialize({ baseDir }) return rewriter } diff --git a/javascript/packages/rewriter/test/rewrite.test.ts b/javascript/packages/rewriter/test/rewrite.test.ts index 2dcc4f38a..54c1d6ade 100644 --- a/javascript/packages/rewriter/test/rewrite.test.ts +++ b/javascript/packages/rewriter/test/rewrite.test.ts @@ -5,29 +5,32 @@ import { Herb } from "@herb-tools/node-wasm" import { rewrite, rewriteString, tailwindClassSorter } from "../src/loader.js" describe("rewrite", () => { + let sorter: Awaited> + beforeAll(async () => { await Herb.load() + sorter = await tailwindClassSorter() }) describe("basic usage", () => { - test("rewrites Node with tailwindClassSorter", async () => { + test("rewrites Node with tailwindClassSorter", () => { const template = '
' const parseResult = Herb.parse(template, { track_whitespace: true }) - const { output, node } = await rewrite(parseResult.value, [tailwindClassSorter()]) + const { output, node } = rewrite(parseResult.value, [sorter]) expect(output).toBe('
') expect(node.type).toBe("AST_DOCUMENT_NODE") }) - test("handles empty rewriters array", async () => { + test("handles empty rewriters array", () => { const template = '
' const parseResult = Herb.parse(template, { track_whitespace: true }) - const { output } = await rewrite(parseResult.value, []) + const { output } = rewrite(parseResult.value, []) expect(output).toBe(template) }) - test("handles multiple elements", async () => { + test("handles multiple elements", () => { const template = dedent`
@@ -40,81 +43,81 @@ describe("rewrite", () => { ` const parseResult = Herb.parse(template, { track_whitespace: true }) - const { output } = await rewrite(parseResult.value, [tailwindClassSorter()]) + const { output } = rewrite(parseResult.value, [sorter]) expect(output).toBe(expected) }) }) describe("rewriteString", () => { - test("rewrites string with tailwindClassSorter", async () => { + test("rewrites string with tailwindClassSorter", () => { const template = '
' - const output = await rewriteString(Herb, template, [tailwindClassSorter()]) + const output = rewriteString(Herb, template, [sorter]) expect(output).toBe('
') }) - test("handles empty rewriters array", async () => { + test("handles empty rewriters array", () => { const template = '
' - const output = await rewriteString(Herb, template, []) + const output = rewriteString(Herb, template, []) expect(output).toBe(template) }) - test("returns original template on parse failure", async () => { + test("returns original template on parse failure", () => { const template = '
{ - test("accepts baseDir option", async () => { + test("accepts baseDir option", () => { const template = '
' const parseResult = Herb.parse(template, { track_whitespace: true }) - const { output } = await rewrite(parseResult.value, [tailwindClassSorter()], { + const { output } = rewrite(parseResult.value, [sorter], { baseDir: "/custom/path" }) expect(output).toBe('
') }) - test("accepts filePath option", async () => { + test("accepts filePath option", () => { const template = '
' const parseResult = Herb.parse(template, { track_whitespace: true }) - const { output } = await rewrite(parseResult.value, [tailwindClassSorter()], { + const { output } = rewrite(parseResult.value, [sorter], { filePath: "/custom/path/file.html.erb" }) expect(output).toBe('
') }) - test("uses process.cwd() as default baseDir", async () => { + test("uses process.cwd() as default baseDir", () => { const template = '
' const parseResult = Herb.parse(template, { track_whitespace: true }) - const { output } = await rewrite(parseResult.value, [tailwindClassSorter()]) + const { output } = rewrite(parseResult.value, [sorter]) expect(output).toBe('
') }) }) describe("complex templates", () => { - test("handles ERB expressions", async () => { + test("handles ERB expressions", () => { const template = '
' - const output = await rewriteString(Herb, template, [tailwindClassSorter()]) + const output = rewriteString(Herb, template, [sorter]) expect(output).toBe('
') }) - test("handles ERB conditionals", async () => { + test("handles ERB conditionals", () => { const template = '
' - const output = await rewriteString(Herb, template, [tailwindClassSorter()]) + const output = rewriteString(Herb, template, [sorter]) expect(output).toBe('
') }) - test("handles multiline templates", async () => { + test("handles multiline templates", () => { const template = dedent`
{
` - const output = await rewriteString(Herb, template, [tailwindClassSorter()]) + const output = rewriteString(Herb, template, [sorter]) expect(output).toBe(expected) }) }) describe("error handling", () => { - test("continues processing on rewriter error", async () => { + test("continues processing on rewriter error", () => { const template = '
' - const errorRewriter = tailwindClassSorter() + const errorRewriter = sorter errorRewriter.rewrite.bind(errorRewriter) errorRewriter.rewrite = () => { @@ -152,7 +155,7 @@ describe("rewrite", () => { } const parseResult = Herb.parse(template, { track_whitespace: true }) - const { output } = await rewrite(parseResult.value, [errorRewriter]) + const { output } = rewrite(parseResult.value, [errorRewriter]) expect(output).toBe(template) }) @@ -160,19 +163,19 @@ describe("rewrite", () => { describe("tailwindClassSorter factory", () => { test("creates rewriter with default options", () => { - const rewriter = tailwindClassSorter() + const rewriter = sorter expect(rewriter.name).toBe("tailwind-class-sorter") }) - test("creates rewriter with custom baseDir", () => { - const rewriter = tailwindClassSorter({ baseDir: "/custom/path" }) + test("creates rewriter with custom baseDir", async () => { + const rewriter = await tailwindClassSorter({ baseDir: "/custom/path" }) expect(rewriter.name).toBe("tailwind-class-sorter") }) test("rewriter has initialize method", async () => { - const rewriter = tailwindClassSorter() + const rewriter = sorter await expect( rewriter.initialize({ baseDir: process.cwd() })