Nuxt/kit: when creating a module, how can I inject a script into the body

Hi there. I am currently creating a nuxt module. One of the things I need to do is, I have a minified .js file I want to inject into the body. But only on client side. I tried
// src/module.ts:
// setup method:

addTemplate({
filename: 'my-script.js',
getContents: () => import(resolve(runtimeDir, 'my-script')),
})

nuxt.options.app.head.script?.push({
src: '/my-script.js',
body: true,
defer: true,
})

// src/runtime/my-script.js
console.log('hello world')
// src/module.ts:
// setup method:

addTemplate({
filename: 'my-script.js',
getContents: () => import(resolve(runtimeDir, 'my-script')),
})

nuxt.options.app.head.script?.push({
src: '/my-script.js',
body: true,
defer: true,
})

// src/runtime/my-script.js
console.log('hello world')
This does include the script directly, so also in SSR. When adding something like if (typeof window !== 'undefined') to only inject it on the client-side, it never gets injected since the src/module.ts seems to only run once on start. I am sure that addTemplate is not the right method. Do you know any method to add a file into the public folder?
14 Replies
harlan
harlan2y ago
You should reference how the color module does it
harlan
harlan2y ago
GitHub
color-mode/module.ts at master · nuxt-modules/color-mode
Dark and Light mode with auto detection made easy with Nuxt 🌗 - color-mode/module.ts at master · nuxt-modules/color-mode
harlan
harlan2y ago
GitHub
color-mode/nitro-plugin.ts at master · nuxt-modules/color-mode
Dark and Light mode with auto detection made easy with Nuxt 🌗 - color-mode/nitro-plugin.ts at master · nuxt-modules/color-mode
harlan
harlan2y ago
probably the easiest
Fabian B.
Fabian B.OP2y ago
Thanks @harlan , I see that this would inject the script as inline, but not actually "upload" or add a JS file to the public folder of the modules' user, right? The usecase is that I have a .min.js script coming from a 3rd party, and this needs to be injected into the public folder of a user.
harlan
harlan2y ago
maybe something like
const modulePublicDir = resolve('runtime/public')

nuxt.hooks.hook('nitro:config', (nitroConfig) => {
nitroConfig.publicAssets = nitroConfig.publicAssets || []
nitroConfig.publicAssets.push({ dir: modulePublicDir, maxAge: 31536000 })
})
const modulePublicDir = resolve('runtime/public')

nuxt.hooks.hook('nitro:config', (nitroConfig) => {
nitroConfig.publicAssets = nitroConfig.publicAssets || []
nitroConfig.publicAssets.push({ dir: modulePublicDir, maxAge: 31536000 })
})
?
Fabian B.
Fabian B.OP2y ago
That looks great, though it doesn't actually add those files to the playground/public folder. I've worked a lot with nuxt, but not yet created a module, so maybe I am overlooking somthing 😄 I saw someone did it with just fs.writeFile, not sure about if that's the best approach. https://github.com/nuxt/nuxt/discussions/17807#discussioncomment-2339732 https://github.com/florian-lefebvre/portfolio/blob/c513428dea912a19ffb684b8b571b08b8882158c/modules/sitemap.ts#L54
harlan
harlan2y ago
so this code basically adds a new public dir for nitro to serve static assets from if you hit the path it should serve the file using fs is fine too if you don't need it in dev
Fabian B.
Fabian B.OP2y ago
I now have implemented it like so:
const addBeaconFile = async () => {
// Read file from runtime dir
const modulePublicDir = join(runtimeDir, '/public')
const file = await fsp.readFile(join(modulePublicDir, 'beacon.min.js'), 'utf-8')

// Write file to public dir of nuxt project
const dirPath = join(nuxt.options.rootDir, '/public/_ca')
await fsp.mkdir(dirPath, { recursive: true })
await fsp.writeFile(join(dirPath, 'b.js'), newFile)
}
addBeaconFile()

// Nuxt 3 and Bridge - inject script
nuxt.hook('nitro:config', config => {
config.plugins.push(resolve(runtimeDir, 'nitro-plugin'))
})
const addBeaconFile = async () => {
// Read file from runtime dir
const modulePublicDir = join(runtimeDir, '/public')
const file = await fsp.readFile(join(modulePublicDir, 'beacon.min.js'), 'utf-8')

// Write file to public dir of nuxt project
const dirPath = join(nuxt.options.rootDir, '/public/_ca')
await fsp.mkdir(dirPath, { recursive: true })
await fsp.writeFile(join(dirPath, 'b.js'), newFile)
}
addBeaconFile()

