= ({ params }) => {
2 | return (
3 |
4 |
DynamicPage Component
5 |
{JSON.stringify(params, null, 2)}
6 |
7 | )
8 | }
9 |
10 | export default DynamicPage
11 |
12 | /**
13 | * @see https://beta.nextjs.org/docs/api-reference/generate-static-params
14 | * @returns
15 | */
16 | export async function generateStaticParams() {
17 | return [...Array(10000)].map((_, index) => ({
18 | dynamic: `page-${index}`,
19 | }))
20 | }
21 |
--------------------------------------------------------------------------------
/examples/app-dir/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export default function RootLayout({
4 | children,
5 | }: {
6 | children: React.ReactNode
7 | }) {
8 | return (
9 |
10 |
11 | {children}
12 |
13 | )
14 | }
15 |
--------------------------------------------------------------------------------
/examples/app-dir/app/page.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const HomePage: React.FC = () => {
4 | return (
5 |
6 |
HomePage Component
7 |
8 | )
9 | }
10 |
11 | export default HomePage
12 |
--------------------------------------------------------------------------------
/examples/app-dir/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/basic-features/typescript for more information.
6 |
--------------------------------------------------------------------------------
/examples/app-dir/next-sitemap.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next-sitemap').IConfig} */
2 | const config = {
3 | siteUrl: process.env.SITE_URL || 'https://example.com',
4 | generateRobotsTxt: true,
5 | sitemapSize: 1000,
6 | // optional
7 | robotsTxtOptions: {
8 | additionalSitemaps: [
9 | 'https://example.com/my-custom-sitemap-1.xml',
10 | 'https://example.com/my-custom-sitemap-2.xml',
11 | 'https://example.com/my-custom-sitemap-3.xml',
12 | ],
13 | },
14 | }
15 |
16 | export default config
17 |
--------------------------------------------------------------------------------
/examples/app-dir/next.config.js:
--------------------------------------------------------------------------------
1 | /**@type {import('next').NextConfig} */
2 | const config = {}
3 |
4 | export default config
5 |
--------------------------------------------------------------------------------
/examples/app-dir/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "with-next-app-dir",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "license": "MIT",
6 | "private": true,
7 | "type": "module",
8 | "scripts": {
9 | "dev": "next",
10 | "build": "next build",
11 | "postbuild": "next-sitemap"
12 | },
13 | "dependencies": {
14 | "@types/react-dom": "^18.2.14",
15 | "next": "^13.5.6",
16 | "react": "^18.2.0",
17 | "react-dom": "^18.2.0"
18 | },
19 | "devDependencies": {
20 | "@corex/workspace": "^4.0.43",
21 | "@types/react": "^18.2.33",
22 | "next-sitemap": "workspace:*",
23 | "turbo": "^1.9.8"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/examples/app-dir/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": false,
8 | "forceConsistentCasingInFileNames": true,
9 | "noEmit": true,
10 | "incremental": true,
11 | "esModuleInterop": true,
12 | "module": "esnext",
13 | "moduleResolution": "node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "jsx": "preserve",
17 | "plugins": [
18 | {
19 | "name": "next"
20 | }
21 | ]
22 | },
23 | "include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"],
24 | "exclude": ["node_modules"]
25 | }
26 |
--------------------------------------------------------------------------------
/examples/basic/.gitignore:
--------------------------------------------------------------------------------
1 | public
--------------------------------------------------------------------------------
/examples/basic/README.md:
--------------------------------------------------------------------------------
1 | # next-sitemap example
2 |
3 | Sitemap generator for next.js. `next-sitemap` will generate a sitemap file for all pages (including all pre-rendered/static pages).
4 |
5 | This package allows the generation of sitemaps along with `robots.txt` and provides the feature to split large sitemaps into multiple files.
6 |
7 | For detailed use case and example check the [documentation](https://github.com/iamvishnusankar/next-sitemap)
8 |
9 | ## Deploy your own
10 |
11 | Deploy the example using [Vercel](https://vercel.com/now):
12 |
13 | [](https://vercel.com/import/project?template=https://github.com/vercel/next.js/tree/canary/examples/with-next-sitemap)
14 |
15 | ## How to use
16 |
17 | [Documentation](https://github.com/iamvishnusankar/next-sitemap)
18 |
19 | ### Using `create-next-app`
20 |
21 | Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init) or [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) to bootstrap the example:
22 |
23 | ```bash
24 | npx create-next-app --example with-next-sitemap with-next-sitemap-app
25 | # or
26 | yarn create next-app --example with-next-sitemap with-next-sitemap-app
27 | ```
28 |
29 | ### Download manually
30 |
31 | Download the example:
32 |
33 | ```bash
34 | curl https://codeload.github.com/vercel/next.js/tar.gz/canary | tar -xz --strip=2 next.js-canary/examples/with-next-sitemap
35 | cd with-next-sitemap
36 | ```
37 |
38 | Install it and run:
39 |
40 | ```bash
41 | npm install
42 | npm run dev
43 | # or
44 | yarn
45 | yarn dev
46 | ```
47 |
48 | Deploy it to the cloud with [Vercel](https://vercel.com/import?filter=next.js&utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).
49 |
--------------------------------------------------------------------------------
/examples/basic/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/basic-features/typescript for more information.
6 |
--------------------------------------------------------------------------------
/examples/basic/next-sitemap.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next-sitemap').IConfig} */
2 | const config = {
3 | siteUrl: process.env.SITE_URL || 'https://example.com',
4 | generateRobotsTxt: true,
5 | sitemapSize: 1000,
6 | // optional
7 | robotsTxtOptions: {
8 | additionalSitemaps: [
9 | 'https://example.com/my-custom-sitemap-1.xml',
10 | 'https://example.com/my-custom-sitemap-2.xml',
11 | 'https://example.com/my-custom-sitemap-3.xml',
12 | ],
13 | },
14 | }
15 |
16 | export default config
17 |
--------------------------------------------------------------------------------
/examples/basic/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "basic",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "license": "MIT",
6 | "private": true,
7 | "type": "module",
8 | "scripts": {
9 | "dev": "next",
10 | "build": "next build",
11 | "postbuild": "next-sitemap"
12 | },
13 | "dependencies": {
14 | "@types/react-dom": "^18.2.14",
15 | "next": "^13.5.6",
16 | "react": "^18.2.0",
17 | "react-dom": "^18.2.0"
18 | },
19 | "devDependencies": {
20 | "@types/react": "^18.2.33",
21 | "next-sitemap": "workspace:*"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/examples/basic/pages/[dynamic]/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { GetStaticPaths, GetStaticProps } from 'next'
3 |
4 | const DynamicPage: React.FC = () => {
5 | return (
6 |
7 |
DynamicPage Component
8 |
9 | )
10 | }
11 |
12 | export const getStaticProps: GetStaticProps = async () => {
13 | return {
14 | props: {
15 | dynamic: 'hello',
16 | },
17 | }
18 | }
19 |
20 | export const getStaticPaths: GetStaticPaths = async () => {
21 | return {
22 | paths: [...Array(10000)].map((_, index) => ({
23 | params: {
24 | dynamic: `page-${index}`,
25 | },
26 | })),
27 | fallback: false,
28 | }
29 | }
30 |
31 | export default DynamicPage
32 |
--------------------------------------------------------------------------------
/examples/basic/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const HelloWorld: React.FC = () => {
4 | return (
5 |
6 |
HelloWorld Component
7 |
8 | )
9 | }
10 |
11 | export default HelloWorld
12 |
--------------------------------------------------------------------------------
/examples/basic/pages/server-sitemap-index.xml/index.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
2 | /* eslint-disable @typescript-eslint/no-empty-function */
3 | import { getServerSideSitemapIndexLegacy } from 'next-sitemap'
4 | import { GetServerSideProps } from 'next'
5 |
6 | export const getServerSideProps: GetServerSideProps = async (ctx) => {
7 | // Method to source urls from cms
8 | // const urls = await fetch('https//example.com/api')
9 |
10 | return getServerSideSitemapIndexLegacy(ctx, [
11 | 'https://example.com/path-1.xml',
12 | 'https://example.com/path-2.xml',
13 | ])
14 | }
15 |
16 | // Default export to prevent next.js errors
17 | export default function SitemapIndex() {}
18 |
--------------------------------------------------------------------------------
/examples/basic/pages/server-sitemap.xml/index.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
2 | /* eslint-disable @typescript-eslint/no-empty-function */
3 | import { getServerSideSitemapLegacy } from 'next-sitemap'
4 | import { GetServerSideProps } from 'next'
5 |
6 | export const getServerSideProps: GetServerSideProps = async (ctx) => {
7 | // Method to source urls from cms
8 | // const urls = await fetch('https//example.com/api')
9 |
10 | return getServerSideSitemapLegacy(ctx, [
11 | {
12 | loc: 'https://example.com',
13 | lastmod: new Date().toISOString(),
14 | // changefreq
15 | // priority
16 | },
17 | {
18 | loc: 'https://example.com/dynamic-path-2',
19 | lastmod: new Date().toISOString(),
20 | // changefreq
21 | // priority
22 | },
23 | ])
24 | }
25 |
26 | // Default export to prevent next.js errors
27 | export default function Sitemap() {}
28 |
--------------------------------------------------------------------------------
/examples/basic/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": false,
8 | "forceConsistentCasingInFileNames": true,
9 | "noEmit": true,
10 | "esModuleInterop": true,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "jsx": "preserve",
16 | "incremental": true
17 | },
18 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
19 | "exclude": ["node_modules"]
20 | }
21 |
--------------------------------------------------------------------------------
/examples/commonjs/.gitignore:
--------------------------------------------------------------------------------
1 | public
--------------------------------------------------------------------------------
/examples/commonjs/README.md:
--------------------------------------------------------------------------------
1 | # next-sitemap example
2 |
3 | Sitemap generator for next.js. `next-sitemap` will generate a sitemap file for all pages (including all pre-rendered/static pages).
4 |
5 | This package allows the generation of sitemaps along with `robots.txt` and provides the feature to split large sitemaps into multiple files.
6 |
7 | For detailed use case and example check the [documentation](https://github.com/iamvishnusankar/next-sitemap)
8 |
9 | ## Deploy your own
10 |
11 | Deploy the example using [Vercel](https://vercel.com/now):
12 |
13 | [](https://vercel.com/import/project?template=https://github.com/vercel/next.js/tree/canary/examples/with-next-sitemap)
14 |
15 | ## How to use
16 |
17 | [Documentation](https://github.com/iamvishnusankar/next-sitemap)
18 |
19 | ### Using `create-next-app`
20 |
21 | Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init) or [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) to bootstrap the example:
22 |
23 | ```bash
24 | npx create-next-app --example with-next-sitemap with-next-sitemap-app
25 | # or
26 | yarn create next-app --example with-next-sitemap with-next-sitemap-app
27 | ```
28 |
29 | ### Download manually
30 |
31 | Download the example:
32 |
33 | ```bash
34 | curl https://codeload.github.com/vercel/next.js/tar.gz/canary | tar -xz --strip=2 next.js-canary/examples/with-next-sitemap
35 | cd with-next-sitemap
36 | ```
37 |
38 | Install it and run:
39 |
40 | ```bash
41 | npm install
42 | npm run dev
43 | # or
44 | yarn
45 | yarn dev
46 | ```
47 |
48 | Deploy it to the cloud with [Vercel](https://vercel.com/import?filter=next.js&utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).
49 |
--------------------------------------------------------------------------------
/examples/commonjs/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/basic-features/typescript for more information.
6 |
--------------------------------------------------------------------------------
/examples/commonjs/next-sitemap.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next-sitemap').IConfig} */
2 | module.exports = {
3 | siteUrl: process.env.SITE_URL || 'https://example.com',
4 | generateRobotsTxt: true,
5 | sitemapSize: 1000,
6 | // optional
7 | robotsTxtOptions: {
8 | additionalSitemaps: [
9 | 'https://example.com/my-custom-sitemap-1.xml',
10 | 'https://example.com/my-custom-sitemap-2.xml',
11 | 'https://example.com/my-custom-sitemap-3.xml',
12 | ],
13 | },
14 | }
15 |
--------------------------------------------------------------------------------
/examples/commonjs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "commonjs",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "license": "MIT",
6 | "private": true,
7 | "scripts": {
8 | "dev": "next",
9 | "build": "next build",
10 | "postbuild": "next-sitemap"
11 | },
12 | "dependencies": {
13 | "@types/react-dom": "^18.2.14",
14 | "next": "^13.5.6",
15 | "react": "^18.2.0",
16 | "react-dom": "^18.2.0"
17 | },
18 | "devDependencies": {
19 | "@types/react": "^18.2.33",
20 | "next-sitemap": "workspace:*"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/examples/commonjs/pages/[dynamic]/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { GetStaticPaths, GetStaticProps } from 'next'
3 |
4 | const DynamicPage: React.FC = () => {
5 | return (
6 |
7 |
DynamicPage Component
8 |
9 | )
10 | }
11 |
12 | export const getStaticProps: GetStaticProps = async () => {
13 | return {
14 | props: {
15 | dynamic: 'hello',
16 | },
17 | }
18 | }
19 |
20 | export const getStaticPaths: GetStaticPaths = async () => {
21 | return {
22 | paths: [...Array(10000)].map((_, index) => ({
23 | params: {
24 | dynamic: `page-${index}`,
25 | },
26 | })),
27 | fallback: false,
28 | }
29 | }
30 |
31 | export default DynamicPage
32 |
--------------------------------------------------------------------------------
/examples/commonjs/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const HelloWorld: React.FC = () => {
4 | return (
5 |
6 |
HelloWorld Component
7 |
8 | )
9 | }
10 |
11 | export default HelloWorld
12 |
--------------------------------------------------------------------------------
/examples/commonjs/pages/server-sitemap-index.xml/index.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
2 | /* eslint-disable @typescript-eslint/no-empty-function */
3 | import { getServerSideSitemapIndexLegacy } from 'next-sitemap'
4 | import { GetServerSideProps } from 'next'
5 |
6 | export const getServerSideProps: GetServerSideProps = async (ctx) => {
7 | // Method to source urls from cms
8 | // const urls = await fetch('https//example.com/api')
9 |
10 | return getServerSideSitemapIndexLegacy(ctx, [
11 | 'https://example.com/path-1.xml',
12 | 'https://example.com/path-2.xml',
13 | ])
14 | }
15 |
16 | // Default export to prevent next.js errors
17 | export default function SitemapIndex() {}
18 |
--------------------------------------------------------------------------------
/examples/commonjs/pages/server-sitemap.xml/index.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
2 | /* eslint-disable @typescript-eslint/no-empty-function */
3 | import { getServerSideSitemapLegacy } from 'next-sitemap'
4 | import { GetServerSideProps } from 'next'
5 |
6 | export const getServerSideProps: GetServerSideProps = async (ctx) => {
7 | // Method to source urls from cms
8 | // const urls = await fetch('https//example.com/api')
9 |
10 | return getServerSideSitemapLegacy(ctx, [
11 | {
12 | loc: 'https://example.com',
13 | lastmod: new Date().toISOString(),
14 | // changefreq
15 | // priority
16 | },
17 | {
18 | loc: 'https://example.com/dynamic-path-2',
19 | lastmod: new Date().toISOString(),
20 | // changefreq
21 | // priority
22 | },
23 | ])
24 | }
25 |
26 | // Default export to prevent next.js errors
27 | export default function Sitemap() {}
28 |
--------------------------------------------------------------------------------
/examples/commonjs/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": false,
8 | "forceConsistentCasingInFileNames": true,
9 | "noEmit": true,
10 | "esModuleInterop": true,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "jsx": "preserve",
16 | "incremental": true
17 | },
18 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
19 | "exclude": ["node_modules"]
20 | }
21 |
--------------------------------------------------------------------------------
/examples/custom-config-file/.gitignore:
--------------------------------------------------------------------------------
1 | public
--------------------------------------------------------------------------------
/examples/custom-config-file/README.md:
--------------------------------------------------------------------------------
1 | # next-sitemap example
2 |
3 | Sitemap generator for next.js. `next-sitemap` will generate a sitemap file for all pages (including all pre-rendered/static pages).
4 |
5 | This package allows the generation of sitemaps along with `robots.txt` and provides the feature to split large sitemaps into multiple files.
6 |
7 | For detailed use case and example check the [documentation](https://github.com/iamvishnusankar/next-sitemap)
8 |
9 | ## Deploy your own
10 |
11 | Deploy the example using [Vercel](https://vercel.com/now):
12 |
13 | [](https://vercel.com/import/project?template=https://github.com/vercel/next.js/tree/canary/examples/with-next-sitemap)
14 |
15 | ## How to use
16 |
17 | [Documentation](https://github.com/iamvishnusankar/next-sitemap)
18 |
19 | ### Using `create-next-app`
20 |
21 | Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init) or [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) to bootstrap the example:
22 |
23 | ```bash
24 | npx create-next-app --example with-next-sitemap with-next-sitemap-app
25 | # or
26 | yarn create next-app --example with-next-sitemap with-next-sitemap-app
27 | ```
28 |
29 | ### Download manually
30 |
31 | Download the example:
32 |
33 | ```bash
34 | curl https://codeload.github.com/vercel/next.js/tar.gz/canary | tar -xz --strip=2 next.js-canary/examples/with-next-sitemap
35 | cd with-next-sitemap
36 | ```
37 |
38 | Install it and run:
39 |
40 | ```bash
41 | npm install
42 | npm run dev
43 | # or
44 | yarn
45 | yarn dev
46 | ```
47 |
48 | Deploy it to the cloud with [Vercel](https://vercel.com/import?filter=next.js&utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).
49 |
--------------------------------------------------------------------------------
/examples/custom-config-file/awesome-sitemap.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next-sitemap').IConfig} */
2 | const config = {
3 | siteUrl: process.env.SITE_URL || 'https://example.com',
4 | generateRobotsTxt: true,
5 | generateIndexSitemap: false,
6 | sitemapSize: 1000,
7 | // optional
8 | robotsTxtOptions: {
9 | additionalSitemaps: [
10 | 'https://example.com/my-custom-sitemap-1.xml',
11 | 'https://example.com/my-custom-sitemap-2.xml',
12 | 'https://example.com/my-custom-sitemap-3.xml',
13 | ],
14 | },
15 | }
16 |
17 | export default config
18 |
--------------------------------------------------------------------------------
/examples/custom-config-file/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/basic-features/typescript for more information.
6 |
--------------------------------------------------------------------------------
/examples/custom-config-file/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "custom-config-file",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "license": "MIT",
6 | "private": true,
7 | "type": "module",
8 | "scripts": {
9 | "dev": "next",
10 | "build": "next build",
11 | "postbuild": "next-sitemap --config=awesome-sitemap.config.js"
12 | },
13 | "dependencies": {
14 | "@types/react-dom": "^18.2.14",
15 | "next": "^13.5.6",
16 | "react": "^18.2.0",
17 | "react-dom": "^18.2.0"
18 | },
19 | "devDependencies": {
20 | "@types/react": "^18.2.33",
21 | "next-sitemap": "workspace:*"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/examples/custom-config-file/pages/[dynamic]/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { GetStaticPaths, GetStaticProps } from 'next'
3 |
4 | const DynamicPage: React.FC = () => {
5 | return (
6 |
7 |
DynamicPage Component
8 |
9 | )
10 | }
11 |
12 | export const getStaticProps: GetStaticProps = async () => {
13 | return {
14 | props: {
15 | dynamic: 'hello',
16 | },
17 | }
18 | }
19 |
20 | export const getStaticPaths: GetStaticPaths = async () => {
21 | return {
22 | paths: [...Array(10000)].map((_, index) => ({
23 | params: {
24 | dynamic: `page-${index}`,
25 | },
26 | })),
27 | fallback: false,
28 | }
29 | }
30 |
31 | export default DynamicPage
32 |
--------------------------------------------------------------------------------
/examples/custom-config-file/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const HelloWorld: React.FC = () => {
4 | return (
5 |
6 |
HelloWorld Component
7 |
8 | )
9 | }
10 |
11 | export default HelloWorld
12 |
--------------------------------------------------------------------------------
/examples/custom-config-file/pages/server-sitemap-index.xml/index.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
2 | /* eslint-disable @typescript-eslint/no-empty-function */
3 | import { getServerSideSitemapIndexLegacy } from 'next-sitemap'
4 | import { GetServerSideProps } from 'next'
5 |
6 | export const getServerSideProps: GetServerSideProps = async (ctx) => {
7 | // Method to source urls from cms
8 | // const urls = await fetch('https//example.com/api')
9 |
10 | return getServerSideSitemapIndexLegacy(ctx, [
11 | 'https://example.com/path-1.xml',
12 | 'https://example.com/path-2.xml',
13 | ])
14 | }
15 |
16 | // Default export to prevent next.js errors
17 | export default function SitemapIndex() {}
18 |
--------------------------------------------------------------------------------
/examples/custom-config-file/pages/server-sitemap.xml/index.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
2 | /* eslint-disable @typescript-eslint/no-empty-function */
3 | import { getServerSideSitemapLegacy } from 'next-sitemap'
4 | import { GetServerSideProps } from 'next'
5 |
6 | export const getServerSideProps: GetServerSideProps = async (ctx) => {
7 | // Method to source urls from cms
8 | // const urls = await fetch('https//example.com/api')
9 |
10 | return getServerSideSitemapLegacy(ctx, [
11 | {
12 | loc: 'https://example.com',
13 | lastmod: new Date().toISOString(),
14 | // changefreq
15 | // priority
16 | },
17 | {
18 | loc: 'https://example.com/dynamic-path-2',
19 | lastmod: new Date().toISOString(),
20 | // changefreq
21 | // priority
22 | },
23 | ])
24 | }
25 |
26 | // Default export to prevent next.js errors
27 | export default function Sitemap() {}
28 |
--------------------------------------------------------------------------------
/examples/custom-config-file/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": false,
8 | "forceConsistentCasingInFileNames": true,
9 | "noEmit": true,
10 | "esModuleInterop": true,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "jsx": "preserve",
16 | "incremental": true
17 | },
18 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
19 | "exclude": ["node_modules"]
20 | }
21 |
--------------------------------------------------------------------------------
/examples/custom-overrides/.gitignore:
--------------------------------------------------------------------------------
1 | public
--------------------------------------------------------------------------------
/examples/custom-overrides/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/basic-features/typescript for more information.
6 |
--------------------------------------------------------------------------------
/examples/custom-overrides/next-sitemap.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next-sitemap').IConfig} */
2 | const config = {
3 | siteUrl: 'https://example.com',
4 | generateRobotsTxt: true,
5 | trailingSlash: true, // Override next.config.js
6 | additionalPaths: async (config) => [
7 | await config.transform(
8 | {
9 | ...config,
10 | trailingSlash: false, // Override for custom path
11 | },
12 | '/additional-page.html',
13 | ),
14 | await config.transform(
15 | {
16 | ...config,
17 | },
18 | '/page-with-trailing-slash',
19 | ),
20 | ],
21 | }
22 |
23 | export default config
24 |
--------------------------------------------------------------------------------
/examples/custom-overrides/next.config.js:
--------------------------------------------------------------------------------
1 | const config = {
2 | reactStrictMode: true,
3 | trailingSlash: true,
4 | }
5 |
6 | export default config
7 |
--------------------------------------------------------------------------------
/examples/custom-overrides/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "with-custom-overrides",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "license": "MIT",
6 | "private": true,
7 | "type": "module",
8 | "scripts": {
9 | "dev": "next",
10 | "build": "next build",
11 | "postbuild": "next-sitemap"
12 | },
13 | "dependencies": {
14 | "@types/react-dom": "^18.2.14",
15 | "next": "^13.5.6",
16 | "react": "^18.2.0",
17 | "react-dom": "^18.2.0"
18 | },
19 | "devDependencies": {
20 | "@types/react": "^18.2.33",
21 | "next-sitemap": "workspace:*"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/examples/custom-overrides/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const HomePage: React.FC = () => {
4 | return (
5 |
6 |
HomePage Component
7 |
8 | )
9 | }
10 |
11 | export default HomePage
12 |
--------------------------------------------------------------------------------
/examples/custom-overrides/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": false,
8 | "forceConsistentCasingInFileNames": true,
9 | "noEmit": true,
10 | "incremental": true,
11 | "esModuleInterop": true,
12 | "module": "esnext",
13 | "moduleResolution": "node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "jsx": "preserve"
17 | },
18 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
19 | "exclude": ["node_modules"]
20 | }
21 |
--------------------------------------------------------------------------------
/examples/custom-robots-txt-transformer/.gitignore:
--------------------------------------------------------------------------------
1 | public
--------------------------------------------------------------------------------
/examples/custom-robots-txt-transformer/README.md:
--------------------------------------------------------------------------------
1 | # next-sitemap example
2 |
3 | Sitemap generator for next.js. `next-sitemap` will generate a sitemap file for all pages (including all pre-rendered/static pages).
4 |
5 | This package allows the generation of sitemaps along with `robots.txt` and provides the feature to split large sitemaps into multiple files.
6 |
7 | For detailed use case and example check the [documentation](https://github.com/iamvishnusankar/next-sitemap)
8 |
9 | ## Deploy your own
10 |
11 | Deploy the example using [Vercel](https://vercel.com/now):
12 |
13 | [](https://vercel.com/import/project?template=https://github.com/vercel/next.js/tree/canary/examples/with-next-sitemap)
14 |
15 | ## How to use
16 |
17 | [Documentation](https://github.com/iamvishnusankar/next-sitemap)
18 |
19 | ### Using `create-next-app`
20 |
21 | Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init) or [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) to bootstrap the example:
22 |
23 | ```bash
24 | npx create-next-app --example with-next-sitemap with-next-sitemap-app
25 | # or
26 | yarn create next-app --example with-next-sitemap with-next-sitemap-app
27 | ```
28 |
29 | ### Download manually
30 |
31 | Download the example:
32 |
33 | ```bash
34 | curl https://codeload.github.com/vercel/next.js/tar.gz/canary | tar -xz --strip=2 next.js-canary/examples/with-next-sitemap
35 | cd with-next-sitemap
36 | ```
37 |
38 | Install it and run:
39 |
40 | ```bash
41 | npm install
42 | npm run dev
43 | # or
44 | yarn
45 | yarn dev
46 | ```
47 |
48 | Deploy it to the cloud with [Vercel](https://vercel.com/import?filter=next.js&utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).
49 |
--------------------------------------------------------------------------------
/examples/custom-robots-txt-transformer/art.js:
--------------------------------------------------------------------------------
1 | export const asciiArt = `⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⣤⣤⣤⣤⣄⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
2 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣤⣶⡿⠛⠋⠉⠉⠉⠉⠉⡿⠛⠻⠷⣶⣤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
3 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣾⠟⠉⠀⠙⣦⣀⣀⣀⣠⡤⠴⡿⣄⡀⠀⠀⠉⠻⢷⣤⡀⠀⠀⠀⠀⠀⠀⠀⠀
4 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⡟⠁⠀⠀⠀⣰⠋⢧⠀⠀⠀⠀⠀⡇⠀⠉⠙⠓⠒⡶⢯⣙⣿⣆⠀⠀⠀⠀⠀⠀⠀
5 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⡟⠀⠀⠀⢀⠞⠁⠀⠈⣳⡤⠤⠴⠚⣟⠛⠒⠒⠒⣺⠳⢤⣀⣉⣻⣷⡀⠀⠀⠀⠀⠀
6 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⡿⠶⠶⣤⣾⡁⠀⠀⢀⡜⠉⣧⣠⣤⣴⣾⣶⠶⠶⠶⠷⣶⣶⣶⣬⣭⣙⣷⡀⠀⠀⠀⠀
7 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⠁⠀⠀⣿⠀⠙⢲⣞⠁⣠⡾⠟⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠙⠻⢷⣄⠀⠀⠀
8 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⡏⠀⠀⠀⡇⠀⠀⢸⠏⣿⡏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢷⡄⠀
9 | ⠀⠀⠀⢀⣠⣴⣶⣶⣦⣾⠃⠀⠀⢠⡇⠀⠀⣸⠀⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣿⡄
10 | ⠀⠀⣠⡿⠿⡄⠀⠀⠈⣿⡀⠀⠀⡼⠀⠀⣴⣃⣤⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⡇
11 | ⠀⣰⡿⠁⠀⠹⡄⠀⠀⣿⠿⣶⣴⡷⠒⠋⠻⡄⠀⢹⣧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⠃
12 | ⢀⣿⠁⠀⠀⠀⢹⣀⣴⡏⠀⠀⠙⢿⣦⠀⠀⢹⡄⠀⠻⣧⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣿⠋⠀
13 | ⢸⡿⢧⠀⠀⢀⡼⠯⣼⡇⠀⠀⠀⠀⠙⣷⡄⠀⣇⡤⠞⠉⠻⢷⣤⣄⣀⣀⠀⠀⠀⠀⠀⠀⣀⣀⣀⣤⣶⣿⣿⣿⠀⠀
14 | ⣿⡇⠈⢧⣠⠎⠀⠀⢸⡇⠀⠀⠀⠀⠀⠘⣿⡞⠛⠢⣄⠀⣠⠏⠈⠉⡿⠛⠛⠛⢻⠛⠛⠛⠛⢿⠉⠁⣴⠟⠁⣿⠀⠀
15 | ⣿⠃⠀⡼⠧⣄⠀⠀⢸⡇⠀⠀⠀⠀⠀⠀⠘⣷⡀⠀⠘⣶⣁⡀⠀⡼⠁⢀⡀⠀⠘⡇⣀⣠⣤⣬⣷⣾⠏⠀⠀⣿⠀⠀
16 | ⣿⣀⡞⠁⠀⠈⢣⡀⢸⡇⠀⠀⠀⠀⠀⠀⠀⢹⣇⠀⡼⠁⠀⠉⣹⠛⠉⠉⡉⠉⢙⣏⠁⠀⠀⠀⣼⡏⠀⠀⠀⣿⠀⠀
17 | ⣿⡿⣄⠀⠀⠀⠀⢳⣼⡇⠀⠀⠀⠀⠀⠀⠀⠈⣿⡾⠁⠀⠀⢀⡇⢠⠂⣜⣠⣤⠸⡟⢣⠀⠀⢰⡿⠀⠀⠀⠀⣿⠀⠀
18 | ⣿⡇⠈⠳⡄⠀⠀⣨⢿⡇⠀⠀⠀⠀⠀⠀⠀⠀⣿⡗⠒⠲⢤⣸⠀⣸⣄⣿⣿⣿⣷⣿⣞⣠⣤⣿⠇⠀⠀⠀⠀⣿⠀⠀
19 | ⢸⣇⠀⠀⢹⡀⡰⠃⢸⡇⠀⠀⠀⠀⠀⠀⠀⠀⢸⡇⠀⠀⠀⣹⢉⡽⣿⢿⣿⣿⣿⣿⣅⠀⠀⣿⠀⠀⠀⠀⢀⣿⠀⠀
20 | ⠘⣿⡀⠀⠈⡿⠁⠀⢸⣷⣦⣤⣤⣄⣀⡀⠀⠀⢸⡇⠀⠀⠀⡟⠘⡅⢇⢸⣿⣿⠇⡇⡸⠀⠀⣿⠀⢀⣀⣠⣼⣿⠀⠀
21 | ⠀⢻⣇⠀⣰⠛⠒⠦⣼⡇⠀⠀⠉⠉⠙⢻⣷⣦⣼⡏⠉⠓⠦⣿⠤⠵⠾⠾⠿⢿⣸⣯⠧⠖⠚⣿⡾⠟⠋⠉⣹⡇⠀⠀
22 | ⠀⠈⢿⣶⠇⠀⠀⠀⢸⣿⣶⣤⣤⣤⣀⣼⣀⣈⣙⣃⡀⠀⠀⢹⡀⠀⠀⢀⣀⣀⣸⣁⣀⣀⣤⣤⣤⣶⠶⠿⣿⡇⠀⠀
23 | ⠀⠀⠈⠻⣷⣄⠀⠀⢘⣧⠀⠀⠉⠉⠉⠙⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⢉⡉⠉⠉⠀⠀⠀⢀⣿⠁⠀⠀
24 | ⠀⠀⠀⠀⠈⠙⠛⠿⠻⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⡷⠶⠶⠶⢶⡶⠿⠿⠿⠛⠋⠀⠀⢀⣀⣤⣾⡿⠀⠀⠀
25 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣤⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⡇⠀⠀⠀⢸⣧⣤⣤⣤⣶⣶⠶⠿⠛⠋⠁⣼⡇⠀⠀⠀
26 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢿⡍⠙⠛⢿⠿⠷⠶⠶⠾⠿⠿⠟⢻⡇⠀⠀⠀⢸⡏⠉⠁⠀⣀⣀⣀⣀⣄⣀⣀⣿⠁⠀⠀⠀
27 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⡟⠲⢦⣼⣀⣀⣀⣤⣤⣀⣀⡀⢸⡇⠀⠀⠀⢸⣷⠖⠚⠉⠉⠀⠀⠀⠀⠀⣸⡏⠀⠀⠀⠀
28 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⣿⠀⠀⡏⠁⠀⠀⠀⠀⠀⠈⠉⣿⠇⠀⠀⠀⠀⢿⣆⠀⠀⠀⠀⠀⠀⠀⣠⡿⠁⠀⠀⠀⠀
29 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠻⣧⣀⡇⠀⠀⠀⠀⠀⢀⣠⣾⠟⠀⠀⠀⠀⠀⠈⠻⢷⣶⣶⣶⣶⡶⠿⠛⠁⠀⠀⠀⠀⠀
30 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠛⠛⠿⠿⠿⠿⠿⠛⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀`
31 |
--------------------------------------------------------------------------------
/examples/custom-robots-txt-transformer/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/basic-features/typescript for more information.
6 |
--------------------------------------------------------------------------------
/examples/custom-robots-txt-transformer/next-sitemap.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next-sitemap').IConfig} */
2 | import { asciiArt } from './art.js'
3 |
4 | const config = {
5 | siteUrl: process.env.SITE_URL || 'https://example.com',
6 | generateRobotsTxt: true,
7 | generateIndexSitemap: false,
8 | sitemapSize: 1000,
9 | robotsTxtOptions: {
10 | transformRobotsTxt: async (_, robotsTxt) => `${robotsTxt}\n\n${asciiArt}`,
11 | },
12 | }
13 |
14 | export default config
15 |
--------------------------------------------------------------------------------
/examples/custom-robots-txt-transformer/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "with-custom-robots-txt-transformer",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "license": "MIT",
6 | "private": true,
7 | "type": "module",
8 | "scripts": {
9 | "dev": "next",
10 | "build": "next build",
11 | "postbuild": "next-sitemap"
12 | },
13 | "dependencies": {
14 | "@types/react-dom": "^18.2.14",
15 | "next": "^13.5.6",
16 | "react": "^18.2.0",
17 | "react-dom": "^18.2.0"
18 | },
19 | "devDependencies": {
20 | "@types/react": "^18.2.33",
21 | "next-sitemap": "workspace:*"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/examples/custom-robots-txt-transformer/pages/[dynamic]/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { GetStaticPaths, GetStaticProps } from 'next'
3 |
4 | const DynamicPage: React.FC = () => {
5 | return (
6 |
7 |
DynamicPage Component
8 |
9 | )
10 | }
11 |
12 | export const getStaticProps: GetStaticProps = async () => {
13 | return {
14 | props: {
15 | dynamic: 'hello',
16 | },
17 | }
18 | }
19 |
20 | export const getStaticPaths: GetStaticPaths = async () => {
21 | return {
22 | paths: [...Array(10000)].map((_, index) => ({
23 | params: {
24 | dynamic: `page-${index}`,
25 | },
26 | })),
27 | fallback: false,
28 | }
29 | }
30 |
31 | export default DynamicPage
32 |
--------------------------------------------------------------------------------
/examples/custom-robots-txt-transformer/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const HelloWorld: React.FC = () => {
4 | return (
5 |
6 |
HelloWorld Component
7 |
8 | )
9 | }
10 |
11 | export default HelloWorld
12 |
--------------------------------------------------------------------------------
/examples/custom-robots-txt-transformer/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": false,
8 | "forceConsistentCasingInFileNames": true,
9 | "noEmit": true,
10 | "esModuleInterop": true,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "jsx": "preserve",
16 | "incremental": true
17 | },
18 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
19 | "exclude": ["node_modules"]
20 | }
21 |
--------------------------------------------------------------------------------
/examples/no-index-sitemaps/.gitignore:
--------------------------------------------------------------------------------
1 | public
--------------------------------------------------------------------------------
/examples/no-index-sitemaps/README.md:
--------------------------------------------------------------------------------
1 | # next-sitemap example
2 |
3 | Sitemap generator for next.js. `next-sitemap` will generate a sitemap file for all pages (including all pre-rendered/static pages).
4 |
5 | This package allows the generation of sitemaps along with `robots.txt` and provides the feature to split large sitemaps into multiple files.
6 |
7 | For detailed use case and example check the [documentation](https://github.com/iamvishnusankar/next-sitemap)
8 |
9 | ## Deploy your own
10 |
11 | Deploy the example using [Vercel](https://vercel.com/now):
12 |
13 | [](https://vercel.com/import/project?template=https://github.com/vercel/next.js/tree/canary/examples/with-next-sitemap)
14 |
15 | ## How to use
16 |
17 | [Documentation](https://github.com/iamvishnusankar/next-sitemap)
18 |
19 | ### Using `create-next-app`
20 |
21 | Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init) or [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) to bootstrap the example:
22 |
23 | ```bash
24 | npx create-next-app --example with-next-sitemap with-next-sitemap-app
25 | # or
26 | yarn create next-app --example with-next-sitemap with-next-sitemap-app
27 | ```
28 |
29 | ### Download manually
30 |
31 | Download the example:
32 |
33 | ```bash
34 | curl https://codeload.github.com/vercel/next.js/tar.gz/canary | tar -xz --strip=2 next.js-canary/examples/with-next-sitemap
35 | cd with-next-sitemap
36 | ```
37 |
38 | Install it and run:
39 |
40 | ```bash
41 | npm install
42 | npm run dev
43 | # or
44 | yarn
45 | yarn dev
46 | ```
47 |
48 | Deploy it to the cloud with [Vercel](https://vercel.com/import?filter=next.js&utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).
49 |
--------------------------------------------------------------------------------
/examples/no-index-sitemaps/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/basic-features/typescript for more information.
6 |
--------------------------------------------------------------------------------
/examples/no-index-sitemaps/next-sitemap.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next-sitemap').IConfig} */
2 | const config = {
3 | siteUrl: process.env.SITE_URL || 'https://example.com',
4 | generateRobotsTxt: true,
5 | generateIndexSitemap: false,
6 | sitemapSize: 1000,
7 | }
8 |
9 | export default config
10 |
--------------------------------------------------------------------------------
/examples/no-index-sitemaps/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "with-no-index-sitemaps",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "license": "MIT",
6 | "private": true,
7 | "type": "module",
8 | "scripts": {
9 | "dev": "next",
10 | "build": "next build",
11 | "postbuild": "next-sitemap"
12 | },
13 | "dependencies": {
14 | "@types/react-dom": "^18.2.14",
15 | "next": "^13.5.6",
16 | "react": "^18.2.0",
17 | "react-dom": "^18.2.0"
18 | },
19 | "devDependencies": {
20 | "@types/react": "^18.2.33",
21 | "next-sitemap": "workspace:*"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/examples/no-index-sitemaps/pages/[dynamic]/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { GetStaticPaths, GetStaticProps } from 'next'
3 |
4 | const DynamicPage: React.FC = () => {
5 | return (
6 |
7 |
DynamicPage Component
8 |
9 | )
10 | }
11 |
12 | export const getStaticProps: GetStaticProps = async () => {
13 | return {
14 | props: {
15 | dynamic: 'hello',
16 | },
17 | }
18 | }
19 |
20 | export const getStaticPaths: GetStaticPaths = async () => {
21 | return {
22 | paths: [...Array(10000)].map((_, index) => ({
23 | params: {
24 | dynamic: `page-${index}`,
25 | },
26 | })),
27 | fallback: false,
28 | }
29 | }
30 |
31 | export default DynamicPage
32 |
--------------------------------------------------------------------------------
/examples/no-index-sitemaps/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const HelloWorld: React.FC = () => {
4 | return (
5 |
6 |
HelloWorld Component
7 |
8 | )
9 | }
10 |
11 | export default HelloWorld
12 |
--------------------------------------------------------------------------------
/examples/no-index-sitemaps/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": false,
8 | "forceConsistentCasingInFileNames": true,
9 | "noEmit": true,
10 | "esModuleInterop": true,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "jsx": "preserve",
16 | "incremental": true
17 | },
18 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
19 | "exclude": ["node_modules"]
20 | }
21 |
--------------------------------------------------------------------------------
/examples/static-export/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | .env*.local
29 |
30 | # vercel
31 | .vercel
32 |
33 | # typescript
34 | *.tsbuildinfo
35 | next-env.d.ts
36 |
--------------------------------------------------------------------------------
/examples/static-export/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | # or
12 | pnpm dev
13 | ```
14 |
15 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
16 |
17 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
18 |
19 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
20 |
21 | ## Learn More
22 |
23 | To learn more about Next.js, take a look at the following resources:
24 |
25 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
26 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
27 |
28 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
29 |
30 | ## Deploy on Vercel
31 |
32 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
33 |
34 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
35 |
--------------------------------------------------------------------------------
/examples/static-export/app/about/more/page.tsx:
--------------------------------------------------------------------------------
1 | const HomePage: React.FC = () => {
2 | return (
3 |
4 |
About more page
5 |
6 | )
7 | }
8 |
9 | export default HomePage
10 |
--------------------------------------------------------------------------------
/examples/static-export/app/about/page.tsx:
--------------------------------------------------------------------------------
1 | const HomePage: React.FC = () => {
2 | return (
3 |
4 |
About page
5 |
6 | )
7 | }
8 |
9 | export default HomePage
10 |
--------------------------------------------------------------------------------
/examples/static-export/app/layout.tsx:
--------------------------------------------------------------------------------
1 | export default function RootLayout({
2 | children,
3 | }: {
4 | children: React.ReactNode
5 | }) {
6 | return (
7 |
8 | {children}
9 |
10 | )
11 | }
12 |
--------------------------------------------------------------------------------
/examples/static-export/app/page.tsx:
--------------------------------------------------------------------------------
1 | const HomePage: React.FC = () => {
2 | return (
3 |
4 |
HomePage Component
5 |
6 | )
7 | }
8 |
9 | export default HomePage
10 |
--------------------------------------------------------------------------------
/examples/static-export/next-sitemap.config.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-var-requires */
2 | /** @type {import('next-sitemap').IConfig} */
3 | const config = {
4 | siteUrl: process.env.SITE_URL || 'https://example.com',
5 | generateRobotsTxt: true,
6 | output: 'export', // Set static output here
7 | }
8 |
9 | module.exports = config
10 |
--------------------------------------------------------------------------------
/examples/static-export/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | output: 'export',
4 | }
5 |
6 | module.exports = nextConfig
7 |
--------------------------------------------------------------------------------
/examples/static-export/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "static-export",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build && next-sitemap",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "devDependencies": {
12 | "next-sitemap": "workspace:*"
13 | },
14 | "dependencies": {
15 | "next": "^13.5.6",
16 | "react": "^18.2.0",
17 | "react-dom": "^18.2.0"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/examples/static-export/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/static-export/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/static-export/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "noEmit": true,
10 | "esModuleInterop": true,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "jsx": "preserve",
16 | "incremental": true,
17 | "plugins": [
18 | {
19 | "name": "next"
20 | }
21 | ],
22 | "paths": {
23 | "@/*": ["./*"]
24 | }
25 | },
26 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
27 | "exclude": ["node_modules"]
28 | }
29 |
--------------------------------------------------------------------------------
/examples/with-next-sitemap-i18n/README.md:
--------------------------------------------------------------------------------
1 | # next-sitemap example
2 |
3 | Sitemap generator for next.js. `next-sitemap` will generate a sitemap file for all pages (including all pre-rendered/static pages).
4 |
5 | This package allows the generation of sitemaps along with `robots.txt` and provides the feature to split large sitemaps into multiple files.
6 |
7 | For detailed use case and example check the [documentation](https://github.com/iamvishnusankar/next-sitemap)
8 |
9 | ## Deploy your own
10 |
11 | Deploy the example using [Vercel](https://vercel.com/now):
12 |
13 | [](https://vercel.com/import/project?template=https://github.com/vercel/next.js/tree/canary/examples/with-next-sitemap)
14 |
15 | ## How to use
16 |
17 | [Documentation](https://github.com/iamvishnusankar/next-sitemap)
18 |
19 | ### Using `create-next-app`
20 |
21 | Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init) or [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) to bootstrap the example:
22 |
23 | ```bash
24 | npx create-next-app --example with-next-sitemap with-next-sitemap-app
25 | # or
26 | yarn create next-app --example with-next-sitemap with-next-sitemap-app
27 | ```
28 |
29 | ### Download manually
30 |
31 | Download the example:
32 |
33 | ```bash
34 | curl https://codeload.github.com/vercel/next.js/tar.gz/canary | tar -xz --strip=2 next.js-canary/examples/with-next-sitemap
35 | cd with-next-sitemap
36 | ```
37 |
38 | Install it and run:
39 |
40 | ```bash
41 | npm install
42 | npm run dev
43 | # or
44 | yarn
45 | yarn dev
46 | ```
47 |
48 | Deploy it to the cloud with [Vercel](https://vercel.com/import?filter=next.js&utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).
49 |
--------------------------------------------------------------------------------
/examples/with-next-sitemap-i18n/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/basic-features/typescript for more information.
6 |
--------------------------------------------------------------------------------
/examples/with-next-sitemap-i18n/next-sitemap.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next-sitemap').IConfig} */
2 | const config = {
3 | siteUrl: process.env.SITE_URL || 'https://example.com',
4 | generateRobotsTxt: true,
5 | // optional
6 | robotsTxtOptions: {
7 | additionalSitemaps: [
8 | 'https://example.com/my-custom-sitemap-1.xml',
9 | 'https://example.com/my-custom-sitemap-2.xml',
10 | 'https://example.com/my-custom-sitemap-3.xml',
11 | ],
12 | },
13 | alternateRefs: [
14 | {
15 | href: 'https://es.example.com',
16 | hreflang: 'es',
17 | },
18 | {
19 | href: 'https://fr.example.com',
20 | hreflang: 'fr',
21 | },
22 | ],
23 | }
24 |
25 | export default config
26 |
--------------------------------------------------------------------------------
/examples/with-next-sitemap-i18n/next.config.js:
--------------------------------------------------------------------------------
1 | const config = {
2 | i18n: {
3 | locales: ['en-US', 'fr', 'nl-NL', 'nl-BE'],
4 | defaultLocale: 'en-US',
5 | },
6 | }
7 |
8 | export default config
9 |
--------------------------------------------------------------------------------
/examples/with-next-sitemap-i18n/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "with-next-sitemap-i18n",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "license": "MIT",
6 | "private": true,
7 | "type": "module",
8 | "scripts": {
9 | "dev": "next",
10 | "build": "next build",
11 | "postbuild": "next-sitemap"
12 | },
13 | "dependencies": {
14 | "@types/react-dom": "^18.2.14",
15 | "next": "^13.5.6",
16 | "react": "^18.2.0",
17 | "react-dom": "^18.2.0"
18 | },
19 | "devDependencies": {
20 | "@types/react": "^18.2.33",
21 | "next-sitemap": "workspace:*"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/examples/with-next-sitemap-i18n/pages/[dynamic]/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { GetStaticPaths, GetStaticProps } from 'next'
3 |
4 | const DynamicPage: React.FC = () => {
5 | return (
6 |
7 |
DynamicPage Component
8 |
9 | )
10 | }
11 |
12 | export const getStaticProps: GetStaticProps = async ({ params }) => {
13 | return {
14 | props: {
15 | dynamic: params.dynamic,
16 | },
17 | }
18 | }
19 |
20 | export const getStaticPaths: GetStaticPaths = async () => {
21 | const pages = [
22 | { id: 'team', locale: 'en-US' },
23 | { id: 'team', locale: 'fr' },
24 | { id: 'team', locale: 'nl-NL' },
25 | { id: 'careers', locale: 'en-US' },
26 | { id: 'careers', locale: 'fr' },
27 | { id: 'careers', locale: 'nl-BE' },
28 | ]
29 |
30 | return {
31 | paths: pages.map(({ id, locale }) => ({
32 | params: {
33 | dynamic: id,
34 | },
35 | locale,
36 | })),
37 | fallback: false,
38 | }
39 | }
40 |
41 | export default DynamicPage
42 |
--------------------------------------------------------------------------------
/examples/with-next-sitemap-i18n/pages/about.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { GetStaticProps } from 'next'
3 |
4 | const About: React.FC = () => {
5 | return (
6 |
7 |
About Component
8 |
9 | )
10 | }
11 |
12 | export const getStaticProps: GetStaticProps = async ({ locale }) => {
13 | if (!['en-US', 'en-NL'].includes(locale)) {
14 | return {
15 | notFound: true,
16 | }
17 | }
18 |
19 | return {
20 | props: {},
21 | }
22 | }
23 |
24 | export default About
25 |
--------------------------------------------------------------------------------
/examples/with-next-sitemap-i18n/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { GetStaticProps } from 'next'
3 |
4 | const HelloWorld: React.FC = () => {
5 | return (
6 |
7 |
HelloWorld Component
8 |
9 | )
10 | }
11 |
12 | export const getStaticProps: GetStaticProps = async ({ locale }) => {
13 | if (!['en-US', 'fr'].includes(locale)) {
14 | return {
15 | notFound: true,
16 | }
17 | }
18 |
19 | return {
20 | props: {},
21 | }
22 | }
23 |
24 | export default HelloWorld
25 |
--------------------------------------------------------------------------------
/examples/with-next-sitemap-i18n/pages/server-sitemap-index.xml/index.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
2 | /* eslint-disable @typescript-eslint/no-empty-function */
3 | import { getServerSideSitemapIndexLegacy } from 'next-sitemap'
4 | import { GetServerSideProps } from 'next'
5 |
6 | export const getServerSideProps: GetServerSideProps = async (ctx) => {
7 | // Method to source urls from cms
8 | // const urls = await fetch('https//example.com/api')
9 |
10 | return getServerSideSitemapIndexLegacy(ctx, [
11 | 'https://example.com/path-1.xml',
12 | 'https://example.com/path-2.xml',
13 | ])
14 | }
15 |
16 | // Default export to prevent next.js errors
17 | export default function SitemapIndex() {}
18 |
--------------------------------------------------------------------------------
/examples/with-next-sitemap-i18n/pages/server-sitemap.xml/index.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
2 | /* eslint-disable @typescript-eslint/no-empty-function */
3 | import { getServerSideSitemapLegacy } from 'next-sitemap'
4 | import { GetServerSideProps } from 'next'
5 |
6 | export const getServerSideProps: GetServerSideProps = async (ctx) => {
7 | // Method to source urls from cms
8 | // const urls = await fetch('https//example.com/api')
9 |
10 | return getServerSideSitemapLegacy(ctx, [
11 | {
12 | loc: 'https://example.com',
13 | lastmod: new Date().toISOString(),
14 | // changefreq
15 | // priority
16 | },
17 | {
18 | loc: 'https://example.com/dynamic-path-2',
19 | lastmod: new Date().toISOString(),
20 | // changefreq
21 | // priority
22 | },
23 | ])
24 | }
25 |
26 | // Default export to prevent next.js errors
27 | export default function Sitemap() {}
28 |
--------------------------------------------------------------------------------
/examples/with-next-sitemap-i18n/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": false,
8 | "forceConsistentCasingInFileNames": true,
9 | "noEmit": true,
10 | "esModuleInterop": true,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "jsx": "preserve",
16 | "incremental": true
17 | },
18 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "next.config.js"],
19 | "exclude": ["node_modules"]
20 | }
21 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "next-sitemap-workspace",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "repository": "https://github.com/iamvishnusankar/next-sitemap.git",
6 | "author": "Vishnu Sankar",
7 | "license": "MIT",
8 | "private": true,
9 | "type": "module",
10 | "workspaces": {
11 | "packages": [
12 | "packages/*",
13 | "examples/*",
14 | "docs"
15 | ]
16 | },
17 | "scripts": {
18 | "clean": "tsc --build --clean",
19 | "dev:test": "bun test --watch",
20 | "dev:tsc": "tsc --build --watch",
21 | "build": "turbo run deploy --force",
22 | "test": "bun test --ci --coverage --verbose",
23 | "lint": "eslint .",
24 | "prettier:check": "prettier --check \"**/*.{js,mjs,cjs,jsx,json,ts,tsx,md,mdx,css,html,yml,yaml,scss,less,graphql,graphqls,gql}\"",
25 | "format": "prettier --write \"**/*.{js,mjs,cjs,jsx,json,ts,tsx,md,mdx,css,html,yml,yaml,scss,less,graphql,graphqls,gql}\""
26 | },
27 | "devDependencies": {
28 | "@corex/workspace": "^4.0.43",
29 | "@typescript-eslint/eslint-plugin": "^6.9.1",
30 | "@typescript-eslint/parser": "^6.9.1",
31 | "eslint": "^8.52.0",
32 | "eslint-config-next": "^14.0.0",
33 | "fast-xml-parser": "^4.3.2",
34 | "prettier": "^3.0.3",
35 | "turbo": "^1.10.16"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/packages/next-sitemap/.npmignore:
--------------------------------------------------------------------------------
1 | src
2 | tsconfig.json
3 |
--------------------------------------------------------------------------------
/packages/next-sitemap/README.md:
--------------------------------------------------------------------------------
1 | [Documentation](https://github.com/iamvishnusankar/next-sitemap)
2 |
--------------------------------------------------------------------------------
/packages/next-sitemap/bin/next-sitemap.cjs:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | /* eslint-disable @typescript-eslint/no-var-requires */
3 | const envLoader = require('@next/env')
4 | const { CLI } = require('../dist/cjs/cli.js')
5 |
6 | // Load environment variables
7 | envLoader.loadEnvConfig(process.cwd(), process.env.NODE_ENV === 'development')
8 |
9 | // Execute CLI
10 | new CLI().execute()
11 |
--------------------------------------------------------------------------------
/packages/next-sitemap/bin/next-sitemap.mjs:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | import envLoader from '@next/env'
3 | import { CLI } from '../dist/esm/cli.js'
4 |
5 | // Load environment variables
6 | envLoader.loadEnvConfig(process.cwd(), process.env.NODE_ENV === 'development')
7 |
8 | // Execute CLI
9 | new CLI().execute()
10 |
--------------------------------------------------------------------------------
/packages/next-sitemap/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "next-sitemap",
3 | "version": "1.0.0",
4 | "description": "Sitemap generator for next.js",
5 | "type": "module",
6 | "main": "./dist/cjs/index.js",
7 | "types": "./dist/@types/index.d.ts",
8 | "exports": {
9 | ".": {
10 | "import": "./dist/esm/index.js",
11 | "require": "./dist/cjs/index.js",
12 | "types": "./dist/@types/index.d.ts"
13 | }
14 | },
15 | "files": [
16 | "dist",
17 | "bin"
18 | ],
19 | "repository": "https://github.com/iamvishnusankar/next-sitemap.git",
20 | "funding": [
21 | {
22 | "url": "https://github.com/iamvishnusankar/next-sitemap.git"
23 | }
24 | ],
25 | "engines": {
26 | "node": ">=14.18"
27 | },
28 | "keywords": [
29 | "nextjs",
30 | "next",
31 | "sitemap",
32 | "seo",
33 | "react"
34 | ],
35 | "author": {
36 | "name": "Vishnu Sankar",
37 | "url": "https://www.iamvishnusankar.com"
38 | },
39 | "bugs": {
40 | "url": "https://github.com/iamvishnusankar/next-sitemap/issues"
41 | },
42 | "license": "MIT",
43 | "sideEffects": false,
44 | "publishConfig": {
45 | "access": "public"
46 | },
47 | "bin": {
48 | "next-sitemap": "./bin/next-sitemap.mjs",
49 | "next-sitemap-cjs": "./bin/next-sitemap.cjs"
50 | },
51 | "scripts": {
52 | "build": "tsc && tsc --module commonjs --outDir dist/cjs",
53 | "postbuild": "tsc --module commonjs --outDir dist/cjs"
54 | },
55 | "dependencies": {
56 | "@corex/deepmerge": "^4.0.43",
57 | "@next/env": "^13.5.6",
58 | "fast-glob": "^3.3.1",
59 | "minimist": "^1.2.8"
60 | },
61 | "peerDependencies": {
62 | "next": "*"
63 | },
64 | "devDependencies": {
65 | "typescript": "^5.2.2"
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/packages/next-sitemap/src/__fixtures__/config.ts:
--------------------------------------------------------------------------------
1 | import type { IConfig } from '../interface.js'
2 | import { withDefaultConfig } from '../utils/defaults.js'
3 |
4 | export const sampleConfig: IConfig = withDefaultConfig({
5 | siteUrl: 'https://example.com',
6 | sourceDir: 'public',
7 | changefreq: 'daily',
8 | priority: 0.7,
9 | sitemapSize: 5000,
10 | generateRobotsTxt: true,
11 | trailingSlash: false,
12 | robotsTxtOptions: {
13 | policies: [
14 | {
15 | userAgent: '*',
16 | allow: '/',
17 | },
18 | {
19 | userAgent: 'black-listed-bot',
20 | disallow: ['/sub-path-1', '/path-2'],
21 | },
22 | {
23 | userAgent: 'friendly-bot',
24 | allow: '/',
25 | crawlDelay: 10,
26 | },
27 | ],
28 | additionalSitemaps: [
29 | 'https://example.com/my-custom-sitemap-1.xml',
30 | 'https://example.com/my-custom-sitemap-2.xml',
31 | 'https://example.com/my-custom-sitemap-3.xml',
32 | ],
33 | },
34 | })
35 |
--------------------------------------------------------------------------------
/packages/next-sitemap/src/builders/__tests__/robots-txt-builder/__snapshots__/generate-robots-txt.test.ts.snap:
--------------------------------------------------------------------------------
1 | // Bun Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`RobotsTxtBuilder generateRobotsTxt: additionalSitemap 1`] = `
4 | "# *
5 | User-agent: *
6 | Allow: /
7 |
8 | # black-listed-bot
9 | User-agent: black-listed-bot
10 | Disallow: /sub-path-1
11 | Disallow: /path-2
12 |
13 | # friendly-bot
14 | User-agent: friendly-bot
15 | Allow: /
16 | Crawl-delay: 10
17 |
18 | # Host
19 | Host: https://example.com
20 |
21 | # Sitemaps
22 | Sitemap: https://example.com/my-custom-sitemap-1.xml
23 | Sitemap: https://example.com/my-custom-sitemap-2.xml
24 | Sitemap: https://example.com/my-custom-sitemap-3.xml
25 | "
26 | `;
27 |
--------------------------------------------------------------------------------
/packages/next-sitemap/src/builders/__tests__/robots-txt-builder/generate-robots-txt.test.ts:
--------------------------------------------------------------------------------
1 | import { sampleConfig } from '../../../__fixtures__/config.js'
2 | import { RobotsTxtBuilder } from '../../robots-txt-builder.js'
3 |
4 | let builder: RobotsTxtBuilder
5 |
6 | beforeEach(() => {
7 | builder = new RobotsTxtBuilder()
8 | })
9 |
10 | describe('RobotsTxtBuilder', () => {
11 | test('generateRobotsTxt: additionalSitemap', () => {
12 | expect(builder.generateRobotsTxt(sampleConfig as any)).toMatchSnapshot()
13 | })
14 | })
15 |
--------------------------------------------------------------------------------
/packages/next-sitemap/src/builders/__tests__/url-set-builder/__snapshots__/absolute-url.test.ts.snap:
--------------------------------------------------------------------------------
1 | // Bun Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`UrlSetBuilder absoluteUrl: With uri encoding 1`] = `"https://example.com/&/'/"/>/</"`;
4 |
--------------------------------------------------------------------------------
/packages/next-sitemap/src/builders/__tests__/url-set-builder/absolute-url.test.ts:
--------------------------------------------------------------------------------
1 | import { sampleConfig } from '../../../__fixtures__/config.js'
2 | import { sampleManifest } from '../../../__fixtures__/manifest.js'
3 | import { UrlSetBuilder } from '../../url-set-builder.js'
4 |
5 | let urlSetBuilder: UrlSetBuilder
6 |
7 | beforeEach(() => {
8 | urlSetBuilder = new UrlSetBuilder(sampleConfig, sampleManifest)
9 | })
10 |
11 | describe('UrlSetBuilder', () => {
12 | test('absoluteUrl: Without trailing slash', () => {
13 | expect(urlSetBuilder.absoluteUrl('https://example.com', '/', false)).toBe(
14 | 'https://example.com',
15 | )
16 |
17 | expect(
18 | urlSetBuilder.absoluteUrl('https://example.com/hello/', '/', false),
19 | ).toBe('https://example.com/hello')
20 | })
21 |
22 | test('absoluteUrl: With trailing slash', () => {
23 | expect(urlSetBuilder.absoluteUrl('https://example.com', '/', true)).toBe(
24 | 'https://example.com/',
25 | )
26 |
27 | expect(
28 | urlSetBuilder.absoluteUrl('https://example.com/hello/', '/', true),
29 | ).toBe('https://example.com/hello/')
30 | })
31 |
32 | test('absoluteUrl: With uri encoding', () => {
33 | expect(
34 | urlSetBuilder.absoluteUrl(`https://example.com/&/'/"/>/<`, '/', true),
35 | ).toMatchSnapshot()
36 | })
37 | })
38 |
--------------------------------------------------------------------------------
/packages/next-sitemap/src/builders/__tests__/url-set-builder/normalize-sitemap-field.test.ts:
--------------------------------------------------------------------------------
1 | import { sampleConfig } from '../../../__fixtures__/config.js'
2 | import { sampleManifest } from '../../../__fixtures__/manifest.js'
3 | import { UrlSetBuilder } from '../../url-set-builder.js'
4 |
5 | describe('UrlSetBuilder', () => {
6 | test('normalizeSitemapField: No sitemap field trailingSlash provided => Use config.trailingSlash', async () => {
7 | // Create builder instance
8 | const builder = new UrlSetBuilder(
9 | {
10 | ...sampleConfig,
11 | trailingSlash: false,
12 | },
13 | sampleManifest,
14 | )
15 |
16 | // Normalize field
17 | const normalizedField = builder.normalizeSitemapField({
18 | changefreq: 'daily',
19 | lastmod: '2021-08-01T00:00:00.000Z',
20 | priority: 0.7,
21 | loc: '/page-2',
22 | alternateRefs: [],
23 | })
24 |
25 | expect(normalizedField).toStrictEqual({
26 | alternateRefs: [],
27 | changefreq: 'daily',
28 | lastmod: '2021-08-01T00:00:00.000Z',
29 | loc: 'https://example.com/page-2',
30 | priority: 0.7,
31 | trailingSlash: false,
32 | })
33 | })
34 |
35 | test('normalizeSitemapField: Sitemap field trailingSlash provided => Use field.trailingSlash', async () => {
36 | // Create builder instance
37 | const builder = new UrlSetBuilder(
38 | {
39 | ...sampleConfig,
40 | trailingSlash: false,
41 | },
42 | sampleManifest,
43 | )
44 |
45 | // Normalize field
46 | const normalizedField = builder.normalizeSitemapField({
47 | changefreq: 'daily',
48 | lastmod: '2021-08-01T00:00:00.000Z',
49 | priority: 0.7,
50 | loc: '/page-2',
51 | alternateRefs: [],
52 | trailingSlash: true,
53 | })
54 |
55 | expect(normalizedField).toStrictEqual({
56 | alternateRefs: [],
57 | changefreq: 'daily',
58 | lastmod: '2021-08-01T00:00:00.000Z',
59 | loc: 'https://example.com/page-2/',
60 | priority: 0.7,
61 | trailingSlash: true,
62 | })
63 | })
64 | })
65 |
--------------------------------------------------------------------------------
/packages/next-sitemap/src/builders/robots-txt-builder.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-non-null-assertion */
2 | import type { IConfig, IRobotPolicy } from '../interface.js'
3 | import { toArray } from '../utils/array.js'
4 |
5 | export class RobotsTxtBuilder {
6 | /**
7 | * Normalize robots.txt policies
8 | * @param policies
9 | * @returns
10 | */
11 | normalizePolicy(policies: IRobotPolicy[]): IRobotPolicy[] {
12 | return policies.map((x) => ({
13 | ...x,
14 | allow: toArray(x.allow ?? []),
15 | disallow: toArray(x.disallow ?? []),
16 | }))
17 | }
18 |
19 | /**
20 | * Add new policy
21 | * @param key
22 | * @param rules
23 | * @returns
24 | */
25 | addPolicies(key: string, rules: string[]): string {
26 | return rules.reduce((prev, curr) => `${prev}${key}: ${curr}\n`, '')
27 | }
28 |
29 | /**
30 | * Generates robots.txt content
31 | * @param config
32 | * @returns
33 | */
34 | generateRobotsTxt(config: IConfig): string {
35 | const { additionalSitemaps, policies } = config.robotsTxtOptions!
36 | const normalizedPolices = this.normalizePolicy(policies!)
37 |
38 | let content = ''
39 |
40 | normalizedPolices.forEach((x) => {
41 | content += `# ${x.userAgent}\nUser-agent: ${x.userAgent}\n`
42 |
43 | if (x.allow) {
44 | content += `${this.addPolicies('Allow', x.allow as string[])}`
45 | }
46 |
47 | if (x.disallow) {
48 | content += `${this.addPolicies('Disallow', x.disallow as string[])}`
49 | }
50 |
51 | if (x.crawlDelay) {
52 | content += `Crawl-delay: ${x.crawlDelay}\n`
53 | }
54 |
55 | content += '\n'
56 | })
57 |
58 | // Append host
59 | content += `# Host\nHost: ${config.siteUrl}\n`
60 |
61 | if (additionalSitemaps && additionalSitemaps.length > 0) {
62 | content += `\n# Sitemaps\n`
63 |
64 | additionalSitemaps.forEach((x) => {
65 | content += `Sitemap: ${x}\n`
66 | })
67 | }
68 |
69 | return content
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/packages/next-sitemap/src/cli.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-non-null-assertion */
2 | import { Logger } from './logger.js'
3 | import { toChunks } from './utils/array.js'
4 | import { ConfigParser } from './parsers/config-parser.js'
5 | import { ManifestParser } from './parsers/manifest-parser.js'
6 | import { UrlSetBuilder } from './builders/url-set-builder.js'
7 | import { ExportableBuilder } from './builders/exportable-builder.js'
8 |
9 | export class CLI {
10 | /**
11 | * Main method
12 | * @returns
13 | */
14 | async main() {
15 | // Load config from `next-sitemap.config.js` along with runtimePaths info
16 | const configParser = new ConfigParser()
17 | const { config, runtimePaths } = await configParser.loadConfig()
18 |
19 | // Load next.js manifest
20 | const manifestParser = new ManifestParser(config, runtimePaths)
21 | const manifest = await manifestParser.loadManifest()
22 |
23 | // Generate url set
24 | const urlSetBuilder = new UrlSetBuilder(config, manifest)
25 | const urlSet = await urlSetBuilder.createUrlSet()
26 |
27 | // Split sitemap into multiple files
28 | const chunks = toChunks(urlSet, config.sitemapSize!)
29 |
30 | // Create ExportableBuilder instance
31 | const expoBuilder = new ExportableBuilder(config, runtimePaths)
32 |
33 | // Register sitemap exports
34 | await expoBuilder.registerSitemaps(chunks)
35 |
36 | // Register index sitemap if user config allows generation
37 | if (config.generateIndexSitemap) {
38 | await expoBuilder.registerIndexSitemap()
39 | }
40 |
41 | // Register robots.txt export if user config allows generation
42 | if (config?.generateRobotsTxt) {
43 | await expoBuilder.registerRobotsTxt()
44 | }
45 |
46 | // Export all files
47 | return expoBuilder.exportAll()
48 | }
49 |
50 | /**
51 | * Execute and log result
52 | * @returns
53 | */
54 | async execute() {
55 | return this.main().then(Logger.generationCompleted).catch(Logger.error)
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/packages/next-sitemap/src/index.ts:
--------------------------------------------------------------------------------
1 | // Export types
2 | export * from './interface.js'
3 |
4 | // Export server side sitemaps
5 | export * from './ssr/response.js'
6 | export * from './ssr/sitemap-index.js'
7 | export * from './ssr/sitemap.js'
8 |
9 | // Export sitemap builder
10 | export * from './builders/sitemap-builder.js'
11 |
--------------------------------------------------------------------------------
/packages/next-sitemap/src/parsers/manifest-parser.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-non-null-assertion */
2 | import type {
3 | INextManifest,
4 | IPreRenderManifest,
5 | IBuildManifest,
6 | IRuntimePaths,
7 | IRoutesManifest,
8 | IConfig,
9 | } from '../interface.js'
10 | import { Logger } from '../logger.js'
11 | import { loadJSON } from '../utils/file.js'
12 | import fg from 'fast-glob'
13 |
14 | export class ManifestParser {
15 | config: IConfig
16 | runtimePaths: IRuntimePaths
17 |
18 | constructor(config: IConfig, runtimePaths: IRuntimePaths) {
19 | this.config = config
20 | this.runtimePaths = runtimePaths
21 | }
22 | /**
23 | * Return paths of html files if config.output = "export"
24 | * @param exportFolder
25 | * @returns
26 | */
27 | async getStaticExportPages(config: IConfig, exportFolder: string) {
28 | // Skip this step if config.output is not export mode
29 | if (config?.output !== 'export') {
30 | return []
31 | }
32 |
33 | // Get html file paths using glob
34 | const htmlFiles = await fg(`${exportFolder}/**/*.html`)
35 |
36 | // Cleanup files
37 | return htmlFiles?.map((file) =>
38 | file
39 | .replace(exportFolder, '')
40 | .replace('index', '')
41 | .replace('.html', '')
42 | .trim(),
43 | )
44 | }
45 |
46 | async loadManifest(): Promise {
47 | // Load build manifest
48 | const buildManifest = await loadJSON(
49 | this.runtimePaths.BUILD_MANIFEST,
50 | )!
51 |
52 | // Throw error if no build manifest exist
53 | if (this.config?.output !== 'export' && !buildManifest) {
54 | throw Logger.noBuildManifest()
55 | }
56 |
57 | // Load pre-render manifest
58 | const preRenderManifest = await loadJSON(
59 | this.runtimePaths.PRERENDER_MANIFEST,
60 | )
61 |
62 | // Load routes manifest
63 | const routesManifest = await loadJSON(
64 | this.runtimePaths.ROUTES_MANIFEST,
65 | )
66 |
67 | // Get static export path when output is set as "export"
68 | const staticExportPages = await this.getStaticExportPages(
69 | this.config,
70 | this.runtimePaths.STATIC_EXPORT_ROOT,
71 | )
72 |
73 | return {
74 | build: buildManifest ?? ({} as any),
75 | preRender: preRenderManifest,
76 | routes: routesManifest,
77 | staticExportPages,
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/packages/next-sitemap/src/ssr/response.ts:
--------------------------------------------------------------------------------
1 | import type { GetServerSidePropsContext } from 'next'
2 |
3 | /**
4 | * Send XML response, supports legacy pages directory
5 | * @param ctx
6 | * @param content
7 | * @returns
8 | */
9 | export const withXMLResponseLegacy = (
10 | ctx: GetServerSidePropsContext,
11 | content: string,
12 | ) => {
13 | if (ctx?.res) {
14 | const { res } = ctx
15 |
16 | // Set header
17 | res.setHeader('Content-Type', 'text/xml')
18 |
19 | // Write the sitemap context to resonse
20 | res.write(content)
21 |
22 | // End response
23 | res.end()
24 | }
25 |
26 | // Empty props
27 | return {
28 | props: {},
29 | }
30 | }
31 |
32 | /**
33 | * Send XML response, as next13+ route response
34 | * @param content
35 | * @param headers Custom request headers
36 | * @returns
37 | */
38 | export const withXMLResponse = (content: string, headers = {}) => {
39 | return new Response(content, {
40 | status: 200,
41 | headers: {
42 | 'Content-Type': 'text/xml',
43 | ...headers,
44 | },
45 | })
46 | }
47 |
--------------------------------------------------------------------------------
/packages/next-sitemap/src/ssr/sitemap-index.ts:
--------------------------------------------------------------------------------
1 | import type { GetServerSidePropsContext } from 'next'
2 | import { SitemapBuilder } from '../builders/sitemap-builder.js'
3 | import { withXMLResponseLegacy, withXMLResponse } from './response.js'
4 |
5 | /**
6 | * Generate index sitemaps on server side, support pages directory
7 | * @param ctx
8 | * @param sitemaps
9 | * @returns
10 | */
11 | export const getServerSideSitemapIndexLegacy = async (
12 | ctx: GetServerSidePropsContext,
13 | sitemaps: string[],
14 | ) => {
15 | // Generate index sitemap xml content
16 | const indexContents = new SitemapBuilder().buildSitemapIndexXml(sitemaps)
17 |
18 | // Return response
19 | return withXMLResponseLegacy(ctx, indexContents)
20 | }
21 |
22 | /**
23 | * Generate index sitemaps on server side, support next13+ route.{ts,js} file.
24 | * To continue using inside pages directory, import `getServerSideSitemapIndexLegacy` instead.
25 | * @param sitemaps
26 | * @param headers Custom request headers
27 | * @returns
28 | */
29 | export const getServerSideSitemapIndex = async (
30 | sitemaps: string[],
31 | headers = {},
32 | ) => {
33 | // Generate index sitemap xml content
34 | const indexContents = new SitemapBuilder().buildSitemapIndexXml(sitemaps)
35 |
36 | // Return response
37 | return withXMLResponse(indexContents, headers)
38 | }
39 |
--------------------------------------------------------------------------------
/packages/next-sitemap/src/ssr/sitemap.ts:
--------------------------------------------------------------------------------
1 | import type { GetServerSidePropsContext } from 'next'
2 | import { withXMLResponseLegacy, withXMLResponse } from './response.js'
3 | import { SitemapBuilder } from '../builders/sitemap-builder.js'
4 | import type { ISitemapField } from '../interface.js'
5 |
6 | /**
7 | * Generate server side sitemaps, supports legacy pages directory
8 | * @param ctx
9 | * @param fields
10 | * @returns
11 | */
12 | export const getServerSideSitemapLegacy = async (
13 | ctx: GetServerSidePropsContext,
14 | fields: ISitemapField[],
15 | ) => {
16 | // Generate sitemap xml
17 | const contents = new SitemapBuilder().buildSitemapXml(fields)
18 |
19 | // Send response
20 | return withXMLResponseLegacy(ctx, contents)
21 | }
22 |
23 | /**
24 | * Generate server side sitemaps, support next13+ route.{ts,js} file.
25 | * To continue using inside pages directory, import `getServerSideSitemapLegacy` instead.
26 | * @param fields
27 | * @param headers Custom request headers
28 | * @returns
29 | */
30 | export const getServerSideSitemap = async (
31 | fields: ISitemapField[],
32 | headers = {},
33 | ) => {
34 | // Generate sitemap xml
35 | const contents = new SitemapBuilder().buildSitemapXml(fields)
36 |
37 | // Send response
38 | return withXMLResponse(contents, headers)
39 | }
40 |
--------------------------------------------------------------------------------
/packages/next-sitemap/src/utils/__tests__/array.test.ts:
--------------------------------------------------------------------------------
1 | import {
2 | removeFromArray,
3 | removeIfMatchPattern,
4 | toArray,
5 | toChunks,
6 | } from '../array.js'
7 |
8 | describe('next-sitemap/array', () => {
9 | test('toChunks', () => {
10 | const inputArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
11 | const chunkSize = 3
12 |
13 | const chunks = toChunks(inputArray, chunkSize)
14 | expect(chunks).toStrictEqual([
15 | [0, 1, 2],
16 | [3, 4, 5],
17 | [6, 7, 8],
18 | [9, 10],
19 | ])
20 | })
21 |
22 | test('toArray', () => {
23 | expect(toArray('hello')).toStrictEqual(['hello'])
24 | expect(toArray(['hello', 'world'])).toStrictEqual(['hello', 'world'])
25 | })
26 |
27 | test('removeFromArray', () => {
28 | expect(removeFromArray([1, 2, 3], [2])).toStrictEqual([1, 3])
29 | expect(removeFromArray([1, 2, 3], [2, 3, 4])).toStrictEqual([1])
30 | })
31 |
32 | test('removeIfMatchPattern', () => {
33 | expect(
34 | removeIfMatchPattern(
35 | ['/hello', '/world', '/something'],
36 | ['/hello*', '/som*'],
37 | ),
38 | ).toStrictEqual(['/world'])
39 | })
40 | })
41 |
--------------------------------------------------------------------------------
/packages/next-sitemap/src/utils/array.ts:
--------------------------------------------------------------------------------
1 | import { matcher } from './matcher.js'
2 |
3 | /**
4 | * Split an array based on size
5 | * @param arr
6 | * @param chunkSize
7 | * @returns
8 | */
9 | export const toChunks = (arr: T[], chunkSize: number): T[][] => {
10 | return arr.reduce>(
11 | (prev, _, i) =>
12 | i % chunkSize ? prev : [...prev, arr.slice(i, i + chunkSize)],
13 | [],
14 | )
15 | }
16 |
17 | /**
18 | * simple method to normalize any string to array
19 | * @param inp
20 | */
21 | export const toArray = (inp: string | string[]): string[] => {
22 | return typeof inp === 'string' ? [inp] : inp
23 | }
24 |
25 | /**
26 | * Returns the difference between two arrays
27 | * @param inputArr input array
28 | * @param toRemoveArr array of elements to be removed
29 | */
30 | export const removeFromArray = (inputArr: T[], toRemoveArr: T[]): T[] => {
31 | return inputArr.filter((x) => !toRemoveArr.includes(x))
32 | }
33 |
34 | /**
35 | * Returns the difference between two arrays, which match input array pattern
36 | * @param inputArr input array
37 | * @param toRemoveArr array of elements to be removed
38 | */
39 | export const removeIfMatchPattern = (
40 | inputArr: string[],
41 | toRemoveArr: string[],
42 | ): string[] => {
43 | const matchedArr = matcher(inputArr, toRemoveArr)
44 |
45 | return removeFromArray(inputArr, matchedArr)
46 | }
47 |
--------------------------------------------------------------------------------
/packages/next-sitemap/src/utils/defaults.ts:
--------------------------------------------------------------------------------
1 | import { IConfig, ISitemapField } from '../interface.js'
2 | import { overwriteMerge } from './merge.js'
3 |
4 | export const defaultSitemapTransformer = async (
5 | config: IConfig,
6 | loc: string,
7 | ): Promise => {
8 | return {
9 | loc,
10 | lastmod: config?.autoLastmod ? new Date().toISOString() : undefined,
11 | changefreq: config?.changefreq,
12 | priority: config?.priority,
13 | alternateRefs: config.alternateRefs ?? [],
14 | trailingSlash: config?.trailingSlash,
15 | }
16 | }
17 |
18 | export const defaultRobotsTxtTransformer = async (_: IConfig, text: string) =>
19 | text
20 |
21 | export const defaultConfig: Partial = {
22 | sourceDir: '.next',
23 | outDir: 'public',
24 | priority: 0.7,
25 | sitemapBaseFileName: 'sitemap',
26 | changefreq: 'daily',
27 | sitemapSize: 5000,
28 | autoLastmod: true,
29 | exclude: [],
30 | transform: defaultSitemapTransformer,
31 | generateIndexSitemap: true,
32 | robotsTxtOptions: {
33 | transformRobotsTxt: defaultRobotsTxtTransformer,
34 | policies: [
35 | {
36 | userAgent: '*',
37 | allow: '/',
38 | },
39 | ],
40 | additionalSitemaps: [],
41 | },
42 | }
43 |
44 | /**
45 | * Set a preset for static export mode
46 | * @param config
47 | * @returns
48 | */
49 | export const getStaticExportConfigPreset = (
50 | config: Partial,
51 | ): Partial => {
52 | // Return empty preset for non static export
53 | if (config?.output !== 'export') {
54 | return {}
55 | }
56 |
57 | return {
58 | sourceDir: 'out',
59 | outDir: 'out',
60 | }
61 | }
62 |
63 | /**
64 | * Get default config
65 | * @param config
66 | * @returns
67 | */
68 | export const withDefaultConfig = (config: Partial): IConfig => {
69 | // Add output.export config
70 | const staticExportConfig = getStaticExportConfigPreset(config)
71 |
72 | return overwriteMerge(defaultConfig, staticExportConfig, config)
73 | }
74 |
--------------------------------------------------------------------------------
/packages/next-sitemap/src/utils/file.ts:
--------------------------------------------------------------------------------
1 | import * as fs from 'node:fs/promises'
2 | import path from 'node:path'
3 |
4 | /**
5 | * Load file
6 | * @param path
7 | * @param throwError
8 | * @returns
9 | */
10 | export const loadJSON = async (path: string): Promise => {
11 | // Get path stat
12 | const stat = await fs.stat(path).catch(() => {
13 | return {
14 | isFile: () => false, // Handle errors gracefully
15 | }
16 | })
17 |
18 | // Return undefined or throw error
19 | if (!stat.isFile()) {
20 | return // Handle errors gracefully
21 | }
22 |
23 | const jsonString = await fs.readFile(path, { encoding: 'utf-8' })
24 |
25 | return JSON.parse(jsonString)
26 | }
27 |
28 | /**
29 | * Export file
30 | * @param filePath
31 | * @param content
32 | * @returns
33 | */
34 | export const exportFile = async (
35 | filePath: string,
36 | content: string,
37 | ): Promise => {
38 | // Target folder
39 | const folder = path.dirname(filePath)
40 |
41 | // Get file stat
42 | const stat = await fs.stat(folder).catch(() => ({
43 | isDirectory: () => false,
44 | }))
45 |
46 | // Directory
47 | if (!stat.isDirectory()) {
48 | await fs.mkdir(folder).catch(() => {
49 | return
50 | })
51 | }
52 |
53 | // Write file
54 | return fs.writeFile(filePath, content)
55 | }
56 |
--------------------------------------------------------------------------------
/packages/next-sitemap/src/utils/merge.ts:
--------------------------------------------------------------------------------
1 | import { merge } from '@corex/deepmerge'
2 |
3 | export const overwriteMerge = (...configs: Array>): T => {
4 | return merge(configs, {
5 | arrayMergeType: 'overwrite',
6 | }) as T
7 | }
8 |
9 | export const combineMerge = (...configs: Array>): T => {
10 | return merge(configs, {
11 | arrayMergeType: 'combine',
12 | }) as T
13 | }
14 |
--------------------------------------------------------------------------------
/packages/next-sitemap/src/utils/path.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-non-null-assertion */
2 | /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
3 | import minimist from 'minimist'
4 | import fs from 'node:fs/promises'
5 | import path from 'node:path'
6 | import { Logger } from '../logger.js'
7 | import { generateUrl } from './url.js'
8 | import type { IConfig, IRuntimePaths } from '../interface.js'
9 | import { pathToFileURL } from 'url'
10 |
11 | /**
12 | * Return absolute path from path segments
13 | * @param pathSegment
14 | * @returns
15 | */
16 | export const getPath = (...pathSegment: string[]): string => {
17 | return path.resolve(process.cwd(), ...pathSegment)
18 | }
19 |
20 | /**
21 | * Return all runtime paths
22 | * @param config
23 | * @returns
24 | */
25 | export const getRuntimePaths = (config: IConfig): IRuntimePaths => {
26 | // Check whether user enabled index sitemap or not
27 | const sitemapIndexEnabled = config?.generateIndexSitemap
28 |
29 | // Set sitemap index file
30 | const SITEMAP_INDEX_FILE = sitemapIndexEnabled
31 | ? getPath(config.outDir!, `${config.sitemapBaseFileName}.xml`)
32 | : undefined
33 |
34 | // Set sitemap index url
35 | const SITEMAP_INDEX_URL = sitemapIndexEnabled
36 | ? generateUrl(config?.siteUrl, `${config.sitemapBaseFileName}.xml`)
37 | : undefined
38 |
39 | return {
40 | BUILD_MANIFEST: getPath(config.sourceDir!, 'build-manifest.json'),
41 | PRERENDER_MANIFEST: getPath(config.sourceDir!, 'prerender-manifest.json'),
42 | ROUTES_MANIFEST: getPath(config.sourceDir!, 'routes-manifest.json'),
43 | EXPORT_MARKER: getPath(config.sourceDir!, 'export-marker.json'),
44 | ROBOTS_TXT_FILE: getPath(config.outDir!, 'robots.txt'),
45 | STATIC_EXPORT_ROOT: getPath(config.outDir!),
46 | SITEMAP_INDEX_URL,
47 | SITEMAP_INDEX_FILE,
48 | }
49 | }
50 |
51 | /**
52 | * Get config file path
53 | * @returns
54 | */
55 | export const getConfigFilePath = async () => {
56 | // Extract args from command
57 | const args = minimist(process.argv.slice(2))
58 |
59 | // Config file path
60 | const configPath = getPath(args.config || 'next-sitemap.config.js')
61 |
62 | // Check file stat
63 | return fs
64 | .stat(configPath)
65 | .then(() => pathToFileURL(configPath).toString())
66 | .catch((err) => {
67 | Logger.noConfigFile()
68 | throw err
69 | })
70 | }
71 |
--------------------------------------------------------------------------------
/packages/next-sitemap/src/utils/url.ts:
--------------------------------------------------------------------------------
1 | export const cleanPath = (text: string): string => {
2 | return text.replace(/([^:])(\/\/+)/g, '$1/')
3 | }
4 |
5 | export const isURL = (text: string): boolean => {
6 | // old: /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$/
7 | return /^https?:\/\//i.test(text)
8 | }
9 |
10 | export const generateUrl = (baseUrl: string, slug: string): string => {
11 | return isURL(slug) ? cleanPath(slug) : cleanPath(`${baseUrl}/${slug}`)
12 | }
13 |
14 | /**
15 | * Checks whether a url is next.js specific or not
16 | * @param path path check
17 | */
18 | export const isNextInternalUrl = (path: string): boolean => {
19 | return new RegExp(
20 | /[^/]*^.[_]|^\/(404|500)$|\/_middleware$|favicon.ico|(?:\[)/g,
21 | ).test(path)
22 | }
23 |
24 | /**
25 | * Creates a replace function to replace the default locale
26 | * Avoids creating the same RegExp within each replace
27 | *
28 | * Replaces only if the path does not contain the locale as an actual valid path
29 | *
30 | * Given a default locale of en-US it replaces:
31 | * /en-US -> /
32 | * /en-US/home -> /home
33 | * /en-US/home/ -> /home/
34 | *
35 | * Does not replace if its actual page
36 | * /en-USA -> /en-USA
37 | * /en-USA/home -> /en-USA/home
38 | * /en-US-home -> /en-US-home
39 | *
40 | * @param defaultLocale defaultLocale as provided by i18n within next config
41 | */
42 | export const createDefaultLocaleReplace = (defaultLocale: string): any => {
43 | const defaultLocaleRegExp = new RegExp(`^/${defaultLocale}($|/)`)
44 | return (path: string): string => path.replace(defaultLocaleRegExp, '/')
45 | }
46 |
47 | /**
48 | * Return UTF-8 encoded urls
49 | * @param path
50 | * @returns
51 | * @link https://developers.google.com/search/docs/advanced/sitemaps/build-sitemap#general-guidelines
52 | */
53 | export const entityEscapedUrl = (path: string): string => {
54 | return path
55 | .replace(/&/g, '&') // decode & to & first, so that we don't replace & again to &
56 | .replace(/&/g, '&')
57 | .replace(/'/g, ''')
58 | .replace(/"/g, '"')
59 | .replace(/>/g, '>')
60 | .replace(/