@linktr.ee/linkapp 0.0.37 → 0.0.39
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/dev-server/featured/main.tsx +1 -1
- package/dev-server/preview/{preview.tsx → Preview.tsx} +1 -1
- package/dev-server/sheet/main.tsx +1 -1
- package/dist/cli.js +1 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/add.d.ts.map +1 -1
- package/dist/commands/add.js +84 -59
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/build.d.ts.map +1 -1
- package/dist/commands/build.js +66 -84
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/deploy.d.ts +15 -10
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +26 -199
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +124 -171
- package/dist/commands/dev.js.map +1 -1
- package/dist/lib/config/resolve-config-path.d.ts +19 -0
- package/dist/lib/config/resolve-config-path.d.ts.map +1 -0
- package/dist/lib/config/resolve-config-path.js +54 -0
- package/dist/lib/config/resolve-config-path.js.map +1 -0
- package/dist/lib/deploy/artifacts.d.ts +21 -0
- package/dist/lib/deploy/artifacts.d.ts.map +1 -0
- package/dist/lib/deploy/artifacts.js +35 -0
- package/dist/lib/deploy/artifacts.js.map +1 -0
- package/dist/lib/deploy/confirmation.d.ts +12 -0
- package/dist/lib/deploy/confirmation.d.ts.map +1 -0
- package/dist/lib/deploy/confirmation.js +30 -0
- package/dist/lib/deploy/confirmation.js.map +1 -0
- package/dist/lib/deploy/context.d.ts +44 -0
- package/dist/lib/deploy/context.d.ts.map +1 -0
- package/dist/lib/deploy/context.js +39 -0
- package/dist/lib/deploy/context.js.map +1 -0
- package/dist/lib/deploy/deploy-output.d.ts +10 -0
- package/dist/lib/deploy/deploy-output.d.ts.map +1 -0
- package/dist/lib/deploy/deploy-output.js +79 -0
- package/dist/lib/deploy/deploy-output.js.map +1 -0
- package/dist/lib/deploy/deploy-phases.d.ts +41 -0
- package/dist/lib/deploy/deploy-phases.d.ts.map +1 -0
- package/dist/lib/deploy/deploy-phases.js +116 -0
- package/dist/lib/deploy/deploy-phases.js.map +1 -0
- package/dist/lib/deploy/deploy-utils.d.ts +53 -0
- package/dist/lib/deploy/deploy-utils.d.ts.map +1 -0
- package/dist/lib/deploy/deploy-utils.js +149 -0
- package/dist/lib/deploy/deploy-utils.js.map +1 -0
- package/dist/lib/deploy/execution.d.ts +24 -0
- package/dist/lib/deploy/execution.d.ts.map +1 -0
- package/dist/lib/deploy/execution.js +29 -0
- package/dist/lib/deploy/execution.js.map +1 -0
- package/dist/lib/deploy/output.d.ts +16 -0
- package/dist/lib/deploy/output.d.ts.map +1 -0
- package/dist/lib/deploy/output.js +115 -0
- package/dist/lib/deploy/output.js.map +1 -0
- package/dist/lib/deploy/pack-project.js +2 -2
- package/dist/lib/deploy/pack-project.js.map +1 -1
- package/dist/lib/deploy/preflight.d.ts +9 -0
- package/dist/lib/deploy/preflight.d.ts.map +1 -0
- package/dist/lib/deploy/preflight.js +59 -0
- package/dist/lib/deploy/preflight.js.map +1 -0
- package/dist/lib/deploy/validation.d.ts.map +1 -1
- package/dist/lib/deploy/validation.js +6 -8
- package/dist/lib/deploy/validation.js.map +1 -1
- package/dist/lib/utils/constants.d.ts +42 -0
- package/dist/lib/utils/constants.d.ts.map +1 -0
- package/dist/lib/utils/constants.js +42 -0
- package/dist/lib/utils/constants.js.map +1 -0
- package/dist/lib/utils/errors.d.ts +49 -0
- package/dist/lib/utils/errors.d.ts.map +1 -0
- package/dist/lib/utils/errors.js +70 -0
- package/dist/lib/utils/errors.js.map +1 -0
- package/dist/lib/utils/formatters.d.ts +34 -0
- package/dist/lib/utils/formatters.d.ts.map +1 -0
- package/dist/lib/utils/formatters.js +59 -0
- package/dist/lib/utils/formatters.js.map +1 -0
- package/dist/lib/utils/output.d.ts +46 -0
- package/dist/lib/utils/output.d.ts.map +1 -0
- package/dist/lib/utils/output.js +66 -0
- package/dist/lib/utils/output.js.map +1 -0
- package/dist/sdk/use-open-popup.d.ts +23 -0
- package/dist/sdk/use-open-popup.d.ts.map +1 -0
- package/dist/sdk/use-open-popup.js +29 -0
- package/dist/sdk/use-open-popup.js.map +1 -0
- package/package.json +3 -28
- package/runtime/index.html +0 -1
package/dist/commands/deploy.js
CHANGED
|
@@ -1,208 +1,35 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
import { initializeDeployment, runPreflightChecks, prepareDeploymentArtifacts, confirmDeployment, executeDeployment, } from '../lib/deploy/deploy-phases.js';
|
|
2
|
+
import { displayDeploymentSuccess, displayDeploymentError, } from '../lib/deploy/deploy-output.js';
|
|
3
|
+
import { DeploymentCancelledError } from '../lib/deploy/deploy-utils.js';
|
|
4
|
+
/**
|
|
5
|
+
* Deploy command - builds and uploads a LinkApp to the Linktree platform.
|
|
6
|
+
*
|
|
7
|
+
* This command orchestrates the entire deployment process:
|
|
8
|
+
* 1. Initialize deployment context (auth, config, LinkApp ID)
|
|
9
|
+
* 2. Run preflight checks (build, validation)
|
|
10
|
+
* 3. Prepare artifacts (manifests, packed files)
|
|
11
|
+
* 4. Confirm deployment with user
|
|
12
|
+
* 5. Execute deployment (upload to API)
|
|
13
|
+
* 6. Display results
|
|
14
|
+
*
|
|
15
|
+
* @param options - Deployment options
|
|
16
|
+
*/
|
|
15
17
|
export async function deployCommand(options) {
|
|
16
|
-
const env = options.qa ? 'QA' : 'Production';
|
|
17
|
-
const startTime = Date.now();
|
|
18
18
|
try {
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
const linkAppId = await getLinkAppId(configPath);
|
|
26
|
-
writeLine(pc.bold(pc.cyan(`\n→ Deploying ${linkAppId} to ${env}`)));
|
|
27
|
-
writeLine(pc.dim('─'.repeat(50)));
|
|
28
|
-
writeLine();
|
|
29
|
-
// Check authentication
|
|
30
|
-
const accessToken = getToken(config.auth.audience);
|
|
31
|
-
if (!accessToken) {
|
|
32
|
-
writeLine(pc.red('✗ Not authenticated'));
|
|
33
|
-
writeLine(pc.dim(` → Run: ${pc.cyan(`npx @linktr.ee/linkapp login${options.qa ? ' --qa' : ''}`)}`));
|
|
34
|
-
writeLine();
|
|
35
|
-
process.exit(1);
|
|
36
|
-
}
|
|
37
|
-
// Run build if needed
|
|
38
|
-
if (!options.skipBuild) {
|
|
39
|
-
const distPath = join(projectPath, 'dist');
|
|
40
|
-
if (!existsSync(distPath)) {
|
|
41
|
-
writeLine(pc.bold('Building project...'));
|
|
42
|
-
const buildStart = Date.now();
|
|
43
|
-
await buildCommand({ sourcemap: false });
|
|
44
|
-
const buildTime = ((Date.now() - buildStart) / 1000).toFixed(1);
|
|
45
|
-
writeLine(pc.dim(` Completed in ${buildTime}s`));
|
|
46
|
-
writeLine();
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
// Pre-deployment validation
|
|
50
|
-
if (!options.skipChecks) {
|
|
51
|
-
writeLine(pc.bold('Pre-deployment checks'));
|
|
52
|
-
const validationResult = await validateProject();
|
|
53
|
-
printValidationResults(validationResult);
|
|
54
|
-
if (!validationResult.success) {
|
|
55
|
-
writeLine();
|
|
56
|
-
writeLine(pc.red('✗ Validation failed'));
|
|
57
|
-
writeLine();
|
|
58
|
-
process.exit(1);
|
|
59
|
-
}
|
|
60
|
-
writeLine();
|
|
61
|
-
}
|
|
62
|
-
const s = p.spinner();
|
|
63
|
-
// Generate manifest files from config
|
|
64
|
-
s.start('Generating manifest files');
|
|
65
|
-
await generateManifestFiles(projectPath);
|
|
66
|
-
s.stop('Manifest files generated');
|
|
67
|
-
// Check if LinkApp exists
|
|
68
|
-
s.start('Checking LinkApp status');
|
|
69
|
-
const apiUrl = options.endpoint ?? `${config.link_types_url}/link-types`;
|
|
70
|
-
const exists = await checkLinkAppExists(apiUrl, linkAppId, accessToken);
|
|
71
|
-
const action = exists ? 'Updating existing LinkApp' : 'Creating new LinkApp';
|
|
72
|
-
s.stop(action);
|
|
73
|
-
// Pack project
|
|
74
|
-
s.start('Packing project files');
|
|
75
|
-
const packedFiles = await packProject({ projectPath });
|
|
76
|
-
s.stop(`Packed ${packedFiles.length} ${packedFiles.length === 1 ? 'item' : 'items'}`);
|
|
77
|
-
// Show files to upload
|
|
78
|
-
writeLine();
|
|
79
|
-
writeLine(pc.bold('Files to upload:'));
|
|
80
|
-
const filesToShow = packedFiles.slice(0, 8);
|
|
81
|
-
for (const file of filesToShow) {
|
|
82
|
-
writeLine(pc.dim(` • ${file}`));
|
|
83
|
-
}
|
|
84
|
-
if (packedFiles.length > 8) {
|
|
85
|
-
writeLine(pc.dim(` • ... and ${packedFiles.length - 8} more files`));
|
|
86
|
-
}
|
|
87
|
-
// Confirm deployment
|
|
88
|
-
if (!options.skipConfirm) {
|
|
89
|
-
writeLine();
|
|
90
|
-
const shouldDeploy = await p.confirm({
|
|
91
|
-
message: `Deploy to ${env}?`,
|
|
92
|
-
initialValue: true,
|
|
93
|
-
});
|
|
94
|
-
if (p.isCancel(shouldDeploy) || !shouldDeploy) {
|
|
95
|
-
writeLine();
|
|
96
|
-
writeLine(pc.yellow('⚠ Deployment cancelled'));
|
|
97
|
-
writeLine();
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
// Upload
|
|
102
|
-
writeLine();
|
|
103
|
-
const uploadStart = Date.now();
|
|
104
|
-
s.start('Uploading assets');
|
|
105
|
-
const result = await uploadAssets({
|
|
106
|
-
projectPath,
|
|
107
|
-
apiUrl,
|
|
108
|
-
linkAppId,
|
|
109
|
-
accessToken,
|
|
110
|
-
isUpdate: exists,
|
|
111
|
-
forceUpdate: options.force,
|
|
112
|
-
});
|
|
113
|
-
const uploadTime = ((Date.now() - uploadStart) / 1000).toFixed(1);
|
|
114
|
-
s.stop(`Assets uploaded (${uploadTime}s)`);
|
|
115
|
-
// Show success
|
|
116
|
-
const totalTime = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
117
|
-
writeLine();
|
|
118
|
-
writeLine(pc.dim('─'.repeat(50)));
|
|
119
|
-
writeLine(pc.green(pc.bold('\n✓ Deployment successful!')));
|
|
120
|
-
writeLine(pc.dim(` Completed in ${totalTime}s`));
|
|
121
|
-
writeLine();
|
|
122
|
-
writeLine(pc.bold('Details:'));
|
|
123
|
-
writeLine(pc.dim(` ID: ${result.linkType.linkTypeId}`));
|
|
124
|
-
writeLine(pc.dim(` Status: ${result.linkType.status}`));
|
|
125
|
-
// Fix timestamp handling - check if it exists and handle both seconds and milliseconds
|
|
126
|
-
if (result.linkType.updatedTimestamp) {
|
|
127
|
-
const timestamp = result.linkType.updatedTimestamp;
|
|
128
|
-
// If timestamp is in seconds (less than year 2100 in seconds), convert to ms
|
|
129
|
-
const timestampMs = timestamp < 10000000000 ? timestamp * 1000 : timestamp;
|
|
130
|
-
const date = new Date(timestampMs);
|
|
131
|
-
writeLine(pc.dim(` Updated: ${date.toLocaleString()}`));
|
|
132
|
-
}
|
|
133
|
-
writeLine();
|
|
134
|
-
writeLine(pc.bold('Next steps:'));
|
|
135
|
-
// Step 1: Build is happening
|
|
136
|
-
if (result.build?.id) {
|
|
137
|
-
const buildUrl = `https://linkapp-ci.replit.app/?linkTypeId=${result.linkType.linkTypeId}&buildId=${result.build.id}`;
|
|
138
|
-
writeLine(pc.cyan(` 1. Building your LinkApp (this may take a few minutes):`));
|
|
139
|
-
writeLine(pc.dim(` ${buildUrl}`));
|
|
140
|
-
writeLine();
|
|
141
|
-
}
|
|
142
|
-
// Step 2: Add to page (only after build completes)
|
|
143
|
-
const createUrl = options.qa
|
|
144
|
-
? `https://qa.linktr.ee/admin?action=create-link&linkType=${result.linkType.linkTypeId}`
|
|
145
|
-
: `https://linktr.ee/admin?action=create-link&linkType=${result.linkType.linkTypeId}`;
|
|
146
|
-
writeLine(pc.cyan(` 2. Once the build completes, add it to your page:`));
|
|
147
|
-
writeLine(pc.dim(` ${createUrl}`));
|
|
148
|
-
writeLine();
|
|
19
|
+
const context = await initializeDeployment(options);
|
|
20
|
+
await runPreflightChecks(context);
|
|
21
|
+
const artifacts = await prepareDeploymentArtifacts(context);
|
|
22
|
+
await confirmDeployment(context, artifacts);
|
|
23
|
+
const result = await executeDeployment(context, artifacts);
|
|
24
|
+
displayDeploymentSuccess(context, result);
|
|
149
25
|
}
|
|
150
26
|
catch (error) {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
writeLine();
|
|
155
|
-
// Check if this is our structured error with API details
|
|
156
|
-
if (error && typeof error === 'object' && 'statusCode' in error && 'messages' in error) {
|
|
157
|
-
const structuredError = error;
|
|
158
|
-
writeLine(pc.bold('Error Details:'));
|
|
159
|
-
writeLine(pc.red(` Status: ${structuredError.statusCode}`));
|
|
160
|
-
if (Array.isArray(structuredError.messages) && structuredError.messages.length > 0) {
|
|
161
|
-
writeLine();
|
|
162
|
-
writeLine(pc.bold(' Validation Errors:'));
|
|
163
|
-
for (const msg of structuredError.messages) {
|
|
164
|
-
writeLine(pc.red(` • ${msg}`));
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
if (structuredError.responseData?.error) {
|
|
168
|
-
writeLine();
|
|
169
|
-
writeLine(pc.bold(' Error Type:'));
|
|
170
|
-
writeLine(pc.red(` ${structuredError.responseData.error}`));
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
else if (error instanceof Error) {
|
|
174
|
-
writeLine(pc.red(` ${error.message}`));
|
|
27
|
+
if (error instanceof DeploymentCancelledError) {
|
|
28
|
+
displayDeploymentError(error);
|
|
29
|
+
return;
|
|
175
30
|
}
|
|
176
|
-
|
|
177
|
-
writeLine(pc.red(` ${String(error)}`));
|
|
178
|
-
}
|
|
179
|
-
writeLine(); // Empty line
|
|
31
|
+
displayDeploymentError(error);
|
|
180
32
|
process.exit(1);
|
|
181
33
|
}
|
|
182
34
|
}
|
|
183
|
-
async function getLinkAppId(configPath) {
|
|
184
|
-
// Load the TypeScript config and derive ID from manifest name (kebab-case)
|
|
185
|
-
const { createJiti } = await import('jiti');
|
|
186
|
-
const jiti = createJiti(import.meta.url, {
|
|
187
|
-
interopDefault: true,
|
|
188
|
-
moduleCache: false,
|
|
189
|
-
});
|
|
190
|
-
try {
|
|
191
|
-
const config = jiti(configPath);
|
|
192
|
-
if (!config?.manifest?.name) {
|
|
193
|
-
throw new Error('manifest.name is required in configuration');
|
|
194
|
-
}
|
|
195
|
-
// Derive ID from name using kebab-case (matches backend behavior)
|
|
196
|
-
return config.manifest.name
|
|
197
|
-
.toLowerCase()
|
|
198
|
-
.replace(/[^a-z0-9]+/g, '-')
|
|
199
|
-
.replace(/^-+|-+$/g, ''); // Remove leading/trailing hyphens
|
|
200
|
-
}
|
|
201
|
-
catch (error) {
|
|
202
|
-
if (error instanceof Error && error.message.includes('manifest.name is required')) {
|
|
203
|
-
throw error;
|
|
204
|
-
}
|
|
205
|
-
throw new Error(`Failed to load manifest.name from config: ${error instanceof Error ? error.message : error}`);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
35
|
//# sourceMappingURL=deploy.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deploy.js","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"deploy.js","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,kBAAkB,EAClB,0BAA0B,EAC1B,iBAAiB,EACjB,iBAAiB,GAElB,MAAM,gCAAgC,CAAA;AACvC,OAAO,EACL,wBAAwB,EACxB,sBAAsB,GACvB,MAAM,gCAAgC,CAAA;AACvC,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAA;AAIxE;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAAsB;IACxD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,oBAAoB,CAAC,OAAO,CAAC,CAAA;QACnD,MAAM,kBAAkB,CAAC,OAAO,CAAC,CAAA;QACjC,MAAM,SAAS,GAAG,MAAM,0BAA0B,CAAC,OAAO,CAAC,CAAA;QAC3D,MAAM,iBAAiB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAA;QAC3C,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAA;QAC1D,wBAAwB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;IAC3C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,wBAAwB,EAAE,CAAC;YAC9C,sBAAsB,CAAC,KAAK,CAAC,CAAA;YAC7B,OAAM;QACR,CAAC;QACD,sBAAsB,CAAC,KAAK,CAAC,CAAA;QAC7B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;AACH,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAmBA,UAAU,UAAU;IAClB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAmSD,wBAAsB,UAAU,CAAC,OAAO,EAAE,UAAU,iBAsUnD"}
|
package/dist/commands/dev.js
CHANGED
|
@@ -14,107 +14,35 @@ const writeLine = (message = '') => {
|
|
|
14
14
|
const __filename = fileURLToPath(import.meta.url);
|
|
15
15
|
const __dirname = dirname(__filename);
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
17
|
+
* Watches linkapp.config.ts for changes and triggers server restart.
|
|
18
|
+
* Based on Rsbuild's watchFilesForRestart implementation.
|
|
19
19
|
*/
|
|
20
|
-
function createConfigWatcherPlugin(userProjectPath) {
|
|
21
|
-
const configCandidates = [
|
|
22
|
-
join(userProjectPath, 'linkapp.config.ts'),
|
|
23
|
-
join(userProjectPath, '.config', 'linkapp.config.ts'),
|
|
24
|
-
];
|
|
25
|
-
const handleConfigReload = (server) => {
|
|
26
|
-
writeLine(pc.cyan(' ○ linkapp.config.ts changed, reloading...'));
|
|
27
|
-
try {
|
|
28
|
-
loadConfig(userProjectPath);
|
|
29
|
-
server.sockWrite('static-changed');
|
|
30
|
-
writeLine(pc.green(' ✓ Config reloaded'));
|
|
31
|
-
}
|
|
32
|
-
catch (error) {
|
|
33
|
-
writeLine(pc.red(' ✗ Failed to reload config'));
|
|
34
|
-
writeLine(pc.dim(` ${error instanceof Error ? error.message : error}`));
|
|
35
|
-
}
|
|
36
|
-
};
|
|
20
|
+
function createConfigWatcherPlugin(userProjectPath, onConfigChange) {
|
|
37
21
|
return {
|
|
38
|
-
name: 'linkapp
|
|
22
|
+
name: 'linkapp:config-watcher',
|
|
39
23
|
setup(api) {
|
|
40
|
-
let
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
catch (error) {
|
|
48
|
-
writeLine(pc.red(' ✗ Failed to close config watcher'));
|
|
49
|
-
writeLine(pc.dim(` ${error instanceof Error ? error.message : error}`));
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
watchers = [];
|
|
53
|
-
for (const timeout of pendingTimers.values()) {
|
|
54
|
-
clearTimeout(timeout);
|
|
55
|
-
}
|
|
56
|
-
pendingTimers.clear();
|
|
57
|
-
};
|
|
58
|
-
api.onBeforeStartDevServer(async ({ server }) => {
|
|
59
|
-
await disposeWatchers();
|
|
60
|
-
const filesToWatch = configCandidates.filter((candidatePath) => existsSync(candidatePath));
|
|
61
|
-
if (filesToWatch.length === 0) {
|
|
24
|
+
let watcher;
|
|
25
|
+
api.onBeforeStartDevServer(async () => {
|
|
26
|
+
const configPaths = [
|
|
27
|
+
resolve(userProjectPath, 'linkapp.config.ts'),
|
|
28
|
+
resolve(userProjectPath, '.config', 'linkapp.config.ts'),
|
|
29
|
+
].filter((path) => existsSync(path));
|
|
30
|
+
if (configPaths.length === 0)
|
|
62
31
|
return;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const timeout = setTimeout(() => {
|
|
71
|
-
pendingTimers.delete(normalizedPath);
|
|
72
|
-
handleConfigReload(server);
|
|
73
|
-
}, 120);
|
|
74
|
-
pendingTimers.set(normalizedPath, timeout);
|
|
75
|
-
};
|
|
76
|
-
try {
|
|
77
|
-
const watcher = chokidar.watch(filesToWatch, {
|
|
78
|
-
ignoreInitial: true,
|
|
79
|
-
awaitWriteFinish: {
|
|
80
|
-
stabilityThreshold: 200,
|
|
81
|
-
pollInterval: 50,
|
|
82
|
-
},
|
|
83
|
-
});
|
|
84
|
-
watcher.on('all', (event, changedPath) => {
|
|
85
|
-
if (!changedPath) {
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
const normalizedChangedPath = changedPath.toString();
|
|
89
|
-
if (!configCandidates.includes(normalizedChangedPath)) {
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
switch (event) {
|
|
93
|
-
case 'add':
|
|
94
|
-
case 'change':
|
|
95
|
-
case 'unlink':
|
|
96
|
-
scheduleReload(normalizedChangedPath);
|
|
97
|
-
break;
|
|
98
|
-
default:
|
|
99
|
-
break;
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
watcher.on('error', (error) => {
|
|
103
|
-
writeLine(pc.red(' ✗ Config watcher encountered an error'));
|
|
104
|
-
writeLine(pc.dim(` ${error instanceof Error ? error.message : error}`));
|
|
105
|
-
});
|
|
106
|
-
watchers.push(watcher);
|
|
107
|
-
}
|
|
108
|
-
catch (error) {
|
|
109
|
-
writeLine(pc.red(' ✗ Failed to watch config file'));
|
|
110
|
-
writeLine(pc.dim(` ${error instanceof Error ? error.message : error}`));
|
|
111
|
-
}
|
|
112
|
-
return async () => {
|
|
113
|
-
await disposeWatchers();
|
|
32
|
+
watcher = chokidar.watch(configPaths, {
|
|
33
|
+
ignoreInitial: true,
|
|
34
|
+
ignorePermissionErrors: true,
|
|
35
|
+
});
|
|
36
|
+
const onChange = async (filePath) => {
|
|
37
|
+
writeLine(pc.cyan(`\n ○ ${relative(userProjectPath, filePath)} changed, restarting dev server...\n`));
|
|
38
|
+
await onConfigChange();
|
|
114
39
|
};
|
|
40
|
+
watcher.on('change', onChange);
|
|
41
|
+
watcher.on('add', onChange);
|
|
42
|
+
watcher.on('unlink', onChange);
|
|
115
43
|
});
|
|
116
44
|
api.onCloseDevServer(async () => {
|
|
117
|
-
await
|
|
45
|
+
await watcher?.close();
|
|
118
46
|
});
|
|
119
47
|
},
|
|
120
48
|
};
|
|
@@ -378,18 +306,26 @@ export async function devCommand(options) {
|
|
|
378
306
|
const featuredEntryPath = join(linkappDir, 'dev-featured-main.tsx');
|
|
379
307
|
writeFileSync(sheetEntryPath, generateSheetEntryPoint(devServerPath), 'utf-8');
|
|
380
308
|
writeFileSync(featuredEntryPath, generateFeaturedEntryPoint(devServerPath, hasFeaturedCarousel), 'utf-8');
|
|
381
|
-
//
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
309
|
+
// Helper to load config (called on initial start and on config changes)
|
|
310
|
+
const loadProjectConfig = () => {
|
|
311
|
+
try {
|
|
312
|
+
const config = loadConfig(userProjectPath);
|
|
313
|
+
return {
|
|
314
|
+
previewProps: config.preview_props || {},
|
|
315
|
+
settingsConfig: config.settings || {},
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
catch (error) {
|
|
319
|
+
writeLine(pc.yellow('⚠ Warning: Could not load config, using default preview props'));
|
|
320
|
+
writeLine(pc.dim(` ${error instanceof Error ? error.message : error}`));
|
|
321
|
+
return {
|
|
322
|
+
previewProps: {},
|
|
323
|
+
settingsConfig: {},
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
// Load config initially
|
|
328
|
+
let { previewProps, settingsConfig } = loadProjectConfig();
|
|
393
329
|
const requestedPort = options.port || 3000;
|
|
394
330
|
const availablePort = await detect(requestedPort);
|
|
395
331
|
// Show warning if the requested port was occupied
|
|
@@ -451,77 +387,85 @@ export async function devCommand(options) {
|
|
|
451
387
|
process.stdin.pause();
|
|
452
388
|
};
|
|
453
389
|
};
|
|
454
|
-
//
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
createConfigWatcherPlugin(userProjectPath),
|
|
463
|
-
createPublicDirPlugin(userPublicDir),
|
|
464
|
-
],
|
|
465
|
-
source: {
|
|
466
|
-
entry: {
|
|
467
|
-
index: resolve(devServerPath, 'preview/main.tsx'),
|
|
468
|
-
sheet: sheetEntryPath,
|
|
469
|
-
featured: featuredEntryPath,
|
|
390
|
+
// Callback wrapper for config watcher (will be assigned after restartServer is defined)
|
|
391
|
+
let restartCallback;
|
|
392
|
+
// Helper to create Rsbuild instance with current config values
|
|
393
|
+
const createRsbuildInstance = async () => {
|
|
394
|
+
return await createRsbuild({
|
|
395
|
+
rsbuildConfig: {
|
|
396
|
+
dev: {
|
|
397
|
+
progressBar: false,
|
|
470
398
|
},
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
399
|
+
plugins: [
|
|
400
|
+
pluginReact(),
|
|
401
|
+
createPublicDirPlugin(userPublicDir),
|
|
402
|
+
createConfigWatcherPlugin(userProjectPath, async () => {
|
|
403
|
+
await restartCallback?.();
|
|
404
|
+
}),
|
|
405
|
+
],
|
|
406
|
+
source: {
|
|
407
|
+
entry: {
|
|
408
|
+
index: resolve(devServerPath, 'preview/main.tsx'),
|
|
409
|
+
sheet: sheetEntryPath,
|
|
410
|
+
featured: featuredEntryPath,
|
|
411
|
+
},
|
|
412
|
+
define: {
|
|
413
|
+
__PREVIEW_PROPS__: JSON.stringify(previewProps),
|
|
414
|
+
__SETTINGS_CONFIG__: JSON.stringify(settingsConfig),
|
|
415
|
+
},
|
|
479
416
|
},
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
index: resolve(devServerPath, 'index.html'),
|
|
486
|
-
sheet: resolve(devServerPath, 'sheet.html'),
|
|
487
|
-
featured: resolve(devServerPath, 'featured.html'),
|
|
488
|
-
};
|
|
489
|
-
return templates[entryName] || resolve(devServerPath, 'index.html');
|
|
417
|
+
resolve: {
|
|
418
|
+
alias: {
|
|
419
|
+
'@': userProjectPath,
|
|
420
|
+
},
|
|
421
|
+
dedupe: ['react', 'react-dom'],
|
|
490
422
|
},
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
watch: true,
|
|
423
|
+
html: {
|
|
424
|
+
template({ entryName }) {
|
|
425
|
+
const templates = {
|
|
426
|
+
index: resolve(devServerPath, 'index.html'),
|
|
427
|
+
sheet: resolve(devServerPath, 'sheet.html'),
|
|
428
|
+
featured: resolve(devServerPath, 'featured.html'),
|
|
429
|
+
};
|
|
430
|
+
return templates[entryName] || resolve(devServerPath, 'index.html');
|
|
431
|
+
},
|
|
501
432
|
},
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
433
|
+
server: {
|
|
434
|
+
port,
|
|
435
|
+
host: 'localhost',
|
|
436
|
+
open: false,
|
|
437
|
+
strictPort: false,
|
|
438
|
+
printUrls: false,
|
|
439
|
+
publicDir: {
|
|
440
|
+
name: userPublicDir,
|
|
441
|
+
watch: true,
|
|
442
|
+
},
|
|
511
443
|
},
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
444
|
+
tools: {
|
|
445
|
+
postcss: (opts) => {
|
|
446
|
+
// Load PostCSS config from dev-server directory
|
|
447
|
+
opts.postcssOptions = {
|
|
448
|
+
...opts.postcssOptions,
|
|
449
|
+
config: resolve(devServerPath, 'postcss.config.mjs'),
|
|
450
|
+
};
|
|
451
|
+
return opts;
|
|
452
|
+
},
|
|
453
|
+
rspack: (config) => {
|
|
454
|
+
// Add linkapp package's node_modules to module resolution paths
|
|
455
|
+
// This allows dev-server files to import dependencies from @linktr.ee/linkapp
|
|
456
|
+
config.resolve = config.resolve || {};
|
|
457
|
+
config.resolve.modules = [
|
|
458
|
+
resolve(__dirname, '../../node_modules'),
|
|
459
|
+
'node_modules',
|
|
460
|
+
];
|
|
461
|
+
return config;
|
|
462
|
+
},
|
|
521
463
|
},
|
|
522
464
|
},
|
|
523
|
-
}
|
|
524
|
-
}
|
|
465
|
+
});
|
|
466
|
+
};
|
|
467
|
+
// Create initial Rsbuild instance
|
|
468
|
+
let rsbuild = await createRsbuildInstance();
|
|
525
469
|
let currentServer;
|
|
526
470
|
let cleanupShortcuts;
|
|
527
471
|
let isRestarting = false;
|
|
@@ -540,10 +484,17 @@ export async function devCommand(options) {
|
|
|
540
484
|
return;
|
|
541
485
|
}
|
|
542
486
|
isRestarting = true;
|
|
543
|
-
writeLine();
|
|
544
487
|
writeLine(pc.cyan(' ↻ Restarting dev server...'));
|
|
545
488
|
try {
|
|
489
|
+
// Close current server
|
|
546
490
|
await currentServer.server.close();
|
|
491
|
+
// Reload config to get fresh preview props
|
|
492
|
+
const freshConfig = loadProjectConfig();
|
|
493
|
+
previewProps = freshConfig.previewProps;
|
|
494
|
+
settingsConfig = freshConfig.settingsConfig;
|
|
495
|
+
// Recreate Rsbuild instance with fresh config
|
|
496
|
+
rsbuild = await createRsbuildInstance();
|
|
497
|
+
// Start new server
|
|
547
498
|
const restartStart = Date.now();
|
|
548
499
|
await startServer(restartStart);
|
|
549
500
|
}
|
|
@@ -588,6 +539,8 @@ export async function devCommand(options) {
|
|
|
588
539
|
process.exit(0);
|
|
589
540
|
}
|
|
590
541
|
};
|
|
542
|
+
// Assign restart callback for config watcher
|
|
543
|
+
restartCallback = restartServer;
|
|
591
544
|
await startServer(startTime);
|
|
592
545
|
cleanupShortcuts = setupKeyboardShortcuts({
|
|
593
546
|
restart: restartServer,
|