sunpeak 0.16.12 → 0.16.17
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 +10 -8
- package/bin/commands/build.mjs +220 -221
- package/bin/commands/dev.mjs +81 -14
- package/bin/commands/start.mjs +24 -12
- package/dist/chatgpt/globals.css +20 -10
- package/dist/chatgpt/index.cjs +4 -3
- package/dist/chatgpt/index.cjs.map +1 -1
- package/dist/chatgpt/index.d.ts +2 -1
- package/dist/chatgpt/index.js +5 -4
- package/dist/claude/index.cjs +1 -1
- package/dist/claude/index.js +1 -1
- package/dist/{discovery-CH80W5l9.js → discovery-DvIQWTez.js} +26 -21
- package/dist/discovery-DvIQWTez.js.map +1 -0
- package/dist/{discovery-DmB8_4QL.cjs → discovery-SviNiBkF.cjs} +26 -21
- package/dist/discovery-SviNiBkF.cjs.map +1 -0
- package/dist/hooks/index.d.ts +10 -4
- package/dist/hooks/use-call-server-tool.d.ts +2 -0
- package/dist/hooks/use-download-file.d.ts +42 -0
- package/dist/hooks/use-list-server-resources.d.ts +56 -0
- package/dist/hooks/use-read-server-resource.d.ts +47 -0
- package/dist/{index-BsWYp00t.cjs → index-BFD3bAHd.cjs} +30 -9
- package/dist/index-BFD3bAHd.cjs.map +1 -0
- package/dist/{index-DJt59490.js → index-CsYoMHyn.js} +4 -3
- package/dist/index-CsYoMHyn.js.map +1 -0
- package/dist/{index-POfU7IR6.cjs → index-DHcaJ5PU.cjs} +4 -3
- package/dist/index-DHcaJ5PU.cjs.map +1 -0
- package/dist/{index-Dyoz9lnx.js → index-wUvmyoCx.js} +30 -9
- package/dist/index-wUvmyoCx.js.map +1 -0
- package/dist/index.cjs +703 -470
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3681 -3448
- package/dist/index.js.map +1 -1
- package/dist/lib/discovery-cli.cjs +2 -2
- package/dist/lib/discovery-cli.cjs.map +1 -1
- package/dist/lib/discovery-cli.js +2 -2
- package/dist/lib/discovery-cli.js.map +1 -1
- package/dist/mcp/index.cjs +1181 -826
- package/dist/mcp/index.cjs.map +1 -1
- package/dist/mcp/index.d.ts +2 -2
- package/dist/mcp/index.js +1182 -827
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/production-server.d.ts +22 -5
- package/dist/mcp/types.d.ts +7 -4
- package/dist/platform/chatgpt/index.cjs +1 -1
- package/dist/platform/chatgpt/index.cjs.map +1 -1
- package/dist/platform/chatgpt/index.d.ts +3 -2
- package/dist/platform/chatgpt/index.js +1 -1
- package/dist/platform/chatgpt/index.js.map +1 -1
- package/dist/platform/chatgpt/use-file-download.d.ts +3 -0
- package/dist/{protocol-BD5jDQEx.js → protocol-CF-P_kw5.js} +1985 -1427
- package/dist/protocol-CF-P_kw5.js.map +1 -0
- package/dist/{protocol-BOjXuK6l.cjs → protocol-CfvM5B6z.cjs} +1936 -1378
- package/dist/protocol-CfvM5B6z.cjs.map +1 -0
- package/dist/simulator/index.cjs +3 -2
- package/dist/simulator/index.cjs.map +1 -1
- package/dist/simulator/index.d.ts +2 -0
- package/dist/simulator/index.js +4 -3
- package/dist/simulator/mcp-app-host.d.ts +7 -0
- package/dist/{simulator-tkLFRzjr.cjs → simulator-BEFsuj9Z.cjs} +654 -570
- package/dist/simulator-BEFsuj9Z.cjs.map +1 -0
- package/dist/{simulator-BUF-_85b.js → simulator-Da9iAupa.js} +655 -571
- package/dist/simulator-Da9iAupa.js.map +1 -0
- package/dist/style.css +20 -10
- package/dist/types/simulation.d.ts +28 -2
- package/dist/{use-app-BpAJqzdE.cjs → use-app-CaTJmpgj.cjs} +741 -705
- package/dist/use-app-CaTJmpgj.cjs.map +1 -0
- package/dist/{use-app-WOUdh1PR.js → use-app-DTTzqi-0.js} +754 -718
- package/dist/use-app-DTTzqi-0.js.map +1 -0
- package/package.json +19 -19
- package/template/README.md +5 -1
- package/template/node_modules/.bin/playwright +2 -2
- package/template/node_modules/.bin/vite +2 -2
- package/template/node_modules/.bin/vitest +2 -2
- package/template/package.json +16 -16
- package/template/src/resources/review/review.test.tsx +205 -14
- package/template/src/resources/review/review.tsx +83 -46
- package/template/src/tools/review.ts +38 -0
- package/template/tests/e2e/review.spec.ts +96 -0
- package/template/tests/simulations/review-diff.json +31 -10
- package/template/tests/simulations/review-post.json +26 -5
- package/template/tests/simulations/review-purchase.json +27 -6
- package/dist/discovery-CH80W5l9.js.map +0 -1
- package/dist/discovery-DmB8_4QL.cjs.map +0 -1
- package/dist/index-BsWYp00t.cjs.map +0 -1
- package/dist/index-DJt59490.js.map +0 -1
- package/dist/index-Dyoz9lnx.js.map +0 -1
- package/dist/index-POfU7IR6.cjs.map +0 -1
- package/dist/protocol-BD5jDQEx.js.map +0 -1
- package/dist/protocol-BOjXuK6l.cjs.map +0 -1
- package/dist/simulator-BUF-_85b.js.map +0 -1
- package/dist/simulator-tkLFRzjr.cjs.map +0 -1
- package/dist/use-app-BpAJqzdE.cjs.map +0 -1
- package/dist/use-app-WOUdh1PR.js.map +0 -1
package/README.md
CHANGED
|
@@ -62,14 +62,16 @@ my-app/
|
|
|
62
62
|
├── src/
|
|
63
63
|
│ ├── resources/
|
|
64
64
|
│ │ └── review/
|
|
65
|
-
│ │ └── review.tsx
|
|
65
|
+
│ │ └── review.tsx # Review UI component + resource metadata.
|
|
66
66
|
│ ├── tools/
|
|
67
|
-
│ │ ├── review-diff.ts
|
|
68
|
-
│ │
|
|
69
|
-
│ └──
|
|
67
|
+
│ │ ├── review-diff.ts # Tool with handler, schema, and optional resource link.
|
|
68
|
+
│ │ ├── review-post.ts # Multiple tools can share one resource.
|
|
69
|
+
│ │ └── review.ts # Backend-only tool (no resource, no UI).
|
|
70
|
+
│ └── server.ts # Optional: auth, server config.
|
|
70
71
|
├── tests/simulations/
|
|
71
|
-
│ ├── review-diff.json
|
|
72
|
-
│
|
|
72
|
+
│ ├── review-diff.json # Mock state for testing (includes serverTools).
|
|
73
|
+
│ ├── review-post.json # Mock state for testing (includes serverTools).
|
|
74
|
+
│ └── review-purchase.json # Mock state for testing (includes serverTools).
|
|
73
75
|
└── package.json
|
|
74
76
|
```
|
|
75
77
|
|
|
@@ -77,7 +79,7 @@ my-app/
|
|
|
77
79
|
2. UI components: Production-ready components following MCP App design guidelines.
|
|
78
80
|
3. Convention over configuration:
|
|
79
81
|
1. Create a UI by creating a `.tsx` file in `src/resources/{name}/` that exports a `ResourceConfig` and a React component ([example below](#resource-component)).
|
|
80
|
-
2. Create a tool by creating a `.ts` file in `src/tools/` that exports `tool` (metadata with resource
|
|
82
|
+
2. Create a tool by creating a `.ts` file in `src/tools/` that exports `tool` (metadata with optional resource link), `schema` (Zod), and a `default` handler ([example below](#tool-file)). Tools without a `resource` field are registered as plain MCP tools (no UI).
|
|
81
83
|
3. Create test state (`Simulation`s) by creating a `.json` file in `tests/simulations/` ([example below](#simulation)).
|
|
82
84
|
|
|
83
85
|
### The `sunpeak` CLI
|
|
@@ -118,7 +120,7 @@ export function ReviewResource() {
|
|
|
118
120
|
|
|
119
121
|
### Tool File
|
|
120
122
|
|
|
121
|
-
Each tool `.ts` file exports metadata (with
|
|
123
|
+
Each tool `.ts` file exports metadata (with an optional resource link for UI tools), a Zod schema, and a handler:
|
|
122
124
|
|
|
123
125
|
```ts
|
|
124
126
|
// src/tools/review-diff.ts
|
package/bin/commands/build.mjs
CHANGED
|
@@ -75,17 +75,11 @@ export async function build(projectRoot = process.cwd(), { quiet = false } = {})
|
|
|
75
75
|
const resourcesDir = path.join(projectRoot, 'src/resources');
|
|
76
76
|
const templateFile = path.join(projectRoot, 'src/index-resource.tsx');
|
|
77
77
|
|
|
78
|
-
//
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
console.error('Expected location: ' + resourcesDir);
|
|
82
|
-
console.error('\nThe build command expects the standard Sunpeak project structure.');
|
|
83
|
-
console.error('If you have customized your project structure, you may need to use');
|
|
84
|
-
console.error('a custom build script instead of "sunpeak build".');
|
|
85
|
-
process.exit(1);
|
|
86
|
-
}
|
|
78
|
+
// Check if resources exist (optional — projects may have only plain tools)
|
|
79
|
+
const hasResources = existsSync(resourcesDir);
|
|
80
|
+
const hasTemplateFile = existsSync(templateFile);
|
|
87
81
|
|
|
88
|
-
if (!
|
|
82
|
+
if (hasResources && !hasTemplateFile) {
|
|
89
83
|
console.error('Error: src/index-resource.tsx not found');
|
|
90
84
|
console.error('Expected location: ' + templateFile);
|
|
91
85
|
console.error('\nThis file is the template entry point for building resources.');
|
|
@@ -93,56 +87,7 @@ export async function build(projectRoot = process.cwd(), { quiet = false } = {})
|
|
|
93
87
|
process.exit(1);
|
|
94
88
|
}
|
|
95
89
|
|
|
96
|
-
// Import vite and plugins from the user's project (not from sunpeak's node_modules)
|
|
97
|
-
// This allows sunpeak to work when installed globally
|
|
98
|
-
// We resolve to ESM entry points to avoid the CJS deprecation warning from Vite
|
|
99
90
|
const require = createRequire(path.join(projectRoot, 'package.json'));
|
|
100
|
-
let viteBuild, react, tailwindcss;
|
|
101
|
-
try {
|
|
102
|
-
const [viteModule, reactModule, tailwindModule] = await Promise.all([
|
|
103
|
-
import(resolveEsmEntry(require, 'vite')),
|
|
104
|
-
import(resolveEsmEntry(require, '@vitejs/plugin-react')),
|
|
105
|
-
import(resolveEsmEntry(require, '@tailwindcss/vite')),
|
|
106
|
-
]);
|
|
107
|
-
viteBuild = viteModule.build;
|
|
108
|
-
react = reactModule.default;
|
|
109
|
-
tailwindcss = tailwindModule.default;
|
|
110
|
-
} catch (error) {
|
|
111
|
-
console.error('Error: Could not load build dependencies from your project.');
|
|
112
|
-
console.error('\nMake sure you have these packages installed in your project:');
|
|
113
|
-
console.error(' - vite');
|
|
114
|
-
console.error(' - @vitejs/plugin-react');
|
|
115
|
-
console.error(' - @tailwindcss/vite');
|
|
116
|
-
console.error('\nRun: npm install -D vite @vitejs/plugin-react @tailwindcss/vite');
|
|
117
|
-
console.error('\nOriginal error:', error.message);
|
|
118
|
-
process.exit(1);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Plugin factory to inline CSS into the JS bundle for all output files
|
|
122
|
-
const inlineCssPlugin = (buildOutDir) => ({
|
|
123
|
-
name: 'inline-css',
|
|
124
|
-
closeBundle() {
|
|
125
|
-
const cssFile = path.join(buildOutDir, 'style.css');
|
|
126
|
-
|
|
127
|
-
if (existsSync(cssFile)) {
|
|
128
|
-
const css = readFileSync(cssFile, 'utf-8');
|
|
129
|
-
const injectCss = `(function(){var s=document.createElement('style');s.textContent=${JSON.stringify(css)};document.head.appendChild(s);})();`;
|
|
130
|
-
|
|
131
|
-
// Find all .js files in the dist directory and inject CSS
|
|
132
|
-
const files = readdirSync(buildOutDir);
|
|
133
|
-
files.forEach((file) => {
|
|
134
|
-
if (file.endsWith('.js')) {
|
|
135
|
-
const jsFile = path.join(buildOutDir, file);
|
|
136
|
-
const js = readFileSync(jsFile, 'utf-8');
|
|
137
|
-
writeFileSync(jsFile, injectCss + js);
|
|
138
|
-
}
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
// Remove the separate CSS file after injecting into all bundles
|
|
142
|
-
unlinkSync(cssFile);
|
|
143
|
-
}
|
|
144
|
-
},
|
|
145
|
-
});
|
|
146
91
|
|
|
147
92
|
// Clean dist and temp directories
|
|
148
93
|
if (existsSync(distDir)) {
|
|
@@ -152,161 +97,213 @@ export async function build(projectRoot = process.cwd(), { quiet = false } = {})
|
|
|
152
97
|
rmSync(tempDir, { recursive: true });
|
|
153
98
|
}
|
|
154
99
|
mkdirSync(distDir, { recursive: true });
|
|
155
|
-
mkdirSync(tempDir, { recursive: true });
|
|
156
100
|
|
|
157
|
-
//
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
.map(entry => {
|
|
161
|
-
const kebabName = entry.name;
|
|
101
|
+
// ========================================================================
|
|
102
|
+
// Build resources (if any exist)
|
|
103
|
+
// ========================================================================
|
|
162
104
|
|
|
163
|
-
|
|
164
|
-
const resourcePath = path.join(resourcesDir, kebabName, resourceFile);
|
|
105
|
+
let resourceFiles = [];
|
|
165
106
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
107
|
+
if (hasResources && hasTemplateFile) {
|
|
108
|
+
// Import vite and plugins from the user's project (not from sunpeak's node_modules)
|
|
109
|
+
// We resolve to ESM entry points to avoid the CJS deprecation warning from Vite
|
|
110
|
+
let viteBuild, react, tailwindcss;
|
|
111
|
+
try {
|
|
112
|
+
const [viteModule, reactModule, tailwindModule] = await Promise.all([
|
|
113
|
+
import(resolveEsmEntry(require, 'vite')),
|
|
114
|
+
import(resolveEsmEntry(require, '@vitejs/plugin-react')),
|
|
115
|
+
import(resolveEsmEntry(require, '@tailwindcss/vite')),
|
|
116
|
+
]);
|
|
117
|
+
viteBuild = viteModule.build;
|
|
118
|
+
react = reactModule.default;
|
|
119
|
+
tailwindcss = tailwindModule.default;
|
|
120
|
+
} catch (error) {
|
|
121
|
+
console.error('Error: Could not load build dependencies from your project.');
|
|
122
|
+
console.error('\nMake sure you have these packages installed in your project:');
|
|
123
|
+
console.error(' - vite');
|
|
124
|
+
console.error(' - @vitejs/plugin-react');
|
|
125
|
+
console.error(' - @tailwindcss/vite');
|
|
126
|
+
console.error('\nRun: npm install -D vite @vitejs/plugin-react @tailwindcss/vite');
|
|
127
|
+
console.error('\nOriginal error:', error.message);
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
170
130
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
console.error('Error: No resource directories found in src/resources/');
|
|
191
|
-
console.error('Each resource should be a directory like: src/resources/review/review.tsx');
|
|
192
|
-
process.exit(1);
|
|
193
|
-
}
|
|
131
|
+
// Plugin factory to inline CSS into the JS bundle for all output files
|
|
132
|
+
const inlineCssPlugin = (buildOutDir) => ({
|
|
133
|
+
name: 'inline-css',
|
|
134
|
+
closeBundle() {
|
|
135
|
+
const cssFile = path.join(buildOutDir, 'style.css');
|
|
136
|
+
|
|
137
|
+
if (existsSync(cssFile)) {
|
|
138
|
+
const css = readFileSync(cssFile, 'utf-8');
|
|
139
|
+
const injectCss = `(function(){var s=document.createElement('style');s.textContent=${JSON.stringify(css)};document.head.appendChild(s);})();`;
|
|
140
|
+
|
|
141
|
+
// Find all .js files in the dist directory and inject CSS
|
|
142
|
+
const files = readdirSync(buildOutDir);
|
|
143
|
+
files.forEach((file) => {
|
|
144
|
+
if (file.endsWith('.js')) {
|
|
145
|
+
const jsFile = path.join(buildOutDir, file);
|
|
146
|
+
const js = readFileSync(jsFile, 'utf-8');
|
|
147
|
+
writeFileSync(jsFile, injectCss + js);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
194
150
|
|
|
195
|
-
|
|
151
|
+
// Remove the separate CSS file after injecting into all bundles
|
|
152
|
+
unlinkSync(cssFile);
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
});
|
|
196
156
|
|
|
197
|
-
|
|
198
|
-
const template = readFileSync(templateFile, 'utf-8');
|
|
157
|
+
mkdirSync(tempDir, { recursive: true });
|
|
199
158
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
process.exit(1);
|
|
206
|
-
}
|
|
159
|
+
// Auto-discover all resources (each resource is a subdirectory)
|
|
160
|
+
resourceFiles = readdirSync(resourcesDir, { withFileTypes: true })
|
|
161
|
+
.filter(entry => entry.isDirectory())
|
|
162
|
+
.map(entry => {
|
|
163
|
+
const kebabName = entry.name;
|
|
207
164
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
console.error('\nThe template file must include this comment where the resource mount should go.');
|
|
211
|
-
console.error('If you have customized this file, ensure it has the required placeholders.');
|
|
212
|
-
process.exit(1);
|
|
213
|
-
}
|
|
165
|
+
const resourceFile = `${kebabName}.tsx`;
|
|
166
|
+
const resourcePath = path.join(resourcesDir, kebabName, resourceFile);
|
|
214
167
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
168
|
+
// Skip directories without a resource file
|
|
169
|
+
if (!existsSync(resourcePath)) {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
219
172
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
173
|
+
// Convert kebab-case to PascalCase: 'review' -> 'Review', 'my-widget' -> 'MyWidget'
|
|
174
|
+
const pascalName = toPascalCase(kebabName);
|
|
175
|
+
const componentFile = resourceFile.replace(/\.tsx$/, '');
|
|
176
|
+
|
|
177
|
+
return {
|
|
178
|
+
componentName: `${pascalName}Resource`,
|
|
179
|
+
componentFile,
|
|
180
|
+
kebabName,
|
|
181
|
+
resourceDir: path.join(resourcesDir, kebabName),
|
|
182
|
+
entry: `.tmp/index-${kebabName}.tsx`,
|
|
183
|
+
jsOutput: `${kebabName}.js`,
|
|
184
|
+
htmlOutput: `${kebabName}.html`,
|
|
185
|
+
buildOutDir: path.join(buildDir, kebabName),
|
|
186
|
+
distOutDir: path.join(distDir, kebabName), // Final output: dist/{resource}/
|
|
187
|
+
};
|
|
188
|
+
})
|
|
189
|
+
.filter(Boolean);
|
|
190
|
+
|
|
191
|
+
if (resourceFiles.length > 0) {
|
|
192
|
+
log('Building all resources...\n');
|
|
193
|
+
|
|
194
|
+
// Read and validate the template
|
|
195
|
+
const template = readFileSync(templateFile, 'utf-8');
|
|
196
|
+
|
|
197
|
+
// Verify template has required placeholders
|
|
198
|
+
if (!template.includes('// RESOURCE_IMPORT')) {
|
|
199
|
+
console.error('Error: src/index-resource.tsx is missing "// RESOURCE_IMPORT" placeholder');
|
|
200
|
+
console.error('\nThe template file must include this comment where the resource import should go.');
|
|
201
|
+
console.error('If you have customized this file, ensure it has the required placeholders.');
|
|
202
|
+
process.exit(1);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (!template.includes('// RESOURCE_MOUNT')) {
|
|
206
|
+
console.error('Error: src/index-resource.tsx is missing "// RESOURCE_MOUNT" placeholder');
|
|
207
|
+
console.error('\nThe template file must include this comment where the resource mount should go.');
|
|
208
|
+
console.error('If you have customized this file, ensure it has the required placeholders.');
|
|
209
|
+
process.exit(1);
|
|
224
210
|
}
|
|
225
211
|
|
|
226
|
-
//
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
build: {
|
|
254
|
-
target: 'es2020',
|
|
255
|
-
outDir: buildOutDir,
|
|
256
|
-
emptyOutDir: true,
|
|
257
|
-
cssCodeSplit: false,
|
|
258
|
-
lib: {
|
|
259
|
-
entry: entryPath,
|
|
260
|
-
name: 'SunpeakApp',
|
|
261
|
-
formats: ['iife'],
|
|
262
|
-
fileName: () => jsOutput,
|
|
263
|
-
},
|
|
264
|
-
rollupOptions: {
|
|
265
|
-
output: {
|
|
266
|
-
inlineDynamicImports: true,
|
|
267
|
-
assetFileNames: 'style.css',
|
|
212
|
+
// Build all resources (but don't copy yet)
|
|
213
|
+
for (let i = 0; i < resourceFiles.length; i++) {
|
|
214
|
+
const { componentName, componentFile, kebabName, entry, jsOutput, buildOutDir } = resourceFiles[i];
|
|
215
|
+
log(`[${i + 1}/${resourceFiles.length}] Building ${kebabName}...`);
|
|
216
|
+
|
|
217
|
+
try {
|
|
218
|
+
// Create build directory if it doesn't exist
|
|
219
|
+
if (!existsSync(buildOutDir)) {
|
|
220
|
+
mkdirSync(buildOutDir, { recursive: true });
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Create entry file from template in temp directory
|
|
224
|
+
const entryContent = template
|
|
225
|
+
.replace('// RESOURCE_IMPORT', `import { ${componentName}, resource } from '../src/resources/${kebabName}/${componentFile}';`)
|
|
226
|
+
.replace('// RESOURCE_MOUNT', `createRoot(root).render(<AppProvider appInfo={{ name: ${JSON.stringify(appName)}, version: ${JSON.stringify(appVersion)} }}><${componentName} /></AppProvider>);`);
|
|
227
|
+
|
|
228
|
+
const entryPath = path.join(projectRoot, entry);
|
|
229
|
+
writeFileSync(entryPath, entryContent);
|
|
230
|
+
|
|
231
|
+
// Build with vite programmatically
|
|
232
|
+
await viteBuild({
|
|
233
|
+
mode: 'production',
|
|
234
|
+
root: projectRoot,
|
|
235
|
+
...(quiet && { logLevel: 'silent' }),
|
|
236
|
+
plugins: [react(), tailwindcss(), inlineCssPlugin(buildOutDir)],
|
|
237
|
+
define: {
|
|
238
|
+
'process.env.NODE_ENV': JSON.stringify('production'),
|
|
268
239
|
},
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
240
|
+
resolve: {
|
|
241
|
+
conditions: ['style', 'import', 'module', 'browser', 'default'],
|
|
242
|
+
alias: {
|
|
243
|
+
'@': path.resolve(projectRoot, 'src'),
|
|
244
|
+
// In workspace dev mode, use local sunpeak source
|
|
245
|
+
...(isTemplate && {
|
|
246
|
+
sunpeak: parentSrc,
|
|
247
|
+
}),
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
build: {
|
|
251
|
+
target: 'es2020',
|
|
252
|
+
outDir: buildOutDir,
|
|
253
|
+
emptyOutDir: true,
|
|
254
|
+
cssCodeSplit: false,
|
|
255
|
+
lib: {
|
|
256
|
+
entry: entryPath,
|
|
257
|
+
name: 'SunpeakApp',
|
|
258
|
+
formats: ['iife'],
|
|
259
|
+
fileName: () => jsOutput,
|
|
260
|
+
},
|
|
261
|
+
rollupOptions: {
|
|
262
|
+
output: {
|
|
263
|
+
inlineDynamicImports: true,
|
|
264
|
+
assetFileNames: 'style.css',
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
minify: true,
|
|
268
|
+
cssMinify: true,
|
|
269
|
+
},
|
|
270
|
+
});
|
|
271
|
+
} catch (error) {
|
|
272
|
+
console.error(`Failed to build ${kebabName}`);
|
|
273
|
+
console.error(error);
|
|
274
|
+
process.exit(1);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
280
277
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
278
|
+
// Now copy all files from build-output to dist/{resource}/
|
|
279
|
+
log('\nCopying built files to dist/...');
|
|
280
|
+
const timestamp = Date.now().toString(36);
|
|
284
281
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
282
|
+
for (const { jsOutput, htmlOutput, buildOutDir, distOutDir, kebabName, componentFile, resourceDir } of resourceFiles) {
|
|
283
|
+
// Create resource-specific output directory
|
|
284
|
+
if (!existsSync(distOutDir)) {
|
|
285
|
+
mkdirSync(distOutDir, { recursive: true });
|
|
286
|
+
}
|
|
290
287
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
288
|
+
// Extract resource metadata from .tsx file and write as JSON
|
|
289
|
+
const srcTsx = path.join(resourceDir, `${componentFile}.tsx`);
|
|
290
|
+
const destJson = path.join(distOutDir, `${kebabName}.json`);
|
|
291
|
+
|
|
292
|
+
const meta = await extractResourceExport(srcTsx);
|
|
293
|
+
// Inject name from directory key if not explicitly set
|
|
294
|
+
meta.name = meta.name ?? kebabName;
|
|
295
|
+
// Generate URI using resource name and build timestamp
|
|
296
|
+
meta.uri = `ui://${meta.name}-${timestamp}`;
|
|
297
|
+
writeFileSync(destJson, JSON.stringify(meta, null, 2));
|
|
298
|
+
log(`✓ Generated ${kebabName}/${kebabName}.json (uri: ${meta.uri})`);
|
|
299
|
+
|
|
300
|
+
// Read built JS file and wrap in HTML shell
|
|
301
|
+
const builtJsFile = path.join(buildOutDir, jsOutput);
|
|
302
|
+
const destHtmlFile = path.join(distOutDir, htmlOutput);
|
|
303
|
+
|
|
304
|
+
if (existsSync(builtJsFile)) {
|
|
305
|
+
const jsContents = readFileSync(builtJsFile, 'utf-8');
|
|
306
|
+
const html = `<!DOCTYPE html>
|
|
310
307
|
<html>
|
|
311
308
|
<head>
|
|
312
309
|
<meta charset="UTF-8">
|
|
@@ -319,32 +316,34 @@ ${jsContents}
|
|
|
319
316
|
</script>
|
|
320
317
|
</body>
|
|
321
318
|
</html>`;
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
319
|
+
writeFileSync(destHtmlFile, html);
|
|
320
|
+
log(`✓ Built ${kebabName}/${htmlOutput}`);
|
|
321
|
+
} else {
|
|
322
|
+
console.error(`Built file not found: ${builtJsFile}`);
|
|
323
|
+
if (existsSync(buildOutDir)) {
|
|
324
|
+
log(` Files in ${buildOutDir}:`, readdirSync(buildOutDir));
|
|
325
|
+
} else {
|
|
326
|
+
log(` Build directory doesn't exist: ${buildOutDir}`);
|
|
327
|
+
}
|
|
328
|
+
process.exit(1);
|
|
329
|
+
}
|
|
333
330
|
|
|
334
|
-
|
|
331
|
+
}
|
|
335
332
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
333
|
+
// Clean up temp and build directories
|
|
334
|
+
if (existsSync(tempDir)) {
|
|
335
|
+
rmSync(tempDir, { recursive: true });
|
|
336
|
+
}
|
|
337
|
+
if (existsSync(buildDir)) {
|
|
338
|
+
rmSync(buildDir, { recursive: true });
|
|
339
|
+
}
|
|
343
340
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
341
|
+
log('\n✓ All resources built successfully!');
|
|
342
|
+
log('\nBuilt resources:');
|
|
343
|
+
for (const { kebabName } of resourceFiles) {
|
|
344
|
+
log(` ${kebabName}`);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
348
347
|
}
|
|
349
348
|
|
|
350
349
|
// ========================================================================
|