@pulse-editor/cli 0.1.1-beta.8 → 0.1.2
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/dist/app.js +3 -1
- package/dist/components/commands/build.js +18 -33
- package/dist/components/commands/create.js +38 -12
- package/dist/components/commands/dev.js +35 -8
- package/dist/components/commands/login.d.ts +2 -2
- package/dist/components/commands/login.js +110 -26
- package/dist/components/commands/preview.js +50 -11
- package/dist/components/commands/publish.js +23 -37
- package/dist/components/commands/{start copy.d.ts → skill.d.ts} +1 -1
- package/dist/components/commands/skill.js +230 -0
- package/dist/components/commands/{preview copy.d.ts → upgrade.d.ts} +1 -1
- package/dist/components/commands/upgrade.js +53 -0
- package/dist/lib/backend/publish-app.d.ts +1 -0
- package/dist/lib/backend/publish-app.js +26 -0
- package/dist/lib/backend-url.d.ts +1 -0
- package/dist/lib/backend-url.js +3 -0
- package/dist/lib/cli-flags.d.ts +18 -0
- package/dist/lib/cli-flags.js +18 -0
- package/dist/lib/manual.js +28 -0
- package/dist/lib/server/express.js +87 -47
- package/dist/lib/server/preview/backend/load-remote.cjs +33 -23
- package/dist/lib/server/preview/frontend/index.html +11 -11
- package/dist/lib/server/utils.js +3 -3
- package/dist/lib/token.js +2 -3
- package/dist/lib/webpack/compile.d.ts +2 -0
- package/dist/lib/webpack/compile.js +30 -0
- package/dist/lib/webpack/configs/mf-client.d.ts +3 -0
- package/dist/lib/webpack/configs/mf-client.js +184 -0
- package/dist/lib/webpack/configs/mf-server.d.ts +2 -0
- package/dist/lib/webpack/configs/mf-server.js +463 -0
- package/dist/lib/webpack/configs/preview.d.ts +3 -0
- package/dist/lib/webpack/configs/preview.js +117 -0
- package/dist/lib/webpack/configs/utils.d.ts +10 -0
- package/dist/lib/webpack/configs/utils.js +172 -0
- package/dist/lib/webpack/dist/pregistered-actions.d.ts +2 -0
- package/dist/lib/webpack/dist/pulse.config.d.ts +7 -0
- package/dist/lib/webpack/dist/src/lib/agents/code-modifier-agent.d.ts +2 -0
- package/dist/lib/webpack/dist/src/lib/agents/vibe-coding-agent.d.ts +3 -0
- package/dist/lib/webpack/dist/src/lib/mcp/utils.d.ts +3 -0
- package/dist/lib/webpack/dist/src/lib/streaming/message-stream-controller.d.ts +10 -0
- package/dist/lib/webpack/dist/src/lib/types.d.ts +58 -0
- package/dist/lib/webpack/dist/src/server-function/generate-code/v1/generate.d.ts +5 -0
- package/dist/lib/webpack/dist/src/server-function/generate-code/v2/generate.d.ts +1 -0
- package/dist/lib/webpack/tsconfig.server.json +19 -0
- package/dist/lib/webpack/webpack-config.d.ts +1 -0
- package/dist/lib/webpack/webpack-config.js +24 -0
- package/dist/lib/webpack/webpack.config.d.ts +2 -0
- package/dist/lib/webpack/webpack.config.js +527 -0
- package/package.json +29 -20
- package/readme.md +7 -1
- package/dist/components/commands/preview copy.js +0 -14
- package/dist/components/commands/start copy.js +0 -14
- package/dist/lib/deps.d.ts +0 -1
- package/dist/lib/deps.js +0 -5
- package/dist/lib/node_module_bin.d.ts +0 -1
- package/dist/lib/node_module_bin.js +0 -7
- package/dist/lib/server/preview/backend/index.d.ts +0 -1
- package/dist/lib/server/preview/backend/index.js +0 -23
|
@@ -1,32 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* This is a local dev server for "npm run dev" and "npm run preview".
|
|
3
3
|
*/
|
|
4
|
-
import
|
|
5
|
-
import cors from
|
|
6
|
-
import dotenv from
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import { networkInterfaces } from
|
|
10
|
-
import
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import
|
|
14
|
-
import {
|
|
4
|
+
import connectLivereload from "connect-livereload";
|
|
5
|
+
import cors from "cors";
|
|
6
|
+
import dotenv from "dotenv";
|
|
7
|
+
import express from "express";
|
|
8
|
+
import livereload from "livereload";
|
|
9
|
+
import { networkInterfaces } from "os";
|
|
10
|
+
import path from "path";
|
|
11
|
+
import { pipeline, Readable } from "stream";
|
|
12
|
+
import { pathToFileURL } from "url";
|
|
13
|
+
import { promisify } from "util";
|
|
14
|
+
import { readConfigFile } from "../webpack/configs/utils.js";
|
|
15
15
|
dotenv.config({
|
|
16
16
|
quiet: true,
|
|
17
17
|
});
|
|
18
|
-
const isPreview = process.env?.[
|
|
19
|
-
const isDev = process.env?.[
|
|
20
|
-
const workspaceId = process.env?.[
|
|
18
|
+
const isPreview = process.env?.["PREVIEW"];
|
|
19
|
+
const isDev = process.env?.["NODE_ENV"] === "development";
|
|
20
|
+
const workspaceId = process.env?.["WORKSPACE_ID"];
|
|
21
21
|
const pulseConfig = await readConfigFile();
|
|
22
22
|
if (isDev || isPreview) {
|
|
23
23
|
const livereloadServer = livereload.createServer({
|
|
24
24
|
// @ts-expect-error override server options
|
|
25
|
-
host:
|
|
25
|
+
host: "0.0.0.0",
|
|
26
26
|
});
|
|
27
|
-
livereloadServer.watch(
|
|
28
|
-
livereloadServer.server.once(
|
|
29
|
-
console.log(
|
|
27
|
+
livereloadServer.watch("dist");
|
|
28
|
+
livereloadServer.server.once("connection", () => {
|
|
29
|
+
console.log("✅ LiveReload connected");
|
|
30
30
|
});
|
|
31
31
|
}
|
|
32
32
|
const app = express();
|
|
@@ -49,57 +49,97 @@ app.use((req, res, next) => {
|
|
|
49
49
|
return next();
|
|
50
50
|
});
|
|
51
51
|
// Serve backend
|
|
52
|
-
app.use(`/${pulseConfig.id}/${pulseConfig.version}/server`, express.static(
|
|
52
|
+
app.use(`/${pulseConfig.id}/${pulseConfig.version}/server`, express.static("dist/server"));
|
|
53
53
|
// Catch backend function calls
|
|
54
54
|
app.all(/^\/server-function\/(.*)/, async (req, res) => {
|
|
55
55
|
const func = req.params[0];
|
|
56
|
-
const url = `${req.protocol}://${req.get(
|
|
56
|
+
const url = `${req.protocol}://${req.get("host")}${req.originalUrl}`;
|
|
57
57
|
// Convert Express req -> Fetch Request
|
|
58
58
|
const request = new Request(url, {
|
|
59
59
|
method: req.method,
|
|
60
60
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
61
61
|
headers: req.headers,
|
|
62
|
-
body: [
|
|
62
|
+
body: ["GET", "HEAD"].includes(req.method)
|
|
63
63
|
? null
|
|
64
64
|
: JSON.stringify(req.body),
|
|
65
65
|
});
|
|
66
|
-
const dir = path.resolve(
|
|
66
|
+
const dir = path.resolve("node_modules/@pulse-editor/cli/dist/lib/server/preview/backend/load-remote.cjs");
|
|
67
67
|
const fileUrl = pathToFileURL(dir).href;
|
|
68
|
-
const {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (response.body) {
|
|
74
|
-
// Convert WHATWG stream to Node.js stream
|
|
75
|
-
const nodeStream = Readable.fromWeb(response.body);
|
|
76
|
-
// Pipe it directly to Express
|
|
77
|
-
await streamPipeline(nodeStream, res);
|
|
68
|
+
const { loadFunc, loadPrice } = await import(fileUrl);
|
|
69
|
+
const price = await loadPrice(func, pulseConfig.id, "http://localhost:3030", pulseConfig.version);
|
|
70
|
+
if (price) {
|
|
71
|
+
// Make func name and price bold in console
|
|
72
|
+
console.log(`🏃 Running function \x1b[1m${func}\x1b[0m, credits consumed: \x1b[1m${price}\x1b[0m`);
|
|
78
73
|
}
|
|
79
74
|
else {
|
|
80
|
-
|
|
75
|
+
console.log(`🏃 Running function \x1b[1m${func}\x1b[0m.`);
|
|
76
|
+
}
|
|
77
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
78
|
+
const loadedFunc = await loadFunc(func, pulseConfig.id, "http://localhost:3030", pulseConfig.version);
|
|
79
|
+
const response = await loadedFunc(request);
|
|
80
|
+
const streamPipeline = promisify(pipeline);
|
|
81
|
+
if (response) {
|
|
82
|
+
// 1️⃣ Set status code
|
|
83
|
+
res.status(response.status);
|
|
84
|
+
// 2️⃣ Copy headers
|
|
85
|
+
response.headers.forEach((value, key) => {
|
|
86
|
+
res.setHeader(key, value);
|
|
87
|
+
});
|
|
88
|
+
// 3️⃣ Pipe body if present
|
|
89
|
+
if (response.body) {
|
|
90
|
+
const nodeStream = Readable.fromWeb(response.body);
|
|
91
|
+
await streamPipeline(nodeStream, res);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
res.end();
|
|
95
|
+
}
|
|
81
96
|
}
|
|
82
97
|
});
|
|
83
98
|
if (isPreview) {
|
|
84
99
|
/* Preview mode */
|
|
85
|
-
app.use(express.static(
|
|
86
|
-
|
|
100
|
+
app.use(express.static("dist/client"));
|
|
101
|
+
// Expose skill actions as REST API endpoints in dev and preview modes
|
|
102
|
+
const skillActions = pulseConfig?.actions || [];
|
|
103
|
+
const skillActionNames = skillActions.map((a) => a.name);
|
|
104
|
+
app.post("/skill/:actionName", async (req, res) => {
|
|
105
|
+
const { actionName } = req.params;
|
|
106
|
+
if (skillActionNames.length > 0 && !skillActionNames.includes(actionName)) {
|
|
107
|
+
res
|
|
108
|
+
.status(404)
|
|
109
|
+
.json({ error: `Skill action "${actionName}" not found.` });
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const dir = path.resolve("node_modules/@pulse-editor/cli/dist/lib/server/preview/backend/load-remote.cjs");
|
|
113
|
+
const fileUrl = pathToFileURL(dir).href;
|
|
114
|
+
const { loadFunc } = await import(fileUrl);
|
|
115
|
+
try {
|
|
116
|
+
const action = await loadFunc(`skill/${actionName}`, pulseConfig.id, "http://localhost:3030", pulseConfig.version);
|
|
117
|
+
const result = await action(req.body);
|
|
118
|
+
res.json(result);
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
122
|
+
console.error(`❌ Error running skill action "${actionName}": ${message}`);
|
|
123
|
+
res.status(500).json({ error: message });
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
app.listen(3030, "0.0.0.0");
|
|
87
127
|
}
|
|
88
128
|
else if (isDev) {
|
|
89
129
|
/* Dev mode */
|
|
90
|
-
app.use(`/${pulseConfig.id}/${pulseConfig.version}`, express.static(
|
|
91
|
-
app.listen(3030,
|
|
130
|
+
app.use(`/${pulseConfig.id}/${pulseConfig.version}`, express.static("dist"));
|
|
131
|
+
app.listen(3030, "0.0.0.0");
|
|
92
132
|
}
|
|
93
133
|
else {
|
|
94
134
|
/* Production mode */
|
|
95
|
-
app.use(`/${pulseConfig.id}/${pulseConfig.version}`, express.static(
|
|
96
|
-
app.listen(3030,
|
|
97
|
-
console.log(`\
|
|
98
|
-
🎉 Your Pulse extension \x1b[1m${pulseConfig.displayName}\x1b[0m is LIVE!
|
|
99
|
-
|
|
100
|
-
⚡️ Local: http://localhost:3030/${pulseConfig.id}/${pulseConfig.version}/
|
|
101
|
-
⚡️ Network: http://${getLocalNetworkIP()}:3030/${pulseConfig.id}/${pulseConfig.version}/
|
|
102
|
-
|
|
135
|
+
app.use(`/${pulseConfig.id}/${pulseConfig.version}`, express.static("dist"));
|
|
136
|
+
app.listen(3030, "0.0.0.0", () => {
|
|
137
|
+
console.log(`\
|
|
138
|
+
🎉 Your Pulse extension \x1b[1m${pulseConfig.displayName}\x1b[0m is LIVE!
|
|
139
|
+
|
|
140
|
+
⚡️ Local: http://localhost:3030/${pulseConfig.id}/${pulseConfig.version}/
|
|
141
|
+
⚡️ Network: http://${getLocalNetworkIP()}:3030/${pulseConfig.id}/${pulseConfig.version}/
|
|
142
|
+
|
|
103
143
|
✨ Try it out in the Pulse Editor and let the magic happen! 🚀`);
|
|
104
144
|
});
|
|
105
145
|
}
|
|
@@ -109,10 +149,10 @@ function getLocalNetworkIP() {
|
|
|
109
149
|
if (!iface)
|
|
110
150
|
continue;
|
|
111
151
|
for (const config of iface) {
|
|
112
|
-
if (config.family ===
|
|
152
|
+
if (config.family === "IPv4" && !config.internal) {
|
|
113
153
|
return config.address; // Returns the first non-internal IPv4 address
|
|
114
154
|
}
|
|
115
155
|
}
|
|
116
156
|
}
|
|
117
|
-
return
|
|
157
|
+
return "localhost"; // Fallback
|
|
118
158
|
}
|
|
@@ -1,23 +1,33 @@
|
|
|
1
|
-
const {
|
|
2
|
-
|
|
3
|
-
async function
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
1
|
+
const {createInstance} = require('@module-federation/runtime');
|
|
2
|
+
|
|
3
|
+
async function importRemoteModule(func, appId, origin, version) {
|
|
4
|
+
const instance = createInstance({
|
|
5
|
+
name: 'server_function_runner',
|
|
6
|
+
remotes: [
|
|
7
|
+
{
|
|
8
|
+
name: appId + '_server',
|
|
9
|
+
entry: `${origin}/${appId}/${version}/server/remoteEntry.js`,
|
|
10
|
+
},
|
|
11
|
+
],
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const loadedModule = await instance.loadRemote(`${appId}_server/${func}`);
|
|
15
|
+
return loadedModule;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async function loadFunc(func, appId, origin, version) {
|
|
19
|
+
// here we assign the return value of the init() function, which can be used to do some more complex
|
|
20
|
+
// things with the module federation runtime
|
|
21
|
+
const module = await importRemoteModule(func, appId, origin, version);
|
|
22
|
+
const loadedFunc = module.default;
|
|
23
|
+
|
|
24
|
+
return loadedFunc;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function loadPrice(func, appId, origin, version) {
|
|
28
|
+
const module = await importRemoteModule(func, appId, origin, version);
|
|
29
|
+
const price = module._CREDIT_PER_CALL;
|
|
30
|
+
return price;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = {loadFunc, loadPrice};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<title>Pulse App</title>
|
|
7
|
-
</head>
|
|
8
|
-
<body style="height: 100vh; width: 100vw">
|
|
9
|
-
<div id="root" style="height: 100%; width: 100%"></div>
|
|
10
|
-
</body>
|
|
11
|
-
</html>
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Pulse App</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body style="height: 100vh; width: 100vw">
|
|
9
|
+
<div id="root" style="height: 100%; width: 100%"></div>
|
|
10
|
+
</body>
|
|
11
|
+
</html>
|
package/dist/lib/server/utils.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import fs from 'fs/promises';
|
|
2
2
|
export async function readConfigFile() {
|
|
3
3
|
// Read pulse.config.json from dist/client
|
|
4
|
-
// Wait until dist/
|
|
4
|
+
// Wait until dist/pulse.config.json exists
|
|
5
5
|
while (true) {
|
|
6
6
|
try {
|
|
7
|
-
await fs.access('dist/
|
|
7
|
+
await fs.access('dist/pulse.config.json');
|
|
8
8
|
break;
|
|
9
9
|
}
|
|
10
10
|
catch (err) {
|
|
@@ -12,6 +12,6 @@ export async function readConfigFile() {
|
|
|
12
12
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
|
-
const data = await fs.readFile('dist/
|
|
15
|
+
const data = await fs.readFile('dist/pulse.config.json', 'utf-8');
|
|
16
16
|
return JSON.parse(data);
|
|
17
17
|
}
|
package/dist/lib/token.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import os from 'os';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import fs from 'fs';
|
|
4
|
+
import { getBackendUrl } from './backend-url.js';
|
|
4
5
|
export function saveToken(token, devMode) {
|
|
5
6
|
// Save the token to .pulse-editor/config.json in user home directory
|
|
6
7
|
const configDir = path.join(os.homedir(), '.pulse-editor');
|
|
@@ -62,9 +63,7 @@ export function isTokenInEnv(devMode) {
|
|
|
62
63
|
return false;
|
|
63
64
|
}
|
|
64
65
|
export async function checkToken(token, devMode) {
|
|
65
|
-
const res = await fetch(devMode
|
|
66
|
-
? 'https://localhost:8080/api/api-keys/check'
|
|
67
|
-
: 'https://pulse-editor.com/api/api-keys/check', {
|
|
66
|
+
const res = await fetch(`${getBackendUrl(devMode)}/api/api-keys/check`, {
|
|
68
67
|
body: JSON.stringify({ token }),
|
|
69
68
|
headers: {
|
|
70
69
|
'Content-Type': 'application/json',
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import webpack from "webpack";
|
|
2
|
+
import { generateTempTsConfig } from "./configs/utils.js";
|
|
3
|
+
import { createWebpackConfig } from "./webpack-config.js";
|
|
4
|
+
export async function webpackCompile(mode, buildTarget, isWatchMode = false) {
|
|
5
|
+
generateTempTsConfig();
|
|
6
|
+
const configs = await createWebpackConfig(mode === "preview", buildTarget ?? "both", mode === "development"
|
|
7
|
+
? "development"
|
|
8
|
+
: mode === "preview"
|
|
9
|
+
? "development"
|
|
10
|
+
: "production");
|
|
11
|
+
const compiler = webpack(configs);
|
|
12
|
+
if (isWatchMode) {
|
|
13
|
+
compiler.watch({}, (err, stats) => {
|
|
14
|
+
if (err) {
|
|
15
|
+
console.error("❌ Webpack build failed", err);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
return compiler;
|
|
20
|
+
}
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
compiler.run((err) => {
|
|
23
|
+
if (err) {
|
|
24
|
+
reject(err);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
resolve();
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { ModuleFederationPlugin } from "@module-federation/enhanced/webpack";
|
|
3
|
+
import CopyWebpackPlugin from "copy-webpack-plugin";
|
|
4
|
+
import fs from "fs";
|
|
5
|
+
import { globSync } from "glob";
|
|
6
|
+
import MiniCssExtractPlugin from "mini-css-extract-plugin";
|
|
7
|
+
import path from "path";
|
|
8
|
+
import ts from "typescript";
|
|
9
|
+
import { discoverAppSkillActions, getLocalNetworkIP, loadPulseConfig, } from "./utils.js";
|
|
10
|
+
class MFClientPlugin {
|
|
11
|
+
projectDirName;
|
|
12
|
+
pulseConfig;
|
|
13
|
+
origin;
|
|
14
|
+
constructor(pulseConfig) {
|
|
15
|
+
this.projectDirName = process.cwd();
|
|
16
|
+
this.pulseConfig = pulseConfig;
|
|
17
|
+
this.origin = getLocalNetworkIP();
|
|
18
|
+
}
|
|
19
|
+
apply(compiler) {
|
|
20
|
+
if (compiler.options.mode === "development") {
|
|
21
|
+
let isFirstRun = true;
|
|
22
|
+
// Before build starts
|
|
23
|
+
// When a file changes and triggers a new compilation
|
|
24
|
+
compiler.hooks.watchRun.tap("ReloadMessagePlugin", () => {
|
|
25
|
+
if (!isFirstRun) {
|
|
26
|
+
console.log("[client] 🔄 reloading app...");
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
console.log("[client] 🔄 building app...");
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
// Log file updates
|
|
33
|
+
compiler.hooks.invalid.tap("LogFileUpdates", (file, changeTime) => {
|
|
34
|
+
console.log(`[watch] change detected in: ${file} at ${new Date(changeTime || Date.now()).toLocaleTimeString()}`);
|
|
35
|
+
});
|
|
36
|
+
const devStartupMessage = `
|
|
37
|
+
🎉 Your Pulse extension \x1b[1m${this.pulseConfig.displayName}\x1b[0m is LIVE!
|
|
38
|
+
|
|
39
|
+
⚡️ Local: http://localhost:3030/${this.pulseConfig.id}/${this.pulseConfig.version}/
|
|
40
|
+
⚡️ Network: http://${this.origin}:3030/${this.pulseConfig.id}/${this.pulseConfig.version}/
|
|
41
|
+
|
|
42
|
+
✨ Try it out in the Pulse Editor and let the magic happen! 🚀
|
|
43
|
+
`;
|
|
44
|
+
// After build finishes
|
|
45
|
+
compiler.hooks.done.tap("ReloadMessagePlugin", () => {
|
|
46
|
+
if (isFirstRun) {
|
|
47
|
+
console.log("[client] ✅ Successfully built client.");
|
|
48
|
+
console.log(devStartupMessage);
|
|
49
|
+
isFirstRun = false;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
console.log("[client] ✅ Reload finished.");
|
|
53
|
+
}
|
|
54
|
+
// Write pulse config to dist
|
|
55
|
+
fs.writeFileSync(path.resolve(this.projectDirName, "dist/pulse.config.json"), JSON.stringify(this.pulseConfig, null, 2));
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
// Print build success/failed message
|
|
60
|
+
compiler.hooks.done.tap("BuildMessagePlugin", (stats) => {
|
|
61
|
+
if (stats.hasErrors()) {
|
|
62
|
+
console.log(`[client] ❌ Failed to build client.`);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
console.log(`[client] ✅ Successfully built client.`);
|
|
66
|
+
// Write pulse config to dist
|
|
67
|
+
fs.writeFileSync(path.resolve(this.projectDirName, "dist/pulse.config.json"), JSON.stringify(this.pulseConfig, null, 2));
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
compiler.hooks.beforeCompile.tap("PulseConfigPlugin", () => {
|
|
72
|
+
let requireFS = false;
|
|
73
|
+
function isWorkspaceHook(node) {
|
|
74
|
+
return (ts.isCallExpression(node) &&
|
|
75
|
+
ts.isIdentifier(node.expression) &&
|
|
76
|
+
[
|
|
77
|
+
"useFileSystem",
|
|
78
|
+
"useFile",
|
|
79
|
+
"useReceiveFile",
|
|
80
|
+
"useTerminal",
|
|
81
|
+
"useWorkspaceInfo",
|
|
82
|
+
].includes(node.expression.text));
|
|
83
|
+
}
|
|
84
|
+
function scanSource(sourceText) {
|
|
85
|
+
const sourceFile = ts.createSourceFile("temp.tsx", sourceText, ts.ScriptTarget.Latest, true);
|
|
86
|
+
const visit = (node) => {
|
|
87
|
+
// Detect: useFileSystem(...)
|
|
88
|
+
if (isWorkspaceHook(node)) {
|
|
89
|
+
requireFS = true;
|
|
90
|
+
}
|
|
91
|
+
ts.forEachChild(node, visit);
|
|
92
|
+
};
|
|
93
|
+
visit(sourceFile);
|
|
94
|
+
}
|
|
95
|
+
globSync(["src/**/*.tsx", "src/**/*.ts"]).forEach((file) => {
|
|
96
|
+
const source = fs.readFileSync(file, "utf8");
|
|
97
|
+
scanSource(source);
|
|
98
|
+
});
|
|
99
|
+
// Persist result
|
|
100
|
+
this.pulseConfig.requireWorkspace = requireFS;
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
export async function makeMFClientConfig(mode) {
|
|
105
|
+
const projectDirName = process.cwd();
|
|
106
|
+
const pulseConfig = await loadPulseConfig();
|
|
107
|
+
const mainComponent = "./src/main.tsx";
|
|
108
|
+
const actions = discoverAppSkillActions();
|
|
109
|
+
return {
|
|
110
|
+
mode: mode,
|
|
111
|
+
name: "client",
|
|
112
|
+
entry: mainComponent,
|
|
113
|
+
output: {
|
|
114
|
+
publicPath: "auto",
|
|
115
|
+
path: path.resolve(projectDirName, "dist/client"),
|
|
116
|
+
},
|
|
117
|
+
resolve: {
|
|
118
|
+
extensions: [".ts", ".tsx", ".js"],
|
|
119
|
+
},
|
|
120
|
+
plugins: [
|
|
121
|
+
new MiniCssExtractPlugin({
|
|
122
|
+
filename: "globals.css",
|
|
123
|
+
}),
|
|
124
|
+
// Copy assets to dist
|
|
125
|
+
new CopyWebpackPlugin({
|
|
126
|
+
patterns: [{ from: "src/assets", to: "assets" }],
|
|
127
|
+
}),
|
|
128
|
+
new ModuleFederationPlugin({
|
|
129
|
+
// Do not use hyphen character '-' in the name
|
|
130
|
+
name: pulseConfig.id + "_client",
|
|
131
|
+
filename: "remoteEntry.js",
|
|
132
|
+
exposes: {
|
|
133
|
+
"./main": mainComponent,
|
|
134
|
+
...actions,
|
|
135
|
+
},
|
|
136
|
+
shared: {
|
|
137
|
+
react: {
|
|
138
|
+
requiredVersion: "19.2.0",
|
|
139
|
+
import: "react", // the "react" package will be used a provided and fallback module
|
|
140
|
+
shareKey: "react", // under this name the shared module will be placed in the share scope
|
|
141
|
+
shareScope: "default", // share scope with this name will be used
|
|
142
|
+
singleton: true, // only a single version of the shared module is allowed
|
|
143
|
+
},
|
|
144
|
+
"react-dom": {
|
|
145
|
+
requiredVersion: "19.2.0",
|
|
146
|
+
singleton: true, // only a single version of the shared module is allowed
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
}),
|
|
150
|
+
new MFClientPlugin(pulseConfig),
|
|
151
|
+
],
|
|
152
|
+
module: {
|
|
153
|
+
rules: [
|
|
154
|
+
{
|
|
155
|
+
test: /\.tsx?$/,
|
|
156
|
+
use: "ts-loader",
|
|
157
|
+
exclude: [/node_modules/, /dist/],
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
test: /\.css$/i,
|
|
161
|
+
use: [
|
|
162
|
+
MiniCssExtractPlugin.loader,
|
|
163
|
+
"css-loader",
|
|
164
|
+
{
|
|
165
|
+
loader: "postcss-loader",
|
|
166
|
+
},
|
|
167
|
+
],
|
|
168
|
+
exclude: [/dist/],
|
|
169
|
+
},
|
|
170
|
+
],
|
|
171
|
+
},
|
|
172
|
+
stats: {
|
|
173
|
+
all: false,
|
|
174
|
+
errors: true,
|
|
175
|
+
warnings: true,
|
|
176
|
+
logging: "warn",
|
|
177
|
+
colors: true,
|
|
178
|
+
assets: false,
|
|
179
|
+
},
|
|
180
|
+
infrastructureLogging: {
|
|
181
|
+
level: "warn",
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
}
|