@pulse-editor/cli 0.1.1-beta.16 → 0.1.1-beta.18
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 +2 -1
- package/dist/components/commands/build.js +5 -33
- package/dist/components/commands/dev.js +22 -9
- package/dist/components/commands/preview.js +37 -8
- package/dist/components/commands/publish.js +21 -35
- package/dist/components/commands/upgrade.d.ts +5 -0
- 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 +27 -0
- package/dist/lib/cli-flags.d.ts +7 -0
- package/dist/lib/cli-flags.js +7 -0
- package/dist/lib/manual.js +11 -0
- package/dist/lib/webpack/compile.d.ts +2 -0
- package/dist/lib/webpack/compile.js +28 -0
- package/dist/lib/webpack/webpack.config.d.ts +2 -0
- package/dist/lib/webpack/webpack.config.js +545 -0
- package/package.json +18 -11
package/dist/app.js
CHANGED
|
@@ -12,6 +12,7 @@ import Build from './components/commands/build.js';
|
|
|
12
12
|
import Preview from './components/commands/preview.js';
|
|
13
13
|
import Start from './components/commands/start.js';
|
|
14
14
|
import Clean from './components/commands/clean.js';
|
|
15
|
+
import Upgrade from './components/commands/upgrade.js';
|
|
15
16
|
export default function App({ cli }) {
|
|
16
17
|
const [command, setCommand] = useState(undefined);
|
|
17
18
|
if (cli.flags.stage) {
|
|
@@ -24,5 +25,5 @@ export default function App({ cli }) {
|
|
|
24
25
|
const cmd = cli.input[0] ?? 'help';
|
|
25
26
|
setCommand(cmd);
|
|
26
27
|
}, [cli.input]);
|
|
27
|
-
return (_jsxs(_Fragment, { children: [cli.flags.stage && (_jsx(Text, { color: 'yellow', children: "\u26A0\uFE0F
|
|
28
|
+
return (_jsxs(_Fragment, { children: [cli.flags.stage && (_jsx(Text, { color: 'yellow', children: "\u26A0\uFE0F You are in development mode." })), command === 'help' ? (_jsx(Help, { cli: cli })) : command === 'chat' ? (_jsx(Chat, { cli: cli })) : command === 'login' ? (_jsx(Login, { cli: cli })) : command === 'logout' ? (_jsx(Logout, { cli: cli })) : command === 'publish' ? (_jsx(Publish, { cli: cli })) : command === 'create' ? (_jsx(Create, { cli: cli })) : command === 'dev' ? (_jsx(Dev, { cli: cli })) : command === 'build' ? (_jsx(Build, { cli: cli })) : command === 'preview' ? (_jsx(Preview, { cli: cli })) : command === 'start' ? (_jsx(Start, { cli: cli })) : command === 'clean' ? (_jsx(Clean, { cli: cli })) : command === 'upgrade' ? (_jsx(Upgrade, { cli: cli })) : (command !== undefined && (_jsxs(_Fragment, { children: [_jsxs(Text, { color: 'redBright', children: ["Invalid command: ", command] }), _jsxs(Text, { children: ["Run ", _jsx(Text, { color: 'blueBright', children: "pulse help" }), " to see the list of available commands."] })] })))] }));
|
|
28
29
|
}
|
|
@@ -3,6 +3,7 @@ import { useEffect } from 'react';
|
|
|
3
3
|
import { execa } from 'execa';
|
|
4
4
|
import fs from 'fs';
|
|
5
5
|
import { cleanDist } from '../../lib/execa-utils/clean.js';
|
|
6
|
+
import { webpackCompile } from '../../lib/webpack/compile.js';
|
|
6
7
|
export default function Build({ cli }) {
|
|
7
8
|
useEffect(() => {
|
|
8
9
|
async function buildProd() {
|
|
@@ -48,40 +49,11 @@ export default function Build({ cli }) {
|
|
|
48
49
|
}
|
|
49
50
|
}
|
|
50
51
|
await cleanDist();
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
await execa('npx webpack --mode production', {
|
|
54
|
-
stdio: 'inherit',
|
|
55
|
-
shell: true,
|
|
56
|
-
env: {
|
|
57
|
-
NODE_OPTIONS: '--import=tsx',
|
|
58
|
-
},
|
|
59
|
-
});
|
|
52
|
+
try {
|
|
53
|
+
await webpackCompile('production', buildTarget);
|
|
60
54
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
await execa('npx webpack --mode production', {
|
|
64
|
-
stdio: 'inherit',
|
|
65
|
-
shell: true,
|
|
66
|
-
env: {
|
|
67
|
-
NODE_OPTIONS: '--import=tsx',
|
|
68
|
-
BUILD_TARGET: 'client',
|
|
69
|
-
},
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
else if (buildTarget === 'server') {
|
|
73
|
-
// Start building server only
|
|
74
|
-
await execa('npx webpack --mode production', {
|
|
75
|
-
stdio: 'inherit',
|
|
76
|
-
shell: true,
|
|
77
|
-
env: {
|
|
78
|
-
NODE_OPTIONS: '--import=tsx',
|
|
79
|
-
BUILD_TARGET: 'server',
|
|
80
|
-
},
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
console.error(`❌ Unknown build target: ${buildTarget}`);
|
|
55
|
+
catch (err) {
|
|
56
|
+
console.error('❌ Webpack build failed', err);
|
|
85
57
|
}
|
|
86
58
|
}
|
|
87
59
|
buildProd();
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Text } from 'ink';
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
3
2
|
import { useEffect } from 'react';
|
|
4
3
|
import { execa } from 'execa';
|
|
5
4
|
import fs from 'fs';
|
|
6
5
|
import { getDepsBinPath } from '../../lib/execa-utils/deps.js';
|
|
7
6
|
import { cleanDist } from '../../lib/execa-utils/clean.js';
|
|
7
|
+
import { webpackCompile } from '../../lib/webpack/compile.js';
|
|
8
8
|
export default function Dev({ cli }) {
|
|
9
9
|
useEffect(() => {
|
|
10
10
|
async function startDevServer() {
|
|
@@ -47,21 +47,34 @@ export default function Dev({ cli }) {
|
|
|
47
47
|
}
|
|
48
48
|
// Start dev server
|
|
49
49
|
await cleanDist();
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
'
|
|
50
|
+
// Start webpack in dev watch mode and watch for changes
|
|
51
|
+
const compiler = await webpackCompile('development', undefined, true);
|
|
52
|
+
// Start server with tsx
|
|
53
|
+
await execa(getDepsBinPath('tsx'), [
|
|
54
|
+
'watch',
|
|
55
|
+
'--clear-screen=false',
|
|
56
|
+
'node_modules/@pulse-editor/cli/dist/lib/server/express.js',
|
|
55
57
|
], {
|
|
56
58
|
stdio: 'inherit',
|
|
57
59
|
shell: true,
|
|
58
60
|
env: {
|
|
59
61
|
NODE_OPTIONS: '--import=tsx',
|
|
60
|
-
NODE_ENV: 'development',
|
|
61
62
|
},
|
|
62
63
|
});
|
|
64
|
+
// Handle process exit to close webpack compiler
|
|
65
|
+
process.on('SIGINT', () => {
|
|
66
|
+
if (compiler && typeof compiler.close === 'function') {
|
|
67
|
+
compiler.close(() => {
|
|
68
|
+
process.exit();
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
process.exit();
|
|
73
|
+
}
|
|
74
|
+
});
|
|
63
75
|
}
|
|
76
|
+
console.log('🚀 Starting development server...');
|
|
64
77
|
startDevServer();
|
|
65
78
|
}, []);
|
|
66
|
-
return
|
|
79
|
+
return _jsx(_Fragment, {});
|
|
67
80
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Text } from 'ink';
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
3
2
|
import { useEffect } from 'react';
|
|
4
3
|
import { execa } from 'execa';
|
|
5
4
|
import fs from 'fs';
|
|
6
5
|
import { getDepsBinPath } from '../../lib/execa-utils/deps.js';
|
|
7
6
|
import { cleanDist } from '../../lib/execa-utils/clean.js';
|
|
7
|
+
import { webpackCompile } from '../../lib/webpack/compile.js';
|
|
8
8
|
export default function Preview({ cli }) {
|
|
9
9
|
useEffect(() => {
|
|
10
10
|
async function startPreviewServer() {
|
|
@@ -47,11 +47,28 @@ export default function Preview({ cli }) {
|
|
|
47
47
|
}
|
|
48
48
|
// Start preview server
|
|
49
49
|
await cleanDist();
|
|
50
|
-
await execa(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
50
|
+
// await execa(
|
|
51
|
+
// getDepsBinPath('concurrently'),
|
|
52
|
+
// [
|
|
53
|
+
// '--prefix',
|
|
54
|
+
// 'none',
|
|
55
|
+
// '"npx webpack --mode development --watch"',
|
|
56
|
+
// '"tsx watch --clear-screen=false node_modules/@pulse-editor/cli/dist/lib/server/express.js"',
|
|
57
|
+
// ],
|
|
58
|
+
// {
|
|
59
|
+
// stdio: 'inherit',
|
|
60
|
+
// shell: true,
|
|
61
|
+
// env: {
|
|
62
|
+
// NODE_OPTIONS: '--import=tsx',
|
|
63
|
+
// PREVIEW: 'true',
|
|
64
|
+
// },
|
|
65
|
+
// },
|
|
66
|
+
// );
|
|
67
|
+
const compiler = await webpackCompile('preview', undefined, true);
|
|
68
|
+
await execa(getDepsBinPath('tsx'), [
|
|
69
|
+
'watch',
|
|
70
|
+
'--clear-screen=false',
|
|
71
|
+
'node_modules/@pulse-editor/cli/dist/lib/server/express.js',
|
|
55
72
|
], {
|
|
56
73
|
stdio: 'inherit',
|
|
57
74
|
shell: true,
|
|
@@ -60,8 +77,20 @@ export default function Preview({ cli }) {
|
|
|
60
77
|
PREVIEW: 'true',
|
|
61
78
|
},
|
|
62
79
|
});
|
|
80
|
+
// Handle process exit to close webpack compiler
|
|
81
|
+
process.on('SIGINT', () => {
|
|
82
|
+
if (compiler && typeof compiler.close === 'function') {
|
|
83
|
+
compiler.close(() => {
|
|
84
|
+
process.exit();
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
process.exit();
|
|
89
|
+
}
|
|
90
|
+
});
|
|
63
91
|
}
|
|
92
|
+
console.log('🚀 Starting preview server...');
|
|
64
93
|
startPreviewServer();
|
|
65
94
|
}, []);
|
|
66
|
-
return
|
|
95
|
+
return _jsx(_Fragment, {});
|
|
67
96
|
}
|
|
@@ -5,12 +5,15 @@ import { checkToken, getToken } from '../../lib/token.js';
|
|
|
5
5
|
import Spinner from 'ink-spinner';
|
|
6
6
|
import fs from 'fs';
|
|
7
7
|
import { $ } from 'execa';
|
|
8
|
+
import { publishApp } from '../../lib/backend/publish-app.js';
|
|
8
9
|
export default function Publish({ cli }) {
|
|
9
10
|
const [isInProjectDir, setIsInProjectDir] = useState(false);
|
|
10
11
|
const [isCheckingAuth, setIsCheckingAuth] = useState(true);
|
|
11
12
|
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
|
12
13
|
const [isBuilding, setIsBuilding] = useState(false);
|
|
13
14
|
const [isBuildingError, setIsBuildingError] = useState(false);
|
|
15
|
+
const [isZipping, setIsZipping] = useState(false);
|
|
16
|
+
const [isZippingError, setIsZippingError] = useState(false);
|
|
14
17
|
const [isPublishing, setIsPublishing] = useState(false);
|
|
15
18
|
const [isPublishingError, setIsPublishingError] = useState(false);
|
|
16
19
|
const [isPublished, setIsPublished] = useState(false);
|
|
@@ -45,56 +48,39 @@ export default function Publish({ cli }) {
|
|
|
45
48
|
// Build the extension
|
|
46
49
|
useEffect(() => {
|
|
47
50
|
async function buildExtension() {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
51
|
+
if (cli.flags.build) {
|
|
52
|
+
setIsBuilding(true);
|
|
53
|
+
try {
|
|
54
|
+
await $ `npm run build`;
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
setIsBuildingError(true);
|
|
58
|
+
setFailureMessage('Build failed. Please run `npm run build` to see the error.');
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
setIsBuilding(false);
|
|
63
|
+
}
|
|
57
64
|
}
|
|
65
|
+
setIsZipping(true);
|
|
58
66
|
// Zip the dist folder
|
|
59
67
|
try {
|
|
60
68
|
await $({ cwd: 'dist' }) `zip -r ../node_modules/@pulse-editor/dist.zip *`;
|
|
61
69
|
}
|
|
62
70
|
catch (error) {
|
|
63
|
-
|
|
64
|
-
setIsBuilding(false);
|
|
71
|
+
setIsZippingError(true);
|
|
65
72
|
setFailureMessage('Failed to zip the build output.');
|
|
66
73
|
return;
|
|
67
74
|
}
|
|
68
75
|
finally {
|
|
69
|
-
|
|
76
|
+
setIsZipping(false);
|
|
70
77
|
}
|
|
71
78
|
await publishExtension();
|
|
72
79
|
}
|
|
73
80
|
async function publishExtension() {
|
|
74
81
|
setIsPublishing(true);
|
|
75
|
-
// Read pulse.config.json for visibility
|
|
76
|
-
const config = JSON.parse(fs.readFileSync('./dist/client/pulse.config.json', 'utf-8'));
|
|
77
|
-
const visibility = config.visibility;
|
|
78
|
-
// Upload the zip file to the server
|
|
79
82
|
try {
|
|
80
|
-
const
|
|
81
|
-
const buffer = fs.readFileSync('./node_modules/@pulse-editor/dist.zip');
|
|
82
|
-
// @ts-ignore Create a Blob from the buffer
|
|
83
|
-
const blob = new Blob([buffer], {
|
|
84
|
-
type: 'application/zip',
|
|
85
|
-
});
|
|
86
|
-
formData.append('file', blob, 'dist.zip');
|
|
87
|
-
formData.append('visibility', visibility);
|
|
88
|
-
// Send the file to the server
|
|
89
|
-
const res = await fetch(cli.flags.stage
|
|
90
|
-
? 'https://localhost:8080/api/app/publish'
|
|
91
|
-
: 'https://pulse-editor.com/api/app/publish', {
|
|
92
|
-
method: 'POST',
|
|
93
|
-
headers: {
|
|
94
|
-
Authorization: `Bearer ${getToken(cli.flags.stage)}`,
|
|
95
|
-
},
|
|
96
|
-
body: formData,
|
|
97
|
-
});
|
|
83
|
+
const res = await publishApp(cli.flags.stage);
|
|
98
84
|
if (res.status === 200) {
|
|
99
85
|
setIsPublished(true);
|
|
100
86
|
}
|
|
@@ -122,5 +108,5 @@ export default function Publish({ cli }) {
|
|
|
122
108
|
buildExtension();
|
|
123
109
|
}
|
|
124
110
|
}, [isAuthenticated]);
|
|
125
|
-
return (_jsx(_Fragment, { children: !isInProjectDir ? (_jsx(Text, { color: 'redBright', children: "\u26D4 The current directory does not contain a Pulse Editor project." })) : isCheckingAuth ? (_jsxs(Box, { children: [_jsx(Spinner, { type: "dots" }), _jsx(Text, { children: " Checking authentication..." })] })) : isAuthenticated ? (_jsxs(_Fragment, { children: [isBuilding && (_jsxs(Box, { children: [_jsx(Spinner, { type: "dots" }), _jsx(Text, { children: " Building..." })] })), isBuildingError && (_jsx(Text, { color: 'redBright', children: "\u274C Error building the extension. Please run `npm run build` to see the error." })),
|
|
111
|
+
return (_jsx(_Fragment, { children: !isInProjectDir ? (_jsx(Text, { color: 'redBright', children: "\u26D4 The current directory does not contain a Pulse Editor project." })) : isCheckingAuth ? (_jsxs(Box, { children: [_jsx(Spinner, { type: "dots" }), _jsx(Text, { children: " Checking authentication..." })] })) : !isAuthenticated ? (_jsxs(Text, { children: ["You are not authenticated or your access token is invalid. Publishing to Extension Marketplace is in Beta access. Please visit", _jsx(Text, { color: 'blueBright', children: " https://pulse-editor.com/beta " }), "to apply for Beta access."] })) : (_jsxs(_Fragment, { children: [isBuilding && (_jsxs(Box, { children: [_jsx(Spinner, { type: "dots" }), _jsx(Text, { children: " Building..." })] })), isBuildingError && (_jsx(Text, { color: 'redBright', children: "\u274C Error building the extension. Please run `npm run build` to see the error." })), isZipping && (_jsxs(Box, { children: [_jsx(Spinner, { type: "dots" }), _jsx(Text, { children: " Compressing build..." })] })), isZippingError && (_jsx(Text, { color: 'redBright', children: "\u274C Error zipping the build output." })), isPublishing && (_jsxs(Box, { children: [_jsx(Spinner, { type: "dots" }), _jsx(Text, { children: " Publishing..." })] })), isPublishingError && (_jsxs(_Fragment, { children: [_jsx(Text, { color: 'redBright', children: "\u274C Failed to publish extension." }), failureMessage && (_jsxs(Text, { color: 'redBright', children: ["Error: ", failureMessage] }))] })), isPublished && (_jsx(Text, { color: 'greenBright', children: "\u2705 Extension published successfully." }))] })) }));
|
|
126
112
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { $ } from 'execa';
|
|
3
|
+
import React, { useEffect, useState } from 'react';
|
|
4
|
+
import { Box, Text } from 'ink';
|
|
5
|
+
import Spinner from 'ink-spinner';
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
export default function Upgrade({ cli }) {
|
|
8
|
+
const [isInProjectDir, setIsInProjectDir] = useState(false);
|
|
9
|
+
const [step, setStep] = useState('check-config');
|
|
10
|
+
const [isError, setIsError] = useState(false);
|
|
11
|
+
const [errorMessage, setErrorMessage] = useState(null);
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
async function checkConfig() {
|
|
14
|
+
// Check if the current dir contains pulse.config.ts
|
|
15
|
+
const currentDir = process.cwd();
|
|
16
|
+
const pulseConfigPath = `${currentDir}/pulse.config.ts`;
|
|
17
|
+
if (fs.existsSync(pulseConfigPath)) {
|
|
18
|
+
setIsInProjectDir(true);
|
|
19
|
+
}
|
|
20
|
+
setStep('upgrade');
|
|
21
|
+
}
|
|
22
|
+
checkConfig();
|
|
23
|
+
}, []);
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
async function start() {
|
|
26
|
+
try {
|
|
27
|
+
await upgradePackages();
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
setIsError(true);
|
|
31
|
+
setErrorMessage(error.message);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (isInProjectDir) {
|
|
35
|
+
start();
|
|
36
|
+
}
|
|
37
|
+
}, [isInProjectDir]);
|
|
38
|
+
async function upgradePackages() {
|
|
39
|
+
const tag = cli.flags.beta ? 'beta' : 'latest';
|
|
40
|
+
await $ `npm install react@${React.version} react-dom@${React.version} --save-exact --silent --force`;
|
|
41
|
+
await $ `npm install -D @pulse-editor/cli@${tag} --silent --force`;
|
|
42
|
+
await $ `npm install @pulse-editor/shared-utils@${tag} @pulse-editor/react-api@${tag} --silent --force`;
|
|
43
|
+
// Remove webpack.config.ts if exists
|
|
44
|
+
const webpackConfigPath = `${process.cwd()}/webpack.config.ts`;
|
|
45
|
+
if (fs.existsSync(webpackConfigPath)) {
|
|
46
|
+
fs.unlinkSync(webpackConfigPath);
|
|
47
|
+
}
|
|
48
|
+
// Uninstall @module-federation/node html-webpack-plugin copy-webpack-plugin glob mini-css-extract-plugin webpack webpack-cli webpack-dev-server
|
|
49
|
+
await $ `npm uninstall @module-federation/node html-webpack-plugin copy-webpack-plugin glob mini-css-extract-plugin webpack webpack-cli webpack-dev-server --silent --force`;
|
|
50
|
+
setStep('done');
|
|
51
|
+
}
|
|
52
|
+
return (_jsx(_Fragment, { children: isError ? (_jsxs(Text, { color: 'redBright', children: ["\u274C An error occurred: ", errorMessage || 'Unknown error'] })) : !isInProjectDir ? (_jsx(Text, { color: 'redBright', children: "\u26D4 The current directory does not contain a Pulse Editor project." })) : step === 'check-config' ? (_jsxs(Box, { children: [_jsx(Spinner, { type: "dots" }), _jsx(Text, { children: " Checking configuration..." })] })) : step === 'upgrade' ? (_jsx(_Fragment, { children: _jsxs(Box, { children: [_jsx(Spinner, { type: "dots" }), _jsx(Text, { children: " Upgrading packages..." })] }) })) : (_jsx(Box, { children: _jsx(Text, { color: 'greenBright', children: "\u2705 Upgrade completed successfully." }) })) }));
|
|
53
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function publishApp(isStage: boolean): Promise<Response>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { getToken } from '../token.js';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
export async function publishApp(isStage) {
|
|
4
|
+
// Upload the zip file to the server
|
|
5
|
+
// Read pulse.config.json for visibility
|
|
6
|
+
const config = JSON.parse(fs.readFileSync('./dist/client/pulse.config.json', 'utf-8'));
|
|
7
|
+
const visibility = config.visibility;
|
|
8
|
+
const formData = new FormData();
|
|
9
|
+
const buffer = fs.readFileSync('./node_modules/@pulse-editor/dist.zip');
|
|
10
|
+
// @ts-ignore Create a Blob from the buffer
|
|
11
|
+
const blob = new Blob([buffer], {
|
|
12
|
+
type: 'application/zip',
|
|
13
|
+
});
|
|
14
|
+
formData.append('file', blob, 'dist.zip');
|
|
15
|
+
formData.append('visibility', visibility);
|
|
16
|
+
// Send the file to the server
|
|
17
|
+
const res = await fetch(isStage
|
|
18
|
+
? 'https://localhost:8080/api/app/publish'
|
|
19
|
+
: 'https://pulse-editor.com/api/app/publish', {
|
|
20
|
+
method: 'POST',
|
|
21
|
+
headers: {
|
|
22
|
+
Authorization: `Bearer ${getToken(isStage)}`,
|
|
23
|
+
},
|
|
24
|
+
body: formData,
|
|
25
|
+
});
|
|
26
|
+
return res;
|
|
27
|
+
}
|
package/dist/lib/cli-flags.d.ts
CHANGED
package/dist/lib/cli-flags.js
CHANGED
package/dist/lib/manual.js
CHANGED
|
@@ -27,6 +27,9 @@ const logout = `\
|
|
|
27
27
|
`;
|
|
28
28
|
const publish = `\
|
|
29
29
|
publish Publish Pulse Editor Extension in current directory to the Pulse Editor Platform.
|
|
30
|
+
Flags:
|
|
31
|
+
--noBuild
|
|
32
|
+
Skip the build step before publishing.
|
|
30
33
|
|
|
31
34
|
`;
|
|
32
35
|
const create = `\
|
|
@@ -71,6 +74,13 @@ const start = `\
|
|
|
71
74
|
const clean = `\
|
|
72
75
|
clean Clean the dist/ directory.
|
|
73
76
|
|
|
77
|
+
`;
|
|
78
|
+
const upgrade = `\
|
|
79
|
+
upgrade Upgrade Pulse Editor CLI and related packages to the latest version.
|
|
80
|
+
Flags:
|
|
81
|
+
--beta
|
|
82
|
+
Upgrade to the latest beta version.
|
|
83
|
+
|
|
74
84
|
`;
|
|
75
85
|
export const commandsManual = {
|
|
76
86
|
help,
|
|
@@ -84,4 +94,5 @@ export const commandsManual = {
|
|
|
84
94
|
build,
|
|
85
95
|
start,
|
|
86
96
|
clean,
|
|
97
|
+
upgrade,
|
|
87
98
|
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import webpack from 'webpack';
|
|
2
|
+
import { createWebpackConfig } from '../../lib/webpack/webpack.config.js';
|
|
3
|
+
export async function webpackCompile(mode, buildTarget, isWatchMode = false) {
|
|
4
|
+
const configs = await createWebpackConfig(mode === 'preview', buildTarget ?? 'both', mode === 'development'
|
|
5
|
+
? 'development'
|
|
6
|
+
: mode === 'preview'
|
|
7
|
+
? 'development'
|
|
8
|
+
: 'production');
|
|
9
|
+
const compiler = webpack(configs);
|
|
10
|
+
if (isWatchMode) {
|
|
11
|
+
compiler.watch({}, (err, stats) => {
|
|
12
|
+
if (err) {
|
|
13
|
+
console.error('❌ Webpack build failed', err);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
return compiler;
|
|
18
|
+
}
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
compiler.run(err => {
|
|
21
|
+
if (err) {
|
|
22
|
+
reject(err);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
resolve();
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { ModuleFederationPlugin } from '@module-federation/enhanced/webpack';
|
|
3
|
+
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
|
|
4
|
+
import HtmlWebpackPlugin from 'html-webpack-plugin';
|
|
5
|
+
import { networkInterfaces } from 'os';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { globSync } from 'glob';
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import CopyWebpackPlugin from 'copy-webpack-plugin';
|
|
10
|
+
import ts from 'typescript';
|
|
11
|
+
import { pathToFileURL } from 'url';
|
|
12
|
+
import mfNode from '@module-federation/node';
|
|
13
|
+
const { NodeFederationPlugin } = mfNode;
|
|
14
|
+
import wp from 'webpack';
|
|
15
|
+
const { webpack } = wp;
|
|
16
|
+
export async function createWebpackConfig(isPreview, buildTarget, mode) {
|
|
17
|
+
const projectDirName = process.cwd();
|
|
18
|
+
async function loadPulseConfig() {
|
|
19
|
+
// compile to js file and import
|
|
20
|
+
const program = ts.createProgram({
|
|
21
|
+
rootNames: [path.join(projectDirName, 'pulse.config.ts')],
|
|
22
|
+
options: {
|
|
23
|
+
module: ts.ModuleKind.ESNext,
|
|
24
|
+
target: ts.ScriptTarget.ES2020,
|
|
25
|
+
outDir: path.join(projectDirName, 'node_modules/.pulse/config'),
|
|
26
|
+
esModuleInterop: true,
|
|
27
|
+
skipLibCheck: true,
|
|
28
|
+
forceConsistentCasingInFileNames: true,
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
program.emit();
|
|
32
|
+
// Fix imports in the generated js file for all files in node_modules/.pulse/config
|
|
33
|
+
globSync('node_modules/.pulse/config/**/*.js', {
|
|
34
|
+
cwd: projectDirName,
|
|
35
|
+
absolute: true,
|
|
36
|
+
}).forEach(jsFile => {
|
|
37
|
+
let content = fs.readFileSync(jsFile, 'utf-8');
|
|
38
|
+
content = content.replace(/(from\s+["']\.\/[^\s"']+)(["'])/g, (match, p1, p2) => {
|
|
39
|
+
// No change if the import already has any extension
|
|
40
|
+
if (p1.match(/\.(js|cjs|mjs|ts|tsx|json)$/)) {
|
|
41
|
+
return match; // No change needed
|
|
42
|
+
}
|
|
43
|
+
return `${p1}.js${p2}`;
|
|
44
|
+
});
|
|
45
|
+
fs.writeFileSync(jsFile, content);
|
|
46
|
+
});
|
|
47
|
+
// Copy package.json if exists
|
|
48
|
+
const pkgPath = path.join(projectDirName, 'package.json');
|
|
49
|
+
if (fs.existsSync(pkgPath)) {
|
|
50
|
+
const destPath = path.join(projectDirName, 'node_modules/.pulse/config/package.json');
|
|
51
|
+
fs.copyFileSync(pkgPath, destPath);
|
|
52
|
+
}
|
|
53
|
+
const compiledConfig = path.join(projectDirName, 'node_modules/.pulse/config/pulse.config.js');
|
|
54
|
+
const mod = await import(pathToFileURL(compiledConfig).href);
|
|
55
|
+
// delete the compiled config after importing
|
|
56
|
+
fs.rmSync(path.join(projectDirName, 'node_modules/.pulse/config'), {
|
|
57
|
+
recursive: true,
|
|
58
|
+
force: true,
|
|
59
|
+
});
|
|
60
|
+
return mod.default;
|
|
61
|
+
}
|
|
62
|
+
const pulseConfig = await loadPulseConfig();
|
|
63
|
+
function getLocalNetworkIP() {
|
|
64
|
+
const interfaces = networkInterfaces();
|
|
65
|
+
for (const iface of Object.values(interfaces)) {
|
|
66
|
+
if (!iface)
|
|
67
|
+
continue;
|
|
68
|
+
for (const config of iface) {
|
|
69
|
+
if (config.family === 'IPv4' && !config.internal) {
|
|
70
|
+
return config.address; // Returns the first non-internal IPv4 address
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return 'localhost'; // Fallback
|
|
75
|
+
}
|
|
76
|
+
const origin = getLocalNetworkIP();
|
|
77
|
+
const previewStartupMessage = `
|
|
78
|
+
🎉 Your Pulse extension preview \x1b[1m${pulseConfig.displayName}\x1b[0m is LIVE!
|
|
79
|
+
|
|
80
|
+
⚡️ Local: http://localhost:3030
|
|
81
|
+
⚡️ Network: http://${origin}:3030
|
|
82
|
+
|
|
83
|
+
✨ Try it out in your browser and let the magic happen! 🚀
|
|
84
|
+
`;
|
|
85
|
+
const devStartupMessage = `
|
|
86
|
+
🎉 Your Pulse extension \x1b[1m${pulseConfig.displayName}\x1b[0m is LIVE!
|
|
87
|
+
|
|
88
|
+
⚡️ Local: http://localhost:3030/${pulseConfig.id}/${pulseConfig.version}/
|
|
89
|
+
⚡️ Network: http://${origin}:3030/${pulseConfig.id}/${pulseConfig.version}/
|
|
90
|
+
|
|
91
|
+
✨ Try it out in the Pulse Editor and let the magic happen! 🚀
|
|
92
|
+
`;
|
|
93
|
+
// #region Node Federation Plugin for Server Functions
|
|
94
|
+
function makeNodeFederationPlugin() {
|
|
95
|
+
function discoverServerFunctions() {
|
|
96
|
+
// Get all .ts files under src/server-function and read use default exports as entry points
|
|
97
|
+
const files = globSync('./src/server-function/**/*.ts');
|
|
98
|
+
const entryPoints = files
|
|
99
|
+
.map(file => file.replaceAll('\\', '/'))
|
|
100
|
+
.map(file => {
|
|
101
|
+
return {
|
|
102
|
+
['./' +
|
|
103
|
+
file.replace('src/server-function/', '').replace(/\.ts$/, '')]: './' + file,
|
|
104
|
+
};
|
|
105
|
+
})
|
|
106
|
+
.reduce((acc, curr) => {
|
|
107
|
+
return { ...acc, ...curr };
|
|
108
|
+
}, {});
|
|
109
|
+
return entryPoints;
|
|
110
|
+
}
|
|
111
|
+
const funcs = discoverServerFunctions();
|
|
112
|
+
console.log(`Discovered server functions:
|
|
113
|
+
${Object.entries(funcs)
|
|
114
|
+
.map(([name, file]) => {
|
|
115
|
+
return ` - ${name.slice(2)} (from ${file})`;
|
|
116
|
+
})
|
|
117
|
+
.join('\n')}
|
|
118
|
+
`);
|
|
119
|
+
return new NodeFederationPlugin({
|
|
120
|
+
name: pulseConfig.id + '_server',
|
|
121
|
+
remoteType: 'script',
|
|
122
|
+
useRuntimePlugin: true,
|
|
123
|
+
library: { type: 'commonjs-module' },
|
|
124
|
+
filename: 'remoteEntry.js',
|
|
125
|
+
exposes: {
|
|
126
|
+
...funcs,
|
|
127
|
+
},
|
|
128
|
+
}, {});
|
|
129
|
+
}
|
|
130
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
131
|
+
function compileServerFunctions(compiler) {
|
|
132
|
+
// Remove existing entry points
|
|
133
|
+
try {
|
|
134
|
+
fs.rmSync('dist/server', { recursive: true, force: true });
|
|
135
|
+
}
|
|
136
|
+
catch (e) {
|
|
137
|
+
console.error('Error removing dist/server:', e);
|
|
138
|
+
console.log('Continuing...');
|
|
139
|
+
}
|
|
140
|
+
// Run a new webpack compilation to pick up new server functions
|
|
141
|
+
const options = {
|
|
142
|
+
...compiler.options,
|
|
143
|
+
watch: false,
|
|
144
|
+
plugins: [
|
|
145
|
+
// Add a new NodeFederationPlugin with updated entry points
|
|
146
|
+
makeNodeFederationPlugin(),
|
|
147
|
+
],
|
|
148
|
+
};
|
|
149
|
+
const newCompiler = webpack(options);
|
|
150
|
+
// Run the new compiler
|
|
151
|
+
newCompiler?.run((err, stats) => {
|
|
152
|
+
if (err) {
|
|
153
|
+
console.error(`[Server] ❌ Error during recompilation:`, err);
|
|
154
|
+
}
|
|
155
|
+
else if (stats?.hasErrors()) {
|
|
156
|
+
console.error(`[Server] ❌ Compilation errors:`, stats.toJson().errors);
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
console.log(`[Server] ✅ Compiled server functions successfully.`);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
// #endregion
|
|
164
|
+
// #region Source file parser for Pulse Config plugin
|
|
165
|
+
class PulseConfigPlugin {
|
|
166
|
+
requireFS = false;
|
|
167
|
+
apply(compiler) {
|
|
168
|
+
compiler.hooks.beforeCompile.tap('PulseConfigPlugin', () => {
|
|
169
|
+
this.requireFS = false;
|
|
170
|
+
globSync(['src/**/*.tsx', 'src/**/*.ts']).forEach(file => {
|
|
171
|
+
const source = fs.readFileSync(file, 'utf8');
|
|
172
|
+
this.scanSource(source);
|
|
173
|
+
});
|
|
174
|
+
// Persist result
|
|
175
|
+
pulseConfig.requireWorkspace = this.requireFS;
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
isWorkspaceHook(node) {
|
|
179
|
+
return (ts.isCallExpression(node) &&
|
|
180
|
+
ts.isIdentifier(node.expression) &&
|
|
181
|
+
[
|
|
182
|
+
'useFileSystem',
|
|
183
|
+
'useFile',
|
|
184
|
+
'useReceiveFile',
|
|
185
|
+
'useTerminal',
|
|
186
|
+
'useWorkspaceInfo',
|
|
187
|
+
].includes(node.expression.text));
|
|
188
|
+
}
|
|
189
|
+
scanSource(sourceText) {
|
|
190
|
+
const sourceFile = ts.createSourceFile('temp.tsx', sourceText, ts.ScriptTarget.Latest, true);
|
|
191
|
+
const visit = (node) => {
|
|
192
|
+
// Detect: useFileSystem(...)
|
|
193
|
+
if (this.isWorkspaceHook(node)) {
|
|
194
|
+
this.requireFS = true;
|
|
195
|
+
}
|
|
196
|
+
ts.forEachChild(node, visit);
|
|
197
|
+
};
|
|
198
|
+
visit(sourceFile);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// #endregion
|
|
202
|
+
// #region Webpack Configs
|
|
203
|
+
const previewClientConfig = {
|
|
204
|
+
mode: mode,
|
|
205
|
+
entry: {
|
|
206
|
+
main: './node_modules/.pulse/server/preview/frontend/index.js',
|
|
207
|
+
},
|
|
208
|
+
output: {
|
|
209
|
+
path: path.resolve(projectDirName, 'dist/client'),
|
|
210
|
+
},
|
|
211
|
+
resolve: {
|
|
212
|
+
extensions: ['.ts', '.tsx', '.js'],
|
|
213
|
+
},
|
|
214
|
+
plugins: [
|
|
215
|
+
new PulseConfigPlugin(),
|
|
216
|
+
new HtmlWebpackPlugin({
|
|
217
|
+
template: './node_modules/.pulse/server/preview/frontend/index.html',
|
|
218
|
+
}),
|
|
219
|
+
new MiniCssExtractPlugin({
|
|
220
|
+
filename: 'globals.css',
|
|
221
|
+
}),
|
|
222
|
+
new CopyWebpackPlugin({
|
|
223
|
+
patterns: [{ from: 'src/assets', to: 'assets' }],
|
|
224
|
+
}),
|
|
225
|
+
{
|
|
226
|
+
apply: compiler => {
|
|
227
|
+
let isFirstRun = true;
|
|
228
|
+
// Before build starts
|
|
229
|
+
compiler.hooks.watchRun.tap('ReloadMessagePlugin', () => {
|
|
230
|
+
if (!isFirstRun) {
|
|
231
|
+
console.log('[client-preview] 🔄 Reloading app...');
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
console.log('[client-preview] 🔄 Building app...');
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
// After build finishes
|
|
238
|
+
compiler.hooks.done.tap('ReloadMessagePlugin', () => {
|
|
239
|
+
if (isFirstRun) {
|
|
240
|
+
console.log('[client-preview] ✅ Successfully built preview.');
|
|
241
|
+
console.log(previewStartupMessage);
|
|
242
|
+
isFirstRun = false;
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
console.log('[client-preview] ✅ Reload finished');
|
|
246
|
+
}
|
|
247
|
+
// Write pulse config to dist
|
|
248
|
+
fs.writeFileSync(path.resolve(projectDirName, 'dist/client/pulse.config.json'), JSON.stringify(pulseConfig, null, 2));
|
|
249
|
+
fs.writeFileSync(path.resolve(projectDirName, 'dist/server/pulse.config.json'), JSON.stringify(pulseConfig, null, 2));
|
|
250
|
+
});
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
],
|
|
254
|
+
watchOptions: {
|
|
255
|
+
ignored: /src\/server-function/,
|
|
256
|
+
},
|
|
257
|
+
module: {
|
|
258
|
+
rules: [
|
|
259
|
+
{
|
|
260
|
+
test: /\.tsx?$/,
|
|
261
|
+
use: 'ts-loader',
|
|
262
|
+
exclude: [/node_modules/, /dist/],
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
test: /\.css$/i,
|
|
266
|
+
use: [
|
|
267
|
+
MiniCssExtractPlugin.loader,
|
|
268
|
+
'css-loader',
|
|
269
|
+
{
|
|
270
|
+
loader: 'postcss-loader',
|
|
271
|
+
},
|
|
272
|
+
],
|
|
273
|
+
},
|
|
274
|
+
],
|
|
275
|
+
},
|
|
276
|
+
stats: {
|
|
277
|
+
all: false,
|
|
278
|
+
errors: true,
|
|
279
|
+
warnings: true,
|
|
280
|
+
logging: 'warn',
|
|
281
|
+
colors: true,
|
|
282
|
+
},
|
|
283
|
+
infrastructureLogging: {
|
|
284
|
+
level: 'warn',
|
|
285
|
+
},
|
|
286
|
+
};
|
|
287
|
+
const previewHostConfig = {
|
|
288
|
+
mode: mode,
|
|
289
|
+
entry: './node_modules/@pulse-editor/cli/dist/lib/server/preview/backend/index.js',
|
|
290
|
+
target: 'async-node',
|
|
291
|
+
output: {
|
|
292
|
+
publicPath: 'auto',
|
|
293
|
+
library: { type: 'commonjs-module' },
|
|
294
|
+
path: path.resolve(projectDirName, 'dist/preview/backend'),
|
|
295
|
+
filename: 'index.cjs',
|
|
296
|
+
},
|
|
297
|
+
resolve: {
|
|
298
|
+
extensions: ['.ts', '.js'],
|
|
299
|
+
},
|
|
300
|
+
module: {
|
|
301
|
+
rules: [
|
|
302
|
+
{
|
|
303
|
+
test: /\.tsx?$/,
|
|
304
|
+
use: [
|
|
305
|
+
{
|
|
306
|
+
loader: 'ts-loader',
|
|
307
|
+
options: {
|
|
308
|
+
transpileOnly: false, // Enables type-checking and .d.ts file emission
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
],
|
|
312
|
+
exclude: [/node_modules/, /dist/],
|
|
313
|
+
},
|
|
314
|
+
],
|
|
315
|
+
},
|
|
316
|
+
plugins: [
|
|
317
|
+
new NodeFederationPlugin({
|
|
318
|
+
remoteType: 'script',
|
|
319
|
+
name: 'preview_host',
|
|
320
|
+
useRuntimePlugin: true,
|
|
321
|
+
exposes: {},
|
|
322
|
+
}, {}),
|
|
323
|
+
],
|
|
324
|
+
stats: {
|
|
325
|
+
all: false,
|
|
326
|
+
errors: true,
|
|
327
|
+
warnings: true,
|
|
328
|
+
logging: 'warn',
|
|
329
|
+
colors: true,
|
|
330
|
+
},
|
|
331
|
+
infrastructureLogging: {
|
|
332
|
+
level: 'warn',
|
|
333
|
+
},
|
|
334
|
+
};
|
|
335
|
+
const mfClientConfig = {
|
|
336
|
+
mode: mode,
|
|
337
|
+
name: 'client',
|
|
338
|
+
entry: './src/main.tsx',
|
|
339
|
+
output: {
|
|
340
|
+
publicPath: 'auto',
|
|
341
|
+
path: path.resolve(projectDirName, 'dist/client'),
|
|
342
|
+
},
|
|
343
|
+
resolve: {
|
|
344
|
+
extensions: ['.ts', '.tsx', '.js'],
|
|
345
|
+
},
|
|
346
|
+
plugins: [
|
|
347
|
+
new PulseConfigPlugin(),
|
|
348
|
+
new MiniCssExtractPlugin({
|
|
349
|
+
filename: 'globals.css',
|
|
350
|
+
}),
|
|
351
|
+
// Copy assets to dist
|
|
352
|
+
new CopyWebpackPlugin({
|
|
353
|
+
patterns: [{ from: 'src/assets', to: 'assets' }],
|
|
354
|
+
}),
|
|
355
|
+
new ModuleFederationPlugin({
|
|
356
|
+
// Do not use hyphen character '-' in the name
|
|
357
|
+
name: pulseConfig.id,
|
|
358
|
+
filename: 'remoteEntry.js',
|
|
359
|
+
exposes: {
|
|
360
|
+
'./main': './src/main.tsx',
|
|
361
|
+
},
|
|
362
|
+
shared: {
|
|
363
|
+
react: {
|
|
364
|
+
requiredVersion: '19.2.0',
|
|
365
|
+
import: 'react', // the "react" package will be used a provided and fallback module
|
|
366
|
+
shareKey: 'react', // under this name the shared module will be placed in the share scope
|
|
367
|
+
shareScope: 'default', // share scope with this name will be used
|
|
368
|
+
singleton: true, // only a single version of the shared module is allowed
|
|
369
|
+
},
|
|
370
|
+
'react-dom': {
|
|
371
|
+
requiredVersion: '19.2.0',
|
|
372
|
+
singleton: true, // only a single version of the shared module is allowed
|
|
373
|
+
},
|
|
374
|
+
},
|
|
375
|
+
}),
|
|
376
|
+
{
|
|
377
|
+
apply: compiler => {
|
|
378
|
+
if (compiler.options.mode === 'development') {
|
|
379
|
+
let isFirstRun = true;
|
|
380
|
+
// Before build starts
|
|
381
|
+
compiler.hooks.watchRun.tap('ReloadMessagePlugin', () => {
|
|
382
|
+
if (!isFirstRun) {
|
|
383
|
+
console.log('[client] 🔄 reloading app...');
|
|
384
|
+
}
|
|
385
|
+
else {
|
|
386
|
+
console.log('[client] 🔄 building app...');
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
// Log file updates
|
|
390
|
+
compiler.hooks.invalid.tap('LogFileUpdates', (file, changeTime) => {
|
|
391
|
+
console.log(`[watch] change detected in: ${file} at ${new Date(changeTime || Date.now()).toLocaleTimeString()}`);
|
|
392
|
+
});
|
|
393
|
+
// After build finishes
|
|
394
|
+
compiler.hooks.done.tap('ReloadMessagePlugin', () => {
|
|
395
|
+
if (isFirstRun) {
|
|
396
|
+
console.log('[client] ✅ Successfully built client.');
|
|
397
|
+
console.log(devStartupMessage);
|
|
398
|
+
isFirstRun = false;
|
|
399
|
+
}
|
|
400
|
+
else {
|
|
401
|
+
console.log('[client] ✅ Reload finished.');
|
|
402
|
+
}
|
|
403
|
+
// Write pulse config to dist
|
|
404
|
+
fs.writeFileSync(path.resolve(projectDirName, 'dist/client/pulse.config.json'), JSON.stringify(pulseConfig, null, 2));
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
else {
|
|
408
|
+
// Print build success/failed message
|
|
409
|
+
compiler.hooks.done.tap('BuildMessagePlugin', stats => {
|
|
410
|
+
if (stats.hasErrors()) {
|
|
411
|
+
console.log(`[client] ❌ Failed to build client.`);
|
|
412
|
+
}
|
|
413
|
+
else {
|
|
414
|
+
console.log(`[client] ✅ Successfully built client.`);
|
|
415
|
+
// Write pulse config to dist
|
|
416
|
+
fs.writeFileSync(path.resolve(projectDirName, 'dist/client/pulse.config.json'), JSON.stringify(pulseConfig, null, 2));
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
},
|
|
421
|
+
},
|
|
422
|
+
],
|
|
423
|
+
module: {
|
|
424
|
+
rules: [
|
|
425
|
+
{
|
|
426
|
+
test: /\.tsx?$/,
|
|
427
|
+
use: 'ts-loader',
|
|
428
|
+
exclude: [/node_modules/, /dist/],
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
test: /\.css$/i,
|
|
432
|
+
use: [
|
|
433
|
+
MiniCssExtractPlugin.loader,
|
|
434
|
+
'css-loader',
|
|
435
|
+
{
|
|
436
|
+
loader: 'postcss-loader',
|
|
437
|
+
},
|
|
438
|
+
],
|
|
439
|
+
exclude: [/dist/],
|
|
440
|
+
},
|
|
441
|
+
],
|
|
442
|
+
},
|
|
443
|
+
stats: {
|
|
444
|
+
all: false,
|
|
445
|
+
errors: true,
|
|
446
|
+
warnings: true,
|
|
447
|
+
logging: 'warn',
|
|
448
|
+
colors: true,
|
|
449
|
+
assets: false,
|
|
450
|
+
},
|
|
451
|
+
infrastructureLogging: {
|
|
452
|
+
level: 'warn',
|
|
453
|
+
},
|
|
454
|
+
};
|
|
455
|
+
const mfServerConfig = {
|
|
456
|
+
mode: mode,
|
|
457
|
+
name: 'server',
|
|
458
|
+
entry: {},
|
|
459
|
+
target: 'async-node',
|
|
460
|
+
output: {
|
|
461
|
+
publicPath: 'auto',
|
|
462
|
+
path: path.resolve(projectDirName, 'dist/server'),
|
|
463
|
+
},
|
|
464
|
+
resolve: {
|
|
465
|
+
extensions: ['.ts', '.js'],
|
|
466
|
+
},
|
|
467
|
+
plugins: [
|
|
468
|
+
{
|
|
469
|
+
apply: compiler => {
|
|
470
|
+
if (compiler.options.mode === 'development') {
|
|
471
|
+
let isFirstRun = true;
|
|
472
|
+
// Before build starts
|
|
473
|
+
compiler.hooks.watchRun.tap('ReloadMessagePlugin', () => {
|
|
474
|
+
if (!isFirstRun) {
|
|
475
|
+
console.log(`[Server] 🔄 Reloading app...`);
|
|
476
|
+
}
|
|
477
|
+
else {
|
|
478
|
+
console.log(`[Server] 🔄 Building app...`);
|
|
479
|
+
}
|
|
480
|
+
compileServerFunctions(compiler);
|
|
481
|
+
});
|
|
482
|
+
// After build finishes
|
|
483
|
+
compiler.hooks.done.tap('ReloadMessagePlugin', () => {
|
|
484
|
+
if (isFirstRun) {
|
|
485
|
+
console.log(`[Server] ✅ Successfully built server.`);
|
|
486
|
+
isFirstRun = false;
|
|
487
|
+
}
|
|
488
|
+
else {
|
|
489
|
+
console.log(`[Server] ✅ Reload finished.`);
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
// Watch for changes in the server-function directory to trigger rebuilds
|
|
493
|
+
compiler.hooks.thisCompilation.tap('WatchServerFunctions', compilation => {
|
|
494
|
+
compilation.contextDependencies.add(path.resolve(projectDirName, 'src/server-function'));
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
else {
|
|
498
|
+
// Print build success/failed message
|
|
499
|
+
compiler.hooks.done.tap('BuildMessagePlugin', stats => {
|
|
500
|
+
if (stats.hasErrors()) {
|
|
501
|
+
console.log(`[Server] ❌ Failed to build server.`);
|
|
502
|
+
}
|
|
503
|
+
else {
|
|
504
|
+
compileServerFunctions(compiler);
|
|
505
|
+
console.log(`[Server] ✅ Successfully built server.`);
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
},
|
|
510
|
+
},
|
|
511
|
+
],
|
|
512
|
+
module: {
|
|
513
|
+
rules: [
|
|
514
|
+
{
|
|
515
|
+
test: /\.tsx?$/,
|
|
516
|
+
use: 'ts-loader',
|
|
517
|
+
exclude: [/node_modules/, /dist/],
|
|
518
|
+
},
|
|
519
|
+
],
|
|
520
|
+
},
|
|
521
|
+
stats: {
|
|
522
|
+
all: false,
|
|
523
|
+
errors: true,
|
|
524
|
+
warnings: true,
|
|
525
|
+
logging: 'warn',
|
|
526
|
+
colors: true,
|
|
527
|
+
},
|
|
528
|
+
infrastructureLogging: {
|
|
529
|
+
level: 'warn',
|
|
530
|
+
},
|
|
531
|
+
};
|
|
532
|
+
// #endregion
|
|
533
|
+
if (isPreview) {
|
|
534
|
+
return [previewClientConfig, previewHostConfig, mfServerConfig];
|
|
535
|
+
}
|
|
536
|
+
else if (buildTarget === 'server') {
|
|
537
|
+
return [mfServerConfig];
|
|
538
|
+
}
|
|
539
|
+
else if (buildTarget === 'client') {
|
|
540
|
+
return [mfClientConfig];
|
|
541
|
+
}
|
|
542
|
+
else {
|
|
543
|
+
return [mfClientConfig, mfServerConfig];
|
|
544
|
+
}
|
|
545
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pulse-editor/cli",
|
|
3
|
-
"version": "0.1.1-beta.
|
|
3
|
+
"version": "0.1.1-beta.18",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"bin": {
|
|
6
6
|
"pulse": "dist/cli.js"
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
},
|
|
12
12
|
"scripts": {
|
|
13
13
|
"build": "tsx build.ts",
|
|
14
|
-
"dev": "tsx watch --include \"./
|
|
14
|
+
"dev": "tsx watch --include \"./src/**/*\" build.ts",
|
|
15
15
|
"test": "prettier --check . && xo && ava",
|
|
16
16
|
"link": "npm link"
|
|
17
17
|
},
|
|
@@ -19,6 +19,10 @@
|
|
|
19
19
|
"dist"
|
|
20
20
|
],
|
|
21
21
|
"dependencies": {
|
|
22
|
+
"@module-federation/enhanced": "0.21.6",
|
|
23
|
+
"@module-federation/node": "^2.7.25",
|
|
24
|
+
"@module-federation/runtime": "0.21.6",
|
|
25
|
+
"@pulse-editor/shared-utils": "^0.1.1-beta.67",
|
|
22
26
|
"concurrently": "^9.2.1",
|
|
23
27
|
"connect-livereload": "^0.6.1",
|
|
24
28
|
"cors": "^2.8.5",
|
|
@@ -34,12 +38,10 @@
|
|
|
34
38
|
"meow": "^14.0.0",
|
|
35
39
|
"openid-client": "^6.8.1",
|
|
36
40
|
"rimraf": "^6.1.2",
|
|
37
|
-
"tsx": "^4.21.0"
|
|
41
|
+
"tsx": "^4.21.0",
|
|
42
|
+
"webpack": "^5.104.0"
|
|
38
43
|
},
|
|
39
44
|
"devDependencies": {
|
|
40
|
-
"@module-federation/enhanced": "0.21.6",
|
|
41
|
-
"@module-federation/node": "2.7.25",
|
|
42
|
-
"@module-federation/runtime": "0.21.6",
|
|
43
45
|
"@sindresorhus/tsconfig": "^8.1.0",
|
|
44
46
|
"@types/connect-livereload": "^0.6.3",
|
|
45
47
|
"@types/cors": "^2.8.19",
|
|
@@ -50,20 +52,25 @@
|
|
|
50
52
|
"@vdemedes/prettier-config": "^2.0.1",
|
|
51
53
|
"ava": "^6.4.1",
|
|
52
54
|
"chalk": "^5.6.2",
|
|
55
|
+
"copy-webpack-plugin": "^13.0.1",
|
|
53
56
|
"eslint-config-xo-react": "^0.29.0",
|
|
54
57
|
"eslint-plugin-react": "^7.37.5",
|
|
55
58
|
"eslint-plugin-react-hooks": "^7.0.1",
|
|
59
|
+
"glob": "^13.0.0",
|
|
60
|
+
"html-webpack-plugin": "^5.6.5",
|
|
56
61
|
"ink-testing-library": "^4.0.0",
|
|
62
|
+
"mini-css-extract-plugin": "^2.9.4",
|
|
57
63
|
"prettier": "^3.7.4",
|
|
58
|
-
"react": "19.2.
|
|
59
|
-
"react-dom": "19.2.
|
|
64
|
+
"react": "19.2.3",
|
|
65
|
+
"react-dom": "19.2.3",
|
|
60
66
|
"ts-node": "^10.9.2",
|
|
61
67
|
"typescript": "^5.9.3",
|
|
68
|
+
"webpack-dev-server": "^5.2.2",
|
|
62
69
|
"xo": "^1.2.3"
|
|
63
70
|
},
|
|
64
71
|
"peerDependencies": {
|
|
65
|
-
"react": "19.2.
|
|
66
|
-
"react-dom": "19.2.
|
|
72
|
+
"react": "19.2.3",
|
|
73
|
+
"react-dom": "19.2.3"
|
|
67
74
|
},
|
|
68
75
|
"ava": {
|
|
69
76
|
"extensions": {
|
|
@@ -82,4 +89,4 @@
|
|
|
82
89
|
}
|
|
83
90
|
},
|
|
84
91
|
"prettier": "@vdemedes/prettier-config"
|
|
85
|
-
}
|
|
92
|
+
}
|