vite-plugin-react-pages
Version:
<p> <a href="https://www.npmjs.com/package/vite-plugin-react-pages" target="_blank" rel="noopener"><img src="https://img.shields.io/npm/v/vite-plugin-react-pages.svg" alt="npm package" /></a> </p>
117 lines (105 loc) • 3.06 kB
text/typescript
import { EventEmitter } from 'events'
import { debounce } from 'mini-debounce'
/**
* Types of page data updates.
*
* add:
* A new page is added.
* The page list module will be updated.
* update:
* A page is updated.
* The page list module will be updated if it is static data change
* The page data module will be updated if it is runtime data change
* delete:
* A page is deleted.
* The page list module will be updated.
* Buffered update of the deleted page will be canceled.
*/
type Update =
| {
type: 'add' | 'delete'
pageId: string
}
| {
type: 'update'
pageId: string
dataType: 'runtime' | 'static'
}
export type ScheduleUpdate = (update: Update) => void
/**
* Buffer page data updates.
* Can flush a batch of updates together
* and cancel unnecessary updates
*/
export class PageUpdateBuffer extends EventEmitter {
/**
* which pages should be updated
*/
private pageUpdateBuffer = new Set<string>()
/**
* whether the page list should be updated
*/
private pageListUpdateBuffer = false
private scheduleFlush: () => void
constructor() {
super()
this.scheduleFlush = debounce(() => {
let havePageUpdate = false
if (this.pageUpdateBuffer.size > 0) {
havePageUpdate = true
const updates = [...this.pageUpdateBuffer.values()]
this.emit('page', updates)
this.pageUpdateBuffer.clear()
}
if (this.pageListUpdateBuffer) {
// if we have just sent a page update,
// we don't need to trigger page list update.
// because during the page update hmr, the page list will automatically get updated
// (because the whole import chain will get re-imported)
if (!havePageUpdate) this.emit('page-list')
this.pageListUpdateBuffer = false
}
}, 100)
}
scheduleUpdate(update: Update) {
switch (update.type) {
case 'add':
this.pageListUpdateBuffer = true
break
case 'update':
if (update.dataType === 'static') this.pageListUpdateBuffer = true
else this.pageUpdateBuffer.add(update.pageId)
break
case 'delete':
this.pageListUpdateBuffer = true
this.pageUpdateBuffer.delete(update.pageId)
break
default:
throw new Error(`invalid update type ${JSON.stringify(update)}`)
}
this.scheduleFlush()
}
async batchUpdate(exec: (scheduleUpdate: ScheduleUpdate) => Promise<void>) {
let updates: Update[] | null = []
const _this = this
try {
await exec(scheduleUpdate)
} finally {
updates.forEach((update) => {
_this.scheduleUpdate(update)
})
updates = null
this.scheduleFlush()
}
function scheduleUpdate(update: Update) {
if (!updates) {
// the batch lifetime has already expired
// add it to buffer directly
_this.scheduleUpdate(update)
return
}
// store it, will flush these updates together later
updates.push(update)
}
}
}