create-ampless 0.2.0-alpha.9 → 1.0.0-alpha.39
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/README.ja.md +77 -0
- package/README.md +4 -1
- package/dist/index.js +99 -47
- package/dist/templates/_shared/AGENTS.ja.md +104 -0
- package/dist/templates/_shared/AGENTS.md +104 -0
- package/dist/templates/_shared/README.ja.md +175 -0
- package/dist/templates/_shared/README.md +175 -0
- package/dist/templates/_shared/RUNBOOK.ja.md +120 -0
- package/dist/templates/_shared/RUNBOOK.md +24 -51
- package/dist/templates/_shared/amplify/auth/post-confirmation/resource.ts +6 -0
- package/dist/templates/_shared/amplify/backend.ts +2 -0
- package/dist/templates/_shared/amplify/data/get-published-post.js +9 -12
- package/dist/templates/_shared/amplify/data/list-posts-by-tag.js +6 -9
- package/dist/templates/_shared/amplify/data/list-published-posts.js +10 -11
- package/dist/templates/_shared/amplify/data/resource.custom.ts +1 -2
- package/dist/templates/_shared/amplify/data/resource.ts +22 -5
- package/dist/templates/_shared/amplify/events/processor-untrusted/resource.ts +4 -0
- package/dist/templates/_shared/amplify/functions/mcp-handler/handler.ts +1 -0
- package/dist/templates/_shared/amplify/functions/mcp-handler/resource.ts +11 -0
- package/dist/templates/_shared/amplify/functions/user-admin/resource.ts +6 -0
- package/dist/templates/_shared/app/(admin)/admin/mcp-tokens/page.tsx +5 -0
- package/dist/templates/_shared/app/globals.css +55 -39
- package/dist/templates/_shared/app/layout.tsx +24 -11
- package/dist/templates/_shared/app/site/[siteId]/r/[slug]/[[...path]]/route.ts +17 -0
- package/dist/templates/_shared/cms.config.ts +1 -23
- package/dist/templates/_shared/components/site-chrome/site-sidebar.tsx +2 -3
- package/dist/templates/_shared/lib/admin.ts +9 -7
- package/dist/templates/_shared/lib/posts-public.ts +1 -1
- package/dist/templates/_shared/package.json +11 -12
- package/dist/templates/_shared/proxy.ts +3 -3
- package/dist/templates/blog/README.ja.md +22 -0
- package/dist/templates/blog/README.md +17 -47
- package/dist/templates/blog/manifest.ts +1 -1
- package/dist/templates/blog/pages/feed.ts +3 -4
- package/dist/templates/blog/pages/home.tsx +5 -8
- package/dist/templates/blog/pages/post.tsx +7 -7
- package/dist/templates/blog/pages/sitemap.ts +3 -4
- package/dist/templates/blog/pages/tag.tsx +4 -4
- package/dist/templates/blog/tokens.css +26 -40
- package/dist/templates/corporate/README.ja.md +18 -0
- package/dist/templates/corporate/README.md +12 -14
- package/dist/templates/corporate/pages/feed.ts +2 -3
- package/dist/templates/corporate/pages/home.tsx +5 -8
- package/dist/templates/corporate/pages/post.tsx +7 -7
- package/dist/templates/corporate/pages/sitemap.ts +2 -3
- package/dist/templates/corporate/pages/tag.tsx +4 -4
- package/dist/templates/corporate/tokens.css +17 -39
- package/dist/templates/dads/README.ja.md +31 -0
- package/dist/templates/dads/README.md +13 -17
- package/dist/templates/dads/pages/feed.ts +2 -3
- package/dist/templates/dads/pages/home.tsx +5 -8
- package/dist/templates/dads/pages/post.tsx +7 -7
- package/dist/templates/dads/pages/sitemap.ts +2 -3
- package/dist/templates/dads/pages/tag.tsx +4 -4
- package/dist/templates/dads/tokens.css +22 -42
- package/dist/templates/docs/README.ja.md +24 -0
- package/dist/templates/docs/README.md +10 -13
- package/dist/templates/docs/pages/feed.ts +2 -3
- package/dist/templates/docs/pages/home.tsx +5 -6
- package/dist/templates/docs/pages/post.tsx +8 -8
- package/dist/templates/docs/pages/sitemap.ts +2 -3
- package/dist/templates/docs/pages/tag.tsx +5 -5
- package/dist/templates/docs/tokens.css +17 -39
- package/dist/templates/landing/README.ja.md +20 -0
- package/dist/templates/landing/README.md +14 -19
- package/dist/templates/landing/pages/feed.ts +3 -4
- package/dist/templates/landing/pages/home.tsx +5 -8
- package/dist/templates/landing/pages/post.tsx +7 -7
- package/dist/templates/landing/pages/sitemap.ts +2 -3
- package/dist/templates/landing/pages/tag.tsx +4 -4
- package/dist/templates/landing/tokens.css +17 -39
- package/dist/templates/minimal/README.ja.md +14 -0
- package/dist/templates/minimal/README.md +9 -47
- package/dist/templates/minimal/pages/feed.ts +3 -4
- package/dist/templates/minimal/pages/home.tsx +3 -4
- package/dist/templates/minimal/pages/post.tsx +6 -6
- package/dist/templates/minimal/pages/sitemap.ts +3 -4
- package/dist/templates/minimal/pages/tag.tsx +3 -3
- package/dist/templates/minimal/tokens.css +17 -39
- package/package.json +1 -1
- package/dist/templates/_shared/app/(admin)/admin/sites/page.tsx +0 -5
- package/dist/templates/_shared/app/site/[siteId]/raw/[slug]/route.ts +0 -5
- package/dist/templates/_shared/lib/admin-site-client.ts +0 -10
- package/dist/templates/_shared/lib/admin-site.ts +0 -12
package/README.ja.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
> English: [README.md](./README.md)
|
|
2
|
+
>
|
|
3
|
+
|
|
4
|
+
# create-ampless
|
|
5
|
+
|
|
6
|
+
[ampless](https://github.com/heavymoons/ampless) プロジェクト向け CLI スキャフォールディングツール。
|
|
7
|
+
|
|
8
|
+
> **プレリリース / アルファ版。** v1.0 まではマイナーバージョンでも破壊的変更が入る可能性があります。`@alpha` タグを使用してください(`@latest` タグは v1.0 まで存在しません)。
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npx create-ampless@alpha
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
ウィザードが以下を順に案内します:
|
|
15
|
+
|
|
16
|
+
1. プロジェクト名
|
|
17
|
+
2. サイト名(デフォルトの `<title>` および OGP `siteName` として使用)
|
|
18
|
+
3. テーマ — v0.1 では `blog`
|
|
19
|
+
4. プラグイン — `seo`、`rss`、`webhook`
|
|
20
|
+
|
|
21
|
+
出力は AWS Amplify Gen 2 バックエンド定義を含む Next.js 16(App Router)プロジェクトです。`/admin` に管理パネル、`/` にパブリックブログが設置され、選択したプラグインが `cms.config.ts` にあらかじめ設定されます。また運用メモ用の `RUNBOOK.md` も生成されます。
|
|
22
|
+
|
|
23
|
+
## 生成されたプロジェクトでの次のステップ
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
cd my-project
|
|
27
|
+
npm install
|
|
28
|
+
npx ampx sandbox # AWS 開発リソースをプロビジョニングし amplify_outputs.json を生成
|
|
29
|
+
npm run dev # http://localhost:3000
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
`/login` でサインアップすると、最初に登録したユーザーが自動的に `ampless-admin` Cognito グループに昇格します。
|
|
33
|
+
|
|
34
|
+
## ワンショットデプロイ:`--deploy`
|
|
35
|
+
|
|
36
|
+
ウィザードは `npx` から Amplify Hosting の URL まで一気に進めることもできます:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npx create-ampless@alpha my-site --deploy
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
このフラグを追加すると、スキャフォールディング後に以下が実行されます:
|
|
43
|
+
|
|
44
|
+
1. `git init` + 初回コミット
|
|
45
|
+
2. GitHub リポジトリを作成(`gh repo create`)してプッシュ
|
|
46
|
+
3. 新しいリポジトリに紐付けた `aws amplify create-app`
|
|
47
|
+
4. `aws amplify create-branch main`
|
|
48
|
+
5. `aws amplify start-job --job-type RELEASE`
|
|
49
|
+
6. `--domain` を指定した場合はオプションで `aws amplify create-domain-association`
|
|
50
|
+
|
|
51
|
+
コマンドラインに指定が不足している場合はインタラクティブに確認します。CI 向けのフル指定の例:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npx create-ampless@alpha my-site --deploy \
|
|
55
|
+
--github-owner my-org \
|
|
56
|
+
--github-private \
|
|
57
|
+
--aws-region us-east-1 \
|
|
58
|
+
--domain example.com --subdomain blog \
|
|
59
|
+
--skip-confirm
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
apex ドメインが同じ AWS アカウントの Route 53 でホストされている場合、ACM の検証が完了すると Amplify が DNS レコードを自動作成します。それ以外の場合、CLI がレジストラで追加すべき正確な CNAME を表示します。
|
|
63
|
+
|
|
64
|
+
### デプロイの要件
|
|
65
|
+
|
|
66
|
+
- [`gh`](https://cli.github.com/) のインストールと認証(`gh auth login`)
|
|
67
|
+
- [`aws`](https://aws.amazon.com/cli/) のインストールと設定(`aws configure`)
|
|
68
|
+
- `repo` スコープを持つ GitHub トークン(`--github-token` → `GITHUB_TOKEN` 環境変数 → `gh auth token` → インタラクティブプロンプトの順で解決)
|
|
69
|
+
|
|
70
|
+
## 動作要件
|
|
71
|
+
|
|
72
|
+
- Node.js >= 20
|
|
73
|
+
- AWS アカウントと `aws configure` の設定済み(サンドボックス / パイプラインデプロイは AWS に直接アクセスします)
|
|
74
|
+
|
|
75
|
+
## ライセンス
|
|
76
|
+
|
|
77
|
+
[MIT](../../LICENSE)
|
package/README.md
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
> 日本語版: [README.ja.md](./README.ja.md)
|
|
2
|
+
>
|
|
3
|
+
|
|
1
4
|
# create-ampless
|
|
2
5
|
|
|
3
6
|
CLI scaffolding tool for [ampless](https://github.com/heavymoons/ampless) projects.
|
|
@@ -15,7 +18,7 @@ The wizard walks you through:
|
|
|
15
18
|
3. Theme — `blog` for v0.1
|
|
16
19
|
4. Plugins — `seo`, `rss`, `webhook`
|
|
17
20
|
|
|
18
|
-
Output is a Next.js
|
|
21
|
+
Output is a Next.js 16 (App Router) project with the AWS Amplify Gen 2 backend definitions, an admin panel at `/admin`, public blog at `/`, the chosen plugins pre-wired in `cms.config.ts`, and a `RUNBOOK.md` for operations notes.
|
|
19
22
|
|
|
20
23
|
## Next steps inside the generated project
|
|
21
24
|
|
package/dist/index.js
CHANGED
|
@@ -435,9 +435,9 @@ function parseDeployArgs(argv) {
|
|
|
435
435
|
var HELP_TEXT = `create-ampless \u2014 scaffold an ampless project
|
|
436
436
|
|
|
437
437
|
Usage:
|
|
438
|
-
npx create-ampless@
|
|
439
|
-
npx create-ampless@
|
|
440
|
-
npx create-ampless@
|
|
438
|
+
npx create-ampless@latest <project-name> [options]
|
|
439
|
+
npx create-ampless@latest --mount [options] # in an existing project dir
|
|
440
|
+
npx create-ampless@latest upgrade [options] # in an existing project dir
|
|
441
441
|
|
|
442
442
|
Options:
|
|
443
443
|
--site-name <name> Site display name (default: "My Blog")
|
|
@@ -487,7 +487,7 @@ copy-theme <source> <target>
|
|
|
487
487
|
(the convention that flags it as user-owned, so upgrade leaves
|
|
488
488
|
it alone). Run inside an existing ampless project.
|
|
489
489
|
|
|
490
|
-
Example: npx create-ampless@
|
|
490
|
+
Example: npx create-ampless@latest copy-theme blog my-blog
|
|
491
491
|
`;
|
|
492
492
|
|
|
493
493
|
// src/deploy-prompts.ts
|
|
@@ -1091,38 +1091,19 @@ function splitDomain(domain, subdomain) {
|
|
|
1091
1091
|
async function rewriteCmsConfigForDomain(projectDir, fullDomain) {
|
|
1092
1092
|
const path = resolve4(projectDir, "cms.config.ts");
|
|
1093
1093
|
if (!existsSync4(path)) {
|
|
1094
|
-
return { urlRewritten: false
|
|
1094
|
+
return { urlRewritten: false };
|
|
1095
1095
|
}
|
|
1096
1096
|
let content = await readFile2(path, "utf-8");
|
|
1097
1097
|
let urlRewritten = false;
|
|
1098
|
-
let sitesInjected = false;
|
|
1099
1098
|
const urlRe = /url:\s*['"]http:\/\/localhost:3000['"]/;
|
|
1100
1099
|
if (urlRe.test(content)) {
|
|
1101
1100
|
content = content.replace(urlRe, `url: 'https://${fullDomain}'`);
|
|
1102
1101
|
urlRewritten = true;
|
|
1103
1102
|
}
|
|
1104
|
-
|
|
1105
|
-
if (!sitesActiveRe.test(content)) {
|
|
1106
|
-
const siteCloseRe = /(\n(\s*)site:\s*\{[\s\S]*?\n\2\},)/;
|
|
1107
|
-
const m = siteCloseRe.exec(content);
|
|
1108
|
-
if (m) {
|
|
1109
|
-
const indent = m[2] ?? " ";
|
|
1110
|
-
const inner = indent + " ";
|
|
1111
|
-
const innermost = inner + " ";
|
|
1112
|
-
const inject = `
|
|
1113
|
-
${indent}sites: {
|
|
1114
|
-
${inner}default: {
|
|
1115
|
-
${innermost}domains: ['${fullDomain}'],
|
|
1116
|
-
${inner}},
|
|
1117
|
-
${indent}},`;
|
|
1118
|
-
content = content.replace(siteCloseRe, `$1${inject}`);
|
|
1119
|
-
sitesInjected = true;
|
|
1120
|
-
}
|
|
1121
|
-
}
|
|
1122
|
-
if (urlRewritten || sitesInjected) {
|
|
1103
|
+
if (urlRewritten) {
|
|
1123
1104
|
await writeFile2(path, content, "utf-8");
|
|
1124
1105
|
}
|
|
1125
|
-
return { urlRewritten
|
|
1106
|
+
return { urlRewritten };
|
|
1126
1107
|
}
|
|
1127
1108
|
async function run(cmd, args, opts = {}) {
|
|
1128
1109
|
const { step, ...execaOpts } = opts;
|
|
@@ -1503,11 +1484,8 @@ async function runDeploy(opts) {
|
|
|
1503
1484
|
const { registrable, subdomain } = splitDomain(opts.domain, opts.subdomain);
|
|
1504
1485
|
const fullDomain = subdomain ? `${subdomain}.${registrable}` : registrable;
|
|
1505
1486
|
const mutations = await rewriteCmsConfigForDomain(opts.projectDir, fullDomain);
|
|
1506
|
-
if (mutations.urlRewritten
|
|
1507
|
-
|
|
1508
|
-
if (mutations.urlRewritten) parts.push(`site.url \u2192 https://${fullDomain}`);
|
|
1509
|
-
if (mutations.sitesInjected) parts.push(`sites.default.domains += ${fullDomain}`);
|
|
1510
|
-
log.info(`cms.config.ts updated: ${parts.join(", ")}`);
|
|
1487
|
+
if (mutations.urlRewritten) {
|
|
1488
|
+
log.info(`cms.config.ts updated: site.url \u2192 https://${fullDomain}`);
|
|
1511
1489
|
}
|
|
1512
1490
|
}
|
|
1513
1491
|
const created = {};
|
|
@@ -1761,6 +1739,14 @@ import { join as join3, relative, extname as extname2, dirname } from "path";
|
|
|
1761
1739
|
import { log as log3, outro } from "@clack/prompts";
|
|
1762
1740
|
import pc3 from "picocolors";
|
|
1763
1741
|
import { execa as execa4 } from "execa";
|
|
1742
|
+
var AMPLESS_MANAGED_APP_PATHS = [
|
|
1743
|
+
"app/(admin)/admin",
|
|
1744
|
+
"app/api/admin",
|
|
1745
|
+
"app/api/media",
|
|
1746
|
+
"app/api/mcp",
|
|
1747
|
+
"app/login",
|
|
1748
|
+
"app/site"
|
|
1749
|
+
];
|
|
1764
1750
|
var AMPLESS_PACKAGES = /* @__PURE__ */ new Set([
|
|
1765
1751
|
"ampless",
|
|
1766
1752
|
"@ampless/admin",
|
|
@@ -1882,6 +1868,63 @@ async function walkDir(dir, base, out) {
|
|
|
1882
1868
|
}
|
|
1883
1869
|
}
|
|
1884
1870
|
}
|
|
1871
|
+
async function listFilesRecursive(root) {
|
|
1872
|
+
const out = [];
|
|
1873
|
+
async function walk(current, relPrefix) {
|
|
1874
|
+
const entries = await readdir3(current, { withFileTypes: true });
|
|
1875
|
+
for (const entry of entries) {
|
|
1876
|
+
const rel = relPrefix ? `${relPrefix}/${entry.name}` : entry.name;
|
|
1877
|
+
if (entry.isDirectory()) {
|
|
1878
|
+
await walk(join3(current, entry.name), rel);
|
|
1879
|
+
} else if (entry.isFile()) {
|
|
1880
|
+
out.push(rel);
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
if (!existsSync5(root)) return out;
|
|
1885
|
+
await walk(root, "");
|
|
1886
|
+
return out;
|
|
1887
|
+
}
|
|
1888
|
+
async function pruneEmptyDirs(root) {
|
|
1889
|
+
if (!existsSync5(root)) return;
|
|
1890
|
+
const entries = await readdir3(root, { withFileTypes: true });
|
|
1891
|
+
for (const entry of entries) {
|
|
1892
|
+
if (entry.isDirectory()) {
|
|
1893
|
+
await pruneEmptyDirs(join3(root, entry.name));
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
const remaining = await readdir3(root);
|
|
1897
|
+
if (remaining.length === 0) {
|
|
1898
|
+
await rm2(root, { recursive: true, force: true });
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
async function findObsoleteAppFiles(destDir, sharedDir) {
|
|
1902
|
+
const obsolete = [];
|
|
1903
|
+
for (const managedPath of AMPLESS_MANAGED_APP_PATHS) {
|
|
1904
|
+
const userPath = join3(destDir, managedPath);
|
|
1905
|
+
if (!existsSync5(userPath)) continue;
|
|
1906
|
+
const templatePath2 = join3(sharedDir, managedPath);
|
|
1907
|
+
const templateFiles = new Set(await listFilesRecursive(templatePath2));
|
|
1908
|
+
const userFiles = await listFilesRecursive(userPath);
|
|
1909
|
+
for (const f of userFiles) {
|
|
1910
|
+
if (!templateFiles.has(f)) {
|
|
1911
|
+
obsolete.push(`${managedPath}/${f}`);
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
return obsolete;
|
|
1916
|
+
}
|
|
1917
|
+
async function removeObsoleteAppFiles(destDir, paths) {
|
|
1918
|
+
for (const rel of paths) {
|
|
1919
|
+
const abs = join3(destDir, rel);
|
|
1920
|
+
if (existsSync5(abs)) {
|
|
1921
|
+
await rm2(abs, { force: true });
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
for (const managedPath of AMPLESS_MANAGED_APP_PATHS) {
|
|
1925
|
+
await pruneEmptyDirs(join3(destDir, managedPath));
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1885
1928
|
function substituteVars(content, vars) {
|
|
1886
1929
|
return content.replace(/\{\{(\w+)\}\}/g, (_, key) => vars[key] ?? `{{${key}}}`);
|
|
1887
1930
|
}
|
|
@@ -1935,21 +1978,25 @@ async function runUpgradeIn(destDir, sharedDir, opts = {}) {
|
|
|
1935
1978
|
const shippedThemes = themeSyncEnabled ? await listShippedThemes(templatesRoot) : [];
|
|
1936
1979
|
const existingThemes = themeSyncEnabled ? await discoverInstalledThemes(destDir) : [];
|
|
1937
1980
|
const preservedThemes = existingThemes.filter((t) => !shippedThemes.includes(t));
|
|
1981
|
+
const obsoleteFiles = await findObsoleteAppFiles(destDir, sharedDir);
|
|
1938
1982
|
log3.info(
|
|
1939
|
-
`replace: ${pc3.green(
|
|
1983
|
+
`replace: ${pc3.green(`${replaceNew.length} added`)} / ${pc3.yellow(`${replaceUpdate.length} updated`)}`
|
|
1940
1984
|
);
|
|
1941
|
-
log3.info(`merge: ${pc3.cyan("package.json: ampless deps
|
|
1985
|
+
log3.info(`merge: ${pc3.cyan("package.json: sync ampless deps and managed scripts with the template")}`);
|
|
1942
1986
|
if (classification.seed.length > 0) {
|
|
1943
1987
|
log3.info(
|
|
1944
|
-
`seed: ${pc3.green(
|
|
1988
|
+
`seed: ${pc3.green(`${seedNew.length} added`)} / ${pc3.dim(`${seedSkipped.length} kept (existing *.custom.ts left untouched)`)}`
|
|
1945
1989
|
);
|
|
1946
1990
|
}
|
|
1947
1991
|
if (themeSyncEnabled) {
|
|
1948
1992
|
log3.info(
|
|
1949
|
-
`themes: ${pc3.cyan(
|
|
1993
|
+
`themes: ${pc3.cyan(`${shippedThemes.length} default themes synced`)} / ${pc3.dim(`${preservedThemes.length} custom (my-*) themes preserved`)}`
|
|
1950
1994
|
);
|
|
1951
1995
|
}
|
|
1952
|
-
|
|
1996
|
+
if (obsoleteFiles.length > 0) {
|
|
1997
|
+
log3.info(`cleanup: ${pc3.yellow(`${obsoleteFiles.length} removed`)} (files under ampless-managed app/ paths that no longer exist in the template)`);
|
|
1998
|
+
}
|
|
1999
|
+
log3.info(`protected: ${pc3.dim(`${classification.protected.length} template files left untouched`)}`);
|
|
1953
2000
|
if (opts.dryRun) {
|
|
1954
2001
|
return {
|
|
1955
2002
|
added: replaceNew,
|
|
@@ -1958,7 +2005,8 @@ async function runUpgradeIn(destDir, sharedDir, opts = {}) {
|
|
|
1958
2005
|
protected: classification.protected,
|
|
1959
2006
|
themesSynced: shippedThemes,
|
|
1960
2007
|
themesPreserved: preservedThemes,
|
|
1961
|
-
packageJsonMerged: false
|
|
2008
|
+
packageJsonMerged: false,
|
|
2009
|
+
obsoleteRemoved: obsoleteFiles
|
|
1962
2010
|
};
|
|
1963
2011
|
}
|
|
1964
2012
|
for (const rel of classification.replace) {
|
|
@@ -1972,6 +2020,7 @@ async function runUpgradeIn(destDir, sharedDir, opts = {}) {
|
|
|
1972
2020
|
await copyWithSubstitution(src, dst, vars);
|
|
1973
2021
|
}
|
|
1974
2022
|
const themeResult = themeSyncEnabled ? await syncThemes(destDir, templatesRoot) : { synced: [], preserved: [] };
|
|
2023
|
+
await removeObsoleteAppFiles(destDir, obsoleteFiles);
|
|
1975
2024
|
const templatePkgRaw = await readFile3(join3(sharedDir, "package.json"), "utf-8");
|
|
1976
2025
|
const templatePkg = JSON.parse(templatePkgRaw);
|
|
1977
2026
|
const indent = detectIndent(projectPkgRaw);
|
|
@@ -1988,9 +2037,12 @@ async function runUpgradeIn(destDir, sharedDir, opts = {}) {
|
|
|
1988
2037
|
}
|
|
1989
2038
|
const templateScripts = templatePkg["scripts"] ?? {};
|
|
1990
2039
|
const projectScripts = projectPkg["scripts"] ?? {};
|
|
1991
|
-
for (const
|
|
1992
|
-
if (
|
|
1993
|
-
|
|
2040
|
+
for (const name of AMPLESS_MANAGED_SCRIPTS) {
|
|
2041
|
+
if (name in templateScripts) {
|
|
2042
|
+
projectScripts[name] = templateScripts[name];
|
|
2043
|
+
} else {
|
|
2044
|
+
delete projectScripts[name];
|
|
2045
|
+
}
|
|
1994
2046
|
}
|
|
1995
2047
|
if (Object.keys(projectScripts).length > 0) {
|
|
1996
2048
|
projectPkg["scripts"] = projectScripts;
|
|
@@ -2008,7 +2060,8 @@ async function runUpgradeIn(destDir, sharedDir, opts = {}) {
|
|
|
2008
2060
|
protected: classification.protected,
|
|
2009
2061
|
themesSynced: themeResult.synced,
|
|
2010
2062
|
themesPreserved: themeResult.preserved,
|
|
2011
|
-
packageJsonMerged: true
|
|
2063
|
+
packageJsonMerged: true,
|
|
2064
|
+
obsoleteRemoved: obsoleteFiles
|
|
2012
2065
|
};
|
|
2013
2066
|
}
|
|
2014
2067
|
async function runUpgrade(args) {
|
|
@@ -2104,8 +2157,8 @@ async function runCopyTheme(args) {
|
|
|
2104
2157
|
const source = args.copyThemeSource;
|
|
2105
2158
|
const target = args.copyThemeTarget;
|
|
2106
2159
|
if (!source || !target) {
|
|
2107
|
-
log4.error("Usage: npx create-ampless@
|
|
2108
|
-
log4.info("Example: npx create-ampless@
|
|
2160
|
+
log4.error("Usage: npx create-ampless@latest copy-theme <source> <target>");
|
|
2161
|
+
log4.info("Example: npx create-ampless@latest copy-theme blog my-blog");
|
|
2109
2162
|
process.exit(1);
|
|
2110
2163
|
}
|
|
2111
2164
|
try {
|
|
@@ -2118,7 +2171,7 @@ async function runCopyTheme(args) {
|
|
|
2118
2171
|
|
|
2119
2172
|
themes-registry.ts updated. Next:
|
|
2120
2173
|
${pc4.cyan(`Open themes/${result.target}/ and start customising`)}
|
|
2121
|
-
${pc4.cyan(`Activate via /admin/sites
|
|
2174
|
+
${pc4.cyan(`Activate via /admin/sites/default/theme`)}`
|
|
2122
2175
|
);
|
|
2123
2176
|
} catch (err) {
|
|
2124
2177
|
log4.error(err instanceof Error ? err.message : String(err));
|
|
@@ -2163,7 +2216,7 @@ async function runMount(args) {
|
|
|
2163
2216
|
if (problem) {
|
|
2164
2217
|
log5.error(problem);
|
|
2165
2218
|
log5.info(
|
|
2166
|
-
"Run `npx create-ampless@
|
|
2219
|
+
"Run `npx create-ampless@latest <name>` first to scaffold, or `cd` into a scaffolded project before passing --mount."
|
|
2167
2220
|
);
|
|
2168
2221
|
process.exit(1);
|
|
2169
2222
|
}
|
|
@@ -2269,8 +2322,7 @@ async function main() {
|
|
|
2269
2322
|
Next steps:
|
|
2270
2323
|
${pc5.cyan("cd")} ${opts.projectName}
|
|
2271
2324
|
${pc5.cyan("npm install")}
|
|
2272
|
-
${pc5.cyan("
|
|
2273
|
-
${pc5.cyan("npm run dev")} ${pc5.dim("# start Next.js")}`
|
|
2325
|
+
${pc5.cyan("npm run sandbox")} ${pc5.dim("# deploy sandbox + start Next.js")}`
|
|
2274
2326
|
);
|
|
2275
2327
|
}
|
|
2276
2328
|
main().catch((err) => {
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
> English: [AGENTS.md](./AGENTS.md)
|
|
2
|
+
>
|
|
3
|
+
# AGENTS.md
|
|
4
|
+
|
|
5
|
+
このファイルは AI コーディングエージェント(Claude Code, Cursor, Codex など)が読む前提で書かれています。人間向けの日常利用ガイドは `README.ja.md` と `RUNBOOK.ja.md` を参照してください。
|
|
6
|
+
|
|
7
|
+
## プロジェクトのレイアウト
|
|
8
|
+
|
|
9
|
+
- `themes/<name>/` — インストール済みのテーマ(tokens、pages、manifest)。アクティブテーマの切り替えは `/admin/sites/<id>/theme` で行う。
|
|
10
|
+
- `themes/my-<name>/` — ユーザーが所有するカスタマイズ済みのテーマコピー(`npm run copy-theme` で作成)。`my-` プレフィックスがついているコピーは `update-ampless` で上書きされない。
|
|
11
|
+
- `themes-registry.ts` — 自動生成ファイル。手動で編集しない。
|
|
12
|
+
- `amplify/` — TypeScript で定義された Amplify Gen 2 バックエンド(Cognito / DynamoDB / S3 / AppSync / Lambda)。
|
|
13
|
+
- `amplify_outputs.json` — `npm run sandbox` / Amplify Hosting が生成するファイル。編集しない。
|
|
14
|
+
- `app/` — Next.js 15 App Router(公開サイト + `/admin` UI)。
|
|
15
|
+
- `components/` — 共有 UI コンポーネント。
|
|
16
|
+
- `lib/` — 共有ユーティリティ(データアクセス、認証など)。
|
|
17
|
+
- `cms.config.ts` — サイト、プラグイン、デフォルト設定。
|
|
18
|
+
- `proxy.ts` — リクエストプロキシの設定。
|
|
19
|
+
|
|
20
|
+
## 触っていい場所・ダメな場所
|
|
21
|
+
|
|
22
|
+
**自由に編集可能:**
|
|
23
|
+
- `themes/my-*/` — ユーザー自身のテーマコピー。
|
|
24
|
+
- `cms.config.ts` — サイト / プラグインの設定。
|
|
25
|
+
- 投稿コンテンツは管理 UI(推奨)または MCP サーバー経由で操作する。
|
|
26
|
+
|
|
27
|
+
**注意して触る(編集前に理由を説明すること):**
|
|
28
|
+
- `app/`、`components/`、`lib/` — 共有シェルの一部。`update-ampless` 後も編集は残るが、上流が同じファイルを変更した場合のマージはユーザー側の責任になる。テーマ関連の変更であれば、カスタムテーマ経由での拡張を優先する。
|
|
29
|
+
- `themes/<official-name>/`(`my-` プレフィックスなし)— 公式テーマは `update-ampless` で上書きされる。カスタマイズしたい場合は先に `npm run copy-theme <official-name> my-<your-name>` を実行し、コピーを編集する。
|
|
30
|
+
- `package.json` — `ampless` / `@ampless/*` のバージョンは一貫して管理する。バージョンアップには `update-ampless` を使う。
|
|
31
|
+
|
|
32
|
+
**編集禁止:**
|
|
33
|
+
- `themes-registry.ts` — scaffold / copy-theme / update コマンドが再生成する。
|
|
34
|
+
- `amplify/` — バックエンドのスキーマ変更はテーブルの再構築とサンドボックスデータの消失を招く可能性がある。触る前にユーザーの明示的な確認を得ること。
|
|
35
|
+
- `amplify_outputs.json` — `npm run sandbox` のたびに再生成される。
|
|
36
|
+
- `.amplify/` — Amplify CLI の作業ディレクトリ。
|
|
37
|
+
- `pnpm-lock.yaml` / `package-lock.json` — パッケージマネージャーに任せる。
|
|
38
|
+
|
|
39
|
+
## テーマのカスタマイズ(標準的な手順)
|
|
40
|
+
|
|
41
|
+
1. 公式テーマをコピーする:
|
|
42
|
+
```bash
|
|
43
|
+
npm run copy-theme blog my-blog
|
|
44
|
+
```
|
|
45
|
+
`my-` プレフィックスがついているコピーは `npm run update-ampless` で上書きされない。ターゲット名が `my-` で始まらない場合、コマンドは実行を拒否する。
|
|
46
|
+
|
|
47
|
+
2. 以下の優先順位で編集する:
|
|
48
|
+
- `themes/my-blog/tokens.css` — 色、タイポグラフィ、スペーシング(`[data-theme='my-blog']` スコープの CSS カスタムプロパティ)。
|
|
49
|
+
- `themes/my-blog/manifest.ts` — 管理 UI(`/admin/sites/<id>/theme`)に公開するフィールドの宣言。
|
|
50
|
+
- `themes/my-blog/pages/` — レイアウト / 構造(Next.js ページモジュール: フィード、サイトマップ、投稿テンプレート)。
|
|
51
|
+
|
|
52
|
+
3. テーマを有効化する: `/admin/sites/<siteId>/theme` を開き、`my-blog` を選択する。テーマ切り替えはランタイム設定 — 再デプロイは不要。
|
|
53
|
+
|
|
54
|
+
4. 動作確認:
|
|
55
|
+
```bash
|
|
56
|
+
npm run dev # http://localhost:3000
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## MCP サーバー(`@ampless/mcp-server`)
|
|
60
|
+
|
|
61
|
+
エージェントが投稿コンテンツを直接クエリ・編集できる。公開ツール: `list_posts`、`get_post`、`create_post`、`update_post`、`delete_post`、`upload_media`、`get_schema`。
|
|
62
|
+
|
|
63
|
+
登録方法:
|
|
64
|
+
|
|
65
|
+
プロジェクトルートの `.mcp.json` に追加する:
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"mcpServers": {
|
|
69
|
+
"ampless": {
|
|
70
|
+
"command": "npx",
|
|
71
|
+
"args": ["-y", "@ampless/mcp-server", "--outputs", "./amplify_outputs.json"],
|
|
72
|
+
"env": {
|
|
73
|
+
"AMPLESS_MCP_EMAIL": "<your-admin-email>",
|
|
74
|
+
"AMPLESS_MCP_PASSWORD": "<your-admin-password>"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
投稿本文は `markdown`、`html`、`tiptap`(JSON ドキュメント)の 3 フォーマットに対応している。
|
|
82
|
+
|
|
83
|
+
## 動作確認の基準
|
|
84
|
+
|
|
85
|
+
変更後は最低限以下を実行すること:
|
|
86
|
+
|
|
87
|
+
- `npm run dev` でブラウザから該当ページを開く。UI / テーマの変更は特に目視確認が必要(Playwright MCP が使える場合はスクリーンショットを活用する)。
|
|
88
|
+
- `npm run build` — 本番ビルドが成功することを確認する。
|
|
89
|
+
- `npm run lint`。
|
|
90
|
+
|
|
91
|
+
UI / テーマを変更した場合、型チェックだけを根拠にタスク完了を報告しない — ブラウザで実際に確認すること。
|
|
92
|
+
|
|
93
|
+
## 既知の制約
|
|
94
|
+
|
|
95
|
+
- **サンドボックスのデータは揮発性。** スキーマに影響する変更は API とテーブルの再構築を引き起こす可能性がある。サンドボックスのコンテンツは使い捨てとして扱うこと。本番データは永続する。
|
|
96
|
+
- **1 Amplify デプロイ = 1 サイト。** 複数サイトを別ドメインで配信したい場合は、サイトごとに Amplify 環境を分けてデプロイする。スキーマの `siteId` カラムは常に `"default"` 固定(前方互換のため残してある)。
|
|
97
|
+
- **AppSync 公開 API キーは `amplify_outputs.json` に含まれ、サイト訪問者から見える。** 低信頼の認証情報として扱うこと — このキーの権限は公開済み投稿の読み取りのみ。`api-key-renewer` Lambda が毎月自動ローテーションするため、手動ローテーションは不要。
|
|
98
|
+
- **最初に登録したユーザーが管理者になる。** 以降のロール変更は Cognito コンソールで行う(RUNBOOK 参照)。
|
|
99
|
+
|
|
100
|
+
## 参照先
|
|
101
|
+
|
|
102
|
+
- `README.ja.md` — サイト運営者向けの日常利用ガイド。
|
|
103
|
+
- `RUNBOOK.ja.md` — 定期的な運用手順(キーローテーション、バックアップ復元など)。
|
|
104
|
+
- `themes/<name>/README.ja.md` — テーマごとのカスタマイズ詳細。
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
> 日本語版: [AGENTS.ja.md](./AGENTS.ja.md)
|
|
2
|
+
>
|
|
3
|
+
# AGENTS.md
|
|
4
|
+
|
|
5
|
+
This file is written for AI coding agents (Claude Code, Cursor, Codex, etc.). For human-readable day-to-day guidance, see `README.md` and `RUNBOOK.md`.
|
|
6
|
+
|
|
7
|
+
## Project layout
|
|
8
|
+
|
|
9
|
+
- `themes/<name>/` — installed themes (tokens, pages, manifest). Switch active theme at `/admin/sites/<id>/theme`.
|
|
10
|
+
- `themes/my-<name>/` — user-owned customised theme copies (created via `npm run copy-theme`). The `my-` prefix is what marks the copy as user-owned; `update-ampless` leaves anything under `themes/my-*/` alone.
|
|
11
|
+
- `themes-registry.ts` — auto-generated, do not hand-edit.
|
|
12
|
+
- `amplify/` — Amplify Gen 2 backend (Cognito / DynamoDB / S3 / AppSync / Lambda) defined in TypeScript.
|
|
13
|
+
- `amplify_outputs.json` — generated by `npm run sandbox` / Amplify Hosting. Do not edit.
|
|
14
|
+
- `app/` — Next.js 15 App Router (public site + `/admin` UI).
|
|
15
|
+
- `components/` — shared UI components.
|
|
16
|
+
- `lib/` — shared utilities (data access, auth, etc.).
|
|
17
|
+
- `cms.config.ts` — site, plugins, defaults.
|
|
18
|
+
- `proxy.ts` — request proxy config.
|
|
19
|
+
|
|
20
|
+
## What you can and can't touch
|
|
21
|
+
|
|
22
|
+
**Free to edit:**
|
|
23
|
+
- `themes/my-*/` — your own theme copies.
|
|
24
|
+
- `cms.config.ts` — site/plugin config.
|
|
25
|
+
- Post content via the admin UI (recommended) or via the MCP server.
|
|
26
|
+
|
|
27
|
+
**Touch with caution (explain why before editing):**
|
|
28
|
+
- `app/`, `components/`, `lib/` — these are part of the shared shell. Edits survive `update-ampless` but you own the merge if upstream changes the same file. Prefer extending via a custom theme if the change is theme-related.
|
|
29
|
+
- `themes/<official-name>/` (no `my-` prefix) — official themes get overwritten by `update-ampless`. If you want to customise, run `npm run copy-theme <official-name> my-<your-name>` first and edit the copy.
|
|
30
|
+
- `package.json` — keep `ampless` / `@ampless/*` versions consistent. Use `update-ampless` to bump them.
|
|
31
|
+
|
|
32
|
+
**Do not edit:**
|
|
33
|
+
- `themes-registry.ts` — regenerated by the scaffold/copy-theme/update commands.
|
|
34
|
+
- `amplify/` — backend schema changes can rebuild tables and wipe sandbox data. Get explicit confirmation from the user before touching.
|
|
35
|
+
- `amplify_outputs.json` — regenerated each `npm run sandbox`.
|
|
36
|
+
- `.amplify/` — Amplify CLI working directory.
|
|
37
|
+
- `pnpm-lock.yaml` / `package-lock.json` — let the package manager update these.
|
|
38
|
+
|
|
39
|
+
## Customising a theme (the standard flow)
|
|
40
|
+
|
|
41
|
+
1. Copy an official theme:
|
|
42
|
+
```bash
|
|
43
|
+
npm run copy-theme blog my-blog
|
|
44
|
+
```
|
|
45
|
+
The `my-` prefix marks the copy as user-owned so `npm run update-ampless` leaves it alone. The target name must start with `my-` or the command refuses to run.
|
|
46
|
+
|
|
47
|
+
2. Edit in this priority order:
|
|
48
|
+
- `themes/my-blog/tokens.css` — colours, typography, spacing (CSS custom properties scoped under `[data-theme='my-blog']`).
|
|
49
|
+
- `themes/my-blog/manifest.ts` — declare which fields are exposed in the admin UI (`/admin/sites/<id>/theme`).
|
|
50
|
+
- `themes/my-blog/pages/` — layout / structure (Next.js page modules: feed, sitemap, post templates).
|
|
51
|
+
|
|
52
|
+
3. Activate the theme: open `/admin/sites/<siteId>/theme` and select `my-blog`. Theme switching is a runtime setting — no redeploy needed.
|
|
53
|
+
|
|
54
|
+
4. Verify:
|
|
55
|
+
```bash
|
|
56
|
+
npm run dev # http://localhost:3000
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## MCP server (`@ampless/mcp-server`)
|
|
60
|
+
|
|
61
|
+
Lets agents query and modify post content directly. Tools exposed: `list_posts`, `get_post`, `create_post`, `update_post`, `delete_post`, `upload_media`, `get_schema`.
|
|
62
|
+
|
|
63
|
+
Registration:
|
|
64
|
+
|
|
65
|
+
Add to `.mcp.json` at the project root:
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"mcpServers": {
|
|
69
|
+
"ampless": {
|
|
70
|
+
"command": "npx",
|
|
71
|
+
"args": ["-y", "@ampless/mcp-server", "--outputs", "./amplify_outputs.json"],
|
|
72
|
+
"env": {
|
|
73
|
+
"AMPLESS_MCP_EMAIL": "<your-admin-email>",
|
|
74
|
+
"AMPLESS_MCP_PASSWORD": "<your-admin-password>"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Post bodies accept three formats: `markdown`, `html`, or `tiptap` (JSON document).
|
|
82
|
+
|
|
83
|
+
## Verification expectations
|
|
84
|
+
|
|
85
|
+
After any change, run at minimum:
|
|
86
|
+
|
|
87
|
+
- `npm run dev` and load the affected page in a browser. UI changes especially require visual confirmation (use Playwright MCP for screenshots when available).
|
|
88
|
+
- `npm run build` — confirms the production build succeeds.
|
|
89
|
+
- `npm run lint`.
|
|
90
|
+
|
|
91
|
+
Do not report a task complete based solely on type checks if a UI/theme was modified — load it in a browser.
|
|
92
|
+
|
|
93
|
+
## Known constraints
|
|
94
|
+
|
|
95
|
+
- **Sandbox data is ephemeral.** Schema-affecting changes can rebuild the API and tables; treat sandbox content as throw-away. Production data is durable.
|
|
96
|
+
- **One Amplify deployment = one site.** To serve multiple sites on different domains, deploy separate Amplify environments. The schema's `siteId` column is always the literal `"default"` (retained for forward-compat).
|
|
97
|
+
- **The AppSync public API key is shipped in `amplify_outputs.json` and visible to any site visitor.** Treat it as a low-trust credential — its only privilege is reading published posts. Auto-rotated monthly by the `api-key-renewer` Lambda; no manual rotation needed.
|
|
98
|
+
- **The first registered user becomes admin.** Subsequent role changes go through the Cognito console (see RUNBOOK).
|
|
99
|
+
|
|
100
|
+
## See also
|
|
101
|
+
|
|
102
|
+
- `README.md` — day-to-day usage for site owners.
|
|
103
|
+
- `RUNBOOK.md` — occasional ops procedures (key rotation, backup restore, etc.).
|
|
104
|
+
- `themes/<name>/README.md` — per-theme customization details.
|