create-ampless 1.0.0-alpha.131 → 1.0.0-alpha.133

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/dist/index.js CHANGED
@@ -2156,6 +2156,80 @@ function detectIndent(jsonStr) {
2156
2156
  if (!m) return 2;
2157
2157
  return m[1].length;
2158
2158
  }
2159
+ function djb2Hash(s) {
2160
+ let h = 5381;
2161
+ for (let i = 0; i < s.length; i++) {
2162
+ h = h * 33 ^ s.charCodeAt(i) | 0;
2163
+ }
2164
+ return (h >>> 0).toString(16);
2165
+ }
2166
+ async function regenerateEditorBootstrap(destDir, projectPkg) {
2167
+ const targetPath = join3(destDir, "app", "(admin)", "admin", "_editor-bootstrap.tsx");
2168
+ const installedPlugins = [];
2169
+ const warnings = [];
2170
+ const deps = projectPkg.dependencies ?? {};
2171
+ for (const pkgName of Object.keys(deps)) {
2172
+ const pkgJsonPath = join3(destDir, "node_modules", pkgName, "package.json");
2173
+ if (!existsSync5(pkgJsonPath)) continue;
2174
+ let mf;
2175
+ try {
2176
+ mf = JSON.parse(await readFile3(pkgJsonPath, "utf-8"));
2177
+ } catch (e) {
2178
+ warnings.push(`${pkgName}: failed to read package.json (${e instanceof Error ? e.message : String(e)}). Skipping editor wiring.`);
2179
+ continue;
2180
+ }
2181
+ const ep = mf?.amplessPlugin?.editorExports;
2182
+ if (typeof ep !== "string") continue;
2183
+ if (!ep.startsWith("./") || ep.includes("..") || ep.startsWith("/") || /[?#]/.test(ep)) {
2184
+ warnings.push(`${pkgName}: amplessPlugin.editorExports="${ep}" is invalid (must be a ./-prefixed relative subpath without .. or query/fragment). Skipping editor wiring.`);
2185
+ continue;
2186
+ }
2187
+ if (!mf?.exports || typeof mf.exports[ep] === "undefined") {
2188
+ warnings.push(`${pkgName}: amplessPlugin.editorExports="${ep}" is not declared in package.json#exports. Skipping editor wiring.`);
2189
+ continue;
2190
+ }
2191
+ installedPlugins.push({ packageName: pkgName, editorExports: ep });
2192
+ }
2193
+ installedPlugins.sort((a, b) => a.packageName.localeCompare(b.packageName));
2194
+ const lines = [];
2195
+ lines.push("'use client'", "");
2196
+ lines.push("// AUTO-GENERATED by `npm run update-ampless`. Do not edit \u2014 your changes");
2197
+ lines.push("// will be overwritten on the next run. To enable / disable a plugin's");
2198
+ lines.push("// editor extension, add / remove the corresponding @ampless/plugin-...");
2199
+ lines.push("// entry in your project's package.json (and register it in cms.config.ts");
2200
+ lines.push("// for runtime).", "");
2201
+ lines.push("import { installAdminEditorExtensions } from '@ampless/admin/editor'");
2202
+ const identifiers = [];
2203
+ const seen = /* @__PURE__ */ new Map();
2204
+ for (const p3 of installedPlugins) {
2205
+ let ident = "__" + p3.packageName.replace(/^@/, "").replace(/[^a-zA-Z0-9]+/g, "_") + "_editor";
2206
+ if (seen.has(ident) && seen.get(ident) !== p3.packageName) {
2207
+ const suffix = djb2Hash(p3.packageName).slice(0, 8);
2208
+ const orig = ident;
2209
+ ident = `${ident}_${suffix}`;
2210
+ warnings.push(`editor-bootstrap codegen: sanitised identifier "${orig}" collides between "${seen.get(orig)}" and "${p3.packageName}". Using "${ident}" for the latter to disambiguate.`);
2211
+ }
2212
+ seen.set(ident, p3.packageName);
2213
+ const importSubpath = p3.editorExports.startsWith("./") ? `${p3.packageName}/${p3.editorExports.slice(2)}` : `${p3.packageName}/${p3.editorExports}`;
2214
+ lines.push(`import { editorExtension as ${ident} } from '${importSubpath}'`);
2215
+ identifiers.push(ident);
2216
+ }
2217
+ lines.push("");
2218
+ lines.push("export function EditorBootstrap({ children }: { children: React.ReactNode }) {");
2219
+ if (identifiers.length === 0) {
2220
+ lines.push(" installAdminEditorExtensions([])");
2221
+ } else {
2222
+ lines.push(" installAdminEditorExtensions([");
2223
+ for (const id of identifiers) lines.push(` ${id},`);
2224
+ lines.push(" ])");
2225
+ }
2226
+ lines.push(" return <>{children}</>");
2227
+ lines.push("}");
2228
+ lines.push("");
2229
+ await mkdir2(dirname(targetPath), { recursive: true });
2230
+ await writeFile3(targetPath, lines.join("\n"), "utf-8");
2231
+ return { path: targetPath, pluginCount: installedPlugins.length, warnings };
2232
+ }
2159
2233
  async function runUpgradeIn(destDir, sharedDir, opts = {}) {
2160
2234
  const derivedRoot = opts.templatesRoot ?? join3(sharedDir, "..");
2161
2235
  const themeSyncEnabled = existsSync5(join3(derivedRoot, "_shared"));
@@ -2274,6 +2348,21 @@ async function runUpgradeIn(destDir, sharedDir, opts = {}) {
2274
2348
  const pm = usePnpm ? "pnpm" : "npm";
2275
2349
  await execa4(pm, ["install"], { cwd: destDir, stdio: "inherit" });
2276
2350
  }
2351
+ const codegenProjectPkg = JSON.parse(
2352
+ await readFile3(join3(destDir, "package.json"), "utf-8")
2353
+ );
2354
+ const bootstrapResult = await regenerateEditorBootstrap(destDir, codegenProjectPkg);
2355
+ if (opts.noInstall) {
2356
+ bootstrapResult.warnings.push(
2357
+ "_editor-bootstrap.tsx was generated without running npm install \u2014 node_modules may be stale. Run `npm run update-ampless` (without --no-install) to pick up newly added plugin editor extensions."
2358
+ );
2359
+ }
2360
+ for (const w of bootstrapResult.warnings) {
2361
+ log3.warn(w);
2362
+ }
2363
+ log3.info(
2364
+ `editor: ${pc3.cyan(`auto-wired ${bootstrapResult.pluginCount} editor extension(s) in _editor-bootstrap.tsx`)}`
2365
+ );
2277
2366
  return {
2278
2367
  added: replaceNew,
2279
2368
  updated: replaceUpdate,
@@ -2283,7 +2372,8 @@ async function runUpgradeIn(destDir, sharedDir, opts = {}) {
2283
2372
  themesPreserved: themeResult.preserved,
2284
2373
  themesQuarantined: themeResult.quarantined,
2285
2374
  packageJsonMerged: true,
2286
- obsoleteRemoved: obsoleteFiles
2375
+ obsoleteRemoved: obsoleteFiles,
2376
+ editorExtensionsWired: bootstrapResult.pluginCount
2287
2377
  };
2288
2378
  }
2289
2379
  async function runUpgrade(args) {
@@ -1,21 +1,13 @@
1
1
  'use client'
2
2
 
3
- // Phase 7 admin-editor extension slot. Templates wire first-party
4
- // embed plugins' tiptap Node extensions here so the admin's
5
- // <TiptapEditor> picks them up. Empty by default — uncomment the
6
- // imports + the registration to enable YouTube / x.com embeds.
7
- //
8
- // Wired into the admin layout via:
9
- // createAdminLayout(admin, { editorBootstrap: EditorBootstrap })
3
+ // Placeholder for fresh scaffolds before `update-ampless` runs.
4
+ // `npm run update-ampless` rewrites this file with auto-wired plugin
5
+ // editor extensions based on the project's installed `@ampless/plugin-*`
6
+ // packages.
10
7
 
11
8
  import { installAdminEditorExtensions } from '@ampless/admin/editor'
12
- // import { youtubeEditor } from '@ampless/plugin-youtube/editor'
13
- // import { tweetEditor } from '@ampless/plugin-x-embed/editor'
14
9
 
15
10
  export function EditorBootstrap({ children }: { children: React.ReactNode }) {
16
- installAdminEditorExtensions([
17
- // youtubeEditor.extension,
18
- // tweetEditor.extension,
19
- ])
11
+ installAdminEditorExtensions([])
20
12
  return <>{children}</>
21
13
  }
@@ -777,25 +777,67 @@ runtime は `src` のホスト allowlist を強制しない。CSP はサイト
777
777
 
778
778
  ## 6d. Admin エディタ拡張の配線 (Phase 7)
779
779
 
780
- admin エディタに tiptap Node 拡張を提供するプラグインは、別途 `./editor` subpath の client-side エントリを出荷する。テンプレート側で `_editor-bootstrap.tsx` から配線する:
780
+ admin エディタに tiptap Node 拡張を提供するプラグインは、別途 `./editor` subpath の client-side エントリを出荷する。
781
+ `app/(admin)/admin/_editor-bootstrap.tsx` は `npm run update-ampless` によって**自動生成**される — 手動で編集してはならない。
782
+
783
+ ### プラグイン著者向け
784
+
785
+ `package.json#amplessPlugin` に `editorExports` を宣言する:
786
+
787
+ ```jsonc
788
+ // packages/plugin-youtube/package.json
789
+ "amplessPlugin": {
790
+ "apiVersion": 1,
791
+ "name": "youtube",
792
+ "trustLevel": "trusted",
793
+ "capabilities": ["contentFields"],
794
+ "editorExports": "./editor" // ← editor module のある subpath
795
+ }
796
+ ```
797
+
798
+ その subpath から `editorExtension` を named export する:
799
+
800
+ ```ts
801
+ // packages/plugin-youtube/src/editor.tsx
802
+ export const editorExtension = AmplessYoutubeNode // tiptap Node または Extension
803
+ ```
804
+
805
+ `package.json#exports` にも同 subpath を宣言すること(codegen がこれを検証する — 未宣言の場合は warning を出してそのプラグインをスキップする):
806
+
807
+ ```jsonc
808
+ "exports": {
809
+ ".": { "import": "./dist/index.js", "types": "./dist/index.d.ts" },
810
+ "./editor": { "import": "./dist/editor.js", "types": "./dist/editor.d.ts" }
811
+ }
812
+ ```
813
+
814
+ ### プラグインユーザー(サイトエンジニア)向け
815
+
816
+ 1. `npm i @ampless/plugin-youtube@alpha` — plugin を dependency として追加する。
817
+ 2. `cms.config.ts` に登録してサーバーサイド renderer を有効化する。
818
+ 3. `npm run update-ampless` — インストール済み plugin の manifest から `_editor-bootstrap.tsx` を自動再生成する。
819
+
820
+ 生成されたファイルはリポジトリにコミットされ、以下のような内容になる:
781
821
 
782
822
  ```tsx
783
- // templates/_shared/app/(admin)/admin/_editor-bootstrap.tsx
823
+ // app/(admin)/admin/_editor-bootstrap.tsx (AUTO-GENERATED — 編集不可)
784
824
  'use client'
825
+ // AUTO-GENERATED by `npm run update-ampless`. Do not edit — your changes
826
+ // will be overwritten on the next run.
785
827
  import { installAdminEditorExtensions } from '@ampless/admin/editor'
786
- import { youtubeEditor } from '@ampless/plugin-youtube/editor'
787
- import { tweetEditor } from '@ampless/plugin-x-embed/editor'
828
+ import { editorExtension as __ampless_plugin_x_embed_editor } from '@ampless/plugin-x-embed/editor'
829
+ import { editorExtension as __ampless_plugin_youtube_editor } from '@ampless/plugin-youtube/editor'
788
830
 
789
831
  export function EditorBootstrap({ children }: { children: React.ReactNode }) {
790
832
  installAdminEditorExtensions([
791
- youtubeEditor.extension,
792
- tweetEditor.extension,
833
+ __ampless_plugin_x_embed_editor,
834
+ __ampless_plugin_youtube_editor,
793
835
  ])
794
836
  return <>{children}</>
795
837
  }
796
838
  ```
797
839
 
798
- その上で layout に渡す:
840
+ このファイルは admin layout に渡される(テンプレートに既に組み込まれているので追加の作業は不要):
799
841
 
800
842
  ```tsx
801
843
  // templates/_shared/app/(admin)/admin/layout.tsx
@@ -806,6 +848,13 @@ export default createAdminLayout(admin, { editorBootstrap: EditorBootstrap })
806
848
 
807
849
  `installAdminEditorExtensions` は idempotent で、render 時に client component 内で呼ばれる。admin の `<TiptapEditor>` は毎回 render 時に登録済みリストを built-in extensions の末尾に spread する。
808
850
 
851
+ ### active source と完全無効化
852
+
853
+ **エディタ配線の active source は `package.json#dependencies`**(= `node_modules`)であり、`cms.config.ts` ではない。
854
+
855
+ - plugin を `cms.config.ts` から外しても `package.json` に dep が残っていれば、editor の paste rule は**有効のまま**になる。editor は tiptap document にそのノード型を挿入できるが、公開 renderer はレンダリングしない(不整合状態 — 避けること)。
856
+ - 完全に無効化するには: `cms.config.ts` から削除 **かつ** `npm uninstall @ampless/plugin-...` を実行し、`npm run update-ampless` で bootstrap ファイルを再生成する。
857
+
809
858
  ### エディタプレビューのパイプライン
810
859
 
811
860
  admin の edit / new post フォームは preview ペインを `<iframe sandbox="allow-scripts">` で表示する。`srcDoc` はテンプレート側 preview Route Handler が返す HTML。テンプレート scaffold は handler を `app/(admin)/admin/preview/route.tsx` に同梱する:
@@ -1026,26 +1026,72 @@ middleware. This is documented in each plugin's README.
1026
1026
  ## 6d. Admin editor extension wiring (Phase 7)
1027
1027
 
1028
1028
  Plugins that contribute tiptap Node extensions to the admin editor
1029
- ship a separate client-side entry under the `./editor` subpath. The
1030
- template wires it up in `_editor-bootstrap.tsx`:
1029
+ ship a separate client-side entry under the `./editor` subpath.
1030
+ `app/(admin)/admin/_editor-bootstrap.tsx` is **auto-generated** by
1031
+ `npm run update-ampless` — you should not edit it by hand.
1032
+
1033
+ ### For plugin authors
1034
+
1035
+ Declare `editorExports` in `package.json#amplessPlugin`:
1036
+
1037
+ ```jsonc
1038
+ // packages/plugin-youtube/package.json
1039
+ "amplessPlugin": {
1040
+ "apiVersion": 1,
1041
+ "name": "youtube",
1042
+ "trustLevel": "trusted",
1043
+ "capabilities": ["contentFields"],
1044
+ "editorExports": "./editor" // ← subpath where the editor module lives
1045
+ }
1046
+ ```
1047
+
1048
+ Export `editorExtension` as a named symbol from that subpath:
1049
+
1050
+ ```ts
1051
+ // packages/plugin-youtube/src/editor.tsx
1052
+ export const editorExtension = AmplessYoutubeNode // tiptap Node or Extension
1053
+ ```
1054
+
1055
+ The subpath must be declared in `package.json#exports` as well (the
1056
+ codegen validates this — a missing `exports` entry triggers a warning
1057
+ and the plugin is skipped):
1058
+
1059
+ ```jsonc
1060
+ "exports": {
1061
+ ".": { "import": "./dist/index.js", "types": "./dist/index.d.ts" },
1062
+ "./editor": { "import": "./dist/editor.js", "types": "./dist/editor.d.ts" }
1063
+ }
1064
+ ```
1065
+
1066
+ ### For plugin users (site engineers)
1067
+
1068
+ 1. `npm i @ampless/plugin-youtube@alpha` — add the plugin as a dependency.
1069
+ 2. Register it in `cms.config.ts` for the server-side renderer.
1070
+ 3. `npm run update-ampless` — regenerates `_editor-bootstrap.tsx`
1071
+ automatically from the installed plugin manifests.
1072
+
1073
+ The generated file is committed to your repo and looks like:
1031
1074
 
1032
1075
  ```tsx
1033
- // templates/_shared/app/(admin)/admin/_editor-bootstrap.tsx
1076
+ // app/(admin)/admin/_editor-bootstrap.tsx (AUTO-GENERATED — do not edit)
1034
1077
  'use client'
1078
+ // AUTO-GENERATED by `npm run update-ampless`. Do not edit — your changes
1079
+ // will be overwritten on the next run.
1035
1080
  import { installAdminEditorExtensions } from '@ampless/admin/editor'
1036
- import { youtubeEditor } from '@ampless/plugin-youtube/editor'
1037
- import { tweetEditor } from '@ampless/plugin-x-embed/editor'
1081
+ import { editorExtension as __ampless_plugin_x_embed_editor } from '@ampless/plugin-x-embed/editor'
1082
+ import { editorExtension as __ampless_plugin_youtube_editor } from '@ampless/plugin-youtube/editor'
1038
1083
 
1039
1084
  export function EditorBootstrap({ children }: { children: React.ReactNode }) {
1040
1085
  installAdminEditorExtensions([
1041
- youtubeEditor.extension,
1042
- tweetEditor.extension,
1086
+ __ampless_plugin_x_embed_editor,
1087
+ __ampless_plugin_youtube_editor,
1043
1088
  ])
1044
1089
  return <>{children}</>
1045
1090
  }
1046
1091
  ```
1047
1092
 
1048
- Then thread it into the layout:
1093
+ The file is then wired into the admin layout (already in the template — no
1094
+ extra work required):
1049
1095
 
1050
1096
  ```tsx
1051
1097
  // templates/_shared/app/(admin)/admin/layout.tsx
@@ -1058,6 +1104,19 @@ export default createAdminLayout(admin, { editorBootstrap: EditorBootstrap })
1058
1104
  inside a client component. The admin's `<TiptapEditor>` spreads the
1059
1105
  registered list onto its built-in extensions on every render.
1060
1106
 
1107
+ ### Active source and full disable
1108
+
1109
+ **The active source for editor wiring is `package.json#dependencies`**
1110
+ (= `node_modules`), not `cms.config.ts`. This means:
1111
+
1112
+ - Removing a plugin from `cms.config.ts` but keeping it in
1113
+ `package.json` leaves the editor paste rule active. The editor can
1114
+ still insert the node type into the tiptap document, but the public
1115
+ renderer won't render it (inconsistent state — avoid).
1116
+ - To fully disable a plugin's editor extension: remove it from
1117
+ `cms.config.ts` **and** run `npm uninstall @ampless/plugin-...`,
1118
+ then `npm run update-ampless` to regenerate the bootstrap file.
1119
+
1061
1120
  ### Editor preview pipeline
1062
1121
 
1063
1122
  The admin's edit / new post forms render the preview pane in an
@@ -26,21 +26,21 @@
26
26
  "@tiptap/pm": "^3.23.6",
27
27
  "@tiptap/react": "^3.23.6",
28
28
  "@tiptap/starter-kit": "^3.23.6",
29
- "@ampless/plugin-analytics-ga4": "^0.2.0-alpha.27",
30
- "@ampless/plugin-cookie-consent": "^0.1.0-alpha.18",
31
- "@ampless/plugin-gtm": "^0.2.0-alpha.26",
32
- "@ampless/plugin-og-image": "^0.2.0-alpha.43",
33
- "@ampless/plugin-plausible": "^0.2.0-alpha.26",
34
- "@ampless/plugin-reading-time": "^0.1.0-alpha.16",
35
- "@ampless/plugin-rss": "^0.2.0-alpha.43",
36
- "@ampless/plugin-schema-jsonld": "^0.1.1-alpha.22",
37
- "@ampless/plugin-seo": "^0.2.0-alpha.43",
38
- "@ampless/plugin-webhook": "^0.2.0-alpha.44",
39
- "@ampless/admin": "^1.0.0-alpha.76",
40
- "@ampless/backend": "^1.0.0-alpha.65",
41
- "@ampless/runtime": "^1.0.0-alpha.52",
29
+ "@ampless/plugin-analytics-ga4": "^0.2.0-alpha.28",
30
+ "@ampless/plugin-cookie-consent": "^0.1.0-alpha.19",
31
+ "@ampless/plugin-gtm": "^0.2.0-alpha.27",
32
+ "@ampless/plugin-og-image": "^0.2.0-alpha.44",
33
+ "@ampless/plugin-plausible": "^0.2.0-alpha.27",
34
+ "@ampless/plugin-reading-time": "^0.1.0-alpha.17",
35
+ "@ampless/plugin-rss": "^0.2.0-alpha.44",
36
+ "@ampless/plugin-schema-jsonld": "^0.1.1-alpha.23",
37
+ "@ampless/plugin-seo": "^0.2.0-alpha.44",
38
+ "@ampless/plugin-webhook": "^0.2.0-alpha.45",
39
+ "@ampless/admin": "^1.0.0-alpha.77",
40
+ "@ampless/backend": "^1.0.0-alpha.66",
41
+ "@ampless/runtime": "^1.0.0-alpha.53",
42
42
  "@digital-go-jp/tailwind-theme-plugin": "^0.3.4",
43
- "ampless": "^1.0.0-alpha.43",
43
+ "ampless": "^1.0.0-alpha.44",
44
44
  "aws-amplify": "^6.17.0",
45
45
  "class-variance-authority": "^0.7.1",
46
46
  "clsx": "^2.1.1",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-ampless",
3
- "version": "1.0.0-alpha.131",
3
+ "version": "1.0.0-alpha.133",
4
4
  "description": "Create a new ampless project",
5
5
  "license": "MIT",
6
6
  "type": "module",