Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

npm run build sitemap.xml create not found #248

Open
todaynogada opened this issue Apr 26, 2022 · 25 comments
Open

npm run build sitemap.xml create not found #248

todaynogada opened this issue Apr 26, 2022 · 25 comments

Comments

@todaynogada
Copy link

todaynogada commented Apr 26, 2022

In npm run dev state, sitemap.xml is created, but in npm run start state after npm run build, sitemap.xml is not created.

Is /sitemap.xml normal after npm run dev command?
After npm run build and after npm run start, sitemap.xml 404 error is returned. I hope it gets patched soon.

Currently, it is temporarily used as https://github.com/benoitdemaegdt/nuxt3-sitemap.

npm 8.5.0
Node 16.14.2
nuxtjs3 v3.0.0-rc.1

@d3xter-dev
Copy link

d3xter-dev commented May 17, 2022

Nuxt 3 removed the addServerMiddleware function (but for whatever reason only on production) see https://v3.nuxtjs.org/api/advanced/kit
The problem is "sitemap-module" uses this function from Nuxt 2 to intercept any incoming requests on /sitemap.xml etc. and returning the xml file.

I am currently working on getting this sitemap-module running on a project with Nuxt 3. If it works out , I may post it here or open a PR.

Basicly you need to use the new server middleware which doesn't have access to the nuxt instance which makes things a bit more complicated but possible.

@d3xter-dev
Copy link

also see nuxt/nuxt#13559

@d3xter-dev
Copy link

d3xter-dev commented May 18, 2022

So I kinda did it 🥳, at least a simple version supporting a single sitemap.xml (no sitemapindex.xml etc)

If there are enough people who need a full working version, I would offer to create a Nuxt 3 Version of this module.

I hope that helps someone 😄

nuxt.config.ts

modules: [
    // ....
    '@nuxtjs/sitemap',
    '~/modules/sitemap/module.js',
  ],

/modules/sitemap/module.js

import { defineNuxtModule } from '@nuxt/kit'

export default defineNuxtModule({
  setup(options, nuxt) {
    /**
     * remove default middleware so our works on dev as well
     */
    nuxt.options.serverMiddleware = nuxt.options.serverMiddleware.filter((mw) => !mw.path.includes('sitemap.xml'))

    /**
     * Use addServerHandler to dynamicly add server middlewares in Nuxt 3
     */
    // addServerHandler({
    //   handler: '/path/to/handler',
    // })

    /**
     * pass routes to nitro runtime config
     */
    nuxt.hook('nitro:build:before', (nitro) => {
      nitro.options.runtimeConfig.sitemap = nuxt.options.runtimeConfig.sitemap
    })

    /**
     * Get all static routes and remove dynamic ones
     */
    nuxt.hook('pages:extend', (routes) => {
      nuxt.options.sitemap.staticRoutes = routes
        .map((r) => r.path)
        .filter((path) => ![':', '*'].some((c) => path.includes(c)))
      nuxt.options.runtimeConfig.sitemap = nuxt.options.sitemap
    })
  },
})

/server/middleware/sitemap.ts

import { defineEventHandler } from 'h3'
import { excludeRoutes } from '@nuxtjs/sitemap/lib/routes.js'
import { createRoutesCache } from '@nuxtjs/sitemap/lib/cache.js'
import { createSitemap } from '@nuxtjs/sitemap/lib/builder.js'
import generateETag from 'etag'
import fresh from 'fresh'

export const globalCache = { staticRoutes: null, routes: null }

const defaults = {
  path: '/sitemap.xml',
  hostname: undefined,
  exclude: [],
  routes: [],
  cacheTime: 1000 * 60 * 15,
  etag: false,
  filter: undefined,
  gzip: false,
  xmlNs: undefined,
  xslUrl: undefined,
  trailingSlash: false,
  lastmod: undefined,
  i18n: undefined,
  defaults: {},
}

