@cmssy/cli 0.22.4 → 1.0.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.
Files changed (260) hide show
  1. package/README.md +49 -650
  2. package/dist/cli.js +706 -328
  3. package/package.json +26 -73
  4. package/templates/block/Component.module.css.tpl +12 -0
  5. package/templates/block/Component.tsx.tpl +15 -0
  6. package/templates/block/block.ts.tpl +11 -0
  7. package/templates/init/app/[[...path]]/page.tsx +19 -0
  8. package/templates/init/app/api/draft/route.ts +5 -0
  9. package/templates/init/blocks/hero/Hero.module.css +131 -0
  10. package/templates/init/blocks/hero/Hero.tsx +100 -0
  11. package/templates/init/blocks/hero/block.ts +21 -0
  12. package/templates/init/cmssy/blocks.ts +5 -0
  13. package/templates/init/cmssy/editor.tsx +10 -0
  14. package/templates/init/cmssy.config.ts +8 -0
  15. package/templates/init/env.example +4 -0
  16. package/templates/init/next.config.mjs +8 -0
  17. package/templates/init/proxy.ts +32 -0
  18. package/config.d.ts +0 -3
  19. package/config.js +0 -4
  20. package/dist/cli.d.ts +0 -3
  21. package/dist/cli.d.ts.map +0 -1
  22. package/dist/cli.js.map +0 -1
  23. package/dist/commands/add-source.d.ts +0 -13
  24. package/dist/commands/add-source.d.ts.map +0 -1
  25. package/dist/commands/add-source.js +0 -273
  26. package/dist/commands/add-source.js.map +0 -1
  27. package/dist/commands/build.d.ts +0 -7
  28. package/dist/commands/build.d.ts.map +0 -1
  29. package/dist/commands/build.js +0 -127
  30. package/dist/commands/build.js.map +0 -1
  31. package/dist/commands/codegen.d.ts +0 -7
  32. package/dist/commands/codegen.d.ts.map +0 -1
  33. package/dist/commands/codegen.js +0 -150
  34. package/dist/commands/codegen.js.map +0 -1
  35. package/dist/commands/configure.d.ts +0 -6
  36. package/dist/commands/configure.d.ts.map +0 -1
  37. package/dist/commands/configure.js +0 -39
  38. package/dist/commands/configure.js.map +0 -1
  39. package/dist/commands/create.d.ts +0 -18
  40. package/dist/commands/create.d.ts.map +0 -1
  41. package/dist/commands/create.js +0 -403
  42. package/dist/commands/create.js.map +0 -1
  43. package/dist/commands/dev.d.ts +0 -6
  44. package/dist/commands/dev.d.ts.map +0 -1
  45. package/dist/commands/dev.js +0 -144
  46. package/dist/commands/dev.js.map +0 -1
  47. package/dist/commands/doctor.d.ts +0 -2
  48. package/dist/commands/doctor.d.ts.map +0 -1
  49. package/dist/commands/doctor.js +0 -230
  50. package/dist/commands/doctor.js.map +0 -1
  51. package/dist/commands/init.d.ts +0 -6
  52. package/dist/commands/init.d.ts.map +0 -1
  53. package/dist/commands/init.js +0 -414
  54. package/dist/commands/init.js.map +0 -1
  55. package/dist/commands/lib.d.ts +0 -17
  56. package/dist/commands/lib.d.ts.map +0 -1
  57. package/dist/commands/lib.js +0 -132
  58. package/dist/commands/lib.js.map +0 -1
  59. package/dist/commands/link.d.ts +0 -8
  60. package/dist/commands/link.d.ts.map +0 -1
  61. package/dist/commands/link.js +0 -116
  62. package/dist/commands/link.js.map +0 -1
  63. package/dist/commands/migrate.d.ts +0 -2
  64. package/dist/commands/migrate.d.ts.map +0 -1
  65. package/dist/commands/migrate.js +0 -231
  66. package/dist/commands/migrate.js.map +0 -1
  67. package/dist/commands/package.d.ts +0 -14
  68. package/dist/commands/package.d.ts.map +0 -1
  69. package/dist/commands/package.js +0 -137
  70. package/dist/commands/package.js.map +0 -1
  71. package/dist/commands/publish-block-buildtime.d.ts +0 -8
  72. package/dist/commands/publish-block-buildtime.d.ts.map +0 -1
  73. package/dist/commands/publish-block-buildtime.js +0 -290
  74. package/dist/commands/publish-block-buildtime.js.map +0 -1
  75. package/dist/commands/publish-template.d.ts +0 -22
  76. package/dist/commands/publish-template.d.ts.map +0 -1
  77. package/dist/commands/publish-template.js +0 -345
  78. package/dist/commands/publish-template.js.map +0 -1
  79. package/dist/commands/publish.d.ts +0 -15
  80. package/dist/commands/publish.d.ts.map +0 -1
  81. package/dist/commands/publish.js +0 -1003
  82. package/dist/commands/publish.js.map +0 -1
  83. package/dist/commands/skills.d.ts +0 -11
  84. package/dist/commands/skills.d.ts.map +0 -1
  85. package/dist/commands/skills.js +0 -178
  86. package/dist/commands/skills.js.map +0 -1
  87. package/dist/commands/sync.d.ts +0 -6
  88. package/dist/commands/sync.d.ts.map +0 -1
  89. package/dist/commands/sync.js +0 -208
  90. package/dist/commands/sync.js.map +0 -1
  91. package/dist/commands/test.d.ts +0 -8
  92. package/dist/commands/test.d.ts.map +0 -1
  93. package/dist/commands/test.js +0 -60
  94. package/dist/commands/test.js.map +0 -1
  95. package/dist/commands/upload.d.ts +0 -11
  96. package/dist/commands/upload.d.ts.map +0 -1
  97. package/dist/commands/upload.js +0 -128
  98. package/dist/commands/upload.js.map +0 -1
  99. package/dist/commands/workspaces.d.ts +0 -2
  100. package/dist/commands/workspaces.d.ts.map +0 -1
  101. package/dist/commands/workspaces.js +0 -62
  102. package/dist/commands/workspaces.js.map +0 -1
  103. package/dist/dev-ui/app.js +0 -1284
  104. package/dist/dev-ui/index.html +0 -1511
  105. package/dist/dev-ui-react/App.tsx +0 -164
  106. package/dist/dev-ui-react/__tests__/previewData.test.ts +0 -193
  107. package/dist/dev-ui-react/components/BlocksList.tsx +0 -232
  108. package/dist/dev-ui-react/components/Editor.tsx +0 -469
  109. package/dist/dev-ui-react/components/Preview.tsx +0 -146
  110. package/dist/dev-ui-react/hooks/useBlocks.ts +0 -80
  111. package/dist/dev-ui-react/index.html +0 -13
  112. package/dist/dev-ui-react/main.tsx +0 -8
  113. package/dist/dev-ui-react/styles.css +0 -856
  114. package/dist/dev-ui-react/types.ts +0 -45
  115. package/dist/skills/claude/cmssy-block.md +0 -454
  116. package/dist/skills/claude/cmssy-mcp-content.md +0 -235
  117. package/dist/test-helpers/index.d.ts +0 -23
  118. package/dist/test-helpers/index.d.ts.map +0 -1
  119. package/dist/test-helpers/index.js +0 -50
  120. package/dist/test-helpers/index.js.map +0 -1
  121. package/dist/types/block-config.d.ts +0 -6
  122. package/dist/types/block-config.d.ts.map +0 -1
  123. package/dist/types/block-config.js +0 -12
  124. package/dist/types/block-config.js.map +0 -1
  125. package/dist/types/publish-job.d.ts +0 -61
  126. package/dist/types/publish-job.d.ts.map +0 -1
  127. package/dist/types/publish-job.js +0 -13
  128. package/dist/types/publish-job.js.map +0 -1
  129. package/dist/utils/api-error.d.ts +0 -10
  130. package/dist/utils/api-error.d.ts.map +0 -1
  131. package/dist/utils/api-error.js +0 -55
  132. package/dist/utils/api-error.js.map +0 -1
  133. package/dist/utils/block-config.d.ts +0 -87
  134. package/dist/utils/block-config.d.ts.map +0 -1
  135. package/dist/utils/block-config.js +0 -214
  136. package/dist/utils/block-config.js.map +0 -1
  137. package/dist/utils/blocks-meta-cache.d.ts +0 -28
  138. package/dist/utils/blocks-meta-cache.d.ts.map +0 -1
  139. package/dist/utils/blocks-meta-cache.js +0 -72
  140. package/dist/utils/blocks-meta-cache.js.map +0 -1
  141. package/dist/utils/builder.d.ts +0 -34
  142. package/dist/utils/builder.d.ts.map +0 -1
  143. package/dist/utils/builder.js +0 -140
  144. package/dist/utils/builder.js.map +0 -1
  145. package/dist/utils/cmssy-config.d.ts +0 -16
  146. package/dist/utils/cmssy-config.d.ts.map +0 -1
  147. package/dist/utils/cmssy-config.js +0 -19
  148. package/dist/utils/cmssy-config.js.map +0 -1
  149. package/dist/utils/config.d.ts +0 -9
  150. package/dist/utils/config.d.ts.map +0 -1
  151. package/dist/utils/config.js +0 -51
  152. package/dist/utils/config.js.map +0 -1
  153. package/dist/utils/dependency-check.d.ts +0 -21
  154. package/dist/utils/dependency-check.d.ts.map +0 -1
  155. package/dist/utils/dependency-check.js +0 -56
  156. package/dist/utils/dependency-check.js.map +0 -1
  157. package/dist/utils/dev-generator/api-routes/blocks.d.ts +0 -3
  158. package/dist/utils/dev-generator/api-routes/blocks.d.ts.map +0 -1
  159. package/dist/utils/dev-generator/api-routes/blocks.js +0 -194
  160. package/dist/utils/dev-generator/api-routes/blocks.js.map +0 -1
  161. package/dist/utils/dev-generator/api-routes/config.d.ts +0 -2
  162. package/dist/utils/dev-generator/api-routes/config.d.ts.map +0 -1
  163. package/dist/utils/dev-generator/api-routes/config.js +0 -107
  164. package/dist/utils/dev-generator/api-routes/config.js.map +0 -1
  165. package/dist/utils/dev-generator/api-routes/preview.d.ts +0 -2
  166. package/dist/utils/dev-generator/api-routes/preview.d.ts.map +0 -1
  167. package/dist/utils/dev-generator/api-routes/preview.js +0 -50
  168. package/dist/utils/dev-generator/api-routes/preview.js.map +0 -1
  169. package/dist/utils/dev-generator/api-routes/workspaces.d.ts +0 -2
  170. package/dist/utils/dev-generator/api-routes/workspaces.d.ts.map +0 -1
  171. package/dist/utils/dev-generator/api-routes/workspaces.js +0 -128
  172. package/dist/utils/dev-generator/api-routes/workspaces.js.map +0 -1
  173. package/dist/utils/dev-generator/helpers.d.ts +0 -23
  174. package/dist/utils/dev-generator/helpers.d.ts.map +0 -1
  175. package/dist/utils/dev-generator/helpers.js +0 -128
  176. package/dist/utils/dev-generator/helpers.js.map +0 -1
  177. package/dist/utils/dev-generator/home-page.d.ts +0 -2
  178. package/dist/utils/dev-generator/home-page.d.ts.map +0 -1
  179. package/dist/utils/dev-generator/home-page.js +0 -643
  180. package/dist/utils/dev-generator/home-page.js.map +0 -1
  181. package/dist/utils/dev-generator/index.d.ts +0 -11
  182. package/dist/utils/dev-generator/index.d.ts.map +0 -1
  183. package/dist/utils/dev-generator/index.js +0 -85
  184. package/dist/utils/dev-generator/index.js.map +0 -1
  185. package/dist/utils/dev-generator/layout.d.ts +0 -3
  186. package/dist/utils/dev-generator/layout.d.ts.map +0 -1
  187. package/dist/utils/dev-generator/layout.js +0 -63
  188. package/dist/utils/dev-generator/layout.js.map +0 -1
  189. package/dist/utils/dev-generator/next-config.d.ts +0 -3
  190. package/dist/utils/dev-generator/next-config.d.ts.map +0 -1
  191. package/dist/utils/dev-generator/next-config.js +0 -111
  192. package/dist/utils/dev-generator/next-config.js.map +0 -1
  193. package/dist/utils/dev-generator/preview-pages.d.ts +0 -3
  194. package/dist/utils/dev-generator/preview-pages.d.ts.map +0 -1
  195. package/dist/utils/dev-generator/preview-pages.js +0 -833
  196. package/dist/utils/dev-generator/preview-pages.js.map +0 -1
  197. package/dist/utils/dev-generator.d.ts +0 -12
  198. package/dist/utils/dev-generator.d.ts.map +0 -1
  199. package/dist/utils/dev-generator.js +0 -1752
  200. package/dist/utils/dev-generator.js.map +0 -1
  201. package/dist/utils/field-schema.d.ts +0 -13
  202. package/dist/utils/field-schema.d.ts.map +0 -1
  203. package/dist/utils/field-schema.js +0 -182
  204. package/dist/utils/field-schema.js.map +0 -1
  205. package/dist/utils/graphql.d.ts +0 -39
  206. package/dist/utils/graphql.d.ts.map +0 -1
  207. package/dist/utils/graphql.js +0 -138
  208. package/dist/utils/graphql.js.map +0 -1
  209. package/dist/utils/lib-manifest.d.ts +0 -7
  210. package/dist/utils/lib-manifest.d.ts.map +0 -1
  211. package/dist/utils/lib-manifest.js +0 -59
  212. package/dist/utils/lib-manifest.js.map +0 -1
  213. package/dist/utils/publish-helpers.d.ts +0 -43
  214. package/dist/utils/publish-helpers.d.ts.map +0 -1
  215. package/dist/utils/publish-helpers.js +0 -215
  216. package/dist/utils/publish-helpers.js.map +0 -1
  217. package/dist/utils/publish-helpers.test.d.ts +0 -2
  218. package/dist/utils/publish-helpers.test.d.ts.map +0 -1
  219. package/dist/utils/publish-helpers.test.js +0 -24
  220. package/dist/utils/publish-helpers.test.js.map +0 -1
  221. package/dist/utils/resolve-workspace.d.ts +0 -6
  222. package/dist/utils/resolve-workspace.d.ts.map +0 -1
  223. package/dist/utils/resolve-workspace.js +0 -32
  224. package/dist/utils/resolve-workspace.js.map +0 -1
  225. package/dist/utils/scanner.d.ts +0 -42
  226. package/dist/utils/scanner.d.ts.map +0 -1
  227. package/dist/utils/scanner.js +0 -156
  228. package/dist/utils/scanner.js.map +0 -1
  229. package/dist/utils/schema-diff.d.ts +0 -22
  230. package/dist/utils/schema-diff.d.ts.map +0 -1
  231. package/dist/utils/schema-diff.js +0 -107
  232. package/dist/utils/schema-diff.js.map +0 -1
  233. package/dist/utils/source-collector.d.ts +0 -19
  234. package/dist/utils/source-collector.d.ts.map +0 -1
  235. package/dist/utils/source-collector.js +0 -565
  236. package/dist/utils/source-collector.js.map +0 -1
  237. package/dist/utils/theme-builder.d.ts +0 -12
  238. package/dist/utils/theme-builder.d.ts.map +0 -1
  239. package/dist/utils/theme-builder.js +0 -117
  240. package/dist/utils/theme-builder.js.map +0 -1
  241. package/dist/utils/type-generator.d.ts +0 -9
  242. package/dist/utils/type-generator.d.ts.map +0 -1
  243. package/dist/utils/type-generator.js +0 -86
  244. package/dist/utils/type-generator.js.map +0 -1
  245. package/dist/utils/version.d.ts +0 -10
  246. package/dist/utils/version.d.ts.map +0 -1
  247. package/dist/utils/version.js +0 -30
  248. package/dist/utils/version.js.map +0 -1
  249. package/dist/utils/versions.d.ts.map +0 -1
  250. package/dist/utils/versions.js.map +0 -1
  251. package/src/dev-app/app/api/blocks/[name]/config/route.ts +0 -140
  252. package/src/dev-app/app/api/blocks/route.ts +0 -61
  253. package/src/dev-app/app/api/config/route.ts +0 -98
  254. package/src/dev-app/app/api/context/route.ts +0 -126
  255. package/src/dev-app/app/api/preview/[name]/route.ts +0 -137
  256. package/src/dev-app/app/api/workspaces/route.ts +0 -124
  257. package/src/dev-app/app/layout.tsx +0 -18
  258. package/src/dev-app/app/page.tsx +0 -1785
  259. package/test.d.ts +0 -1
  260. package/test.js +0 -2
