From b10c8f879196564aa1943e1530ee6272be2e097e Mon Sep 17 00:00:00 2001 From: Harlan Wilton Date: Thu, 18 Sep 2025 09:24:06 +1000 Subject: [PATCH 1/2] feat: add source option to Google Tag Manager MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add source option to GoogleTagManagerOptions schema to allow custom GTM script URLs for server-side implementations. - Add source parameter to GoogleTagManagerOptions - Update script URL construction to use custom source - Add documentation and examples for server-side GTM setup Resolves #482 ๐Ÿค– Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../scripts/tracking/google-tag-manager.md | 35 ++- src/runtime/registry/google-tag-manager.ts | 5 +- tasks/implementation-roadmap.md | 245 ++++++++++++++++++ tasks/issue-466-triage.md | 111 ++++++++ tasks/issue-466.md | 115 ++++++++ tasks/issue-481-triage.md | 134 ++++++++++ tasks/issue-481.md | 111 ++++++++ tasks/issue-482-triage.md | 173 +++++++++++++ tasks/issue-482.md | 83 ++++++ tasks/issue-483-triage.md | 170 ++++++++++++ tasks/issue-483.md | 70 +++++ tasks/issue-485-triage.md | 225 ++++++++++++++++ tasks/issue-485.md | 35 +++ tasks/issue-490-triage.md | 210 +++++++++++++++ tasks/issue-490.md | 54 ++++ 15 files changed, 1774 insertions(+), 2 deletions(-) create mode 100644 tasks/implementation-roadmap.md create mode 100644 tasks/issue-466-triage.md create mode 100644 tasks/issue-466.md create mode 100644 tasks/issue-481-triage.md create mode 100644 tasks/issue-481.md create mode 100644 tasks/issue-482-triage.md create mode 100644 tasks/issue-482.md create mode 100644 tasks/issue-483-triage.md create mode 100644 tasks/issue-483.md create mode 100644 tasks/issue-485-triage.md create mode 100644 tasks/issue-485.md create mode 100644 tasks/issue-490-triage.md create mode 100644 tasks/issue-490.md diff --git a/docs/content/scripts/tracking/google-tag-manager.md b/docs/content/scripts/tracking/google-tag-manager.md index 2b39a2aa..cf04690c 100644 --- a/docs/content/scripts/tracking/google-tag-manager.md +++ b/docs/content/scripts/tracking/google-tag-manager.md @@ -154,6 +154,9 @@ export const GoogleTagManagerOptions = object({ /** Referrer policy for analytics requests */ authReferrerPolicy: optional(string()), + + /** The URL of the script; useful for server-side GTM */ + source: optional(string()), }) ``` @@ -163,7 +166,37 @@ export const GoogleTagManagerOptions = object({ type GoogleTagManagerInput = typeof GoogleTagManagerOptions & { onBeforeGtmStart?: (gtag: Gtag) => void } ``` -## Example +## Examples + +### Server-Side GTM Setup + +Using a custom GTM script source for server-side implementations: + +```ts +// nuxt.config.ts +export default defineNuxtConfig({ + scripts: { + registry: { + googleTagManager: { + id: 'GTM-XXXXXX', + source: 'https://your-domain.com/gtm.js' + } + } + } +}) +``` + +```vue + + +``` + +### Basic Usage Using Google Tag Manager only in production while using `dataLayer` to send a conversion event. diff --git a/src/runtime/registry/google-tag-manager.ts b/src/runtime/registry/google-tag-manager.ts index 96ab5506..fedd339e 100644 --- a/src/runtime/registry/google-tag-manager.ts +++ b/src/runtime/registry/google-tag-manager.ts @@ -109,6 +109,9 @@ export const GoogleTagManagerOptions = object({ /** Referrer policy for analytics requests */ authReferrerPolicy: optional(string()), + + /** The URL of the script; useful for server-side GTM */ + source: optional(string()), }) export type GoogleTagManagerInput = RegistryScriptInput @@ -132,7 +135,7 @@ export function useScriptGoogleTagManager( return { scriptInput: { - src: withQuery('https://www.googletagmanager.com/gtm.js', { + src: withQuery(opts.source || 'https://www.googletagmanager.com/gtm.js', { id: opts.id, l: opts.l, gtm_auth: opts.auth, diff --git a/tasks/implementation-roadmap.md b/tasks/implementation-roadmap.md new file mode 100644 index 00000000..929b25e6 --- /dev/null +++ b/tasks/implementation-roadmap.md @@ -0,0 +1,245 @@ +# Nuxt Scripts Issues Implementation Roadmap + +## Overview + +This document outlines the implementation plan for 6 triaged GitHub issues, organized by priority and complexity. Start with quick wins to deliver immediate value, then progress to more complex features. + +## Quick Wins (Immediate Implementation - 1-2 days) + +### ๐Ÿš€ Priority 1: Issue #482 - Google Tag Manager Source Option + +**Estimated Time**: 2-3 hours +**Risk**: Very Low +**Impact**: High (enables server-side GTM setups) + +**Files to modify**: +1. `src/runtime/registry/google-tag-manager.ts` (lines 82-112, 135) +2. `docs/content/scripts/tracking/google-tag-manager.md` + +**Implementation steps**: +1. Add `source: optional(string())` to `GoogleTagManagerOptions` schema +2. Update URL construction: `withQuery(opts.source || 'https://www.googletagmanager.com/gtm.js', { ... })` +3. Add JSDoc documentation for the new option +4. Update documentation with examples +5. Add playground example + +**Code changes**: +```typescript +// In GoogleTagManagerOptions schema +/** The URL of the script; useful for server-side GTM */ +source: optional(string()), + +// In script loading logic +src: withQuery(opts.source || 'https://www.googletagmanager.com/gtm.js', { + // existing query parameters +}) +``` + +**Testing**: Basic URL parameter testing, no breaking changes + +--- + +### ๐Ÿš€ Priority 2: Issue #490 - Multi-Domain GTAG Documentation + +**Estimated Time**: 3-4 hours +**Risk**: None (documentation only) +**Impact**: High (helps many users with multi-language apps) + +**Files to modify**: +1. `docs/content/scripts/analytics/google-analytics.md` +2. `playground/pages/third-parties/google-analytics/` (add examples) + +**Implementation steps**: +1. Add "Multi-Domain Setup" section to GA docs +2. Add "Dynamic Configuration" examples +3. Include i18n integration examples +4. Add plugin and composable examples +5. Create playground examples for different scenarios + +**Example content to add**: +```markdown +## Multi-Domain and i18n Setup + +### Using with @nuxtjs/i18n +[Plugin example, composable example, runtime config example] + +### Custom Domain Detection +[Domain-based configuration examples] + +### Best Practices +[Performance considerations, data segmentation strategies] +``` + +**Testing**: Documentation review, example verification + +--- + +### ๐Ÿš€ Priority 3: Issue #481 - DevTools 500 Error Fix + +**Estimated Time**: 1-2 hours +**Risk**: Low +**Impact**: Medium (fixes broken DevTools functionality) + +**Files to modify**: +1. `client/app.vue` (lines 15, 57) + +**Implementation steps**: +1. Add null safety to `syncScripts` function +2. Add safe access when calling `syncScripts` +3. Test with Nuxt 4 setup +4. Verify no regression in Nuxt 3 + +**Code changes**: +```typescript +// In syncScripts function +function syncScripts(_scripts: any[]) { + if (!_scripts || typeof _scripts !== 'object') { + scripts.value = {} + return + } + // ... rest of function +} + +// In DevTools connection +syncScripts(client.host.nuxt._scripts || {}) +``` + +**Testing**: Create Nuxt 4 setup with empty script IDs, verify DevTools loads + +## Medium Priority (1-2 weeks) + +### ๐Ÿ”ง Priority 4: Issue #483 - Nuxt 4 TypeScript Types + +**Estimated Time**: 1-2 days +**Risk**: Medium (requires TypeScript system understanding) +**Impact**: High (critical for Nuxt 4 compatibility) + +**Files to modify**: +1. `src/module.ts` (around line 227) + +**Implementation approach**: +Use Nuxt's TypeScript hooks to ensure proper path mapping: +```typescript +nuxt.hooks.hook('typescript:setup', async (options) => { + if (options.tsConfig?.compilerOptions?.paths) { + options.tsConfig.compilerOptions.paths['#nuxt-scripts/*'] = [ + await resolvePath('./runtime/*') + ] + } +}) +``` + +**Testing requirements**: +- Test with Nuxt 3.16+ and Nuxt 4 +- Verify type resolution in `nuxt.config.ts` +- Ensure backward compatibility + +--- + +### ๐Ÿ”ง Priority 5: Issue #466 - Bundle Documentation and Runtime Access + +**Estimated Time**: 2-3 days +**Risk**: Medium (involves build/runtime coordination) +**Impact**: High (major UX improvement for bundling) + +**Phase 1 - Documentation Fix (Quick)**: 1-2 hours +**Files**: `docs/content/docs/1.guides/2.bundling.md` + +Add section explaining: +- Build-time vs runtime behavior +- Static URL requirements +- Manual injection patterns +- Limitations and workarounds + +**Phase 2 - Runtime Src Access (Complex)**: 1-2 days +**Files**: +- `src/plugins/transform.ts` +- `src/runtime/composables/useScript.ts` + +Preserve bundled src in script options and expose in return value. + +## Lower Priority (Future Releases) + +### ๐Ÿ”ฎ Priority 6: Issue #485 - Force Download Option + +**Estimated Time**: 1-2 days +**Risk**: Medium (affects cache logic) +**Impact**: Medium (useful for development workflows) + +**Files to modify**: +1. `src/runtime/types.ts` - Add `forceDownload` type +2. `src/plugins/transform.ts` - Modify cache logic +3. `src/module.ts` - Pass configuration to transformer + +**Implementation approach**: +```typescript +// Skip cache if forceDownload is true +if (!forceDownload && await storage.hasItem(`bundle:${filename}`)) { + // return cached version +} +// proceed with download +``` + +**Performance considerations**: +- Default to `false` to maintain performance +- Add warnings about build time impact +- Document best practices + +## Implementation Guidelines + +### Before Starting +1. **Review triage files**: Read detailed analysis in `tasks/issue-XXX-triage.md` +2. **Set up test environment**: Ensure you can test locally with both Nuxt 3 and 4 +3. **Check dependencies**: Verify all required tools and packages + +### During Implementation +1. **Follow existing patterns**: Match code style and architectural decisions +2. **Add comprehensive tests**: Include unit tests and integration tests +3. **Update TypeScript types**: Ensure type safety for new features +4. **Document changes**: Update relevant documentation + +### Quality Checklist +- [ ] **No breaking changes**: All changes are backward compatible +- [ ] **Type safety**: All new options are properly typed +- [ ] **Performance**: Consider impact on build times and bundle size +- [ ] **Documentation**: Update docs and add examples +- [ ] **Testing**: Add tests for new functionality +- [ ] **Cross-version compatibility**: Test with Nuxt 3.16+ and Nuxt 4 + +### Testing Strategy +1. **Unit tests**: For individual functions and components +2. **Integration tests**: For module behavior and TypeScript resolution +3. **Playground testing**: Use existing playground for manual testing +4. **Cross-platform testing**: Test on different operating systems if relevant + +## Success Metrics + +### Quick Wins (Week 1) +- [ ] Issue #482: GTM source option working +- [ ] Issue #490: Multi-domain documentation published +- [ ] Issue #481: DevTools error fixed + +### Medium Priority (Week 2-3) +- [ ] Issue #483: Nuxt 4 TypeScript types working +- [ ] Issue #466: Bundle documentation improved + +### Future Release +- [ ] Issue #485: Force download option implemented +- [ ] All issues have comprehensive test coverage +- [ ] Documentation is complete and accurate + +## Additional Notes + +### Code Review Focus Areas +1. **Type safety**: Ensure all new options are properly typed +2. **Performance impact**: Minimize build time and runtime overhead +3. **Backward compatibility**: No breaking changes to existing APIs +4. **Error handling**: Graceful degradation for edge cases + +### Common Pitfalls to Avoid +1. **Hardcoded values**: Use configurable options where appropriate +2. **Missing null checks**: Always handle undefined/null values +3. **Breaking type changes**: Maintain existing type contracts +4. **Insufficient testing**: Test edge cases and error conditions + +Start with the quick wins to build momentum and deliver immediate value to users. The order is optimized for impact vs. effort ratio. \ No newline at end of file diff --git a/tasks/issue-466-triage.md b/tasks/issue-466-triage.md new file mode 100644 index 00000000..088a1705 --- /dev/null +++ b/tasks/issue-466-triage.md @@ -0,0 +1,111 @@ +# Issue #466 Triage: Bundle Auto-Injection and Documentation + +## Summary +**Issue**: `bundle: true` does not automatically inject bundled script nor update src, and this is undocumented + +**Status**: Bug with documentation gap +**Priority**: Medium-High (affects user expectations and UX) +**Type**: Feature enhancement + documentation improvement + +## Problem Description + +Users expect that setting `bundle: true` will either: +1. Automatically replace the script src with the bundled version at runtime, OR +2. Automatically inject the bundled script into the DOM + +Current behavior only does build-time URL transformation in source code, which leaves users confused about how to access the bundled script. + +## Root Cause Analysis + +### How Bundling Currently Works +- **Build-time only**: The transform plugin (`src/plugins/transform.ts`) replaces URLs during build +- **Static analysis**: Only works with literal string URLs (no dynamic/computed URLs) +- **AST transformation**: Replaces `src` in source code and removes `bundle: true` option +- **No runtime API**: Users cannot access the transformed src URL at runtime + +### What Users Actually Get vs Expect + +**Current behavior:** +```typescript +// Source code: +useScript('https://example.com/script.js', { bundle: true }) + +// Gets transformed to: +useScript('/_scripts/[hash].js', {}) + +// But runtime script instance doesn't expose the new src +``` + +**User expectation:** +```typescript +const { src } = useScript('https://example.com/script.js', { bundle: true }) +console.log(src) // Should be '/_scripts/[hash].js' but isn't available +``` + +## Investigation Findings + +### Key Files Involved +- `src/plugins/transform.ts` - Build-time bundling transform +- `src/runtime/composables/useScript.ts` - Main useScript implementation +- `docs/content/docs/1.guides/2.bundling.md` - Bundle documentation + +### Current Implementation Gap +1. **Runtime access missing**: No way to get the transformed src URL from script instance +2. **Documentation unclear**: Doesn't explain build-time vs runtime behavior +3. **No auto-injection**: No option to automatically inject bundled scripts into DOM +4. **Dynamic URL limitations**: Bundle only works with static strings + +## Proposed Solutions + +### 1. Documentation Fix (Immediate) +- Update bundling docs to clarify build-time transformation behavior +- Add examples showing the code transformation that occurs +- Explain when/why users need manual injection +- Document static URL requirement + +### 2. Runtime Src Access (Medium Priority) +Add runtime access to the transformed src: +```typescript +const script = useScript('https://example.com/script.js', { bundle: true }) +// script.src or script.entry.props.src should contain '/_scripts/[hash].js' +``` + +### 3. Auto-Injection Option (Lower Priority) +Add automatic injection capability: +```typescript +useScript('https://example.com/script.js', { + bundle: true, + inject: true // Automatically inject into DOM +}) +``` + +## Technical Implementation Strategy + +### Phase 1: Documentation (Quick Win) +- File: `docs/content/docs/1.guides/2.bundling.md` +- Add section explaining build-time vs runtime behavior +- Include examples of manual injection patterns +- Document limitations (static URLs only) + +### Phase 2: Runtime Src Access +- Modify transform plugin to preserve bundled src in script options +- Update `useScript` to expose the transformed src in return value +- Add TypeScript types for the new API + +### Phase 3: Auto-Injection (Future Enhancement) +- Add `inject` option to script options +- Implement automatic DOM injection logic +- Update documentation with new option + +## Complexity Assessment +- **Documentation Fix**: Low complexity, high impact +- **Runtime Src Access**: Medium complexity, involves build/runtime coordination +- **Auto-Injection**: Medium-high complexity, needs careful DOM management + +## Impact +- **User Experience**: High - resolves major confusion point +- **Breaking Changes**: None for documentation and runtime access +- **Adoption**: Would improve bundle feature usability significantly + +## Recommendation +Start with documentation improvements as quick win, then implement runtime src access for better developer experience. \ No newline at end of file diff --git a/tasks/issue-466.md b/tasks/issue-466.md new file mode 100644 index 00000000..aa391d46 --- /dev/null +++ b/tasks/issue-466.md @@ -0,0 +1,115 @@ +bundle: true does not automatically inject bundled script nor update src, and this is undocumented #466 +Open +Open +bundle: true does not automatically inject bundled script nor update src, and this is undocumented +#466 +@agracia-foticos +Description +agracia-foticos +(Alberto Gracia) +opened on Jun 11 +๐Ÿ› The bug +When using useScript() with the bundle: true option, the documentation implies that the script will be bundled and served from the local domain automatically โ€” but this does not happen. + +Instead, the script is still loaded from the original external URL unless the developer manually uses proxy.src and injects their own +` + +harlan-zw +harlan-zw commented on Jun 11 +harlan-zw +on Jun 11 ยท edited by harlan-zw +Member +This sounds like a bug as this is what it should be doing. + +Maybe also a bug with it working in dev + +joaopedrodcf +joaopedrodcf commented 2 days ago +joaopedrodcf +(Joรฃo Ferreira) +2 days ago +I have problems with this way + +` + + +` + +This example wouldn't;t work with the bundle option, as far as I tested bundle option only works when you have a full url without variables + +This doesn't work as there is a variable interpolating + + +This works without the interpolation + + +So basically as far as I know if you have a script that requires interpolation ( example different scripts per country wouldn't work ) + +Maybe a solution right now would be replicate the useScript with the different urls per country but that would be too much or @nuxt/scripts needs to provide a way to bundle it even if there is interpolation ( the hard thing is how nuxt scripts knows al the possibilities to generate all those different scripts ) + +@harlan-zw diff --git a/tasks/issue-481-triage.md b/tasks/issue-481-triage.md new file mode 100644 index 00000000..fe4df452 --- /dev/null +++ b/tasks/issue-481-triage.md @@ -0,0 +1,134 @@ +# Issue #481 Triage: DevTools 500 Error - "can't convert undefined to object" + +## Summary +**Issue**: Error 500 Internal server error on DevTools scripts panel with error "can't convert undefined to object" + +**Status**: Bug - Initialization timing issue +**Priority**: Medium (DevTools UX issue, doesn't affect production) +**Type**: Bug fix - Nuxt 4 compatibility + +## Problem Description + +Users get a 500 error when accessing the Scripts panel in Nuxt DevTools with the error message "can't convert undefined to object". This appears to be specific to Nuxt 4 setups and occurs when `nuxtApp._scripts` is undefined during DevTools initialization. + +## Root Cause Analysis + +### Technical Root Cause +**File**: `client/app.vue:15` + +The error occurs in the `syncScripts` function: +```typescript +function syncScripts(_scripts: any[]) { + scripts.value = Object.fromEntries( + Object.entries({ ..._scripts }) // ERROR: _scripts is undefined + .map(([key, script]) => { /* ... */ }), + ) +} +``` + +### Initialization Sequence Problem +1. **DevTools connects early**: DevTools client connects before scripts are initialized +2. **`_scripts` undefined**: `nuxtApp._scripts` is only initialized when `useScript()` is first called +3. **Empty config triggers issue**: User has empty IDs in config, so no scripts get loaded +4. **Unsafe access**: DevTools tries to access undefined `_scripts` object + +### Why This Happens in Nuxt 4 +- **Timing changes**: Nuxt 4 has different initialization timing +- **Stricter type checking**: May affect how undefined values are handled +- **DevTools integration updates**: Changes in how DevTools connects to the app + +## Investigation Findings + +### Key Files Involved +- `client/app.vue` - DevTools UI component with the error +- `src/runtime/composables/useScript.ts` - Where `_scripts` is conditionally initialized +- User config with empty script IDs prevents initialization + +### Current Implementation Gap +1. **Unsafe object spread**: `{ ..._scripts }` fails when `_scripts` is undefined +2. **Conditional initialization**: `_scripts` only created when scripts are used +3. **No fallback handling**: DevTools assumes `_scripts` always exists + +### Error Flow +```typescript +// 1. DevTools connects +onDevtoolsClientConnected(async (client) => { + // 2. Tries to sync undefined _scripts + syncScripts(client.host.nuxt._scripts) // undefined +}) + +// 3. syncScripts fails on object spread +function syncScripts(_scripts: any[]) { + Object.entries({ ..._scripts }) // TypeError: can't convert undefined to object +} +``` + +## Proposed Solutions + +### 1. Quick Fix - Add Null Safety (Immediate) +**File**: `client/app.vue` +```typescript +function syncScripts(_scripts: any[]) { + // Add null check + if (!_scripts || typeof _scripts !== 'object') { + scripts.value = {} + return + } + + scripts.value = Object.fromEntries( + Object.entries({ ..._scripts }) + .map(([key, script]) => { /* existing logic */ }), + ) +} +``` + +### 2. Safe Access Pattern (Immediate) +**File**: `client/app.vue` +```typescript +onDevtoolsClientConnected(async (client) => { + // Use safe access with fallback + syncScripts(client.host.nuxt._scripts || {}) +}) +``` + +### 3. Early Initialization (Better Long-term) +**File**: `src/module.ts` or plugin +```typescript +if (nuxt.options.dev) { + // Ensure _scripts is always available for DevTools + nuxt.hooks.hook('app:created', (nuxtApp) => { + nuxtApp._scripts = nuxtApp._scripts || {} + }) +} +``` + +## Technical Implementation Strategy + +### Phase 1: Immediate Fix (Low Risk) +- Add null safety checks in `syncScripts` function +- Add safe access when calling `syncScripts` +- Test with Nuxt 4 setup + +### Phase 2: Robust Solution (Medium Risk) +- Initialize `_scripts` earlier in development mode +- Ensure consistent behavior across Nuxt 3.16+ and Nuxt 4 +- Add error boundaries for DevTools UI + +## Complexity Assessment +- **Null Safety Fix**: Very low complexity, immediate impact +- **Early Initialization**: Low complexity, requires testing across versions +- **DevTools Error Handling**: Low-medium complexity, improves UX + +## Testing Strategy +1. **Reproduce locally**: Create Nuxt 4 setup with empty script IDs +2. **Verify fix**: Ensure DevTools panel loads without errors +3. **Regression test**: Test with existing script configurations +4. **Cross-version test**: Verify Nuxt 3.16+ compatibility + +## Impact +- **User Experience**: High - fixes broken DevTools functionality +- **Breaking Changes**: None - purely additive safety measures +- **Development Workflow**: Improves DevTools reliability + +## Recommendation +Implement both quick fixes (phases 1) immediately as they're low-risk and high-impact. The null safety approach is bulletproof and handles edge cases gracefully. \ No newline at end of file diff --git a/tasks/issue-481.md b/tasks/issue-481.md new file mode 100644 index 00000000..89386c4a --- /dev/null +++ b/tasks/issue-481.md @@ -0,0 +1,111 @@ +Error 500 Internal server error on devtools scripts panel #481 +Open +Open +Error 500 Internal server error on devtools scripts panel +#481 +@Kumzy +Description +Kumzy +(Julien) +opened on Jul 24 ยท edited by Kumzy +Hello, when trying to access through DevTools to the Scripts panel, I have an error 500 on the panel with the following error + +can't convert undefined to object + +I can not find any logs to see where the error comes from. + +Here is part of my nuxt.config.ts (excluded some parts) + +export default defineNuxtConfig({ +devtools: { +enabled: true, +}, +runtimeConfig: { +public: { +scripts: { +googleAnalytics: { +id: "", +}, +clarity: { +id: "", +}, +googleTagManager: { +id: "", +}, +}, +}, +}, +modules: [ +"@vueuse/nuxt", +"@nuxtjs/i18n", +"@nuxt/image", +"@nuxtjs/sitemap", +"@nuxtjs/robots", +"nuxt-schema-org", +"nuxt-og-image", +"nuxt-link-checker", +"nuxt-seo-utils", +"@vee-validate/nuxt", +"@nuxt/content", +"@dargmuesli/nuxt-cookie-control", +"nuxt-security", +"nuxt-viewport", +"@nuxt/scripts", +], +scripts: { +enabled: true, +debug: true, +registry: { +googleTagManager: true, +clarity: true, +googleAnalytics: true, +}, +}, +}); +I am running Nuxt4 + +Also I could not reproduce using Stackblitz + +Activity +Kumzy +Kumzy commented on Jul 25 +Kumzy +(Julien) +on Jul 25 +Author +Is there anything I can do to get the trace of the error? + +joaopedrodcf +joaopedrodcf commented 2 days ago +joaopedrodcf +(Joรฃo Ferreira) +2 days ago +Can you add an example in stackblitz with a minimal reproduction ? + +Kumzy +Kumzy commented 2 days ago +Kumzy +(Julien) +2 days ago +Author +Sorry as I said in the original post, I did not succeed reproducing the error using Stackblitz. +I tried to upgrade all dependencies and the error is still there. + +joaopedrodcf +joaopedrodcf commented yesterday +joaopedrodcf +(Joรฃo Ferreira) +yesterday +Can you check your lock file and check the exact dependencies and on stackblitz add the same ones ? because you may be installing a different version because of the carrot ^ etc on package json + +Kumzy +Kumzy commented 13 hours ago +Kumzy +(Julien) +13 hours ago +Author +I was indeed at version 0.11.10 +Upgraded to the latest release 0.11.13 and the issue is still there + +The Stackblitz link when creating an issue has nuxt/scripts at version 0.10.X + diff --git a/tasks/issue-482-triage.md b/tasks/issue-482-triage.md new file mode 100644 index 00000000..8e635e45 --- /dev/null +++ b/tasks/issue-482-triage.md @@ -0,0 +1,173 @@ +# Issue #482 Triage: Google Tag Manager - Source Option + +## Summary +**Issue**: Add `source` option to Google Tag Manager for custom script URLs (server-side GTM) + +**Status**: Enhancement request - straightforward feature addition +**Priority**: Medium (useful for enterprise/server-side GTM setups) +**Type**: Feature enhancement + +## Problem Description + +Users want to customize the GTM script URL for server-side implementations, similar to the `source` option available in zadigetvoltaire/nuxt-gtm. Currently, the GTM script URL is hardcoded to `https://www.googletagmanager.com/gtm.js`. + +## Root Cause Analysis + +### Current Implementation +**File**: `src/runtime/registry/google-tag-manager.ts:135` + +The GTM script URL is hardcoded: +```typescript +src: withQuery('https://www.googletagmanager.com/gtm.js', { + id: opts.id, + l: opts.l, + gtm_auth: opts.auth, + // ... other parameters +}) +``` + +### Missing Feature +**File**: `src/runtime/registry/google-tag-manager.ts:82-112` + +The `GoogleTagManagerOptions` schema lacks a `source` option: +```typescript +export const GoogleTagManagerOptions = object({ + id: string(), + l: optional(string()), + auth: optional(string()), + preview: optional(string()), + cookiesWin: optional(union([boolean(), literal('x')])), + debug: optional(union([boolean(), literal('x')])), + npa: optional(union([boolean(), literal('1')])), + dataLayer: optional(string()), + envName: optional(string()), + authReferrerPolicy: optional(string()), + // MISSING: source option +}) +``` + +## Investigation Findings + +### Key Files Involved +- `src/runtime/registry/google-tag-manager.ts` - Main GTM implementation +- `docs/content/scripts/tracking/google-tag-manager.md` - Documentation + +### Current Workaround +Users can manually specify the full URL: +```typescript +useScriptGoogleTagManager({ + scriptInput: { + src: "https://www.mydomain.com/gtm.js?id=" + gtmId, + }, +}); +``` + +**Problems with workaround**: +- Bypasses built-in query parameter handling +- Loses validation and type safety +- Requires manual URL construction + +### Use Cases for Source Option +1. **Server-side GTM**: Custom GTM implementations hosted on own domain +2. **Proxy setups**: GTM proxied through own infrastructure +3. **Enterprise environments**: Custom GTM deployments +4. **Privacy compliance**: First-party GTM hosting + +## Proposed Solution + +### Implementation Requirements + +#### 1. Schema Update +**File**: `src/runtime/registry/google-tag-manager.ts` + +Add `source` option to `GoogleTagManagerOptions`: +```typescript +export const GoogleTagManagerOptions = object({ + /** GTM container ID (format: GTM-XXXXXX) */ + id: string(), + + /** The URL of the script; useful for server-side GTM */ + source: optional(string()), + + /** Optional dataLayer variable name */ + l: optional(string()), + + // ... existing options +}) +``` + +#### 2. URL Construction Update +**File**: `src/runtime/registry/google-tag-manager.ts:135` + +Update script URL logic: +```typescript +src: withQuery(opts.source || 'https://www.googletagmanager.com/gtm.js', { + id: opts.id, + l: opts.l, + gtm_auth: opts.auth, + // ... existing query parameters +}) +``` + +#### 3. Documentation Update +**File**: `docs/content/scripts/tracking/google-tag-manager.md` + +Add documentation for the new `source` option with examples. + +#### 4. Type Definition Update +Update TypeScript definitions to include the new option. + +## Technical Implementation Strategy + +### Phase 1: Core Implementation +1. **Update schema**: Add `source` option to `GoogleTagManagerOptions` +2. **Update URL logic**: Use custom source when provided +3. **Add JSDoc**: Document the new option + +### Phase 2: Documentation and Testing +1. **Update docs**: Add examples of server-side GTM usage +2. **Add tests**: Test custom source URLs +3. **Update playground**: Add example in documentation playground + +## Complexity Assessment +- **Implementation**: Very low complexity (5-10 lines of code) +- **Testing**: Low complexity (standard URL parameter testing) +- **Documentation**: Low complexity (add new option to existing docs) +- **Breaking Changes**: None (purely additive) + +## Benefits +1. **Feature Parity**: Matches functionality from zadigetvoltaire/nuxt-gtm +2. **Enterprise Support**: Enables server-side GTM setups +3. **Type Safety**: Maintains schema validation and TypeScript support +4. **Clean API**: Integrates seamlessly with existing GTM options + +## Implementation Details + +### Minimal Code Change +```typescript +// Schema addition (line ~85) +/** The URL of the script; useful for server-side GTM */ +source: optional(string()), + +// URL construction update (line 135) +src: withQuery(opts.source || 'https://www.googletagmanager.com/gtm.js', { + // existing query parameters +}) +``` + +### Example Usage +```typescript +useScriptGoogleTagManager({ + id: 'GTM-XXXXXX', + source: 'https://my-custom-domain.com/gtm.js' // Custom GTM source +}) +``` + +## Risk Assessment +- **Risk Level**: Very low +- **Breaking Changes**: None +- **Backward Compatibility**: 100% maintained +- **Testing Complexity**: Minimal + +## Recommendation +This is a straightforward enhancement with high user value and very low implementation risk. The change is minimal, non-breaking, and provides important functionality for enterprise GTM deployments. \ No newline at end of file diff --git a/tasks/issue-482.md b/tasks/issue-482.md new file mode 100644 index 00000000..52345fb1 --- /dev/null +++ b/tasks/issue-482.md @@ -0,0 +1,83 @@ +Google Tag Manager - Source option #482 +Open +@mattgrah-am +Description +Matt Graham +opened on Aug 6 +๐Ÿ†’ Your use case +Is it possible to add the source option into the Google tag manager paramaters like it is in zadigetvoltaire/nuxt-gtm + +/** +* The URL of the script; useful for server-side GTM. +* +* @default https://www.googletagmanager.com/gtm.js + */ + source?: string; + ๐Ÿ†• The solution you'd like + GoogleTagManagerOptions would include the ability to add the source as an option for server side GTM + +export const GoogleTagManagerOptions = object({ +/** GTM container ID (format: GTM-XXXXXX) */ +id: string(), + +/** Optional dataLayer variable name */ +l: optional(string()), + +/** Authentication token for environment-specific container versions */ +auth: optional(string()), + +/** Preview environment name */ +preview: optional(string()), + +/** Forces GTM cookies to take precedence when true */ +cookiesWin: optional(union([boolean(), literal('x')])), + +/** Enables debug mode when true */ +debug: optional(union([boolean(), literal('x')])), + +/** No Personal Advertising - disables advertising features when true */ +npa: optional(union([boolean(), literal('1')])), + +/** Custom dataLayer name (alternative to "l" property) */ +dataLayer: optional(string()), + +/** Environment name for environment-specific container */ +envName: optional(string()), + +/** Referrer policy for analytics requests */ +authReferrerPolicy: optional(string()), +}) +๐Ÿ” Alternatives you've considered +No response + +โ„น๏ธ Additional info +No response + +Activity + +mattgrah-am +added +enhancement +New feature or request +on Aug 6 +MuhammadM1998 +MuhammadM1998 commented on Aug 7 +MuhammadM1998 +(Muhammad Mahmoud) +on Aug 7 +There's already a PR for this #408 + +joaopedrodcf +joaopedrodcf commented 2 days ago +joaopedrodcf +(Joรฃo Ferreira) +2 days ago +Maybe you can try for now to use the src to add the full path: + +const gtmId = 'XXX'; + +useScriptGoogleTagManager({ +scriptInput: { +src: "https://www.mydomain.com/gtm.js?id=" + gtmId, +}, +}); diff --git a/tasks/issue-483-triage.md b/tasks/issue-483-triage.md new file mode 100644 index 00000000..865b7d68 --- /dev/null +++ b/tasks/issue-483-triage.md @@ -0,0 +1,170 @@ +# Issue #483 Triage: Types Inferred as "any" in Nuxt 4 TypeScript Configuration + +## Summary +**Issue**: Types of all keys in scripts.registry inferred as any in nuxt.config.ts in Nuxt 4 tsconfig setup + +**Status**: Bug - Nuxt 4 compatibility issue with TypeScript configuration +**Priority**: Medium-High (affects developer experience in Nuxt 4) +**Type**: Bug fix - TypeScript/Module integration + +## Problem Description + +When using Nuxt 4's new TypeScript configuration setup, all keys inside `scripts.registry` are being inferred as `any` instead of their proper types. This works in StackBlitz (which uses Nuxt 3 tsconfig pattern) but fails locally with Nuxt 4's new separated tsconfig approach. + +## Root Cause Analysis + +### Nuxt 4 TypeScript Configuration Changes +**Nuxt 3**: Single `.nuxt/tsconfig.json` extended by project tsconfig +**Nuxt 4**: Multiple specialized tsconfig files: +- `.nuxt/tsconfig.json` - Main application types +- `.nuxt/tsconfig.node.json` - Node context (`nuxt.config.ts`, modules) +- `.nuxt/tsconfig.server.json` - Server context + +### Technical Root Cause +**Missing Path Mappings in Node Context** + +The nuxt-scripts module correctly registers its alias in the main tsconfig: +```json +// .nuxt/tsconfig.json โœ… HAS MAPPINGS +"paths": { + "#nuxt-scripts": ["../../src/runtime"], + "#nuxt-scripts/*": ["../../src/runtime/*"] +} +``` + +But these mappings are **missing** from the node context: +```json +// .nuxt/tsconfig.node.json โŒ MISSING MAPPINGS +"paths": { + // #nuxt-scripts mappings not present +} +``` + +Since `nuxt.config.ts` uses the node context for type resolution, it can't resolve the script registry types. + +## Investigation Findings + +### Key Files Involved +- `src/module.ts` - Module registration and alias setup +- `src/runtime/types.ts` - ScriptRegistry interface definition +- `.nuxt/tsconfig.node.json` - Missing path mappings for node context + +### Current Module Implementation +**File**: `src/module.ts:109-110` + +The module correctly registers aliases: +```typescript +nuxt.options.alias['#nuxt-scripts-validator'] = await resolvePath(`./runtime/validation/${(nuxt.options.dev || nuxt.options._prepare) ? 'valibot' : 'mock'}`) +nuxt.options.alias['#nuxt-scripts'] = await resolvePath('./runtime') +``` + +**File**: `src/module.ts:175-209` + +Type generation happens via `addTypeTemplate`, but only for runtime context. + +### User's Working Solution +The user found this manual fix works: +```json +// In tsconfig.node.json +"#nuxt-scripts/*": [ + "../node_modules/.pnpm/@nuxt+scripts@0.11.10_.../node_modules/@nuxt/scripts/dist/runtime/*" +] +``` + +This manually adds the path mapping that should be automatically included. + +### Why StackBlitz Works vs Local +- **StackBlitz**: Uses legacy Nuxt 3 tsconfig pattern (`extends .nuxt/tsconfig.json`) +- **Local Nuxt 4**: Uses new separated tsconfig with project references +- **Result**: Module aliases don't automatically propagate to node context in Nuxt 4 + +## Proposed Solutions + +### 1. Module-Level Fix (Recommended) +**File**: `src/module.ts` + +Ensure aliases are registered for node context in Nuxt 4: +```typescript +// Add after existing alias registration +nuxt.options.alias['#nuxt-scripts'] = await resolvePath('./runtime') + +// For Nuxt 4 compatibility - ensure node context has the mappings +if (nuxt.options.typescript?.tsConfig) { + const nodeConfig = nuxt.options.typescript.tsConfig + if (nodeConfig && typeof nodeConfig === 'object') { + nodeConfig.compilerOptions = nodeConfig.compilerOptions || {} + nodeConfig.compilerOptions.paths = nodeConfig.compilerOptions.paths || {} + nodeConfig.compilerOptions.paths['#nuxt-scripts/*'] = [await resolvePath('./runtime/*')] + } +} +``` + +### 2. Type Template for Node Context +**File**: `src/module.ts` + +Add additional type template specifically for node context: +```typescript +// After existing addTypeTemplate calls +addTypeTemplate({ + filename: 'types/nuxt-scripts-node.d.ts', + getContents: () => [ + 'declare module "#nuxt-scripts" {', + ' // Node context type declarations', + '}', + ].join('\n'), +}) +``` + +### 3. Hook into Nuxt's TypeScript Config Generation +**File**: `src/module.ts` + +Use Nuxt's typescript hooks to ensure proper path mapping: +```typescript +nuxt.hooks.hook('typescript:setup', async (options) => { + // Ensure our alias is included in all TypeScript contexts + if (options.tsConfig?.compilerOptions?.paths) { + options.tsConfig.compilerOptions.paths['#nuxt-scripts/*'] = [ + await resolvePath('./runtime/*') + ] + } +}) +``` + +## Technical Implementation Strategy + +### Phase 1: Investigation and Minimal Fix +1. **Reproduce locally**: Create Nuxt 4 project and confirm issue +2. **Test user solution**: Verify manual path mapping works +3. **Implement hook-based fix**: Use typescript:setup hook approach + +### Phase 2: Robust Module Integration +1. **Universal alias registration**: Ensure aliases work in all contexts +2. **Nuxt version detection**: Handle Nuxt 3 vs 4 differences gracefully +3. **Type generation updates**: Ensure proper type propagation + +### Phase 3: Testing and Documentation +1. **Cross-version testing**: Test with Nuxt 3.16+ and Nuxt 4 +2. **Integration tests**: Add TypeScript resolution tests +3. **Documentation update**: Note any Nuxt 4 specific requirements + +## Complexity Assessment +- **Hook-based Fix**: Low-medium complexity, requires TypeScript hook understanding +- **Universal Alias**: Medium complexity, needs careful Nuxt version handling +- **Type Template**: Low complexity, standard module pattern +- **Breaking Changes**: None - purely additive compatibility fix + +## Impact +- **Developer Experience**: High - fixes broken TypeScript in Nuxt 4 +- **Adoption**: Critical for Nuxt 4 compatibility +- **Breaking Changes**: None - backward compatible +- **Ecosystem**: Improves Nuxt 4 ecosystem compatibility + +## Risk Assessment +- **Risk Level**: Low-medium (TypeScript configuration changes) +- **Backward Compatibility**: Should maintain Nuxt 3 compatibility +- **Testing Requirements**: Need comprehensive cross-version testing + +## Recommendation +Implement the hook-based approach first (Phase 1) as it's the most direct solution that works with Nuxt's existing TypeScript infrastructure. This ensures the module properly integrates with Nuxt 4's new tsconfig approach while maintaining backward compatibility. + +The core issue is architectural - nuxt-scripts needs to explicitly ensure its type mappings are available in the node context, not just the runtime context. \ No newline at end of file diff --git a/tasks/issue-483.md b/tasks/issue-483.md new file mode 100644 index 00000000..25da6501 --- /dev/null +++ b/tasks/issue-483.md @@ -0,0 +1,70 @@ +Types of all keys in scripts.registry inferred as any in nuxt.config.ts in Nuxt 4 tsconfig setup #483 +Open +Open +Types of all keys in scripts.registry inferred as any in nuxt.config.ts in Nuxt 4 tsconfig setup +#483 +@benedictleejh +Description +benedictleejh +(Benedict Lee) +opened on Aug 7 +๐Ÿ› The bug +When trying to setup scripts in Nuxt config, all keys inside the registry config key are being inferred as any by TypeScript: + +Image +๐Ÿ› ๏ธ To reproduce +stackblitz.com/edit/nuxt-starter-wqfrm3oc?file=nuxt.config.ts + +๐ŸŒˆ Expected behavior +The types should be properly inferred as per the type definitions provided by Nuxt Scripts. + +โ„น๏ธ Additional context +I can consistently replicate this locally on Windows, Mac, and Linux (WSL OpenSUSE Tumbleweed), but for some reason, the types work on StackBlitz. I have tried switching to pnpm 8 locally to match StackBlitz, but the types are still being inferred as any. + +The StackBlitz project, and all my attempted local reproductions were created from the minimal Nuxt starter, with the Nuxt Scripts module added; the Nuxt Scripts starter in StackBlitz refused to provide me with types on hover. + +This was seen using Nuxt 4.0.3, TypeScript 5.9.2, pnpm 10.14.0. + +I could just be missing some configuration somewhere, but I can't figure out how the StackBlitz and local repos are different. + +Activity + +benedictleejh +added +bug +Something isn't working +on Aug 7 +joaopedrodcf +joaopedrodcf commented 2 days ago +joaopedrodcf +(Joรฃo Ferreira) +2 days ago +Hey @benedictleejh, + +Checked stackblitz and its as you say it does work there I it also works on my projects ) + +Did you tried to create a new project on your local machine with the same code as in stackblitz ? If it does work after that should be smth related with your project setup. + +Some ideas that may help + +Maybe try delete the pnpm.lock and install again. +Maybe delete the cache +git clean local only files like node modules and try pnpm install again + +benedictleejh +benedictleejh commented yesterday +benedictleejh +(Benedict Lee) +yesterday ยท edited by benedictleejh +Author +Ah, I figured it out. The stackblitz uses the old Nuxt root tsconfig where it extended .nuxt/tsconfig.json, whereas my reproductions were based on the new Nuxt 4 tsconfig, as I created a new Nuxt 4 project for them: nuxt/starter@v4/tsconfig.json. So the problem is reduced now to the types not working in the new Nuxt 4 tsconfig. + +After some testing, it seems the key is these lines in tsconfig: + +"#nuxt-scripts/*": [ +"../node_modules/.pnpm/@nuxt+scripts@0.11.10_@unhe_df90e380bb1753ca9de77cea7c1c8b00/node_modules/@nuxt/scripts/dist/runtime/*" +], +After adding these lines to tsconfig.node.json (which is the tsconfig context for nuxt.config.ts), the types work. That said, it's probably best for Nuxt scripts to add all of its types that are appropriate to Nuxt's node context, as other things could be different as well, just not surfaced as an issue yet. + +I'll update the title for the issue so its more clear what the cause is. + diff --git a/tasks/issue-485-triage.md b/tasks/issue-485-triage.md new file mode 100644 index 00000000..71bc1488 --- /dev/null +++ b/tasks/issue-485-triage.md @@ -0,0 +1,225 @@ +# Issue #485 Triage: Force Download Option for Scripts + +## Summary +**Issue**: Add `forceDownload` option to force scripts to be downloaded at compile time even if they already exist in cache + +**Status**: Enhancement request - moderate implementation complexity +**Priority**: Low-Medium (useful for development, less critical for production) +**Type**: Feature enhancement + +## Problem Description + +Users want the ability to force scripts to be re-downloaded during build time, even if they already exist in the cache. This is useful when: +- Scripts might have been updated at the source +- Development workflows need fresh scripts +- Debugging script-related issues +- Ensuring latest versions of third-party scripts + +Requested API: +```typescript +scripts: { + defaultScriptOptions: { + bundle: true, + forceDownload: true + } +} +``` + +## Root Cause Analysis + +### Current Caching Behavior +**File**: `src/plugins/transform.ts:69` + +The bundling system has aggressive caching: +```typescript +// Current cache check - always returns cached version if exists +if (await storage.hasItem(`bundle:${filename}`)) { + const res = await storage.getItemRaw(`bundle:${filename}`) + // Returns cached version, never re-downloads + return +} +``` + +### Cache Storage System +**File**: `src/assets.ts` + +- **Storage Location**: `node_modules/.cache/nuxt/scripts` +- **Cache Keys**: `bundle:${filename}` format +- **Current Invalidation**: Only cleared in production builds, not in development + +### Missing Feature +No mechanism exists to bypass cache for individual scripts or globally via configuration. + +## Investigation Findings + +### Key Files Involved +- `src/plugins/transform.ts` - Main bundling transform with cache logic +- `src/assets.ts` - Cache storage management +- `src/runtime/types.ts` - Type definitions +- `src/module.ts` - Module configuration and defaultScriptOptions + +### Current Architecture +1. **Build-time Transform**: AST analysis finds scripts with `bundle: true` +2. **Cache Check**: Checks `bundleStorage()` for existing file +3. **Download Logic**: Only downloads if not in cache +4. **File Transformation**: Replaces URLs in source code + +### Configuration Flow +```typescript +// nuxt.config.ts +scripts: { defaultScriptOptions: { bundle: true } } + โ†“ +// module.ts - passes to transformer +defaultBundle: config.defaultScriptOptions?.bundle + โ†“ +// transform.ts - uses in bundling logic +``` + +## Proposed Solution + +### Implementation Strategy + +#### 1. Type System Updates +**File**: `src/runtime/types.ts` + +Add `forceDownload` to `NuxtUseScriptOptions`: +```typescript +/** + * Force download scripts even if they exist in cache. + * Useful for development or ensuring fresh scripts. + * @default false + */ +forceDownload?: boolean +``` + +#### 2. Module Configuration Support +**File**: `src/module.ts` + +Pass `forceDownload` option to transformer: +```typescript +addBuildPlugin(NuxtScriptBundleTransformer({ + // ... existing options + forceDownload: config.defaultScriptOptions?.forceDownload, +})) +``` + +#### 3. Transform Plugin Updates +**File**: `src/plugins/transform.ts` + +**A. Update transformer interface** (line 21): +```typescript +export interface AssetBundlerTransformerOptions { + // ... existing options + forceDownload?: boolean +} +``` + +**B. Modify cache check logic** (line 69): +```typescript +// Skip cache if forceDownload is true +if (!forceDownload && await storage.hasItem(`bundle:${filename}`)) { + const res = await storage.getItemRaw(`bundle:${filename}`) + // ... return cached version + return +} +// Proceed with download... +``` + +**C. Pass option through download calls** (around line 257): +```typescript +const forceDownload = options.forceDownload || scriptForceDownload || false + +await downloadScript({ + src, + url, + filename, + forceDownload +}, renderedScript, options.fetchOptions) +``` + +#### 4. Support Both Global and Per-Script Configuration +```typescript +// Global configuration +scripts: { + defaultScriptOptions: { + bundle: true, + forceDownload: true + } +} + +// Per-script override +useScript('https://example.com/script.js', { + bundle: true, + forceDownload: true +}) +``` + +## Technical Implementation Strategy + +### Phase 1: Core Implementation +1. **Update types**: Add `forceDownload` to `NuxtUseScriptOptions` +2. **Modify cache logic**: Bypass cache when `forceDownload` is true +3. **Update module config**: Pass option to transformer +4. **Test basic functionality**: Global and per-script configuration + +### Phase 2: Enhanced Features +1. **Smart defaults**: Consider dev vs production behavior +2. **Performance warnings**: Warn about build time impact +3. **Selective download**: Fine-grained control over which scripts to force + +### Phase 3: Documentation and Optimization +1. **Update documentation**: Add examples and use cases +2. **Performance guidelines**: Best practices for using forceDownload +3. **Integration tests**: Ensure cache behavior works correctly + +## Complexity Assessment +- **Implementation**: Medium complexity (requires cache logic changes) +- **Type Safety**: Low complexity (straightforward type addition) +- **Configuration**: Low complexity (uses existing patterns) +- **Testing**: Medium complexity (need cache behavior tests) +- **Breaking Changes**: None (purely additive feature) + +## Performance Implications + +### Potential Impact +- **Build Time**: โฌ†๏ธ Increased due to re-downloading scripts +- **Network Usage**: โฌ†๏ธ Additional bandwidth consumption +- **Development Flow**: โฌ‡๏ธ Slower builds with many scripts +- **CI/CD**: โฌ‡๏ธ Longer deployment times if enabled + +### Mitigation Strategies +1. **Default to false**: Maintain current performance by default +2. **Development recommendation**: Use primarily in development +3. **Selective application**: Only force download specific scripts +4. **Warning system**: Alert developers about performance impact + +## Use Cases + +### Primary Use Cases +1. **Development**: Fresh scripts during development workflow +2. **Debugging**: Eliminate cache as source of issues +3. **CI/CD**: Ensure latest versions in automated builds +4. **Script Updates**: When third-party scripts are updated + +### Edge Cases +1. **Network Failures**: Handle download failures gracefully +2. **Large Scripts**: Performance impact with big script files +3. **Many Scripts**: Compound effect of multiple force downloads + +## Risk Assessment +- **Risk Level**: Low-Medium +- **Breaking Changes**: None (optional feature) +- **Backward Compatibility**: 100% maintained +- **Performance Risk**: Manageable with proper defaults and warnings + +## Recommendation + +This is a valuable enhancement that addresses a real developer need. The implementation is straightforward and follows existing architectural patterns. + +**Recommended approach**: +1. Start with basic implementation (Phase 1) +2. Default to `false` to maintain current performance +3. Add clear documentation about performance implications +4. Consider smart defaults (e.g., enable in dev mode only) + +The feature would integrate cleanly with existing systems and provide developers with the control they need over script caching without affecting default behavior. \ No newline at end of file diff --git a/tasks/issue-485.md b/tasks/issue-485.md new file mode 100644 index 00000000..cd608bf0 --- /dev/null +++ b/tasks/issue-485.md @@ -0,0 +1,35 @@ +Force to download files in nuxt.config.ts on compile time #485 +Open +@agracia-foticos +Description +agracia-foticos +(Alberto Gracia) +opened on Aug 14 ยท edited by agracia-foticos +๐Ÿ†’ Your use case +Force to download files in nuxt.config.ts on compile time, if file exists doesnt download in compile time, but file maybe can change... force download always trough param in nuxt.config.ts + +๐Ÿ†• The solution you'd like +add a param in nuxt.config.ts +scripts: { defaultScriptOptions: { bundle: true, forceDownload:true }}, + +to force download anytime the scripts + +๐Ÿ” Alternatives you've considered +No response + +โ„น๏ธ Additional info +No response + +Activity + +agracia-foticos +added +enhancement +New feature or request +on Aug 14 +joaopedrodcf +joaopedrodcf commented 2 days ago +joaopedrodcf +(Joรฃo Ferreira) +2 days ago +What's the difference on the current behavior don't we always in compile time generate the bundle of the scripts marked with bundle ? diff --git a/tasks/issue-490-triage.md b/tasks/issue-490-triage.md new file mode 100644 index 00000000..ca8d4e96 --- /dev/null +++ b/tasks/issue-490-triage.md @@ -0,0 +1,210 @@ +# Issue #490 Triage: Load GTAG ID by Domain or Language + +## Summary +**Issue**: Load different Google Analytics GTAG IDs based on domain or language in multi-language apps + +**Status**: Documentation request - functionality already exists +**Priority**: Low (documentation enhancement, not missing functionality) +**Type**: Documentation improvement + +## Problem Description + +User has a multi-language app with different domains and wants to load different Google Analytics tracking IDs based on the current domain/language. They're looking for a way to configure this globally rather than managing it per component. + +## Root Cause Analysis + +### Functionality Already Exists +After investigating the codebase, **the requested functionality is already fully supported** by the current nuxt-scripts implementation. The issue is not missing features but rather missing documentation and examples. + +### Current Google Analytics Implementation +**File**: `src/runtime/registry/google-analytics.ts` + +The Google Analytics implementation: +- โœ… Supports dynamic ID configuration +- โœ… Accepts reactive/computed values +- โœ… Supports multiple instances with different IDs +- โœ… Integrates with runtime configuration +- โœ… Works with i18n and domain detection + +### Suggested Solution (Already Works) +**From issue comments** - this approach is valid and works: +```typescript +// Get the country +const currentCountry = useCurrentCountry() + +// Map of id to country +const gtagPerCountry = { + 'pt-pt': 'XXX', + 'fr-fr': 'YYY', +} + +const googleAnalytics = useScriptGoogleAnalytics({ + id: gtagPerCountry[currentCountry] +}) +``` + +## Investigation Findings + +### Current Capabilities +1. **Dynamic Configuration**: Scripts accept reactive values for all parameters +2. **Multiple Instances**: Support for multiple GA instances with unique keys +3. **Runtime Config**: Integration with Nuxt's runtime configuration +4. **Global Setup**: Can be configured globally via plugins or composables + +### Example Implementations That Work Today + +#### Option 1: Plugin-based Global Configuration +```typescript +// plugins/analytics.client.ts +export default defineNuxtPlugin(() => { + const { locale } = useI18n() // or domain detection logic + + const gtagPerLocale = { + 'en': 'G-ENGLISH-ID', + 'fr': 'G-FRENCH-ID', + 'pt': 'G-PORTUGUESE-ID' + } + + const googleAnalytics = useScriptGoogleAnalytics({ + id: gtagPerLocale[locale.value] + }) + + return { + provide: { + analytics: googleAnalytics + } + } +}) +``` + +#### Option 2: Composable Approach +```typescript +// composables/useAnalyticsConfig.ts +export const useAnalyticsConfig = () => { + const { locale } = useI18n() + + const analyticsConfig = { + 'en': 'G-ENGLISH-ID', + 'fr': 'G-FRENCH-ID', + 'pt': 'G-PORTUGUESE-ID' + } + + return useScriptGoogleAnalytics({ + id: analyticsConfig[locale.value] + }) +} +``` + +#### Option 3: Multiple Instances for Different Domains +```typescript +// For complex multi-domain setups +const { gtag: enGtag } = useScriptGoogleAnalytics({ + key: 'en', + id: 'G-ENGLISH-ID', +}) + +const { gtag: frGtag } = useScriptGoogleAnalytics({ + key: 'fr', + id: 'G-FRENCH-ID', +}) +``` + +### Integration with Existing Modules +- **@nuxtjs/i18n**: โœ… Works with locale detection +- **Custom domain detection**: โœ… Works with any domain logic +- **Runtime configuration**: โœ… Supports environment-based config +- **SSR/Client-side**: โœ… Works in both contexts + +## Proposed Solution + +### Documentation Enhancement (Immediate) +**File**: `docs/content/scripts/analytics/google-analytics.md` + +Add new sections: + +#### 1. Multi-Domain Setup Section +```markdown +## Multi-Domain and i18n Setup + +For multi-language or multi-domain applications, you can configure different Google Analytics IDs based on locale or domain: + +### Using with @nuxtjs/i18n +[Include examples...] + +### Custom Domain Detection +[Include examples...] + +### Global Configuration +[Include plugin and composable examples...] +``` + +#### 2. Dynamic Configuration Section +```markdown +## Dynamic Configuration + +Google Analytics can be configured with reactive values: +[Include reactive/computed examples...] +``` + +#### 3. Best Practices Section +```markdown +## Best Practices for Multi-Language Apps + +### When to Use Multiple Instances vs Single Instance +### Performance Considerations +### Data Segmentation Strategies +``` + +### Documentation Files to Update +1. **`docs/content/scripts/analytics/google-analytics.md`** - Main GA documentation +2. **`docs/content/docs/1.guides/`** - Add multi-domain guide +3. **`playground/pages/third-parties/google-analytics/`** - Add examples + +## Technical Implementation Strategy + +### Phase 1: Documentation (Immediate) +1. **Update GA documentation**: Add multi-domain examples +2. **Create examples**: Add playground examples for different scenarios +3. **Best practices guide**: Document recommended approaches + +### Phase 2: Enhanced Examples (Short-term) +1. **i18n integration examples**: Show @nuxtjs/i18n integration +2. **Domain detection examples**: Various domain detection strategies +3. **Plugin templates**: Ready-to-use plugin examples + +### Phase 3: Advanced Features (Future) +1. **Helper utilities**: Composables for common multi-domain patterns +2. **Configuration validation**: Type-safe configuration helpers +3. **Migration guides**: From other analytics solutions + +## Complexity Assessment +- **Documentation**: Very low complexity, high impact +- **Examples**: Low complexity, immediate value +- **Helper utilities**: Low-medium complexity, nice-to-have +- **Breaking Changes**: None - purely additive documentation + +## Impact +- **User Experience**: High - solves a common multi-language app need +- **Adoption**: Improves understanding of existing capabilities +- **SEO/Analytics**: Enables proper multi-domain analytics setup + +## Alternative Solutions Considered + +### Auto-Configuration (Not Recommended) +Could add automatic locale/domain detection, but this would: +- Be opinionated about i18n modules +- Reduce flexibility for custom setups +- Add complexity without significant benefit + +### New APIs (Not Needed) +Could add new helper functions, but existing APIs already provide all needed functionality. + +## Recommendation + +This is a **documentation issue, not a feature request**. The functionality already exists and works well. The solution is: + +1. **Immediate**: Update Google Analytics documentation with multi-domain examples +2. **Short-term**: Add playground examples and best practices guide +3. **Optional**: Create helper utilities if demand is high + +The suggested solution from the issue comments is correct and should be documented as the recommended approach. No code changes are needed - only documentation improvements to help users discover and implement these patterns. \ No newline at end of file diff --git a/tasks/issue-490.md b/tasks/issue-490.md new file mode 100644 index 00000000..ce0162d7 --- /dev/null +++ b/tasks/issue-490.md @@ -0,0 +1,54 @@ +Load GTAG ID by domain or language #490 +Open +@EduardoMateos +Description +EduardoMateos +(Eduardo Mateos Soto) +opened 3 weeks ago +๐Ÿ“š Is your documentation request related to a problem? +Hi, I have a multi-language app and I want to globally load a different gtag depending on the language. + +I use different domains, each domain with its own language, in i18n. + +Do you have any idea how I can do this? I canโ€™t find the right solution. + +thanks! + +๐Ÿ” Where should you find it? +No response + +โ„น๏ธ Additional context +No response + +Activity + +EduardoMateos +added +documentation +Improvements or additions to documentation +3 weeks ago +joaopedrodcf +joaopedrodcf commented 2 days ago +joaopedrodcf +(Joรฃo Ferreira) +2 days ago +Hey @EduardoMateos, + +in a vue component + + +// Get the country +const currentCountry = useCurrentCountry() + +// Map of id to country can be done in a config file as well +const gtagPerCountry = { +'pt-pt': 'XXX', +'fr-fr': 'YYY', +... +} + + +const googleAnalytics = useScriptGoogleAnalytics({ +id: gtagPerCountry[currentCountry] +}) +Wouldn't smth like this work for you ? From e961e716c3bcb6272bb86473467ade3318b7e3ae Mon Sep 17 00:00:00 2001 From: Harlan Wilton Date: Thu, 18 Sep 2025 09:29:03 +1000 Subject: [PATCH 2/2] docs: add comprehensive multi-domain and i18n setup for Google Analytics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add detailed documentation and playground examples for setting up Google Analytics with multi-language and multi-domain applications. - Add Multi-Domain and i18n Setup section to GA docs - Include examples for @nuxtjs/i18n integration - Add domain-based analytics configuration - Create composable and plugin approaches - Add best practices for performance and data segmentation - Create interactive playground example demonstrating locale and domain-based tracking Resolves #490 ๐Ÿค– Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../scripts/analytics/google-analytics.md | 182 ++++++++++++++++ .../google-analytics/multi-domain.vue | 204 ++++++++++++++++++ src/runtime/types.ts | 14 +- 3 files changed, 393 insertions(+), 7 deletions(-) create mode 100644 playground/pages/third-parties/google-analytics/multi-domain.vue diff --git a/docs/content/scripts/analytics/google-analytics.md b/docs/content/scripts/analytics/google-analytics.md index 3e6a38d1..69c41534 100644 --- a/docs/content/scripts/analytics/google-analytics.md +++ b/docs/content/scripts/analytics/google-analytics.md @@ -152,6 +152,188 @@ export const GoogleAnalyticsOptions = object({ }) ``` +## Multi-Domain and i18n Setup + +When working with multi-language applications or different domains, you may need to dynamically configure Google Analytics based on the current locale or domain. + +### Using with @nuxtjs/i18n + +For applications using [@nuxtjs/i18n](https://i18n.nuxtjs.org/), you can configure different Analytics IDs per locale: + +::code-group + +```vue [pages/index.vue - Plugin Approach] + +``` + +```vue [composables/useAnalytics.ts - Composable Approach] + +``` + +```ts [nuxt.config.ts - Runtime Config Approach] +export default defineNuxtConfig({ + // Runtime configuration for different locales + runtimeConfig: { + public: { + scripts: { + googleAnalytics: { + ids: { + en: process.env.GA_ID_EN, + es: process.env.GA_ID_ES, + fr: process.env.GA_ID_FR + } + } + } + } + } +}) + +// Then in your component: +// const config = useRuntimeConfig() +// const { locale } = useI18n() +// const gaId = config.public.scripts.googleAnalytics.ids[locale.value] +// useScriptGoogleAnalytics({ id: gaId }) +``` + +:: + +### Custom Domain Detection + +For applications with different domains for different markets: + +```vue [app.vue] + +``` + +### Advanced: Dynamic Configuration with Multiple Properties + +For complex setups where you need to send data to multiple Analytics properties: + +```vue [composables/useMultiAnalytics.ts] + +``` + +### Best Practices + +1. **Performance Considerations**: Only load one Analytics script per page to avoid performance issues +2. **Data Segmentation**: Use custom dimensions to segment data by locale/region instead of separate properties when possible +3. **Cookie Domain**: Ensure your cookie domain settings in GA match your multi-domain setup +4. **Cross-Domain Tracking**: Configure cross-domain tracking if users navigate between different domains + +```vue [Example: Locale-specific events] + +``` + ## Example Using Google Analytics only in production while using `gtag` to send a conversion event. diff --git a/playground/pages/third-parties/google-analytics/multi-domain.vue b/playground/pages/third-parties/google-analytics/multi-domain.vue new file mode 100644 index 00000000..53ab88fa --- /dev/null +++ b/playground/pages/third-parties/google-analytics/multi-domain.vue @@ -0,0 +1,204 @@ + + + diff --git a/src/runtime/types.ts b/src/runtime/types.ts index 6f45bc7d..45e2d356 100644 --- a/src/runtime/types.ts +++ b/src/runtime/types.ts @@ -173,16 +173,16 @@ export type RegistryScriptInput< Usable extends boolean = false, CanBypassOptions extends boolean = true, > - = (InferIfSchema - & { + = (InferIfSchema + & { /** * A unique key to use for the script, this can be used to load multiple of the same script with different options. */ - key?: string - scriptInput?: ScriptInput - scriptOptions?: Omit - }) - | Partial> & ( + key?: string + scriptInput?: ScriptInput + scriptOptions?: Omit + }) + | Partial> & ( CanBypassOptions extends true ? { /** * A unique key to use for the script, this can be used to load multiple of the same script with different options.