handoff-app 1.1.0-0 → 1.1.0-experimental.1

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 (135) hide show
  1. package/Changelog.md +5 -0
  2. package/config/docs/playground.md +14 -0
  3. package/config/docs/system.md +2 -0
  4. package/dist/app/components/Component/Cards.js +17 -0
  5. package/dist/app-builder/build.d.ts +6 -1
  6. package/dist/app-builder/build.js +43 -14
  7. package/dist/app-builder/index.d.ts +1 -0
  8. package/dist/app-builder/index.js +9 -1
  9. package/dist/app-builder/isr-paths.d.ts +44 -0
  10. package/dist/app-builder/isr-paths.js +187 -0
  11. package/dist/app-builder/watchers.d.ts +5 -1
  12. package/dist/app-builder/watchers.js +59 -1
  13. package/dist/commands/index.js +16 -2
  14. package/dist/commands/isr/component.d.ts +10 -0
  15. package/dist/commands/isr/component.js +63 -0
  16. package/dist/commands/isr/pages.d.ts +11 -0
  17. package/dist/commands/isr/pages.js +64 -0
  18. package/dist/commands/platform/init.d.ts +6 -0
  19. package/dist/commands/platform/init.js +231 -0
  20. package/dist/commands/platform/login.d.ts +6 -0
  21. package/dist/commands/platform/login.js +108 -0
  22. package/dist/commands/platform/logout.d.ts +6 -0
  23. package/dist/commands/platform/logout.js +88 -0
  24. package/dist/commands/platform/pull.d.ts +8 -0
  25. package/dist/commands/platform/pull.js +112 -0
  26. package/dist/commands/platform/push.d.ts +8 -0
  27. package/dist/commands/platform/push.js +428 -0
  28. package/dist/config/runtime.d.ts +5 -0
  29. package/dist/config/runtime.js +67 -4
  30. package/dist/index.d.ts +4 -1
  31. package/dist/index.js +2 -2
  32. package/dist/pipeline/index.d.ts +1 -0
  33. package/dist/pipeline/index.js +3 -1
  34. package/dist/pipeline/pages.d.ts +6 -0
  35. package/dist/pipeline/pages.js +21 -0
  36. package/dist/platform/client.d.ts +122 -0
  37. package/dist/platform/client.js +228 -0
  38. package/dist/platform/credentials.d.ts +40 -0
  39. package/dist/platform/credentials.js +91 -0
  40. package/dist/platform/sync.d.ts +68 -0
  41. package/dist/platform/sync.js +349 -0
  42. package/dist/transformers/plugins/csf-render.js +18 -1
  43. package/dist/transformers/plugins/ssr-render.js +14 -0
  44. package/dist/transformers/preview/page/api.d.ts +8 -0
  45. package/dist/transformers/preview/page/api.js +74 -0
  46. package/dist/transformers/preview/page/builder.d.ts +7 -0
  47. package/dist/transformers/preview/page/builder.js +173 -0
  48. package/dist/transformers/preview/page/types.d.ts +38 -0
  49. package/dist/transformers/preview/page/types.js +2 -0
  50. package/dist/transformers/utils/build.d.ts +18 -0
  51. package/dist/transformers/utils/build.js +108 -0
  52. package/dist/types/config.d.ts +19 -0
  53. package/docs/cli.md +66 -52
  54. package/docs/components.md +107 -0
  55. package/docs/isr.md +113 -0
  56. package/docs/pages.md +152 -0
  57. package/docs/rest.yml +590 -0
  58. package/docs/rest_api.md +78 -0
  59. package/eslint.config.mjs +18 -0
  60. package/handoff.config.js +90 -0
  61. package/package.json +10 -6
  62. package/src/app/components/Page/PageLists.tsx +218 -0
  63. package/src/app/components/Page/types.ts +19 -0
  64. package/src/app/components/Playground/ComponentCard.tsx +78 -0
  65. package/src/app/components/Playground/ComponentLibrary.tsx +99 -0
  66. package/src/app/components/Playground/EditContext.tsx +159 -0
  67. package/src/app/components/Playground/EditSheet.tsx +80 -0
  68. package/src/app/components/Playground/MediaBrowser.tsx +56 -0
  69. package/src/app/components/Playground/PlaygroundBuilder.tsx +160 -0
  70. package/src/app/components/Playground/PlaygroundContext.tsx +276 -0
  71. package/src/app/components/Playground/Preview.tsx +174 -0
  72. package/src/app/components/Playground/SortableItem.tsx +45 -0
  73. package/src/app/components/Playground/TemplateManager.tsx +81 -0
  74. package/src/app/components/Playground/Wizard/ApiKeySettings.tsx +125 -0
  75. package/src/app/components/Playground/Wizard/WizardDialog.tsx +228 -0
  76. package/src/app/components/Playground/Wizard/llm-client.ts +87 -0
  77. package/src/app/components/Playground/Wizard/prompt-builder.ts +78 -0
  78. package/src/app/components/Playground/Wizard/response-parser.ts +81 -0
  79. package/src/app/components/Playground/fields/ButtonField.tsx +24 -0
  80. package/src/app/components/Playground/fields/Field.tsx +112 -0
  81. package/src/app/components/Playground/fields/FieldLabel.tsx +20 -0
  82. package/src/app/components/Playground/fields/ImageField.tsx +92 -0
  83. package/src/app/components/Playground/fields/LinkField.tsx +24 -0
  84. package/src/app/components/Playground/fields/RichTextField.tsx +34 -0
  85. package/src/app/components/Playground/fields/TextField.tsx +13 -0
  86. package/src/app/components/Playground/types.ts +30 -0
  87. package/src/app/components/ui/dialog.tsx +80 -0
  88. package/src/app/components/ui/textarea.tsx +19 -0
  89. package/src/app/components/util/index.ts +61 -8
  90. package/src/app/next.config.mjs +2 -10
  91. package/src/app/pages/playground/index.tsx +30 -0
  92. package/src/app/pages/system/page/[page]/index.tsx +300 -0
  93. package/src/app/pages/system/page/index.tsx +124 -0
  94. package/src/app/tsconfig.json +1 -1
  95. package/src/app-builder/build.ts +59 -16
  96. package/src/app-builder/index.ts +9 -0
  97. package/src/app-builder/isr-paths.ts +183 -0
  98. package/src/app-builder/watchers.ts +59 -0
  99. package/src/commands/index.ts +14 -0
  100. package/src/commands/isr/component.ts +68 -0
  101. package/src/commands/isr/pages.ts +68 -0
  102. package/src/commands/platform/init.ts +229 -0
  103. package/src/commands/platform/login.ts +96 -0
  104. package/src/commands/platform/logout.ts +63 -0
  105. package/src/commands/platform/pull.ts +98 -0
  106. package/src/commands/platform/push.ts +469 -0
  107. package/src/config/runtime.ts +76 -0
  108. package/src/index.ts +3 -3
  109. package/src/pipeline/index.ts +1 -0
  110. package/src/pipeline/pages.ts +10 -0
  111. package/src/platform/client.ts +335 -0
  112. package/src/platform/credentials.ts +106 -0
  113. package/src/platform/sync.ts +428 -0
  114. package/src/transformers/plugins/csf-render.ts +17 -0
  115. package/src/transformers/plugins/ssr-render.ts +17 -8
  116. package/src/transformers/preview/page/api.ts +65 -0
  117. package/src/transformers/preview/page/builder.ts +200 -0
  118. package/src/transformers/preview/page/types.ts +37 -0
  119. package/src/transformers/utils/build.ts +112 -0
  120. package/src/types/config.ts +21 -0
  121. package/test_components/example/example.js +110 -0
  122. package/test_components/example/style.scss +56 -0
  123. package/test_components/example/template.hbs +19 -0
  124. package/test_components/example-csf/example-csf.js +16 -0
  125. package/test_components/example-csf/style.scss +52 -0
  126. package/test_components/example-csf/template.stories.tsx +97 -0
  127. package/test_components/example-react/example-react.js +69 -0
  128. package/test_components/example-react/style.scss +56 -0
  129. package/test_components/example-react/template.tsx +49 -0
  130. package/test_pages/boilerplate/boilerplate.json +73 -0
  131. package/tsconfig.json +1 -1
  132. package/dist/app/components/Component/Cards.jsx +0 -32
  133. package/docs/api.md +0 -279
  134. package/docs/api_spec.yml +0 -0
  135. package/docs/props.md +0 -34
