@ampless/plugin-gtm 0.1.1-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.ja.md +84 -0
- package/README.md +84 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.js +70 -0
- package/package.json +43 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ampless contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.ja.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
> English: [README.md](./README.md)
|
|
2
|
+
>
|
|
3
|
+
|
|
4
|
+
# @ampless/plugin-gtm
|
|
5
|
+
|
|
6
|
+
[ampless](https://github.com/heavymoons/ampless) 向け Google Tag Manager プラグイン。
|
|
7
|
+
|
|
8
|
+
> **プレリリース / アルファ版。** v1.0 まではマイナーバージョンでも破壊的変更が入る可能性があります。
|
|
9
|
+
|
|
10
|
+
descriptor ベースのプラグイン head/body 注入 API を使って、公開ページに GTM 標準の 2 つのスニペットを挿入します。
|
|
11
|
+
|
|
12
|
+
1. `<head>` 内に async ローダーのインライン `<script>`
|
|
13
|
+
2. `<body>` 末尾に対応する `<noscript>` iframe(JavaScript 無効の訪問者でも GTM 経由でページビューを登録できる)
|
|
14
|
+
|
|
15
|
+
コンテナ ID はデプロイ後 **`/admin/plugins` から編集可能** で、`cms.config.ts` の constructor 引数は初期デフォルトのシードに過ぎません。AWS のデータ権限は不要で、`trust_level` は `untrusted`、公開 Next.js プロセス内で描画時に動きます。
|
|
16
|
+
|
|
17
|
+
## インストール
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @ampless/plugin-gtm@alpha
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## 設定
|
|
24
|
+
|
|
25
|
+
`cms.config.ts` に記述します:
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { defineConfig } from 'ampless'
|
|
29
|
+
import gtmPlugin from '@ampless/plugin-gtm'
|
|
30
|
+
|
|
31
|
+
export default defineConfig({
|
|
32
|
+
// ...
|
|
33
|
+
plugins: [
|
|
34
|
+
gtmPlugin({
|
|
35
|
+
// 初期コンテナ ID。デプロイ後は /admin/plugins から編集可能。
|
|
36
|
+
// 空にしておけば無効化したまま ship できる。
|
|
37
|
+
containerId: '',
|
|
38
|
+
}),
|
|
39
|
+
],
|
|
40
|
+
})
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
| オプション | デフォルト | 備考 |
|
|
44
|
+
|---|---|---|
|
|
45
|
+
| `containerId` | `''` | 初期 GTM コンテナ ID。例: `GTM-XXXXXXX`。マニフェストの default を seed するだけで、ライブ値はリクエスト時に `/admin/plugins` から読み出される。空文字でプラグインを無効化したまま ship できる。 |
|
|
46
|
+
| `instanceId` | `'gtm'` | script / noscript 要素 id と設定保存キーの namespace。同サイトに複数の GTM コンテナを入れる場合に分ける。 |
|
|
47
|
+
|
|
48
|
+
## 管理画面からコンテナ ID を編集する
|
|
49
|
+
|
|
50
|
+
デプロイ後、コンテナ ID は `/admin/plugins` → **Google Tag Manager** で編集できます。空文字保存で `cms.config.ts` から削除せずに無効化、`GTM-XXXXXXX` を保存すれば有効化されます。変更内容は site-settings の S3 ミラーが反映された後(数秒)に公開サイトに反映されます。admin form は cache invalidation を少し遅延発火するので、processor の rebuild 完了前に公開側が古い snapshot を fetch する race を避けています。
|
|
51
|
+
|
|
52
|
+
## コンテナ ID の取得
|
|
53
|
+
|
|
54
|
+
1. [Google Tag Manager](https://tagmanager.google.com/) にサインインし、対象サイトのワークスペースを選択。
|
|
55
|
+
2. 画面右上にコンテナ ID(`GTM-XXXXXXX` の形式)が表示されます。
|
|
56
|
+
3. それを上の `containerId` に設定するか、管理画面のフォームに貼り付けます。
|
|
57
|
+
|
|
58
|
+
`GTM-XXXXXXX` はコンテナ識別子であって書き込み認証には使われないため、ソースコードにコミットして問題ありません。
|
|
59
|
+
|
|
60
|
+
### ID パターンについて
|
|
61
|
+
|
|
62
|
+
admin form はコンテナ ID を `^$|^GTM-[A-Z0-9]+$` でバリデートします(空、または「`GTM-` で始まりその後英数字」の実用形)。Google の [公式インストールドキュメント](https://support.google.com/tagmanager/answer/14847097) は厳密フォーマットを明文化していないため、これは意図的に緩い sanity check です。Google が今後より広い文字集合を使い始めたら issue を立ててください、パターンを広げます。
|
|
63
|
+
|
|
64
|
+
## 複数インスタンス
|
|
65
|
+
|
|
66
|
+
各 `gtmPlugin(...)` 呼び出しは描画 DOM と admin 設定保存の両方で独立した `instanceId` namespace を持ちます:
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
plugins: [
|
|
70
|
+
gtmPlugin({ instanceId: 'marketing', containerId: 'GTM-AAA' }),
|
|
71
|
+
gtmPlugin({ instanceId: 'product', containerId: 'GTM-BBB' }),
|
|
72
|
+
]
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
admin form では各 instance が個別パネルとして表示されます。
|
|
76
|
+
|
|
77
|
+
## トラストレベル
|
|
78
|
+
|
|
79
|
+
`untrusted`。プラグインは `@ampless/runtime` が検証・描画する head / body descriptor を返すだけです。DynamoDB、S3、Lambda プロセッサーには一切触れません。
|
|
80
|
+
|
|
81
|
+
## まだやらないこと
|
|
82
|
+
|
|
83
|
+
- **CSP nonce 連携** — インラインローダー script は `nonce` 無しで emit されます。ampless サイトに CSP enforcement を入れる段で別 RFP として middleware → SSR → descriptor の通り道を設計します
|
|
84
|
+
- **GTM コンテナのインポート** — このプラグインはローダーを注入するだけです。tags / triggers / variables は通常のサイトと同じく GTM の Web UI 内で設定してください
|
package/README.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
> 日本語版: [README.ja.md](./README.ja.md)
|
|
2
|
+
>
|
|
3
|
+
|
|
4
|
+
# @ampless/plugin-gtm
|
|
5
|
+
|
|
6
|
+
Google Tag Manager plugin for [ampless](https://github.com/heavymoons/ampless).
|
|
7
|
+
|
|
8
|
+
> **Pre-release / alpha.** Breaking changes possible in any minor version until v1.0.
|
|
9
|
+
|
|
10
|
+
Drops Google Tag Manager's standard two-part snippet onto every public page through the descriptor-based plugin head/body injection API:
|
|
11
|
+
|
|
12
|
+
1. The async loader inline `<script>` in `<head>`.
|
|
13
|
+
2. The matching `<noscript>` iframe at the end of `<body>`, so visitors with JavaScript disabled still register a pageview through GTM.
|
|
14
|
+
|
|
15
|
+
The container ID is **editable from `/admin/plugins`** after deploy — the constructor argument in `cms.config.ts` just seeds the initial default. No AWS data permissions are required; the plugin's `trust_level` is `untrusted` and everything runs at request time inside the public Next.js process.
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @ampless/plugin-gtm@alpha
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Configure
|
|
24
|
+
|
|
25
|
+
In `cms.config.ts`:
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { defineConfig } from 'ampless'
|
|
29
|
+
import gtmPlugin from '@ampless/plugin-gtm'
|
|
30
|
+
|
|
31
|
+
export default defineConfig({
|
|
32
|
+
// ...
|
|
33
|
+
plugins: [
|
|
34
|
+
gtmPlugin({
|
|
35
|
+
// Initial container ID. Editable from /admin/plugins after deploy.
|
|
36
|
+
// Leave empty to ship the plugin disabled and turn it on later.
|
|
37
|
+
containerId: '',
|
|
38
|
+
}),
|
|
39
|
+
],
|
|
40
|
+
})
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
| Option | Default | Notes |
|
|
44
|
+
|---|---|---|
|
|
45
|
+
| `containerId` | `''` | Initial GTM container ID, e.g. `GTM-XXXXXXX`. Seeds the manifest default — the live value is read from `/admin/plugins` at request time. Set to `''` to ship the plugin disabled. |
|
|
46
|
+
| `instanceId` | `'gtm'` | Namespace used for the script / noscript element ids and the settings storage key. Set distinct values when registering multiple GTM containers on the same site. |
|
|
47
|
+
|
|
48
|
+
## Editing the container ID from the admin UI
|
|
49
|
+
|
|
50
|
+
After a deploy, the container ID lives at `/admin/plugins` → **Google Tag Manager**. Saving an empty value disables the plugin without removing it from `cms.config.ts`; saving `GTM-XXXXXXX` enables it. Changes are reflected on the public site after the site-settings S3 mirror finishes (a few seconds; the admin form delays the cache invalidation slightly so the rebuild has time to complete).
|
|
51
|
+
|
|
52
|
+
## Getting your container ID
|
|
53
|
+
|
|
54
|
+
1. Sign in to [Google Tag Manager](https://tagmanager.google.com/) and pick the workspace for the site.
|
|
55
|
+
2. The container ID (`GTM-XXXXXXX`) is shown in the top-right of the workspace UI.
|
|
56
|
+
3. Copy it into the `containerId` option above, or paste it into the admin form.
|
|
57
|
+
|
|
58
|
+
`GTM-XXXXXXX` is safe to commit to source control — it identifies the container but does not authenticate writes.
|
|
59
|
+
|
|
60
|
+
### A note on the ID pattern
|
|
61
|
+
|
|
62
|
+
The admin form validates the container ID against `^$|^GTM-[A-Z0-9]+$` — empty or the practical "starts with `GTM-`, then alphanumeric" shape. Google's [official install docs](https://support.google.com/tagmanager/answer/14847097) don't publish a strict format, so this is intentionally a loose sanity-check rather than a tight schema. If Google starts issuing a wider character set, file an issue and we'll widen the pattern.
|
|
63
|
+
|
|
64
|
+
## Multiple instances
|
|
65
|
+
|
|
66
|
+
Each `gtmPlugin(...)` call gets its own `instanceId` namespace, both in the rendered DOM and in the admin settings storage:
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
plugins: [
|
|
70
|
+
gtmPlugin({ instanceId: 'marketing', containerId: 'GTM-AAA' }),
|
|
71
|
+
gtmPlugin({ instanceId: 'product', containerId: 'GTM-BBB' }),
|
|
72
|
+
]
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The admin form lists each instance as its own panel.
|
|
76
|
+
|
|
77
|
+
## Trust level
|
|
78
|
+
|
|
79
|
+
`untrusted`. The plugin only contributes head and body descriptors that are validated and rendered by `@ampless/runtime`. It does not touch DynamoDB, S3, or any Lambda processor.
|
|
80
|
+
|
|
81
|
+
## What it does not do
|
|
82
|
+
|
|
83
|
+
- **CSP nonce propagation** — the inline loader script is emitted without a `nonce`. ampless sites don't enforce a CSP today; once that lands, a dedicated RFP will wire `nonce` end-to-end (middleware → SSR → descriptor).
|
|
84
|
+
- **GTM container imports** — this plugin only injects the loader. Tags / triggers / variables are configured inside the GTM web UI like any other site.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { AmplessPlugin } from 'ampless';
|
|
2
|
+
|
|
3
|
+
interface GtmOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Optional fallback Google Tag Manager container ID, e.g.
|
|
6
|
+
* `"GTM-XXXXXXX"`. Used only when no admin-stored value exists in
|
|
7
|
+
* `pk='siteconfig', sk='plugins.<instanceId>.containerId'`. An
|
|
8
|
+
* empty string disables the plugin (both `publicHead` and
|
|
9
|
+
* `publicBodyEnd` return `[]`), which is the recommended way to
|
|
10
|
+
* keep the dependency wired up while toggling GTM off — for
|
|
11
|
+
* example in staging or before consent is granted.
|
|
12
|
+
*
|
|
13
|
+
* New deployments should leave it empty and configure the value
|
|
14
|
+
* from `/admin/plugins`.
|
|
15
|
+
*/
|
|
16
|
+
containerId?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Optional namespace for this instance. Defaults to `'gtm'`. Set
|
|
19
|
+
* distinct values when registering the plugin twice (e.g. a
|
|
20
|
+
* marketing + a product container on the same site).
|
|
21
|
+
*/
|
|
22
|
+
instanceId?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Factory for the GTM plugin. Returns a plugin manifest that emits
|
|
26
|
+
* the GTM loader inline script in `<head>` and the matching
|
|
27
|
+
* `<noscript>` iframe at the end of `<body>`. The plugin is
|
|
28
|
+
* `untrusted` — it does not need any AWS data permissions because
|
|
29
|
+
* everything happens inside the public Next.js process at render
|
|
30
|
+
* time.
|
|
31
|
+
*
|
|
32
|
+
* The container ID is read at request time through
|
|
33
|
+
* `ctx.setting<string>('containerId')`, with the constructor
|
|
34
|
+
* argument seeding the manifest `default` for first-time installs
|
|
35
|
+
* before an admin has saved a value.
|
|
36
|
+
*/
|
|
37
|
+
declare function gtmPlugin(options?: GtmOptions): AmplessPlugin;
|
|
38
|
+
|
|
39
|
+
export { type GtmOptions, gtmPlugin as default };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { definePlugin } from "ampless";
|
|
3
|
+
function gtmPlugin(options = {}) {
|
|
4
|
+
const { containerId = "", instanceId = "gtm" } = options;
|
|
5
|
+
return definePlugin({
|
|
6
|
+
name: "gtm",
|
|
7
|
+
instanceId,
|
|
8
|
+
displayName: { en: "Google Tag Manager", ja: "Google Tag Manager" },
|
|
9
|
+
apiVersion: 1,
|
|
10
|
+
trust_level: "untrusted",
|
|
11
|
+
capabilities: ["publicHead", "publicBody", "adminSettings"],
|
|
12
|
+
settings: {
|
|
13
|
+
public: [
|
|
14
|
+
{
|
|
15
|
+
type: "text",
|
|
16
|
+
key: "containerId",
|
|
17
|
+
label: {
|
|
18
|
+
en: "Container ID",
|
|
19
|
+
ja: "\u30B3\u30F3\u30C6\u30CA ID"
|
|
20
|
+
},
|
|
21
|
+
description: {
|
|
22
|
+
en: "GTM container ID (e.g. GTM-XXXXXXX). Leave blank to disable the plugin without removing it from cms.config.",
|
|
23
|
+
ja: "GTM \u306E\u30B3\u30F3\u30C6\u30CA ID (\u4F8B: GTM-XXXXXXX)\u3002\u7A7A\u306B\u3059\u308B\u3068 cms.config \u304B\u3089\u524A\u9664\u305B\u305A\u306B\u30D7\u30E9\u30B0\u30A4\u30F3\u3092\u7121\u52B9\u5316\u3067\u304D\u307E\u3059\u3002"
|
|
24
|
+
},
|
|
25
|
+
// Accept either empty (disable) or a well-formed GTM
|
|
26
|
+
// container id. Google's install docs don't publish a
|
|
27
|
+
// strict format, so we keep this loose — empty + the
|
|
28
|
+
// common `GTM-XXXX...` shape is the practical check.
|
|
29
|
+
pattern: "^$|^GTM-[A-Z0-9]+$",
|
|
30
|
+
placeholder: "GTM-XXXXXXX",
|
|
31
|
+
default: containerId
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
},
|
|
35
|
+
publicHead(ctx) {
|
|
36
|
+
const id = (ctx.setting("containerId") ?? "").trim();
|
|
37
|
+
if (!id) return [];
|
|
38
|
+
return [
|
|
39
|
+
{
|
|
40
|
+
type: "inlineScript",
|
|
41
|
+
id: `gtm-loader-${instanceId}`,
|
|
42
|
+
strategy: "afterInteractive",
|
|
43
|
+
// GTM's official "Add this code to the <head>" snippet,
|
|
44
|
+
// minified. JSON.stringify the container ID so any
|
|
45
|
+
// accidental quote/control char would stay quoted instead
|
|
46
|
+
// of breaking out of the literal.
|
|
47
|
+
body: `(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);})(window,document,'script','dataLayer',${JSON.stringify(id)});`
|
|
48
|
+
}
|
|
49
|
+
];
|
|
50
|
+
},
|
|
51
|
+
publicBodyEnd(ctx) {
|
|
52
|
+
const id = (ctx.setting("containerId") ?? "").trim();
|
|
53
|
+
if (!id) return [];
|
|
54
|
+
return [
|
|
55
|
+
{
|
|
56
|
+
type: "noscript",
|
|
57
|
+
id: `gtm-noscript-${instanceId}`,
|
|
58
|
+
// The matching `<noscript>` iframe fallback. The runtime
|
|
59
|
+
// pipes `html` through `dangerouslySetInnerHTML`, which is
|
|
60
|
+
// safe here because we built the value from a
|
|
61
|
+
// validator-checked container ID + a fixed template.
|
|
62
|
+
html: `<iframe src="https://www.googletagmanager.com/ns.html?id=${encodeURIComponent(id)}" height="0" width="0" style="display:none;visibility:hidden"></iframe>`
|
|
63
|
+
}
|
|
64
|
+
];
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
export {
|
|
69
|
+
gtmPlugin as default
|
|
70
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ampless/plugin-gtm",
|
|
3
|
+
"version": "0.1.1-alpha.0",
|
|
4
|
+
"description": "Google Tag Manager plugin for ampless",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"README.md"
|
|
16
|
+
],
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/heavymoons/ampless.git",
|
|
23
|
+
"directory": "packages/plugin-gtm"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://github.com/heavymoons/ampless/tree/main/packages/plugin-gtm#readme",
|
|
26
|
+
"bugs": "https://github.com/heavymoons/ampless/issues",
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"ampless": "1.0.0-alpha.18"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"ampless",
|
|
32
|
+
"plugin",
|
|
33
|
+
"gtm",
|
|
34
|
+
"google-tag-manager",
|
|
35
|
+
"analytics"
|
|
36
|
+
],
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsup",
|
|
39
|
+
"dev": "tsup --watch",
|
|
40
|
+
"lint": "tsc --noEmit",
|
|
41
|
+
"test": "vitest run --passWithNoTests"
|
|
42
|
+
}
|
|
43
|
+
}
|