Skip to content

Commit 2e49348

Browse files
committed
fix: missing texts issue
1 parent 5992d77 commit 2e49348

File tree

3 files changed

+78
-88
lines changed

3 files changed

+78
-88
lines changed

readme.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,10 @@ npm i @egoistdeveloper/twcss-to-sass
2727

2828
## Todo
2929

30-
- [ ] ~~Fix missing texts with child element~~ (himalaya does not support it)
31-
- [ ] ~~Fix self-closing tags like `link`, `base`, `area`, `br` etc~~
32-
- [ ] ~~Fix url conflict with class name in comment line~~
30+
- [x] ~~Fix missing texts with child element~~
31+
- [x] ~~Fix self-closing tags like `link`, `base`, `area`, `br` etc~~
32+
- [x] ~~Fix url conflict with class name in comment line~~
33+
- [ ] Add option for duplicated classes
3334
- [ ] Fix `group` and `peer` utility classes issue on SASS
3435
- [ ] Filter non tailwind classes
3536
- [ ] Order classes

src/interfaces/html-node.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@ export interface IHtmlNode {
1818
filterAttributes: IAttribute | null
1919
sassClassName: string
2020
children: this[]
21+
hasElementChildren: boolean
2122
length: number
2223
}

src/twcss-to-sass.ts

