@cmssy/cli 0.22.4 → 9.1.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.
- package/dist/index.js +435 -0
- package/package.json +25 -86
- package/LICENSE +0 -21
- package/README.md +0 -692
- package/config.d.ts +0 -3
- package/config.js +0 -4
- package/dist/cli.d.ts +0 -3
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -340
- package/dist/cli.js.map +0 -1
- package/dist/commands/add-source.d.ts +0 -13
- package/dist/commands/add-source.d.ts.map +0 -1
- package/dist/commands/add-source.js +0 -273
- package/dist/commands/add-source.js.map +0 -1
- package/dist/commands/build.d.ts +0 -7
- package/dist/commands/build.d.ts.map +0 -1
- package/dist/commands/build.js +0 -127
- package/dist/commands/build.js.map +0 -1
- package/dist/commands/codegen.d.ts +0 -7
- package/dist/commands/codegen.d.ts.map +0 -1
- package/dist/commands/codegen.js +0 -150
- package/dist/commands/codegen.js.map +0 -1
- package/dist/commands/configure.d.ts +0 -6
- package/dist/commands/configure.d.ts.map +0 -1
- package/dist/commands/configure.js +0 -39
- package/dist/commands/configure.js.map +0 -1
- package/dist/commands/create.d.ts +0 -18
- package/dist/commands/create.d.ts.map +0 -1
- package/dist/commands/create.js +0 -403
- package/dist/commands/create.js.map +0 -1
- package/dist/commands/dev.d.ts +0 -6
- package/dist/commands/dev.d.ts.map +0 -1
- package/dist/commands/dev.js +0 -144
- package/dist/commands/dev.js.map +0 -1
- package/dist/commands/doctor.d.ts +0 -2
- package/dist/commands/doctor.d.ts.map +0 -1
- package/dist/commands/doctor.js +0 -230
- package/dist/commands/doctor.js.map +0 -1
- package/dist/commands/init.d.ts +0 -6
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/init.js +0 -414
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/lib.d.ts +0 -17
- package/dist/commands/lib.d.ts.map +0 -1
- package/dist/commands/lib.js +0 -132
- package/dist/commands/lib.js.map +0 -1
- package/dist/commands/link.d.ts +0 -8
- package/dist/commands/link.d.ts.map +0 -1
- package/dist/commands/link.js +0 -116
- package/dist/commands/link.js.map +0 -1
- package/dist/commands/migrate.d.ts +0 -2
- package/dist/commands/migrate.d.ts.map +0 -1
- package/dist/commands/migrate.js +0 -231
- package/dist/commands/migrate.js.map +0 -1
- package/dist/commands/package.d.ts +0 -14
- package/dist/commands/package.d.ts.map +0 -1
- package/dist/commands/package.js +0 -137
- package/dist/commands/package.js.map +0 -1
- package/dist/commands/publish-block-buildtime.d.ts +0 -8
- package/dist/commands/publish-block-buildtime.d.ts.map +0 -1
- package/dist/commands/publish-block-buildtime.js +0 -290
- package/dist/commands/publish-block-buildtime.js.map +0 -1
- package/dist/commands/publish-template.d.ts +0 -22
- package/dist/commands/publish-template.d.ts.map +0 -1
- package/dist/commands/publish-template.js +0 -345
- package/dist/commands/publish-template.js.map +0 -1
- package/dist/commands/publish.d.ts +0 -15
- package/dist/commands/publish.d.ts.map +0 -1
- package/dist/commands/publish.js +0 -1003
- package/dist/commands/publish.js.map +0 -1
- package/dist/commands/skills.d.ts +0 -11
- package/dist/commands/skills.d.ts.map +0 -1
- package/dist/commands/skills.js +0 -178
- package/dist/commands/skills.js.map +0 -1
- package/dist/commands/sync.d.ts +0 -6
- package/dist/commands/sync.d.ts.map +0 -1
- package/dist/commands/sync.js +0 -208
- package/dist/commands/sync.js.map +0 -1
- package/dist/commands/test.d.ts +0 -8
- package/dist/commands/test.d.ts.map +0 -1
- package/dist/commands/test.js +0 -60
- package/dist/commands/test.js.map +0 -1
- package/dist/commands/upload.d.ts +0 -11
- package/dist/commands/upload.d.ts.map +0 -1
- package/dist/commands/upload.js +0 -128
- package/dist/commands/upload.js.map +0 -1
- package/dist/commands/workspaces.d.ts +0 -2
- package/dist/commands/workspaces.d.ts.map +0 -1
- package/dist/commands/workspaces.js +0 -62
- package/dist/commands/workspaces.js.map +0 -1
- package/dist/dev-ui/app.js +0 -1284
- package/dist/dev-ui/index.html +0 -1511
- package/dist/dev-ui-react/App.tsx +0 -164
- package/dist/dev-ui-react/__tests__/previewData.test.ts +0 -193
- package/dist/dev-ui-react/components/BlocksList.tsx +0 -232
- package/dist/dev-ui-react/components/Editor.tsx +0 -469
- package/dist/dev-ui-react/components/Preview.tsx +0 -146
- package/dist/dev-ui-react/hooks/useBlocks.ts +0 -80
- package/dist/dev-ui-react/index.html +0 -13
- package/dist/dev-ui-react/main.tsx +0 -8
- package/dist/dev-ui-react/styles.css +0 -856
- package/dist/dev-ui-react/types.ts +0 -45
- package/dist/skills/claude/cmssy-block.md +0 -454
- package/dist/skills/claude/cmssy-mcp-content.md +0 -235
- package/dist/test-helpers/index.d.ts +0 -23
- package/dist/test-helpers/index.d.ts.map +0 -1
- package/dist/test-helpers/index.js +0 -50
- package/dist/test-helpers/index.js.map +0 -1
- package/dist/types/block-config.d.ts +0 -6
- package/dist/types/block-config.d.ts.map +0 -1
- package/dist/types/block-config.js +0 -12
- package/dist/types/block-config.js.map +0 -1
- package/dist/types/publish-job.d.ts +0 -61
- package/dist/types/publish-job.d.ts.map +0 -1
- package/dist/types/publish-job.js +0 -13
- package/dist/types/publish-job.js.map +0 -1
- package/dist/utils/api-error.d.ts +0 -10
- package/dist/utils/api-error.d.ts.map +0 -1
- package/dist/utils/api-error.js +0 -55
- package/dist/utils/api-error.js.map +0 -1
- package/dist/utils/block-config.d.ts +0 -87
- package/dist/utils/block-config.d.ts.map +0 -1
- package/dist/utils/block-config.js +0 -214
- package/dist/utils/block-config.js.map +0 -1
- package/dist/utils/blocks-meta-cache.d.ts +0 -28
- package/dist/utils/blocks-meta-cache.d.ts.map +0 -1
- package/dist/utils/blocks-meta-cache.js +0 -72
- package/dist/utils/blocks-meta-cache.js.map +0 -1
- package/dist/utils/builder.d.ts +0 -34
- package/dist/utils/builder.d.ts.map +0 -1
- package/dist/utils/builder.js +0 -140
- package/dist/utils/builder.js.map +0 -1
- package/dist/utils/cmssy-config.d.ts +0 -16
- package/dist/utils/cmssy-config.d.ts.map +0 -1
- package/dist/utils/cmssy-config.js +0 -19
- package/dist/utils/cmssy-config.js.map +0 -1
- package/dist/utils/config.d.ts +0 -9
- package/dist/utils/config.d.ts.map +0 -1
- package/dist/utils/config.js +0 -51
- package/dist/utils/config.js.map +0 -1
- package/dist/utils/dependency-check.d.ts +0 -21
- package/dist/utils/dependency-check.d.ts.map +0 -1
- package/dist/utils/dependency-check.js +0 -56
- package/dist/utils/dependency-check.js.map +0 -1
- package/dist/utils/dev-generator/api-routes/blocks.d.ts +0 -3
- package/dist/utils/dev-generator/api-routes/blocks.d.ts.map +0 -1
- package/dist/utils/dev-generator/api-routes/blocks.js +0 -194
- package/dist/utils/dev-generator/api-routes/blocks.js.map +0 -1
- package/dist/utils/dev-generator/api-routes/config.d.ts +0 -2
- package/dist/utils/dev-generator/api-routes/config.d.ts.map +0 -1
- package/dist/utils/dev-generator/api-routes/config.js +0 -107
- package/dist/utils/dev-generator/api-routes/config.js.map +0 -1
- package/dist/utils/dev-generator/api-routes/preview.d.ts +0 -2
- package/dist/utils/dev-generator/api-routes/preview.d.ts.map +0 -1
- package/dist/utils/dev-generator/api-routes/preview.js +0 -50
- package/dist/utils/dev-generator/api-routes/preview.js.map +0 -1
- package/dist/utils/dev-generator/api-routes/workspaces.d.ts +0 -2
- package/dist/utils/dev-generator/api-routes/workspaces.d.ts.map +0 -1
- package/dist/utils/dev-generator/api-routes/workspaces.js +0 -128
- package/dist/utils/dev-generator/api-routes/workspaces.js.map +0 -1
- package/dist/utils/dev-generator/helpers.d.ts +0 -23
- package/dist/utils/dev-generator/helpers.d.ts.map +0 -1
- package/dist/utils/dev-generator/helpers.js +0 -128
- package/dist/utils/dev-generator/helpers.js.map +0 -1
- package/dist/utils/dev-generator/home-page.d.ts +0 -2
- package/dist/utils/dev-generator/home-page.d.ts.map +0 -1
- package/dist/utils/dev-generator/home-page.js +0 -643
- package/dist/utils/dev-generator/home-page.js.map +0 -1
- package/dist/utils/dev-generator/index.d.ts +0 -11
- package/dist/utils/dev-generator/index.d.ts.map +0 -1
- package/dist/utils/dev-generator/index.js +0 -85
- package/dist/utils/dev-generator/index.js.map +0 -1
- package/dist/utils/dev-generator/layout.d.ts +0 -3
- package/dist/utils/dev-generator/layout.d.ts.map +0 -1
- package/dist/utils/dev-generator/layout.js +0 -63
- package/dist/utils/dev-generator/layout.js.map +0 -1
- package/dist/utils/dev-generator/next-config.d.ts +0 -3
- package/dist/utils/dev-generator/next-config.d.ts.map +0 -1
- package/dist/utils/dev-generator/next-config.js +0 -111
- package/dist/utils/dev-generator/next-config.js.map +0 -1
- package/dist/utils/dev-generator/preview-pages.d.ts +0 -3
- package/dist/utils/dev-generator/preview-pages.d.ts.map +0 -1
- package/dist/utils/dev-generator/preview-pages.js +0 -833
- package/dist/utils/dev-generator/preview-pages.js.map +0 -1
- package/dist/utils/dev-generator.d.ts +0 -12
- package/dist/utils/dev-generator.d.ts.map +0 -1
- package/dist/utils/dev-generator.js +0 -1752
- package/dist/utils/dev-generator.js.map +0 -1
- package/dist/utils/field-schema.d.ts +0 -13
- package/dist/utils/field-schema.d.ts.map +0 -1
- package/dist/utils/field-schema.js +0 -182
- package/dist/utils/field-schema.js.map +0 -1
- package/dist/utils/graphql.d.ts +0 -39
- package/dist/utils/graphql.d.ts.map +0 -1
- package/dist/utils/graphql.js +0 -138
- package/dist/utils/graphql.js.map +0 -1
- package/dist/utils/lib-manifest.d.ts +0 -7
- package/dist/utils/lib-manifest.d.ts.map +0 -1
- package/dist/utils/lib-manifest.js +0 -59
- package/dist/utils/lib-manifest.js.map +0 -1
- package/dist/utils/publish-helpers.d.ts +0 -43
- package/dist/utils/publish-helpers.d.ts.map +0 -1
- package/dist/utils/publish-helpers.js +0 -215
- package/dist/utils/publish-helpers.js.map +0 -1
- package/dist/utils/publish-helpers.test.d.ts +0 -2
- package/dist/utils/publish-helpers.test.d.ts.map +0 -1
- package/dist/utils/publish-helpers.test.js +0 -24
- package/dist/utils/publish-helpers.test.js.map +0 -1
- package/dist/utils/resolve-workspace.d.ts +0 -6
- package/dist/utils/resolve-workspace.d.ts.map +0 -1
- package/dist/utils/resolve-workspace.js +0 -32
- package/dist/utils/resolve-workspace.js.map +0 -1
- package/dist/utils/scanner.d.ts +0 -42
- package/dist/utils/scanner.d.ts.map +0 -1
- package/dist/utils/scanner.js +0 -156
- package/dist/utils/scanner.js.map +0 -1
- package/dist/utils/schema-diff.d.ts +0 -22
- package/dist/utils/schema-diff.d.ts.map +0 -1
- package/dist/utils/schema-diff.js +0 -107
- package/dist/utils/schema-diff.js.map +0 -1
- package/dist/utils/source-collector.d.ts +0 -19
- package/dist/utils/source-collector.d.ts.map +0 -1
- package/dist/utils/source-collector.js +0 -565
- package/dist/utils/source-collector.js.map +0 -1
- package/dist/utils/theme-builder.d.ts +0 -12
- package/dist/utils/theme-builder.d.ts.map +0 -1
- package/dist/utils/theme-builder.js +0 -117
- package/dist/utils/theme-builder.js.map +0 -1
- package/dist/utils/type-generator.d.ts +0 -9
- package/dist/utils/type-generator.d.ts.map +0 -1
- package/dist/utils/type-generator.js +0 -86
- package/dist/utils/type-generator.js.map +0 -1
- package/dist/utils/version.d.ts +0 -10
- package/dist/utils/version.d.ts.map +0 -1
- package/dist/utils/version.js +0 -30
- package/dist/utils/version.js.map +0 -1
- package/dist/utils/versions.d.ts.map +0 -1
- package/dist/utils/versions.js.map +0 -1
- package/src/dev-app/app/api/blocks/[name]/config/route.ts +0 -140
- package/src/dev-app/app/api/blocks/route.ts +0 -61
- package/src/dev-app/app/api/config/route.ts +0 -98
- package/src/dev-app/app/api/context/route.ts +0 -126
- package/src/dev-app/app/api/preview/[name]/route.ts +0 -137
- package/src/dev-app/app/api/workspaces/route.ts +0 -124
- package/src/dev-app/app/layout.tsx +0 -18
- package/src/dev-app/app/page.tsx +0 -1785
- package/test.d.ts +0 -1
- package/test.js +0 -2
package/dist/commands/publish.js
DELETED
|
@@ -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
|