@fluid-app/fluid-cli-theme-dev 0.1.3 → 0.1.5

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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @fluid-app/fluid-cli-theme-dev@0.1.3 build /home/runner/_work/fluid-mono/fluid-mono/packages/cli/theme-dev
2
+ > @fluid-app/fluid-cli-theme-dev@0.1.5 build /home/runner/_work/fluid-mono/fluid-mono/packages/cli/theme-dev
3
3
  > tsdown
4
4
 
5
5
  ℹ tsdown v0.21.0 powered by rolldown v1.0.0-rc.7
@@ -8,11 +8,9 @@
8
8
  ℹ target: node18
9
9
  ℹ tsconfig: tsconfig.json
10
10
  ℹ Build start
11
- ℹ dist/index.mjs 39.25 kB │ gzip: 11.30 kB
12
- ℹ dist/index.mjs.map 87.52 kB │ gzip: 21.90 kB
13
- ℹ dist/index.d.mts.map  0.11 kB │ gzip: 0.12 kB
14
- ℹ dist/index.d.mts  0.19 kB │ gzip: 0.16 kB
15
- ℹ 4 files, total: 127.07 kB
16
- [PLUGIN_TIMINGS] Warning: Your build spent significant time in plugin `rolldown-plugin-dts:generate`. See https://rolldown.rs/options/checks#plugintimings for more details.
17
-
18
- ✔ Build complete in 3302ms
11
+ ℹ dist/index.mjs  42.13 kB │ gzip: 11.81 kB
12
+ ℹ dist/index.mjs.map 112.29 kB │ gzip: 24.77 kB
13
+ ℹ dist/index.d.mts.map  0.11 kB │ gzip: 0.12 kB
14
+ ℹ dist/index.d.mts  0.19 kB │ gzip: 0.16 kB
15
+ ℹ 4 files, total: 154.73 kB
16
+ ✔ Build complete in 1283ms
package/dist/index.mjs CHANGED
@@ -610,6 +610,92 @@ function watchTheme(root, handler) {
610
610
  return () => watcher.close();
611
611
  }
612
612
  //#endregion
613
+ //#region ../../api-clients/themes/src/namespaces/v0.ts
614
+ /**
615
+ * List application themes
616
+ * Get all application themes with optional filters
617
+ *
618
+ * @param client - Fetch client instance
619
+ * @param [params] - params
620
+ */
621
+ async function listApplicationThemes(client, params) {
622
+ return client.get(`/api/application_themes`, params);
623
+ }
624
+ /**
625
+ * Create an application theme
626
+ *
627
+ *
628
+ * @param client - Fetch client instance
629
+ * @param body - body
630
+ */
631
+ async function createApplicationTheme(client, body) {
632
+ return client.post(`/api/application_themes`, body);
633
+ }
634
+ /**
635
+ * Get an application theme
636
+ *
637
+ *
638
+ * @param client - Fetch client instance
639
+ * @param id - id
640
+ * @param [params] - params
641
+ */
642
+ async function getApplicationTheme(client, id, params) {
643
+ return client.get(`/api/application_themes/${id}`, params);
644
+ }
645
+ /**
646
+ * Returns available themeables for a given type scoped to the theme's company
647
+ *
648
+ *
649
+ * @param client - Fetch client instance
650
+ * @param id - id
651
+ * @param [params] - params
652
+ */
653
+ async function getApplicationThemeAvailableThemeables(client, id, params) {
654
+ return client.get(`/api/application_themes/${id}/available_themeables`, params);
655
+ }
656
+ /**
657
+ * Publishes the theme
658
+ *
659
+ *
660
+ * @param client - Fetch client instance
661
+ * @param id - id
662
+ */
663
+ async function publishApplicationTheme(client, id) {
664
+ return client.post(`/api/application_themes/${id}/publish`);
665
+ }
666
+ /**
667
+ * Lists all theme resources
668
+ *
669
+ *
670
+ * @param client - Fetch client instance
671
+ * @param application_theme_id - application_theme_id
672
+ */
673
+ async function listThemeResources(client, application_theme_id) {
674
+ return client.get(`/api/application_themes/${application_theme_id}/resources`);
675
+ }
676
+ /**
677
+ * Updates a theme resource
678
+ *
679
+ *
680
+ * @param client - Fetch client instance
681
+ * @param application_theme_id - application_theme_id
682
+ * @param body - body
683
+ */
684
+ async function updateThemeResource(client, application_theme_id, body) {
685
+ return client.put(`/api/application_themes/${application_theme_id}/resources`, body);
686
+ }
687
+ /**
688
+ * Deletes a theme resource
689
+ *
690
+ *
691
+ * @param client - Fetch client instance
692
+ * @param application_theme_id - application_theme_id
693
+ * @param body - body
694
+ */
695
+ async function deleteThemeResource(client, application_theme_id, body) {
696
+ return client.delete(`/api/application_themes/${application_theme_id}/resources`, { body });
697
+ }
698
+ //#endregion
613
699
  //#region src/theme/syncer.ts