Lines changed: 73 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -90,100 +90,80 @@ function getAttributes(
9090
*
9191
* @param {array} styleElements
9292
*/
93-
function getStyleContents(styleElements: IHtmlNode[]): IHtmlNode[] {
94-
return styleElements.map((element: IHtmlNode) => {
95-
const styleContents = element.children
96-
.filter((x: IHtmlNode) => (x.type = 'text'))
97-
.map((x: IHtmlNode) => x.content)
98-
.join('')
99-
100-
return <IHtmlNode>(<unknown>{
101-
tagName: 'style',
102-
text: 'STYLE',
103-
filterAttributes: {
104-
style: styleContents,
105-
},
106-
})
93+
function getStyleContents(element: IHtmlNode): IHtmlNode {
94+
return <IHtmlNode>(<unknown>{
95+
tagName: 'style',
96+
text: 'STYLE',
97+
filterAttributes: {
98+
style: element.content,
99+
},
107100
})
108101
}
109102

103+
let styles: IHtmlNode[] = []
104+
110105
/**
111106
* Filter IHtmlNode array by node type and tagName
112107
*
113108
* @param {string} htmlJson
114109
*
115110
* @returns Object
116111
*/
117-
function filterHtmlData(
118-
htmlJson: IHtmlNode[] | IHtmlNode,
119-
nestedOrder = 1
120-
): IHtmlNode[] | null {
121-
if (htmlJson && Array.isArray(htmlJson)) {
122-
const parentNode = htmlJson.filter(
123-
(x: IHtmlNode) =>
124-
(x.type == 'element' || x.type == 'comment') && x.tagName != 'style'
125-
),
126-
styleElements = htmlJson.filter((x) => x.tagName == 'style')
127-
128-
let styleList: IHtmlNode[] = []
129-
130-
if (styleElements && styleElements.length) {
131-
styleList = getStyleContents(styleElements)
132-
}
112+
function filterHtmlData(nodeTree: IHtmlNode[], deepth = 0): IHtmlNode[] {
113+
if (nodeTree.length > 0) {
114+
// we do need to empty or doctype declaration
115+
nodeTree = nodeTree.filter(
116+
(x: IHtmlNode) => x.content !== ' ' && x.tagName != '!doctype'
117+
)
133118

134-
if (parentNode && parentNode.length) {
119+
if (nodeTree && nodeTree.length) {
135120
const elementList: IHtmlNode[] | null = []
136121

137-
parentNode.forEach((node: IHtmlNode) => {
138-
if (Array.isArray(node.children)) {
139-
// find previous comment
140-
for (let i = 0; i < parentNode.length; i++) {
141-
if (parentNode[i] == node) {
142-
const _node = parentNode[i - 1]
143-
144-
if (node) {
145-
node.comment =
146-
_node && _node.type == 'comment' && _node.content
147-
? Utils.cleanText(_node.content, true)
148-
: null
149-
}
150-
151-
break
152-
}
122+
nodeTree.forEach((node: IHtmlNode, index) => {
123+
if (node.type == 'element') {
124+
if (node.tagName == 'style') {
125+
styles.push(getStyleContents(node))
126+
} else {
127+
node.filterAttributes = getAttributes(node.attributes, [
128+
'class',
129+
'style',
130+
])
153131
}
154132

155-
node.order = nestedOrder
156-
157-
const children: IHtmlNode[] | null = filterHtmlData(
158-
node.children,
159-
nestedOrder + 1
160-
)
133+
// find element's comment in previous node
134+
node.comment =
135+
nodeTree[index - 1] && nodeTree[index - 1].type == 'comment'
136+
? Utils.cleanText(nodeTree[index - 1].content, true)
137+
: null
138+
}
161139

162-
if (children && children.length) {
163-
node.children = children.filter(
164-
(x: IHtmlNode) => x.type == 'element'
165-
)
166-
}
140+
// allow only html elements and texts
141+
if (node.type != 'comment' && node.tagName !== 'style') {
142+
elementList.push(node)
167143
}
168144

169-
// get only class and inline style attributes
170-
node.filterAttributes = getAttributes(node.attributes, [
171-
'class',
172-
'style',
173-
])
145+
// let's go deeper
146+
if (Array.isArray(node.children) && node.children.length > 0) {
147+
node.order = ++deepth
174148

175-
if (node.filterAttributes !== null || node.children !== null) {
176-
elementList?.push(node)
149+
const childNodes: IHtmlNode[] = filterHtmlData(node.children, deepth)
150+
151+
if (childNodes && childNodes.length) {
152+
node.children = childNodes.filter(
153+
(x: IHtmlNode) => x.tagName != 'style' && x.type != 'comment'
154+
)
155+
156+
node.hasElementChildren =
157+
node.children.filter((x) => x.type == 'element').length > 0
158+
}
177159
}
178160
})
179161

180-
if (elementList && elementList.length) {
181-
return [...styleList, ...elementList]
182-
}
162+
return nodeTree
183163
}
184164
}
185165

186-
return null
166+
return []
187167
}
188168

189169
/**
@@ -197,6 +177,8 @@ function filterHtmlData(
197177
function getClassName(node: IHtmlNode, deepth: number): string {
198178
let className = ''
199179

180+
const exceptTagNames = ['html', 'head', 'body', 'style']
181+
200182
if (node.comment && _defaultOptions.useCommentBlocksAsClassName) {
201183
let classSlug = _defaultOptions.classNameOptions.prefix
202184

@@ -213,7 +195,11 @@ function getClassName(node: IHtmlNode, deepth: number): string {
213195
classSlug += _defaultOptions.classNameOptions.suffix
214196

215197
className = '.' + classSlug
216-
} else if (node.tagName != 'div') {
198+
} else if (
199+
exceptTagNames.indexOf(node.tagName) > -1 ||
200+
(!node.hasElementChildren && node.tagName != 'div')
201+
) {
202+
// TODO: add excape option for tag names
217203
className = `${node.tagName}`
218204
} else {
219205
className = `.class-${node.tagName}-${deepth}`
@@ -243,10 +229,6 @@ function getSassTree(nodeTree: IHtmlNode[] | IHtmlNode, deepth = 0) {
243229
let treeString = '',
244230
subTreeString = ''
245231

246-
if (node.filterAttributes === null && node.children === null) {
247-
return ''
248-
}
249-
250232
if (Array.isArray(node.children) && node.children.length) {
251233
++deepth
252234
subTreeString = getSassTree(node, deepth)
@@ -311,16 +293,11 @@ function getSassTree(nodeTree: IHtmlNode[] | IHtmlNode, deepth = 0) {
311293
*/
312294
function getHtmlTree(nodeTree: IHtmlNode[], deepth = 0): string {
313295
if (nodeTree) {
314-
if (!Array.isArray(nodeTree)) {
315-
// nodeTree = nodeTree.children
316-
}
317-
318296
let htmlTree = ''
319297

320298
nodeTree.forEach(function (node: IHtmlNode, index) {
299+
const className = getClassName(node, deepth)
321300
if (node.type == 'element') {
322-
const className = getClassName(node, deepth)
323-
324301
if (_defaultOptions.printComments) {
325302
if (node.comment) {
326303
htmlTree += `\n<!-- ${node.comment.trim()} -->`
@@ -352,10 +329,16 @@ function getHtmlTree(nodeTree: IHtmlNode[], deepth = 0): string {
352329
}>`
353330
}
354331

355-
const innerText = node.children
356-
?.filter((child) => child.type === 'text')
357-
.map((child) => child.content)
358-
.join('')
332+
// inner text
333+
334+
const textChildNodes = node.children?.filter(
335+
(child) => child.type === 'text'
336+
)
337+
338+
const innerText =
339+
node.children?.length == textChildNodes?.length
340+
? textChildNodes.map((child) => child.content).join('')
341+
: ''
359342

360343
// inner text
361344
htmlTree += innerText ? `\n${innerText}\n` : ''
@@ -381,6 +364,9 @@ function getHtmlTree(nodeTree: IHtmlNode[], deepth = 0): string {
381364
htmlTree +=
382365
(!isVoidElement ? `</${node.tagName}>` : '') +
383366
(!isNextNodeSibling ? '\n' : '')
367+
} else if (node.type == 'text') {
368+
// inner text
369+
htmlTree += node.content ? `\n${node.content}\n` : ''
384370
}
385371
})
386372

@@ -402,6 +388,8 @@ export function convertToSass(
402388
html: string,
403389
options: ITwToSassOptions | null = defaultOptions
404390
): null | IConverterResult {
391+
styles = []
392+
405393
if (html && html.length) {
406394
if (options) {
407395
_defaultOptions = {
@@ -412,7 +400,7 @@ export function convertToSass(
412400

413401
html = Utils.cleanText(html)
414402

415-
const htmlJson: IHtmlNode[] | IHtmlNode = parse(html)
403+
const htmlJson: IHtmlNode[] = parse(html)
416404

417405
const filteredHtmlData = filterHtmlData(htmlJson)
418406

0 commit comments

Comments
 (0)