Skip to content

Commit

Permalink
fix: support fully svelte scoped css
Browse files Browse the repository at this point in the history
  • Loading branch information
kazupon committed Dec 26, 2024
1 parent 27a4492 commit 998955e
Show file tree
Hide file tree
Showing 49 changed files with 119 additions and 142 deletions.
22 changes: 9 additions & 13 deletions packages/svelte-template-compiler/src/ir/svelte.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,14 +301,13 @@ describe('enableStructures', () => {
expect(awaitBlock.parent).toEqual(div)
expect(awaitBlock.prev).toEqual(h)
expect(awaitBlock.next).toBeUndefined()
// {:pending}
const pendingBlock = awaitBlock.pending as SveltePendingBlock
expect(pendingBlock.parent).toEqual(awaitBlock)
expect(pendingBlock.prev).toBeUndefined()
expect(pendingBlock.next).toBeUndefined()
// p inside {:pending}
// p inside {#await}
const pInsidePending = pendingBlock.children[1]
expect(pInsidePending.parent).toEqual(pendingBlock)
expect(pInsidePending.parent).toEqual(awaitBlock)
expect(pInsidePending.prev).toBeUndefined()
// {:then}
const thenBlock = awaitBlock.then as SvelteThenBlock
Expand All @@ -317,7 +316,7 @@ describe('enableStructures', () => {
expect(thenBlock.next).toBeUndefined()
// p inside {:then}
const pInsideThen = thenBlock.children[1]
expect(pInsideThen.parent).toEqual(thenBlock)
expect(pInsideThen.parent).toEqual(awaitBlock)
expect(pInsideThen.prev).toBeUndefined()
// {:catch}
const catchBlock = awaitBlock.catch as SvelteElseBlock
Expand All @@ -326,7 +325,7 @@ describe('enableStructures', () => {
expect(catchBlock.next).toBeUndefined()
// p inside {:catch}
const pInsideCatch = catchBlock.children[1]
expect(pInsideCatch.parent).toEqual(catchBlock)
expect(pInsideCatch.parent).toEqual(awaitBlock)
expect(pInsideCatch.prev).toBeUndefined()
})

Expand Down Expand Up @@ -358,14 +357,13 @@ describe('enableStructures', () => {
expect(awaitBlock.parent).toEqual(div)
expect(awaitBlock.prev).toEqual(h)
expect(awaitBlock.next).toBeUndefined()
// {:pending}
const pendingBlock = awaitBlock.pending as SveltePendingBlock
expect(pendingBlock.parent).toEqual(awaitBlock)
expect(pendingBlock.prev).toBeUndefined()
expect(pendingBlock.next).toBeUndefined()
// p inside {:pending}
// p inside {:await}
const pInsidePending = pendingBlock.children[1]
expect(pInsidePending.parent).toEqual(pendingBlock)
expect(pInsidePending.parent).toEqual(awaitBlock)
expect(pInsidePending.prev).toBeUndefined()
// {:then}
const thenBlock = awaitBlock.then as SvelteThenBlock
Expand All @@ -374,7 +372,7 @@ describe('enableStructures', () => {
expect(thenBlock.next).toBeUndefined()
// p inside {:then}
const pInsideThen = thenBlock.children[1]
expect(pInsideThen.parent).toEqual(thenBlock)
expect(pInsideThen.parent).toEqual(awaitBlock)
expect(pInsideThen.prev).toBeUndefined()
})

Expand Down Expand Up @@ -404,7 +402,6 @@ describe('enableStructures', () => {
expect(awaitBlock.parent).toEqual(div)
expect(awaitBlock.prev).toEqual(h)
expect(awaitBlock.next).toBeUndefined()
// {:pending}
const pendingBlock = awaitBlock.pending as SveltePendingBlock
expect(pendingBlock.parent).toEqual(awaitBlock)
expect(pendingBlock.prev).toBeUndefined()
Expand All @@ -416,7 +413,7 @@ describe('enableStructures', () => {
expect(thenBlock.next).toBeUndefined()
// p inside {:then}
const pInsideThen = thenBlock.children[1]
expect(pInsideThen.parent).toEqual(thenBlock)
expect(pInsideThen.parent).toEqual(awaitBlock)
expect(pInsideThen.prev).toBeUndefined()
})

Expand Down Expand Up @@ -446,7 +443,6 @@ describe('enableStructures', () => {
expect(awaitBlock.parent).toEqual(div)
expect(awaitBlock.prev).toEqual(h)
expect(awaitBlock.next).toBeUndefined()
// {:pending}
const pendingBlock = awaitBlock.pending as SveltePendingBlock
expect(pendingBlock.parent).toEqual(awaitBlock)
expect(pendingBlock.prev).toBeUndefined()
Expand All @@ -463,7 +459,7 @@ describe('enableStructures', () => {
expect(catchBlock.next).toBeUndefined()
// p inside {:catch}
const pInsideCatch = catchBlock.children[1]
expect(pInsideCatch.parent).toEqual(catchBlock)
expect(pInsideCatch.parent).toEqual(awaitBlock)
expect(pInsideCatch.prev).toBeUndefined()
})
})
16 changes: 9 additions & 7 deletions packages/svelte-template-compiler/src/ir/svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,21 +229,23 @@ export type CompatLocationable = {
export function enableStructures(node: SvelteTemplateNode): void {
let last: SvelteTemplateNode | undefined
const children = node.children || []
if (__DEV__) {
console.log('enableStructures type:', node.type, node.parent?.type, children.length)

if (node.type === 'Fragment') {
node.parent = null // eslint-disable-line unicorn/no-null
}

children.forEach(child => {
if (__DEV__) {
console.log('enableStructures child type:', child.type)
}
const parent =
isSveltePendingBlock(node) || isSvelteThenBlock(node) || isSvelteCatchBlock(node)
? node.parent
: node

children.forEach(child => {
// ignores
if (isSvelteText(child) || isSvelteComponentTag(child)) {
return
}

child.parent = node
child.parent = parent

if (last) {
last.next = child
Expand Down
46 changes: 21 additions & 25 deletions packages/svelte-template-compiler/src/style/selector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,17 +58,17 @@ export class Block {
root: boolean
combinator: CssNode | null
selectors: CssNode[]
start: number | null
end: number | null
start: number
end: number
shouldEncapsulate: boolean

constructor(combinator: CssNode | null) {
this.combinator = combinator
this.host = false
this.root = false
this.selectors = []
this.start = null // eslint-disable-line unicorn/no-null
this.end = null // eslint-disable-line unicorn/no-null
this.start = -1
this.end = -1
this.shouldEncapsulate = false
}

Expand Down Expand Up @@ -100,7 +100,6 @@ function groupSelectors(selector: CssNode): Block[] {

if (hasChildren(selector)) {
selector.children?.forEach((child, _index) => {
// console.log('groupSelectors', child.type, JSON.stringify(child), index)
if (isWhiteSpace(child) || isCombinator(child)) {
block = new Block(child)
blocks.push(block)
Expand All @@ -118,7 +117,6 @@ export function applySelector(
node: SvelteElement | undefined,
toEncapsulate: { node: SvelteElement; block: Block }[]
): boolean {
// console.log('applySelector', '-', node?.type, node?.name, JSON.stringify(node?.attributes), blocks.length, '-')
const block = blocks.pop()

if (!block) {
Expand Down Expand Up @@ -170,6 +168,8 @@ export function applySelector(
toEncapsulate.push({ node, block })
return true
}

return false
} else if (isCombinator(block.combinator) && block.combinator.name === '>') {
const hasGlobalParent = blocks.every(block => block.global)

Expand All @@ -188,7 +188,6 @@ export function applySelector(
isCombinator(block.combinator) &&
(block.combinator.name === '+' || block.combinator.name === '~')
) {
// console.log('applySelector', 'type', node.type, 'combinator', block.combinator.name, JSON.stringify(block))
const [siblings, hasSlotSibling] = getPossibleElementSiblings(
node,
block.combinator.name === '+'
Expand All @@ -207,7 +206,6 @@ export function applySelector(
}

for (const possibleSibling of siblings.keys()) {
// console.log('applySelector possibleSibling', possibleSibling.type, possibleSibling.name)
// eslint-disable-next-line unicorn/prefer-spread
if (applySelector(blocks.slice(), possibleSibling as SvelteElement, toEncapsulate)) {
toEncapsulate.push({ node, block })
Expand Down Expand Up @@ -240,11 +238,13 @@ const WHITELIST_ATTRIBUTE_SELECTOR = new Map([
])

function blockMightApplyToNode(block: Block, node: SvelteElement): BlockAppliesToNode {
// console.log('blockMightApplyToNode', node.type, node.name, JSON.stringify(node.attributes), block.selectors.length)
if (block.host || block.root) {
return BlockAppliesToNode.NotPossible
}

let i = block.selectors.length
while (i--) {
const selector = block.selectors[i]
// console.log('blockMightApplyToNode selector', selector.type, JSON.stringify(selector), i)

const name =
hasName(selector) &&
Expand Down Expand Up @@ -333,7 +333,6 @@ function attributeMatches(
operator: string,
caseInsensitive: boolean
): boolean {
// console.log('attributeMatches', name, expectedValue, operator, caseInsensitive)
// const spread = node.attributes.find((attr) => attr.type === 'Spread');
const spread = node.attributes.find(attr => isSvelteSpreadAttribute(attr))
if (spread) {
Expand Down Expand Up @@ -537,9 +536,10 @@ function isDynamicElement(node: SvelteElement): boolean {
}

function getElementParent(node: SvelteTemplateNode): SvelteElement | undefined {
let parent: SvelteTemplateNode | undefined = node
let parent: SvelteTemplateNode | undefined | null = node
// while ((parent = parent.parent) && !(isSvelteElement(parent) || parent?.type === 'Fragment'));
while ((parent = parent.parent) && !isSvelteElement(parent));
return parent
return parent as unknown as SvelteElement
}

enum NodeExist {
Expand All @@ -551,18 +551,19 @@ function getPossibleElementSiblings(
node: SvelteTemplateNode,
adjacentOnly: boolean
): [Map<SvelteTemplateNode, NodeExist>, boolean] {
// console.log('getPossibleElementSiblings', node.type, JSON.stringify(node.attributes), adjacentOnly)
const result = new Map<SvelteTemplateNode, NodeExist>()

let prev: SvelteTemplateNode | undefined = node
let hasSlotSibling = false
let slotSiblingFound = false

while (([prev, slotSiblingFound] = findPreviousSibling(prev)) && prev) {
// console.log('getPossibleElementSiblings loop', prev?.type)
if (isSvelteElement(prev)) {
hasSlotSibling = hasSlotSibling || slotSiblingFound
while (([prev, slotSiblingFound] = findPreviousSibling(prev))) {
hasSlotSibling = hasSlotSibling || slotSiblingFound
if (!prev) {
break
}

if (isSvelteElement(prev)) {
if (
// eslint-disable-next-line unicorn/prefer-array-some
!prev.attributes.find(attr => isSvelteAttribute(attr) && attr.name.toLowerCase() === 'slot')
Expand All @@ -575,7 +576,6 @@ function getPossibleElementSiblings(
}
} else if (prev.type === 'EachBlock' || prev.type === 'IfBlock' || prev.type === 'AwaitBlock') {
const possibleLastChild = getPossibleLastChild(prev, adjacentOnly)
// console.log('getPossibleElementSiblings possibleLastChild', possibleLastChild)
addToMap(possibleLastChild, result)
if (adjacentOnly && hasDefiniteElements(possibleLastChild)) {
return [result, hasSlotSibling]
Expand All @@ -584,17 +584,15 @@ function getPossibleElementSiblings(
}

if (!prev || !adjacentOnly) {
let parent: SvelteTemplateNode | undefined = node
let parent: SvelteTemplateNode | undefined | null = node
let skipEachForLastChild = node.type === 'ElseBlock'
// console.log('getPossibleElementSiblings deep', parent?.type, skipEachForLastChild)
while (
(parent = parent.parent) &&
(parent.type === 'EachBlock' ||
parent.type === 'IfBlock' ||
parent.type === 'ElseBlock' ||
parent.type === 'AwaitBlock')
) {
// console.log('getPossibleElementSiblings deep in', parent?.type)
const [possibleSiblings, slotSiblingFound] = getPossibleElementSiblings(parent, adjacentOnly)
hasSlotSibling = hasSlotSibling || slotSiblingFound
addToMap(possibleSiblings, result)
Expand Down Expand Up @@ -640,7 +638,7 @@ function findPreviousSibling(node: SvelteTemplateNode): [SvelteTemplateNode | un
currentNode = currentNode.parent
}
currentNode = currentNode.prev
} while (currentNode != null && isSvelteSlot(currentNode)) // eslint-disable-line unicorn/no-null
} while (currentNode && isSvelteSlot(currentNode))

return [currentNode, hasSlotSibling]
}
Expand All @@ -649,7 +647,6 @@ function getPossibleLastChild(
block: SvelteTemplateNode,
adjacentOnly: boolean
): Map<SvelteTemplateNode, NodeExist> {
// console.log('getPossibleLastChild', block.type, adjacentOnly)
const result = new Map<SvelteTemplateNode, NodeExist>()
const blockChildren = block.children || []

Expand Down Expand Up @@ -757,7 +754,6 @@ function loopChild(

for (let i = children.length - 1; i >= 0; i--) {
const child = children[i]
// console.log('loopChild child.type', child.type)

if (isSvelteElement(child)) {
result.set(child, NodeExist.Definitely)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,12 +181,11 @@ describe('render with fixtures', async () => {
const ast = parse(input)
enableStructures(ast.html)
const stylesheet = new SvelteStylesheet(
Object.assign({}, { ast: ast.css!, source: input }, config.compilerOptions || {})
Object.assign({}, { ast: ast.css!, source: input }, config.compileOptions || {})
)
walk(ast.html, {
enter(node) {
if (isSvelteElement(node)) {
// console.log('applying ...', node.type, node.name, JSON.stringify(node.attributes))
stylesheet.apply(node)
}
}
Expand Down
Loading

0 comments on commit 998955e

Please sign in to comment.