UNPKG

@tanstack/router-plugin

Version:

Modern and scalable routing for React applications

154 lines (133 loc) 4.07 kB
import { isAbsolute, join, normalize, resolve } from 'node:path' import { generator, resolveConfigPath } from '@tanstack/router-generator' import { getConfig } from './config' import type { UnpluginFactory } from 'unplugin' import type { Config } from './config' let lock = false const checkLock = () => lock const setLock = (bool: boolean) => { lock = bool } const PLUGIN_NAME = 'unplugin:router-generator' export const unpluginRouterGeneratorFactory: UnpluginFactory< Partial<Config> | undefined > = (options = {}) => { let ROOT: string = process.cwd() let userConfig = options as Config const getRoutesDirectoryPath = () => { return isAbsolute(userConfig.routesDirectory) ? userConfig.routesDirectory : join(ROOT, userConfig.routesDirectory) } const generate = async () => { if (checkLock()) { return } setLock(true) try { await generator(userConfig, process.cwd()) } catch (err) { console.error(err) console.info() } finally { setLock(false) } } const handleFile = async ( file: string, event: 'create' | 'update' | 'delete', ) => { const filePath = normalize(file) if (filePath === resolveConfigPath({ configDirectory: ROOT })) { userConfig = getConfig(options, ROOT) return } if ( event === 'update' && filePath === resolve(userConfig.generatedRouteTree) ) { // skip generating routes if the generated route tree is updated return } const routesDirectoryPath = getRoutesDirectoryPath() if (filePath.startsWith(routesDirectoryPath)) { await generate() } } const run: (cb: () => Promise<void> | void) => Promise<void> = async (cb) => { if (userConfig.enableRouteGeneration ?? true) { await cb() } } return { name: 'router-generator-plugin', async watchChange(id, { event }) { await run(async () => { await handleFile(id, event) }) }, vite: { async configResolved(config) { ROOT = config.root userConfig = getConfig(options, ROOT) await run(generate) }, }, async rspack(compiler) { userConfig = getConfig(options, ROOT) if (compiler.options.mode === 'production') { compiler.hooks.beforeRun.tapPromise(PLUGIN_NAME, async () => { await run(generate) }) } else { // rspack watcher doesn't register newly created files const routesDirectoryPath = getRoutesDirectoryPath() const chokidar = await import('chokidar') chokidar .watch(routesDirectoryPath, { ignoreInitial: true }) .on('add', async () => { await run(generate) }) let generated = false compiler.hooks.watchRun.tapPromise(PLUGIN_NAME, async () => { if (!generated) { generated = true return run(generate) } }) } }, async webpack(compiler) { userConfig = getConfig(options, ROOT) if (compiler.options.mode === 'production') { compiler.hooks.beforeRun.tapPromise(PLUGIN_NAME, async () => { await run(generate) }) } else { // webpack watcher doesn't register newly created files const routesDirectoryPath = getRoutesDirectoryPath() const chokidar = await import('chokidar') chokidar .watch(routesDirectoryPath, { ignoreInitial: true }) .on('add', async () => { await run(generate) }) let generated = false compiler.hooks.watchRun.tapPromise(PLUGIN_NAME, async () => { if (!generated) { generated = true return run(generate) } }) } if (compiler.options.mode === 'production') { compiler.hooks.done.tap(PLUGIN_NAME, (stats) => { console.info('✅ ' + PLUGIN_NAME + ': route-tree generation done') setTimeout(() => { process.exit(0) }) }) } }, } }