create-ampless 0.2.0-alpha.6 → 0.2.0-alpha.7

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
@@ -2,8 +2,8 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { spinner as spinner2, outro, log as log3 } from "@clack/prompts";
5
- import { existsSync as existsSync2 } from "fs";
6
- import { basename as basename3, resolve as resolve4 } from "path";
5
+ import { existsSync as existsSync4 } from "fs";
6
+ import { basename as basename3, resolve as resolve5 } from "path";
7
7
 
8
8
  // src/prompts.ts
9
9
  import * as p from "@clack/prompts";
@@ -208,6 +208,7 @@ var STRING_FLAGS = /* @__PURE__ */ new Set([
208
208
  ]);
209
209
  var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
210
210
  "--deploy",
211
+ "--mount",
211
212
  "--github-private",
212
213
  "--create-iam-role",
213
214
  "--skip-confirm",
@@ -217,6 +218,7 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
217
218
  function parseDeployArgs(argv) {
218
219
  const out = {
219
220
  deploy: false,
221
+ mount: false,
220
222
  githubPrivate: false,
221
223
  createIamRole: false,
222
224
  skipConfirm: false,
@@ -237,6 +239,9 @@ function parseDeployArgs(argv) {
237
239
  case "--deploy":
238
240
  out.deploy = true;
239
241
  break;
242
+ case "--mount":
243
+ out.mount = true;
244
+ break;
240
245
  case "--github-private":
241
246
  out.githubPrivate = true;
242
247
  break;
@@ -324,6 +329,7 @@ var HELP_TEXT = `create-ampless \u2014 scaffold an ampless project
324
329
 
325
330
  Usage:
326
331
  npx create-ampless@alpha <project-name> [options]
332
+ npx create-ampless@alpha --mount [options] # in an existing project dir
327
333
 
328
334
  Options:
329
335
  --site-name <name> Site display name (default: "My Blog")
@@ -335,6 +341,12 @@ Options:
335
341
  (default: seo)
336
342
  --deploy Also create GitHub repo + Amplify Hosting app and
337
343
  kick off the first deploy after scaffolding
344
+ --mount Skip scaffolding and mount the CURRENT directory
345
+ onto a new GitHub repo + Amplify Hosting app.
346
+ Use after you've scaffolded and tested locally
347
+ with 'npx ampx sandbox' and now want to publish.
348
+ Implies --deploy. Scaffold flags (--site-name,
349
+ --themes, --plugins) are ignored.
338
350
  --github-owner <login> GitHub owner (user or org). Defaults to the
339
351
  authenticated 'gh' user
340
352
  --github-private Create a private repo (default: public)
@@ -361,8 +373,9 @@ import { execa as execa3 } from "execa";
361
373
 
362
374
  // src/deploy.ts
363
375
  import { execa as execa2 } from "execa";
376
+ import { existsSync as existsSync3 } from "fs";
364
377
  import { writeFile as writeFile2 } from "fs/promises";
365
- import { basename as basename2, resolve as resolve3 } from "path";
378
+ import { basename as basename2, resolve as resolve4 } from "path";
366
379
  import { spinner, log } from "@clack/prompts";
367
380
  import pc2 from "picocolors";
368
381
 
@@ -787,7 +800,7 @@ async function runPreflight(opts, extra = {}) {
787
800
  if (hasGh) await checkGhAuth(problems);
788
801
  const hasAws = await checkAwsInstalled(problems);
789
802
  const identity = hasAws ? await checkAwsCreds(opts, problems) : void 0;
790
- if (hasGh && opts.githubOwner) {
803
+ if (hasGh && opts.githubOwner && !extra.mountMode) {
791
804
  await checkGithubRepoFree(opts, opts.githubOwner, problems);
792
805
  }
793
806
  if (identity) {
@@ -838,6 +851,55 @@ function printPreflightReport(problems) {
838
851
  `);
839
852
  }
840
853
 
854
+ // src/mount.ts
855
+ import { existsSync as existsSync2 } from "fs";
856
+ import { resolve as resolve3 } from "path";
857
+ function validateMountableProject(destDir) {
858
+ const required = ["package.json", "cms.config.ts"];
859
+ for (const f of required) {
860
+ if (!existsSync2(resolve3(destDir, f))) {
861
+ return `Not an ampless project (missing ${f} in ${destDir})`;
862
+ }
863
+ }
864
+ const amplifyFiles = ["amplify/backend.ts", "amplify/data/resource.ts"];
865
+ if (!amplifyFiles.some((f) => existsSync2(resolve3(destDir, f)))) {
866
+ return `Not an ampless project (missing amplify/ in ${destDir})`;
867
+ }
868
+ return null;
869
+ }
870
+ var MOUNT_DEFAULT_GITIGNORE = `# Dependencies
871
+ node_modules/
872
+
873
+ # Next.js
874
+ .next/
875
+ .amplify/
876
+
877
+ # Amplify outputs (regenerated on every deploy / sandbox)
878
+ amplify_outputs.json
879
+
880
+ # Env / OS noise
881
+ .env
882
+ .env.local
883
+ .env.*.local
884
+ .DS_Store
885
+
886
+ # Logs
887
+ npm-debug.log*
888
+ yarn-debug.log*
889
+ yarn-error.log*
890
+ `;
891
+ function originPointsAt(origin, owner, name) {
892
+ const candidates = [
893
+ `https://github.com/${owner}/${name}`,
894
+ `https://github.com/${owner}/${name}.git`,
895
+ `git@github.com:${owner}/${name}`,
896
+ `git@github.com:${owner}/${name}.git`,
897
+ `ssh://git@github.com/${owner}/${name}`,
898
+ `ssh://git@github.com/${owner}/${name}.git`
899
+ ];
900
+ return candidates.includes(origin);
901
+ }
902
+
841
903
  // src/deploy.ts
842
904
  var AMPLIFY_BUILD_SPEC = `version: 1
843
905
  frontend:
@@ -945,27 +1007,108 @@ function awsArgs(opts, extra) {
945
1007
  base.push("--output", "json");
946
1008
  return base;
947
1009
  }
948
- async function gitInitCommit(dir) {
949
- await run("git", ["init", "-b", "main"], { cwd: dir, step: "git init" });
950
- await run("git", ["add", "."], { cwd: dir, step: "git add" });
951
- await run(
952
- "git",
953
- ["commit", "-m", "Initial scaffold (create-ampless)"],
954
- { cwd: dir, step: "git commit" }
955
- );
1010
+ async function isGitRepo(dir) {
1011
+ const r = await execa2("git", ["rev-parse", "--git-dir"], { cwd: dir, reject: false });
1012
+ return r.exitCode === 0;
1013
+ }
1014
+ async function isDirty(dir) {
1015
+ const r = await execa2("git", ["status", "--porcelain"], { cwd: dir, reject: false });
1016
+ return (r.stdout?.toString() ?? "").trim().length > 0;
1017
+ }
1018
+ async function currentBranch(dir) {
1019
+ const r = await execa2("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
1020
+ cwd: dir,
1021
+ reject: false
1022
+ });
1023
+ if (r.exitCode !== 0) return null;
1024
+ const name = r.stdout?.toString().trim();
1025
+ return name && name !== "HEAD" ? name : null;
1026
+ }
1027
+ async function ensureGitignore(dir) {
1028
+ const target = resolve4(dir, ".gitignore");
1029
+ if (existsSync3(target)) return;
1030
+ await writeFile2(target, MOUNT_DEFAULT_GITIGNORE, "utf-8");
1031
+ }
1032
+ async function gitInitOrReuse(dir, mount) {
1033
+ const repo = await isGitRepo(dir);
1034
+ if (!repo) {
1035
+ await run("git", ["init", "-b", "main"], { cwd: dir, step: "git init" });
1036
+ }
1037
+ const dirty = await isDirty(dir);
1038
+ if (dirty) {
1039
+ await run("git", ["add", "."], { cwd: dir, step: "git add" });
1040
+ const message = mount && repo ? "Prepare for create-ampless --mount" : "Initial scaffold (create-ampless)";
1041
+ await run("git", ["commit", "-m", message], { cwd: dir, step: "git commit" });
1042
+ }
1043
+ if (mount) {
1044
+ const branch = await currentBranch(dir);
1045
+ if (branch && branch !== "main") {
1046
+ log.warn(
1047
+ `Current branch is "${branch}" \u2014 Amplify Hosting will be wired to the "main" branch. Push from "main" later, or rename your branch with \`git branch -m main\`.`
1048
+ );
1049
+ }
1050
+ }
956
1051
  }