@@ -1,1003 +0,0 @@
1
- import chalk from "chalk";
2
- import fs from "fs-extra";
3
- import { GraphQLClient } from "graphql-request";
4
- import inquirer from "inquirer";
5
- import ora from "ora";
6
- import path from "path";
7
- import semver from "semver";
8
- import { hasConfig, loadConfig } from "../utils/config.js";
9
- import { GET_WORKSPACE_BLOCKS_QUERY, IMPORT_BLOCK_MUTATION, IMPORT_TEMPLATE_MUTATION, UPDATE_THEME_MUTATION, } from "../utils/graphql.js";
10
- import { loadBlockConfig, validateDefaultValues, validateSchema, } from "../utils/block-config.js";
11
- import { extractBlockType, convertConfigToPagesData, loadTemplateConfig, } from "../utils/publish-helpers.js";
12
- import { scanTheme } from "../utils/scanner.js";
13
- import { convertThemeToInput } from "../utils/theme-builder.js";
14
- import { packageResource } from "./package.js";
15
- import { uploadPackage } from "./upload.js";
16
- import { diffSchema, hasBreakingChanges, } from "../utils/schema-diff.js";
17
- export async function publishCommand(packageNames = [], options) {
18
- console.log(chalk.blue.bold("\n📦 Cmssy - Publish\n"));
19
- // Validate flags: must have --workspace
20
- if (!options.workspace) {
21
- console.error(chalk.red("✖ Specify publish target:\n") +
22
- chalk.white(" --workspace <id> Publish to workspace\n") +
23
- chalk.white("\nExample: cmssy publish --all --workspace abc123\n"));
24
- process.exit(1);
25
- }
26
- // Check configuration
27
- if (!hasConfig()) {
28
- console.error(chalk.red("✖ Not configured. Run: cmssy link\n"));
29
- process.exit(1);
30
- }
31
- const config = loadConfig();
32
- // Get workspace ID if --workspace without value
33
- let workspaceId = options.workspace;
34
- if (typeof options.workspace === "boolean" || options.workspace === "") {
35
- // Flag provided without value, check .env
36
- if (config.workspaceId) {
37
- workspaceId = config.workspaceId;
38
- console.log(chalk.gray(`Using workspace ID from .env: ${workspaceId}\n`));
39
- }
40
- else {
41
- const answer = await inquirer.prompt([
42
- {
43
- type: "input",
44
- name: "workspaceId",
45
- message: "Enter Workspace ID:",
46
- validate: (input) => {
47
- if (!input) {
48
- return "Workspace ID is required (or set CMSSY_WORKSPACE_ID in .env)";
49
- }
50
- return true;
51
- },
52
- },
53
- ]);
54
- workspaceId = answer.workspaceId;
55
- }
56
- }
57
- // Find cmssy.config.js
58
- const configPath = path.join(process.cwd(), "cmssy.config.js");
59
- if (!fs.existsSync(configPath)) {
60
- console.error(chalk.red("✖ Not a cmssy project (missing cmssy.config.js)\n"));
61
- process.exit(1);
62
- }
63
- // Scan for packages to publish
64
- let packages = await scanPackages(packageNames, options);
65
- // Auto-detect and add template dependencies (blocks used in pages.json)
66
- // Only for workspace publish, not library
67
- if (options.workspace) {
68
- const templatesToProcess = packages.filter((p) => p.type === "template");
69
- for (const template of templatesToProcess) {
70
- const pagesJsonPath = path.join(template.path, "pages.json");
71
- let requiredBlockTypes = extractBlockTypesFromPagesJson(pagesJsonPath);
72
- // Fallback: if no pages.json, load from config.ts
73
- if (requiredBlockTypes.length === 0) {
74
- requiredBlockTypes = extractBlockTypesFromConfig(template.path, process.cwd());
75
- }
76
- if (requiredBlockTypes.length > 0) {
77
- // Find which blocks exist in the project
78
- const availableBlocks = findProjectBlocks(requiredBlockTypes);
79
- // Check which blocks are not already in the packages list
80
- const existingBlockNames = packages
81
- .filter((p) => p.type === "block")
82
- .map((p) => p.name);
83
- const missingBlocks = availableBlocks.filter((b) => !existingBlockNames.includes(b));
84
- if (missingBlocks.length > 0) {
85
- console.log(chalk.cyan(`\n📦 Auto-detected dependencies for ${template.name}:\n`));
86
- missingBlocks.forEach((b) => console.log(chalk.gray(` • ${b}`)));
87
- console.log("");
88
- // Scan and add missing blocks
89
- const dependencyPackages = await scanPackages(missingBlocks, {
90
- ...options,
91
- all: false,
92
- });
93
- // Insert dependencies BEFORE the template
94
- const templateIndex = packages.findIndex((p) => p.name === template.name);
95
- packages = [
96
- ...packages.slice(0, templateIndex),
97
- ...dependencyPackages,
98
- ...packages.slice(templateIndex),
99
- ];
100
- }
101
- }
102
- }
103
- }
104
- if (packages.length === 0) {
105
- console.log(chalk.yellow("⚠ No packages found to publish\n"));
106
- if (packageNames.length > 0) {
107
- console.log(chalk.gray("Packages specified:"));
108
- packageNames.forEach((name) => console.log(chalk.gray(` • ${name}`)));
109
- }
110
- return;
111
- }
112
- // Show current versions
113
- console.log(chalk.cyan("Current versions:\n"));
114
- packages.forEach((pkg) => {
115
- console.log(chalk.white(` ${pkg.packageJson.name}: ${chalk.bold(pkg.packageJson.version)}`));
116
- });
117
- console.log("");
118
- // Version bumping - interactive or from flags
119
- let bumpType = null;
120
- // --no-bump flag explicitly disables version bump
121
- if (options.bump === false) {
122
- bumpType = null;
123
- console.log(chalk.gray("Version bump disabled (--no-bump)\n"));
124
- }
125
- else if (options.patch || options.minor || options.major) {
126
- // Use flag-based bump
127
- bumpType = options.patch ? "patch" : options.minor ? "minor" : "major";
128
- }
129
- else {
130
- // Interactive prompt - show calculated versions for first package as example
131
- const examplePkg = packages[0];
132
- const currentVersion = examplePkg.packageJson.version;
133
- const patchVersion = semver.inc(currentVersion, "patch");
134
- const minorVersion = semver.inc(currentVersion, "minor");
135
- const majorVersion = semver.inc(currentVersion, "major");
136
- const answer = await inquirer.prompt([
137
- {
138
- type: "list",
139
- name: "bumpType",
140
- message: "Select version bump:",
141
- choices: [
142
- {
143
- name: `Patch (${currentVersion} → ${patchVersion}) - Bug fixes`,
144
- value: "patch",
145
- },
146
- {
147
- name: `Minor (${currentVersion} → ${minorVersion}) - New features, backward compatible`,
148
- value: "minor",
149
- },
150
- {
151
- name: `Major (${currentVersion} → ${majorVersion}) - Breaking changes`,
152
- value: "major",
153
- },
154
- {
155
- name: "No version bump - publish current version",
156
- value: null,
157
- },
158
- ],
159
- },
160
- ]);
161
- bumpType = answer.bumpType;
162
- }
163
- // Apply version bump if selected
164
- if (bumpType) {
165
- console.log(chalk.cyan(`\nVersion bump: ${bumpType}\n`));
166
- for (const pkg of packages) {
167
- const oldVersion = pkg.packageJson.version;
168
- const newVersion = semver.inc(oldVersion, bumpType);
169
- if (!newVersion) {
170
- console.error(chalk.red(`✖ Invalid version for ${pkg.name}: ${oldVersion}\n`));
171
- continue;
172
- }
173
- pkg.packageJson.version = newVersion;
174
- // Update package.json
175
- const pkgPath = path.join(pkg.path, "package.json");
176
- fs.writeJsonSync(pkgPath, pkg.packageJson, { spaces: 2 });
177
- console.log(chalk.gray(` ${pkg.name}: ${oldVersion} → ${newVersion}`));
178
- }
179
- console.log("");
180
- }
181
- console.log(chalk.cyan(`Publishing ${packages.length} package(s):\n`));
182
- packages.forEach((pkg) => {
183
- console.log(chalk.white(` • ${pkg.packageJson.name} ${chalk.bold("v" + pkg.packageJson.version)}`));
184
- });
185
- console.log("");
186
- // Schema diff: compare local vs remote and warn about breaking changes
187
- {
188
- const blocksWithConfig = packages.filter((p) => p.type === "block" && p.blockConfig?.schema);
189
- if (blocksWithConfig.length > 0) {
190
- // Fetch remote blocks for schema comparison
191
- let remoteBlocks = [];
192
- try {
193
- const client = new GraphQLClient(config.apiUrl, {
194
- headers: {
195
- "Content-Type": "application/json",
196
- Authorization: `Bearer ${config.apiToken}`,
197
- "X-Workspace-ID": workspaceId,
198
- },
199
- });
200
- const result = await client.request(GET_WORKSPACE_BLOCKS_QUERY);
201
- remoteBlocks = result.workspaceBlocks || [];
202
- }
203
- catch (error) {
204
- console.warn(chalk.yellow(" ⚠ Could not fetch remote blocks; schema diff skipped."));
205
- if (error instanceof Error) {
206
- console.warn(chalk.gray(` ${error.message}`));
207
- }
208
- console.log("");
209
- }
210
- let hasAnyBreaking = false;
211
- for (const pkg of blocksWithConfig) {
212
- const blockType = extractBlockType(pkg.packageJson.name);
213
- const remote = remoteBlocks.find((b) => b.blockType === blockType);
214
- if (!remote || !remote.schemaFields?.length)
215
- continue;
216
- // Convert remote schemaFields array to Schema object
217
- const remoteSchema = {};
218
- for (const f of remote.schemaFields) {
219
- remoteSchema[f.key] = {
220
- type: f.type,
221
- label: f.label,
222
- required: f.required,
223
- defaultValue: f.defaultValue,
224
- };
225
- }
226
- const changes = diffSchema(pkg.blockConfig.schema, remoteSchema);
227
- if (changes.length === 0)
228
- continue;
229
- console.log(chalk.bold(` Schema changes for ${pkg.name}:\n`));
230
- for (const c of changes) {
231
- if (c.kind === "breaking") {
232
- console.log(chalk.red(` ⚠ BREAKING: ${c.message}`));
233
- }
234
- else {
235
- console.log(chalk.gray(` ℹ ${c.message}`));
236
- }
237
- }
238
- console.log("");
239
- if (hasBreakingChanges(changes)) {
240
- hasAnyBreaking = true;
241
- }
242
- }
243
- if (hasAnyBreaking && !options.dryRun && !options.force) {
244
- const answer = await inquirer.prompt([
245
- {
246
- type: "confirm",
247
- name: "proceed",
248
- message: "Breaking schema changes detected. Continue publishing?",
249
- default: false,
250
- },
251
- ]);
252
- if (!answer.proceed) {
253
- console.log(chalk.yellow("\nPublish cancelled.\n"));
254
- return;
255
- }
256
- }
257
- }
258
- }
259
- if (options.overwriteContent) {
260
- console.log(chalk.yellow(" ⚠ --overwrite-content will reset all page content to defaults\n"));
261
- }
262
- if (options.dryRun) {
263
- console.log(chalk.yellow("🔍 Dry run mode - nothing will be published\n"));
264
- return;
265
- }
266
- // --zip mode: package into ZIPs and upload
267
- if (options.zip) {
268
- console.log(chalk.cyan(`🏢 Target: Workspace (${workspaceId})\n` +
269
- " Mode: ZIP package + upload\n"));
270
- const outputDir = path.join(process.cwd(), "packages");
271
- await fs.ensureDir(outputDir);
272
- // Package all blocks into ZIPs
273
- for (const pkg of packages) {
274
- await packageResource({
275
- name: pkg.name,
276
- type: pkg.type,
277
- dir: pkg.path,
278
- packageJson: pkg.packageJson,
279
- }, outputDir);
280
- }
281
- // Upload all ZIPs
282
- let successCount = 0;
283
- let failCount = 0;
284
- for (const pkg of packages) {
285
- const version = pkg.packageJson.version || "1.0.0";
286
- const zipPath = path.join(outputDir, `${pkg.name}-${version}.zip`);
287
- const result = await uploadPackage(zipPath, workspaceId, config.apiUrl, config.apiToken);
288
- if (result.success) {
289
- successCount++;
290
- }
291
- else {
292
- failCount++;
293
- }
294
- }
295
- console.log("");
296
- if (failCount === 0) {
297
- console.log(chalk.green.bold(`✓ ${successCount} package(s) uploaded successfully\n`));
298
- }
299
- else {
300
- console.log(chalk.yellow(`⚠ ${successCount} succeeded, ${failCount} failed\n`));
301
- }
302
- return;
303
- }
304
- // Default: direct GraphQL publish
305
- console.log(chalk.cyan(`🏢 Target: Workspace (${workspaceId})\n` +
306
- " Status: Published directly\n"));
307
- // Publish each package
308
- let successCount = 0;
309
- let errorCount = 0;
310
- const publishedBlocks = [];
311
- for (const pkg of packages) {
312
- const spinner = ora(`Publishing ${pkg.packageJson.name} to workspace...`).start();
313
- try {
314
- await publishToWorkspace(pkg, workspaceId, config.apiToken, config.apiUrl, !options.overwriteContent);
315
- spinner.succeed(chalk.green(`${pkg.packageJson.name} published to workspace`));
316
- successCount++;
317
- if (pkg.type === "block") {
318
- // Derive blockType same way as publishToWorkspace
319
- const blockType = pkg.packageJson.name
320
- .replace(/@[^/]+\//, "")
321
- .replace(/^blocks\./, "");
322
- publishedBlocks.push({ name: pkg.name, blockType, path: pkg.path });
323
- }
324
- }
325
- catch (error) {
326
- spinner.fail(chalk.red(`✖ ${pkg.packageJson.name} failed`));
327
- // Extract detailed error information from GraphQL errors
328
- let errorMessage = error.message || "Unknown error";
329
- let errorCode = null;
330
- let isPlanLimitError = false;
331
- // graphql-request wraps errors in response.errors array
332
- if (error.response?.errors && error.response.errors.length > 0) {
333
- const graphqlError = error.response.errors[0];
334
- errorMessage = graphqlError.message;
335
- errorCode = graphqlError.extensions?.code || null;
336
- isPlanLimitError =
337
- errorCode === "PLAN_LIMIT_EXCEEDED" ||
338
- errorMessage.toLowerCase().includes("limit reached");
339
- // Show additional details for plan limit errors
340
- if (graphqlError.extensions?.resource) {
341
- console.error("");
342
- console.error(chalk.yellow.bold(" ⚠ Plan Limit Reached"));
343
- console.error(chalk.yellow(` Resource: ${graphqlError.extensions.resource}`));
344
- if (graphqlError.extensions.current !== undefined) {
345
- console.error(chalk.yellow(` Usage: ${graphqlError.extensions.current}/${graphqlError.extensions.limit}`));
346
- }
347
- if (graphqlError.extensions.plan) {
348
- console.error(chalk.yellow(` Plan: ${graphqlError.extensions.plan}`));
349
- }
350
- console.error(chalk.gray(" Upgrade your plan at: https://cmssy.com/pricing"));
351
- console.error("");
352
- }
353
- }
354
- // Show error message prominently
355
- if (isPlanLimitError) {
356
- console.error(chalk.red.bold(` ${errorMessage}`));
357
- if (errorCode) {
358
- console.error(chalk.gray(` Error code: ${errorCode}`));
359
- }
360
- }
361
- else {
362
- console.error(chalk.red(` Error: ${errorMessage}`));
363
- if (errorCode) {
364
- console.error(chalk.gray(` Code: ${errorCode}`));
365
- }
366
- }
367
- console.error("");
368
- errorCount++;
369
- }
370
- }
371
- console.log("");
372
- if (errorCount === 0) {
373
- console.log(chalk.green.bold(`✓ ${successCount} package(s) published successfully\n`));
374
- }
375
- else {
376
- console.log(chalk.yellow(`⚠ ${successCount} succeeded, ${errorCount} failed\n`));
377
- }
378
- // Publish theme if present when using --all
379
- if (options.all) {
380
- const themeConfig = await scanTheme();
381
- if (themeConfig) {
382
- const spinner = ora("Publishing theme to workspace...").start();
383
- try {
384
- const themeClient = new GraphQLClient(config.apiUrl, {
385
- headers: {
386
- "Content-Type": "application/json",
387
- Authorization: `Bearer ${config.apiToken}`,
388
- "X-Workspace-ID": workspaceId,
389
- },
390
- });
391
- const themeInput = convertThemeToInput(themeConfig);
392
- await themeClient.request(UPDATE_THEME_MUTATION, { input: themeInput });
393
- spinner.succeed(chalk.green(`Theme "${themeConfig.name}" published to workspace`));
394
- }
395
- catch (error) {
396
- const msg = error?.response?.errors?.[0]?.message ?? error.message;
397
- if (msg?.includes("paid plan") || msg?.includes("PLAN_LIMIT")) {
398
- spinner.warn(chalk.yellow("Theme publish skipped: theme customization requires a paid plan"));
399
- }
400
- else {
401
- spinner.fail(chalk.red(`Theme publish failed: ${msg}`));
402
- }
403
- }
404
- }
405
- }
406
- }
407
- /**
408
- * Extract block type names from pages.json (pages + layout positions)
409
- * Supports all layout positions: header, footer, sidebar_left, sidebar_right, top, bottom
410
- * @example "@cmssy-marketing/blocks.hero" -> "hero"
411
- */
412
- function extractBlockTypesFromPagesJson(pagesJsonPath) {
413
- if (!fs.existsSync(pagesJsonPath)) {
414
- return [];
415
- }
416
- const pagesData = fs.readJsonSync(pagesJsonPath);
417
- const blockTypes = new Set();
418
- // Extract from pages
419
- for (const page of pagesData.pages || []) {
420
- for (const block of page.blocks || []) {
421
- if (block.type) {
422
- // Convert full name to simple type: "@scope/blocks.hero" -> "hero"
423
- let blockType = block.type;
424
- if (blockType.includes("/")) {
425
- blockType = blockType.split("/").pop();
426
- }
427
- if (blockType.startsWith("blocks.")) {
428
- blockType = blockType.substring(7);
429
- }
430
- blockTypes.add(blockType);
431
- }
432
- }
433
- }
434
- // Extract from global layoutPositions
435
- for (const [_position, data] of Object.entries(pagesData.layoutPositions || {})) {
436
- if (data.type) {
437
- let blockType = data.type;
438
- if (blockType.includes("/")) {
439
- blockType = blockType.split("/").pop();
440
- }
441
- if (blockType.startsWith("blocks.")) {
442
- blockType = blockType.substring(7);
443
- }
444
- blockTypes.add(blockType);
445
- }
446
- }
447
- // Extract from per-page layoutPositions
448
- for (const page of pagesData.pages || []) {
449
- for (const [_position, data] of Object.entries(page.layoutPositions || {})) {
450
- if (data.type) {
451
- let blockType = data.type;
452
- if (blockType.includes("/")) {
453
- blockType = blockType.split("/").pop();
454
- }
455
- if (blockType.startsWith("blocks.")) {
456
- blockType = blockType.substring(7);
457
- }
458
- blockTypes.add(blockType);
459
- }
460
- }
461
- }
462
- return Array.from(blockTypes);
463
- }
464
- /**
465
- * Find which blocks from a list exist in the project's blocks/ directory
466
- */
467
- function findProjectBlocks(blockTypes) {
468
- const blocksDir = path.join(process.cwd(), "blocks");
469
- if (!fs.existsSync(blocksDir)) {
470
- return [];
471
- }
472
- const existingBlocks = fs
473
- .readdirSync(blocksDir, { withFileTypes: true })
474
- .filter((d) => d.isDirectory())
475
- .map((d) => d.name);
476
- return blockTypes.filter((bt) => existingBlocks.includes(bt));
477
- }
478
- /**
479
- * Extract block types from config.ts (fallback when pages.json is missing).
480
- */
481
- function extractBlockTypesFromConfig(templatePath, projectRoot) {
482
- const config = loadTemplateConfig(templatePath, projectRoot);
483
- if (!config)
484
- return [];
485
- const blockTypes = new Set();
486
- // Extract from pages
487
- for (const page of config.pages || []) {
488
- for (const block of page.blocks || []) {
489
- if (block.type) {
490
- blockTypes.add(extractBlockType(block.type));
491
- }
492
- }
493
- // Per-page layout positions
494
- if (Array.isArray(page.layoutPositions)) {
495
- for (const lp of page.layoutPositions) {
496
- if (lp.type)
497
- blockTypes.add(extractBlockType(lp.type));
498
- }
499
- }
500
- }
501
- // Extract from global layoutPositions (array format from defineTemplate)
502
- if (Array.isArray(config.layoutPositions)) {
503
- for (const lp of config.layoutPositions) {
504
- if (lp.type)
505
- blockTypes.add(extractBlockType(lp.type));
506
- }
507
- }
508
- return Array.from(blockTypes);
509
- }
510
- async function scanPackages(packageNames, options) {
511
- const packages = [];
512
- // Scan blocks
513
- const blocksDir = path.join(process.cwd(), "blocks");
514
- if (fs.existsSync(blocksDir)) {
515
- const blockDirs = fs
516
- .readdirSync(blocksDir, { withFileTypes: true })
517
- .filter((dirent) => dirent.isDirectory())
518
- .map((dirent) => dirent.name);
519
- for (const blockName of blockDirs) {
520
- // Filter: --all OR packageNames includes this block
521
- if (!options.all && !packageNames.includes(blockName)) {
522
- continue;
523
- }
524
- const blockPath = path.join(blocksDir, blockName);
525
- const pkgPath = path.join(blockPath, "package.json");
526
- if (!fs.existsSync(pkgPath)) {
527
- console.warn(chalk.yellow(`Warning: ${blockName} has no package.json, skipping`));
528
- continue;
529
- }
530
- const packageJson = fs.readJsonSync(pkgPath);
531
- // Load config.ts
532
- const blockConfig = await loadBlockConfig(blockPath);
533
- if (!blockConfig && !packageJson.cmssy) {
534
- console.warn(chalk.yellow(`Warning: ${blockName} has no config.ts or package.json cmssy section, skipping`));
535
- continue;
536
- }
537
- packages.push({
538
- type: "block",
539
- name: blockName,
540
- path: blockPath,
541
- packageJson,
542
- blockConfig,
543
- });
544
- }
545
- }
546
- // Scan templates
547
- const templatesDir = path.join(process.cwd(), "templates");
548
- if (fs.existsSync(templatesDir)) {
549
- const templateDirs = fs
550
- .readdirSync(templatesDir, { withFileTypes: true })
551
- .filter((dirent) => dirent.isDirectory())
552
- .map((dirent) => dirent.name);
553
- for (const templateName of templateDirs) {
554
- // Filter: --all OR packageNames includes this template
555
- if (!options.all && !packageNames.includes(templateName)) {
556
- continue;
557
- }
558
- const templatePath = path.join(templatesDir, templateName);
559
- const pkgPath = path.join(templatePath, "package.json");
560
- if (!fs.existsSync(pkgPath)) {
561
- console.warn(chalk.yellow(`Warning: ${templateName} has no package.json, skipping`));
562
- continue;
563
- }
564
- const packageJson = fs.readJsonSync(pkgPath);
565
- // Load config.ts
566
- const blockConfig = await loadBlockConfig(templatePath);
567
- if (!blockConfig && !packageJson.cmssy) {
568
- console.warn(chalk.yellow(`Warning: ${templateName} has no config.ts or package.json cmssy section, skipping`));
569
- continue;
570
- }
571
- packages.push({
572
- type: "template",
573
- name: templateName,
574
- path: templatePath,
575
- packageJson,
576
- blockConfig,
577
- });
578
- }
579
- }
580
- return packages;
581
- }
582
- // Bundle source code with esbuild (combines all local imports into single
583
- // file). CommonJS format to avoid ES module export statements.
584
- async function bundleSourceCode(packagePath, serverActionFiles, serverActionNames) {
585
- const { build } = await import("esbuild");
586
- const srcDir = path.join(packagePath, "src");
587
- const tsxPath = path.join(srcDir, "index.tsx");
588
- const tsPath = path.join(srcDir, "index.ts");
589
- let entryPoint;
590
- if (fs.existsSync(tsxPath)) {
591
- entryPoint = tsxPath;
592
- }
593
- else if (fs.existsSync(tsPath)) {
594
- entryPoint = tsPath;
595
- }
596
- else {
597
- throw new Error(`Source code not found. Expected ${tsxPath} or ${tsPath}`);
598
- }
599
- // If server action files exist, use esbuild plugin to replace their
600
- // contents with stubs that call globalThis.__cmssyCallAction (CMS-224)
601
- const plugins = serverActionFiles?.length && serverActionNames?.length
602
- ? [createServerActionStubPlugin(serverActionFiles, serverActionNames)]
603
- : [];
604
- const result = await build({
605
- entryPoints: [entryPoint],
606
- bundle: true,
607
- write: false,
608
- format: "cjs", // CommonJS format (module.exports) - compatible with SSR VM
609
- platform: "browser", // Browser platform to avoid Node.js globals like 'process'
610
- jsx: "transform", // Transform JSX to React.createElement
611
- loader: { ".tsx": "tsx", ".ts": "ts", ".css": "empty" },
612
- external: [
613
- "react",
614
- "react-dom",
615
- "react/jsx-runtime",
616
- "next/image",
617
- "next/link",
618
- "next/font",
619
- "next/script",
620
- ],
621
- minify: true, // Minify for smaller bundle size
622
- define: {
623
- // Replace process.env references with static values
624
- "process.env.NODE_ENV": '"production"',
625
- },
626
- plugins,
627
- });
628
- const bundledCode = result.outputFiles[0].text;
629
- return bundledCode;
630
- }
631
- /**
632
- * Detect files with "use server" directive in block's src/ directory.
633
- * Only file-level directives are detected (top of file).
634
- */
635
- function detectServerActionFiles(packagePath) {
636
- const srcDir = path.join(packagePath, "src");
637
- if (!fs.existsSync(srcDir))
638
- return [];
639
- const actionFiles = [];
640
- const files = fs.readdirSync(srcDir).filter((f) => /\.(ts|tsx)$/.test(f));
641
- for (const file of files) {
642
- const filePath = path.join(srcDir, file);
643
- const content = fs.readFileSync(filePath, "utf-8");
644
- const lines = content.split("\n");
645
- for (const line of lines) {
646
- const trimmed = line.trim();
647
- if (trimmed === "" ||
648
- trimmed.startsWith("//") ||
649
- trimmed.startsWith("/*"))
650
- continue;
651
- if (trimmed === '"use server"' ||
652
- trimmed === "'use server'" ||
653
- trimmed === '"use server";' ||
654
- trimmed === "'use server';") {
655
- actionFiles.push(filePath);
656
- }
657
- break;
658
- }
659
- }
660
- return actionFiles;
661
- }
662
- /**
663
- * Bundle server action files separately for server-side execution.
664
- * Returns the bundled code and list of exported action function names.
665
- */
666
- async function bundleServerActions(actionFiles) {
667
- const { build } = await import("esbuild");
668
- const result = await build({
669
- entryPoints: actionFiles,
670
- bundle: true,
671
- write: false,
672
- format: "cjs",
673
- platform: "node",
674
- loader: { ".tsx": "tsx", ".ts": "ts" },
675
- external: ["react", "react-dom", "react/jsx-runtime"],
676
- minify: true,
677
- metafile: true,
678
- define: {
679
- "process.env.NODE_ENV": '"production"',
680
- },
681
- });
682
- const code = result.outputFiles[0].text;
683
- const actionNames = [];
684
- if (result.metafile) {
685
- for (const output of Object.values(result.metafile.outputs)) {
686
- if (output.exports) {
687
- actionNames.push(...output.exports.filter((e) => e !== "default" && e !== "__esModule"));
688
- }
689
- }
690
- }
691
- return { code, actionNames };
692
- }
693
- /**
694
- * Create an esbuild plugin that replaces "use server" file contents
695
- * with client-side stubs that call globalThis.__cmssyCallAction.
696
- */
697
- function createServerActionStubPlugin(actionFiles, actionNames) {
698
- const actionFileSet = new Set(actionFiles.map((f) => path.resolve(f)));
699
- return {
700
- name: "server-action-stub",
701
- setup(build) {
702
- build.onLoad({ filter: /\.(ts|tsx)$/ }, (args) => {
703
- if (!actionFileSet.has(path.resolve(args.path)))
704
- return null;
705
- const stubs = actionNames
706
- .map((name) => `module.exports.${name} = function() { return globalThis.__cmssyCallAction("${name}", Array.prototype.slice.call(arguments)); };`)
707
- .join("\n");
708
- return {
709
- contents: `"use strict";\n${stubs}`,
710
- loader: "js",
711
- };
712
- });
713
- },
714
- };
715
- }
716
- // Compile CSS with optional Tailwind support
717
- async function compileCss(packagePath, bundledSourceCode) {
718
- const srcDir = path.join(packagePath, "src");
719
- const cssPath = path.join(srcDir, "index.css");
720
- if (!fs.existsSync(cssPath)) {
721
- return undefined;
722
- }
723
- let cssContent = fs.readFileSync(cssPath, "utf-8");
724
- // If no Tailwind/PostCSS imports, return raw CSS
725
- if (!cssContent.includes("@import") && !cssContent.includes("@tailwind")) {
726
- return cssContent;
727
- }
728
- // Load PostCSS from project
729
- const { default: postcss } = await import("postcss");
730
- // Check for Tailwind v4 vs v3
731
- const projectRoot = process.cwd();
732
- const projectPackageJson = fs.readJsonSync(path.join(projectRoot, "package.json"));
733
- const hasTailwindV4 = !!(projectPackageJson.devDependencies?.["@tailwindcss/postcss"] ||
734
- projectPackageJson.dependencies?.["@tailwindcss/postcss"]);
735
- if (hasTailwindV4) {
736
- // Tailwind v4: @tailwindcss/postcss handles @import itself, no postcss-import needed
737
- const tailwindV4Path = path.join(projectRoot, "node_modules", "@tailwindcss/postcss", "dist", "index.mjs");
738
- const tailwindV4Module = await import(tailwindV4Path);
739
- const tailwindPlugin = tailwindV4Module.default || tailwindV4Module;
740
- const result = await postcss([tailwindPlugin]).process(cssContent, {
741
- from: cssPath,
742
- });
743
- return result.css;
744
- }
745
- else {
746
- // Tailwind v3: needs postcss-import + tailwindcss
747
- const postcssImportPath = path.join(projectRoot, "node_modules", "postcss-import", "index.js");
748
- const { default: postcssImport } = await import(postcssImportPath);
749
- const importPlugin = postcssImport({
750
- path: [path.join(projectRoot, "styles")],
751
- });
752
- cssContent = cssContent.replace(/@import\s+["']tailwindcss["'];?/g, "@tailwind base;\n@tailwind components;\n@tailwind utilities;");
753
- const tailwindcssPath = path.join(projectRoot, "node_modules", "tailwindcss", "lib", "index.js");
754
- const tailwindcssModule = await import(tailwindcssPath);
755
- const tailwindcss = tailwindcssModule.default || tailwindcssModule;
756
- const tailwindPlugin = tailwindcss({
757
- content: [{ raw: bundledSourceCode, extension: "tsx" }],
758
- });
759
- const result = await postcss([importPlugin, tailwindPlugin]).process(cssContent, {
760
- from: cssPath,
761
- });
762
- return result.css;
763
- }
764
- }
765
- async function publishToWorkspace(pkg, workspaceId, apiToken, apiUrl, preserveContent) {
766
- const { packageJson, path: packagePath, blockConfig, type: packageType, } = pkg;
767
- // Use blockConfig if available, fallback to package.json cmssy
768
- const metadata = blockConfig || packageJson.cmssy || {};
769
- // Validate schema + defaultValue types at publish time
770
- if (blockConfig?.schema) {
771
- const schemaResult = await validateSchema(blockConfig.schema, packagePath);
772
- const defaultsResult = validateDefaultValues(blockConfig.schema);
773
- const allErrors = [
774
- ...schemaResult.errors.map((e) => `[schema] ${e}`),
775
- ...defaultsResult.errors.map((e) => `[defaultValue] ${e}`),
776
- ];
777
- if (allErrors.length > 0) {
778
- throw new Error(`Validation failed for ${packageJson.name}:\n${allErrors.join("\n")}`);
779
- }
780
- }
781
- // Generate block_type from package name
782
- // @cmssy/blocks.hero -> hero
783
- const blockType = packageJson.name
784
- .replace(/@[^/]+\//, "")
785
- .replace(/^blocks\./, "")
786
- .replace(/^templates\./, "");
787
- // Templates have no source code — skip compilation
788
- let bundledSourceCode;
789
- let compiledCss;
790
- let serverActionCode;
791
- let serverActionNames = [];
792
- if (packageType !== "template") {
793
- // Detect "use server" files (CMS-224)
794
- const actionFiles = detectServerActionFiles(packagePath);
795
- if (actionFiles.length > 0) {
796
- const actionBundle = await bundleServerActions(actionFiles);
797
- serverActionCode = actionBundle.code;
798
- serverActionNames = actionBundle.actionNames;
799
- console.log(chalk.cyan(` ⚡ Server actions detected: ${serverActionNames.join(", ")}`));
800
- }
801
- // Bundle source code (combines all local imports into single CJS file)
802
- // If server actions exist, stubs replace "use server" file contents
803
- bundledSourceCode = await bundleSourceCode(packagePath, actionFiles.length > 0 ? actionFiles : undefined, serverActionNames.length > 0 ? serverActionNames : undefined);
804
- // Compile CSS (with Tailwind if needed)
805
- compiledCss = await compileCss(packagePath, bundledSourceCode);
806
- }
807
- // Convert config.ts schema to schemaFields if using blockConfig
808
- let schemaFields = metadata.schemaFields || [];
809
- if (blockConfig && blockConfig.schema) {
810
- schemaFields = convertSchemaToFields(blockConfig.schema);
811
- }
812
- // Build input with inline sourceCode and cssCode
813
- // Backend will handle uploading to Blob Storage
814
- const input = {
815
- blockType,
816
- name: metadata.displayName || metadata.name || packageJson.name,
817
- description: packageJson.description || metadata.description || "",
818
- icon: metadata.icon || "Blocks",
819
- category: metadata.category || "Custom",
820
- sourceCode: bundledSourceCode,
821
- cssCode: compiledCss,
822
- schemaFields,
823
- defaultContent: extractDefaultContent(blockConfig?.schema || {}),
824
- sourceRegistry: "local",
825
- sourceItem: packageJson.name,
826
- version: packageJson.version || "1.0.0",
827
- packageType, // "block" or "template"
828
- preserveContent,
829
- // Server action support (CMS-224)
830
- serverActionCode: serverActionCode || undefined,
831
- serverActions: serverActionNames.length > 0 ? serverActionNames : undefined,
832
- };
833
- // Add layoutPosition if defined (for layout blocks)
834
- if (blockConfig?.layoutPosition) {
835
- input.layoutPosition = blockConfig.layoutPosition;
836
- }
837
- // Add requires if defined
838
- if (blockConfig?.requires) {
839
- input.requires = blockConfig.requires;
840
- }
841
- // Hydration strategy — false (default) keeps the block server-only.
842
- if (blockConfig?.useClient !== undefined) {
843
- input.useClient = blockConfig.useClient;
844
- }
845
- // Check if this is a template with pages data (pages.json or config.ts)
846
- const isTemplateType = packageType === "template";
847
- const pagesJsonPath = path.join(packagePath, "pages.json");
848
- const hasPagesJson = isTemplateType && fs.existsSync(pagesJsonPath);
849
- // Fallback: load from config.ts if no pages.json
850
- let hasTemplateData = hasPagesJson;
851
- let configPagesData = null;
852
- if (isTemplateType && !hasPagesJson) {
853
- const templateConfig = loadTemplateConfig(packagePath, process.cwd());
854
- if (templateConfig &&
855
- (templateConfig.pages || templateConfig.layoutPositions)) {
856
- configPagesData = convertConfigToPagesData(templateConfig);
857
- hasTemplateData = true;
858
- }
859
- }
860
- // Create client with workspace header
861
- const client = new GraphQLClient(apiUrl, {
862
- headers: {
863
- "Content-Type": "application/json",
864
- Authorization: `Bearer ${apiToken}`,
865
- "X-Workspace-ID": workspaceId,
866
- },
867
- });
868
- // Send mutation with timeout using Promise.race
869
- const TIMEOUT_MS = 180000; // 3 minutes
870
- let timeoutId;
871
- const timeoutPromise = new Promise((_, reject) => {
872
- timeoutId = setTimeout(() => {
873
- reject(new Error("Block upload timed out after 3 minutes. This may be due to:\n" +
874
- " - Large file size (try reducing bundle size)\n" +
875
- " - Slow network connection\n" +
876
- " - Backend processing issues\n" +
877
- "Check backend logs for more details."));
878
- }, TIMEOUT_MS);
879
- });
880
- // Use different mutation for templates with pages
881
- if (hasTemplateData) {
882
- // Load pages data from pages.json or config.ts fallback
883
- const pagesData = hasPagesJson
884
- ? fs.readJsonSync(pagesJsonPath)
885
- : configPagesData;
886
- // Convert to mutation input format
887
- // IMPORTANT: Convert full block names to simple types
888
- // "@cmssy-marketing/blocks.hero" -> "hero"
889
- const pages = (pagesData.pages || []).map((page) => {
890
- // Normalize slug: homepage = "/", others strip leading slashes
891
- const slug = page.slug === "/" ? "/" : page.slug.replace(/^\/+/, "");
892
- const result = {
893
- name: page.name,
894
- slug,
895
- blocks: (page.blocks || []).map((block) => ({
896
- type: extractBlockType(block.type),
897
- content: block.content || {},
898
- })),
899
- };
900
- // Pass pageType if defined (e.g. "post" for blog post pages)
901
- if (page.pageType) {
902
- result.pageType = page.pageType;
903
- }
904
- // Pass explicit parentSlug if defined (normalize: strip leading slashes)
905
- if (page.parentSlug) {
906
- result.parentSlug = page.parentSlug.replace(/^\/+/, "");
907
- }
908
- // Per-page layout positions (e.g. sidebar_left only on /docs)
909
- if (page.layoutPositions) {
910
- // Support both object format (pages.json) and array format (config.ts)
911
- const lpEntries = Array.isArray(page.layoutPositions)
912
- ? page.layoutPositions.map((lp) => [lp.position, lp])
913
- : Object.entries(page.layoutPositions);
914
- result.layoutPositions = lpEntries.map(([position, data]) => ({
915
- position,
916
- type: extractBlockType(data.type),
917
- content: data.content || {},
918
- }));
919
- }
920
- return result;
921
- });
922
- // Convert layoutPositions — support both object (pages.json) and array (config.ts) formats
923
- const rawLayoutPositions = pagesData.layoutPositions || {};
924
- const layoutEntries = Array.isArray(rawLayoutPositions)
925
- ? rawLayoutPositions.map((lp) => [lp.position, lp])
926
- : Object.entries(rawLayoutPositions);
927
- const layoutPositions = layoutEntries.map(([position, data]) => ({
928
- position,
929
- type: extractBlockType(data.type),
930
- content: data.content || {},
931
- }));
932
- // Extract unique block types required by this template
933
- const requiredBlockTypes = new Set();
934
- for (const page of pages) {
935
- for (const block of page.blocks) {
936
- requiredBlockTypes.add(block.type);
937
- }
938
- // Include per-page layout position block types
939
- if (page.layoutPositions) {
940
- for (const lp of page.layoutPositions) {
941
- requiredBlockTypes.add(lp.type);
942
- }
943
- }
944
- }
945
- for (const lp of layoutPositions) {
946
- requiredBlockTypes.add(lp.type);
947
- }
948
- // Add pages, layoutPositions, and requiredBlocks to input
949
- input.pages = pages;
950
- input.layoutPositions = layoutPositions;
951
- input.requiredBlocks = Array.from(requiredBlockTypes);
952
- // Add pageTypes if defined in template config
953
- const pageTypes = pagesData?.pageTypes;
954
- if (Array.isArray(pageTypes) && pageTypes.length > 0) {
955
- input.pageTypes = pageTypes;
956
- }
957
- // Remove fields not supported by ImportTemplateInput
958
- // (these are only for blocks, not templates)
959
- delete input.packageType;
960
- delete input.sourceCode;
961
- delete input.cssCode;
962
- const requestPromise = client.request(IMPORT_TEMPLATE_MUTATION, { input });
963
- try {
964
- const result = await Promise.race([requestPromise, timeoutPromise]);
965
- clearTimeout(timeoutId);
966
- if (!result.importTemplate?.success) {
967
- throw new Error(result.importTemplate?.message ||
968
- "Failed to import template to workspace");
969
- }
970
- // Log template import summary
971
- const { pagesCreated, pagesUpdated } = result.importTemplate;
972
- console.log(chalk.gray(` └─ ${pagesCreated} pages created, ${pagesUpdated} updated`));
973
- }
974
- catch (error) {
975
- clearTimeout(timeoutId);
976
- throw error;
977
- }
978
- }
979
- else {
980
- // Standard block import — remove fields not in ImportBlockInput
981
- delete input.preserveContent;
982
- delete input.packageType;
983
- const requestPromise = client.request(IMPORT_BLOCK_MUTATION, { input });
984
- try {
985
- const result = await Promise.race([requestPromise, timeoutPromise]);
986
- clearTimeout(timeoutId);
987
- if (!result.importBlock) {
988
- throw new Error("Failed to import block to workspace");
989
- }
990
- }
991
- catch (error) {
992
- clearTimeout(timeoutId);
993
- throw error;
994
- }
995
- }
996
- }
997
- // Re-export from publish-helpers (single source of truth)
998
- // DO NOT duplicate convertSchemaToFields here
999
- import { convertSchemaToFields } from "../utils/publish-helpers.js";
1000
- // Single source of truth shared with publish-block-buildtime so the
1001
- // two publish paths can't drift on what "default content" means.
1002
- import { extractDefaultContent } from "../utils/block-config.js";
1003
- //# sourceMappingURL=publish.js.map