614
700
  var Syncer = class {
615
701
  checksums = /* @__PURE__ */ new Map();
@@ -619,11 +705,11 @@ var Syncer = class {
619
705
  this.themeRoot = themeRoot;
620
706
  }
621
707
  async fetchChecksums() {
622
- const body = await this.api.get(`/api/application_themes/${this.themeId}/resources`);
708
+ const body = await listThemeResources(this.api, this.themeId);
623
709
  this.updateChecksums(body.application_theme_resources ?? []);
624
710
  }
625
711
  updateChecksums(resources) {
626
- for (const r of resources) if (r.key) this.checksums.set(r.key, r.checksum);
712
+ for (const r of resources) if (r.key && r.checksum) this.checksums.set(r.key, r.checksum);
627
713
  for (const key of this.checksums.keys()) if (this.checksums.has(`${key}.liquid`)) this.checksums.delete(key);
628
714
  }
629
715
  hasChanged(file) {
@@ -633,14 +719,13 @@ var Syncer = class {
633
719
  return [...this.checksums.keys()];
634
720
  }
635
721
  async uploadFile(file) {
636
- const path = `/api/application_themes/${this.themeId}/resources`;
637
- if (file.isText) await this.api.put(path, { application_theme_resource: {
722
+ if (file.isText) await updateThemeResource(this.api, this.themeId, { application_theme_resource: {
638
723
  key: file.relativePath,
639
724
  content: file.read()
640
725
  } });
641
- else await this.uploadBinaryFile(file, path);
726
+ else await this.uploadBinaryFile(file);
642
727
  }
643
- async uploadBinaryFile(file, resourcePath) {
728
+ async uploadBinaryFile(file) {
644
729
  const asset = (await this.api.post("/api/dam/assets", { placeholder_asset: {
645
730
  description: `Uploaded via Fluid CLI: ${file.name}`,
646
731
  mime_type: file.mime.name,
@@ -675,7 +760,7 @@ var Syncer = class {
675
760
  if (ikBody.height) backfillPayload["asset"]["height"] = ikBody.height;
676
761
  if (ikBody.width) backfillPayload["asset"]["width"] = ikBody.width;
677
762
  const backfillBody = await this.api.post("/api/dam/assets/backfill_imagekit", backfillPayload);
678
- await this.api.put(resourcePath, { application_theme_resource: {
763
+ await updateThemeResource(this.api, this.themeId, { application_theme_resource: {
679
764
  key: file.relativePath,
680
765
  dam_asset: {
681
766
  dam_asset_code: backfillBody.asset.code,
@@ -702,13 +787,13 @@ var Syncer = class {
702
787
  }[category] ?? "files"}/${assetCode}`;
703
788
  }
704
789
  async deleteRemoteFile(relativePath) {
705
- await this.api.delete(`/api/application_themes/${this.themeId}/resources`, { body: { application_theme_resource: { key: relativePath } } });
790
+ await deleteThemeResource(this.api, this.themeId, { application_theme_resource: { key: relativePath } });
706
791
  this.checksums.delete(relativePath);
707
792
  }
708
793
  async downloadAll() {
709
- const body = await this.api.get(`/api/application_themes/${this.themeId}/resources`);
710
- this.updateChecksums(body.application_theme_resources ?? []);
711
- return body.application_theme_resources ?? [];
794
+ const resources = (await listThemeResources(this.api, this.themeId)).application_theme_resources ?? [];
795
+ this.updateChecksums(resources);
796
+ return resources;
712
797
  }
713
798
  async downloadBinaryAsset(url) {
714
799
  const resp = await fetch(url);
@@ -767,7 +852,10 @@ var Syncer = class {
767
852
  if (resource.resource_type === "FileResource" && resource.url) {
768
853
  const buf = await this.downloadBinaryAsset(resource.url);
769
854
  file.write(buf);
770
- } else if (resource.content !== void 0) file.write(resource.content);
855
+ } else if (resource.content !== void 0 && resource.content !== null) {
856
+ const content = typeof resource.content === "string" ? resource.content : JSON.stringify(resource.content);
857
+ file.write(content);
858
+ }
771
859
  result.downloaded++;
772
860
  } catch (e) {
773
861
  result.errors.push(`Download ${resource.key}: ${e}`);
@@ -856,7 +944,7 @@ async function startDevServer(api, theme, themeRoot, opts, onReady) {
856
944
  //#region src/commands/dev.ts
857
945
  async function ensureDevTheme(api, identifier) {
858
946
  if (identifier) {
859
- const body = await api.get("/api/application_themes");
947
+ const body = await listApplicationThemes(api);
860
948
  const found = (body.application_themes ?? []).find((t) => String(t.id) === identifier) ?? (body.application_themes ?? []).find((t) => t.name.toLowerCase() === identifier.toLowerCase());
861
949
  if (!found) {
862
950
  console.error(`Theme not found: ${identifier}`);
@@ -866,17 +954,16 @@ async function ensureDevTheme(api, identifier) {
866
954
  }
867
955
  const { devThemeId } = getPluginState();
868
956
  if (devThemeId) try {
869
- const body = await api.get(`/api/application_themes/${devThemeId}`);
957
+ const body = await getApplicationTheme(api, devThemeId);
870
958
  if (body.application_theme) {
871
959
  console.log(`Using existing dev theme #${devThemeId}`);
872
960
  return body.application_theme;
873
961
  }
874
962
  } catch {}
875
963
  const { hostname } = await import("node:os");
876
- const name = `Development (${hostname().split(".")[0] ?? "dev"}-${Math.random().toString(36).slice(2, 8)})`.slice(0, 50);
877
- const theme = (await api.post("/api/application_themes", { application_theme: {
878
- name,
879
- role: "development"
964
+ const theme = (await createApplicationTheme(api, { application_theme: {
965
+ name: `Development (${hostname().split(".")[0] ?? "dev"}-${Math.random().toString(36).slice(2, 8)})`.slice(0, 50),
966
+ status: "development"
880
967
  } })).application_theme;
881
968
  setPluginState({
882
969
  devThemeId: theme.id,
@@ -917,7 +1004,7 @@ function createDevCommand() {
917
1004
  id: theme.id,
918
1005
  name: theme.name,
919
1006
  company,
920
- editorUrl: theme.editor_url
1007
+ editorUrl: theme.editor_url ?? void 0
921
1008
  }, themeRoot, {
922
1009
  host: opts.host,
923
1010
  port,
@@ -934,8 +1021,8 @@ function createDevCommand() {
934
1021
  //#endregion
935
1022
  //#region src/commands/push.ts
936
1023
  async function selectTheme(api) {
937
- const themes = (await api.get("/api/application_themes")).application_themes ?? [];
938
- if (!themes.length) {
1024
+ const themeList = (await listApplicationThemes(api)).application_themes ?? [];
1025
+ if (!themeList.length) {
939
1026
  console.error("No themes found.");
940
1027
  process.exit(1);
941
1028
  }
@@ -943,7 +1030,7 @@ async function selectTheme(api) {
943
1030
  type: "select",
944
1031
  name: "id",
945
1032
  message: "Select a theme to push to",
946
- choices: themes.map((t) => ({
1033
+ choices: themeList.map((t) => ({
947
1034
  title: `${t.name} (#${t.id})`,
948
1035
  value: t.id
949
1036
  }))
@@ -952,11 +1039,11 @@ async function selectTheme(api) {
952
1039
  console.error("No theme selected.");
953
1040
  process.exit(1);
954
1041
  }
955
- return themes.find((t) => t.id === id);
1042
+ return themeList.find((t) => t.id === id);
956
1043
  }
957
1044
  async function findTheme(api, identifier) {
958
- const themes = (await api.get("/api/application_themes")).application_themes ?? [];
959
- const found = themes.find((t) => String(t.id) === identifier) ?? themes.find((t) => t.name.toLowerCase() === identifier.toLowerCase());
1045
+ const themeList = (await listApplicationThemes(api)).application_themes ?? [];
1046
+ const found = themeList.find((t) => String(t.id) === identifier) ?? themeList.find((t) => t.name.toLowerCase() === identifier.toLowerCase());
960
1047
  if (!found) {
961
1048
  console.error(`No theme found with identifier: ${identifier}`);
962
1049
  process.exit(1);
@@ -964,7 +1051,7 @@ async function findTheme(api, identifier) {
964
1051
  return found;
965
1052
  }
966
1053
  function createPushCommand() {
967
- return new Command("push").description("Push local theme files to a remote theme").option("-t, --theme <name-or-id>", "Theme name or ID to push to").option("-n, --nodelete", "Do not delete remote files missing locally").option("-f, --force", "Skip schema validation").option("-p, --publish", "Publish the theme after pushing").option("--root <path>", "Theme root directory", ".").action(async (opts) => {
1054
+ return new Command("push").description("Push local theme files to a remote theme").option("-t, --theme <name-or-id>", "Theme name or ID to push to").option("-n, --nodelete", "Do not delete remote files missing locally").option("-f, --force", "Skip schema validation").option("-p, --publish", "Publish the theme after pushing").option("-u, --unpublished", "Create a new unpublished theme and push to it").option("--root <path>", "Theme root directory", ".").action(async (opts) => {
968
1055
  requireToken();
969
1056
  const themeRoot = new ThemeRoot(opts.root);
970
1057
  if (!themeRoot.isValid()) {
@@ -972,7 +1059,23 @@ function createPushCommand() {
972
1059
  process.exit(1);
973
1060
  }
974
1061
  const api = createApiClient();
975
- const theme = opts.theme ? await findTheme(api, opts.theme) : await selectTheme(api);
1062
+ let theme;
1063
+ if (opts.unpublished) {
1064
+ const { name } = await prompts({
1065
+ type: "text",
1066
+ name: "name",
1067
+ message: "Name for the new theme"
1068
+ }, { onCancel: () => process.exit(130) });
1069
+ if (!name) {
1070
+ console.error("Theme name is required.");
1071
+ process.exit(1);
1072
+ }
1073
+ theme = (await createApplicationTheme(api, { application_theme: {
1074
+ name,
1075
+ status: "draft"
1076
+ } })).application_theme;
1077
+ console.log(`Created unpublished theme: ${theme.name} (#${theme.id})`);
1078
+ } else theme = opts.theme ? await findTheme(api, opts.theme) : await selectTheme(api);
976
1079
  const syncer = new Syncer(api, theme.id, themeRoot);
977
1080
  const spinner = ora(`Pushing to ${theme.name} (#${theme.id})…`).start();
978
1081
  const result = await syncer.uploadTheme({
@@ -988,7 +1091,7 @@ function createPushCommand() {
988
1091
  if (opts.publish) {
989
1092
  const pubSpinner = ora("Publishing theme…").start();
990
1093
  try {
991
- await api.post(`/api/application_themes/${theme.id}/publish`);
1094
+ await publishApplicationTheme(api, theme.id);
992
1095
  pubSpinner.succeed("Theme published.");
993
1096
  } catch (e) {
994
1097
  pubSpinner.fail(`Publish failed: ${e}`);
@@ -999,13 +1102,13 @@ function createPushCommand() {
999
1102
  //#endregion
1000
1103
  //#region src/commands/pull.ts
1001
1104
  async function selectOrFindTheme(api, identifier) {
1002
- const themes = (await api.get("/api/application_themes")).application_themes ?? [];
1003
- if (!themes.length) {
1105
+ const themeList = (await listApplicationThemes(api)).application_themes ?? [];
1106
+ if (!themeList.length) {
1004
1107
  console.error("No themes found.");
1005
1108
  process.exit(1);
1006
1109
  }
1007
1110
  if (identifier) {
1008
- const found = themes.find((t) => String(t.id) === identifier) ?? themes.find((t) => t.name.toLowerCase() === identifier.toLowerCase());
1111
+ const found = themeList.find((t) => String(t.id) === identifier) ?? themeList.find((t) => t.name.toLowerCase() === identifier.toLowerCase());
1009
1112
  if (!found) {
1010
1113
  console.error(`No theme found with identifier: ${identifier}`);
1011
1114
  process.exit(1);
@@ -1016,7 +1119,7 @@ async function selectOrFindTheme(api, identifier) {
1016
1119
  type: "select",
1017
1120
  name: "id",
1018
1121
  message: "Select a theme to pull",
1019
- choices: themes.map((t) => ({
1122
+ choices: themeList.map((t) => ({
1020
1123
  title: `${t.name} (#${t.id})`,
1021
1124
  value: t.id
1022
1125
  }))
@@ -1025,7 +1128,7 @@ async function selectOrFindTheme(api, identifier) {
1025
1128
  console.error("No theme selected.");
1026
1129
  process.exit(1);
1027
1130
  }
1028
- return themes.find((t) => t.id === id);
1131
+ return themeList.find((t) => t.id === id);
1029
1132
  }
1030
1133
  function createPullCommand() {
1031
1134
  return new Command("pull").description("Pull a remote theme to your local directory").option("-t, --theme <name-or-id>", "Theme name or ID to pull").option("-n, --nodelete", "Do not delete local files missing on remote").option("--root <path>", "Theme root directory", ".").action(async (opts) => {
@@ -1159,6 +1262,12 @@ const RESOURCE_ROUTES = [
1159
1262
  type: "enrollment_pack",
1160
1263
  template: "/home/enrollments/%s",
1161
1264
  fallback: "/home/join"
1265
+ },
1266
+ {
1267
+ label: "Page",
1268
+ type: "page",
1269
+ template: "/home/pages/%s",
1270
+ fallback: "/home/pages"
1162
1271
  }
1163
1272
  ];
1164
1273
  function createNavigateCommand() {
@@ -1193,7 +1302,7 @@ function createNavigateCommand() {
1193
1302
  let path;
1194
1303
  if (typeof dest === "string") path = dest;
1195
1304
  else {
1196
- const resources = (await createApiClient().get(`/api/application_themes/${themeId}/available_themeables`, {
1305
+ const resources = (await getApplicationThemeAvailableThemeables(createApiClient(), themeId, {
1197
1306
  themeable: dest.resourceType,
1198
1307
  per_page: 50
1199
1308
  })).available_themeables ?? [];
@@ -1206,7 +1315,7 @@ function createNavigateCommand() {
1206
1315
  name: "slug",
1207
1316
  message: `Select a ${dest.label.toLowerCase()}`,
1208
1317
  choices: resources.map((r) => ({
1209
- title: r.title ?? r.slug,
1318
+ title: r.title ?? r.slug ?? "Untitled",
1210
1319
  value: r.slug
1211
1320
  }))
1212
1321
  }, { onCancel });