Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main'
Browse files Browse the repository at this point in the history
# Conflicts:
#	README.md
  • Loading branch information
harlan-zw committed Apr 12, 2024
2 parents 813cbb0 + 7b28f4f commit affa8b0
Show file tree
Hide file tree
Showing 54 changed files with 4,787 additions and 2,270 deletions.
2 changes: 2 additions & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
shamefully-hoist=true
strict-peer-dependencies=false
192 changes: 4 additions & 188 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ Better Privacy, Performance, and DX for Third-Party Scripts in Nuxt Apps.
## Features

- 🪨 [useScript by Unhead](https://unhead.unjs.io/usage/composables/use-script)
- 🦥 Improve your site performance with better script loading strategies
- 🎃 Powerful proxy API for SSR handling, lazy loading, and error handling
- 🎁 20+ third-party scripts composables and components
- ⏬ Serve scripts from your own server
- 🕵️ Privacy Features - Protect end users identity, provide consent to scripts.
Expand All @@ -36,193 +34,11 @@ To get started, simply run:
npx nuxi@latest module add @nuxt/scripts
```

To start using Nuxt Scripts, you can use the [useScript](https://unhead.unjs.io/usage/composables/use-script) composable to load your third-party scripts.
Done! You can now start using Nuxt Scripts in your Nuxt app.

### Confetti Preview

If you want to get a preview for how the module works, you can use the registry script for [JS Confetti](https://github.com/loonywizard/js-confetti).

```ts
// place anywhere, just works in SSR
const { addConfetti } = useScriptConfetti({ version: 'latest' }, {
trigger: 'onNuxtReady' // loads when the browser is idle
})
// will be executated on the client when the script is loaded
addConfetti({ emojis: ['🌈', '⚡️', '💥', '', '💫', '🌸'] })
```

## Registry

The registry is a collection of third-party scripts with out-of-the-box composable and component integrations for Nuxt Scripts.

Some registry scripts are stubs for external modules. When using these it will prompt to install the module for you.

- [Cloudflare Turnstile](#cloudflare-turnstile) - Nuxt Turnstile
- [Cloudflare Web Analytics](#cloudflare-web-analytics)
- [Confetti](#confetti)
- [Facebook Pixel](#facebook-pixel)
- [Fathom Analytics](#fathom-analytics)
- [Google Analytics](#google-analytics) - Nuxt Third Party Capital
- [Google Maps](#google-maps) - Nuxt Third Party Capital
- [GoogleMapsEmbed](#google-maps-embed)
- [Google Tag Manager](#google-tag-manager) - Nuxt Third Party Capital
- [Hotjar](#hotjar)
- [Intercom](#intercom)
- [Matomo Analytics](#matomo-analytics)
- [Segment](#segment)
- [Stripe](#stripe)
- [StripePricingTableEmbed](#stripe-pricing-table-mbed)
- [Vimeo](#vimeo-embed)
- [VimeoEmbed](#vimeo-embed)
- [X Pixel](#x-pixel)
- [YouTube](#youtube) - Nuxt Third Party Capital
- [YouTubeEmbed](#youtube-embed)

## Guides

### Loading Custom Scripts Globally

If you prefer a config based approach, you can load scripts globally by defining them in your `nuxt.config.ts`.

```ts
export default defineNuxtConfig({
scripts: {
globals: [
'https://example.com/script.js',
{
assetStrategy: 'bundle'
}
]
}
})
```

### Bundling Scripts

Bundling scripts can allow you to serve them from your own server, improving privacy and performance. It
can also help to get around ad blockers and other privacy tools when you need a script to load.

You can opt-in to have your scripts bundled by using the `assetStrategy` option. As this is
analyzed at build time, you must define it statically.

```ts
useScript('https://example.com/script.js', {
assetStrategy: 'bundle'
})
// script.js will be downloaded and bundled with your app as a static asset
```

### Overriding Scripts

When working with modules that use Nuxt Script, you may want to modify the
behavior of the script. This is especially useful for
changing the asset strategy of a script as it needs to be defined statically.

To do so you can use the `overrides` module option.

```ts
export default defineNuxtConfig({
scripts: {
overrides: {
// the key is either a specified key or the script src
confetti: {
assetStrategy: 'bundle'
}
}
}
})
```

### Privacy and Cookie Consent

Nuxt Scripts provides a `createScriptConsentTrigger` composable that allows you to load scripts based on user's consent.

You can either use it by providing a resolvable consent (ref, promise) option or by using `accept()`.

```ts
export const agreedToCookiesScriptConsent = createScriptConsentTrigger()
// ...
useScript('https://www.google-analytics.com/analytics.js', {
trigger: agreedToCookiesScriptConsent
})
// ...
agreedToCookiesScriptConsent.accept()
```

```ts
const agreedToCookies = ref(false)
useScript('https://www.google-analytics.com/analytics.js', {
// will be loaded in when the ref is true
trigger: createScriptConsentTrigger({
consent: agreedToCookies
})
})
```

### Sending Page Events

When using tracking scripts, it's common to send an event when the page changes. Due to Nuxt's head implementation being
async, the page title is not always available on route change immediately.

`useAnalyticsPageEvent` solves this by providing you with the page title and path when they change.

```ts
useAnalyticsPageEvent(({ title, path }) => {
// triggered on route change
gtag('event', 'page_view', {
page_title: title,
page_location: 'https://example.com',
page_path: path
})
})
```

## API

### `useScript`

Please see the [useScript](https://unhead.unjs.io/usage/composables/use-script) documentation.

### `createScriptConsentTrigger`

**(options: ScriptConsentTriggerOptions) => { accept: () => void } & Promise<void>**

Creates a consent trigger for a script.

#### Arguments

- `consent` (optional) - A ref, promise, or boolean that resolves to the user's consent. Defaults to `undefined`.
- `loadOnNuxtReady` (optional) - If consent is provided before the browser idle, wait for the browser to be idle before loading the script. Defaults to `false`.

#### Returns

- `accept` - A function that can be called to accept the consent and load the script.

```ts
const trigger = createScriptConsentTrigger()
// accept the consent and load the script
trigger.accept()
```

### `useAnalyticsPageEvent`

**(callback?: (page: { title: string, path: string }) => void) => Ref<{ title: string, path: string }>**

Access the current page title and path and trigger an event when they change.

#### Arguments

- `callback` (optional) - A function that will be called when the page title or path changes.

#### Returns

- A ref containing the current page title and path.

```ts
const pageCtx = useAnalyticsPageEvent()
// will always be the current page title
pageCtx.value.title
```
Check out the [📖 &nbsp;docs](https://nuxt-scripts.vercel.app/) or these resources to get started:
- [👉 &nbsp;Script Registry](https://nuxt-scripts.vercel.app/scripts)
- [👉 &nbsp;useScript](https://unhead.unjs.io/usage/composables/use-script)

## License

Expand Down
5 changes: 5 additions & 0 deletions docs/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Production license for @nuxt/ui-pro, get one at https://ui.nuxt.com/pro/purchase
NUXT_UI_PRO_LICENSE=

# Public URL, used for OG Image when running nuxt generate
NUXT_PUBLIC_SITE_URL=
1 change: 1 addition & 0 deletions docs/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
shamefully-hoist=true
1 change: 1 addition & 0 deletions docs/.nuxtrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
imports.autoImport=true
46 changes: 46 additions & 0 deletions docs/app.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
export default defineAppConfig({
ui: {
primary: 'green',
gray: 'slate',
footer: {
bottom: {
left: 'text-sm text-gray-500 dark:text-gray-400',
wrapper: 'border-t border-gray-200 dark:border-gray-800',
},
},
variables: {
dark: {
background: 'var(--color-gray-950)',
},
},
},
seo: {
siteName: 'Nuxt Scripts',
},
header: {
links: [{
'icon': 'i-simple-icons-github',
'to': 'https://github.com/nuxt/scripts',
'target': '_blank',
'aria-label': 'Nuxt Scripts',
}],
},
toc: {
title: 'Table of Contents',
bottom: {
title: 'Community',
edit: 'https://github.com/nnuxt/scripts/edit/main/docs/content',
links: [{
icon: 'i-heroicons-star',
label: 'Star on GitHub',
to: 'https://github.com/nuxt/scripts',
target: '_blank',
}, {
icon: 'i-simple-icons-nuxtdotjs',
label: 'Nuxt Scripts Admin',
to: 'https://admin.hub.nuxt.com',
target: '_blank',
}],
},
},
})
50 changes: 50 additions & 0 deletions docs/app.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<script setup lang="ts">
import type { ParsedContent } from '@nuxt/content/dist/runtime/types'
const { seo } = useAppConfig()
const { data: navigation } = await useAsyncData('navigation', () => fetchContentNavigation())
const { data: files } = useLazyFetch<ParsedContent[]>('/api/search.json', {
default: () => [],
server: false,
})
useHead({
meta: [
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
],
link: [
{ rel: 'icon', href: '/favicon.ico' },
],
htmlAttrs: {
lang: 'en',
},
})
useSeoMeta({
ogSiteName: seo?.siteName,
twitterCard: 'summary_large_image',
})
provide('navigation', navigation.value?.[0]?.children || [])
</script>

<template>
<div class="bg-white dark:bg-gray-950">
<Header />

<UMain>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</UMain>

<Footer />

<ClientOnly>
<LazyUContentSearch :files="files" :navigation="navigation" />
</ClientOnly>

<UNotifications />
</div>
</template>
26 changes: 26 additions & 0 deletions docs/components/Footer.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<script setup lang="ts">
const { footer } = useAppConfig()
const links = [
]
</script>

<template>
<UFooter :links="links">
<template #left>
<span class="inline">Made by <a href="https://nuxtlabs.com" target="_blank" class="text-gray-900 dark:text-gray-50 hover:underline underline-offset-2 font-medium">Harlan Wilton</a>.</span>
</template>

<template #right>
<UColorModeButton v-if="footer?.colorMode" />
<template v-if="footer?.links">
<UButton
v-for="(link, index) of footer?.links"
:key="index"
v-bind="{ color: 'gray', variant: 'ghost', ...link }"
/>
</template>
</template>
</UFooter>
</template>
47 changes: 47 additions & 0 deletions docs/components/Header.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<script setup lang="ts">
import type { NavItem } from '@nuxt/content/dist/runtime/types'
const navigation = inject<NavItem[]>('navigation', [])
const { metaSymbol } = useShortcuts()
const { header } = useAppConfig()
const links = [
{
label: 'Guides',
to: '/docs/getting-started',
},
{
label: 'Script Registry',
to: '/scripts',
},
{
label: 'API',
to: '/docs/api',
},
]
</script>

<template>
<UHeader :ui="{}" :links="links">
<template #logo>
<Logo />
</template>

<template #right>
<UTooltip text="Search" :shortcuts="[metaSymbol, 'K']" :popper="{ strategy: 'absolute' }">
<UContentSearchButton :label="null" />
</UTooltip>
<UColorModeButton />
<template v-if="header?.links">
<UButton
v-for="(link, index) of header.links"
:key="index"
v-bind="{ color: 'gray', variant: 'ghost', ...link }"
/>
</template>
</template>
<template #panel>
<UNavigationTree :links="mapContentNavigation(navigation)" />
</template>
</UHeader>
</template>
5 changes: 5 additions & 0 deletions docs/components/Logo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<template>
<div>
Nuxt<span style="color: #00dc82;">Scripts</span>
</div>
</template>
Loading

0 comments on commit affa8b0

Please sign in to comment.