package/Changelog.md CHANGED
@@ -6,6 +6,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to
7
7
  [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
8
8
 
9
+ ## \[1.2.0]
10
+
11
+ * Upgrade to Next16
12
+ * Add pagination data structure to arrays
13
+
9
14
  ## \[1.1.0] - 2026-03-05
10
15
 
11
16
  This release introduces two major new features. First, handoff now can do automatic
@@ -0,0 +1,14 @@
1
+ ---
2
+ title: Playground
3
+ description: Build pages from design system components. Select, arrange, edit, and preview components in a live builder.
4
+ weight: 50
5
+ menuTitle: 'Playground'
6
+ metaTitle: 'Playground | Handoff Design System'
7
+ metaDescription: 'Interactive playground for building pages from design system components.'
8
+ enabled: true
9
+ menu:
10
+ - title: Playground
11
+ menu:
12
+ - path: playground
13
+ title: Builder
14
+ ---
@@ -16,4 +16,6 @@ menu:
16
16
  tokens: true
17
17
  - title: Components
18
18
  components: true
19
+ - title: Pages
20
+ pages: true
19
21
  ---
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const jsx_runtime_1 = require("react/jsx-runtime");
4
+ const lucide_react_1 = require("lucide-react");
5
+ const PracticeLine = ({ line, type }) => ((0, jsx_runtime_1.jsxs)("li", { className: "flex items-start gap-3", children: [type === 'positive' ? ((0, jsx_runtime_1.jsx)(lucide_react_1.Check, { className: "mt-[5px] h-3 w-3 shrink-0 text-emerald-600", strokeWidth: 3 })) : type === 'negative' ? ((0, jsx_runtime_1.jsx)(lucide_react_1.X, { className: "mt-[5px] h-3 w-3 shrink-0 text-gray-400", strokeWidth: 3 })) : ((0, jsx_runtime_1.jsx)("span", { className: "mt-[5px] h-3 w-3 shrink-0" })), (0, jsx_runtime_1.jsx)("p", { className: "text-sm", children: line })] }));
6
+ const CardItem = ({ card }) => {
7
+ // Split content by newlines and render each line as a separate item
8
+ const lines = card.content.split('\n').filter((line) => line.trim());
9
+ return ((0, jsx_runtime_1.jsx)("ul", { className: "space-y-3 text-emerald-800", children: lines.map((line, index) => ((0, jsx_runtime_1.jsx)(PracticeLine, { line: line, type: card.type }, `do-rule-${index}`))) }));
10
+ };
11
+ const Cards = ({ cards, maxCardsPerRow = 2, className = '' }) => {
12
+ if (!cards || cards.length === 0) {
13
+ return null;
14
+ }
15
+ return ((0, jsx_runtime_1.jsx)("div", { className: "grid grid-cols-[repeat(auto-fit,minmax(100%,1fr))] gap-6 sm:grid-cols-[repeat(auto-fit,minmax(300px,1fr))]", children: cards.map((card, index) => ((0, jsx_runtime_1.jsxs)("div", { className: "relative rounded-lg border bg-gray-50 p-8 text-gray-600 dark:bg-gray-800", children: [(0, jsx_runtime_1.jsx)("h2", { className: "mb-3 font-normal text-gray-700", children: card.title }), (0, jsx_runtime_1.jsx)(CardItem, { card: card })] }, `card-${index}`))) }));
16
+ };
17
+ exports.default = Cards;
@@ -1,8 +1,13 @@
1
1
  import Handoff from '..';
2
2
  /**
3
3
  * Build the Next.js documentation application.
4
+ * @param copyOnlyPaths - If set, do not remove output dir; copy only these paths (relative to out/) into existing output (merge).
5
+ * @param deployDir - If set, copy output to this directory instead of default (workingPath/sitesDirectory/projectId).
4
6
  */
5
- declare const buildApp: (handoff: Handoff, skipComponents?: boolean) => Promise<void>;
7
+ declare const buildApp: (handoff: Handoff, skipComponents?: boolean, options?: {
8
+ copyOnlyPaths?: string[];
9
+ deployDir?: string;
10
+ }) => Promise<void>;
6
11
  /**
7
12
  * Watch the Next.js application.
8
13
  * Starts a custom dev server with Handoff-specific watchers and hot-reloading.
@@ -17,12 +17,14 @@ const cross_spawn_1 = __importDefault(require("cross-spawn"));
17
17
  const fs_extra_1 = __importDefault(require("fs-extra"));
18
18
  const path_1 = __importDefault(require("path"));
19
19
  const components_1 = require("../pipeline/components");
20
+ const pages_1 = require("../pipeline/pages");
21
+ const builder_1 = __importDefault(require("../transformers/preview/component/builder"));
20
22
  const css_1 = require("../transformers/preview/component/css");
21
23
  const javascript_1 = require("../transformers/preview/component/javascript");
22
- const builder_1 = __importDefault(require("../transformers/preview/component/builder"));
23
24
  const logger_1 = require("../utils/logger");
24
25
  const client_config_1 = require("./client-config");
25
26
  const paths_1 = require("./paths");
27
+ const builder_2 = require("../transformers/preview/page/builder");
26
28
  const watchers_1 = require("./watchers");
27
29
  const websocket_1 = require("./websocket");
28
30
  /**
@@ -81,19 +83,24 @@ const initializeProjectApp = (handoff) => __awaiter(void 0, void 0, void 0, func
81
83
  });
82
84
  /**
83
85
  * Build the Next.js documentation application.
86
+ * @param copyOnlyPaths - If set, do not remove output dir; copy only these paths (relative to out/) into existing output (merge).
87
+ * @param deployDir - If set, copy output to this directory instead of default (workingPath/sitesDirectory/projectId).
84
88
  */
85
- const buildApp = (handoff, skipComponents) => __awaiter(void 0, void 0, void 0, function* () {
89
+ const buildApp = (handoff, skipComponents, options) => __awaiter(void 0, void 0, void 0, function* () {
86
90
  skipComponents = skipComponents !== null && skipComponents !== void 0 ? skipComponents : false;
91
+ const copyOnlyPaths = options === null || options === void 0 ? void 0 : options.copyOnlyPaths;
92
+ const deployDir = options === null || options === void 0 ? void 0 : options.deployDir;
87
93
  // Perform cleanup
88
94
  yield cleanupAppDirectory(handoff);
89
95
  // Build components
90
96
  if (!skipComponents) {
91
97
  yield (0, components_1.buildComponents)(handoff);
92
98
  }
99
+ // Build pages (must run after components so rendered outputs exist)
100
+ yield (0, pages_1.buildPages)(handoff);
93
101
  // Prepare app
94
102
  const appPath = yield initializeProjectApp(handoff);
95
103
  yield (0, client_config_1.persistClientConfig)(handoff);
96
- // Build app
97
104
  const buildResult = cross_spawn_1.default.sync('npx', ['next', 'build'], {
98
105
  cwd: appPath,
99
106
  stdio: 'inherit',
@@ -106,16 +113,35 @@ const buildApp = (handoff, skipComponents) => __awaiter(void 0, void 0, void 0,
106
113
  }
107
114
  throw new Error(errorMsg);
108
115
  }
109
- // Ensure output root directory exists
110
- const outputRoot = path_1.default.resolve(handoff.workingPath, handoff.sitesDirectory);
111
- yield fs_extra_1.default.ensureDir(outputRoot);
112
- // Clean the project output directory (if exists)
113
- const output = path_1.default.resolve(outputRoot, handoff.getProjectId());
114
- if (fs_extra_1.default.existsSync(output)) {
115
- yield fs_extra_1.default.remove(output);
116
+ // Resolve output directory: explicit deployDir or default under working path
117
+ const outputRoot = deployDir
118
+ ? path_1.default.resolve(deployDir)
119
+ : path_1.default.resolve(handoff.workingPath, handoff.sitesDirectory);
120
+ const output = deployDir ? outputRoot : path_1.default.resolve(outputRoot, handoff.getProjectId());
121
+ if (!deployDir) {
122
+ yield fs_extra_1.default.ensureDir(path_1.default.resolve(handoff.workingPath, handoff.sitesDirectory));
123
+ }
124
+ if (copyOnlyPaths && copyOnlyPaths.length > 0) {
125
+ // Partial deploy: merge only the listed paths into existing output
126
+ yield fs_extra_1.default.ensureDir(output);
127
+ const outDir = path_1.default.resolve(appPath, 'out');
128
+ for (const relativePath of copyOnlyPaths) {
129
+ const src = path_1.default.join(outDir, relativePath);
130
+ const dest = path_1.default.join(output, relativePath);
131
+ if (fs_extra_1.default.existsSync(src)) {
132
+ yield fs_extra_1.default.ensureDir(path_1.default.dirname(dest));
133
+ yield fs_extra_1.default.copy(src, dest, { overwrite: true });
134
+ }
135
+ }
136
+ }
137
+ else {
138
+ // Full deploy: replace output with full out
139
+ if (fs_extra_1.default.existsSync(output)) {
140
+ yield fs_extra_1.default.remove(output);
141
+ }
142
+ yield fs_extra_1.default.ensureDir(output);
143
+ yield fs_extra_1.default.copy(path_1.default.resolve(appPath, 'out'), output);
116
144
  }
117
- // Copy the build files into the project output directory
118
- yield fs_extra_1.default.copy(path_1.default.resolve(appPath, 'out'), output);
119
145
  });
120
146
  /**
121
147
  * Watch the Next.js application.
@@ -128,6 +154,8 @@ const watchApp = (handoff) => __awaiter(void 0, void 0, void 0, function* () {
128
154
  yield (0, builder_1.default)(handoff, undefined, undefined, { useCache: true });
129
155
  yield (0, javascript_1.buildMainJS)(handoff);
130
156
  yield (0, css_1.buildMainCss)(handoff);
157
+ // Build pages after components
158
+ yield (0, builder_2.processPages)(handoff);
131
159
  const appPath = yield initializeProjectApp(handoff);
132
160
  // Persist client configuration
133
161
  yield (0, client_config_1.persistClientConfig)(handoff);
@@ -142,7 +170,7 @@ const watchApp = (handoff) => __awaiter(void 0, void 0, void 0, function* () {
142
170
  // create empty directory
143
171
  yield fs_extra_1.default.ensureDir(moduleOutput);
144
172
  }
145
- const nextProcess = (0, cross_spawn_1.default)('npx', ['next', 'dev', '--turbopack', '--port', String(port)], {
173
+ const nextProcess = (0, cross_spawn_1.default)('npx', ['next', 'dev', '--port', String(port)], {
146
174
  cwd: appPath,
147
175
  stdio: 'inherit',
148
176
  env: Object.assign(Object.assign({}, process.env), { NODE_ENV: 'development' }),
@@ -172,6 +200,7 @@ const watchApp = (handoff) => __awaiter(void 0, void 0, void 0, function* () {
172
200
  (0, watchers_1.watchRuntimeConfiguration)(handoff, state);
173
201
  (0, watchers_1.watchGlobalEntries)(handoff, state, chokidarConfig);
174
202
  (0, watchers_1.watchPages)(handoff, chokidarConfig);
203
+ (0, watchers_1.watchRuntimePages)(handoff, state);
175
204
  });
176
205
  exports.watchApp = watchApp;
177
206
  /**
@@ -190,7 +219,7 @@ const devApp = (handoff) => __awaiter(void 0, void 0, void 0, function* () {
190
219
  // Persist client configuration
191
220
  yield (0, client_config_1.persistClientConfig)(handoff);
192
221
  // Run
193
- const devResult = cross_spawn_1.default.sync('npx', ['next', 'dev', '--turbopack', '--port', String((_b = (_a = handoff.config.app.ports) === null || _a === void 0 ? void 0 : _a.app) !== null && _b !== void 0 ? _b : 3000)], {
222
+ const devResult = cross_spawn_1.default.sync('npx', ['next', 'dev', '--port', String((_b = (_a = handoff.config.app.ports) === null || _a === void 0 ? void 0 : _a.app) !== null && _b !== void 0 ? _b : 3000)], {
194
223
  cwd: appPath,
195
224
  stdio: 'inherit',
196
225
  env: Object.assign(Object.assign({}, process.env), { NODE_ENV: 'development' }),
@@ -1,2 +1,3 @@
1
1
  export { buildComponents } from '../pipeline/components';
2
2
  export { default, devApp, watchApp } from './build';
3
+ export { getL1StaticPathSegments, getL2StaticPathSegments, normalizeRoutePath, resolveAffectedOutputPaths, resolveComponentAffectedOutputPaths, resolveComponentAffectedOutputPathsBatch, routePathToOutputPaths, } from './isr-paths';
@@ -3,10 +3,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.watchApp = exports.devApp = exports.default = exports.buildComponents = void 0;
6
+ exports.routePathToOutputPaths = exports.resolveComponentAffectedOutputPathsBatch = exports.resolveComponentAffectedOutputPaths = exports.resolveAffectedOutputPaths = exports.normalizeRoutePath = exports.getL2StaticPathSegments = exports.getL1StaticPathSegments = exports.watchApp = exports.devApp = exports.default = exports.buildComponents = void 0;
7
7
  var components_1 = require("../pipeline/components");
8
8
  Object.defineProperty(exports, "buildComponents", { enumerable: true, get: function () { return components_1.buildComponents; } });
9
9
  var build_1 = require("./build");
10
10
  Object.defineProperty(exports, "default", { enumerable: true, get: function () { return __importDefault(build_1).default; } });
11
11
  Object.defineProperty(exports, "devApp", { enumerable: true, get: function () { return build_1.devApp; } });
12
12
  Object.defineProperty(exports, "watchApp", { enumerable: true, get: function () { return build_1.watchApp; } });
13
+ var isr_paths_1 = require("./isr-paths");
14
+ Object.defineProperty(exports, "getL1StaticPathSegments", { enumerable: true, get: function () { return isr_paths_1.getL1StaticPathSegments; } });
15
+ Object.defineProperty(exports, "getL2StaticPathSegments", { enumerable: true, get: function () { return isr_paths_1.getL2StaticPathSegments; } });
16
+ Object.defineProperty(exports, "normalizeRoutePath", { enumerable: true, get: function () { return isr_paths_1.normalizeRoutePath; } });
17
+ Object.defineProperty(exports, "resolveAffectedOutputPaths", { enumerable: true, get: function () { return isr_paths_1.resolveAffectedOutputPaths; } });
18
+ Object.defineProperty(exports, "resolveComponentAffectedOutputPaths", { enumerable: true, get: function () { return isr_paths_1.resolveComponentAffectedOutputPaths; } });
19
+ Object.defineProperty(exports, "resolveComponentAffectedOutputPathsBatch", { enumerable: true, get: function () { return isr_paths_1.resolveComponentAffectedOutputPathsBatch; } });
20
+ Object.defineProperty(exports, "routePathToOutputPaths", { enumerable: true, get: function () { return isr_paths_1.routePathToOutputPaths; } });
@@ -0,0 +1,44 @@
1
+ import Handoff from '..';
2
+ /**
3
+ * Returns level-1 path segments (one segment, e.g. "guidelines") for doc/pages.
4
+ */
5
+ export declare function getL1StaticPathSegments(modulePath: string, workingPath: string): string[];
6
+ /**
7
+ * Returns level-2 path segments { level1, level2 } for doc/pages.
8
+ */
9
+ export declare function getL2StaticPathSegments(modulePath: string, workingPath: string): Array<{
10
+ level1: string;
11
+ level2: string;
12
+ }>;
13
+ /**
14
+ * Normalize a route path (e.g. "/guidelines" or "foundations/colors") to a segment path without leading slash.
15
+ */
16
+ export declare function normalizeRoutePath(routePath: string): string;
17
+ /**
18
+ * Resolve a single route path to the corresponding static output file path(s) under out/.
19
+ * Next.js static export with trailingSlash produces path/index.html for each route.
20
+ */
21
+ export declare function routePathToOutputPaths(routePath: string): string[];
22
+ /**
23
+ * Resolve affected static output file paths (relative to out/) for ISR.
24
+ *
25
+ * @param handoff - Handoff instance (used for modulePath, workingPath)
26
+ * @param options.path - Single route path to invalidate (e.g. "/guidelines", "foundations/colors")
27
+ * @param options.paths - Multiple route paths to invalidate
28
+ * @param options.menuChanged - If true, return all menu-linked routes (all doc pages + system index pages)
29
+ * @returns Relative paths under out/, e.g. ["index.html", "guidelines/index.html"]
30
+ */
31
+ export declare function resolveAffectedOutputPaths(handoff: Handoff, options: {
32
+ path?: string;
33
+ paths?: string[];
34
+ menuChanged?: boolean;
35
+ }): string[];
36
+ /**
37
+ * Resolve affected static output file paths for a component ISR update.
38
+ * Returns paths under out/ for the component route, component list, and API JSON files.
39
+ */
40
+ export declare function resolveComponentAffectedOutputPaths(componentId: string): string[];
41
+ /**
42
+ * Resolve affected output paths for multiple components (union).
43
+ */
44
+ export declare function resolveComponentAffectedOutputPathsBatch(componentIds: string[]): string[];
@@ -0,0 +1,187 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getL1StaticPathSegments = getL1StaticPathSegments;
7
+ exports.getL2StaticPathSegments = getL2StaticPathSegments;
8
+ exports.normalizeRoutePath = normalizeRoutePath;
9
+ exports.routePathToOutputPaths = routePathToOutputPaths;
10
+ exports.resolveAffectedOutputPaths = resolveAffectedOutputPaths;
11
+ exports.resolveComponentAffectedOutputPaths = resolveComponentAffectedOutputPaths;
12
+ exports.resolveComponentAffectedOutputPathsBatch = resolveComponentAffectedOutputPathsBatch;
13
+ /**
14
+ * ISR path resolver: computes which static output files (under out/) are affected
15
+ * for a given page path or "menu changed", for use with partial deploy.
16
+ *
17
+ * Mirrors the route structure from buildL1StaticPaths / buildL2StaticPaths (see app/components/util)
18
+ * but runs in CLI context using handoff paths instead of process.env.
19
+ */
20
+ const fs_extra_1 = __importDefault(require("fs-extra"));
21
+ const path_1 = __importDefault(require("path"));
22
+ /** Route segments excluded from doc/page path generation (same as knownPaths in util) */
23
+ const KNOWN_PATHS = [
24
+ 'assets',
25
+ 'assets/fonts',
26
+ 'assets/icons',
27
+ 'assets/logos',
28
+ 'foundations',
29
+ 'foundations/colors',
30
+ 'foundations/icons',
31
+ 'foundations/effects',
32
+ 'foundations/logos',
33
+ 'foundations/logo',
34
+ 'foundations/typography',
35
+ 'system',
36
+ 'system/component',
37
+ 'playground',
38
+ ];
39
+ /**
40
+ * Returns level-1 path segments (one segment, e.g. "guidelines") for doc/pages.
41
+ */
42
+ function getL1StaticPathSegments(modulePath, workingPath) {
43
+ const docRoot = path_1.default.resolve(modulePath, 'config/docs');
44
+ const pageRoot = path_1.default.resolve(workingPath, 'pages');
45
+ if (!fs_extra_1.default.existsSync(docRoot)) {
46
+ return [];
47
+ }
48
+ let files = fs_extra_1.default.readdirSync(docRoot);
49
+ if (fs_extra_1.default.existsSync(pageRoot)) {
50
+ // ES5-compatible way to merge and deduplicate both arrays
51
+ var moreFiles = fs_extra_1.default.readdirSync(pageRoot);
52
+ for (var i = 0; i < moreFiles.length; i++) {
53
+ if (files.indexOf(moreFiles[i]) === -1) {
54
+ files.push(moreFiles[i]);
55
+ }
56
+ }
57
+ }
58
+ return files
59
+ .filter((fileName) => {
60
+ const inDoc = path_1.default.join(docRoot, fileName);
61
+ const inPages = path_1.default.join(pageRoot, fileName);
62
+ if (fs_extra_1.default.existsSync(inDoc))
63
+ return !fs_extra_1.default.lstatSync(inDoc).isDirectory();
64
+ if (fs_extra_1.default.existsSync(inPages))
65
+ return !fs_extra_1.default.lstatSync(inPages).isDirectory();
66
+ return false;
67
+ })
68
+ .filter((fileName) => fileName.endsWith('.md'))
69
+ .map((fileName) => fileName.replace('.md', ''))
70
+ .filter((segment) => !KNOWN_PATHS.includes(segment));
71
+ }
72
+ /**
73
+ * Returns level-2 path segments { level1, level2 } for doc/pages.
74
+ */
75
+ function getL2StaticPathSegments(modulePath, workingPath) {
76
+ const docRoot = path_1.default.resolve(modulePath, 'config/docs');
77
+ const pageRoot = path_1.default.resolve(workingPath, 'pages');
78
+ if (!fs_extra_1.default.existsSync(docRoot)) {
79
+ return [];
80
+ }
81
+ let list = fs_extra_1.default.readdirSync(docRoot);
82
+ if (fs_extra_1.default.existsSync(pageRoot)) {
83
+ list = [...new Set([...list, ...fs_extra_1.default.readdirSync(pageRoot)])];
84
+ }
85
+ const result = [];
86
+ for (const fileName of list) {
87
+ let dirPath;
88
+ if (fs_extra_1.default.existsSync(path_1.default.join(pageRoot, fileName))) {
89
+ dirPath = path_1.default.join(pageRoot, fileName);
90
+ }
91
+ else if (fs_extra_1.default.existsSync(path_1.default.join(docRoot, fileName))) {
92
+ dirPath = path_1.default.join(docRoot, fileName);
93
+ }
94
+ else {
95
+ continue;
96
+ }
97
+ if (!fs_extra_1.default.lstatSync(dirPath).isDirectory())
98
+ continue;
99
+ const subFiles = fs_extra_1.default.readdirSync(dirPath);
100
+ for (const subFile of subFiles) {
101
+ if (!subFile.endsWith('.md'))
102
+ continue;
103
+ const level2 = subFile.replace('.md', '');
104
+ const fullKey = `${fileName}/${level2}`;
105
+ if (KNOWN_PATHS.includes(fullKey))
106
+ continue;
107
+ result.push({ level1: fileName, level2 });
108
+ }
109
+ }
110
+ return result;
111
+ }
112
+ /**
113
+ * Normalize a route path (e.g. "/guidelines" or "foundations/colors") to a segment path without leading slash.
114
+ */
115
+ function normalizeRoutePath(routePath) {
116
+ return routePath.replace(/^\/+/, '').replace(/\/+$/, '');
117
+ }
118
+ /**
119
+ * Resolve a single route path to the corresponding static output file path(s) under out/.
120
+ * Next.js static export with trailingSlash produces path/index.html for each route.
121
+ */
122
+ function routePathToOutputPaths(routePath) {
123
+ const segment = normalizeRoutePath(routePath);
124
+ if (!segment)
125
+ return ['index.html'];
126
+ return [`${segment}/index.html`];
127
+ }
128
+ /**
129
+ * Resolve affected static output file paths (relative to out/) for ISR.
130
+ *
131
+ * @param handoff - Handoff instance (used for modulePath, workingPath)
132
+ * @param options.path - Single route path to invalidate (e.g. "/guidelines", "foundations/colors")
133
+ * @param options.paths - Multiple route paths to invalidate
134
+ * @param options.menuChanged - If true, return all menu-linked routes (all doc pages + system index pages)
135
+ * @returns Relative paths under out/, e.g. ["index.html", "guidelines/index.html"]
136
+ */
137
+ function resolveAffectedOutputPaths(handoff, options) {
138
+ const { path: singlePath, paths: pathList, menuChanged } = options;
139
+ const modulePath = handoff.modulePath;
140
+ const workingPath = handoff.workingPath;
141
+ if (menuChanged) {
142
+ const l1 = getL1StaticPathSegments(modulePath, workingPath);
143
+ const l2 = getL2StaticPathSegments(modulePath, workingPath);
144
+ const out = ['index.html'];
145
+ for (const segment of l1) {
146
+ out.push(`${segment}/index.html`);
147
+ }
148
+ for (const { level1, level2 } of l2) {
149
+ out.push(`${level1}/${level2}/index.html`);
150
+ }
151
+ out.push('system/index.html', 'system/component/index.html');
152
+ return [...new Set(out)];
153
+ }
154
+ if (singlePath) {
155
+ return routePathToOutputPaths(singlePath);
156
+ }
157
+ if (pathList && pathList.length > 0) {
158
+ const out = [];
159
+ for (const p of pathList) {
160
+ out.push(...routePathToOutputPaths(p));
161
+ }
162
+ return [...new Set(out)];
163
+ }
164
+ return [];
165
+ }
166
+ /**
167
+ * Resolve affected static output file paths for a component ISR update.
168
+ * Returns paths under out/ for the component route, component list, and API JSON files.
169
+ */
170
+ function resolveComponentAffectedOutputPaths(componentId) {
171
+ return [
172
+ `system/component/${componentId}/index.html`,
173
+ 'system/component/index.html',
174
+ `api/component/${componentId}.json`,
175
+ 'api/components.json',
176
+ ];
177
+ }
178
+ /**
179
+ * Resolve affected output paths for multiple components (union).
180
+ */
181
+ function resolveComponentAffectedOutputPathsBatch(componentIds) {
182
+ const out = ['system/component/index.html', 'api/components.json'];
183
+ for (const id of componentIds) {
184
+ out.push(`system/component/${id}/index.html`, `api/component/${id}.json`);
185
+ }
186
+ return [...new Set(out)];
187
+ }
@@ -34,11 +34,15 @@ export declare const mapEntryTypeToSegment: (type: keyof ComponentListObject["en
34
34
  *
35
35
  * @returns A Map of paths to watch and their entry types
36
36
  */
37
- export declare const getRuntimeComponentsPathsToWatch: (handoff: Handoff) => Map<string, "template" | "js" | "scss">;
37
+ export declare const getRuntimeComponentsPathsToWatch: (handoff: Handoff) => Map<string, "js" | "scss" | "template">;
38
38
  /**
39
39
  * Watches runtime components for changes.
40
40
  */
41
41
  export declare const watchRuntimeComponents: (handoff: Handoff, state: WatcherState, runtimeComponentPathsToWatch: Map<string, keyof ComponentListObject["entries"]>) => void;
42
+ /**
43
+ * Watches runtime page config files for changes and triggers page rebuilds.
44
+ */
45
+ export declare const watchRuntimePages: (handoff: Handoff, state: WatcherState) => void;
42
46
  /**
43
47
  * Watches the runtime configuration for changes.
44
48
  */
@@ -35,13 +35,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
35
35
  return (mod && mod.__esModule) ? mod : { "default": mod };
36
36
  };
37
37
  Object.defineProperty(exports, "__esModule", { value: true });
38
- exports.watchRuntimeConfiguration = exports.watchRuntimeComponents = exports.getRuntimeComponentsPathsToWatch = exports.mapEntryTypeToSegment = exports.watchGlobalEntries = exports.watchPages = exports.watchAppSource = exports.watchPublicDirectory = void 0;
38
+ exports.watchRuntimeConfiguration = exports.watchRuntimePages = exports.watchRuntimeComponents = exports.getRuntimeComponentsPathsToWatch = exports.mapEntryTypeToSegment = exports.watchGlobalEntries = exports.watchPages = exports.watchAppSource = exports.watchPublicDirectory = void 0;
39
39
  const chokidar_1 = __importDefault(require("chokidar"));
40
40
  const fs_extra_1 = __importDefault(require("fs-extra"));
41
41
  const path_1 = __importDefault(require("path"));
42
42
  const css_1 = require("../transformers/preview/component/css");
43
43
  const javascript_1 = require("../transformers/preview/component/javascript");
44
44
  const builder_1 = __importStar(require("../transformers/preview/component/builder"));
45
+ const builder_2 = require("../transformers/preview/page/builder");
45
46
  const logger_1 = require("../utils/logger");
46
47
  const client_config_1 = require("./client-config");
47
48
  const paths_1 = require("./paths");
@@ -260,6 +261,63 @@ const watchRuntimeComponents = (handoff, state, runtimeComponentPathsToWatch) =>
260
261
  }
261
262
  };
262
263
  exports.watchRuntimeComponents = watchRuntimeComponents;
264
+ /**
265
+ * Watches runtime page config files for changes and triggers page rebuilds.
266
+ */
267
+ const watchRuntimePages = (handoff, state) => {
268
+ var _a, _b, _c, _d, _e;
269
+ const runtimePages = (_c = (_b = (_a = handoff.runtimeConfig) === null || _a === void 0 ? void 0 : _a.entries) === null || _b === void 0 ? void 0 : _b.pages) !== null && _c !== void 0 ? _c : {};
270
+ const pageConfigPaths = handoff.getConfigFilePaths().filter((configPath) => {
271
+ const dirName = path_1.default.basename(path_1.default.dirname(configPath));
272
+ return dirName in runtimePages;
273
+ });
274
+ // Also watch page directories directly
275
+ const pageIds = Object.keys(runtimePages);
276
+ if (pageIds.length === 0 && pageConfigPaths.length === 0)
277
+ return;
278
+ const pathsToWatch = pageConfigPaths.length > 0 ? pageConfigPaths : [];
279
+ // Add page config entry paths from the config
280
+ for (const pageId of pageIds) {
281
+ const page = runtimePages[pageId];
282
+ if (page.path && fs_extra_1.default.existsSync(page.path)) {
283
+ pathsToWatch.push(page.path);
284
+ }
285
+ }
286
+ // Watch the page entry directories from config
287
+ if ((_e = (_d = handoff.config.entries) === null || _d === void 0 ? void 0 : _d.pages) === null || _e === void 0 ? void 0 : _e.length) {
288
+ for (const pagePath of handoff.config.entries.pages) {
289
+ const resolvedPath = path_1.default.resolve(handoff.workingPath, pagePath);
290
+ if (fs_extra_1.default.existsSync(resolvedPath)) {
291
+ pathsToWatch.push(resolvedPath);
292
+ }
293
+ }
294
+ }
295
+ if (pathsToWatch.length === 0)
296
+ return;
297
+ chokidar_1.default.watch(pathsToWatch, { ignoreInitial: true }).on('all', (event, file) => __awaiter(void 0, void 0, void 0, function* () {
298
+ switch (event) {
299
+ case 'add':
300
+ case 'change':
301
+ case 'unlink':
302
+ if (!state.debounce) {
303
+ state.debounce = true;
304
+ try {
305
+ logger_1.Logger.warn('Page config changed. Rebuilding pages...');
306
+ handoff.reload();
307
+ yield (0, builder_2.processPages)(handoff);
308
+ }
309
+ catch (e) {
310
+ logger_1.Logger.error('Error processing pages:', e);
311
+ }
312
+ finally {
313
+ state.debounce = false;
314
+ }
315
+ }
316
+ break;
317
+ }
318
+ }));
319
+ };
320
+ exports.watchRuntimePages = watchRuntimePages;
263
321
  /**
264
322
  * Watches the runtime configuration for changes.
265
323
  */
@@ -12,9 +12,16 @@ const pages_1 = __importDefault(require("./eject/pages"));
12
12
  const theme_1 = __importDefault(require("./eject/theme"));
13
13
  const fetch_1 = __importDefault(require("./fetch"));
14
14
  const init_1 = __importDefault(require("./init"));
15
- const component_1 = __importDefault(require("./make/component"));
15
+ const component_1 = __importDefault(require("./isr/component"));
16
+ const pages_2 = __importDefault(require("./isr/pages"));
17
+ const component_2 = __importDefault(require("./make/component"));
16
18
  const page_1 = __importDefault(require("./make/page"));
17
19
  const template_1 = __importDefault(require("./make/template"));
20
+ const init_2 = __importDefault(require("./platform/init"));
21
+ const login_1 = __importDefault(require("./platform/login"));
22
+ const logout_1 = __importDefault(require("./platform/logout"));
23
+ const pull_1 = __importDefault(require("./platform/pull"));
24
+ const push_1 = __importDefault(require("./platform/push"));
18
25
  const scaffold_1 = __importDefault(require("./scaffold"));
19
26
  const start_1 = __importDefault(require("./start"));
20
27
  const components_2 = __importDefault(require("./validate/components"));
@@ -27,9 +34,16 @@ exports.commands = [
27
34
  theme_1.default,
28
35
  fetch_1.default,
29
36
  init_1.default,
30
- page_1.default,
37
+ pages_2.default,
31
38
  component_1.default,
39
+ page_1.default,
40
+ component_2.default,
32
41
  template_1.default,
42
+ login_1.default,
43
+ init_2.default,
44
+ pull_1.default,
45
+ push_1.default,
46
+ logout_1.default,
33
47
  scaffold_1.default,
34
48
  start_1.default,
35
49
  components_2.default,
@@ -0,0 +1,10 @@
1
+ import { CommandModule } from 'yargs';
2
+ import { SharedArgs } from '../types';
3
+ export interface IsrComponentArgs extends SharedArgs {
4
+ id?: string;
5
+ ids?: string[];
6
+ partial?: boolean;
7
+ deployDir?: string;
8
+ }
9
+ declare const command: CommandModule<{}, IsrComponentArgs>;
10
+ export default command;