957
1052
  function shortName2(opts) {
958
1053
  return basename2(opts.projectDir);
959
1054
  }
1055
+ async function ghRepoExists(name, token) {
1056
+ const r = await execa2("gh", ["repo", "view", name], {
1057
+ reject: false,
1058
+ env: { ...process.env, GH_TOKEN: token }
1059
+ });
1060
+ return r.exitCode === 0;
1061
+ }
1062
+ async function getOriginUrl(dir) {
1063
+ const r = await execa2("git", ["remote", "get-url", "origin"], {
1064
+ cwd: dir,
1065
+ reject: false
1066
+ });
1067
+ if (r.exitCode !== 0) return null;
1068
+ const url = r.stdout?.toString().trim();
1069
+ return url ? url : null;
1070
+ }
1071
+ function repoHttpsUrl(owner, name) {
1072
+ return `https://github.com/${owner}/${name}`;
1073
+ }
960
1074
  async function ghRepoCreate(opts) {
1075
+ const owner = opts.githubOwner;
1076
+ const name = shortName2(opts);
1077
+ const fullName = `${owner}/${name}`;
961
1078
  const visibility = opts.githubPrivate ? "--private" : "--public";
962
- const name = `${opts.githubOwner}/${shortName2(opts)}`;
1079
+ const exists = await ghRepoExists(fullName, opts.githubToken);
1080
+ if (exists) {
1081
+ const origin = await getOriginUrl(opts.projectDir);
1082
+ if (origin && !originPointsAt(origin, owner, name)) {
1083
+ throw new Error(
1084
+ `Existing 'origin' remote points at ${origin}, but --mount expects ${fullName}.
1085
+ Remove or rename the existing remote (\`git remote remove origin\`) and re-run.`
1086
+ );
1087
+ }
1088
+ if (!origin) {
1089
+ await run(
1090
+ "git",
1091
+ ["remote", "add", "origin", `https://github.com/${fullName}.git`],
1092
+ { cwd: opts.projectDir, step: "git remote add origin" }
1093
+ );
1094
+ }
1095
+ await run(
1096
+ "git",
1097
+ ["push", "-u", "origin", "HEAD:main"],
1098
+ {
1099
+ cwd: opts.projectDir,
1100
+ step: "git push origin main",
1101
+ env: { ...process.env, GH_TOKEN: opts.githubToken }
1102
+ }
1103
+ );
1104
+ return repoHttpsUrl(owner, name);
1105
+ }
963
1106
  const out = await run(
964
1107
  "gh",
965
1108
  [
966
1109
  "repo",
967
1110
  "create",
968
- name,
1111
+ fullName,
969
1112
  "--source",
970
1113
  opts.projectDir,
971
1114
  "--push",
@@ -981,7 +1124,7 @@ async function ghRepoCreate(opts) {
981
1124
  );
982
1125
  const match = out.match(/https:\/\/github\.com\/[^\s]+/g);
983
1126
  if (match && match.length > 0) return match[match.length - 1].replace(/[.,]$/, "");
984
- return `https://github.com/${name}`;
1127
+ return repoHttpsUrl(owner, name);
985
1128
  }
986
1129
  async function amplifyCreateApp(opts, repoUrl, iamServiceRoleArn) {
987
1130
  const cmd = [
@@ -1188,7 +1331,8 @@ var PreflightError = class extends Error {
1188
1331
  async function runDeploy(opts) {
1189
1332
  const pre = await runPreflight(opts, {
1190
1333
  iamServiceRoleArn: opts.iamServiceRoleArn,
1191
- createIamRole: opts.createIamRole === true
1334
+ createIamRole: opts.createIamRole === true,
1335
+ mountMode: opts.mount === true
1192
1336
  });
1193
1337
  if (pre.problems.length > 0) {
1194
1338
  printPreflightReport(pre.problems);
@@ -1209,7 +1353,7 @@ async function runDeploy(opts) {
1209
1353
  );
1210
1354
  }
1211
1355
  }
1212
- await writeFile2(resolve3(opts.projectDir, "amplify.yml"), AMPLIFY_BUILD_SPEC, "utf-8");
1356
+ await writeFile2(resolve4(opts.projectDir, "amplify.yml"), AMPLIFY_BUILD_SPEC, "utf-8");
1213
1357
  const created = {};
1214
1358
  const fail = (step, cause) => {
1215
1359
  const msg = cause instanceof Error ? cause.message : String(cause);
@@ -1233,10 +1377,14 @@ Re-run after cleaning up.` : "";
1233
1377
  ${msg}${cleanupBlock}`);
1234
1378
  };
1235
1379
  let s = spinner();
1236
- s.start("git init + initial commit");
1380
+ const gitMsg = opts.mount ? "git: preparing repo for mount" : "git init + initial commit";
1381
+ s.start(gitMsg);
1237
1382
  try {
1238
- await gitInitCommit(opts.projectDir);
1239
- s.stop("git: committed initial scaffold");
1383
+ if (opts.mount) {
1384
+ await ensureGitignore(opts.projectDir);
1385
+ }
1386
+ await gitInitOrReuse(opts.projectDir, opts.mount === true);
1387
+ s.stop(opts.mount ? "git: ready" : "git: committed initial scaffold");
1240
1388
  } catch (err) {
1241
1389
  s.stop("git: failed");
1242
1390
  fail("git init / commit", err);
@@ -1468,6 +1616,65 @@ function buildNonInteractiveOpts(args) {
1468
1616
  plugins
1469
1617
  };
1470
1618
  }
1619
+ function warnIgnoredScaffoldFlags(args) {
1620
+ const ignored = [];
1621
+ if (args.siteName) ignored.push("--site-name");
1622
+ if (args.themes) ignored.push("--themes");
1623
+ if (args.plugins) ignored.push("--plugins");
1624
+ if (args.projectName) ignored.push("<project-name> positional");
1625
+ if (ignored.length > 0) {
1626
+ log3.warn(`--mount mode ignores: ${ignored.join(", ")}`);
1627
+ }
1628
+ }
1629
+ async function runMount(args) {
1630
+ const destDir = process.cwd();
1631
+ const projectName = basename3(destDir);
1632
+ warnIgnoredScaffoldFlags(args);
1633
+ const problem = validateMountableProject(destDir);
1634
+ if (problem) {
1635
+ log3.error(problem);
1636
+ log3.info(
1637
+ "Run `npx create-ampless@alpha <name>` first to scaffold, or `cd` into a scaffolded project before passing --mount."
1638
+ );
1639
+ process.exit(1);
1640
+ }
1641
+ const deployOpts = await gatherDeployOptions(args, destDir, projectName);
1642
+ if (!deployOpts) return;
1643
+ try {
1644
+ const result = await runDeploy({ ...deployOpts, mount: true });
1645
+ printDeployResult(result);
1646
+ } catch (err) {
1647
+ if (err instanceof PreflightError) {
1648
+ process.exit(1);
1649
+ }
1650
+ log3.error(err instanceof Error ? err.message : String(err));
1651
+ process.exit(1);
1652
+ }
1653
+ }
1654
+ function printDeployResult(result) {
1655
+ const lines = [
1656
+ `${pc3.green("\u2714")} Project deployed`,
1657
+ ``,
1658
+ ` GitHub: ${pc3.cyan(result.githubRepoUrl)}`,
1659
+ ` Amplify app: ${pc3.cyan(result.amplifyAppId)}`,
1660
+ ` Amplify URL: ${pc3.cyan(result.amplifyAppUrl)}`
1661
+ ];
1662
+ if (result.domainUrl) {
1663
+ lines.push(` Custom domain: ${pc3.cyan(result.domainUrl)}`);
1664
+ }
1665
+ if (result.domainVerification && result.domainVerification.length > 0) {
1666
+ lines.push("", ` ${pc3.bold("Add these DNS records to verify the domain:")}`);
1667
+ for (const v of result.domainVerification) {
1668
+ lines.push(` ${v.cname} CNAME ${v.target}`);
1669
+ }
1670
+ }
1671
+ lines.push(
1672
+ "",
1673
+ ` First build is now running in Amplify Hosting.`,
1674
+ ` Watch it at ${pc3.cyan(`https://console.aws.amazon.com/amplify/home#/${result.amplifyAppId}`)}`
1675
+ );
1676
+ outro(lines.join("\n"));
1677
+ }
1471
1678
  async function main() {
1472
1679
  const args = parseDeployArgs(process.argv.slice(2));
1473
1680
  if (args.help) {
@@ -1477,6 +1684,10 @@ async function main() {
1477
1684
  for (const flag of args.unknown) {
1478
1685
  log3.warn(`Unknown argument ignored: ${flag}`);
1479
1686
  }
1687
+ if (args.mount) {
1688
+ await runMount(args);
1689
+ return;
1690
+ }
1480
1691
  let opts;
1481
1692
  if (args.skipConfirm) {
1482
1693
  opts = buildNonInteractiveOpts(args);
@@ -1484,8 +1695,8 @@ async function main() {
1484
1695
  opts = await runPrompts(args.projectName);
1485
1696
  }
1486
1697
  if (!opts) return;
1487
- const destDir = resolve4(process.cwd(), opts.projectName);
1488
- if (existsSync2(destDir)) {
1698
+ const destDir = resolve5(process.cwd(), opts.projectName);
1699
+ if (existsSync4(destDir)) {
1489
1700
  log3.error(`Directory already exists: ${destDir}`);
1490
1701
  process.exit(1);
1491
1702
  }
@@ -1505,28 +1716,7 @@ async function main() {
1505
1716
  if (!deployOpts) return;
1506
1717
  try {
1507
1718
  const result = await runDeploy(deployOpts);
1508
- const lines = [
1509
- `${pc3.green("\u2714")} Project deployed`,
1510
- ``,
1511
- ` GitHub: ${pc3.cyan(result.githubRepoUrl)}`,
1512
- ` Amplify app: ${pc3.cyan(result.amplifyAppId)}`,
1513
- ` Amplify URL: ${pc3.cyan(result.amplifyAppUrl)}`
1514
- ];
1515
- if (result.domainUrl) {
1516
- lines.push(` Custom domain: ${pc3.cyan(result.domainUrl)}`);
1517
- }
1518
- if (result.domainVerification && result.domainVerification.length > 0) {
1519
- lines.push("", ` ${pc3.bold("Add these DNS records to verify the domain:")}`);
1520
- for (const v of result.domainVerification) {
1521
- lines.push(` ${v.cname} CNAME ${v.target}`);
1522
- }
1523
- }
1524
- lines.push(
1525
- "",
1526
- ` First build is now running in Amplify Hosting.`,
1527
- ` Watch it at ${pc3.cyan(`https://console.aws.amazon.com/amplify/home#/${result.amplifyAppId}`)}`
1528
- );
1529
- outro(lines.join("\n"));
1719
+ printDeployResult(result);
1530
1720
  } catch (err) {
1531
1721
  if (err instanceof PreflightError) {
1532
1722
  process.exit(1);
@@ -24,9 +24,9 @@
24
24
  "@ampless/plugin-rss": "^0.2.0-alpha.1",
25
25
  "@ampless/plugin-seo": "^0.2.0-alpha.1",
26
26
  "@ampless/plugin-webhook": "^0.2.0-alpha.1",
27
- "@ampless/admin": "^0.2.0-alpha.5",
28
- "@ampless/backend": "^0.2.0-alpha.1",
29
- "@ampless/runtime": "^0.2.0-alpha.2",
27
+ "@ampless/admin": "^0.2.0-alpha.6",
28
+ "@ampless/backend": "^0.2.0-alpha.2",
29
+ "@ampless/runtime": "^0.2.0-alpha.3",
30
30
  "@digital-go-jp/tailwind-theme-plugin": "^0.3.4",
31
31
  "ampless": "^0.2.0-alpha.1",
32
32
  "aws-amplify": "^6.10.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-ampless",
3
- "version": "0.2.0-alpha.6",
3
+ "version": "0.2.0-alpha.7",
4
4
  "description": "Create a new ampless project",
5
5
  "license": "MIT",
6
6
  "type": "module",