handoff-app 1.1.2 → 1.1.3-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.
- package/Changelog.md +81 -0
- package/dist/app-builder/build.js +2 -0
- package/dist/app-builder/watchers.d.ts +9 -1
- package/dist/app-builder/watchers.js +59 -5
- package/dist/cli.js +1 -1
- package/package.json +2 -1
- package/src/app/components/ComponentGuidelines.tsx +3 -1
- package/src/app/components/Markdown/MarkdownComponents.tsx +183 -50
- package/src/app/components/Navigation/AnchorNav.tsx +28 -28
- package/src/app/components/util/index.ts +99 -79
- package/src/app/css/index.css +4 -0
- package/src/app/pages/{[level1] → [...slug]}/index.tsx +24 -31
- package/src/app/pages/assets/fonts.tsx +3 -2
- package/src/app/pages/assets/index.tsx +3 -2
- package/src/app/pages/assets/logos.tsx +3 -2
- package/src/app/pages/foundations/colors.tsx +3 -2
- package/src/app/pages/foundations/effects.tsx +3 -2
- package/src/app/pages/foundations/icons/index.tsx +3 -2
- package/src/app/pages/foundations/index.tsx +3 -2
- package/src/app/pages/foundations/logo.tsx +3 -2
- package/src/app/pages/foundations/typography.tsx +3 -2
- package/src/app/pages/index.tsx +3 -2
- package/src/app/pages/system/component/[component]/index.tsx +3 -2
- package/src/app/pages/system/index.tsx +3 -2
- package/src/app/pages/system/tokens/foundations/colors.tsx +3 -2
- package/src/app/pages/system/tokens/foundations/effects.tsx +3 -2
- package/src/app/pages/system/tokens/foundations/typography.tsx +3 -2
- package/src/app-builder/build.ts +3 -1
- package/src/app-builder/watchers.ts +66 -4
- package/src/cli.ts +1 -1
- package/src/app/pages/[level1]/[level2]/index.tsx +0 -104
|
@@ -3,8 +3,9 @@ import { groupBy, upperFirst } from 'lodash';
|
|
|
3
3
|
import type { GetStaticProps } from 'next';
|
|
4
4
|
import ReactMarkdown from 'react-markdown';
|
|
5
5
|
import rehypeRaw from 'rehype-raw';
|
|
6
|
+
import remarkGfm from 'remark-gfm';
|
|
6
7
|
import Layout from '../../../../components/Layout/Main';
|
|
7
|
-
import { MarkdownComponents } from '../../../../components/Markdown/MarkdownComponents';
|
|
8
|
+
import { MarkdownComponents, remarkCodeMeta } from '../../../../components/Markdown/MarkdownComponents';
|
|
8
9
|
import AnchorNav from '../../../../components/Navigation/AnchorNav';
|
|
9
10
|
import HeadersType from '../../../../components/Typography/Headers';
|
|
10
11
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '../../../../components/ui/table';
|
|
@@ -61,7 +62,7 @@ const ComponentsPage = ({ content, menu, metadata, current, config, design }: Fo
|
|
|
61
62
|
<div className="mt-10 lg:gap-10 lg:py-8 xl:grid xl:grid-cols-[1fr_280px]">
|
|
62
63
|
<div>
|
|
63
64
|
<div className="prose">
|
|
64
|
-
<ReactMarkdown components={MarkdownComponents} rehypePlugins={[rehypeRaw]}>
|
|
65
|
+
<ReactMarkdown components={MarkdownComponents} remarkPlugins={[remarkGfm, remarkCodeMeta]} rehypePlugins={[rehypeRaw]}>
|
|
65
66
|
{content}
|
|
66
67
|
</ReactMarkdown>
|
|
67
68
|
</div>
|
|
@@ -4,8 +4,9 @@ import type { GetStaticProps } from 'next';
|
|
|
4
4
|
import React, { ReactElement } from 'react';
|
|
5
5
|
import ReactMarkdown from 'react-markdown';
|
|
6
6
|
import rehypeRaw from 'rehype-raw';
|
|
7
|
+
import remarkGfm from 'remark-gfm';
|
|
7
8
|
import Layout from '../../../../components/Layout/Main';
|
|
8
|
-
import { MarkdownComponents } from '../../../../components/Markdown/MarkdownComponents';
|
|
9
|
+
import { MarkdownComponents, remarkCodeMeta } from '../../../../components/Markdown/MarkdownComponents';
|
|
9
10
|
import AnchorNav from '../../../../components/Navigation/AnchorNav';
|
|
10
11
|
import HeadersType from '../../../../components/Typography/Headers';
|
|
11
12
|
import { Table, TableBody, TableCell, TableRow } from '../../../../components/ui/table';
|
|
@@ -53,7 +54,7 @@ const ComponentsPage = ({ content, menu, metadata, current, config, design }: Fo
|
|
|
53
54
|
<div className="mt-10 lg:gap-10 lg:py-8 xl:grid xl:grid-cols-[1fr_280px]">
|
|
54
55
|
<div>
|
|
55
56
|
<div className="prose">
|
|
56
|
-
<ReactMarkdown components={MarkdownComponents} rehypePlugins={[rehypeRaw]}>
|
|
57
|
+
<ReactMarkdown components={MarkdownComponents} remarkPlugins={[remarkGfm, remarkCodeMeta]} rehypePlugins={[rehypeRaw]}>
|
|
57
58
|
{content}
|
|
58
59
|
</ReactMarkdown>
|
|
59
60
|
</div>
|
package/src/app-builder/build.ts
CHANGED
|
@@ -9,7 +9,7 @@ import processComponents from '../transformers/preview/component/builder';
|
|
|
9
9
|
import { Logger } from '../utils/logger';
|
|
10
10
|
import { generateTokensApi, persistClientConfig } from './client-config';
|
|
11
11
|
import { getAppPath, syncPublicFiles } from './paths';
|
|
12
|
-
import { WatcherState, getRuntimeComponentsPathsToWatch, watchAppSource, watchGlobalEntries, watchPages, watchPublicDirectory, watchRuntimeComponents, watchRuntimeConfiguration } from './watchers';
|
|
12
|
+
import { WatcherState, getRuntimeComponentsPathsToWatch, watchAppSource, watchComponentDirectories, watchGlobalEntries, watchPages, watchPublicDirectory, watchRuntimeComponents, watchRuntimeConfiguration } from './watchers';
|
|
13
13
|
import { createWebSocketServer } from './websocket';
|
|
14
14
|
|
|
15
15
|
const escapeForSingleQuotedJsString = (value: string): string => value.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
|
|
@@ -193,11 +193,13 @@ export const watchApp = async (handoff: Handoff): Promise<void> => {
|
|
|
193
193
|
debounce: false,
|
|
194
194
|
runtimeComponentsWatcher: null,
|
|
195
195
|
runtimeConfigurationWatcher: null,
|
|
196
|
+
componentDirectoriesWatcher: null,
|
|
196
197
|
};
|
|
197
198
|
|
|
198
199
|
watchPublicDirectory(handoff, wss, state, chokidarConfig);
|
|
199
200
|
watchRuntimeComponents(handoff, state, getRuntimeComponentsPathsToWatch(handoff));
|
|
200
201
|
watchRuntimeConfiguration(handoff, state);
|
|
202
|
+
watchComponentDirectories(handoff, state, chokidarConfig);
|
|
201
203
|
watchGlobalEntries(handoff, state, chokidarConfig);
|
|
202
204
|
watchPages(handoff, chokidarConfig);
|
|
203
205
|
};
|
|
@@ -15,6 +15,7 @@ export interface WatcherState {
|
|
|
15
15
|
debounce: boolean;
|
|
16
16
|
runtimeComponentsWatcher: chokidar.FSWatcher | null;
|
|
17
17
|
runtimeConfigurationWatcher: chokidar.FSWatcher | null;
|
|
18
|
+
componentDirectoriesWatcher: chokidar.FSWatcher | null;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
/**
|
|
@@ -258,13 +259,9 @@ export const watchRuntimeConfiguration = (handoff: Handoff, state: WatcherState)
|
|
|
258
259
|
state.debounce = true;
|
|
259
260
|
try {
|
|
260
261
|
file = path.dirname(file);
|
|
261
|
-
// Reload the Handoff instance to pick up configuration changes
|
|
262
262
|
handoff.reload();
|
|
263
|
-
// After reloading, persist the updated client configuration
|
|
264
263
|
await persistClientConfig(handoff);
|
|
265
|
-
// Restart the runtime components watcher to track potentially updated/added/removed components
|
|
266
264
|
watchRuntimeComponents(handoff, state, getRuntimeComponentsPathsToWatch(handoff));
|
|
267
|
-
// Process components based on the updated configuration and file path
|
|
268
265
|
await processComponents(handoff, path.basename(file));
|
|
269
266
|
} catch (e) {
|
|
270
267
|
Logger.error('Error reloading runtime configuration:', e);
|
|
@@ -277,3 +274,68 @@ export const watchRuntimeConfiguration = (handoff: Handoff, state: WatcherState)
|
|
|
277
274
|
});
|
|
278
275
|
}
|
|
279
276
|
};
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Watches the parent component directories from config.entries.components
|
|
280
|
+
* for new components being added. When a new config file (e.g. button.json)
|
|
281
|
+
* appears in a new subdirectory, reloads the runtime config and restarts
|
|
282
|
+
* the component/configuration watchers so the new component is picked up.
|
|
283
|
+
*/
|
|
284
|
+
export const watchComponentDirectories = (handoff: Handoff, state: WatcherState, chokidarConfig: chokidar.WatchOptions) => {
|
|
285
|
+
if (state.componentDirectoriesWatcher) {
|
|
286
|
+
state.componentDirectoriesWatcher.close();
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const componentPaths = handoff.config.entries?.components ?? [];
|
|
290
|
+
if (componentPaths.length === 0) return;
|
|
291
|
+
|
|
292
|
+
const dirsToWatch: string[] = [];
|
|
293
|
+
for (const componentPath of componentPaths) {
|
|
294
|
+
const resolved = path.resolve(handoff.workingPath, componentPath);
|
|
295
|
+
if (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory()) {
|
|
296
|
+
dirsToWatch.push(resolved);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (dirsToWatch.length === 0) return;
|
|
301
|
+
|
|
302
|
+
const knownComponents = new Set(Object.keys(handoff.runtimeConfig?.entries?.components ?? {}));
|
|
303
|
+
|
|
304
|
+
state.componentDirectoriesWatcher = chokidar.watch(dirsToWatch, {
|
|
305
|
+
...chokidarConfig,
|
|
306
|
+
depth: 1,
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
state.componentDirectoriesWatcher.on('add', async (file) => {
|
|
310
|
+
const basename = path.basename(file);
|
|
311
|
+
const dirName = path.basename(path.dirname(file));
|
|
312
|
+
|
|
313
|
+
const isConfigFile = basename.endsWith('.json') || basename.endsWith('.js') || basename.endsWith('.cjs');
|
|
314
|
+
const isNewComponent = isConfigFile && basename.startsWith(dirName) && !knownComponents.has(dirName);
|
|
315
|
+
|
|
316
|
+
if (!isNewComponent) return;
|
|
317
|
+
|
|
318
|
+
if (!state.debounce) {
|
|
319
|
+
state.debounce = true;
|
|
320
|
+
try {
|
|
321
|
+
Logger.warn(`New component detected: ${dirName}. Reloading configuration...`);
|
|
322
|
+
handoff.reload();
|
|
323
|
+
knownComponents.add(dirName);
|
|
324
|
+
|
|
325
|
+
// Update known set with all current components
|
|
326
|
+
for (const id of Object.keys(handoff.runtimeConfig?.entries?.components ?? {})) {
|
|
327
|
+
knownComponents.add(id);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
await persistClientConfig(handoff);
|
|
331
|
+
watchRuntimeComponents(handoff, state, getRuntimeComponentsPathsToWatch(handoff));
|
|
332
|
+
watchRuntimeConfiguration(handoff, state);
|
|
333
|
+
await processComponents(handoff, dirName);
|
|
334
|
+
} catch (e) {
|
|
335
|
+
Logger.error('Error processing new component:', e);
|
|
336
|
+
} finally {
|
|
337
|
+
state.debounce = false;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
};
|
package/src/cli.ts
CHANGED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import { GetStaticProps } from 'next';
|
|
2
|
-
import Head from 'next/head';
|
|
3
|
-
import Link from 'next/link';
|
|
4
|
-
import ReactMarkdown from 'react-markdown';
|
|
5
|
-
import rehypeRaw from 'rehype-raw';
|
|
6
|
-
import Layout from '../../../components/Layout/Main';
|
|
7
|
-
import { MarkdownComponents } from '../../../components/Markdown/MarkdownComponents';
|
|
8
|
-
import HeadersType from '../../../components/Typography/Headers';
|
|
9
|
-
import {
|
|
10
|
-
buildL2StaticPaths,
|
|
11
|
-
DocumentationProps,
|
|
12
|
-
fetchDocPageMarkdown,
|
|
13
|
-
getClientRuntimeConfig,
|
|
14
|
-
IParams,
|
|
15
|
-
reduceSlugToString,
|
|
16
|
-
} from '../../../components/util';
|
|
17
|
-
|
|
18
|
-
export interface SubPageType {
|
|
19
|
-
params: {
|
|
20
|
-
level1: string;
|
|
21
|
-
level2: string;
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* Render all index pages
|
|
26
|
-
* @returns
|
|
27
|
-
*/
|
|
28
|
-
export async function getStaticPaths() {
|
|
29
|
-
return {
|
|
30
|
-
paths: buildL2StaticPaths(),
|
|
31
|
-
fallback: false,
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Get all the markdown data, build a menu tree, and then fetch the contents
|
|
36
|
-
* of the current page for rendering
|
|
37
|
-
*
|
|
38
|
-
* This is all done statically at build time
|
|
39
|
-
* @param context GetStaticProps
|
|
40
|
-
* @returns
|
|
41
|
-
*/
|
|
42
|
-
export const getStaticProps: GetStaticProps = async (context) => {
|
|
43
|
-
// Read current slug
|
|
44
|
-
const { level1 } = context.params as IParams;
|
|
45
|
-
let { level2 } = context.params as IParams;
|
|
46
|
-
if (!level2) {
|
|
47
|
-
level2 = '404';
|
|
48
|
-
}
|
|
49
|
-
return {
|
|
50
|
-
props: {
|
|
51
|
-
...fetchDocPageMarkdown(`docs/${level1}/`, reduceSlugToString(level2), `/${level1}`).props,
|
|
52
|
-
config: getClientRuntimeConfig(),
|
|
53
|
-
},
|
|
54
|
-
};
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Render Docs page
|
|
59
|
-
* @param param0
|
|
60
|
-
* @returns
|
|
61
|
-
*/
|
|
62
|
-
export default function DocSubPage({ content, menu, metadata, current, config }: DocumentationProps) {
|
|
63
|
-
if (content) {
|
|
64
|
-
return (
|
|
65
|
-
<Layout config={config} menu={menu} current={current} metadata={metadata}>
|
|
66
|
-
<div className="flex flex-col gap-2 pb-7">
|
|
67
|
-
<HeadersType.H1>{metadata.title}</HeadersType.H1>
|
|
68
|
-
<p className="text-lg leading-relaxed text-gray-600 dark:text-gray-300">{metadata.description}</p>
|
|
69
|
-
</div>
|
|
70
|
-
<div>
|
|
71
|
-
<div className="prose mb-10">
|
|
72
|
-
<ReactMarkdown components={MarkdownComponents} rehypePlugins={[rehypeRaw]}>
|
|
73
|
-
{content}
|
|
74
|
-
</ReactMarkdown>
|
|
75
|
-
</div>
|
|
76
|
-
</div>
|
|
77
|
-
</Layout>
|
|
78
|
-
);
|
|
79
|
-
} else {
|
|
80
|
-
return (
|
|
81
|
-
<div className="flex min-h-screen flex-col items-center justify-center bg-white dark:bg-gray-900">
|
|
82
|
-
<Head>
|
|
83
|
-
<title>404 - Page Not Found</title>
|
|
84
|
-
<meta name="description" content="Page Not Found" />
|
|
85
|
-
</Head>
|
|
86
|
-
<div className="flex flex-col items-center">
|
|
87
|
-
<div className="mb-2 text-7xl font-bold text-gray-800 dark:text-white">404</div>
|
|
88
|
-
<h1 className="mb-4 text-2xl font-semibold text-gray-700 dark:text-gray-300">Oops! Page not found.</h1>
|
|
89
|
-
<p className="mb-6 max-w-md text-center text-gray-500 dark:text-gray-400">
|
|
90
|
-
Sorry, the page you are looking for does not exist or has been moved.
|
|
91
|
-
<br />
|
|
92
|
-
Please check the URL or return to the homepage.
|
|
93
|
-
</p>
|
|
94
|
-
<Link
|
|
95
|
-
href="/"
|
|
96
|
-
className="rounded-md bg-blue-600 px-6 py-2 font-medium text-white shadow-md transition-colors duration-200 hover:bg-blue-700"
|
|
97
|
-
>
|
|
98
|
-
Go Home
|
|
99
|
-
</Link>
|
|
100
|
-
</div>
|
|
101
|
-
</div>
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
|
-
}
|