handoff-app 2.0.0-7 → 2.0.0-9
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/README.md +130 -14
- package/UPGRADE.md +20 -0
- package/dist/app-builder/build.js +10 -2
- package/dist/app-builder/client-config.js +3 -1
- package/dist/app-builder/watchers.js +17 -7
- package/dist/cache/build-cache.js +5 -1
- package/dist/catalog/normalize.js +3 -0
- package/dist/catalog/previews.js +5 -4
- package/dist/catalog/types.d.ts +8 -0
- package/dist/cli/auth/device.d.ts +6 -1
- package/dist/cli/auth/device.js +5 -2
- package/dist/cli/auth/store.d.ts +15 -6
- package/dist/cli/auth/store.js +72 -19
- package/dist/commands/login/index.d.ts +5 -0
- package/dist/commands/login/index.js +18 -6
- package/dist/commands/logout/index.d.ts +5 -0
- package/dist/commands/logout/index.js +54 -15
- package/dist/commands/types.d.ts +2 -0
- package/dist/commands/utils.d.ts +21 -7
- package/dist/commands/utils.js +11 -10
- package/dist/config/catalog-include.d.ts +2 -2
- package/dist/config/defaults.d.ts +3 -3
- package/dist/config/defaults.js +54 -48
- package/dist/config/env.d.ts +9 -0
- package/dist/config/env.js +43 -0
- package/dist/config/errors.d.ts +4 -0
- package/dist/config/errors.js +11 -0
- package/dist/config/from-env.d.ts +24 -0
- package/dist/config/from-env.js +7 -0
- package/dist/config/helpers.d.ts +2 -0
- package/dist/config/helpers.js +3 -1
- package/dist/config/index.d.ts +2 -1
- package/dist/config/index.js +6 -1
- package/dist/config/loader.d.ts +29 -11
- package/dist/config/loader.js +121 -46
- package/dist/config/resolve-env.d.ts +5 -0
- package/dist/config/resolve-env.js +102 -0
- package/dist/config/runtime.d.ts +3 -3
- package/dist/config/runtime.js +8 -10
- package/dist/config/validator.d.ts +2 -2
- package/dist/config/validator.js +2 -4
- package/dist/handlebars.d.ts +10 -2
- package/dist/index.d.ts +33 -4
- package/dist/index.js +60 -16
- package/dist/pipeline/index.d.ts +1 -1
- package/dist/pipeline/index.js +1 -1
- package/dist/registry/asset-storage/adapters/vercel-blob.js +1 -1
- package/dist/registry/asset-storage/resolve.d.ts +2 -2
- package/dist/registry/asset-storage/resolve.js +4 -1
- package/dist/registry/checkout/index.js +4 -4
- package/dist/registry/connection.d.ts +28 -24
- package/dist/registry/connection.js +53 -67
- package/dist/registry/db/driver.d.ts +4 -11
- package/dist/registry/db/driver.js +4 -16
- package/dist/registry/db/index.d.ts +1 -1
- package/dist/registry/db/index.js +1 -2
- package/dist/registry/publish/index.js +4 -4
- package/dist/store/filesystem.d.ts +2 -2
- package/dist/transformers/plugins/ssr-render.js +40 -15
- package/dist/transformers/preview/component/css.d.ts +1 -0
- package/dist/transformers/preview/component/css.js +85 -47
- package/dist/transformers/preview/pattern/html.js +3 -1
- package/dist/transformers/preview/types.d.ts +13 -0
- package/dist/transformers/utils/build.d.ts +19 -0
- package/dist/transformers/utils/build.js +83 -1
- package/dist/transformers/utils/module.js +2 -1
- package/dist/types/config.d.ts +54 -29
- package/docs/api.md +31 -0
- package/docs/cli.md +2 -2
- package/package.json +1 -1
- package/src/app/components/Component/PageSliceResolver.tsx +1 -0
- package/src/app/components/Component/Preview.tsx +380 -189
- package/src/app/components/Component/slices/ComponentDisplaySlice.tsx +4 -0
- package/src/app/pages/account/tokens.tsx +3 -1
- package/src/app-builder/build.ts +10 -2
- package/src/app-builder/client-config.ts +5 -3
- package/src/app-builder/watchers.ts +18 -8
- package/src/cache/build-cache.ts +5 -1
- package/src/catalog/README.md +3 -0
- package/src/catalog/normalize.ts +2 -0
- package/src/catalog/previews.ts +6 -3
- package/src/catalog/types.ts +8 -0
- package/src/cli/auth/device.ts +8 -3
- package/src/cli/auth/store.ts +75 -18
- package/src/commands/login/index.ts +20 -6
- package/src/commands/logout/index.ts +74 -20
- package/src/commands/types.ts +2 -0
- package/src/commands/utils.ts +14 -5
- package/src/config/README.md +61 -1
- package/src/config/catalog-include.ts +2 -2
- package/src/config/defaults.ts +24 -14
- package/src/config/env.ts +41 -0
- package/src/config/errors.ts +7 -0
- package/src/config/from-env.ts +26 -0
- package/src/config/helpers.ts +4 -2
- package/src/config/index.ts +4 -1
- package/src/config/loader.ts +158 -50
- package/src/config/resolve-env.ts +94 -0
- package/src/config/runtime.ts +12 -7
- package/src/config/validator.ts +4 -6
- package/src/handlebars.ts +8 -2
- package/src/index.d.ts +2 -2
- package/src/index.ts +85 -8
- package/src/pipeline/index.ts +1 -1
- package/src/registry/asset-storage/adapters/vercel-blob.ts +1 -1
- package/src/registry/asset-storage/resolve.ts +6 -3
- package/src/registry/checkout/index.ts +5 -5
- package/src/registry/connection.ts +72 -71
- package/src/registry/db/driver.ts +6 -17
- package/src/registry/db/index.ts +0 -1
- package/src/registry/publish/index.ts +5 -5
- package/src/store/filesystem.ts +2 -2
- package/src/transformers/plugins/ssr-render.ts +53 -16
- package/src/transformers/preview/component/css.ts +93 -50
- package/src/transformers/preview/pattern/html.ts +3 -1
- package/src/transformers/preview/types.ts +14 -0
- package/src/transformers/utils/build.ts +80 -0
- package/src/transformers/utils/module.ts +4 -2
- package/src/types/config.ts +51 -29
- package/templates/default/env.tpl +10 -0
- package/templates/default/gitignore.tpl +7 -0
- package/templates/default/handoff.config.js.tpl +21 -172
- package/templates/default/handoff.config.ts.tpl +21 -156
- package/templates/default/handoff.config.with-examples.js.tpl +20 -171
- package/templates/default/handoff.config.with-examples.ts.tpl +20 -155
|
@@ -18,6 +18,9 @@ export interface BasePageSlice {
|
|
|
18
18
|
type: PageSliceType;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
/** A fluid preview renders at the width the reader's stage has, so it is never scaled down. */
|
|
22
|
+
export type PreviewWidth = 'fluid' | number;
|
|
23
|
+
|
|
21
24
|
export interface BestPracticesPageSlice extends BasePageSlice {
|
|
22
25
|
type: 'BEST_PRACTICES';
|
|
23
26
|
}
|
|
@@ -27,6 +30,8 @@ export interface ComponentDisplayPageSlice extends BasePageSlice {
|
|
|
27
30
|
showPreview?: boolean;
|
|
28
31
|
showCodeHighlight?: boolean;
|
|
29
32
|
defaultHeight?: string;
|
|
33
|
+
/** Viewport width for the preview, in pixels. Defaults to 'fluid'. */
|
|
34
|
+
defaultWidth?: PreviewWidth;
|
|
30
35
|
filterBy?: Filter;
|
|
31
36
|
}
|
|
32
37
|
|
|
@@ -115,6 +120,8 @@ export type ComponentObject = {
|
|
|
115
120
|
story?: string;
|
|
116
121
|
/** Optional path to schema file (if available) */
|
|
117
122
|
schema?: string;
|
|
123
|
+
/** Declaration file path; a preview `render` is read from it at build and hydration time. */
|
|
124
|
+
declaration?: string;
|
|
118
125
|
/** Optional path to templates directory */
|
|
119
126
|
templates?: string;
|
|
120
127
|
};
|
|
@@ -192,6 +199,8 @@ export type TransformComponentTokensResult = {
|
|
|
192
199
|
story?: string;
|
|
193
200
|
schema?: string;
|
|
194
201
|
templates?: string;
|
|
202
|
+
/** See `ComponentObject.entries.declaration`. */
|
|
203
|
+
declaration?: string;
|
|
195
204
|
};
|
|
196
205
|
renderer?: RendererKind;
|
|
197
206
|
/** See `ComponentObject.sourceFormat`. Distinct from `format`, the preview's code language. */
|
|
@@ -212,6 +221,11 @@ export type OptionalPreviewRender = {
|
|
|
212
221
|
values: { [key: string]: string | string[] | any };
|
|
213
222
|
url: string;
|
|
214
223
|
usage?: string;
|
|
224
|
+
/**
|
|
225
|
+
* Build-time only: the server renders it and the client re-reads it from the declaration module.
|
|
226
|
+
* `JSON.stringify` drops it from published artifacts.
|
|
227
|
+
*/
|
|
228
|
+
render?: (args: Record<string, any>) => unknown;
|
|
215
229
|
/** Optional source preview/story key used to derive this preview */
|
|
216
230
|
sourcePreview?: string;
|
|
217
231
|
};
|
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
import esbuild from 'esbuild';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
|
|
4
|
+
const STYLE_EXTENSIONS = ['.css', '.scss', '.sass'];
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Keeps co-located stylesheet imports out of the JavaScript graph; the CSS pipeline compiles them.
|
|
8
|
+
*
|
|
9
|
+
* esbuild has no `.scss` loader, and a `.css` import fails too because these builds run with
|
|
10
|
+
* `write: false` and no output path. `empty` rather than a plugin: `esbuild.buildSync` rejects
|
|
11
|
+
* plugins, and the declaration and config loaders are synchronous.
|
|
12
|
+
*
|
|
13
|
+
* CSS Modules are not covered — the empty module leaves the class map undefined.
|
|
14
|
+
*/
|
|
15
|
+
export const STYLE_IMPORT_LOADERS: Record<string, esbuild.Loader> = Object.fromEntries(
|
|
16
|
+
STYLE_EXTENSIONS.map((extension) => [extension, 'empty' as esbuild.Loader])
|
|
17
|
+
);
|
|
18
|
+
|
|
4
19
|
/**
|
|
5
20
|
* Default esbuild configuration for SSR builds
|
|
6
21
|
*/
|
|
@@ -11,6 +26,7 @@ export const DEFAULT_SSR_BUILD_CONFIG: esbuild.BuildOptions = {
|
|
|
11
26
|
platform: 'node',
|
|
12
27
|
jsx: 'automatic',
|
|
13
28
|
external: ['react', 'react-dom', '@opentelemetry/api'],
|
|
29
|
+
loader: STYLE_IMPORT_LOADERS,
|
|
14
30
|
};
|
|
15
31
|
|
|
16
32
|
/**
|
|
@@ -24,8 +40,72 @@ export const DEFAULT_CLIENT_BUILD_CONFIG: esbuild.BuildOptions = {
|
|
|
24
40
|
jsx: 'automatic',
|
|
25
41
|
sourcemap: false,
|
|
26
42
|
minify: false,
|
|
43
|
+
loader: STYLE_IMPORT_LOADERS,
|
|
27
44
|
};
|
|
28
45
|
|
|
46
|
+
/** Apply after a consumer hook: a hook that replaces `loader` would otherwise drop these. */
|
|
47
|
+
export function withStyleLoaders<T extends esbuild.BuildOptions>(config: T): T {
|
|
48
|
+
return { ...config, loader: { ...config.loader, ...STYLE_IMPORT_LOADERS } };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Stylesheets of the module graph in cascade order: every import of a module before the module
|
|
53
|
+
* itself, siblings in source order.
|
|
54
|
+
*
|
|
55
|
+
* This is the order a bundler emits CSS in, and the cascade depends on it. A component that
|
|
56
|
+
* overrides a rule of a component it imports ties that rule on specificity, so the later of the
|
|
57
|
+
* two wins. esbuild's `onLoad` callbacks do not give this order: they fire roughly breadth-first,
|
|
58
|
+
* which puts a component's own stylesheet ahead of the ones it imports and reverses every such
|
|
59
|
+
* override.
|
|
60
|
+
*/
|
|
61
|
+
function orderStyleImports(metafile: esbuild.Metafile, entryPath: string): string[] {
|
|
62
|
+
const entryKey = Object.keys(metafile.inputs).find((key) => path.resolve(key) === entryPath);
|
|
63
|
+
if (!entryKey) return [];
|
|
64
|
+
|
|
65
|
+
const ordered: string[] = [];
|
|
66
|
+
const visited = new Set<string>();
|
|
67
|
+
|
|
68
|
+
const visit = (key: string) => {
|
|
69
|
+
if (visited.has(key)) return;
|
|
70
|
+
visited.add(key);
|
|
71
|
+
|
|
72
|
+
for (const imported of metafile.inputs[key]?.imports ?? []) {
|
|
73
|
+
if (!imported.external && metafile.inputs[imported.path]) visit(imported.path);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (STYLE_EXTENSIONS.includes(path.extname(key))) ordered.push(path.resolve(key));
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
visit(entryKey);
|
|
80
|
+
|
|
81
|
+
return ordered;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Stylesheet paths imported anywhere in a component's module graph, in cascade order.
|
|
86
|
+
*
|
|
87
|
+
* An unbuildable graph yields an empty list rather than throwing, leaving the CSS step with the
|
|
88
|
+
* declared entry.
|
|
89
|
+
*/
|
|
90
|
+
export async function collectStyleImports(entryPath: string, handoff: any): Promise<string[]> {
|
|
91
|
+
const baseConfig: esbuild.BuildOptions = {
|
|
92
|
+
...DEFAULT_SSR_BUILD_CONFIG,
|
|
93
|
+
entryPoints: [entryPath],
|
|
94
|
+
logLevel: 'silent',
|
|
95
|
+
};
|
|
96
|
+
const hookedConfig = handoff?.config?.hooks?.ssrBuildConfig ? handoff.config.hooks.ssrBuildConfig(baseConfig) : baseConfig;
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
// The `empty` loader keeps stylesheets out of the JavaScript, and the metafile still records
|
|
100
|
+
// them as inputs, so the graph carries the order.
|
|
101
|
+
const result = await esbuild.build({ ...withStyleLoaders(hookedConfig), metafile: true });
|
|
102
|
+
|
|
103
|
+
return result.metafile ? orderStyleImports(result.metafile, entryPath) : [];
|
|
104
|
+
} catch {
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
29
109
|
/**
|
|
30
110
|
* Resolves a module from multiple search directories
|
|
31
111
|
* @param id - Module ID to resolve
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import esbuild from 'esbuild';
|
|
2
2
|
import { ModuleEvaluationResult } from '../types';
|
|
3
|
-
import { DEFAULT_SSR_BUILD_CONFIG } from './build';
|
|
3
|
+
import { DEFAULT_SSR_BUILD_CONFIG, withStyleLoaders } from './build';
|
|
4
4
|
|
|
5
5
|
function createModuleBuildConfig(entryPath: string, handoff: any): esbuild.BuildOptions {
|
|
6
6
|
const defaultBuildConfig: esbuild.BuildOptions = {
|
|
@@ -8,9 +8,11 @@ function createModuleBuildConfig(entryPath: string, handoff: any): esbuild.Build
|
|
|
8
8
|
entryPoints: [entryPath],
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
const hookedBuildConfig = handoff.config?.hooks?.ssrBuildConfig
|
|
12
12
|
? handoff.config.hooks.ssrBuildConfig(defaultBuildConfig)
|
|
13
13
|
: defaultBuildConfig;
|
|
14
|
+
|
|
15
|
+
return withStyleLoaders(hookedBuildConfig);
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
function evaluateBuiltModule(code: string): ModuleEvaluationResult {
|
package/src/types/config.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { EnvValue, EnvSecret, RuntimeEnvReference } from '../config/from-env';
|
|
1
2
|
import { BuildOptions } from 'esbuild';
|
|
2
3
|
import type Handlebars from 'handlebars';
|
|
3
4
|
import { Types as HandoffTypes } from 'handoff-core';
|
|
@@ -58,8 +59,8 @@ export type RegisterHandlebarsHelpersContext = {
|
|
|
58
59
|
|
|
59
60
|
export interface NextAppConfig {
|
|
60
61
|
theme?: string;
|
|
61
|
-
title
|
|
62
|
-
client
|
|
62
|
+
title?: string;
|
|
63
|
+
client?: string;
|
|
63
64
|
google_tag_manager?: string | null | undefined;
|
|
64
65
|
googleTagManager?: string | null | undefined;
|
|
65
66
|
type_copy?: string;
|
|
@@ -68,15 +69,17 @@ export interface NextAppConfig {
|
|
|
68
69
|
typeSort?: string[];
|
|
69
70
|
color_sort?: string[];
|
|
70
71
|
colorSort?: string[];
|
|
71
|
-
|
|
72
|
+
/** Omit to keep the defaults; a declared block merges onto them rather than replacing them. */
|
|
73
|
+
breakpoints?: Breakpoints;
|
|
72
74
|
component_sort?: string[];
|
|
73
75
|
componentSort?: string[];
|
|
74
76
|
base_path?: string;
|
|
75
77
|
basePath?: string;
|
|
76
|
-
|
|
78
|
+
/** @default true */
|
|
79
|
+
attribution?: boolean;
|
|
77
80
|
ports?: {
|
|
78
|
-
app: number
|
|
79
|
-
websocket: number
|
|
81
|
+
app: EnvValue<number>;
|
|
82
|
+
websocket: EnvValue<number>;
|
|
80
83
|
};
|
|
81
84
|
}
|
|
82
85
|
|
|
@@ -139,13 +142,8 @@ export interface HandoffRuntimeConfig {
|
|
|
139
142
|
};
|
|
140
143
|
/** Registry-mode host settings. Env-var values are stored as names, never as secrets. */
|
|
141
144
|
registry?: {
|
|
142
|
-
/**
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* @deprecated Fixed registry secrets are no longer authorized. This option remains parseable
|
|
146
|
-
* only so existing configuration files do not break.
|
|
147
|
-
*/
|
|
148
|
-
apiTokenEnv?: string;
|
|
145
|
+
/** Environment reference to the database URL. @default fromEnv('DATABASE_URL') */
|
|
146
|
+
databaseUrl?: RuntimeEnvReference<string>;
|
|
149
147
|
database?: {
|
|
150
148
|
/**
|
|
151
149
|
* PostgreSQL is the supported database. `driver` selects the built-in connection driver — how
|
|
@@ -168,11 +166,11 @@ export interface HandoffRuntimeConfig {
|
|
|
168
166
|
adapter?: 'database' | 'vercel-blob' | 'custom';
|
|
169
167
|
/** For `adapter: "custom"` - server-only module path default-exporting a `defineAssetStorage` adapter. */
|
|
170
168
|
module?: string;
|
|
171
|
-
/** For `adapter: "vercel-blob"` -
|
|
172
|
-
|
|
169
|
+
/** For `adapter: "vercel-blob"` - environment reference to the Blob read/write token. @default fromEnv('BLOB_READ_WRITE_TOKEN') */
|
|
170
|
+
token?: RuntimeEnvReference<string>;
|
|
173
171
|
/** Max bytes kept inline in the database `bytea` column (larger uploads are rejected). @default 4194304 */
|
|
174
172
|
maxInlineBytes?: number;
|
|
175
|
-
/** Non-secret
|
|
173
|
+
/** Non-secret options, JSON-encoded into the bundle. Environment references are rejected. Pass variable names instead. */
|
|
176
174
|
options?: Record<string, unknown>;
|
|
177
175
|
};
|
|
178
176
|
};
|
|
@@ -181,22 +179,21 @@ export interface HandoffRuntimeConfig {
|
|
|
181
179
|
* `mode: 'workspace'` plus this block — it is not a third runtime mode.
|
|
182
180
|
*/
|
|
183
181
|
registryConnection?: {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
accessTokenEnv?: string;
|
|
182
|
+
/** @default fromEnv('HANDOFF_REGISTRY_URL', { default: '' }) */
|
|
183
|
+
url?: EnvValue<string>;
|
|
184
|
+
/** @default fromEnv('HANDOFF_REGISTRY_ACCESS_TOKEN', { default: '' }) */
|
|
185
|
+
accessToken?: EnvSecret<string>;
|
|
189
186
|
};
|
|
190
187
|
}
|
|
191
188
|
|
|
192
189
|
export interface Config {
|
|
193
|
-
dev_access_token?: string | null
|
|
194
|
-
devAccessToken?: string | null
|
|
195
|
-
figma_project_id?: string | null
|
|
196
|
-
figmaProjectId?: string | null
|
|
197
|
-
exportsOutputDirectory?: string
|
|
198
|
-
sitesOutputDirectory?: string
|
|
199
|
-
useVariables?: boolean
|
|
190
|
+
dev_access_token?: EnvSecret<string | null>;
|
|
191
|
+
devAccessToken?: EnvSecret<string | null>;
|
|
192
|
+
figma_project_id?: EnvValue<string | null>;
|
|
193
|
+
figmaProjectId?: EnvValue<string | null>;
|
|
194
|
+
exportsOutputDirectory?: EnvValue<string>;
|
|
195
|
+
sitesOutputDirectory?: EnvValue<string>;
|
|
196
|
+
useVariables?: EnvValue<boolean>;
|
|
200
197
|
/**
|
|
201
198
|
* Configuration for React component docs generation (handoff-docgen).
|
|
202
199
|
*/
|
|
@@ -278,6 +275,9 @@ export interface Config {
|
|
|
278
275
|
|
|
279
276
|
/**
|
|
280
277
|
* Optional hook to override the SSR build configuration used in the ssrRenderPlugin
|
|
278
|
+
*
|
|
279
|
+
* Stylesheet loaders are re-applied after this hook, so a `.css`/`.scss`/`.sass` import stays out
|
|
280
|
+
* of the JavaScript graph whatever the hook returns.
|
|
281
281
|
* @param config - The default esbuild configuration
|
|
282
282
|
* @returns Modified esbuild configuration
|
|
283
283
|
* @example
|
|
@@ -292,6 +292,9 @@ export interface Config {
|
|
|
292
292
|
|
|
293
293
|
/**
|
|
294
294
|
* Optional hook to override the client-side build configuration used in the ssrRenderPlugin
|
|
295
|
+
*
|
|
296
|
+
* Stylesheet loaders are re-applied after this hook, so a `.css`/`.scss`/`.sass` import stays out
|
|
297
|
+
* of the JavaScript graph whatever the hook returns.
|
|
295
298
|
* @param config - The default esbuild configuration
|
|
296
299
|
* @returns Modified esbuild configuration
|
|
297
300
|
* @example
|
|
@@ -396,7 +399,26 @@ export interface ClientRuntimeConfig {
|
|
|
396
399
|
connected: boolean;
|
|
397
400
|
}
|
|
398
401
|
|
|
399
|
-
|
|
402
|
+
/** Values available after config loading. Deferred references retain their names. */
|
|
403
|
+
export type ResolvedConfig = Omit<
|
|
404
|
+
Config,
|
|
405
|
+
'dev_access_token' | 'devAccessToken' | 'figma_project_id' | 'figmaProjectId' |
|
|
406
|
+
'exportsOutputDirectory' | 'sitesOutputDirectory' | 'useVariables' | 'app' | 'runtime'
|
|
407
|
+
> & {
|
|
408
|
+
dev_access_token?: string | null;
|
|
409
|
+
devAccessToken?: string | null;
|
|
410
|
+
figma_project_id?: string | null;
|
|
411
|
+
figmaProjectId?: string | null;
|
|
412
|
+
exportsOutputDirectory?: string;
|
|
413
|
+
sitesOutputDirectory?: string;
|
|
414
|
+
useVariables?: boolean;
|
|
415
|
+
app?: Omit<NextAppConfig, 'ports'> & { ports?: { app: number; websocket: number } };
|
|
416
|
+
runtime?: Omit<HandoffRuntimeConfig, 'registryConnection'> & {
|
|
417
|
+
registryConnection?: { url?: string; accessToken?: string };
|
|
418
|
+
};
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
export type ClientConfig = Pick<ResolvedConfig, 'app' | 'exportsOutputDirectory' | 'sitesOutputDirectory' | 'assets_zip_links' | 'useVariables'> & {
|
|
400
422
|
runtime: ClientRuntimeConfig;
|
|
401
423
|
};
|
|
402
424
|
|
|
@@ -1,2 +1,12 @@
|
|
|
1
1
|
HANDOFF_FIGMA_PROJECT_ID={{figmaProjectId}}
|
|
2
2
|
HANDOFF_DEV_ACCESS_TOKEN={{figmaAccessToken}}
|
|
3
|
+
|
|
4
|
+
# Optional. Select a Handoff configuration profile.
|
|
5
|
+
# HANDOFF_PROFILE=local
|
|
6
|
+
#
|
|
7
|
+
# A selected profile also reads .env.<profile> on top of this file, when that file exists.
|
|
8
|
+
|
|
9
|
+
# Optional. Connect this workspace to a registry.
|
|
10
|
+
# A token is created in the registry under Account > Access tokens.
|
|
11
|
+
# HANDOFF_REGISTRY_URL=https://registry.example.com
|
|
12
|
+
# HANDOFF_REGISTRY_ACCESS_TOKEN=hnd_...
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { defineConfig } = require('handoff-app');
|
|
1
|
+
const { defineConfig, fromEnv } = require('handoff-app');
|
|
2
2
|
/** @typedef {import('handoff-app').Config} HandoffConfig */
|
|
3
3
|
|
|
4
4
|
/** @type {HandoffConfig} */
|
|
@@ -42,181 +42,30 @@ const config = {
|
|
|
42
42
|
}
|
|
43
43
|
},
|
|
44
44
|
|
|
45
|
-
// catalog
|
|
46
|
-
//
|
|
47
|
-
//
|
|
48
|
-
// * collection directory whose subdirectories are each treated as an item. An item declares
|
|
49
|
-
// * an implementation (React, Handlebars) or a composition of other items.
|
|
50
|
-
// */
|
|
51
|
-
// include: ["components", "patterns"],
|
|
52
|
-
// },
|
|
53
|
-
|
|
54
|
-
// Runtime settings, all optional. The default is workspace mode with the MCP endpoint served.
|
|
55
|
-
// `mcp: false` leaves the /api/mcp/ endpoint, and the connect affordance in the docs header, out
|
|
56
|
-
// of the build; the route then answers 404.
|
|
57
|
-
// The registry block is only needed when deploying a hosted registry, and is used by
|
|
58
|
-
// `build --target registry` and `db:migrate`. The default workspace mode uses no database.
|
|
59
|
-
// Handoff runs on PostgreSQL, and `driver` just picks the connection driver to use, not
|
|
60
|
-
// the database engine. Secrets are referenced by env var name and never written here.
|
|
61
|
-
// runtime: {
|
|
62
|
-
// mode: "registry",
|
|
63
|
-
// mcp: false,
|
|
64
|
-
// registry: {
|
|
65
|
-
// databaseUrlEnv: "DATABASE_URL",
|
|
66
|
-
// database: { driver: "pg" }, // 'pg' | 'neon'
|
|
67
|
-
// },
|
|
68
|
-
// },
|
|
69
|
-
|
|
70
|
-
// Optional handoff-docgen settings used by component docs generation
|
|
71
|
-
// reactDocgen: {
|
|
72
|
-
// maxDepth: 7,
|
|
73
|
-
// excludeDirectories: ["dist", "build", ".next"]
|
|
74
|
-
// },
|
|
75
|
-
|
|
76
|
-
hooks: {
|
|
77
|
-
/**
|
|
78
|
-
* Optional validation callback for components
|
|
79
|
-
* @param component - The component instance to validate
|
|
80
|
-
* @returns A record of validation results where keys are validation types and values are detailed validation results
|
|
81
|
-
*
|
|
82
|
-
* @example
|
|
83
|
-
* validateComponent: async (component) => ({
|
|
84
|
-
* a11y: {
|
|
85
|
-
* description: 'Accessibility validation check',
|
|
86
|
-
* passed: true,
|
|
87
|
-
* messages: ['No accessibility issues found']
|
|
88
|
-
* },
|
|
89
|
-
* responsive: {
|
|
90
|
-
* description: 'Responsive design validation',
|
|
91
|
-
* passed: false,
|
|
92
|
-
* messages: ['Component breaks at mobile breakpoint']
|
|
93
|
-
* }
|
|
94
|
-
* })
|
|
95
|
-
*/
|
|
96
|
-
// validateComponent: async (component) => {
|
|
97
|
-
// // Add your custom validation logic here
|
|
98
|
-
// return {};
|
|
99
|
-
// },
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Optional hook to override the SSR build configuration used in the ssrRenderPlugin
|
|
103
|
-
* @param config - The default esbuild configuration
|
|
104
|
-
* @returns Modified esbuild configuration
|
|
105
|
-
*
|
|
106
|
-
* @example
|
|
107
|
-
* ssrBuildConfig: (config) => {
|
|
108
|
-
* // Modify the esbuild config as needed
|
|
109
|
-
* return config;
|
|
110
|
-
* }
|
|
111
|
-
*/
|
|
112
|
-
// ssrBuildConfig: (config) => {
|
|
113
|
-
// // Add your custom SSR build configuration here
|
|
114
|
-
// return config;
|
|
115
|
-
// },
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Optional hook to override the client-side build configuration used in the ssrRenderPlugin
|
|
119
|
-
* @param config - The default esbuild configuration
|
|
120
|
-
* @returns Modified esbuild configuration
|
|
121
|
-
*
|
|
122
|
-
* @example
|
|
123
|
-
* clientBuildConfig: (config) => {
|
|
124
|
-
* // Modify the esbuild config as needed
|
|
125
|
-
* return config;
|
|
126
|
-
* }
|
|
127
|
-
*/
|
|
128
|
-
// clientBuildConfig: (config) => {
|
|
129
|
-
// // Add your custom client build configuration here
|
|
130
|
-
// return config;
|
|
131
|
-
// },
|
|
45
|
+
// Where catalog items live. A path is either an item directory, or a collection directory whose
|
|
46
|
+
// subdirectories are each an item.
|
|
47
|
+
// catalog: { include: ["components", "patterns"] },
|
|
132
48
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
//
|
|
142
|
-
//
|
|
143
|
-
// return exports.default;
|
|
144
|
-
// },
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Optional hook to transform the schema into properties
|
|
148
|
-
* @param schema - The schema object to transform
|
|
149
|
-
* @returns The transformed properties object
|
|
150
|
-
*/
|
|
151
|
-
// schemaToProperties: (schema) => {
|
|
152
|
-
// // Add your custom schema to properties transformation here
|
|
153
|
-
// return {};
|
|
154
|
-
// },
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Optional hook to override the JavaScript Vite configuration
|
|
158
|
-
* @param config - The default Vite configuration
|
|
159
|
-
* @returns Modified Vite configuration
|
|
160
|
-
*
|
|
161
|
-
* @example
|
|
162
|
-
* jsBuildConfig: (config) => {
|
|
163
|
-
* // Modify the Vite config as needed
|
|
164
|
-
* return config;
|
|
165
|
-
* }
|
|
166
|
-
*/
|
|
167
|
-
// jsBuildConfig: (config) => {
|
|
168
|
-
// // Add your custom JavaScript build configuration here
|
|
169
|
-
// return config;
|
|
170
|
-
// },
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Optional hook to override the CSS Vite configuration
|
|
174
|
-
* @param config - The default Vite configuration
|
|
175
|
-
* @returns Modified Vite configuration
|
|
176
|
-
*
|
|
177
|
-
* @example
|
|
178
|
-
* cssBuildConfig: (config) => {
|
|
179
|
-
* // Modify the Vite config as needed
|
|
180
|
-
* return config;
|
|
181
|
-
* }
|
|
182
|
-
*/
|
|
183
|
-
// cssBuildConfig: (config) => {
|
|
184
|
-
// // Add your custom CSS build configuration here
|
|
185
|
-
// return config;
|
|
49
|
+
// `mode` selects the runtime: "workspace" (no database) or "registry" (Postgres). The registry
|
|
50
|
+
// block is read only by `build --target registry` and `db:migrate`, and secrets are referenced by
|
|
51
|
+
// env var name. A named profile (`handoff.config.registry.*`, `--profile registry`) is a good home
|
|
52
|
+
// for it.
|
|
53
|
+
runtime: {
|
|
54
|
+
mode: "workspace",
|
|
55
|
+
// mcp: false,
|
|
56
|
+
// registry: {
|
|
57
|
+
// databaseUrl: fromEnv("DATABASE_URL"),
|
|
58
|
+
// database: { driver: "pg" }, // 'pg' | 'neon'
|
|
186
59
|
// },
|
|
60
|
+
},
|
|
187
61
|
|
|
188
|
-
|
|
189
|
-
* Optional hook to override the HTML Vite configuration
|
|
190
|
-
* @param config - The default Vite configuration
|
|
191
|
-
* @returns Modified Vite configuration
|
|
192
|
-
*
|
|
193
|
-
* @example
|
|
194
|
-
* htmlBuildConfig: (config) => {
|
|
195
|
-
* // Modify the Vite config as needed
|
|
196
|
-
* return config;
|
|
197
|
-
* }
|
|
198
|
-
*/
|
|
199
|
-
// htmlBuildConfig: (config) => {
|
|
200
|
-
// // Add your custom HTML build configuration here
|
|
201
|
-
// return config;
|
|
202
|
-
// },
|
|
62
|
+
// reactDocgen: { maxDepth: 7, excludeDirectories: ["dist", "build", ".next"] },
|
|
203
63
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
* @param context.injectFieldWrappers - True when generating inspect-mode previews
|
|
210
|
-
*
|
|
211
|
-
* @example
|
|
212
|
-
* registerHandlebarsHelpers: ({ handlebars, componentId }) => {
|
|
213
|
-
* handlebars.registerHelper('upperId', () => componentId.toUpperCase());
|
|
214
|
-
* }
|
|
215
|
-
*/
|
|
216
|
-
// registerHandlebarsHelpers: ({ handlebars, componentId, properties, injectFieldWrappers }) => {
|
|
217
|
-
// // Add your custom Handlebars helpers here
|
|
218
|
-
// },
|
|
219
|
-
},
|
|
64
|
+
// Optional build hooks: component validation, esbuild and Vite config overrides, Handlebars
|
|
65
|
+
// helper registration. Signatures and examples are on the `Config` type.
|
|
66
|
+
// hooks: {
|
|
67
|
+
// validateComponent: async (component) => ({}),
|
|
68
|
+
// },
|
|
220
69
|
};
|
|
221
70
|
|
|
222
71
|
module.exports = defineConfig(config);
|