export default defineEventHandler(async (event) => {
  const config = useRuntimeConfig()
  const sitemap = { ...defaults, ...config.sitemap }

  if ([sitemap.pathGzip, sitemap.path].includes(event.req.url)) {
    const generateDynamicRoutes = async (slug) => {
      console.log('[vue-sitemap] generate dynamic routes')

      const routes = []
      // make requests to an API or something and build an array of routes

      return routes
    }

    /**
     * Init cache if not active yet
     */
    if (!globalCache.staticRoutes) {
      /**
       * add dynamic routes
       */
      sitemap.routes = async () => [
        ...(await generateDynamicRoutes('news')),
      ]

      globalCache.staticRoutes = () => excludeRoutes(sitemap.exclude, sitemap.staticRoutes)
      globalCache.routes = createRoutesCache(globalCache, sitemap)
    }

    if (sitemap.gzip) {
      try {
        // Init sitemap
        const routes = await globalCache.routes.get('routes')
        const gzip = await createSitemap(sitemap, routes, '/', event.req).toGzip()
        // Check cache headers
        if (validHttpCache(gzip, sitemap.etag, event.req, event.res)) {
          return
        }
        // Send http response
        event.res.setHeader('Content-Type', 'application/gzip')
        return event.res.end(gzip)
      } catch (err) {
        return err
      }
    }

    try {
      // Init sitemap
      const routes = await globalCache.routes.get('routes')
      const xml = await createSitemap(sitemap, routes, '/', event.req).toXML()
      // Check cache headers
      if (validHttpCache(xml, sitemap.etag, event.req, event.res)) {
        return
      }

      // Send http response
      event.res.setHeader('Content-Type', 'application/xml')
      return xml
    } catch (err) {
      return err
    }
  }
})

function validHttpCache(entity, options, req, res) {
  if (!options) {
    return false
  }
  const { hash } = options
  const etag = hash ? hash(entity, options) : generateETag(entity, options)
  if (fresh(req.headers, { etag })) {
    res.statusCode = 304
    res.end()
    return true
  }
  // Add ETag header
  res.setHeader('ETag', etag)
  return false
}

@Lars-
Copy link

Lars- commented May 19, 2022

I guess I'm missing something. I get the following error when going to /sitemap.xml:

[nuxt] [request error] Cannot read properties of undefined (reading 'map')
  at createError (./node_modules/h3/dist/index.mjs:185:15)  
  at ./node_modules/h3/dist/index.mjs:430:17  
  at processTicksAndRejections (node:internal/process/task_queues:96:5)  
  at async Server.nodeHandler (./node_modules/h3/dist/index.mjs:367:7)
