@herb-tools/rewriter 0.9.3 → 0.9.4
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/dist/index.cjs +238 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +238 -5
- package/dist/index.esm.js.map +1 -1
- package/dist/loader.cjs +239 -4
- package/dist/loader.cjs.map +1 -1
- package/dist/loader.esm.js +239 -5
- package/dist/loader.esm.js.map +1 -1
- package/dist/types/built-ins/erb-string-to-direct-output.d.ts +21 -0
- package/dist/types/built-ins/index.d.ts +2 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/rewriter-factories.d.ts +2 -0
- package/package.json +4 -4
- package/src/built-ins/erb-string-to-direct-output.ts +176 -0
- package/src/built-ins/index.ts +5 -0
- package/src/index.ts +2 -0
- package/src/rewriter-factories.ts +5 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ASTRewriter } from "../ast-rewriter.js";
|
|
2
|
+
import type { RewriteContext } from "../context.js";
|
|
3
|
+
import type { Node, PrismNode } from "@herb-tools/core";
|
|
4
|
+
export interface TextPart {
|
|
5
|
+
type: "text";
|
|
6
|
+
content: string;
|
|
7
|
+
}
|
|
8
|
+
export interface ExpressionPart {
|
|
9
|
+
type: "expression";
|
|
10
|
+
expression: string;
|
|
11
|
+
}
|
|
12
|
+
export type ReplacementPart = TextPart | ExpressionPart;
|
|
13
|
+
export declare class ERBStringToDirectOutputRewriter extends ASTRewriter {
|
|
14
|
+
get name(): string;
|
|
15
|
+
get description(): string;
|
|
16
|
+
rewrite<T extends Node>(node: T, _context: RewriteContext): T;
|
|
17
|
+
static isStringOutputNode(prismNode: PrismNode): boolean;
|
|
18
|
+
static extractStringContent(stringNode: PrismNode, source: string): string;
|
|
19
|
+
static extractExpressionSource(embeddedNode: PrismNode, source: string): string | null;
|
|
20
|
+
static extractReplacementParts(prismNode: PrismNode, source: string): ReplacementPart[] | null;
|
|
21
|
+
}
|
|
@@ -12,5 +12,7 @@ export declare function getBuiltinRewriter(name: string): RewriterClass | undefi
|
|
|
12
12
|
*/
|
|
13
13
|
export declare function getBuiltinRewriterNames(): string[];
|
|
14
14
|
export { ActionViewTagHelperToHTMLRewriter } from "./action-view-tag-helper-to-html.js";
|
|
15
|
+
export { ERBStringToDirectOutputRewriter } from "./erb-string-to-direct-output.js";
|
|
15
16
|
export { HTMLToActionViewTagHelperRewriter } from "./html-to-action-view-tag-helper.js";
|
|
16
17
|
export { TailwindClassSorterRewriter } from "./tailwind-class-sorter.js";
|
|
18
|
+
export type { TextPart, ExpressionPart, ReplacementPart } from "./erb-string-to-direct-output.js";
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { ASTRewriter } from "./ast-rewriter.js";
|
|
2
2
|
export { ActionViewTagHelperToHTMLRewriter } from "./built-ins/action-view-tag-helper-to-html.js";
|
|
3
|
+
export { ERBStringToDirectOutputRewriter } from "./built-ins/erb-string-to-direct-output.js";
|
|
3
4
|
export { HTMLToActionViewTagHelperRewriter } from "./built-ins/html-to-action-view-tag-helper.js";
|
|
4
5
|
export { StringRewriter } from "./string-rewriter.js";
|
|
5
6
|
export { asMutable } from "./mutable.js";
|
|
@@ -10,3 +11,4 @@ export type { Mutable } from "./mutable.js";
|
|
|
10
11
|
export type { RewriterClass } from "./type-guards.js";
|
|
11
12
|
export type { Rewriter, RewriteOptions, RewriteResult } from "./rewrite.js";
|
|
12
13
|
export type { TailwindClassSorterOptions } from "./rewriter-factories.js";
|
|
14
|
+
export type { TextPart, ExpressionPart, ReplacementPart } from "./built-ins/erb-string-to-direct-output.js";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { TailwindClassSorterRewriter } from "./built-ins/tailwind-class-sorter.js";
|
|
2
2
|
import { ActionViewTagHelperToHTMLRewriter } from "./built-ins/action-view-tag-helper-to-html.js";
|
|
3
|
+
import { ERBStringToDirectOutputRewriter } from "./built-ins/erb-string-to-direct-output.js";
|
|
3
4
|
import { HTMLToActionViewTagHelperRewriter } from "./built-ins/html-to-action-view-tag-helper.js";
|
|
4
5
|
export interface TailwindClassSorterOptions {
|
|
5
6
|
/**
|
|
@@ -29,4 +30,5 @@ export interface TailwindClassSorterOptions {
|
|
|
29
30
|
*/
|
|
30
31
|
export declare function tailwindClassSorter(options?: TailwindClassSorterOptions): Promise<TailwindClassSorterRewriter>;
|
|
31
32
|
export declare function actionViewTagHelperToHTML(): ActionViewTagHelperToHTMLRewriter;
|
|
33
|
+
export declare function erbStringToDirectOutput(): ERBStringToDirectOutputRewriter;
|
|
32
34
|
export declare function htmlToActionViewTagHelper(): HTMLToActionViewTagHelperRewriter;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@herb-tools/rewriter",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.4",
|
|
4
4
|
"description": "Rewriter system for transforming HTML+ERB AST nodes and formatted strings",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://herb-tools.dev",
|
|
@@ -38,12 +38,12 @@
|
|
|
38
38
|
}
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@herb-tools/core": "0.9.
|
|
42
|
-
"@herb-tools/tailwind-class-sorter": "0.9.
|
|
41
|
+
"@herb-tools/core": "0.9.4",
|
|
42
|
+
"@herb-tools/tailwind-class-sorter": "0.9.4",
|
|
43
43
|
"tinyglobby": "^0.2.15"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@herb-tools/printer": "0.9.
|
|
46
|
+
"@herb-tools/printer": "0.9.4"
|
|
47
47
|
},
|
|
48
48
|
"files": [
|
|
49
49
|
"package.json",
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { Visitor, isERBOutputNode, createLiteral, createERBOutputNode, findParentArray } from "@herb-tools/core"
|
|
2
|
+
|
|
3
|
+
import { ASTRewriter } from "../ast-rewriter.js"
|
|
4
|
+
|
|
5
|
+
import type { RewriteContext } from "../context.js"
|
|
6
|
+
import type { Node, ERBContentNode, PrismNode } from "@herb-tools/core"
|
|
7
|
+
|
|
8
|
+
const STRING_NODE_TYPE = "StringNode"
|
|
9
|
+
const INTERPOLATED_STRING_NODE_TYPE = "InterpolatedStringNode"
|
|
10
|
+
const EMBEDDED_STATEMENTS_NODE_TYPE = "EmbeddedStatementsNode"
|
|
11
|
+
|
|
12
|
+
export interface TextPart {
|
|
13
|
+
type: "text"
|
|
14
|
+
content: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface ExpressionPart {
|
|
18
|
+
type: "expression"
|
|
19
|
+
expression: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type ReplacementPart = TextPart | ExpressionPart
|
|
23
|
+
|
|
24
|
+
class ERBStringToDirectOutputVisitor extends Visitor {
|
|
25
|
+
private root: Node
|
|
26
|
+
|
|
27
|
+
constructor(root: Node) {
|
|
28
|
+
super()
|
|
29
|
+
this.root = root
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
visitERBContentNode(node: ERBContentNode): void {
|
|
33
|
+
if (!isERBOutputNode(node)) {
|
|
34
|
+
this.visitChildNodes(node)
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const prismNode = node.prismNode
|
|
39
|
+
if (!prismNode) {
|
|
40
|
+
this.visitChildNodes(node)
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const source = node.source
|
|
45
|
+
if (!source) {
|
|
46
|
+
this.visitChildNodes(node)
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!ERBStringToDirectOutputRewriter.isStringOutputNode(prismNode)) {
|
|
51
|
+
this.visitChildNodes(node)
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const replacementParts = ERBStringToDirectOutputRewriter.extractReplacementParts(prismNode, source)
|
|
56
|
+
|
|
57
|
+
if (!replacementParts) {
|
|
58
|
+
this.visitChildNodes(node)
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const tagOpening = node.tag_opening?.value ?? "<%="
|
|
63
|
+
const tagClosing = node.tag_closing?.value ?? "%>"
|
|
64
|
+
|
|
65
|
+
const parentInfo = findParentArray(this.root, node)
|
|
66
|
+
|
|
67
|
+
if (!parentInfo) {
|
|
68
|
+
this.visitChildNodes(node)
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const { array: parentArray, index: nodeIndex } = parentInfo
|
|
73
|
+
const replacementNodes: Node[] = []
|
|
74
|
+
|
|
75
|
+
for (const part of replacementParts) {
|
|
76
|
+
if (part.type === "text") {
|
|
77
|
+
replacementNodes.push(createLiteral(part.content))
|
|
78
|
+
} else {
|
|
79
|
+
replacementNodes.push(createERBOutputNode(` ${part.expression.trim()} `, tagOpening, tagClosing))
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
parentArray.splice(nodeIndex, 1, ...replacementNodes)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export class ERBStringToDirectOutputRewriter extends ASTRewriter {
|
|
88
|
+
get name(): string {
|
|
89
|
+
return "erb-string-to-direct-output"
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
get description(): string {
|
|
93
|
+
return "Replaces ERB string output with direct text and expression tags"
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
rewrite<T extends Node>(node: T, _context: RewriteContext): T {
|
|
97
|
+
const visitor = new ERBStringToDirectOutputVisitor(node)
|
|
98
|
+
|
|
99
|
+
visitor.visit(node)
|
|
100
|
+
|
|
101
|
+
return node
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
static isStringOutputNode(prismNode: PrismNode): boolean {
|
|
105
|
+
const nodeType = prismNode.constructor.name
|
|
106
|
+
|
|
107
|
+
return nodeType === STRING_NODE_TYPE || nodeType === INTERPOLATED_STRING_NODE_TYPE
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
static extractStringContent(stringNode: PrismNode, source: string): string {
|
|
111
|
+
const unescapedValue = stringNode.unescaped?.value
|
|
112
|
+
|
|
113
|
+
if (typeof unescapedValue === "string") {
|
|
114
|
+
return unescapedValue
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const location = stringNode.contentLoc
|
|
118
|
+
|
|
119
|
+
if (location) {
|
|
120
|
+
return source.substring(location.startOffset, location.startOffset + location.length)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return ""
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
static extractExpressionSource(embeddedNode: PrismNode, source: string): string | null {
|
|
127
|
+
const openingLocation = embeddedNode.openingLoc
|
|
128
|
+
const closingLocation = embeddedNode.closingLoc
|
|
129
|
+
|
|
130
|
+
if (!openingLocation || !closingLocation) return null
|
|
131
|
+
|
|
132
|
+
const expressionStart = openingLocation.startOffset + openingLocation.length
|
|
133
|
+
const expressionEnd = closingLocation.startOffset
|
|
134
|
+
|
|
135
|
+
return source.substring(expressionStart, expressionEnd)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
static extractReplacementParts(prismNode: PrismNode, source: string): ReplacementPart[] | null {
|
|
139
|
+
const nodeType = prismNode.constructor.name
|
|
140
|
+
|
|
141
|
+
if (nodeType === STRING_NODE_TYPE) {
|
|
142
|
+
const textContent = this.extractStringContent(prismNode, source)
|
|
143
|
+
return [{ type: "text", content: textContent }]
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (nodeType === INTERPOLATED_STRING_NODE_TYPE) {
|
|
147
|
+
const parts = prismNode.parts
|
|
148
|
+
|
|
149
|
+
if (!parts || parts.length === 0) return null
|
|
150
|
+
|
|
151
|
+
const replacementParts: ReplacementPart[] = []
|
|
152
|
+
|
|
153
|
+
for (const part of parts) {
|
|
154
|
+
const partType = part.constructor.name
|
|
155
|
+
|
|
156
|
+
if (partType === STRING_NODE_TYPE) {
|
|
157
|
+
const textContent = this.extractStringContent(part, source)
|
|
158
|
+
|
|
159
|
+
if (textContent) {
|
|
160
|
+
replacementParts.push({ type: "text", content: textContent })
|
|
161
|
+
}
|
|
162
|
+
} else if (partType === EMBEDDED_STATEMENTS_NODE_TYPE) {
|
|
163
|
+
const expression = this.extractExpressionSource(part, source)
|
|
164
|
+
|
|
165
|
+
if (expression) {
|
|
166
|
+
replacementParts.push({ type: "expression", expression })
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return replacementParts.length > 0 ? replacementParts : null
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return null
|
|
175
|
+
}
|
|
176
|
+
}
|
package/src/built-ins/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ActionViewTagHelperToHTMLRewriter } from "./action-view-tag-helper-to-html.js"
|
|
2
|
+
import { ERBStringToDirectOutputRewriter } from "./erb-string-to-direct-output.js"
|
|
2
3
|
import { HTMLToActionViewTagHelperRewriter } from "./html-to-action-view-tag-helper.js"
|
|
3
4
|
import { TailwindClassSorterRewriter } from "./tailwind-class-sorter.js"
|
|
4
5
|
|
|
@@ -9,6 +10,7 @@ import type { RewriterClass } from "../type-guards.js"
|
|
|
9
10
|
*/
|
|
10
11
|
export const builtinRewriters: RewriterClass[] = [
|
|
11
12
|
ActionViewTagHelperToHTMLRewriter,
|
|
13
|
+
ERBStringToDirectOutputRewriter,
|
|
12
14
|
HTMLToActionViewTagHelperRewriter,
|
|
13
15
|
TailwindClassSorterRewriter
|
|
14
16
|
]
|
|
@@ -36,5 +38,8 @@ export function getBuiltinRewriterNames(): string[] {
|
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
export { ActionViewTagHelperToHTMLRewriter } from "./action-view-tag-helper-to-html.js"
|
|
41
|
+
export { ERBStringToDirectOutputRewriter } from "./erb-string-to-direct-output.js"
|
|
39
42
|
export { HTMLToActionViewTagHelperRewriter } from "./html-to-action-view-tag-helper.js"
|
|
40
43
|
export { TailwindClassSorterRewriter } from "./tailwind-class-sorter.js"
|
|
44
|
+
|
|
45
|
+
export type { TextPart, ExpressionPart, ReplacementPart } from "./erb-string-to-direct-output.js"
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { ASTRewriter } from "./ast-rewriter.js"
|
|
2
2
|
export { ActionViewTagHelperToHTMLRewriter } from "./built-ins/action-view-tag-helper-to-html.js"
|
|
3
|
+
export { ERBStringToDirectOutputRewriter } from "./built-ins/erb-string-to-direct-output.js"
|
|
3
4
|
export { HTMLToActionViewTagHelperRewriter } from "./built-ins/html-to-action-view-tag-helper.js"
|
|
4
5
|
export { StringRewriter } from "./string-rewriter.js"
|
|
5
6
|
|
|
@@ -13,3 +14,4 @@ export type { Mutable } from "./mutable.js"
|
|
|
13
14
|
export type { RewriterClass } from "./type-guards.js"
|
|
14
15
|
export type { Rewriter, RewriteOptions, RewriteResult } from "./rewrite.js"
|
|
15
16
|
export type { TailwindClassSorterOptions } from "./rewriter-factories.js"
|
|
17
|
+
export type { TextPart, ExpressionPart, ReplacementPart } from "./built-ins/erb-string-to-direct-output.js"
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { TailwindClassSorterRewriter } from "./built-ins/tailwind-class-sorter.js"
|
|
2
2
|
import { ActionViewTagHelperToHTMLRewriter } from "./built-ins/action-view-tag-helper-to-html.js"
|
|
3
|
+
import { ERBStringToDirectOutputRewriter } from "./built-ins/erb-string-to-direct-output.js"
|
|
3
4
|
import { HTMLToActionViewTagHelperRewriter } from "./built-ins/html-to-action-view-tag-helper.js"
|
|
4
5
|
|
|
5
6
|
export interface TailwindClassSorterOptions {
|
|
@@ -42,6 +43,10 @@ export function actionViewTagHelperToHTML(): ActionViewTagHelperToHTMLRewriter {
|
|
|
42
43
|
return new ActionViewTagHelperToHTMLRewriter()
|
|
43
44
|
}
|
|
44
45
|
|
|
46
|
+
export function erbStringToDirectOutput(): ERBStringToDirectOutputRewriter {
|
|
47
|
+
return new ERBStringToDirectOutputRewriter()
|
|
48
|
+
}
|
|
49
|
+
|
|
45
50
|
export function htmlToActionViewTagHelper(): HTMLToActionViewTagHelperRewriter {
|
|
46
51
|
return new HTMLToActionViewTagHelperRewriter()
|
|
47
52
|
}
|