-
Notifications
You must be signed in to change notification settings - Fork 293
Inject gtm code conditionally #2167
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
base: trunk
Are you sure you want to change the base?
Changes from all commits
15567ed
4d4146d
bab424c
f6f9b0a
61f5338
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# WordPress Playground configuration | ||
# Copy this file to .env to customize your local deployment | ||
|
||
# Google Analytics/GTM Configuration | ||
# Leave empty to disable analytics | ||
VITE_GOOGLE_ANALYTICS_ID=G-SVTNFCP8T7 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
# WordPress Playground Configuration | ||
|
||
This document outlines how to configure your self-hosted WordPress Playground instance. | ||
|
||
## Environment Variables | ||
|
||
WordPress Playground uses environment variables for configuration. These can be set in the following files: | ||
|
||
- `.env` - Default configuration (included in repository) | ||
- `.env.local` - Local overrides (not committed to Git) | ||
|
||
## Available Configuration Options | ||
|
||
### Google Analytics | ||
|
||
The Google Analytics/GTM integration can be configured using: | ||
|
||
``` | ||
VITE_GOOGLE_ANALYTICS_ID=your-ga4-id | ||
``` | ||
|
||
To disable Google Analytics completely, set the value to an empty string: | ||
|
||
``` | ||
VITE_GOOGLE_ANALYTICS_ID= | ||
``` | ||
|
||
The Google Analytics script is automatically injected into the `<head>` section of all HTML pages during the build process. If the environment variable is not set or is empty, no analytics code will be included in the final HTML output, improving privacy and performance for self-hosted instances that don't require tracking. | ||
|
||
This configuration applies to: | ||
|
||
- The main application (`index.html`) | ||
- The WordPress PR previewer (`public/wordpress.html`) | ||
- The Gutenberg PR previewer (`public/gutenberg.html`) | ||
- All demo and builder HTML files | ||
|
||
## How to Configure Your Self-Hosted Instance | ||
|
||
1. Clone the repository | ||
2. Create a `.env.local` file with your custom configuration | ||
3. Build the project according to the main README instructions | ||
|
||
Example `.env.local` file: | ||
|
||
``` | ||
# Custom Google Analytics ID for my self-hosted instance | ||
VITE_GOOGLE_ANALYTICS_ID=G-MYANALYTICS123 | ||
``` | ||
|
||
## Building With Custom Configuration | ||
|
||
The environment variables are applied at build time. Make sure your custom `.env.local` file is in place before running: | ||
|
||
```bash | ||
# Standard build | ||
npm run build:website | ||
|
||
# Verbose build with analytics logging | ||
npm run build:website -- --verbose | ||
``` | ||
|
||
## Technical Implementation | ||
|
||
The analytics integration uses a custom Vite plugin that inserts the Google Analytics script at the end of the `<head>` section in all HTML files during the build process. This approach: | ||
|
||
1. Keeps analytics configuration separate from the code | ||
2. Ensures no analytics code is included in the HTML when disabled | ||
3. Requires no placeholder comments in the HTML source files | ||
4. Provides a clean way to customize analytics for self-hosted instances | ||
5. Maintains clean indentation and formatting in the output HTML | ||
6. Operates silently by default (logs can be enabled with `--verbose`) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
/* eslint-disable no-console */ | ||
import { Plugin } from 'vite'; | ||
import { existsSync, readFileSync, writeFileSync } from 'node:fs'; | ||
import { join } from 'node:path'; | ||
|
||
// Plugin options interface | ||
interface AnalyticsPluginOptions { | ||
verbose?: boolean; | ||
} | ||
|
||
/** | ||
* Vite plugin to inject Google Analytics into the head tag of HTML files | ||
* | ||
* @param options Plugin options | ||
* @returns {Plugin} A Vite plugin that processes HTML files during build | ||
*/ | ||
export function analyticsInjectionPlugin( | ||
options: AnalyticsPluginOptions = {} | ||
): Plugin { | ||
// Default options | ||
const { verbose = false } = options; | ||
|
||
// Shared analytics script template | ||
const getAnalyticsScript = (id: string) => { | ||
return ` | ||
<script async src="https://www.googletagmanager.com/gtag/js?id=${id}"></script> | ||
<script> | ||
window.dataLayer = window.dataLayer || []; | ||
function gtag() { dataLayer.push(arguments); } | ||
gtag('js', new Date()); | ||
gtag('config', '${id}'); | ||
</script> | ||
`; | ||
}; | ||
|
||
// Helper function for conditional logging | ||
const log = (msg: string, isError = false) => { | ||
// Only log if it's an error or verbose mode is enabled | ||
if (isError || verbose) { | ||
isError ? console.error(msg) : console.log(msg); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nitpick: linter complains about the console statement. It is configured this way to enforce using the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree. I've added the ignore rule for the entire file. |
||
} | ||
}; | ||
|
||
return { | ||
name: 'vite-plugin-analytics-injection', | ||
apply: 'build', // Only apply during build, not dev | ||
|
||
writeBundle(options, bundle) { | ||
const googleAnalyticsId = process.env.VITE_GOOGLE_ANALYTICS_ID; | ||
|
||
if (!googleAnalyticsId) { | ||
log( | ||
'Google Analytics disabled - no tracking will be added to HTML files.' | ||
); | ||
return; | ||
} | ||
|
||
log('Processing HTML files for Google Analytics injection...'); | ||
|
||
// Files to process - include all HTML files that need analytics | ||
const htmlFiles = [ | ||
'index.html', | ||
'wordpress.html', | ||
'gutenberg.html', | ||
]; | ||
const outputDir = options.dir || ''; | ||
|
||
let processedCount = 0; | ||
let skippedCount = 0; | ||
let notFoundCount = 0; | ||
|
||
htmlFiles.forEach((htmlFile) => { | ||
const outputPath = join(outputDir, htmlFile); | ||
|
||
if (existsSync(outputPath)) { | ||
log(`Processing ${htmlFile} for analytics...`); | ||
|
||
try { | ||
// Read file | ||
const content = readFileSync(outputPath, 'utf8'); | ||
|
||
// Check if the file already has analytics (to avoid duplicate injection) | ||
if ( | ||
content.includes( | ||
`gtag('config', '${googleAnalyticsId}')` | ||
) | ||
) { | ||
log( | ||
`Analytics already present in ${htmlFile}, skipping.` | ||
); | ||
skippedCount++; | ||
return; | ||
} | ||
|
||
// Find the closing head tag | ||
const headCloseIndex = content.indexOf('</head>'); | ||
if (headCloseIndex === -1) { | ||
log( | ||
`Could not find </head> tag in ${htmlFile}, skipping.`, | ||
true | ||
); | ||
skippedCount++; | ||
return; | ||
} | ||
|
||
// Insert the analytics script right before the closing head tag | ||
const analyticsScript = | ||
getAnalyticsScript(googleAnalyticsId); | ||
const updatedContent = | ||
content.substring(0, headCloseIndex) + | ||
analyticsScript + | ||
content.substring(headCloseIndex); | ||
|
||
// Write back | ||
writeFileSync(outputPath, updatedContent, 'utf8'); | ||
log(`Successfully injected analytics into ${htmlFile}`); | ||
processedCount++; | ||
} catch (error) { | ||
log(`Error processing ${htmlFile}: ${error}`, true); | ||
} | ||
} else { | ||
log(`File not found in build directory: ${outputPath}`); | ||
notFoundCount++; | ||
} | ||
}); | ||
|
||
// Always show summary even in non-verbose mode | ||
if (processedCount > 0) { | ||
log( | ||
`Analytics injection: ${processedCount} files processed, ${skippedCount} skipped, ${notFoundCount} not found.` | ||
); | ||
} | ||
}, | ||
}; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@andreilupu can we please add a test that confirms GTM is loaded, and
window.gtag
is defined, if the VITE_GOOGLE_ANALYTICS_ID environment variable is provided?