@herb-tools/rewriter 0.9.2 → 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 +808 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +808 -17
- package/dist/index.esm.js.map +1 -1
- package/dist/loader.cjs +809 -16
- package/dist/loader.cjs.map +1 -1
- package/dist/loader.esm.js +809 -17
- package/dist/loader.esm.js.map +1 -1
- package/dist/types/built-ins/action-view-tag-helper-to-html.d.ts +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/action-view-tag-helper-to-html.ts +54 -10
- package/src/built-ins/erb-string-to-direct-output.ts +176 -0
- package/src/built-ins/html-to-action-view-tag-helper.ts +19 -4
- package/src/built-ins/index.ts +5 -0
- package/src/index.ts +2 -0
- package/src/rewriter-factories.ts +5 -0
|
@@ -4,5 +4,5 @@ import type { Node } from "@herb-tools/core";
|
|
|
4
4
|
export declare class ActionViewTagHelperToHTMLRewriter extends ASTRewriter {
|
|
5
5
|
get name(): string;
|
|
6
6
|
get description(): string;
|
|
7
|
-
rewrite<T extends Node>(node: T,
|
|
7
|
+
rewrite<T extends Node>(node: T, context: RewriteContext): T;
|
|
8
8
|
}
|
|
@@ -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",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Visitor, Location, HTMLOpenTagNode, HTMLCloseTagNode, HTMLElementNode, HTMLAttributeValueNode, WhitespaceNode, ERBContentNode } from "@herb-tools/core"
|
|
2
|
-
import { isHTMLAttributeNode, isERBOpenTagNode, isRubyLiteralNode, isRubyHTMLAttributesSplatNode, createSyntheticToken } from "@herb-tools/core"
|
|
2
|
+
import { isHTMLAttributeNode, isERBOpenTagNode, isRubyLiteralNode, isRubyHTMLAttributesSplatNode, isWhitespaceNode, createSyntheticToken } from "@herb-tools/core"
|
|
3
3
|
|
|
4
4
|
import { ASTRewriter } from "../ast-rewriter.js"
|
|
5
5
|
import { asMutable } from "../mutable.js"
|
|
@@ -17,6 +17,45 @@ function createWhitespaceNode(): WhitespaceNode {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
class ActionViewTagHelperToHTMLVisitor extends Visitor {
|
|
20
|
+
private shallow: boolean
|
|
21
|
+
private includeBody: boolean
|
|
22
|
+
|
|
23
|
+
constructor(options: { shallow?: boolean; includeBody?: boolean } = {}) {
|
|
24
|
+
super()
|
|
25
|
+
this.shallow = options.shallow ?? false
|
|
26
|
+
this.includeBody = options.includeBody ?? true
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
visitHTMLOpenTagNode(node: HTMLOpenTagNode): void {
|
|
30
|
+
const newChildren: Node[] = []
|
|
31
|
+
|
|
32
|
+
for (let index = 0; index < node.children.length; index++) {
|
|
33
|
+
const child = node.children[index]
|
|
34
|
+
|
|
35
|
+
if (isHTMLAttributeNode(child)) {
|
|
36
|
+
if (child.equals && child.equals.value !== "=") {
|
|
37
|
+
asMutable(child).equals = createSyntheticToken("=")
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (child.value) {
|
|
41
|
+
this.transformAttributeValue(child.value)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const previous = index > 0 ? node.children[index - 1] : null
|
|
45
|
+
|
|
46
|
+
if (!previous || !isWhitespaceNode(previous)) {
|
|
47
|
+
newChildren.push(createWhitespaceNode())
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
newChildren.push(child)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
asMutable(node).children = newChildren
|
|
55
|
+
|
|
56
|
+
this.visitChildNodes(node)
|
|
57
|
+
}
|
|
58
|
+
|
|
20
59
|
visitHTMLElementNode(node: HTMLElementNode): void {
|
|
21
60
|
if (!node.element_source) {
|
|
22
61
|
this.visitChildNodes(node)
|
|
@@ -104,7 +143,9 @@ class ActionViewTagHelperToHTMLVisitor extends Visitor {
|
|
|
104
143
|
|
|
105
144
|
asMutable(node).element_source = "HTML"
|
|
106
145
|
|
|
107
|
-
if (
|
|
146
|
+
if (!this.includeBody) {
|
|
147
|
+
asMutable(node).body = []
|
|
148
|
+
} else if (node.body) {
|
|
108
149
|
asMutable(node).body = node.body.map(child => {
|
|
109
150
|
if (isRubyLiteralNode(child)) {
|
|
110
151
|
return new ERBContentNode({
|
|
@@ -120,7 +161,10 @@ class ActionViewTagHelperToHTMLVisitor extends Visitor {
|
|
|
120
161
|
})
|
|
121
162
|
}
|
|
122
163
|
|
|
123
|
-
this.
|
|
164
|
+
if (!this.shallow) {
|
|
165
|
+
this.visit(child)
|
|
166
|
+
}
|
|
167
|
+
|
|
124
168
|
return child
|
|
125
169
|
})
|
|
126
170
|
}
|
|
@@ -150,12 +194,12 @@ class ActionViewTagHelperToHTMLVisitor extends Visitor {
|
|
|
150
194
|
})
|
|
151
195
|
|
|
152
196
|
mutableValue.children = newChildren
|
|
197
|
+
}
|
|
153
198
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
199
|
+
if (!value.quoted) {
|
|
200
|
+
mutableValue.quoted = true
|
|
201
|
+
mutableValue.open_quote = createSyntheticToken('"')
|
|
202
|
+
mutableValue.close_quote = createSyntheticToken('"')
|
|
159
203
|
}
|
|
160
204
|
}
|
|
161
205
|
}
|
|
@@ -169,8 +213,8 @@ export class ActionViewTagHelperToHTMLRewriter extends ASTRewriter {
|
|
|
169
213
|
return "Converts ActionView tag helpers to raw HTML elements"
|
|
170
214
|
}
|
|
171
215
|
|
|
172
|
-
rewrite<T extends Node>(node: T,
|
|
173
|
-
const visitor = new ActionViewTagHelperToHTMLVisitor()
|
|
216
|
+
rewrite<T extends Node>(node: T, context: RewriteContext): T {
|
|
217
|
+
const visitor = new ActionViewTagHelperToHTMLVisitor({ shallow: context.shallow, includeBody: context.includeBody })
|
|
174
218
|
|
|
175
219
|
visitor.visit(node)
|
|
176
220
|
|
|
@@ -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
|
+
}
|
|
@@ -124,9 +124,10 @@ class HTMLToActionViewTagHelperVisitor extends Visitor {
|
|
|
124
124
|
const isAnchor = tagName.value === "a"
|
|
125
125
|
const isTurboFrame = tagName.value === "turbo-frame"
|
|
126
126
|
const isScript = tagName.value === "script"
|
|
127
|
+
const isImg = tagName.value === "img"
|
|
127
128
|
const attributes = openTag.children.filter(child => !isWhitespaceNode(child))
|
|
128
|
-
const hasSrcAttribute = isScript && attributes.some(child => isHTMLAttributeNode(child) && getStaticAttributeName(child.name!) === "src")
|
|
129
|
-
const { attributes: attributesString, href, id, src } = serializeAttributes(attributes, { extractHref: isAnchor, extractId: isTurboFrame, extractSrc: isScript })
|
|
129
|
+
const hasSrcAttribute = (isScript || isImg) && attributes.some(child => isHTMLAttributeNode(child) && getStaticAttributeName(child.name!) === "src")
|
|
130
|
+
const { attributes: attributesString, href, id, src } = serializeAttributes(attributes, { extractHref: isAnchor, extractId: isTurboFrame, extractSrc: isScript || isImg })
|
|
130
131
|
const hasBody = node.body && node.body.length > 0 && !node.is_void
|
|
131
132
|
const isInlineContent = hasBody && isTextOnlyBody(node.body)
|
|
132
133
|
|
|
@@ -145,6 +146,9 @@ class HTMLToActionViewTagHelperVisitor extends Visitor {
|
|
|
145
146
|
} else if (isScript) {
|
|
146
147
|
content = this.buildJavascriptTagContent(node, attributesString, isInlineContent)
|
|
147
148
|
elementSource = "ActionView::Helpers::JavaScriptHelper#javascript_tag"
|
|
149
|
+
} else if (isImg) {
|
|
150
|
+
content = this.buildImageTagContent(attributesString, src)
|
|
151
|
+
elementSource = "ActionView::Helpers::AssetTagHelper#image_tag"
|
|
148
152
|
} else {
|
|
149
153
|
content = this.buildTagContent(tagName.value, node, attributesString, isInlineContent)
|
|
150
154
|
elementSource = "ActionView::Helpers::TagHelper#tag"
|
|
@@ -165,7 +169,7 @@ class HTMLToActionViewTagHelperVisitor extends Visitor {
|
|
|
165
169
|
asMutable(node).element_source = elementSource
|
|
166
170
|
|
|
167
171
|
const isInlineLiteralContent = isScript && hasBody && node.body.length === 1 && isLiteralNode(node.body[0]) && !node.body[0].content.includes("\n")
|
|
168
|
-
const isInlineForm = isInlineContent || isInlineLiteralContent || (isTurboFrame && !hasBody) || (isScript && hasSrcAttribute)
|
|
172
|
+
const isInlineForm = isInlineContent || isInlineLiteralContent || (isTurboFrame && !hasBody) || (isScript && hasSrcAttribute) || isImg
|
|
169
173
|
|
|
170
174
|
if (node.is_void) {
|
|
171
175
|
asMutable(node).close_tag = null
|
|
@@ -270,6 +274,17 @@ class HTMLToActionViewTagHelperVisitor extends Visitor {
|
|
|
270
274
|
return argString ? ` javascript_include_tag ${argString} ` : ` javascript_include_tag `
|
|
271
275
|
}
|
|
272
276
|
|
|
277
|
+
private buildImageTagContent(attributes: string, source: string | null): string {
|
|
278
|
+
const args: string[] = []
|
|
279
|
+
|
|
280
|
+
if (source) args.push(source)
|
|
281
|
+
if (attributes) args.push(attributes)
|
|
282
|
+
|
|
283
|
+
const argString = args.join(", ")
|
|
284
|
+
|
|
285
|
+
return argString ? ` image_tag ${argString} ` : ` image_tag `
|
|
286
|
+
}
|
|
287
|
+
|
|
273
288
|
private buildLinkToContent(node: HTMLElementNode, attribute: string, href: string | null, isInlineContent: boolean): string {
|
|
274
289
|
const args: string[] = []
|
|
275
290
|
|
|
@@ -301,7 +316,7 @@ export class HTMLToActionViewTagHelperRewriter extends ASTRewriter {
|
|
|
301
316
|
}
|
|
302
317
|
|
|
303
318
|
get description(): string {
|
|
304
|
-
return "Converts raw HTML elements to ActionView tag helpers (tag.*, turbo_frame_tag, javascript_tag, javascript_include_tag)"
|
|
319
|
+
return "Converts raw HTML elements to ActionView tag helpers (tag.*, turbo_frame_tag, javascript_tag, javascript_include_tag, image_tag)"
|
|
305
320
|
}
|
|
306
321
|
|
|
307
322
|
rewrite<T extends Node>(node: T, _context: RewriteContext): T {
|
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
|
}
|