@herb-tools/rewriter 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +342 -0
- package/dist/index.cjs +3802 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.esm.js +3793 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/loader.cjs +12480 -0
- package/dist/loader.cjs.map +1 -0
- package/dist/loader.esm.js +12446 -0
- package/dist/loader.esm.js.map +1 -0
- package/dist/types/ast-rewriter.d.ts +64 -0
- package/dist/types/built-ins/index.d.ts +14 -0
- package/dist/types/built-ins/tailwind-class-sorter.d.ts +13 -0
- package/dist/types/context.d.ts +19 -0
- package/dist/types/custom-rewriter-loader.d.ts +67 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/loader.d.ts +6 -0
- package/dist/types/mutable.d.ts +17 -0
- package/dist/types/rewrite.d.ts +77 -0
- package/dist/types/rewriter-factories.d.ts +28 -0
- package/dist/types/string-rewriter.d.ts +54 -0
- package/dist/types/type-guards.d.ts +20 -0
- package/package.json +54 -0
- package/src/ast-rewriter.ts +70 -0
- package/src/built-ins/index.ts +33 -0
- package/src/built-ins/tailwind-class-sorter.ts +278 -0
- package/src/context.ts +21 -0
- package/src/custom-rewriter-loader.ts +187 -0
- package/src/index.ts +13 -0
- package/src/loader.ts +8 -0
- package/src/mutable.ts +22 -0
- package/src/rewrite.ts +132 -0
- package/src/rewriter-factories.ts +37 -0
- package/src/string-rewriter.ts +60 -0
- package/src/type-guards.ts +56 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { Node } from "@herb-tools/core";
|
|
2
|
+
import type { RewriteContext } from "./context.js";
|
|
3
|
+
/**
|
|
4
|
+
* Base class for AST rewriters that transform AST nodes before formatting
|
|
5
|
+
*
|
|
6
|
+
* AST rewriters receive a Node and can mutate it in place or return a modified Node.
|
|
7
|
+
* They run before the formatting step.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { ASTRewriter, asMutable } from "@herb-tools/rewriter"
|
|
12
|
+
* import { Visitor } from "@herb-tools/core"
|
|
13
|
+
*
|
|
14
|
+
* class MyRewriter extends ASTRewriter {
|
|
15
|
+
* get name() { return "my-rewriter" }
|
|
16
|
+
* get description() { return "My custom AST rewriter" }
|
|
17
|
+
*
|
|
18
|
+
* async initialize(context) {
|
|
19
|
+
* // Load config, initialize dependencies, etc.
|
|
20
|
+
* }
|
|
21
|
+
*
|
|
22
|
+
* rewrite(node, context) {
|
|
23
|
+
* // Use visitor pattern to traverse and modify AST
|
|
24
|
+
* const visitor = new MyVisitor()
|
|
25
|
+
* visitor.visit(node)
|
|
26
|
+
*
|
|
27
|
+
* return node
|
|
28
|
+
* }
|
|
29
|
+
* }
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare abstract class ASTRewriter {
|
|
33
|
+
/**
|
|
34
|
+
* Unique identifier for this rewriter
|
|
35
|
+
* Used in configuration and error messages
|
|
36
|
+
*/
|
|
37
|
+
abstract get name(): string;
|
|
38
|
+
/**
|
|
39
|
+
* Human-readable description of what this rewriter does
|
|
40
|
+
*/
|
|
41
|
+
abstract get description(): string;
|
|
42
|
+
/**
|
|
43
|
+
* Optional async initialization hook
|
|
44
|
+
*
|
|
45
|
+
* Called once before the first rewrite operation. Use this to:
|
|
46
|
+
* - Load configuration files
|
|
47
|
+
* - Initialize dependencies
|
|
48
|
+
* - Perform expensive setup operations
|
|
49
|
+
*
|
|
50
|
+
* @param context - Context with baseDir and optional filePath
|
|
51
|
+
*/
|
|
52
|
+
initialize(_context: RewriteContext): Promise<void>;
|
|
53
|
+
/**
|
|
54
|
+
* Transform the AST node
|
|
55
|
+
*
|
|
56
|
+
* This method is called synchronously for each file being formatted.
|
|
57
|
+
* Modify the AST in place or return a new Node.
|
|
58
|
+
*
|
|
59
|
+
* @param node - The AST node from @herb-tools/core
|
|
60
|
+
* @param context - Context with filePath and baseDir
|
|
61
|
+
* @returns The modified Node (can be the same object mutated in place)
|
|
62
|
+
*/
|
|
63
|
+
abstract rewrite<T extends Node>(node: T, context: RewriteContext): T;
|
|
64
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { RewriterClass } from "../type-guards.js";
|
|
2
|
+
export { TailwindClassSorterRewriter } from "./tailwind-class-sorter.js";
|
|
3
|
+
/**
|
|
4
|
+
* All built-in rewriters available in the package
|
|
5
|
+
*/
|
|
6
|
+
export declare const builtinRewriters: RewriterClass[];
|
|
7
|
+
/**
|
|
8
|
+
* Get a built-in rewriter by name
|
|
9
|
+
*/
|
|
10
|
+
export declare function getBuiltinRewriter(name: string): RewriterClass | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* Get all built-in rewriter names
|
|
13
|
+
*/
|
|
14
|
+
export declare function getBuiltinRewriterNames(): string[];
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ASTRewriter } from "../ast-rewriter.js";
|
|
2
|
+
import type { RewriteContext } from "../context.js";
|
|
3
|
+
import type { Node } from "@herb-tools/core";
|
|
4
|
+
/**
|
|
5
|
+
* Built-in rewriter that sorts Tailwind CSS classes in class and className attributes
|
|
6
|
+
*/
|
|
7
|
+
export declare class TailwindClassSorterRewriter extends ASTRewriter {
|
|
8
|
+
private sorter?;
|
|
9
|
+
get name(): string;
|
|
10
|
+
get description(): string;
|
|
11
|
+
initialize(context: RewriteContext): Promise<void>;
|
|
12
|
+
rewrite<T extends Node>(node: T, _context: RewriteContext): T;
|
|
13
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context passed to rewriters during initialization and rewriting
|
|
3
|
+
*
|
|
4
|
+
* Provides information about the current file and project being processed
|
|
5
|
+
*/
|
|
6
|
+
export interface RewriteContext {
|
|
7
|
+
/**
|
|
8
|
+
* Path to the file being rewritten (if available)
|
|
9
|
+
*/
|
|
10
|
+
filePath?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Base directory of the project
|
|
13
|
+
*/
|
|
14
|
+
baseDir: string;
|
|
15
|
+
/**
|
|
16
|
+
* Additional context data that can be added by the framework
|
|
17
|
+
*/
|
|
18
|
+
[key: string]: any;
|
|
19
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { RewriterClass } from "./type-guards.js";
|
|
2
|
+
export interface CustomRewriterLoaderOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Base directory to search for custom rewriters
|
|
5
|
+
* Defaults to current working directory
|
|
6
|
+
*/
|
|
7
|
+
baseDir?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Glob patterns to search for custom rewriter files
|
|
10
|
+
* Defaults to looking in .herb/rewriters/
|
|
11
|
+
*/
|
|
12
|
+
patterns?: string[];
|
|
13
|
+
/**
|
|
14
|
+
* Whether to suppress errors when loading custom rewriters
|
|
15
|
+
* Defaults to false
|
|
16
|
+
*/
|
|
17
|
+
silent?: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Loads custom rewriters from the user's project
|
|
21
|
+
*
|
|
22
|
+
* Auto-discovers rewriter files in `.herb/rewriters/` by default
|
|
23
|
+
* and dynamically imports them for use in the formatter.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* const loader = new CustomRewriterLoader({ baseDir: process.cwd() })
|
|
28
|
+
* const customRewriters = await loader.loadRewriters()
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare class CustomRewriterLoader {
|
|
32
|
+
private baseDir;
|
|
33
|
+
private patterns;
|
|
34
|
+
private silent;
|
|
35
|
+
constructor(options?: CustomRewriterLoaderOptions);
|
|
36
|
+
/**
|
|
37
|
+
* Discovers custom rewriter files in the project
|
|
38
|
+
*/
|
|
39
|
+
discoverRewriterFiles(): Promise<string[]>;
|
|
40
|
+
/**
|
|
41
|
+
* Loads a single rewriter file
|
|
42
|
+
*/
|
|
43
|
+
loadRewriterFile(filePath: string): Promise<RewriterClass[]>;
|
|
44
|
+
/**
|
|
45
|
+
* Loads all custom rewriters from the project
|
|
46
|
+
*/
|
|
47
|
+
loadRewriters(): Promise<RewriterClass[]>;
|
|
48
|
+
/**
|
|
49
|
+
* Loads all custom rewriters and returns detailed information about each rewriter
|
|
50
|
+
*/
|
|
51
|
+
loadRewritersWithInfo(): Promise<{
|
|
52
|
+
rewriters: RewriterClass[];
|
|
53
|
+
rewriterInfo: Array<{
|
|
54
|
+
name: string;
|
|
55
|
+
path: string;
|
|
56
|
+
}>;
|
|
57
|
+
duplicateWarnings: string[];
|
|
58
|
+
}>;
|
|
59
|
+
/**
|
|
60
|
+
* Static helper to check if custom rewriters exist in a project
|
|
61
|
+
*/
|
|
62
|
+
static hasCustomRewriters(baseDir?: string): Promise<boolean>;
|
|
63
|
+
/**
|
|
64
|
+
* Static helper to load custom rewriters and merge with built-in rewriters
|
|
65
|
+
*/
|
|
66
|
+
static loadAndMergeRewriters(builtinRewriters: RewriterClass[], options?: CustomRewriterLoaderOptions): Promise<RewriterClass[]>;
|
|
67
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { ASTRewriter } from "./ast-rewriter.js";
|
|
2
|
+
export { StringRewriter } from "./string-rewriter.js";
|
|
3
|
+
export { asMutable } from "./mutable.js";
|
|
4
|
+
export { isASTRewriterClass, isStringRewriterClass, isRewriterClass } from "./type-guards.js";
|
|
5
|
+
export { rewrite, rewriteString } from "./rewrite.js";
|
|
6
|
+
export type { RewriteContext } from "./context.js";
|
|
7
|
+
export type { Mutable } from "./mutable.js";
|
|
8
|
+
export type { RewriterClass } from "./type-guards.js";
|
|
9
|
+
export type { Rewriter, RewriteOptions, RewriteResult } from "./rewrite.js";
|
|
10
|
+
export type { TailwindClassSorterOptions } from "./rewriter-factories.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * from "./index.js";
|
|
2
|
+
export { CustomRewriterLoader } from "./custom-rewriter-loader.js";
|
|
3
|
+
export type { CustomRewriterLoaderOptions } from "./custom-rewriter-loader.js";
|
|
4
|
+
export { TailwindClassSorterRewriter } from "./built-ins/index.js";
|
|
5
|
+
export { tailwindClassSorter } from "./rewriter-factories.js";
|
|
6
|
+
export { builtinRewriters, getBuiltinRewriter, getBuiltinRewriterNames } from "./built-ins/index.js";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility type for making readonly properties mutable
|
|
3
|
+
*
|
|
4
|
+
* This is useful when you need to modify AST nodes which typically have
|
|
5
|
+
* readonly properties. Use sparingly and only in rewriter contexts.
|
|
6
|
+
*/
|
|
7
|
+
export type Mutable<T> = T extends ReadonlyArray<infer U> ? Array<Mutable<U>> : T extends object ? {
|
|
8
|
+
-readonly [K in keyof T]: Mutable<T[K]>;
|
|
9
|
+
} : T;
|
|
10
|
+
/**
|
|
11
|
+
* Cast a readonly value to a mutable version
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* const literalNode = asMutable(node)
|
|
15
|
+
* literalNode.content = "new value"
|
|
16
|
+
*/
|
|
17
|
+
export declare function asMutable<T>(node: T): Mutable<T>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { ASTRewriter } from "./ast-rewriter.js";
|
|
2
|
+
import { StringRewriter } from "./string-rewriter.js";
|
|
3
|
+
import type { HerbBackend, Node } from "@herb-tools/core";
|
|
4
|
+
export type Rewriter = ASTRewriter | StringRewriter;
|
|
5
|
+
export interface RewriteOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Base directory for resolving configuration files
|
|
8
|
+
* Defaults to process.cwd()
|
|
9
|
+
*/
|
|
10
|
+
baseDir?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Optional file path for context
|
|
13
|
+
*/
|
|
14
|
+
filePath?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface RewriteResult {
|
|
17
|
+
/**
|
|
18
|
+
* The rewritten template string
|
|
19
|
+
*/
|
|
20
|
+
output: string;
|
|
21
|
+
/**
|
|
22
|
+
* The rewritten AST node
|
|
23
|
+
*/
|
|
24
|
+
node: Node;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Rewrite an AST Node using the provided rewriters
|
|
28
|
+
*
|
|
29
|
+
* This is the main rewrite function that operates on AST nodes.
|
|
30
|
+
* For string input, use `rewriteString()` instead.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* import { Herb } from '@herb-tools/node-wasm'
|
|
35
|
+
* import { rewrite } from '@herb-tools/rewriter'
|
|
36
|
+
* import { tailwindClassSorter } from '@herb-tools/rewriter/loader'
|
|
37
|
+
*
|
|
38
|
+
* await Herb.load()
|
|
39
|
+
*
|
|
40
|
+
* const template = '<div class="text-red-500 p-4 mt-2"></div>'
|
|
41
|
+
* const parseResult = Herb.parse(template)
|
|
42
|
+
* const { output, node } = rewrite(parseResult.value, [tailwindClassSorter()])
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* @param node - The AST Node to rewrite
|
|
46
|
+
* @param rewriters - Array of rewriter instances to apply
|
|
47
|
+
* @param options - Optional configuration for the rewrite operation
|
|
48
|
+
* @returns Object containing the rewritten string and Node
|
|
49
|
+
*/
|
|
50
|
+
export declare function rewrite<T extends Node>(node: T, rewriters: Rewriter[], options?: RewriteOptions): RewriteResult & {
|
|
51
|
+
node: T;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Rewrite an HTML+ERB template string
|
|
55
|
+
*
|
|
56
|
+
* Convenience wrapper around `rewrite()` that parses the string first.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* import { Herb } from '@herb-tools/node-wasm'
|
|
61
|
+
* import { rewriteString } from '@herb-tools/rewriter'
|
|
62
|
+
* import { tailwindClassSorter } from '@herb-tools/rewriter/loader'
|
|
63
|
+
*
|
|
64
|
+
* await Herb.load()
|
|
65
|
+
*
|
|
66
|
+
* const template = '<div class="text-red-500 p-4 mt-2"></div>'
|
|
67
|
+
* const output = rewriteString(Herb, template, [tailwindClassSorter()])
|
|
68
|
+
* // output: '<div class="mt-2 p-4 text-red-500"></div>'
|
|
69
|
+
* ```
|
|
70
|
+
*
|
|
71
|
+
* @param herb - The Herb backend instance for parsing
|
|
72
|
+
* @param template - The HTML+ERB template string to rewrite
|
|
73
|
+
* @param rewriters - Array of rewriter instances to apply
|
|
74
|
+
* @param options - Optional configuration for the rewrite operation
|
|
75
|
+
* @returns The rewritten template string
|
|
76
|
+
*/
|
|
77
|
+
export declare function rewriteString(herb: HerbBackend, template: string, rewriters: Rewriter[], options?: RewriteOptions): string;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { TailwindClassSorterRewriter } from "./built-ins/tailwind-class-sorter.js";
|
|
2
|
+
export interface TailwindClassSorterOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Base directory for resolving Tailwind configuration
|
|
5
|
+
* Defaults to process.cwd()
|
|
6
|
+
*/
|
|
7
|
+
baseDir?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Factory function for creating a Tailwind class sorter rewriter
|
|
11
|
+
*
|
|
12
|
+
* Automatically initializes the rewriter before returning it.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* import { rewrite } from '@herb-tools/rewriter'
|
|
17
|
+
* import { tailwindClassSorter } from '@herb-tools/rewriter/loader'
|
|
18
|
+
*
|
|
19
|
+
* const template = '<div class="text-red-500 p-4 mt-2"></div>'
|
|
20
|
+
* const sorter = await tailwindClassSorter()
|
|
21
|
+
* const result = rewrite(template, [sorter])
|
|
22
|
+
* // Result: '<div class="mt-2 p-4 text-red-500"></div>'
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* @param options - Optional configuration for the Tailwind class sorter
|
|
26
|
+
* @returns A configured and initialized TailwindClassSorterRewriter instance
|
|
27
|
+
*/
|
|
28
|
+
export declare function tailwindClassSorter(options?: TailwindClassSorterOptions): Promise<TailwindClassSorterRewriter>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { RewriteContext } from "./context.js";
|
|
2
|
+
/**
|
|
3
|
+
* Base class for string rewriters that transform the formatted output
|
|
4
|
+
*
|
|
5
|
+
* String rewriters receive the formatted string and can modify it before
|
|
6
|
+
* returning the final output. They run after the formatting step.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { StringRewriter } from "@herb-tools/rewriter"
|
|
11
|
+
*
|
|
12
|
+
* class AddTrailingNewline extends StringRewriter {
|
|
13
|
+
* get name() { return "add-trailing-newline" }
|
|
14
|
+
* get description() { return "Ensures file ends with a newline" }
|
|
15
|
+
*
|
|
16
|
+
* rewrite(formatted, context) {
|
|
17
|
+
* return formatted.endsWith("\n") ? formatted : formatted + "\n"
|
|
18
|
+
* }
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare abstract class StringRewriter {
|
|
23
|
+
/**
|
|
24
|
+
* Unique identifier for this rewriter
|
|
25
|
+
* Used in configuration and error messages
|
|
26
|
+
*/
|
|
27
|
+
abstract get name(): string;
|
|
28
|
+
/**
|
|
29
|
+
* Human-readable description of what this rewriter does
|
|
30
|
+
*/
|
|
31
|
+
abstract get description(): string;
|
|
32
|
+
/**
|
|
33
|
+
* Optional async initialization hook
|
|
34
|
+
*
|
|
35
|
+
* Called once before the first rewrite operation. Use this to:
|
|
36
|
+
* - Load configuration files
|
|
37
|
+
* - Initialize dependencies
|
|
38
|
+
* - Perform expensive setup operations
|
|
39
|
+
*
|
|
40
|
+
* @param context - Context with baseDir and optional filePath
|
|
41
|
+
*/
|
|
42
|
+
initialize(_context: RewriteContext): Promise<void>;
|
|
43
|
+
/**
|
|
44
|
+
* Transform the formatted string
|
|
45
|
+
*
|
|
46
|
+
* This method is called synchronously for each file being formatted.
|
|
47
|
+
* Return the modified string.
|
|
48
|
+
*
|
|
49
|
+
* @param formatted - The formatted string output from the formatter
|
|
50
|
+
* @param context - Context with filePath and baseDir
|
|
51
|
+
* @returns The modified string
|
|
52
|
+
*/
|
|
53
|
+
abstract rewrite(formatted: string, context: RewriteContext): string;
|
|
54
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ASTRewriter } from "./ast-rewriter.js";
|
|
2
|
+
import { StringRewriter } from "./string-rewriter.js";
|
|
3
|
+
/**
|
|
4
|
+
* Type guard to check if a class is an ASTRewriter
|
|
5
|
+
* Uses duck typing to work across module boundaries
|
|
6
|
+
*/
|
|
7
|
+
export declare function isASTRewriterClass(obj: any): obj is new () => ASTRewriter;
|
|
8
|
+
/**
|
|
9
|
+
* Type guard to check if a class is a StringRewriter
|
|
10
|
+
* Uses duck typing to work across module boundaries
|
|
11
|
+
*/
|
|
12
|
+
export declare function isStringRewriterClass(obj: any): obj is new () => StringRewriter;
|
|
13
|
+
/**
|
|
14
|
+
* Union type for all rewriter classes
|
|
15
|
+
*/
|
|
16
|
+
export type RewriterClass = (new () => ASTRewriter) | (new () => StringRewriter);
|
|
17
|
+
/**
|
|
18
|
+
* Type guard to check if a class is any kind of rewriter
|
|
19
|
+
*/
|
|
20
|
+
export declare function isRewriterClass(obj: any): obj is RewriterClass;
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@herb-tools/rewriter",
|
|
3
|
+
"version": "0.8.0",
|
|
4
|
+
"description": "Rewriter system for transforming HTML+ERB AST nodes and formatted strings",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://herb-tools.dev",
|
|
7
|
+
"bugs": "https://github.com/marcoroth/herb/issues/new?title=Package%20%60@herb-tools/rewriter%60:%20",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/marcoroth/herb.git",
|
|
11
|
+
"directory": "javascript/packages/rewriter"
|
|
12
|
+
},
|
|
13
|
+
"main": "./dist/index.cjs",
|
|
14
|
+
"module": "./dist/index.esm.js",
|
|
15
|
+
"require": "./dist/index.cjs",
|
|
16
|
+
"types": "./dist/types/index.d.ts",
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "yarn clean && rollup -c rollup.config.mjs",
|
|
19
|
+
"dev": "rollup -c rollup.config.mjs -w",
|
|
20
|
+
"clean": "rimraf dist",
|
|
21
|
+
"test": "vitest run",
|
|
22
|
+
"test:watch": "vitest --watch",
|
|
23
|
+
"prepublishOnly": "yarn clean && yarn build && yarn test"
|
|
24
|
+
},
|
|
25
|
+
"exports": {
|
|
26
|
+
"./package.json": "./package.json",
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./dist/types/index.d.ts",
|
|
29
|
+
"import": "./dist/index.esm.js",
|
|
30
|
+
"require": "./dist/index.cjs",
|
|
31
|
+
"default": "./dist/index.esm.js"
|
|
32
|
+
},
|
|
33
|
+
"./loader": {
|
|
34
|
+
"types": "./dist/types/loader.d.ts",
|
|
35
|
+
"import": "./dist/loader.esm.js",
|
|
36
|
+
"require": "./dist/loader.cjs",
|
|
37
|
+
"default": "./dist/loader.esm.js"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@herb-tools/core": "0.8.0",
|
|
42
|
+
"@herb-tools/tailwind-class-sorter": "0.8.0",
|
|
43
|
+
"glob": "^11.0.3"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@herb-tools/printer": "0.8.0"
|
|
47
|
+
},
|
|
48
|
+
"files": [
|
|
49
|
+
"package.json",
|
|
50
|
+
"README.md",
|
|
51
|
+
"dist/",
|
|
52
|
+
"src/"
|
|
53
|
+
]
|
|
54
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { Node } from "@herb-tools/core"
|
|
2
|
+
import type { RewriteContext } from "./context.js"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Base class for AST rewriters that transform AST nodes before formatting
|
|
6
|
+
*
|
|
7
|
+
* AST rewriters receive a Node and can mutate it in place or return a modified Node.
|
|
8
|
+
* They run before the formatting step.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { ASTRewriter, asMutable } from "@herb-tools/rewriter"
|
|
13
|
+
* import { Visitor } from "@herb-tools/core"
|
|
14
|
+
*
|
|
15
|
+
* class MyRewriter extends ASTRewriter {
|
|
16
|
+
* get name() { return "my-rewriter" }
|
|
17
|
+
* get description() { return "My custom AST rewriter" }
|
|
18
|
+
*
|
|
19
|
+
* async initialize(context) {
|
|
20
|
+
* // Load config, initialize dependencies, etc.
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* rewrite(node, context) {
|
|
24
|
+
* // Use visitor pattern to traverse and modify AST
|
|
25
|
+
* const visitor = new MyVisitor()
|
|
26
|
+
* visitor.visit(node)
|
|
27
|
+
*
|
|
28
|
+
* return node
|
|
29
|
+
* }
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export abstract class ASTRewriter {
|
|
34
|
+
/**
|
|
35
|
+
* Unique identifier for this rewriter
|
|
36
|
+
* Used in configuration and error messages
|
|
37
|
+
*/
|
|
38
|
+
abstract get name(): string
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Human-readable description of what this rewriter does
|
|
42
|
+
*/
|
|
43
|
+
abstract get description(): string
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Optional async initialization hook
|
|
47
|
+
*
|
|
48
|
+
* Called once before the first rewrite operation. Use this to:
|
|
49
|
+
* - Load configuration files
|
|
50
|
+
* - Initialize dependencies
|
|
51
|
+
* - Perform expensive setup operations
|
|
52
|
+
*
|
|
53
|
+
* @param context - Context with baseDir and optional filePath
|
|
54
|
+
*/
|
|
55
|
+
async initialize(_context: RewriteContext): Promise<void> {
|
|
56
|
+
// Override in subclass if needed
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Transform the AST node
|
|
61
|
+
*
|
|
62
|
+
* This method is called synchronously for each file being formatted.
|
|
63
|
+
* Modify the AST in place or return a new Node.
|
|
64
|
+
*
|
|
65
|
+
* @param node - The AST node from @herb-tools/core
|
|
66
|
+
* @param context - Context with filePath and baseDir
|
|
67
|
+
* @returns The modified Node (can be the same object mutated in place)
|
|
68
|
+
*/
|
|
69
|
+
abstract rewrite<T extends Node>(node: T, context: RewriteContext): T
|
|
70
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { RewriterClass } from "../type-guards.js"
|
|
2
|
+
|
|
3
|
+
export { TailwindClassSorterRewriter } from "./tailwind-class-sorter.js"
|
|
4
|
+
import { TailwindClassSorterRewriter } from "./tailwind-class-sorter.js"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* All built-in rewriters available in the package
|
|
8
|
+
*/
|
|
9
|
+
export const builtinRewriters: RewriterClass[] = [
|
|
10
|
+
TailwindClassSorterRewriter
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Get a built-in rewriter by name
|
|
15
|
+
*/
|
|
16
|
+
export function getBuiltinRewriter(name: string): RewriterClass | undefined {
|
|
17
|
+
return builtinRewriters.find(RewriterClass => {
|
|
18
|
+
const instance = new RewriterClass()
|
|
19
|
+
|
|
20
|
+
return instance.name === name
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Get all built-in rewriter names
|
|
26
|
+
*/
|
|
27
|
+
export function getBuiltinRewriterNames(): string[] {
|
|
28
|
+
return builtinRewriters.map(RewriterClass => {
|
|
29
|
+
const instance = new RewriterClass()
|
|
30
|
+
|
|
31
|
+
return instance.name
|
|
32
|
+
})
|
|
33
|
+
}
|