handoff-app 1.0.3 → 1.0.5
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 +16 -5
- package/README.md +93 -57
- package/dist/cli.js +1 -1
- package/dist/commands/index.d.ts +1 -1
- package/dist/commands/index.js +2 -0
- package/dist/commands/init/index.d.ts +5 -0
- package/dist/commands/init/index.js +26 -0
- package/dist/create.d.ts +5 -0
- package/dist/create.js +293 -0
- package/dist/index.js +4 -1
- package/dist/transformers/plugins/ssr-render.js +4 -3
- package/dist/transformers/preview/component/api.js +3 -2
- package/dist/transformers/preview/component/builder.js +2 -1
- package/dist/transformers/utils/handlebars.js +3 -2
- package/package.json +1 -1
- package/src/app/components/Component/Preview.tsx +121 -121
- package/src/app/components/ComponentSearch.tsx +1 -1
- package/src/app/components/util/index.ts +17 -9
- package/src/app/pages/system/component/[component]/index.tsx +1 -1
- package/src/app/pages/system/index.tsx +2 -2
- package/src/app/pages/system/tokens/index.tsx +10 -10
- package/src/cli.ts +1 -1
- package/src/commands/index.ts +2 -0
- package/src/commands/init/index.ts +17 -0
- package/src/create.ts +302 -0
- package/src/index.ts +3 -0
- package/src/transformers/plugins/ssr-render.ts +3 -3
- package/src/transformers/preview/component/api.ts +1 -1
- package/src/transformers/preview/component/builder.ts +1 -1
- package/src/transformers/utils/handlebars.ts +2 -2
- package/templates/default/README.md.tpl +15 -0
- package/templates/default/components/button/Button.tsx.tpl +92 -0
- package/templates/default/components/button/button.js.tpl +62 -0
- package/templates/default/env.tpl +2 -0
- package/templates/default/gitignore.tpl +7 -0
- package/templates/default/handoff.config.js.tpl +181 -0
- package/templates/default/handoff.config.with-examples.js.tpl +182 -0
- package/templates/default/package.json.tpl +20 -0
- package/templates/default/tsconfig.json.tpl +24 -0
- package/dist/commands/platform/init.d.ts +0 -6
- package/dist/commands/platform/init.js +0 -231
- package/dist/commands/platform/login.d.ts +0 -6
- package/dist/commands/platform/login.js +0 -108
- package/dist/commands/platform/logout.d.ts +0 -6
- package/dist/commands/platform/logout.js +0 -88
- package/dist/commands/platform/pull.d.ts +0 -8
- package/dist/commands/platform/pull.js +0 -112
- package/dist/commands/platform/push.d.ts +0 -8
- package/dist/commands/platform/push.js +0 -428
- package/dist/platform/client.d.ts +0 -122
- package/dist/platform/client.js +0 -228
- package/dist/platform/credentials.d.ts +0 -40
- package/dist/platform/credentials.js +0 -91
- package/dist/platform/sync.d.ts +0 -68
- package/dist/platform/sync.js +0 -349
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
/** @type {import('handoff-app').Config} */
|
|
4
|
+
module.exports = {
|
|
5
|
+
app: {
|
|
6
|
+
theme: "default",
|
|
7
|
+
title: "{{projectName}} Design System",
|
|
8
|
+
client: "{{projectName}}",
|
|
9
|
+
google_tag_manager: null,
|
|
10
|
+
attribution: true,
|
|
11
|
+
type_copy: "Almost before we knew it, we had left the ground.",
|
|
12
|
+
type_sort: [
|
|
13
|
+
"Heading 1",
|
|
14
|
+
"Heading 2",
|
|
15
|
+
"Heading 3",
|
|
16
|
+
"Heading 4",
|
|
17
|
+
"Heading 5",
|
|
18
|
+
"Heading 6",
|
|
19
|
+
"Paragraph",
|
|
20
|
+
"Subheading",
|
|
21
|
+
"Blockquote",
|
|
22
|
+
"Input Labels",
|
|
23
|
+
"Link"
|
|
24
|
+
],
|
|
25
|
+
color_sort: [
|
|
26
|
+
"primary",
|
|
27
|
+
"secondary",
|
|
28
|
+
"extra",
|
|
29
|
+
"system"
|
|
30
|
+
],
|
|
31
|
+
component_sort: [
|
|
32
|
+
"primary",
|
|
33
|
+
"secondary",
|
|
34
|
+
"transparent"
|
|
35
|
+
],
|
|
36
|
+
base_path: "",
|
|
37
|
+
breakpoints: {
|
|
38
|
+
sm: { size: 576, name: "Small" },
|
|
39
|
+
md: { size: 768, name: "Medium" },
|
|
40
|
+
lg: { size: 992, name: "Large" },
|
|
41
|
+
xl: { size: 1200, name: "Extra Large" }
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
// entries: {
|
|
46
|
+
// /**
|
|
47
|
+
// * Array of component paths to be included in the build
|
|
48
|
+
// * Add your component directories here
|
|
49
|
+
// */
|
|
50
|
+
// components: ["components/button"]
|
|
51
|
+
// },
|
|
52
|
+
|
|
53
|
+
hooks: {
|
|
54
|
+
/**
|
|
55
|
+
* Optional validation callback for components
|
|
56
|
+
* @param component - The component instance to validate
|
|
57
|
+
* @returns A record of validation results where keys are validation types and values are detailed validation results
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* validateComponent: async (component) => ({
|
|
61
|
+
* a11y: {
|
|
62
|
+
* description: 'Accessibility validation check',
|
|
63
|
+
* passed: true,
|
|
64
|
+
* messages: ['No accessibility issues found']
|
|
65
|
+
* },
|
|
66
|
+
* responsive: {
|
|
67
|
+
* description: 'Responsive design validation',
|
|
68
|
+
* passed: false,
|
|
69
|
+
* messages: ['Component breaks at mobile breakpoint']
|
|
70
|
+
* }
|
|
71
|
+
* })
|
|
72
|
+
*/
|
|
73
|
+
// validateComponent: async (component) => {
|
|
74
|
+
// // Add your custom validation logic here
|
|
75
|
+
// return {};
|
|
76
|
+
// },
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Optional hook to override the SSR build configuration used in the ssrRenderPlugin
|
|
80
|
+
* @param config - The default esbuild configuration
|
|
81
|
+
* @returns Modified esbuild configuration
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ssrBuildConfig: (config) => {
|
|
85
|
+
* // Modify the esbuild config as needed
|
|
86
|
+
* return config;
|
|
87
|
+
* }
|
|
88
|
+
*/
|
|
89
|
+
// ssrBuildConfig: (config) => {
|
|
90
|
+
// // Add your custom SSR build configuration here
|
|
91
|
+
// return config;
|
|
92
|
+
// },
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Optional hook to override the client-side build configuration used in the ssrRenderPlugin
|
|
96
|
+
* @param config - The default esbuild configuration
|
|
97
|
+
* @returns Modified esbuild configuration
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* clientBuildConfig: (config) => {
|
|
101
|
+
* // Modify the esbuild config as needed
|
|
102
|
+
* return config;
|
|
103
|
+
* }
|
|
104
|
+
*/
|
|
105
|
+
// clientBuildConfig: (config) => {
|
|
106
|
+
// // Add your custom client build configuration here
|
|
107
|
+
// return config;
|
|
108
|
+
// },
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Optional hook to specify which export property contains the schema
|
|
112
|
+
* @param exports - The module exports object containing the schema
|
|
113
|
+
* @returns The schema object from the exports
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* getSchemaFromExports: (exports) => exports.customSchema || exports.default
|
|
117
|
+
*/
|
|
118
|
+
// getSchemaFromExports: (exports) => {
|
|
119
|
+
// // Add your custom schema extraction logic here
|
|
120
|
+
// return exports.default;
|
|
121
|
+
// },
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Optional hook to transform the schema into properties
|
|
125
|
+
* @param schema - The schema object to transform
|
|
126
|
+
* @returns The transformed properties object
|
|
127
|
+
*/
|
|
128
|
+
// schemaToProperties: (schema) => {
|
|
129
|
+
// // Add your custom schema to properties transformation here
|
|
130
|
+
// return {};
|
|
131
|
+
// },
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Optional hook to override the JavaScript Vite configuration
|
|
135
|
+
* @param config - The default Vite configuration
|
|
136
|
+
* @returns Modified Vite configuration
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* jsBuildConfig: (config) => {
|
|
140
|
+
* // Modify the Vite config as needed
|
|
141
|
+
* return config;
|
|
142
|
+
* }
|
|
143
|
+
*/
|
|
144
|
+
// jsBuildConfig: (config) => {
|
|
145
|
+
// // Add your custom JavaScript build configuration here
|
|
146
|
+
// return config;
|
|
147
|
+
// },
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Optional hook to override the CSS Vite configuration
|
|
151
|
+
* @param config - The default Vite configuration
|
|
152
|
+
* @returns Modified Vite configuration
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* cssBuildConfig: (config) => {
|
|
156
|
+
* // Modify the Vite config as needed
|
|
157
|
+
* return config;
|
|
158
|
+
* }
|
|
159
|
+
*/
|
|
160
|
+
// cssBuildConfig: (config) => {
|
|
161
|
+
// // Add your custom CSS build configuration here
|
|
162
|
+
// return config;
|
|
163
|
+
// },
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Optional hook to override the HTML Vite configuration
|
|
167
|
+
* @param config - The default Vite configuration
|
|
168
|
+
* @returns Modified Vite configuration
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* htmlBuildConfig: (config) => {
|
|
172
|
+
* // Modify the Vite config as needed
|
|
173
|
+
* return config;
|
|
174
|
+
* }
|
|
175
|
+
*/
|
|
176
|
+
// htmlBuildConfig: (config) => {
|
|
177
|
+
// // Add your custom HTML build configuration here
|
|
178
|
+
// return config;
|
|
179
|
+
// },
|
|
180
|
+
},
|
|
181
|
+
};
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
/** @type {import('handoff-app').Config} */
|
|
5
|
+
module.exports = {
|
|
6
|
+
app: {
|
|
7
|
+
theme: "default",
|
|
8
|
+
title: "{{projectName}} Design System",
|
|
9
|
+
client: "{{projectName}}",
|
|
10
|
+
google_tag_manager: null,
|
|
11
|
+
attribution: true,
|
|
12
|
+
type_copy: "Almost before we knew it, we had left the ground.",
|
|
13
|
+
type_sort: [
|
|
14
|
+
"Heading 1",
|
|
15
|
+
"Heading 2",
|
|
16
|
+
"Heading 3",
|
|
17
|
+
"Heading 4",
|
|
18
|
+
"Heading 5",
|
|
19
|
+
"Heading 6",
|
|
20
|
+
"Paragraph",
|
|
21
|
+
"Subheading",
|
|
22
|
+
"Blockquote",
|
|
23
|
+
"Input Labels",
|
|
24
|
+
"Link"
|
|
25
|
+
],
|
|
26
|
+
color_sort: [
|
|
27
|
+
"primary",
|
|
28
|
+
"secondary",
|
|
29
|
+
"extra",
|
|
30
|
+
"system"
|
|
31
|
+
],
|
|
32
|
+
component_sort: [
|
|
33
|
+
"primary",
|
|
34
|
+
"secondary",
|
|
35
|
+
"transparent"
|
|
36
|
+
],
|
|
37
|
+
base_path: "",
|
|
38
|
+
breakpoints: {
|
|
39
|
+
sm: { size: 576, name: "Small" },
|
|
40
|
+
md: { size: 768, name: "Medium" },
|
|
41
|
+
lg: { size: 992, name: "Large" },
|
|
42
|
+
xl: { size: 1200, name: "Extra Large" }
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
entries: {
|
|
47
|
+
/**
|
|
48
|
+
* Array of component paths to be included in the build
|
|
49
|
+
* Add your component directories here
|
|
50
|
+
*/
|
|
51
|
+
components: ["components/button"]
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
hooks: {
|
|
55
|
+
/**
|
|
56
|
+
* Optional validation callback for components
|
|
57
|
+
* @param component - The component instance to validate
|
|
58
|
+
* @returns A record of validation results where keys are validation types and values are detailed validation results
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* validateComponent: async (component) => ({
|
|
62
|
+
* a11y: {
|
|
63
|
+
* description: 'Accessibility validation check',
|
|
64
|
+
* passed: true,
|
|
65
|
+
* messages: ['No accessibility issues found']
|
|
66
|
+
* },
|
|
67
|
+
* responsive: {
|
|
68
|
+
* description: 'Responsive design validation',
|
|
69
|
+
* passed: false,
|
|
70
|
+
* messages: ['Component breaks at mobile breakpoint']
|
|
71
|
+
* }
|
|
72
|
+
* })
|
|
73
|
+
*/
|
|
74
|
+
// validateComponent: async (component) => {
|
|
75
|
+
// // Add your custom validation logic here
|
|
76
|
+
// return {};
|
|
77
|
+
// },
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Optional hook to override the SSR build configuration used in the ssrRenderPlugin
|
|
81
|
+
* @param config - The default esbuild configuration
|
|
82
|
+
* @returns Modified esbuild configuration
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ssrBuildConfig: (config) => {
|
|
86
|
+
* // Modify the esbuild config as needed
|
|
87
|
+
* return config;
|
|
88
|
+
* }
|
|
89
|
+
*/
|
|
90
|
+
// ssrBuildConfig: (config) => {
|
|
91
|
+
// // Add your custom SSR build configuration here
|
|
92
|
+
// return config;
|
|
93
|
+
// },
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Optional hook to override the client-side build configuration used in the ssrRenderPlugin
|
|
97
|
+
* @param config - The default esbuild configuration
|
|
98
|
+
* @returns Modified esbuild configuration
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* clientBuildConfig: (config) => {
|
|
102
|
+
* // Modify the esbuild config as needed
|
|
103
|
+
* return config;
|
|
104
|
+
* }
|
|
105
|
+
*/
|
|
106
|
+
// clientBuildConfig: (config) => {
|
|
107
|
+
// // Add your custom client build configuration here
|
|
108
|
+
// return config;
|
|
109
|
+
// },
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Optional hook to specify which export property contains the schema
|
|
113
|
+
* @param exports - The module exports object containing the schema
|
|
114
|
+
* @returns The schema object from the exports
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* getSchemaFromExports: (exports) => exports.customSchema || exports.default
|
|
118
|
+
*/
|
|
119
|
+
// getSchemaFromExports: (exports) => {
|
|
120
|
+
// // Add your custom schema extraction logic here
|
|
121
|
+
// return exports.default;
|
|
122
|
+
// },
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Optional hook to transform the schema into properties
|
|
126
|
+
* @param schema - The schema object to transform
|
|
127
|
+
* @returns The transformed properties object
|
|
128
|
+
*/
|
|
129
|
+
// schemaToProperties: (schema) => {
|
|
130
|
+
// // Add your custom schema to properties transformation here
|
|
131
|
+
// return {};
|
|
132
|
+
// },
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Optional hook to override the JavaScript Vite configuration
|
|
136
|
+
* @param config - The default Vite configuration
|
|
137
|
+
* @returns Modified Vite configuration
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* jsBuildConfig: (config) => {
|
|
141
|
+
* // Modify the Vite config as needed
|
|
142
|
+
* return config;
|
|
143
|
+
* }
|
|
144
|
+
*/
|
|
145
|
+
// jsBuildConfig: (config) => {
|
|
146
|
+
// // Add your custom JavaScript build configuration here
|
|
147
|
+
// return config;
|
|
148
|
+
// },
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Optional hook to override the CSS Vite configuration
|
|
152
|
+
* @param config - The default Vite configuration
|
|
153
|
+
* @returns Modified Vite configuration
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* cssBuildConfig: (config) => {
|
|
157
|
+
* // Modify the Vite config as needed
|
|
158
|
+
* return config;
|
|
159
|
+
* }
|
|
160
|
+
*/
|
|
161
|
+
// cssBuildConfig: (config) => {
|
|
162
|
+
// // Add your custom CSS build configuration here
|
|
163
|
+
// return config;
|
|
164
|
+
// },
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Optional hook to override the HTML Vite configuration
|
|
168
|
+
* @param config - The default Vite configuration
|
|
169
|
+
* @returns Modified Vite configuration
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* htmlBuildConfig: (config) => {
|
|
173
|
+
* // Modify the Vite config as needed
|
|
174
|
+
* return config;
|
|
175
|
+
* }
|
|
176
|
+
*/
|
|
177
|
+
// htmlBuildConfig: (config) => {
|
|
178
|
+
// // Add your custom HTML build configuration here
|
|
179
|
+
// return config;
|
|
180
|
+
// },
|
|
181
|
+
},
|
|
182
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{projectName}}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A handoff app project",
|
|
5
|
+
"private": true,
|
|
6
|
+
"scripts": {
|
|
7
|
+
"start": "handoff-app start",
|
|
8
|
+
"dev": "handoff-app dev",
|
|
9
|
+
"build": "handoff-app build:app",
|
|
10
|
+
"fetch": "handoff-app fetch"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"handoff-app": "^1.0.1"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {},
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=16.0.0",
|
|
18
|
+
"npm": ">=8.0.0"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
5
|
+
"jsx": "react-jsx",
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"moduleResolution": "bundler",
|
|
8
|
+
"resolveJsonModule": true,
|
|
9
|
+
"allowJs": true,
|
|
10
|
+
"strict": true,
|
|
11
|
+
"esModuleInterop": true,
|
|
12
|
+
"skipLibCheck": true,
|
|
13
|
+
"forceConsistentCasingInFileNames": true,
|
|
14
|
+
"allowSyntheticDefaultImports": true,
|
|
15
|
+
"isolatedModules": true,
|
|
16
|
+
"noEmit": true
|
|
17
|
+
},
|
|
18
|
+
"include": [
|
|
19
|
+
"components/**/*"
|
|
20
|
+
],
|
|
21
|
+
"exclude": [
|
|
22
|
+
"node_modules"
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
-
});
|
|
33
|
-
};
|
|
34
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
35
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
36
|
-
};
|
|
37
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
-
const p = __importStar(require("@clack/prompts"));
|
|
39
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
40
|
-
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
41
|
-
const path_1 = __importDefault(require("path"));
|
|
42
|
-
const client_1 = require("../../platform/client");
|
|
43
|
-
const credentials_1 = require("../../platform/credentials");
|
|
44
|
-
const DEFAULT_PLATFORM_URL = 'http://localhost:3000';
|
|
45
|
-
const CONFIG_FILES = ['handoff.config.json', 'handoff.config.js', 'handoff.config.cjs'];
|
|
46
|
-
const command = {
|
|
47
|
-
command: 'platform:init',
|
|
48
|
-
describe: 'Link this directory to a Handoff platform project',
|
|
49
|
-
builder: (yargs) => {
|
|
50
|
-
return yargs.option('url', {
|
|
51
|
-
type: 'string',
|
|
52
|
-
description: 'Platform URL',
|
|
53
|
-
default: process.env.HANDOFF_PLATFORM_URL || DEFAULT_PLATFORM_URL,
|
|
54
|
-
});
|
|
55
|
-
},
|
|
56
|
-
handler: (args) => __awaiter(void 0, void 0, void 0, function* () {
|
|
57
|
-
const baseUrl = (args.url || DEFAULT_PLATFORM_URL).replace(/\/+$/, '');
|
|
58
|
-
const workingDir = process.cwd();
|
|
59
|
-
p.intro(chalk_1.default.bold('Handoff Platform Init'));
|
|
60
|
-
// 1. Check login
|
|
61
|
-
const token = (0, credentials_1.getToken)(baseUrl);
|
|
62
|
-
if (!token) {
|
|
63
|
-
p.cancel(`Not logged in to ${baseUrl}. Run ${chalk_1.default.cyan('handoff-app platform:login')} first.`);
|
|
64
|
-
process.exit(1);
|
|
65
|
-
}
|
|
66
|
-
const spinner = p.spinner();
|
|
67
|
-
try {
|
|
68
|
-
// 2. Fetch project list
|
|
69
|
-
spinner.start('Fetching projects...');
|
|
70
|
-
const projects = yield (0, client_1.listProjects)(baseUrl, token);
|
|
71
|
-
spinner.stop('Projects loaded.');
|
|
72
|
-
if (projects.length === 0) {
|
|
73
|
-
p.cancel('No projects found. Create a project in the Handoff platform first.');
|
|
74
|
-
process.exit(1);
|
|
75
|
-
}
|
|
76
|
-
// 3. Interactive project selection
|
|
77
|
-
const projectId = yield p.select({
|
|
78
|
-
message: 'Select a project to link:',
|
|
79
|
-
options: projects.map((proj) => ({
|
|
80
|
-
value: proj.id,
|
|
81
|
-
label: `${proj.name} (${proj.orgName})`,
|
|
82
|
-
hint: `${proj.slug} — ${proj.role}`,
|
|
83
|
-
})),
|
|
84
|
-
});
|
|
85
|
-
if (p.isCancel(projectId)) {
|
|
86
|
-
p.cancel('Init cancelled.');
|
|
87
|
-
process.exit(0);
|
|
88
|
-
}
|
|
89
|
-
const selectedProject = projects.find((proj) => proj.id === projectId);
|
|
90
|
-
// 4. Check if a local config exists
|
|
91
|
-
const existingConfigFile = CONFIG_FILES.find((file) => fs_extra_1.default.existsSync(path_1.default.resolve(workingDir, file)));
|
|
92
|
-
if (!existingConfigFile) {
|
|
93
|
-
// No local config — pull it from the remote project
|
|
94
|
-
spinner.start('No local config found. Fetching config from platform...');
|
|
95
|
-
try {
|
|
96
|
-
const manifest = yield (0, client_1.getManifest)(baseUrl, token, selectedProject.id);
|
|
97
|
-
// Look for handoff.config.js in the manifest
|
|
98
|
-
const remoteConfigKey = Object.keys(manifest.files).find((key) => key === 'handoff.config.js' || key === 'handoff.config.json' || key === 'handoff.config.cjs');
|
|
99
|
-
if (remoteConfigKey) {
|
|
100
|
-
const content = yield (0, client_1.downloadFile)(baseUrl, token, selectedProject.id, remoteConfigKey);
|
|
101
|
-
const localConfigPath = path_1.default.resolve(workingDir, remoteConfigKey);
|
|
102
|
-
fs_extra_1.default.writeFileSync(localConfigPath, content);
|
|
103
|
-
spinner.stop(`Downloaded ${remoteConfigKey} from platform.`);
|
|
104
|
-
// Now inject the platform fields into it
|
|
105
|
-
yield injectPlatformFields(localConfigPath, selectedProject.id, baseUrl);
|
|
106
|
-
}
|
|
107
|
-
else {
|
|
108
|
-
spinner.stop('No remote config found either. Creating a new config.');
|
|
109
|
-
// Create a minimal config with just the platform fields
|
|
110
|
-
const configPath = path_1.default.resolve(workingDir, 'handoff.config.js');
|
|
111
|
-
const configContent = generateMinimalConfig(selectedProject.id, baseUrl);
|
|
112
|
-
fs_extra_1.default.writeFileSync(configPath, configContent);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
catch (err) {
|
|
116
|
-
spinner.stop('Could not fetch remote config.');
|
|
117
|
-
// Fall back to creating a minimal config
|
|
118
|
-
const configPath = path_1.default.resolve(workingDir, 'handoff.config.js');
|
|
119
|
-
const configContent = generateMinimalConfig(selectedProject.id, baseUrl);
|
|
120
|
-
fs_extra_1.default.writeFileSync(configPath, configContent);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
else {
|
|
124
|
-
// Local config exists — add/update platform fields
|
|
125
|
-
const configPath = path_1.default.resolve(workingDir, existingConfigFile);
|
|
126
|
-
yield injectPlatformFields(configPath, selectedProject.id, baseUrl);
|
|
127
|
-
}
|
|
128
|
-
p.note([
|
|
129
|
-
`Project: ${chalk_1.default.bold(selectedProject.name)}`,
|
|
130
|
-
`Organization: ${chalk_1.default.dim(selectedProject.orgName)}`,
|
|
131
|
-
`Project ID: ${chalk_1.default.dim(selectedProject.id)}`,
|
|
132
|
-
`Platform: ${chalk_1.default.dim(baseUrl)}`,
|
|
133
|
-
].join('\n'), 'Linked successfully');
|
|
134
|
-
p.outro(`Run ${chalk_1.default.cyan('handoff-app platform:pull')} to sync files from the platform.`);
|
|
135
|
-
}
|
|
136
|
-
catch (err) {
|
|
137
|
-
spinner.stop('Init failed.');
|
|
138
|
-
p.cancel(err.message || 'Init failed');
|
|
139
|
-
process.exit(1);
|
|
140
|
-
}
|
|
141
|
-
}),
|
|
142
|
-
};
|
|
143
|
-
/**
|
|
144
|
-
* Inject platformProjectId and platformUrl into an existing config file.
|
|
145
|
-
*
|
|
146
|
-
* For JSON files, parse and re-serialize.
|
|
147
|
-
* For JS/CJS files, use a regex approach to add/update the fields.
|
|
148
|
-
*/
|
|
149
|
-
function injectPlatformFields(configPath, projectId, platformUrl) {
|
|
150
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
151
|
-
const ext = path_1.default.extname(configPath);
|
|
152
|
-
if (ext === '.json') {
|
|
153
|
-
const config = fs_extra_1.default.readJsonSync(configPath);
|
|
154
|
-
config.platformProjectId = projectId;
|
|
155
|
-
config.platformUrl = platformUrl;
|
|
156
|
-
fs_extra_1.default.writeJsonSync(configPath, config, { spaces: 2 });
|
|
157
|
-
}
|
|
158
|
-
else {
|
|
159
|
-
// JS or CJS file — read as text, inject/replace fields
|
|
160
|
-
let content = fs_extra_1.default.readFileSync(configPath, 'utf8');
|
|
161
|
-
content = upsertConfigField(content, 'platformProjectId', JSON.stringify(projectId));
|
|
162
|
-
content = upsertConfigField(content, 'platformUrl', JSON.stringify(platformUrl));
|
|
163
|
-
fs_extra_1.default.writeFileSync(configPath, content, 'utf8');
|
|
164
|
-
}
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
/**
|
|
168
|
-
* Insert or replace a top-level field in a JS config export.
|
|
169
|
-
* Looks for `fieldName: ...` and replaces the value, or appends
|
|
170
|
-
* the field before the closing `};` of module.exports.
|
|
171
|
-
*/
|
|
172
|
-
function upsertConfigField(source, fieldName, value) {
|
|
173
|
-
// Pattern to match an existing field assignment (with optional trailing comma)
|
|
174
|
-
const existingPattern = new RegExp(`(${fieldName}\\s*:\\s*)([^,}\\n]+)(,?)`);
|
|
175
|
-
if (existingPattern.test(source)) {
|
|
176
|
-
return source.replace(existingPattern, `$1${value}$3`);
|
|
177
|
-
}
|
|
178
|
-
// Insert before the last `};` (end of module.exports)
|
|
179
|
-
// Handle both `module.exports = {` and `export default {` styles
|
|
180
|
-
// Also ensure the previous line ends with a comma so the JS stays valid
|
|
181
|
-
const closingBrace = /(\n)(};?\s*$)/;
|
|
182
|
-
if (closingBrace.test(source)) {
|
|
183
|
-
// Ensure the last property line before the closing brace has a trailing comma
|
|
184
|
-
source = ensureTrailingComma(source);
|
|
185
|
-
return source.replace(closingBrace, `$1 ${fieldName}: ${value},\n$2`);
|
|
186
|
-
}
|
|
187
|
-
// Fallback: just append as a comment warning
|
|
188
|
-
return source + `\n// Added by platform:init — could not auto-inject:\n// ${fieldName}: ${value}\n`;
|
|
189
|
-
}
|
|
190
|
-
/**
|
|
191
|
-
* Ensure the last property line before the closing `};` has a trailing comma.
|
|
192
|
-
* This prevents invalid JS when appending new properties to an object literal.
|
|
193
|
-
*/
|
|
194
|
-
function ensureTrailingComma(source) {
|
|
195
|
-
const lines = source.split('\n');
|
|
196
|
-
// Find the closing `};` or `}` at the start of a line (module.exports / export default)
|
|
197
|
-
let closingIdx = -1;
|
|
198
|
-
for (let i = lines.length - 1; i >= 0; i--) {
|
|
199
|
-
if (/^};?\s*$/.test(lines[i])) {
|
|
200
|
-
closingIdx = i;
|
|
201
|
-
break;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
if (closingIdx <= 0)
|
|
205
|
-
return source;
|
|
206
|
-
// Walk backwards past blank lines and comment-only lines to find the last property line
|
|
207
|
-
let lastPropIdx = closingIdx - 1;
|
|
208
|
-
while (lastPropIdx >= 0 && (/^\s*$/.test(lines[lastPropIdx]) || /^\s*\/\//.test(lines[lastPropIdx]))) {
|
|
209
|
-
lastPropIdx--;
|
|
210
|
-
}
|
|
211
|
-
if (lastPropIdx < 0)
|
|
212
|
-
return source;
|
|
213
|
-
// Strip inline comments for the check, but preserve them in the output
|
|
214
|
-
const stripped = lines[lastPropIdx].replace(/\s*\/\/.*$/, '').trimEnd();
|
|
215
|
-
if (stripped.length > 0 && !stripped.endsWith(',') && !stripped.endsWith('{')) {
|
|
216
|
-
// Insert a comma after the last significant character, preserving any inline comment
|
|
217
|
-
lines[lastPropIdx] = lines[lastPropIdx].replace(/(\S)(\s*(?:\/\/.*)?)$/, '$1,$2');
|
|
218
|
-
}
|
|
219
|
-
return lines.join('\n');
|
|
220
|
-
}
|
|
221
|
-
/**
|
|
222
|
-
* Generate a minimal handoff.config.js with platform fields.
|
|
223
|
-
*/
|
|
224
|
-
function generateMinimalConfig(projectId, platformUrl) {
|
|
225
|
-
return `module.exports = {
|
|
226
|
-
platformProjectId: ${JSON.stringify(projectId)},
|
|
227
|
-
platformUrl: ${JSON.stringify(platformUrl)},
|
|
228
|
-
};
|
|
229
|
-
`;
|
|
230
|
-
}
|
|
231
|
-
exports.default = command;
|