diff --git a/src/plugins/transform.ts b/src/plugins/transform.ts index 7f45e742..64e2b3bb 100644 --- a/src/plugins/transform.ts +++ b/src/plugins/transform.ts @@ -45,7 +45,10 @@ function normalizeScriptData(src: string, assetsBaseURL: string = '/_scripts'): `${ohash(url)}.js`, // force an extension ].filter(Boolean).join('-') const nuxt = tryUseNuxt() - return { url: joinURL(joinURL(nuxt?.options.app.baseURL || '', assetsBaseURL), file), filename: file } + // Use cdnURL if available, otherwise fall back to baseURL + const cdnURL = nuxt?.options.runtimeConfig?.app?.cdnURL || nuxt?.options.app?.cdnURL || '' + const baseURL = cdnURL || nuxt?.options.app.baseURL || '' + return { url: joinURL(joinURL(baseURL, assetsBaseURL), file), filename: file } } return { url: src } } diff --git a/test/e2e/cdn.test.ts b/test/e2e/cdn.test.ts new file mode 100644 index 00000000..b0ecc008 --- /dev/null +++ b/test/e2e/cdn.test.ts @@ -0,0 +1,40 @@ +import { fileURLToPath } from 'node:url' +import { describe, expect, it } from 'vitest' +// import { createResolver } from '@nuxt/kit' +import { $fetch, setup } from '@nuxt/test-utils/e2e' + +// const { resolve } = createResolver(import.meta.url) + +describe('cdnURL', async () => { + await setup({ + rootDir: fileURLToPath(new URL('../fixtures/cdn', import.meta.url)), + nuxtConfig: { + nitro: { + prerender: { + routes: ['/'], + }, + }, + }, + }) + + it('should use cdnURL for bundled scripts', async () => { + const html = await $fetch('/') + + // Check that the page loads + expect(html).toContain('CDN URL Test') + + // Check that script tags use the CDN URL + const scriptTags = html.match(/]*src="([^"]+)"[^>]*>/g) || [] + const bundledScripts = scriptTags.filter(tag => tag.includes('/_scripts/')) + + bundledScripts.forEach((scriptTag) => { + const srcMatch = scriptTag.match(/src="([^"]+)"/) + if (srcMatch) { + expect(srcMatch[1]).toMatch(/^https:\/\/cdn\.example\.com\/_scripts\//) + } + }) + }) + + // Runtime test would require a real CDN to be set up + // The static test above verifies the CDN URL is used in the generated HTML +}) diff --git a/test/fixtures/cdn/app.vue b/test/fixtures/cdn/app.vue new file mode 100644 index 00000000..a874fb4e --- /dev/null +++ b/test/fixtures/cdn/app.vue @@ -0,0 +1,35 @@ + + + diff --git a/test/fixtures/cdn/nuxt.config.ts b/test/fixtures/cdn/nuxt.config.ts new file mode 100644 index 00000000..88a1ce23 --- /dev/null +++ b/test/fixtures/cdn/nuxt.config.ts @@ -0,0 +1,11 @@ +export default defineNuxtConfig({ + modules: ['@nuxt/scripts'], + app: { + cdnURL: 'https://cdn.example.com', + }, + scripts: { + defaultScriptOptions: { + bundle: true, + }, + }, +}) diff --git a/test/unit/transform.test.ts b/test/unit/transform.test.ts index 5a7544bf..51c29a58 100644 --- a/test/unit/transform.test.ts +++ b/test/unit/transform.test.ts @@ -49,6 +49,28 @@ vi.mock('@nuxt/kit', async (og) => { return { options: { buildDir: '.nuxt', + app: { + baseURL: '/', + }, + runtimeConfig: { + app: {}, + }, + }, + hooks: { + hook: vi.fn(), + }, + } + }, + tryUseNuxt() { + return { + options: { + buildDir: '.nuxt', + app: { + baseURL: '/', + }, + runtimeConfig: { + app: {}, + }, }, hooks: { hook: vi.fn(), @@ -312,6 +334,22 @@ const _sfc_main = /* @__PURE__ */ _defineComponent({ expect(code.includes('useScript(\'/_scripts/vFJ41_fzYQOTRPr3v6G1PkI0hc5tMy0HGrgFjhaJhOI.js\', {')).toBeTruthy() }) + it('uses baseURL without cdnURL', async () => { + vi.mocked(hash).mockImplementationOnce(() => 'beacon.min') + + const code = await transform( + `const instance = useScript('https://static.cloudflareinsights.com/beacon.min.js', { + bundle: true, + })`, + { + assetsBaseURL: '/_scripts', + }, + ) + + // Without cdnURL configured, it should use baseURL + expect(code).toMatchInlineSnapshot(`"const instance = useScript('/_scripts/beacon.min.js', )"`) + }) + describe.todo('fallbackOnSrcOnBundleFail', () => { beforeEach(() => { vi.mocked($fetch).mockImplementationOnce(() => Promise.reject(new Error('fetch error')))