Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): generator events #1695

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
101 changes: 53 additions & 48 deletions packages/core/src/generator/index.ts
Expand Up @@ -13,6 +13,8 @@ export class UnoGenerator {
public parentOrders = new Map<string, number>()
public events = createNanoEvents<{
config: (config: ResolvedConfig) => void
tokens: (context: { tokens: Set<string> }) => void
preflights: (context: { preflights: Set<string> }) => void
}>()

constructor(
Expand Down Expand Up @@ -144,63 +146,66 @@ export class UnoGenerator {
const sheet = new Map<string, StringifiedUtil[]>()
let preflightsMap: Record<string, string> = {}

const tokenPromises = Array.from(tokens).map(async (raw) => {
if (matched.has(raw))
return
const tokensPromise = (async () => {
await Promise.all(Array.from(tokens).map(async (raw) => {
if (matched.has(raw))
return

const payload = await this.parseToken(raw)
if (payload == null)
return

matched.add(raw)

for (const item of payload) {
const parent = item[3] || ''
const layer = item[4]?.layer
if (!sheet.has(parent))
sheet.set(parent, [])
sheet.get(parent)!.push(item)
if (layer)
layerSet.add(layer)
}
}))

const payload = await this.parseToken(raw)
if (payload == null)
return
this.events.emit('tokens', { tokens: new Set(matched) })
})()

matched.add(raw)
const preflightsPromise = (async () => {
const generatedPreflights = new Set<string>()

for (const item of payload) {
const parent = item[3] || ''
const layer = item[4]?.layer
if (!sheet.has(parent))
sheet.set(parent, [])
sheet.get(parent)!.push(item)
if (layer)
layerSet.add(layer)
}
})
if (preflights) {
const preflightContext: PreflightContext = {
generator: this,
theme: this.config.theme,
}

const preflightPromise = (async () => {
if (!preflights)
return
const preflightLayerSet = new Set<string>([])
this.config.preflights.forEach(({ layer = LAYER_PREFLIGHTS }) => {
layerSet.add(layer)
preflightLayerSet.add(layer)
})

const preflightContext: PreflightContext = {
generator: this,
theme: this.config.theme,
preflightsMap = Object.fromEntries(
await Promise.all(Array.from(preflightLayerSet).map(
async (layer) => {
const preflights = (await Promise.all(
this.config.preflights
.filter(i => (i.layer || LAYER_PREFLIGHTS) === layer)
.map(async i => await i.getCSS(preflightContext)),
)).filter(Boolean) as string[]

preflights.forEach(p => generatedPreflights.add(p))
return [layer, preflights.join(nl)]
},
)),
)
}

const preflightLayerSet = new Set<string>([])
this.config.preflights.forEach(({ layer = LAYER_PREFLIGHTS }) => {
layerSet.add(layer)
preflightLayerSet.add(layer)
})

preflightsMap = Object.fromEntries(
await Promise.all(Array.from(preflightLayerSet).map(
async (layer) => {
const preflights = await Promise.all(
this.config.preflights
.filter(i => (i.layer || LAYER_PREFLIGHTS) === layer)
.map(async i => await i.getCSS(preflightContext)),
)
const css = preflights
.filter(Boolean)
.join(nl)
return [layer, css]
},
)),
)
this.events.emit('preflights', { preflights: generatedPreflights })
})()

await Promise.all([
...tokenPromises,
preflightPromise,
])
await Promise.all([tokensPromise, preflightsPromise])

const layers = this.config.sortLayers(Array
.from(layerSet)
Expand Down
78 changes: 78 additions & 0 deletions test/generate-async.test.ts
Expand Up @@ -46,3 +46,81 @@ describe('generate-async', () => {
expect(order).eql([1, 2])
})
})

describe('firing-events', () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could have a separate events.test.ts for this.

test('config-event', async () => {
const order: number[] = []
const uno = createGenerator()

uno.events.on('config', () => order.push(order.length))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not very sure what order.length is used for, maybe we could just use a fixed number?

expect(order).eql([])

uno.setConfig({})
expect(order).eql([0])
})

test('tokens-generated-event', async () => {
const order: number[] = []
const uno = createGenerator({
rules: [
[/^rule$/, () => new Promise(resolve => setTimeout(() => {
order.push(1)
resolve('/* rule */')
}, 10))],
],
preflights: [
{
getCSS: () => new Promise(resolve => setTimeout(() => {
order.push(2)
resolve('/* preflight */')
}, 20)),
},
],
})
uno.events.on('tokens', () => order.push(3))
await uno.generate('rule')
expect(order).eql([1, 3, 2])
})

test('preflight-generated-event', async () => {
const order: number[] = []
const uno = createGenerator({
rules: [
[/^rule$/, () => new Promise(resolve => setTimeout(() => {
order.push(2)
resolve('/* rule */')
}, 20))],
],
preflights: [
{
getCSS: () => new Promise(resolve => setTimeout(() => {
order.push(1)
resolve('/* preflight */')
}, 10)),
},
],
})
uno.events.on('preflights', () => order.push(3))
await uno.generate('rule')
expect(order).eql([1, 3, 2])
})

test('preflight-event-fires-without-preflight', async () => {
const order: number[] = []
const uno = createGenerator({
rules: [
[/^rule$/, () => new Promise(resolve => setTimeout(() => {
order.push(10)
resolve('/* rule */')
}, 10))],
],
})
uno.events.on('preflights', () => order.push(order.length))

await uno.generate('rule', { preflights: false })
expect(order).eql([0, 10])

await uno.generate('rule', { preflights: true })
expect(order).eql([0, 10, 2])
})
})