@linktr.ee/linkapp 0.0.48 → 0.0.49
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 +1 -1
- package/dev-server/components/form/array-field.tsx +115 -0
- package/dev-server/components/form/file-field.tsx +48 -0
- package/dev-server/components/form/form-element.tsx +304 -0
- package/dev-server/components/form/link-behavior-field.tsx +68 -0
- package/dev-server/components/form/location-field.tsx +60 -0
- package/dev-server/components/settings-preview.tsx +138 -302
- package/dev-server/components/ui/checkbox.tsx +29 -0
- package/dev-server/components/ui/dialog.tsx +2 -10
- package/dev-server/components/ui/field.tsx +24 -49
- package/dev-server/components/ui/input.tsx +20 -21
- package/dev-server/components/ui/label.tsx +4 -4
- package/dev-server/components/ui/radio-group.tsx +37 -0
- package/dev-server/components/ui/select.tsx +153 -0
- package/dev-server/components/ui/switch.tsx +31 -0
- package/dev-server/components/ui/tabs.tsx +1 -1
- package/dev-server/components/ui/textarea.tsx +18 -19
- package/dev-server/env.d.ts +4 -1
- package/dev-server/expanded/main.tsx +20 -22
- package/dev-server/expanded.html +0 -1
- package/dev-server/featured/main.tsx +29 -36
- package/dev-server/featured-carousel.html +0 -1
- package/dev-server/featured.html +0 -1
- package/dev-server/index.html +1 -7
- package/dev-server/lib/utils.ts +3 -3
- package/dev-server/package.json +3 -3
- package/dev-server/postcss/tailwind-source-fallback.js +2 -7
- package/dev-server/postcss.config.mjs +2 -2
- package/dev-server/preview/Preview.tsx +310 -350
- package/dev-server/preview/main.tsx +8 -8
- package/dev-server/preview/preview.css +0 -1
- package/dev-server/public/site.webmanifest +1 -1
- package/dev-server/rsbuild.config.ts +1 -1
- package/dev-server/shared/dev-parent-simulator.ts +219 -0
- package/dev-server/shared/theme-presets.ts +71 -75
- package/dev-server/shared/theme-utils.ts +11 -11
- package/dist/cli.js +18 -12
- package/dist/cli.js.map +1 -1
- package/dist/commands/add.d.ts.map +1 -1
- package/dist/commands/add.js +27 -42
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/build.d.ts.map +1 -1
- package/dist/commands/build.js +26 -16
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/deploy.d.ts +1 -11
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +3 -13
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +132 -388
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +17 -29
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/logout.d.ts.map +1 -1
- package/dist/commands/logout.js +6 -11
- package/dist/commands/logout.js.map +1 -1
- package/dist/commands/rollback.d.ts +10 -0
- package/dist/commands/rollback.d.ts.map +1 -0
- package/dist/commands/rollback.js +148 -0
- package/dist/commands/rollback.js.map +1 -0
- package/dist/commands/status.d.ts +8 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +96 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/test-url-match-rules.d.ts.map +1 -1
- package/dist/commands/test-url-match-rules.js +20 -26
- package/dist/commands/test-url-match-rules.js.map +1 -1
- package/dist/lib/auth/device-flow.d.ts +1 -1
- package/dist/lib/auth/device-flow.d.ts.map +1 -1
- package/dist/lib/auth/device-flow.js +3 -3
- package/dist/lib/auth/device-flow.js.map +1 -1
- package/dist/lib/auth/token-storage.d.ts.map +1 -1
- package/dist/lib/auth/token-storage.js +14 -37
- package/dist/lib/auth/token-storage.js.map +1 -1
- package/dist/lib/build/detect-layouts.d.ts.map +1 -1
- package/dist/lib/build/detect-layouts.js +27 -13
- package/dist/lib/build/detect-layouts.js.map +1 -1
- package/dist/lib/config/load-config.d.ts.map +1 -1
- package/dist/lib/config/load-config.js +0 -2
- package/dist/lib/config/load-config.js.map +1 -1
- package/dist/lib/deploy/deploy-output.d.ts +2 -1
- package/dist/lib/deploy/deploy-output.d.ts.map +1 -1
- package/dist/lib/deploy/deploy-output.js +9 -1
- package/dist/lib/deploy/deploy-output.js.map +1 -1
- package/dist/lib/deploy/deploy-phases.d.ts +2 -0
- package/dist/lib/deploy/deploy-phases.d.ts.map +1 -1
- package/dist/lib/deploy/deploy-phases.js +9 -23
- package/dist/lib/deploy/deploy-phases.js.map +1 -1
- package/dist/lib/deploy/deploy-utils.d.ts +15 -7
- package/dist/lib/deploy/deploy-utils.d.ts.map +1 -1
- package/dist/lib/deploy/deploy-utils.js +49 -36
- package/dist/lib/deploy/deploy-utils.js.map +1 -1
- package/dist/lib/deploy/generate-manifest-files.d.ts.map +1 -1
- package/dist/lib/deploy/generate-manifest-files.js +13 -39
- package/dist/lib/deploy/generate-manifest-files.js.map +1 -1
- package/dist/lib/deploy/pack-project.d.ts.map +1 -1
- package/dist/lib/deploy/pack-project.js +34 -20
- package/dist/lib/deploy/pack-project.js.map +1 -1
- package/dist/lib/deploy/slot-manager.d.ts +54 -0
- package/dist/lib/deploy/slot-manager.d.ts.map +1 -0
- package/dist/lib/deploy/slot-manager.js +72 -0
- package/dist/lib/deploy/slot-manager.js.map +1 -0
- package/dist/lib/deploy/test-url-match-rules.d.ts +10 -2
- package/dist/lib/deploy/test-url-match-rules.d.ts.map +1 -1
- package/dist/lib/deploy/test-url-match-rules.js +1 -1
- package/dist/lib/deploy/test-url-match-rules.js.map +1 -1
- package/dist/lib/deploy/upload.d.ts +1 -0
- package/dist/lib/deploy/upload.d.ts.map +1 -1
- package/dist/lib/deploy/upload.js +15 -24
- package/dist/lib/deploy/upload.js.map +1 -1
- package/dist/lib/deploy/validation.d.ts.map +1 -1
- package/dist/lib/deploy/validation.js +43 -48
- package/dist/lib/deploy/validation.js.map +1 -1
- package/dist/lib/rsbuild/config-factory.d.ts.map +1 -1
- package/dist/lib/rsbuild/config-factory.js +10 -17
- package/dist/lib/rsbuild/config-factory.js.map +1 -1
- package/dist/lib/rsbuild/plugins/asset-versioning.d.ts.map +1 -1
- package/dist/lib/rsbuild/plugins/asset-versioning.js +4 -14
- package/dist/lib/rsbuild/plugins/asset-versioning.js.map +1 -1
- package/dist/lib/rsbuild/plugins/brotli-compression.d.ts.map +1 -1
- package/dist/lib/rsbuild/plugins/brotli-compression.js +4 -4
- package/dist/lib/rsbuild/plugins/brotli-compression.js.map +1 -1
- package/dist/lib/rsbuild/plugins/copy-public.d.ts.map +1 -1
- package/dist/lib/rsbuild/plugins/copy-public.js.map +1 -1
- package/dist/lib/rsbuild/postcss/tailwind-source-fallback.d.ts.map +1 -1
- package/dist/lib/rsbuild/postcss/tailwind-source-fallback.js +1 -3
- package/dist/lib/rsbuild/postcss/tailwind-source-fallback.js.map +1 -1
- package/dist/lib/utils/console.d.ts +8 -0
- package/dist/lib/utils/console.d.ts.map +1 -0
- package/dist/lib/utils/console.js +10 -0
- package/dist/lib/utils/console.js.map +1 -0
- package/dist/lib/utils/filesystem.d.ts +9 -0
- package/dist/lib/utils/filesystem.d.ts.map +1 -0
- package/dist/lib/utils/filesystem.js +30 -0
- package/dist/lib/utils/filesystem.js.map +1 -0
- package/dist/lib/utils/formatters.d.ts +8 -0
- package/dist/lib/utils/formatters.d.ts.map +1 -0
- package/dist/lib/utils/formatters.js +22 -0
- package/dist/lib/utils/formatters.js.map +1 -0
- package/dist/lib/utils/index.d.ts +7 -0
- package/dist/lib/utils/index.d.ts.map +1 -0
- package/dist/lib/utils/index.js +7 -0
- package/dist/lib/utils/index.js.map +1 -0
- package/dist/lib/utils/setup-runtime.d.ts.map +1 -1
- package/dist/lib/utils/setup-runtime.js +22 -63
- package/dist/lib/utils/setup-runtime.js.map +1 -1
- package/dist/schema/config.schema.d.ts +9 -48
- package/dist/schema/config.schema.d.ts.map +1 -1
- package/dist/schema/config.schema.js +119 -120
- package/dist/schema/config.schema.js.map +1 -1
- package/dist/sdk/hooks/mocks.d.ts +9 -0
- package/dist/sdk/hooks/mocks.d.ts.map +1 -0
- package/dist/sdk/hooks/mocks.js +17 -0
- package/dist/sdk/hooks/mocks.js.map +1 -0
- package/dist/sdk/hooks/use-audience-manager.d.ts +44 -0
- package/dist/sdk/hooks/use-audience-manager.d.ts.map +1 -0
- package/dist/sdk/hooks/use-audience-manager.js +109 -0
- package/dist/sdk/hooks/use-audience-manager.js.map +1 -0
- package/dist/sdk/hooks/use-ip.d.ts +45 -0
- package/dist/sdk/hooks/use-ip.d.ts.map +1 -0
- package/dist/sdk/hooks/use-ip.js +46 -0
- package/dist/sdk/hooks/use-ip.js.map +1 -0
- package/dist/sdk/hooks/use-sdk-request.d.ts +46 -0
- package/dist/sdk/hooks/use-sdk-request.d.ts.map +1 -0
- package/dist/sdk/hooks/use-sdk-request.js +65 -0
- package/dist/sdk/hooks/use-sdk-request.js.map +1 -0
- package/dist/sdk/hooks/use-theme.d.ts +45 -0
- package/dist/sdk/hooks/use-theme.d.ts.map +1 -0
- package/dist/sdk/hooks/use-theme.js +97 -0
- package/dist/sdk/hooks/use-theme.js.map +1 -0
- package/dist/sdk/hooks/use-visitor.d.ts +41 -0
- package/dist/sdk/hooks/use-visitor.d.ts.map +1 -0
- package/dist/sdk/hooks/use-visitor.js +42 -0
- package/dist/sdk/hooks/use-visitor.js.map +1 -0
- package/dist/sdk/hooks/validation.d.ts +8 -0
- package/dist/sdk/hooks/validation.d.ts.map +1 -0
- package/dist/sdk/hooks/validation.js +13 -0
- package/dist/sdk/hooks/validation.js.map +1 -0
- package/dist/sdk/index.d.ts +17 -5
- package/dist/sdk/index.d.ts.map +1 -1
- package/dist/sdk/index.js +16 -5
- package/dist/sdk/index.js.map +1 -1
- package/dist/sdk/message-bus.d.ts +59 -0
- package/dist/sdk/message-bus.d.ts.map +1 -0
- package/dist/sdk/message-bus.js +152 -0
- package/dist/sdk/message-bus.js.map +1 -0
- package/dist/sdk/messages.d.ts +121 -0
- package/dist/sdk/messages.d.ts.map +1 -0
- package/dist/sdk/messages.js +9 -0
- package/dist/sdk/messages.js.map +1 -0
- package/dist/sdk/send-message.d.ts +1 -1
- package/dist/sdk/send-message.js +18 -18
- package/dist/sdk/send-message.js.map +1 -1
- package/dist/sdk/use-expand-link-app.d.ts +3 -3
- package/dist/sdk/use-expand-link-app.d.ts.map +1 -1
- package/dist/sdk/use-expand-link-app.js +9 -5
- package/dist/sdk/use-expand-link-app.js.map +1 -1
- package/dist/types.d.ts +235 -55
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +8 -3
- package/dist/types.js.map +1 -1
- package/package.json +3 -9
package/dist/commands/dev.js
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
import pc from 'picocolors';
|
|
2
2
|
import { createRsbuild, logger } from '@rsbuild/core';
|
|
3
3
|
import { pluginReact } from '@rsbuild/plugin-react';
|
|
4
|
-
import { dirname,
|
|
4
|
+
import { dirname, join, relative, resolve } from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
import { validateLayouts, detectLayouts } from '../lib/build/detect-layouts.js';
|
|
7
7
|
import { loadConfig } from '../lib/config/load-config.js';
|
|
8
8
|
import { detect } from 'detect-port';
|
|
9
|
-
import { existsSync,
|
|
9
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
10
10
|
import chokidar from 'chokidar';
|
|
11
|
-
const writeLine = (message = '') => {
|
|
12
|
-
process.stdout.write(`${message}\n`);
|
|
13
|
-
};
|
|
14
11
|
const __filename = fileURLToPath(import.meta.url);
|
|
15
12
|
const __dirname = dirname(__filename);
|
|
16
13
|
/**
|
|
@@ -34,7 +31,7 @@ function createConfigWatcherPlugin(userProjectPath, onConfigChange) {
|
|
|
34
31
|
ignorePermissionErrors: true,
|
|
35
32
|
});
|
|
36
33
|
const onChange = async (filePath) => {
|
|
37
|
-
|
|
34
|
+
console.log(pc.cyan(`\n ○ ${relative(userProjectPath, filePath)} changed, restarting dev server...\n`));
|
|
38
35
|
await onConfigChange();
|
|
39
36
|
};
|
|
40
37
|
watcher.on('change', onChange);
|
|
@@ -48,293 +45,79 @@ function createConfigWatcherPlugin(userProjectPath, onConfigChange) {
|
|
|
48
45
|
};
|
|
49
46
|
}
|
|
50
47
|
/**
|
|
51
|
-
*
|
|
52
|
-
* while matching the legacy dev server behavior.
|
|
48
|
+
* Serves app/icon.svg from user's project at /app-icon.svg
|
|
53
49
|
*/
|
|
54
|
-
function
|
|
55
|
-
const contentTypes = {
|
|
56
|
-
css: 'text/css',
|
|
57
|
-
gif: 'image/gif',
|
|
58
|
-
html: 'text/html',
|
|
59
|
-
ico: 'image/x-icon',
|
|
60
|
-
jpeg: 'image/jpeg',
|
|
61
|
-
jpg: 'image/jpeg',
|
|
62
|
-
js: 'application/javascript',
|
|
63
|
-
json: 'application/json',
|
|
64
|
-
png: 'image/png',
|
|
65
|
-
svg: 'image/svg+xml',
|
|
66
|
-
txt: 'text/plain',
|
|
67
|
-
webp: 'image/webp',
|
|
68
|
-
};
|
|
50
|
+
function createAppIconPlugin(userProjectPath) {
|
|
69
51
|
return {
|
|
70
|
-
name: 'linkapp:
|
|
52
|
+
name: 'linkapp:app-icon',
|
|
71
53
|
setup(api) {
|
|
72
54
|
api.onBeforeStartDevServer(async ({ server }) => {
|
|
73
|
-
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
const publicDirRoot = resolve(userPublicDir);
|
|
55
|
+
const iconPath = join(userProjectPath, 'app', 'icon.svg');
|
|
77
56
|
server.middlewares.use((req, res, next) => {
|
|
78
|
-
if (
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const method = req.method ?? 'GET';
|
|
83
|
-
if (method !== 'GET' && method !== 'HEAD') {
|
|
84
|
-
next();
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
const [rawPath] = req.url.split('?');
|
|
88
|
-
if (!rawPath || rawPath === '/' || rawPath === '') {
|
|
89
|
-
next();
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
let decodedPath;
|
|
93
|
-
try {
|
|
94
|
-
decodedPath = decodeURIComponent(rawPath);
|
|
95
|
-
}
|
|
96
|
-
catch {
|
|
97
|
-
next();
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
const normalizedPath = decodedPath.startsWith('/') ? decodedPath : `/${decodedPath}`;
|
|
101
|
-
const targetPath = resolve(publicDirRoot, `.${normalizedPath}`);
|
|
102
|
-
const relativePath = relative(publicDirRoot, targetPath);
|
|
103
|
-
if (relativePath.startsWith('..') ||
|
|
104
|
-
relativePath.includes(`..${sep}`) ||
|
|
105
|
-
targetPath === publicDirRoot) {
|
|
106
|
-
next();
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
let stats;
|
|
110
|
-
try {
|
|
111
|
-
stats = statSync(targetPath);
|
|
112
|
-
}
|
|
113
|
-
catch {
|
|
114
|
-
next();
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
if (!stats.isFile()) {
|
|
118
|
-
next();
|
|
57
|
+
if (req.url === '/app-icon.svg' && existsSync(iconPath)) {
|
|
58
|
+
res.setHeader('Content-Type', 'image/svg+xml');
|
|
59
|
+
res.setHeader('Cache-Control', 'no-cache');
|
|
60
|
+
res.end(readFileSync(iconPath));
|
|
119
61
|
return;
|
|
120
62
|
}
|
|
121
|
-
|
|
122
|
-
const contentType = (extension && contentTypes[extension]) || 'application/octet-stream';
|
|
123
|
-
res.setHeader('Content-Type', contentType);
|
|
124
|
-
res.setHeader('Cache-Control', 'no-cache');
|
|
125
|
-
if (method === 'HEAD') {
|
|
126
|
-
res.end();
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
try {
|
|
130
|
-
const content = readFileSync(targetPath);
|
|
131
|
-
res.end(content);
|
|
132
|
-
}
|
|
133
|
-
catch (error) {
|
|
134
|
-
next(error);
|
|
135
|
-
}
|
|
63
|
+
next();
|
|
136
64
|
});
|
|
137
65
|
});
|
|
138
66
|
},
|
|
139
67
|
};
|
|
140
68
|
}
|
|
141
69
|
/**
|
|
142
|
-
* Generates
|
|
70
|
+
* Generates an entry point for layout components (expanded, featured, featured-carousel)
|
|
143
71
|
*/
|
|
144
|
-
function
|
|
145
|
-
|
|
72
|
+
function generateEntryPoint(devServerPath, config) {
|
|
73
|
+
const { componentImport, componentName, includeFont = false, includeGroupLayoutOption = false } = config;
|
|
146
74
|
const themePresetsPath = resolve(devServerPath, 'shared/theme-presets');
|
|
147
75
|
const themeUtilsPath = resolve(devServerPath, 'shared/theme-utils');
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
import { getThemeFromUrl, mergeThemeProps } from "${themeUtilsPath}";
|
|
154
|
-
|
|
155
|
-
// Declare global window properties for theme and font application
|
|
156
|
-
declare global {
|
|
157
|
-
interface Window {
|
|
158
|
-
__linkapp_applyTheme?: (variables: Record<string, string>) => void
|
|
159
|
-
__linkapp_applyFont?: (fontData: {
|
|
160
|
-
fontFamily: string
|
|
161
|
-
fontStyle?: string
|
|
162
|
-
cssUrl?: string
|
|
163
|
-
}) => void
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Preview props injected by dev server via Rsbuild define
|
|
168
|
-
declare const __PREVIEW_PROPS__: Record<string, unknown>;
|
|
169
|
-
|
|
170
|
-
// Extract just the variables from THEME_PRESETS for theme lookups
|
|
171
|
-
const THEME_VARS = Object.fromEntries(
|
|
172
|
-
Object.entries(THEME_PRESETS).map(([key, { variables }]) => [key, variables])
|
|
173
|
-
);
|
|
174
|
-
|
|
175
|
-
// Get theme variables from URL (defaults to 'default' theme)
|
|
176
|
-
const themeVariables = getThemeFromUrl(THEME_VARS) || THEME_PRESETS.default.variables;
|
|
177
|
-
|
|
178
|
-
// Get the selected theme key from URL or default to "default"
|
|
179
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
180
|
-
const themeKey = urlParams.get("theme") || "default";
|
|
181
|
-
const selectedTheme = THEME_PRESETS[themeKey as keyof typeof THEME_PRESETS] || THEME_PRESETS.default;
|
|
182
|
-
|
|
183
|
-
// Merge with preview props
|
|
184
|
-
const previewProps = __PREVIEW_PROPS__ || {};
|
|
185
|
-
const mergedProps = mergeThemeProps(themeVariables, previewProps);
|
|
186
|
-
|
|
187
|
-
// Apply theme CSS variables on mount (always apply, even if default)
|
|
188
|
-
if (window.__linkapp_applyTheme) {
|
|
189
|
-
window.__linkapp_applyTheme(themeVariables);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Apply font on mount
|
|
76
|
+
const fontDeclaration = includeFont
|
|
77
|
+
? ' __linkapp_applyFont?: (fontData: { fontFamily: string; fontStyle?: string; cssUrl?: string }) => void'
|
|
78
|
+
: '';
|
|
79
|
+
const fontApplication = includeFont
|
|
80
|
+
? `
|
|
193
81
|
if (selectedTheme.font && window.__linkapp_applyFont) {
|
|
194
82
|
window.__linkapp_applyFont(selectedTheme.font);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
const rootElement = document.getElementById("root");
|
|
198
|
-
if (!rootElement) {
|
|
199
|
-
throw new Error("Root element not found");
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
createRoot(rootElement).render(
|
|
203
|
-
<StrictMode>
|
|
204
|
-
<Expanded {...mergedProps} />
|
|
205
|
-
</StrictMode>,
|
|
206
|
-
);
|
|
207
|
-
`;
|
|
208
|
-
}
|
|
209
|
-
/**
|
|
210
|
-
* Generates the featured-carousel entry point (always uses FeaturedCarousel component)
|
|
211
|
-
*/
|
|
212
|
-
function generateFeaturedCarouselEntryPoint(devServerPath) {
|
|
213
|
-
// Use absolute paths to shared theme files in the dev-server directory
|
|
214
|
-
const themePresetsPath = resolve(devServerPath, 'shared/theme-presets');
|
|
215
|
-
const themeUtilsPath = resolve(devServerPath, 'shared/theme-utils');
|
|
216
|
-
return `import FeaturedCarousel from "@/app/featured-carousel";
|
|
217
|
-
import { StrictMode } from "react";
|
|
218
|
-
import { createRoot } from "react-dom/client";
|
|
219
|
-
import "@/app/globals.css";
|
|
220
|
-
import { THEME_PRESETS } from "${themePresetsPath}";
|
|
221
|
-
import { getThemeFromUrl, mergeThemeProps } from "${themeUtilsPath}";
|
|
222
|
-
|
|
223
|
-
// Declare global window property for theme application
|
|
224
|
-
declare global {
|
|
225
|
-
interface Window {
|
|
226
|
-
__linkapp_applyTheme?: (variables: Record<string, string>) => void
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// Preview props injected by dev server via Rsbuild define
|
|
231
|
-
declare const __PREVIEW_PROPS__: Record<string, unknown>;
|
|
232
|
-
|
|
233
|
-
// Extract just the variables from THEME_PRESETS for theme lookups
|
|
234
|
-
const THEME_VARS = Object.fromEntries(
|
|
235
|
-
Object.entries(THEME_PRESETS).map(([key, { variables }]) => [key, variables])
|
|
236
|
-
);
|
|
237
|
-
|
|
238
|
-
// Get theme variables from URL
|
|
239
|
-
const themeVariables = getThemeFromUrl(THEME_VARS) || THEME_PRESETS.default.variables;
|
|
240
|
-
|
|
241
|
-
// Merge with preview props
|
|
242
|
-
const previewProps = __PREVIEW_PROPS__ || {};
|
|
243
|
-
const mergedProps = mergeThemeProps(themeVariables, previewProps);
|
|
244
|
-
|
|
245
|
-
// Apply theme CSS variables on mount (always apply, even if default)
|
|
246
|
-
if (window.__linkapp_applyTheme) {
|
|
247
|
-
window.__linkapp_applyTheme(themeVariables);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
const rootElement = document.getElementById("root");
|
|
251
|
-
if (!rootElement) {
|
|
252
|
-
throw new Error("Root element not found");
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
createRoot(rootElement).render(
|
|
256
|
-
<StrictMode>
|
|
257
|
-
<FeaturedCarousel {...mergedProps} />
|
|
258
|
-
</StrictMode>,
|
|
259
|
-
);
|
|
260
|
-
`;
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* Generates the featured entry point dynamically based on available layouts
|
|
264
|
-
*/
|
|
265
|
-
function generateFeaturedEntryPoint(devServerPath, hasFeaturedCarousel) {
|
|
266
|
-
// Use absolute paths to shared theme files in the dev-server directory
|
|
267
|
-
const themePresetsPath = resolve(devServerPath, 'shared/theme-presets');
|
|
268
|
-
const themeUtilsPath = resolve(devServerPath, 'shared/theme-utils');
|
|
269
|
-
const featuredCarouselImport = hasFeaturedCarousel
|
|
270
|
-
? `import FeaturedCarousel from "@/app/featured-carousel";`
|
|
83
|
+
}`
|
|
271
84
|
: '';
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
const LayoutComponent = (groupLayoutOptionParam === 'carousel' && FeaturedCarousel)
|
|
275
|
-
? FeaturedCarousel
|
|
276
|
-
: Featured;`
|
|
277
|
-
: `// Only Featured layout is available
|
|
278
|
-
const LayoutComponent = Featured;`;
|
|
279
|
-
return `import Featured from "@/app/featured";
|
|
280
|
-
${featuredCarouselImport}
|
|
281
|
-
import { StrictMode } from "react";
|
|
85
|
+
return `import ${componentName} from "${componentImport}";
|
|
86
|
+
${config.additionalImports || ''}import { StrictMode } from "react";
|
|
282
87
|
import { createRoot } from "react-dom/client";
|
|
283
88
|
import "@/app/globals.css";
|
|
284
89
|
import { THEME_PRESETS } from "${themePresetsPath}";
|
|
285
90
|
import { getThemeFromUrl, mergeThemeProps } from "${themeUtilsPath}";
|
|
286
91
|
|
|
287
|
-
// Declare global window properties for theme and font application
|
|
288
92
|
declare global {
|
|
289
93
|
interface Window {
|
|
290
94
|
__linkapp_applyTheme?: (variables: Record<string, string>) => void
|
|
291
|
-
|
|
292
|
-
fontFamily: string
|
|
293
|
-
fontStyle?: string
|
|
294
|
-
cssUrl?: string
|
|
295
|
-
}) => void
|
|
296
|
-
}
|
|
95
|
+
${fontDeclaration} }
|
|
297
96
|
}
|
|
298
97
|
|
|
299
|
-
// Preview props injected by dev server via Rsbuild define
|
|
300
98
|
declare const __PREVIEW_PROPS__: Record<string, unknown>;
|
|
301
99
|
|
|
302
|
-
// Extract just the variables from THEME_PRESETS for theme lookups
|
|
303
100
|
const THEME_VARS = Object.fromEntries(
|
|
304
101
|
Object.entries(THEME_PRESETS).map(([key, { variables }]) => [key, variables])
|
|
305
102
|
);
|
|
306
103
|
|
|
307
|
-
// Get theme variables and groupLayoutOption from URL
|
|
308
104
|
const params = new URLSearchParams(window.location.search);
|
|
309
|
-
const groupLayoutOptionParam = params.get('groupLayoutOption');
|
|
105
|
+
${includeGroupLayoutOption ? `const groupLayoutOptionParam = params.get('groupLayoutOption');\n` : ''}const themeKey = params.get("theme") || "default";
|
|
310
106
|
const themeVariables = getThemeFromUrl(THEME_VARS) || THEME_PRESETS.default.variables;
|
|
311
|
-
|
|
312
|
-
// Get the selected theme for font application
|
|
313
|
-
const themeKey = params.get("theme") || "default";
|
|
314
107
|
const selectedTheme = THEME_PRESETS[themeKey as keyof typeof THEME_PRESETS] || THEME_PRESETS.default;
|
|
315
108
|
|
|
316
|
-
// Merge with preview props and add groupLayoutOption
|
|
317
109
|
const previewProps = __PREVIEW_PROPS__ || {};
|
|
318
|
-
const mergedProps = mergeThemeProps(themeVariables, previewProps, {
|
|
319
|
-
groupLayoutOption: groupLayoutOptionParam || undefined,
|
|
320
|
-
});
|
|
110
|
+
const mergedProps = mergeThemeProps(themeVariables, previewProps${includeGroupLayoutOption ? ', { groupLayoutOption: groupLayoutOptionParam || undefined }' : ''});
|
|
321
111
|
|
|
322
|
-
// Apply theme CSS variables on mount (always apply, even if default)
|
|
323
112
|
if (window.__linkapp_applyTheme) {
|
|
324
113
|
window.__linkapp_applyTheme(themeVariables);
|
|
325
114
|
}
|
|
326
|
-
|
|
327
|
-
// Apply font on mount
|
|
328
|
-
if (selectedTheme.font && window.__linkapp_applyFont) {
|
|
329
|
-
window.__linkapp_applyFont(selectedTheme.font);
|
|
330
|
-
}
|
|
115
|
+
${fontApplication}
|
|
331
116
|
|
|
332
117
|
const rootElement = document.getElementById("root");
|
|
333
|
-
if (!rootElement)
|
|
334
|
-
throw new Error("Root element not found");
|
|
335
|
-
}
|
|
118
|
+
if (!rootElement) throw new Error("Root element not found");
|
|
336
119
|
|
|
337
|
-
${componentSelection}
|
|
120
|
+
${config.componentSelection || `const LayoutComponent = ${componentName};`}
|
|
338
121
|
|
|
339
122
|
createRoot(rootElement).render(
|
|
340
123
|
<StrictMode>
|
|
@@ -353,41 +136,78 @@ export async function devCommand(options) {
|
|
|
353
136
|
});
|
|
354
137
|
try {
|
|
355
138
|
const startTime = Date.now();
|
|
139
|
+
// Detect headless mode
|
|
140
|
+
const layoutDetection = detectLayouts(userProjectPath);
|
|
141
|
+
const isHeadless = layoutDetection.layouts.length === 0;
|
|
142
|
+
if (isHeadless) {
|
|
143
|
+
console.log(`
|
|
144
|
+
${pc.cyan('📋 Headless LinkApp - No frontend to serve')}
|
|
145
|
+
${pc.dim('This LinkApp only contains settings schema')}
|
|
146
|
+
`);
|
|
147
|
+
try {
|
|
148
|
+
const config = loadConfig(userProjectPath);
|
|
149
|
+
if (config.settings) {
|
|
150
|
+
console.log(`${pc.bold('Settings schema:')}
|
|
151
|
+
${JSON.stringify(config.settings, null, 2)}
|
|
152
|
+
`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
console.log(`${pc.yellow('⚠ Warning: Could not load config')}
|
|
157
|
+
${pc.dim(` ${error instanceof Error ? error.message : error}`)}
|
|
158
|
+
`);
|
|
159
|
+
}
|
|
160
|
+
console.log(pc.dim('To develop UI, work in linktr.ee-profiles repo\n'));
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
356
163
|
// Validate layouts exist
|
|
357
164
|
const validation = validateLayouts(userProjectPath);
|
|
358
165
|
if (!validation.valid) {
|
|
359
|
-
|
|
166
|
+
console.log(pc.red('✗ Invalid project structure'));
|
|
360
167
|
for (const error of validation.errors) {
|
|
361
|
-
|
|
168
|
+
console.log(pc.red(` ${error}`));
|
|
362
169
|
}
|
|
363
170
|
process.exit(1);
|
|
364
171
|
}
|
|
365
172
|
// Get dev server path (needed for generating entry points and later for Rsbuild config)
|
|
366
173
|
const devServerPath = resolve(__dirname, '../../dev-server');
|
|
367
|
-
// Detect available layouts for dynamic entry generation
|
|
368
|
-
const
|
|
369
|
-
const
|
|
370
|
-
const
|
|
371
|
-
const expandedLayout = layoutDetection.layouts.find(l => l.name === 'expanded');
|
|
174
|
+
// Detect available layouts for dynamic entry generation (already detected above for headless check)
|
|
175
|
+
const hasFeatured = layoutDetection.layouts.some((l) => l.name === 'featured');
|
|
176
|
+
const hasFeaturedCarousel = layoutDetection.layouts.some((l) => l.name === 'featured-carousel');
|
|
177
|
+
const expandedLayout = layoutDetection.layouts.find((l) => l.name === 'expanded');
|
|
372
178
|
const expandedImportPath = expandedLayout
|
|
373
179
|
? `@/app/${expandedLayout.fileName.replace(/\.(jsx?|tsx?)$/, '')}`
|
|
374
180
|
: '@/app/expanded';
|
|
375
181
|
// Generate dynamic entry points in .linkapp directory
|
|
376
|
-
const linkappDir = join(userProjectPath,
|
|
182
|
+
const linkappDir = join(userProjectPath, '.linkapp');
|
|
377
183
|
if (!existsSync(linkappDir)) {
|
|
378
184
|
mkdirSync(linkappDir, { recursive: true });
|
|
379
185
|
}
|
|
380
|
-
const expandedEntryPath = join(linkappDir,
|
|
381
|
-
const featuredEntryPath = join(linkappDir,
|
|
382
|
-
const featuredCarouselEntryPath = join(linkappDir,
|
|
383
|
-
writeFileSync(expandedEntryPath,
|
|
384
|
-
|
|
186
|
+
const expandedEntryPath = join(linkappDir, 'dev-expanded-main.tsx');
|
|
187
|
+
const featuredEntryPath = join(linkappDir, 'dev-featured-main.tsx');
|
|
188
|
+
const featuredCarouselEntryPath = join(linkappDir, 'dev-featured-carousel-main.tsx');
|
|
189
|
+
writeFileSync(expandedEntryPath, generateEntryPoint(devServerPath, {
|
|
190
|
+
componentImport: expandedImportPath,
|
|
191
|
+
componentName: 'Expanded',
|
|
192
|
+
includeFont: true,
|
|
193
|
+
}), 'utf-8');
|
|
385
194
|
if (hasFeatured) {
|
|
386
|
-
writeFileSync(featuredEntryPath,
|
|
195
|
+
writeFileSync(featuredEntryPath, generateEntryPoint(devServerPath, {
|
|
196
|
+
componentImport: '@/app/featured',
|
|
197
|
+
componentName: 'Featured',
|
|
198
|
+
includeFont: true,
|
|
199
|
+
includeGroupLayoutOption: true,
|
|
200
|
+
additionalImports: hasFeaturedCarousel ? `import FeaturedCarousel from "@/app/featured-carousel";\n` : '',
|
|
201
|
+
componentSelection: hasFeaturedCarousel
|
|
202
|
+
? `const LayoutComponent = (groupLayoutOptionParam === 'carousel' && FeaturedCarousel) ? FeaturedCarousel : Featured;`
|
|
203
|
+
: undefined,
|
|
204
|
+
}), 'utf-8');
|
|
387
205
|
}
|
|
388
|
-
// Only generate featured-carousel entry if the layout exists
|
|
389
206
|
if (hasFeaturedCarousel) {
|
|
390
|
-
writeFileSync(featuredCarouselEntryPath,
|
|
207
|
+
writeFileSync(featuredCarouselEntryPath, generateEntryPoint(devServerPath, {
|
|
208
|
+
componentImport: '@/app/featured-carousel',
|
|
209
|
+
componentName: 'FeaturedCarousel',
|
|
210
|
+
}), 'utf-8');
|
|
391
211
|
}
|
|
392
212
|
// Helper to load config (called on initial start and on config changes)
|
|
393
213
|
const loadProjectConfig = () => {
|
|
@@ -396,82 +216,40 @@ export async function devCommand(options) {
|
|
|
396
216
|
return {
|
|
397
217
|
previewProps: config.preview_props || {},
|
|
398
218
|
settingsConfig: config.settings || {},
|
|
219
|
+
manifestConfig: config.manifest || { name: 'LinkApp', tagline: '' },
|
|
399
220
|
};
|
|
400
221
|
}
|
|
401
222
|
catch (error) {
|
|
402
|
-
|
|
403
|
-
|
|
223
|
+
console.log(pc.yellow('⚠ Warning: Could not load config, using default preview props'));
|
|
224
|
+
console.log(pc.dim(` ${error instanceof Error ? error.message : error}`));
|
|
404
225
|
return {
|
|
405
226
|
previewProps: {},
|
|
406
227
|
settingsConfig: {},
|
|
228
|
+
manifestConfig: { name: 'LinkApp', tagline: '' },
|
|
407
229
|
};
|
|
408
230
|
}
|
|
409
231
|
};
|
|
410
232
|
// Load config initially
|
|
411
|
-
let { previewProps, settingsConfig } = loadProjectConfig();
|
|
233
|
+
let { previewProps, settingsConfig, manifestConfig } = loadProjectConfig();
|
|
412
234
|
const requestedPort = options.port || 3000;
|
|
413
235
|
const availablePort = await detect(requestedPort);
|
|
414
236
|
// Show warning if the requested port was occupied
|
|
415
237
|
if (requestedPort !== availablePort) {
|
|
416
|
-
|
|
417
|
-
|
|
238
|
+
console.log(pc.yellow(` ⚠ Port ${requestedPort} is in use, using ${availablePort} instead`));
|
|
239
|
+
console.log();
|
|
418
240
|
}
|
|
419
241
|
const port = availablePort;
|
|
420
242
|
const userPublicDir = join(userProjectPath, 'public');
|
|
421
243
|
const printReadyMessage = (readyTime, urls) => {
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
244
|
+
console.log();
|
|
245
|
+
console.log(pc.bold(pc.cyan(' LinkApp')) + pc.dim(' ready'));
|
|
246
|
+
console.log();
|
|
425
247
|
if (urls.length > 0) {
|
|
426
|
-
|
|
248
|
+
console.log(` ${pc.green('➜')} ${pc.bold('Local:')} ${pc.cyan(urls[0])}`);
|
|
427
249
|
}
|
|
428
|
-
|
|
429
|
-
|
|
250
|
+
console.log();
|
|
251
|
+
console.log(pc.green(` ✓ Ready in ${readyTime}s`));
|
|
430
252
|
};
|
|
431
|
-
const setupKeyboardShortcuts = (handlers) => {
|
|
432
|
-
if (!process.stdin.isTTY) {
|
|
433
|
-
return () => { };
|
|
434
|
-
}
|
|
435
|
-
const handleData = (data) => {
|
|
436
|
-
if (data === '\u0003') {
|
|
437
|
-
void handlers.exit();
|
|
438
|
-
return;
|
|
439
|
-
}
|
|
440
|
-
const key = data.trim().toLowerCase();
|
|
441
|
-
if (key === '') {
|
|
442
|
-
return;
|
|
443
|
-
}
|
|
444
|
-
switch (key) {
|
|
445
|
-
case 'r':
|
|
446
|
-
void handlers.restart();
|
|
447
|
-
break;
|
|
448
|
-
case 'u':
|
|
449
|
-
handlers.showUrls();
|
|
450
|
-
break;
|
|
451
|
-
case 'c':
|
|
452
|
-
handlers.clear();
|
|
453
|
-
break;
|
|
454
|
-
case 'q':
|
|
455
|
-
void handlers.exit();
|
|
456
|
-
break;
|
|
457
|
-
default:
|
|
458
|
-
break;
|
|
459
|
-
}
|
|
460
|
-
};
|
|
461
|
-
process.stdin.setRawMode(true);
|
|
462
|
-
process.stdin.resume();
|
|
463
|
-
process.stdin.setEncoding('utf8');
|
|
464
|
-
process.stdin.on('data', handleData);
|
|
465
|
-
return () => {
|
|
466
|
-
process.stdin.off('data', handleData);
|
|
467
|
-
if (process.stdin.isTTY) {
|
|
468
|
-
process.stdin.setRawMode(false);
|
|
469
|
-
}
|
|
470
|
-
process.stdin.pause();
|
|
471
|
-
};
|
|
472
|
-
};
|
|
473
|
-
// Callback wrapper for config watcher (will be assigned after restartServer is defined)
|
|
474
|
-
let restartCallback;
|
|
475
253
|
// Helper to create Rsbuild instance with current config values
|
|
476
254
|
const createRsbuildInstance = async () => {
|
|
477
255
|
return await createRsbuild({
|
|
@@ -481,26 +259,24 @@ export async function devCommand(options) {
|
|
|
481
259
|
},
|
|
482
260
|
plugins: [
|
|
483
261
|
pluginReact(),
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
}),
|
|
262
|
+
createAppIconPlugin(userProjectPath),
|
|
263
|
+
// Use lazy callback to avoid TDZ error - restartServer is defined after this function
|
|
264
|
+
createConfigWatcherPlugin(userProjectPath, () => restartServer()),
|
|
488
265
|
],
|
|
489
266
|
source: {
|
|
490
267
|
entry: {
|
|
491
|
-
index: resolve(devServerPath,
|
|
268
|
+
index: resolve(devServerPath, 'preview/main.tsx'),
|
|
492
269
|
expanded: expandedEntryPath,
|
|
493
270
|
sheet: expandedEntryPath, // Legacy alias for expanded layout
|
|
494
271
|
...(hasFeatured ? { featured: featuredEntryPath } : {}),
|
|
495
|
-
...(hasFeaturedCarousel
|
|
496
|
-
? { "featured-carousel": featuredCarouselEntryPath }
|
|
497
|
-
: {}),
|
|
272
|
+
...(hasFeaturedCarousel ? { 'featured-carousel': featuredCarouselEntryPath } : {}),
|
|
498
273
|
},
|
|
499
274
|
define: {
|
|
500
275
|
__PREVIEW_PROPS__: JSON.stringify(previewProps),
|
|
501
276
|
__SETTINGS_CONFIG__: JSON.stringify(settingsConfig),
|
|
277
|
+
__MANIFEST_CONFIG__: JSON.stringify(manifestConfig),
|
|
502
278
|
__HAS_FEATURED__: JSON.stringify(hasFeatured),
|
|
503
|
-
|
|
279
|
+
__HAS_FEATURED_CAROUSEL__: JSON.stringify(hasFeaturedCarousel),
|
|
504
280
|
},
|
|
505
281
|
},
|
|
506
282
|
resolve: {
|
|
@@ -510,6 +286,7 @@ export async function devCommand(options) {
|
|
|
510
286
|
dedupe: ['react', 'react-dom'],
|
|
511
287
|
},
|
|
512
288
|
html: {
|
|
289
|
+
title: manifestConfig.name || 'LinkApp',
|
|
513
290
|
template({ entryName }) {
|
|
514
291
|
const templates = {
|
|
515
292
|
index: resolve(devServerPath, 'index.html'),
|
|
@@ -535,24 +312,26 @@ export async function devCommand(options) {
|
|
|
535
312
|
tools: {
|
|
536
313
|
postcss: (opts) => {
|
|
537
314
|
// Load PostCSS config from dev-server directory
|
|
538
|
-
opts.postcssOptions
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
};
|
|
315
|
+
const postcssOptions = (opts.postcssOptions ?? {});
|
|
316
|
+
postcssOptions.config = resolve(devServerPath, 'postcss.config.mjs');
|
|
317
|
+
opts.postcssOptions = postcssOptions;
|
|
542
318
|
return opts;
|
|
543
319
|
},
|
|
544
320
|
rspack: (config) => {
|
|
545
321
|
// Add linkapp package's node_modules to module resolution paths
|
|
546
322
|
// This allows dev-server files to import dependencies from @linktr.ee/linkapp
|
|
547
323
|
// In workspaces, dependencies might be hoisted, so we check multiple locations
|
|
548
|
-
config.resolve = config.resolve
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
resolve(
|
|
553
|
-
'node_modules', //
|
|
324
|
+
config.resolve = config.resolve ?? {};
|
|
325
|
+
// Get the linkapp package root (from dist/commands/ go up 2 levels)
|
|
326
|
+
const linkappPackageRoot = resolve(__dirname, '../..');
|
|
327
|
+
const additionalNodeModulesPaths = [
|
|
328
|
+
resolve(linkappPackageRoot, 'node_modules'), // Package's own node_modules
|
|
329
|
+
resolve(linkappPackageRoot, '../node_modules'), // packages/node_modules (if hoisted to packages level)
|
|
330
|
+
resolve(linkappPackageRoot, '../../node_modules'), // Root monorepo node_modules
|
|
554
331
|
];
|
|
555
|
-
|
|
332
|
+
// Append to existing modules array rather than replacing
|
|
333
|
+
const existingModules = config.resolve.modules ?? ['node_modules'];
|
|
334
|
+
config.resolve.modules = [...additionalNodeModulesPaths, ...existingModules];
|
|
556
335
|
return config;
|
|
557
336
|
},
|
|
558
337
|
},
|
|
@@ -562,87 +341,52 @@ export async function devCommand(options) {
|
|
|
562
341
|
// Create initial Rsbuild instance
|
|
563
342
|
let rsbuild = await createRsbuildInstance();
|
|
564
343
|
let currentServer;
|
|
565
|
-
let cleanupShortcuts;
|
|
566
344
|
let isRestarting = false;
|
|
567
345
|
let isShuttingDown = false;
|
|
568
|
-
let currentUrls = [];
|
|
569
|
-
let lastReadyTime = '';
|
|
570
|
-
const startServer = async (startedAt) => {
|
|
571
|
-
const result = await rsbuild.startDevServer();
|
|
572
|
-
currentServer = result;
|
|
573
|
-
currentUrls = result.urls;
|
|
574
|
-
lastReadyTime = ((Date.now() - startedAt) / 1000).toFixed(1);
|
|
575
|
-
printReadyMessage(lastReadyTime, currentUrls);
|
|
576
|
-
};
|
|
577
346
|
const restartServer = async () => {
|
|
578
347
|
if (isShuttingDown || isRestarting || !currentServer) {
|
|
579
348
|
return;
|
|
580
349
|
}
|
|
581
350
|
isRestarting = true;
|
|
582
|
-
|
|
351
|
+
const restartStartTime = Date.now();
|
|
352
|
+
console.log(pc.cyan(' ↻ Restarting dev server...'));
|
|
583
353
|
try {
|
|
584
|
-
// Close current server
|
|
585
354
|
await currentServer.server.close();
|
|
586
|
-
// Reload config to get fresh preview props
|
|
587
355
|
const freshConfig = loadProjectConfig();
|
|
588
356
|
previewProps = freshConfig.previewProps;
|
|
589
357
|
settingsConfig = freshConfig.settingsConfig;
|
|
590
|
-
|
|
358
|
+
manifestConfig = freshConfig.manifestConfig;
|
|
591
359
|
rsbuild = await createRsbuildInstance();
|
|
592
|
-
|
|
593
|
-
const
|
|
594
|
-
|
|
360
|
+
currentServer = await rsbuild.startDevServer();
|
|
361
|
+
const restartTime = ((Date.now() - restartStartTime) / 1000).toFixed(1);
|
|
362
|
+
printReadyMessage(restartTime, currentServer.urls);
|
|
595
363
|
}
|
|
596
364
|
catch (error) {
|
|
597
365
|
currentServer = undefined;
|
|
598
|
-
|
|
599
|
-
|
|
366
|
+
console.log(pc.red('✗ Failed to restart dev server'));
|
|
367
|
+
console.log(pc.dim(` ${error instanceof Error ? error.message : error}`));
|
|
600
368
|
}
|
|
601
369
|
finally {
|
|
602
370
|
isRestarting = false;
|
|
603
371
|
}
|
|
604
372
|
};
|
|
605
|
-
const showUrls = () => {
|
|
606
|
-
writeLine();
|
|
607
|
-
if (currentUrls.length > 0) {
|
|
608
|
-
writeLine(` ${pc.green('➜')} ${pc.bold('Local:')} ${pc.cyan(currentUrls[0])}`);
|
|
609
|
-
}
|
|
610
|
-
writeLine();
|
|
611
|
-
};
|
|
612
|
-
const clearConsole = () => {
|
|
613
|
-
console.clear();
|
|
614
|
-
if (lastReadyTime) {
|
|
615
|
-
printReadyMessage(lastReadyTime, currentUrls);
|
|
616
|
-
}
|
|
617
|
-
};
|
|
618
373
|
const shutdown = async () => {
|
|
619
|
-
if (isShuttingDown)
|
|
374
|
+
if (isShuttingDown)
|
|
620
375
|
return;
|
|
621
|
-
}
|
|
622
376
|
isShuttingDown = true;
|
|
623
|
-
cleanupShortcuts?.();
|
|
624
377
|
try {
|
|
625
|
-
|
|
626
|
-
await currentServer.server.close();
|
|
627
|
-
}
|
|
378
|
+
await currentServer?.server.close();
|
|
628
379
|
}
|
|
629
|
-
catch
|
|
630
|
-
|
|
631
|
-
writeLine(pc.dim(` ${closeError instanceof Error ? closeError.message : closeError}`));
|
|
380
|
+
catch {
|
|
381
|
+
// Ignore close errors during shutdown
|
|
632
382
|
}
|
|
633
383
|
finally {
|
|
634
384
|
process.exit(0);
|
|
635
385
|
}
|
|
636
386
|
};
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
cleanupShortcuts = setupKeyboardShortcuts({
|
|
641
|
-
restart: restartServer,
|
|
642
|
-
showUrls,
|
|
643
|
-
clear: clearConsole,
|
|
644
|
-
exit: shutdown,
|
|
645
|
-
});
|
|
387
|
+
currentServer = await rsbuild.startDevServer();
|
|
388
|
+
const readyTime = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
389
|
+
printReadyMessage(readyTime, currentServer.urls);
|
|
646
390
|
process.on('SIGINT', () => {
|
|
647
391
|
void shutdown();
|
|
648
392
|
});
|
|
@@ -650,7 +394,7 @@ export async function devCommand(options) {
|
|
|
650
394
|
await new Promise(() => { });
|
|
651
395
|
}
|
|
652
396
|
catch (error) {
|
|
653
|
-
|
|
397
|
+
console.log(pc.red('✗ Failed to start server'));
|
|
654
398
|
console.error(pc.red('✗ Error:'), error);
|
|
655
399
|
process.exit(1);
|
|
656
400
|
}
|