ScriptV2: Domain change refinement (#5657)

* Improve "back to settings" button

* Dark mode support for change domain

* Purge CDN cache on domain change

* Allow npm installation_type

* Detect npm installation type in detector

* Support npm installation type in onboarding

* Show warning in change domain flow for npm

* Make CE tests happy

* Cleanup

* npm_likely -> npm

* Cleanup
This commit is contained in:
Karl-Aksel Puulmann 2025-08-21 12:25:39 +03:00 committed by GitHub
parent d5ee36d47f
commit bcf8b422e1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 115 additions and 28 deletions

View File

@ -139,6 +139,7 @@ defmodule Plausible.InstallationSupport.Checks.Detection do
do: [ do: [
v1_detected: data["v1Detected"], v1_detected: data["v1Detected"],
gtm_likely: data["gtmLikely"], gtm_likely: data["gtmLikely"],
npm: data["npm"],
wordpress_likely: data["wordpressLikely"], wordpress_likely: data["wordpressLikely"],
wordpress_plugin: data["wordpressPlugin"], wordpress_plugin: data["wordpressPlugin"],
service_error: nil service_error: nil

View File

@ -9,6 +9,7 @@ defmodule Plausible.InstallationSupport.Detection.Diagnostics do
gtm_likely: nil, gtm_likely: nil,
wordpress_likely: nil, wordpress_likely: nil,
wordpress_plugin: nil, wordpress_plugin: nil,
npm: nil,
service_error: nil service_error: nil
@type t :: %__MODULE__{} @type t :: %__MODULE__{}
@ -39,6 +40,16 @@ defmodule Plausible.InstallationSupport.Detection.Diagnostics do
) )
end end
def interpret(
%__MODULE__{
npm: true,
service_error: nil
} = diagnostics,
_url
) do
get_result("npm", diagnostics)
end
def interpret( def interpret(
%__MODULE__{ %__MODULE__{
service_error: nil service_error: nil
@ -78,6 +89,7 @@ defmodule Plausible.InstallationSupport.Detection.Diagnostics do
data: %{ data: %{
v1_detected: diagnostics.v1_detected, v1_detected: diagnostics.v1_detected,
wordpress_plugin: diagnostics.wordpress_plugin, wordpress_plugin: diagnostics.wordpress_plugin,
npm: diagnostics.npm,
suggested_technology: suggested_technology suggested_technology: suggested_technology
} }
} }

View File

@ -86,20 +86,16 @@ defmodule PlausibleWeb.Live.ChangeDomainV2 do
</:subtitle> </:subtitle>
<:footer> <:footer>
<.focus_list> <.styled_link href={Routes.site_path(@socket, :settings_general, @site.domain)}>
<:item> Back to Site Settings
<.styled_link href={Routes.site_path(@socket, :settings_general, @site.domain)}> </.styled_link>
Go to Site Settings
</.styled_link>
</:item>
</.focus_list>
</:footer> </:footer>
<.async_result :let={detection_result} assign={@detection_result}> <.async_result :let={detection_result} assign={@detection_result}>
<:loading> <:loading>
<div class="flex items-center"> <div class="flex items-center">
<.spinner class="w-4 h-4 mr-2" /> <.spinner class="w-4 h-4 mr-2" />
<span class="text-sm text-gray-600">Checking your new domain...</span> <span class="text-sm text-gray-600 dark:text-gray-400">Checking your new domain...</span>
</div> </div>
</:loading> </:loading>
@ -107,17 +103,21 @@ defmodule PlausibleWeb.Live.ChangeDomainV2 do
<.generic_notice /> <.generic_notice />
</:failed> </:failed>
<.wordpress_plugin_notice :if={ <.success_notice :if={detection_result} detection_result={detection_result} />
detection_result && detection_result.v1_detected && detection_result.wordpress_plugin
} />
<.generic_notice :if={
detection_result && detection_result.v1_detected && !detection_result.wordpress_plugin
} />
</.async_result> </.async_result>
</.focus_box> </.focus_box>
""" """
end end
defp success_notice(assigns) do
case assigns.detection_result do
%{v1_detected: true, wordpress_plugin: true} -> wordpress_plugin_notice(assigns)
%{v1_detected: true, wordpress_plugin: false} -> generic_notice(assigns)
%{v1_detected: false, npm: true} -> generic_notice(assigns)
_ -> ~H""
end
end
defp wordpress_plugin_notice(assigns) do defp wordpress_plugin_notice(assigns) do
~H""" ~H"""
<.notice class="mt-4" title="Additional Steps Required"> <.notice class="mt-4" title="Additional Steps Required">
@ -149,6 +149,8 @@ defmodule PlausibleWeb.Live.ChangeDomainV2 do
end end
def handle_info({:domain_changed, updated_site}, socket) do def handle_info({:domain_changed, updated_site}, socket) do
PlausibleWeb.Tracker.purge_tracker_script_cache!(updated_site)
{:noreply, {:noreply,
socket socket
|> assign(site: updated_site) |> assign(site: updated_site)
@ -168,18 +170,9 @@ defmodule PlausibleWeb.Live.ChangeDomainV2 do
case detection_result do case detection_result do
%Result{ %Result{
ok?: true, ok?: true,
data: %{ data: data
v1_detected: v1_detected,
wordpress_plugin: wordpress_plugin
}
} -> } ->
{:ok, {:ok, %{detection_result: data}}
%{
detection_result: %{
v1_detected: v1_detected,
wordpress_plugin: wordpress_plugin
}
}}
%Result{ok?: false, errors: errors} -> %Result{ok?: false, errors: errors} ->
{:error, List.first(errors, :unknown_reason)} {:error, List.first(errors, :unknown_reason)}

View File

@ -24,7 +24,7 @@ defmodule PlausibleWeb.Plugins.API.Schemas.TrackerScriptConfiguration do
installation_type: %Schema{ installation_type: %Schema{
type: :string, type: :string,
description: "Tracker Script Installation Type", description: "Tracker Script Installation Type",
enum: ["manual", "wordpress", "gtm"] enum: ["manual", "wordpress", "gtm", "npm"]
}, },
hash_based_routing: %Schema{type: :boolean, description: "Hash Based Routing"}, hash_based_routing: %Schema{type: :boolean, description: "Hash Based Routing"},
outbound_links: %Schema{type: :boolean, description: "Track Outbound Links"}, outbound_links: %Schema{type: :boolean, description: "Track Outbound Links"},

View File

@ -17,7 +17,7 @@ defmodule PlausibleWeb.Plugins.API.Schemas.TrackerScriptConfiguration.UpdateRequ
installation_type: %Schema{ installation_type: %Schema{
type: :string, type: :string,
description: "Tracker Script Installation Type", description: "Tracker Script Installation Type",
enum: ["manual", "wordpress", "gtm"] enum: ["manual", "wordpress", "gtm", "npm"]
}, },
hash_based_routing: %Schema{type: :boolean, description: "Hash Based Routing"}, hash_based_routing: %Schema{type: :boolean, description: "Hash Based Routing"},
outbound_links: %Schema{type: :boolean, description: "Track Outbound Links"}, outbound_links: %Schema{type: :boolean, description: "Track Outbound Links"},

View File

@ -83,6 +83,11 @@ defmodule PlausibleWeb.Tracker do
end end
on_ee do on_ee do
def purge_tracker_script_cache!(site) do
tracker_script_configuration = get_or_create_tracker_script_configuration!(site)
purge_cache!(tracker_script_configuration.id)
end
defp should_purge_cache?(changeset) do defp should_purge_cache?(changeset) do
Map.keys(changeset.changes) != [:installation_type] Map.keys(changeset.changes) != [:installation_type]
end end
@ -96,6 +101,8 @@ defmodule PlausibleWeb.Tracker do
) )
|> Oban.insert!() |> Oban.insert!()
end end
else
def purge_tracker_script_cache!(_site), do: nil
end end
def update_script_configuration!(site, config_update, changeset_type) do def update_script_configuration!(site, config_update, changeset_type) do

View File

@ -223,6 +223,31 @@ defmodule PlausibleWeb.Live.ChangeDomainV2Test do
refute html =~ "also update the site" refute html =~ "also update the site"
end end
test "success page shows generic npm notice when detected", %{conn: conn, site: site} do
stub_detection_result(%{
"v1Detected" => false,
"gtmLikely" => false,
"npm" => true,
"wordpressLikely" => false,
"wordpressPlugin" => false
})
new_domain = "new-example.com"
{:ok, lv, _html} = live(conn, "/#{site.domain}/change-domain-v2")
lv
|> element("form")
|> render_submit(%{site: %{domain: new_domain}})
assert_patch(lv, "/#{new_domain}/change-domain-v2/success")
html = render_async(lv, 500)
assert html =~ "<i>must</i>"
assert html =~ "also update the site"
assert html =~ "Plausible Installation"
assert html =~ "within 72 hours"
end
test "success page handles detection error gracefully", %{conn: conn, site: site} do test "success page handles detection error gracefully", %{conn: conn, site: site} do
stub_detection_error() stub_detection_error()

View File

@ -301,6 +301,21 @@ defmodule PlausibleWeb.Live.InstallationV2Test do
assert text(html) =~ "We've detected your website is using Google Tag Manager" assert text(html) =~ "We've detected your website is using Google Tag Manager"
end end
test "detected NPM installation shows npm tab", %{conn: conn, site: site} do
stub_detection_result(%{
"v1Detected" => false,
"gtmLikely" => false,
"npm" => true,
"wordpressLikely" => false,
"wordpressPlugin" => false
})
{lv, _} = get_lv(conn, site)
html = render_async(lv, 500)
assert html =~ "Verify NPM installation"
end
test "shows v1 detection warning for manual installation", %{conn: conn, site: site} do test "shows v1 detection warning for manual installation", %{conn: conn, site: site} do
stub_dns_lookup_a_records(site.domain) stub_dns_lookup_a_records(site.domain)
stub_detection_manual_with_v1() stub_detection_manual_with_v1()
@ -468,6 +483,7 @@ defmodule PlausibleWeb.Live.InstallationV2Test do
stub_detection_result(%{ stub_detection_result(%{
"v1Detected" => false, "v1Detected" => false,
"gtmLikely" => false, "gtmLikely" => false,
"npm" => false,
"wordpressLikely" => false, "wordpressLikely" => false,
"wordpressPlugin" => false "wordpressPlugin" => false
}) })
@ -477,6 +493,7 @@ defmodule PlausibleWeb.Live.InstallationV2Test do
stub_detection_result(%{ stub_detection_result(%{
"v1Detected" => false, "v1Detected" => false,
"gtmLikely" => false, "gtmLikely" => false,
"npm" => false,
"wordpressLikely" => true, "wordpressLikely" => true,
"wordpressPlugin" => false "wordpressPlugin" => false
}) })
@ -486,6 +503,7 @@ defmodule PlausibleWeb.Live.InstallationV2Test do
stub_detection_result(%{ stub_detection_result(%{
"v1Detected" => false, "v1Detected" => false,
"gtmLikely" => true, "gtmLikely" => true,
"npm" => false,
"wordpressLikely" => false, "wordpressLikely" => false,
"wordpressPlugin" => false "wordpressPlugin" => false
}) })
@ -495,6 +513,7 @@ defmodule PlausibleWeb.Live.InstallationV2Test do
stub_detection_result(%{ stub_detection_result(%{
"v1Detected" => true, "v1Detected" => true,
"gtmLikely" => false, "gtmLikely" => false,
"npm" => false,
"wordpressLikely" => false, "wordpressLikely" => false,
"wordpressPlugin" => false "wordpressPlugin" => false
}) })
@ -504,6 +523,7 @@ defmodule PlausibleWeb.Live.InstallationV2Test do
stub_detection_result(%{ stub_detection_result(%{
"v1Detected" => true, "v1Detected" => true,
"gtmLikely" => false, "gtmLikely" => false,
"npm" => false,
"wordpressLikely" => true, "wordpressLikely" => true,
"wordpressPlugin" => false "wordpressPlugin" => false
}) })

View File

@ -0,0 +1,7 @@
export function checkNPM(document) {
if (typeof document === 'object') {
return window.plausible?.s === 'npm'
}
return false
}

View File

@ -1,6 +1,7 @@
import { waitForPlausibleFunction } from "./plausible-function-check" import { waitForPlausibleFunction } from "./plausible-function-check"
import { checkWordPress } from "./check-wordpress" import { checkWordPress } from "./check-wordpress"
import { checkGTM } from "./check-gtm" import { checkGTM } from "./check-gtm"
import { checkNPM } from "./check-npm"
window.scanPageBeforePlausibleInstallation = async function({ detectV1, debug, timeoutMs }) { window.scanPageBeforePlausibleInstallation = async function({ detectV1, debug, timeoutMs }) {
function log(message) { function log(message) {
@ -24,6 +25,9 @@ window.scanPageBeforePlausibleInstallation = async function({ detectV1, debug, t
const gtmLikely = checkGTM(document) const gtmLikely = checkGTM(document)
log(`gtmLikely: ${gtmLikely}`) log(`gtmLikely: ${gtmLikely}`)
const npm = checkNPM(document)
log(`npm: ${npm}`)
return { return {
data: { data: {
completed: true, completed: true,
@ -31,6 +35,7 @@ window.scanPageBeforePlausibleInstallation = async function({ detectV1, debug, t
wordpressPlugin: wordpressPlugin, wordpressPlugin: wordpressPlugin,
wordpressLikely: wordpressLikely, wordpressLikely: wordpressLikely,
gtmLikely: gtmLikely, gtmLikely: gtmLikely,
npm: npm
} }
} }
} }

View File

@ -52,6 +52,21 @@ test.describe('detector.js (tech recognition)', () => {
expect(result.data.wordpressPlugin).toBe(false) expect(result.data.wordpressPlugin).toBe(false)
expect(result.data.wordpressLikely).toBe(false) expect(result.data.wordpressLikely).toBe(false)
expect(result.data.gtmLikely).toBe(false) expect(result.data.gtmLikely).toBe(false)
expect(result.data.npm).toBe(false)
})
test('npm is reported correctly', async ({ page }, { testId }) => {
const { url } = await initializePageDynamically(page, {
testId,
scriptConfig: `<script type="module">import {init} from "/tracker/js/npm_package/plausible.js"; init({domain: "abc.de"});</script>`
})
const result = await detect(page, {url: url, detectV1: false})
expect(result.data.wordpressPlugin).toBe(false)
expect(result.data.wordpressLikely).toBe(false)
expect(result.data.gtmLikely).toBe(false)
expect(result.data.npm).toBe(true)
}) })
}) })
@ -77,6 +92,7 @@ test.describe('detector.js (v1 detection)', () => {
expect(result.data.wordpressPlugin).toBe(true) expect(result.data.wordpressPlugin).toBe(true)
expect(result.data.wordpressLikely).toBe(true) expect(result.data.wordpressLikely).toBe(true)
expect(result.data.gtmLikely).toBe(true) expect(result.data.gtmLikely).toBe(true)
expect(result.data.npm).toBe(false)
}) })
test('v1Detected is false when plausible function does not exist', async ({ page }, { testId }) => { test('v1Detected is false when plausible function does not exist', async ({ page }, { testId }) => {
@ -91,6 +107,7 @@ test.describe('detector.js (v1 detection)', () => {
expect(result.data.wordpressPlugin).toBe(false) expect(result.data.wordpressPlugin).toBe(false)
expect(result.data.wordpressLikely).toBe(false) expect(result.data.wordpressLikely).toBe(false)
expect(result.data.gtmLikely).toBe(false) expect(result.data.gtmLikely).toBe(false)
expect(result.data.npm).toBe(false)
}) })
test('v1Detected is false when v2 plausible installed', async ({ page }, { testId }) => { test('v1Detected is false when v2 plausible installed', async ({ page }, { testId }) => {