diff --git a/src/index.ts b/src/index.ts index 76119c2..6cc522f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,57 +10,63 @@ export type PluginOptions = { getSlug?: GetSlug } +export type HookOptions = Parameters< + AstroIntegration['hooks']['astro:config:setup'] & { 0: any } +>[0] + export type Redirects = { [old: string]: string } +export async function initPlugin( + hookOptions: HookOptions, + options?: PluginOptions +) { + const _contentDir = options?.contentDir || 'src/pages/' + const _getSlug = options?.getSlug || getSlugFromFilePath + const _contentDirPath = path.join(process.cwd(), _contentDir) + const { logger, config, command, updateConfig } = hookOptions + + try { + const markdownFiles = await getMarkdownFiles(_contentDirPath) + if (!markdownFiles?.length) { + logger.warn('No markdown files found') + return + } + + const redirects = await getRedirects( + markdownFiles, + _contentDirPath, + _getSlug, + command + ) + if (!redirects || !Object.keys(redirects).length) { + logger.warn('No redirects found in markdown files') + return + } + + updateConfig({ redirects }) + + const redirectFilePath = path.join( + config.cacheDir.pathname, // Default is ./node_modules/.astro/ + 'redirect_from.json' + ) + await writeJson(redirectFilePath, redirects) + + logger.info( + `Added ${Object.keys(redirects).length} redirects to Astro config` + ) + } catch (error: any) { + logger.error((error as Error).message) + } +} + export default function astroRedirectFrom( options?: PluginOptions ): AstroIntegration { - const _contentDir = options?.contentDir || 'src/pages/' - const _getSlug = options?.getSlug || getSlugFromFilePath - const _contentDirPath = path.join(process.cwd(), _contentDir) - return { name: 'redirect-from', hooks: { - 'astro:config:setup': async ({ - config, - command, - updateConfig, - logger - }) => { - try { - const markdownFiles = await getMarkdownFiles(_contentDirPath) - if (!markdownFiles?.length) { - logger.warn('No markdown files found') - return - } - - const redirects = await getRedirects( - markdownFiles, - _contentDirPath, - _getSlug, - command - ) - if (!redirects || !Object.keys(redirects).length) { - logger.info('No redirects found in markdown files') - return - } - - updateConfig({ redirects }) - - const redirectFilePath = path.join( - config.cacheDir.pathname, // Default is ./node_modules/.astro/ - 'redirect_from.json' - ) - await writeJson(redirectFilePath, redirects) - - logger.info( - `Added ${Object.keys(redirects).length} redirects to Astro config` - ) - } catch (error: any) { - logger.error((error as Error).message) - } - } + 'astro:config:setup': async (hookOptions) => + await initPlugin(hookOptions, options) } } } diff --git a/test/index.test.ts b/test/index.test.ts new file mode 100644 index 0000000..e2f00e8 --- /dev/null +++ b/test/index.test.ts @@ -0,0 +1,75 @@ +import { describe, it, expect, vi } from 'vitest' +import astroRedirectFrom, { initPlugin } from '../src/index' +import * as utils from '../src/utils' +import * as redirects from '../src/getRedirects' + +describe('initPlugin', () => { + const mockLogger = { + warn: vi.fn(), + info: vi.fn(), + error: vi.fn() + } + + const hookOptionsMock = { + logger: mockLogger, + config: { + cacheDir: { pathname: './node_modules/.astro/' } + }, + command: 'dev', + updateConfig: vi.fn() + } + + it('should handle no markdown files scenario', async () => { + const getMarkdownFilesSpy = vi.spyOn(utils, 'getMarkdownFiles') + getMarkdownFilesSpy.mockResolvedValue([]) + + await initPlugin(hookOptionsMock as any) + + expect(mockLogger.warn).toBeCalledWith('No markdown files found') + expect(hookOptionsMock.updateConfig).not.toBeCalled() + }) + + it('should handle no redirects found scenario', async () => { + const getMarkdownFilesSpy = vi.spyOn(utils, 'getMarkdownFiles') + getMarkdownFilesSpy.mockResolvedValue(['test.md', 'test2.md']) + + const getRedirectsSpy = vi.spyOn(redirects, 'getRedirects') + getRedirectsSpy.mockResolvedValue({}) + + await initPlugin(hookOptionsMock as any) + + expect(mockLogger.warn).toBeCalledWith( + 'No redirects found in markdown files' + ) + expect(hookOptionsMock.updateConfig).not.toBeCalled() + }) + + it('should handle redirects found scenario', async () => { + const getMarkdownFilesSpy = vi.spyOn(utils, 'getMarkdownFiles') + getMarkdownFilesSpy.mockResolvedValue(['test.md', 'test2.md']) + + const getRedirectsSpy = vi.spyOn(redirects, 'getRedirects') + getRedirectsSpy.mockResolvedValue({ '/old': '/new' }) + + const writeJsonSpy = vi.spyOn(utils, 'writeJson') + writeJsonSpy.mockImplementation(() => Promise.resolve()) + + await initPlugin(hookOptionsMock as any) + + expect(hookOptionsMock.updateConfig).toBeCalledWith({ + redirects: { '/old': '/new' } + }) + expect(mockLogger.info).toBeCalledWith('Added 1 redirects to Astro config') + expect(writeJsonSpy).toBeCalled() + }) +}) + +describe('astroRedirectFrom', () => { + it('should return an AstroIntegration object', () => { + const result = astroRedirectFrom() + + expect(result).toHaveProperty('name', 'redirect-from') + expect(result).toHaveProperty('hooks') + expect(result.hooks).toHaveProperty('astro:config:setup') + }) +})