// Nuxt 3 and Bridge - inject script
nuxt.hook('nitro:config', config => {
config.plugins.push(resolve(runtimeDir, 'nitro-plugin'))
})
and the nitro plugin
import type { NitroAppPlugin } from 'nitropack'

export default <NitroAppPlugin>(nitro) => {
nitro.hooks.hook('render:html', htmlContext => {
htmlContext.body.push('<script src="/_ca/b.js"></script>')
})
}
import type { NitroAppPlugin } from 'nitropack'

export default <NitroAppPlugin>(nitro) => {
nitro.hooks.hook('render:html', htmlContext => {
htmlContext.body.push('<script src="/_ca/b.js"></script>')
})
}
which works perfectly well. Thanks again 🙂
harlan
harlan2y ago
looks good, only issue is that issues will have an untracked git file with this setup if that's not an issue then all good
Fabian B.
Fabian B.OP2y ago
Yeah that's right. But since the script is only needed in prod and also I plan to change the content inside of the script based on user config (since it contains a hardcoded url I want users to override), I think it's perfectly fine for the file not to be in git, but to be generated on the fly in build. A small follow-up, maybe you've stumbled on this. I have now released the package on a @Next tag. It is on npm with nuxt-cloudflare-analytics@next. When testing this module in the playground, everything works fine. Installing this module in another real nuxt app, I get the following error on npm run build:
ERROR Package import specifier "#nuxt-cloudflare-analytics" is not defined in package /Users/fabian/Desktop/code/madebyfabian-com/node_modules/nuxt-cloudflare-analytics/package.json imported from /Users/fabian/Desktop/code/madebyfabian-com/node_modules/nuxt-cloudflare-analytics/dist/runtime/nitro-plugin.mjs

at new NodeError (node:internal/errors:387:5)
at throwImportNotDefined (node:internal/modules/esm/resolve:354:9)
at packageImportsResolve (node:internal/modules/esm/resolve:737:3)
at moduleResolve (node:internal/modules/esm/resolve:895:21)
at defaultResolve (node:internal/modules/esm/resolve:1115:11)
at nextResolve (node:internal/modules/esm/loader:163:28)
at ESMLoader.resolve (node:internal/modules/esm/loader:837:30)
at ESMLoader.getModuleJob (node:internal/modules/esm/loader:424:18)
at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:76:40)
at link (node:internal/modules/esm/module_job:75:36)
ERROR Package import specifier "#nuxt-cloudflare-analytics" is not defined in package /Users/fabian/Desktop/code/madebyfabian-com/node_modules/nuxt-cloudflare-analytics/package.json imported from /Users/fabian/Desktop/code/madebyfabian-com/node_modules/nuxt-cloudflare-analytics/dist/runtime/nitro-plugin.mjs

at new NodeError (node:internal/errors:387:5)
at throwImportNotDefined (node:internal/modules/esm/resolve:354:9)
at packageImportsResolve (node:internal/modules/esm/resolve:737:3)
at moduleResolve (node:internal/modules/esm/resolve:895:21)
at defaultResolve (node:internal/modules/esm/resolve:1115:11)
at nextResolve (node:internal/modules/esm/loader:163:28)
at ESMLoader.resolve (node:internal/modules/esm/loader:837:30)
at ESMLoader.getModuleJob (node:internal/modules/esm/loader:424:18)
at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:76:40)
at link (node:internal/modules/esm/module_job:75:36)
I've tried to implement it like i the color-module: https://github.com/hamlogic/nuxt-cloudflare-analytics/blob/next/src/module.ts https://github.com/hamlogic/nuxt-cloudflare-analytics/blob/next/src/runtime/nitro-plugin.ts Do you have an idea on why this seems to fail? Thanks in advance! @harlan Solved it, nevermind 😄
Unknown User
Unknown User2y ago
Message Not Public
Sign In & Join Server To View
Fabian B.
Fabian B.OP2y ago
I forgot to include these 3 lines
config.externals = config.externals || {}
config.externals.inline = config.externals.inline || []
config.externals.inline.push(runtimeDir)
config.externals = config.externals || {}
config.externals.inline = config.externals.inline || []
config.externals.inline.push(runtimeDir)
and they are somehow required
Fabian B.
Fabian B.OP2y ago
GitHub
GitHub - hamlogic/nuxt-cloudflare-analytics: Add Cloudflare Web Ana...
Add Cloudflare Web Analytics to your Nuxt Project. Contribute to hamlogic/nuxt-cloudflare-analytics development by creating an account on GitHub.
Want results from more Discord servers?
Add your server