0.1.1 • Published 9 months ago

@coana-tech/next-plugin-metadata-extractor v0.1.1

Weekly downloads
-
License
MIT
Repository
github
Last release
9 months ago

@coana-tech/next-plugin-metadata-extractor

A Next.js plugin that automatically extracts page metadata to a JSON file for site-wide search functionality.

Features

  • Automatically collects static metadata from Next.js pages during development and build
  • Updates metadata in real-time during development
  • TypeScript support
  • Zero configuration required

Example Metadata Output

// metadata.json
[
  {
    "pathname": "/home",
    "metadata": {
      "title": "Home Page",
      "description": "Home Page Description"
    }
  },
  {
    "pathname": "/about",
    "metadata": {
      "title": "About Page",
      "description": "About Page Description",
      "keywords": ["about", "page", "description"]
    }
  }
]

Installation

npm install @coana-tech/next-plugin-metadata-extractor
# or
pnpm add @coana-tech/next-plugin-metadata-extractor
# or
yarn add @coana-tech/next-plugin-metadata-extractor

Usage

  1. Add the plugin to your next.config.js:
// next.config.js
const { MetadataCollectorPlugin } = require('@coana-tech/next-plugin-metadata-extractor');

/** @type {import('next').NextConfig} */
const nextConfig = {
  webpack: (config, { isServer, dev }) => {
    if (isServer) {
      config.plugins.push(new MetadataCollectorPlugin({ dev }));
    }
    return config;
  }
};

module.exports = nextConfig;
  1. Define static metadata in your pages:
// app/page.tsx
import type { Metadata } from 'next';

export const metadata: Metadata = {
  title: "My Page",
  description: "My Page Description",
};

See the Next.js documentation for more information about defining metadata.

NOTE: The plugin does not support dynamically computed metadata.

  1. Start the development server:
npm run dev
  1. Fetch the metadata.json file from the public directory:
// app/components/Metadata.tsx
import { useState, useEffect } from 'react';
import type { PageMetadata } from '@coana-tech/next-plugin-metadata-extractor';

function Component() {
  const [metadata, setMetadata] = useState<PageMetadata[]>([]);

  useEffect(() => {
    fetch('/metadata.json')
      .then(res => res.json())
      .then(setMetadata);
  }, []);

  return (
    <div>
      {metadata.map(item => (
        <div key={item.pathname}>{item.metadata.title}</div>
      ))}
    </div>
  );
}

Configuration

The MetadataCollectorPlugin accepts the following configuration options:

OptionDescriptionDefault
outputPathSet output path for the metadata file, must end with .jsonpublic/metadata.json
dirSet directory to search for page filesapp
filesSet files to collect from metadata, supports glob patterns["**/page.tsx", "**/page.ts", "**/page.jsx", "**/page.js"]
keysSet keys to collect from metadata['title', 'description', 'keywords', 'category']
devEnable development mode with debouncingfalse
debounceTimeDebounce time in milliseconds for dev mode updates1000

Advanced Usage

Server-side usage

If you don't want the metadata file to be public, set the outputPath to a server directory and create a server action to fetch the metadata.

Here's an example:

// next.config.js
const nextConfig = {
  webpack: (config, { isServer, dev }) => {
    isServer && config.plugins.push(new MetadataCollectorPlugin(
      { 
        dev, 
        outputPath: 'src/actions/metadata.json', // <-- output path
      }
    ));
    return config;
  }
};

// src/actions/metadata.ts
import metadata from './metadata.json';
export async function getMetadata() {
  const { userId } = auth();

  if (!userId) {
    throw new Error('Unauthorized');
  }

  return metadata;
} 

Adding Fuzzy Search

You can enhance the search functionality with fuzzy matching using libraries like Fuse.js. Here's an example:

import Fuse from 'fuse.js';
import type { PageMetadata } from '@coana-tech/next-plugin-metadata-extractor';

function FuzzySearch() {
  const [fuse, setFuse] = useState<Fuse<PageMetadata> | null>(null);
  const [results, setResults] = useState<PageMetadata[]>([]);

  useEffect(() => {
    // Load metadata and initialize Fuse.js
    fetch('/metadata.json')
      .then(res => res.json())
      .then(data => {
        setFuse(new Fuse(data, {
          keys: ['metadata.title', 'metadata.description'],
          threshold: 0.3,
          distance: 100
        }));
      });
  }, []);

  const handleSearch = useCallback((term: string) => {
    if (!fuse || !term.trim()) {
      setResults([]);
      return;
    }
    setResults(fuse.search(term).map(result => result.item));
  }, [fuse]);

  return (
    <div>
      <input type="text" onChange={e => handleSearch(e.target.value)} />
      <ul>
        {results.map(result => (
          <li key={result.pathname}>{result.metadata.title}</li>
        ))}
      </ul>
    </div>
  );
}

See Fuse.js documentation for more configuration options.

Compatibility

This plugin is tested with Next.js 14.

License

MIT