[nitro] Error while generating error response FetchError: 404  (/__nuxt_error?url=/sitemap.xml&statusCode=500&statusMessage=H3Error&message=Cannot+read+p
roperties+of+undefined+(reading+'map')&description=%3Cpre%3E%3Cspan+class=%22stack+internal%22%3Eat+createError+(./node_modules/h3/dist/index.mjs:185:15)
%3C/span%3E%0A%3Cspan+class=%22stack+internal%22%3Eat+./node_modules/h3/dist/index.mjs:430:17%3C/span%3E%0A%3Cspan+class=%22stack+internal%22%3Eat+proces
sTicksAndRejections+(node:internal/process/task_queues:96:5)%3C/span%3E%0A%3Cspan+class=%22stack+internal%22%3Eat+async+Server.nodeHandler+(./node_modules/h3/dist/index.mjs:367:7)%3C/span%3E%3C/pre%3E)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async Object.errorhandler [as onError] (file:///home/ljansen/......./.nuxt/dev/index.mjs:311:16)
    at async Server.nodeHandler (file:///home/ljansen/........../node_modules/h3/dist/index.mjs:370:9)

What am I doing wrong?

I added this to nuxt.config.ts (in addition to the things you posted above):

sitemap: {
        hostname: 'https://www.ljpc.nl',
        gzip: true,
        path: '/sitemap.xml',
        routes: [],
        defaults: {
            changefreq: 'daily',
            priority: 1,
            lastmod: new Date()
        }
    }

@d3xter-dev
Copy link

d3xter-dev commented May 20, 2022

sitemap.staticRoutes doesn't seem to be set in the sitemap.ts?
is the nuxt module loaded correctly ?

try console.log in the sitemap.ts

  const config = useRuntimeConfig()
  const sitemap = { ...defaults, ...config.sitemap }
  console.log(sitemap)

and in the module.js

    nuxt.hook('pages:extend', (routes) => { 
        console.log('adding static routes to nitro config....')
        ......
     })

static routes are only set once during build time so if you add a new static page you need to restart the server e.g. yarn dev / yarn build && yarn start

@scottingle
Copy link

scottingle commented May 23, 2022

@d3xter-dev

Followed your steps but getting undefined errors on nuxt.options.serverMiddleware?

Edit: Error appears to no longer be occurring.

@Lars-
Copy link

Lars- commented May 23, 2022

@d3xter-dev I do see the "adding static routes to nitro config" console log.
And on every page load I see this logged:

{
  path: '/sitemap.xml',
  hostname: undefined,
  exclude: [],
  routes: [],
  cacheTime: 900000,
  etag: false,
  filter: undefined,
  gzip: false,
  xmlNs: undefined,
  xslUrl: undefined,
  trailingSlash: false,
  lastmod: undefined,
  i18n: undefined,
  defaults: {}
}

@d3xter-dev
Copy link

d3xter-dev commented May 23, 2022

@Lars- yeah the staticRotues are missing so, something seems to be wrong in the modules.js does routes in pages:extend contain all your routes? and does the nuxt.options.runtimeConfig.sitemap in nuxt.hook('nitro:build:before', (nitro) include a prop staticRoutes with all your routes?

@scottingle you could try nuxt.options.serverMiddleware = nuxt.options.serverMiddleware.filter((mw) => mw?.path && !mw.path.includes('sitemap.xml')) ?

@Lars-
Copy link

Lars- commented May 23, 2022

I'll check later. It takes a bit too much time for now unfortunately. But I really do appreciate the replies. If I know more, I'll let you know.

@scottingle
Copy link

scottingle commented May 23, 2022

Would this work with dynamic routes?

@d3xter-dev
Copy link

d3xter-dev commented May 23, 2022

@scottingle nope, they should be excluded thats what's the

 nuxt.options.sitemap.staticRoutes = routes
        .map((r) => r.path)
        .filter((path) => ![':', '*'].some((c) => path.includes(c)))

is for, dynamic routes need to be added in the sitemap.ts
if we take product for example I would do a request to my endpoint where I get the products from and take alle the ids I get to build an array of routes which we can cache then see

      /**
       * add dynamic routes
       */
      sitemap.routes = async () => [
        ...(await generateDynamicRoutes('products')),
      ]
      ....

@heychazza
Copy link

Hey @d3xter-dev, thanks for providing a solution for Nuxt 3!

Does this support Nuxt Content 2?

@d3xter-dev
Copy link

@heychazza not sure what you mean by that

@heychazza
Copy link

@heychazza not sure what you mean by that

Does this support the new version of Nuxt Content?

@desaintflorent
Copy link

Thank you @d3xter-dev for helping make this plugin work with V3 ! Can you confirm me that this fix doesn't work with a sitemapindex ? I'm using an index of sitemaps on my app and waiting to switch to V3.

@d3xter-dev
Copy link

d3xter-dev commented May 24, 2022

@heychazza not sure what you mean by that

Does this support the new version of Nuxt Content?

@heychazza ah that's another nuxt module I see, should be fine as long as Nuxt Content loads before the module.js I posted above and pages:extends contains all routes added by Nuxt Content (just add a console.log to check, only runs once per build)

@desaintflorent yeah you would have to add support for a sitemapindex.xml in the middleware or create another one by copying + adapting following code:

function registerSitemapIndex(options, globalCache, nuxtInstance, depth = 0) {
const base = nuxtInstance.options.router.base
// Init options
options = setDefaultSitemapIndexOptions(options, nuxtInstance)
if (options.gzip) {
// Add server middleware for sitemapindex.xml.gz
nuxtInstance.addServerMiddleware({
path: options.pathGzip,
handler(req, res, next) {
// Init sitemap index
const sitemapIndex = createSitemapIndex(options, base, req)
const gzip = gzipSync(sitemapIndex)
// Check cache headers
if (validHttpCache(gzip, options.etag, req, res)) {
return
}
// Send http response
res.setHeader('Content-Type', 'application/gzip')
res.end(gzip)
},
})
}
// Add server middleware for sitemapindex.xml
nuxtInstance.addServerMiddleware({
path: options.path,
handler(req, res, next) {
// Init sitemap index
const xml = createSitemapIndex(options, base, req)
// Check cache headers
if (validHttpCache(xml, options.etag, req, res)) {
return
}
// Send http response
res.setHeader('Content-Type', 'application/xml')
res.end(xml)
},
})
// Register linked sitemaps
options.sitemaps.forEach((sitemapOptions) => registerSitemaps(sitemapOptions, globalCache, nuxtInstance, depth + 1))
}

@fskarmeta
Copy link

I would greatly appreciate a nuxt3 module for this :d, and sooner than later, thousands of devs.

@ROBJkE
Copy link

ROBJkE commented Jun 13, 2022

@d3xter-dev Pog. Could you provide a Repository to contribute on? Seems the next-sitemap guys are kinda inactive

@jmdmacapagal
Copy link

is this still not available in nuxt 3?

@d3xter-dev
Copy link

We decided that we'll start working on porting the whole module to Nuxt 3 in the next few days 😄
Link to our repo https://github.com/funken-studio/sitemap-module-nuxt-3

@jmdmacapagal
Copy link

jmdmacapagal commented Jun 17, 2022

wow thank you @d3xter-dev looking forward to it!

@heychazza
Copy link

Damn seems nuxt content isn't yet supported by this :(

@heychazza not sure what you mean by that

Does this support the new version of Nuxt Content?

@heychazza ah that's another nuxt module I see, should be fine as long as Nuxt Content loads before the module.js I posted above and pages:extends contains all routes added by Nuxt Content (just add a console.log to check, only runs once per build)

@desaintflorent yeah you would have to add support for a sitemapindex.xml in the middleware or create another one by copying + adapting following code:

function registerSitemapIndex(options, globalCache, nuxtInstance, depth = 0) {
const base = nuxtInstance.options.router.base
// Init options
options = setDefaultSitemapIndexOptions(options, nuxtInstance)
if (options.gzip) {
// Add server middleware for sitemapindex.xml.gz
nuxtInstance.addServerMiddleware({
path: options.pathGzip,
handler(req, res, next) {
// Init sitemap index
const sitemapIndex = createSitemapIndex(options, base, req)
const gzip = gzipSync(sitemapIndex)
// Check cache headers
if (validHttpCache(gzip, options.etag, req, res)) {
return
}
// Send http response
res.setHeader('Content-Type', 'application/gzip')
res.end(gzip)
},
})
}
// Add server middleware for sitemapindex.xml
nuxtInstance.addServerMiddleware({
path: options.path,
handler(req, res, next) {
// Init sitemap index
const xml = createSitemapIndex(options, base, req)
// Check cache headers
if (validHttpCache(xml, options.etag, req, res)) {
return
}
// Send http response
res.setHeader('Content-Type', 'application/xml')
res.end(xml)
},
})
// Register linked sitemaps
options.sitemaps.forEach((sitemapOptions) => registerSitemaps(sitemapOptions, globalCache, nuxtInstance, depth + 1))
}

CleanShot 2022-06-18 at 9 59 30@2x

@d3xter-dev
Copy link

d3xter-dev commented Jun 28, 2022

I am happy to announce 🥳
We finally finished a first version for Nuxt 3, feel free to check it out. 🎉

yarn add @funken-studio/sitemap-nuxt-3

https://github.com/funken-studio/sitemap-module-nuxt-3/tree/experimental
https://www.npmjs.com/package/@funken-studio/sitemap-nuxt-3

Please give me feedback :)

@mak33v
Copy link

mak33v commented Aug 14, 2022

@d3xter-dev
thanks for your module '~/modules/sitemap/module.js', it works fine with server build, but with 'nuxt generate' map file is not generated in output dir, what could be the problem?

@Neilblaze
Copy link

Damn seems nuxt content isn't yet supported by this :(

@heychazza not sure what you mean by that

Does this support the new version of Nuxt Content?

@heychazza ah that's another nuxt module I see, should be fine as long as Nuxt Content loads before the module.js I posted above and pages:extends contains all routes added by Nuxt Content (just add a console.log to check, only runs once per build)
@desaintflorent yeah you would have to add support for a sitemapindex.xml in the middleware or create another one by copying + adapting following code:

function registerSitemapIndex(options, globalCache, nuxtInstance, depth = 0) {
const base = nuxtInstance.options.router.base
// Init options
options = setDefaultSitemapIndexOptions(options, nuxtInstance)
if (options.gzip) {
// Add server middleware for sitemapindex.xml.gz
nuxtInstance.addServerMiddleware({
path: options.pathGzip,
handler(req, res, next) {
// Init sitemap index
const sitemapIndex = createSitemapIndex(options, base, req)
const gzip = gzipSync(sitemapIndex)
// Check cache headers
if (validHttpCache(gzip, options.etag, req, res)) {
return
}
// Send http response
res.setHeader('Content-Type', 'application/gzip')
res.end(gzip)
},
})
}
// Add server middleware for sitemapindex.xml
nuxtInstance.addServerMiddleware({
path: options.path,
handler(req, res, next) {
// Init sitemap index
const xml = createSitemapIndex(options, base, req)
// Check cache headers
if (validHttpCache(xml, options.etag, req, res)) {
return
}
// Send http response
res.setHeader('Content-Type', 'application/xml')
res.end(xml)
},
})
// Register linked sitemaps
options.sitemaps.forEach((sitemapOptions) => registerSitemaps(sitemapOptions, globalCache, nuxtInstance, depth + 1))
}

CleanShot 2022-06-18 at 9 59 30@2x

Yeah, you're correct. I'm also facing the same issue for nuxt content :(

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests