diff --git a/packages/core/src/generator/index.ts b/packages/core/src/generator/index.ts index 5e930e2d52..fc64a356b1 100644 --- a/packages/core/src/generator/index.ts +++ b/packages/core/src/generator/index.ts @@ -13,6 +13,8 @@ export class UnoGenerator { public parentOrders = new Map() public events = createNanoEvents<{ config: (config: ResolvedConfig) => void + tokens: (context: { tokens: Set }) => void + preflights: (context: { preflights: Set }) => void }>() constructor( @@ -144,63 +146,66 @@ export class UnoGenerator { const sheet = new Map() let preflightsMap: Record = {} - 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() - 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([]) + 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([]) - 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) diff --git a/test/generate-async.test.ts b/test/generate-async.test.ts index aa6160f628..2c20c39266 100644 --- a/test/generate-async.test.ts +++ b/test/generate-async.test.ts @@ -46,3 +46,81 @@ describe('generate-async', () => { expect(order).eql([1, 2]) }) }) + +describe('firing-events', () => { + test('config-event', async () => { + const order: number[] = [] + const uno = createGenerator() + + uno.events.on('config', () => order.push(order.length)) + 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]) + }) +})