@pulse-editor/cli 0.1.1-beta.0 → 0.1.1-beta.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 +7 -2
- package/dist/components/commands/build.d.ts +5 -0
- package/dist/components/commands/build.js +77 -0
- package/dist/components/commands/clean.d.ts +5 -0
- package/dist/components/commands/clean.js +10 -0
- package/dist/components/commands/create.js +8 -4
- package/dist/components/commands/dev.d.ts +5 -0
- package/dist/components/commands/dev.js +49 -0
- package/dist/components/commands/login.js +5 -5
- package/dist/components/commands/logout.js +1 -1
- package/dist/components/commands/preview copy.d.ts +5 -0
- package/dist/components/commands/preview copy.js +14 -0
- package/dist/components/commands/preview.d.ts +5 -0
- package/dist/components/commands/preview.js +52 -0
- package/dist/components/commands/publish.js +4 -4
- package/dist/components/commands/start copy.d.ts +5 -0
- package/dist/components/commands/start copy.js +14 -0
- package/dist/components/commands/start.d.ts +5 -0
- package/dist/components/commands/start.js +20 -0
- package/dist/lib/cli-flags.d.ts +5 -1
- package/dist/lib/cli-flags.js +5 -1
- package/dist/lib/deps.d.ts +1 -0
- package/dist/lib/deps.js +5 -0
- package/dist/lib/execa-utils/clean.d.ts +1 -0
- package/dist/lib/execa-utils/clean.js +9 -0
- package/dist/lib/execa-utils/deps.d.ts +1 -0
- package/dist/lib/execa-utils/deps.js +5 -0
- package/dist/lib/node_module_bin.d.ts +1 -0
- package/dist/lib/node_module_bin.js +7 -0
- package/dist/lib/server/express.d.ts +4 -0
- package/dist/lib/server/express.js +118 -0
- package/dist/lib/server/preview/backend/index.d.ts +1 -0
- package/dist/lib/server/preview/backend/index.js +23 -0
- package/dist/lib/server/preview/backend/load-remote.cjs +23 -0
- package/dist/lib/server/preview/frontend/index.d.ts +1 -0
- package/dist/lib/server/preview/frontend/index.html +11 -0
- package/dist/lib/server/preview/frontend/index.js +10 -0
- package/dist/lib/server/utils.d.ts +1 -0
- package/dist/lib/server/utils.js +17 -0
- package/package.json +29 -14
package/dist/app.js
CHANGED
|
@@ -7,9 +7,14 @@ import Help from './components/commands/help.js';
|
|
|
7
7
|
import Chat from './components/commands/chat.js';
|
|
8
8
|
import Logout from './components/commands/logout.js';
|
|
9
9
|
import Create from './components/commands/create.js';
|
|
10
|
+
import Dev from './components/commands/dev.js';
|
|
11
|
+
import Build from './components/commands/build.js';
|
|
12
|
+
import Preview from './components/commands/preview.js';
|
|
13
|
+
import Start from './components/commands/start.js';
|
|
14
|
+
import Clean from './components/commands/clean.js';
|
|
10
15
|
export default function App({ cli }) {
|
|
11
16
|
const [command, setCommand] = useState(undefined);
|
|
12
|
-
if (cli.flags.
|
|
17
|
+
if (cli.flags.stage) {
|
|
13
18
|
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';
|
|
14
19
|
}
|
|
15
20
|
else {
|
|
@@ -19,5 +24,5 @@ export default function App({ cli }) {
|
|
|
19
24
|
const cmd = cli.input[0] ?? 'help';
|
|
20
25
|
setCommand(cmd);
|
|
21
26
|
}, [cli.input]);
|
|
22
|
-
return (_jsxs(_Fragment, { children: [cli.flags.
|
|
27
|
+
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 !== 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."] })] })))] }));
|
|
23
28
|
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect } from 'react';
|
|
3
|
+
import { execa } from 'execa';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import { cleanDist } from '../../lib/execa-utils/clean.js';
|
|
6
|
+
export default function Build({ cli }) {
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
async function buildProd() {
|
|
9
|
+
const buildTarget = cli.flags.target;
|
|
10
|
+
console.log(`🚧 Building for target: ${buildTarget || 'both client and server'}`);
|
|
11
|
+
// Move node_modules/@pulse-editor/cli/dist/lib/server to node_modules/.pulse/server
|
|
12
|
+
if (buildTarget === 'server' || !buildTarget) {
|
|
13
|
+
if (fs.existsSync('node_modules/.pulse/server')) {
|
|
14
|
+
// Remove existing directory
|
|
15
|
+
if (process.platform === 'win32') {
|
|
16
|
+
await execa('rmdir /S /Q node_modules\\.pulse\\server', {
|
|
17
|
+
shell: true,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
await execa('rm -rf node_modules/.pulse/server', {
|
|
22
|
+
shell: true,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (process.platform === 'win32') {
|
|
27
|
+
await execa('xcopy /E /I node_modules\\@pulse-editor\\cli\\dist\\lib\\server node_modules\\.pulse\\server', {
|
|
28
|
+
shell: true,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
await execa('cp -r node_modules/@pulse-editor/cli/dist/lib/server/* node_modules/.pulse/server/', {
|
|
33
|
+
shell: true,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
await cleanDist();
|
|
38
|
+
if (buildTarget === undefined) {
|
|
39
|
+
// Start building
|
|
40
|
+
await execa('npx webpack --mode production', {
|
|
41
|
+
stdio: 'inherit',
|
|
42
|
+
shell: true,
|
|
43
|
+
env: {
|
|
44
|
+
NODE_OPTIONS: '--import=tsx',
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
else if (buildTarget === 'client') {
|
|
49
|
+
// Start building client only
|
|
50
|
+
await execa('npx webpack --mode production', {
|
|
51
|
+
stdio: 'inherit',
|
|
52
|
+
shell: true,
|
|
53
|
+
env: {
|
|
54
|
+
NODE_OPTIONS: '--import=tsx',
|
|
55
|
+
BUILD_TARGET: 'client',
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
else if (buildTarget === 'server') {
|
|
60
|
+
// Start building server only
|
|
61
|
+
await execa('npx webpack --mode production', {
|
|
62
|
+
stdio: 'inherit',
|
|
63
|
+
shell: true,
|
|
64
|
+
env: {
|
|
65
|
+
NODE_OPTIONS: '--import=tsx',
|
|
66
|
+
BUILD_TARGET: 'server',
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
console.error(`❌ Unknown build target: ${buildTarget}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
buildProd();
|
|
75
|
+
}, []);
|
|
76
|
+
return _jsx(_Fragment, {});
|
|
77
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect } from 'react';
|
|
3
|
+
import { cleanDist } from '../../lib/execa-utils/clean.js';
|
|
4
|
+
export default function Clean({ cli }) {
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
// clean code
|
|
7
|
+
cleanDist();
|
|
8
|
+
}, []);
|
|
9
|
+
return _jsx(_Fragment, {});
|
|
10
|
+
}
|
|
@@ -93,13 +93,17 @@ export default function Create({ cli }) {
|
|
|
93
93
|
}
|
|
94
94
|
// Modify the package.json file to update the name
|
|
95
95
|
setCreateMessage(_jsxs(Box, { children: [_jsx(Spinner, { type: "dots" }), _jsx(Text, { children: " Initializing project..." })] }));
|
|
96
|
+
/* Setup pulse.config.ts */
|
|
97
|
+
const pulseConfigPath = path.join(process.cwd(), name, 'pulse.config.ts');
|
|
98
|
+
let pulseConfig = fs.readFileSync(pulseConfigPath, 'utf8');
|
|
99
|
+
// Modify visibility by matching the block that starts with 'visibility:',
|
|
100
|
+
// and replacing the entire line with the new visibility value.
|
|
101
|
+
pulseConfig = pulseConfig.replace(/visibility:\s*['"`](public|unlisted|private)['"`],?/, `visibility: '${visibility}',`);
|
|
102
|
+
fs.writeFileSync(pulseConfigPath, pulseConfig);
|
|
103
|
+
/* Setup packages.json */
|
|
96
104
|
const packageJsonPath = path.join(process.cwd(), name, 'package.json');
|
|
97
105
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
98
106
|
packageJson.name = name.replaceAll('-', '_');
|
|
99
|
-
// Modify the visibility
|
|
100
|
-
packageJson['pulse-editor-marketplace'] = {
|
|
101
|
-
visibility,
|
|
102
|
-
};
|
|
103
107
|
// Write the modified package.json back to the file
|
|
104
108
|
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
105
109
|
// Remove the .git directory
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Text } from 'ink';
|
|
3
|
+
import { useEffect } from 'react';
|
|
4
|
+
import { execa } from 'execa';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import { getDepsBinPath } from '../../lib/execa-utils/deps.js';
|
|
7
|
+
import { cleanDist } from '../../lib/execa-utils/clean.js';
|
|
8
|
+
export default function Dev({ cli }) {
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
async function startDevServer() {
|
|
11
|
+
// Move node_modules/@pulse-editor/cli/dist/lib/server to node_modules/.pulse/server
|
|
12
|
+
if (fs.existsSync('node_modules/.pulse/server')) {
|
|
13
|
+
// Remove existing directory
|
|
14
|
+
if (process.platform === 'win32') {
|
|
15
|
+
await execa('rmdir /S /Q node_modules\\.pulse\\server', {
|
|
16
|
+
shell: true,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
await execa('rm -rf node_modules/.pulse/server', {
|
|
21
|
+
shell: true,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (process.platform === 'win32') {
|
|
26
|
+
await execa('xcopy /E /I node_modules\\@pulse-editor\\cli\\dist\\lib\\server node_modules\\.pulse\\server', {
|
|
27
|
+
shell: true,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
await execa('cp -r node_modules/@pulse-editor/cli/dist/lib/server/* node_modules/.pulse/server/', {
|
|
32
|
+
shell: true,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
// Start dev server
|
|
36
|
+
await cleanDist();
|
|
37
|
+
await execa(`${getDepsBinPath('concurrently')} --prefix none "webpack --mode development --watch" "tsx watch --clear-screen=false node_modules/@pulse-editor/cli/dist/lib/server/express.js"`, {
|
|
38
|
+
stdio: 'inherit',
|
|
39
|
+
shell: true,
|
|
40
|
+
env: {
|
|
41
|
+
NODE_OPTIONS: '--import=tsx',
|
|
42
|
+
NODE_ENV: 'development',
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
startDevServer();
|
|
47
|
+
}, []);
|
|
48
|
+
return (_jsx(_Fragment, { children: _jsx(Text, { children: "Starting dev server..." }) }));
|
|
49
|
+
}
|
|
@@ -30,7 +30,7 @@ export default function Login({ cli }) {
|
|
|
30
30
|
const { exit } = useApp();
|
|
31
31
|
// Check login method
|
|
32
32
|
useEffect(() => {
|
|
33
|
-
const savedToken = getToken(cli.flags.
|
|
33
|
+
const savedToken = getToken(cli.flags.stage);
|
|
34
34
|
setIsShowLoginMethod(!savedToken && !cli.flags.token && !cli.flags.flow);
|
|
35
35
|
if (savedToken) {
|
|
36
36
|
setLoginMethod('token');
|
|
@@ -48,7 +48,7 @@ export default function Login({ cli }) {
|
|
|
48
48
|
useEffect(() => {
|
|
49
49
|
// Only check token validity when it is set
|
|
50
50
|
if (loginMethod === 'token' && token.length > 0) {
|
|
51
|
-
checkToken(token, cli.flags.
|
|
51
|
+
checkToken(token, cli.flags.stage).then(isValid => {
|
|
52
52
|
setIsAuthenticated(isValid);
|
|
53
53
|
setIsCheckingAuth(false);
|
|
54
54
|
});
|
|
@@ -68,13 +68,13 @@ export default function Login({ cli }) {
|
|
|
68
68
|
return;
|
|
69
69
|
}
|
|
70
70
|
setToken(value);
|
|
71
|
-
} })] })) : isCheckingAuth ? (_jsxs(Box, { children: [_jsx(Spinner, { type: "dots" }), _jsx(Text, { children: " Checking authentication..." })] })) : isAuthenticated ? (_jsxs(_Fragment, { children: [_jsx(Text, { children: "\u2705 You are signed in successfully." }), !isTokenInEnv(cli.flags.
|
|
72
|
-
getToken(cli.flags.
|
|
71
|
+
} })] })) : isCheckingAuth ? (_jsxs(Box, { children: [_jsx(Spinner, { type: "dots" }), _jsx(Text, { children: " Checking authentication..." })] })) : isAuthenticated ? (_jsxs(_Fragment, { children: [_jsx(Text, { children: "\u2705 You are signed in successfully." }), !isTokenInEnv(cli.flags.stage) &&
|
|
72
|
+
getToken(cli.flags.stage) !== token && (_jsxs(_Fragment, { children: [_jsx(Text, { children: "\uD83D\uDFE2 It is recommended to save your access token as an environment variable PE_ACCESS_TOKEN." }), _jsxs(Box, { children: [_jsxs(Text, { children: ["\u26A0\uFE0F (NOT recommended) Or, do you want to save access token to user home directory? (y/n)", ' '] }), _jsx(TextInput, { value: saveTokenInput, onChange: setSaveTokenInput, onSubmit: value => {
|
|
73
73
|
if (value.length === 0) {
|
|
74
74
|
return;
|
|
75
75
|
}
|
|
76
76
|
if (value === 'y') {
|
|
77
|
-
saveToken(token, cli.flags.
|
|
77
|
+
saveToken(token, cli.flags.stage);
|
|
78
78
|
setIsTokenSaved(true);
|
|
79
79
|
setTimeout(() => {
|
|
80
80
|
exit();
|
|
@@ -6,7 +6,7 @@ import Spinner from 'ink-spinner';
|
|
|
6
6
|
export default function Logout({ cli }) {
|
|
7
7
|
const [isLoggedOut, setIsLoggedOut] = useState(false);
|
|
8
8
|
useEffect(() => {
|
|
9
|
-
saveToken(undefined, cli.flags.
|
|
9
|
+
saveToken(undefined, cli.flags.stage);
|
|
10
10
|
setIsLoggedOut(true);
|
|
11
11
|
}, []);
|
|
12
12
|
return (_jsx(_Fragment, { children: isLoggedOut ? (_jsx(Text, { children: "\uD83D\uDE80 Successfully logged out!" })) : (_jsxs(Box, { children: [_jsx(Spinner, { type: "dots" }), _jsx(Text, { children: " Logging out..." })] })) }));
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Text } from 'ink';
|
|
3
|
+
import { useEffect } from 'react';
|
|
4
|
+
import { execa } from 'execa';
|
|
5
|
+
export default function Preview({ cli }) {
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
// Start dev server
|
|
8
|
+
execa('npm run preview', {
|
|
9
|
+
stdio: 'inherit',
|
|
10
|
+
shell: true,
|
|
11
|
+
});
|
|
12
|
+
}, []);
|
|
13
|
+
return (_jsx(_Fragment, { children: _jsx(Text, { children: "Starting preview server..." }) }));
|
|
14
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Text } from 'ink';
|
|
3
|
+
import { useEffect } from 'react';
|
|
4
|
+
import { execa } from 'execa';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import { getDepsBinPath } from '../../lib/execa-utils/deps.js';
|
|
7
|
+
import { cleanDist } from '../../lib/execa-utils/clean.js';
|
|
8
|
+
export default function Preview({ cli }) {
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
async function startPreviewServer() {
|
|
11
|
+
// Move node_modules/@pulse-editor/cli/dist/lib/server to node_modules/.pulse/server
|
|
12
|
+
if (fs.existsSync('node_modules/.pulse/server')) {
|
|
13
|
+
// Remove existing directory
|
|
14
|
+
if (process.platform === 'win32') {
|
|
15
|
+
await execa('rmdir /S /Q node_modules\\.pulse\\server', {
|
|
16
|
+
shell: true,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
await execa('rm -rf node_modules/.pulse/server', {
|
|
21
|
+
shell: true,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
await execa('mkdir "node_modules/.pulse/server"', {
|
|
26
|
+
shell: true,
|
|
27
|
+
});
|
|
28
|
+
if (process.platform === 'win32') {
|
|
29
|
+
await execa('xcopy /E /I node_modules\\@pulse-editor\\cli\\dist\\lib\\server node_modules\\.pulse\\server', {
|
|
30
|
+
shell: true,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
await execa('cp -r node_modules/@pulse-editor/cli/dist/lib/server/* node_modules/.pulse/server/', {
|
|
35
|
+
shell: true,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
// Start preview server
|
|
39
|
+
await cleanDist();
|
|
40
|
+
await execa(`${getDepsBinPath('concurrently')} --prefix none "npx webpack --mode development --watch" "tsx watch --clear-screen=false node_modules/@pulse-editor/cli/dist/lib/server/express.js"`, {
|
|
41
|
+
stdio: 'inherit',
|
|
42
|
+
shell: true,
|
|
43
|
+
env: {
|
|
44
|
+
NODE_OPTIONS: '--import=tsx',
|
|
45
|
+
PREVIEW: 'true',
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
startPreviewServer();
|
|
50
|
+
}, []);
|
|
51
|
+
return (_jsx(_Fragment, { children: _jsx(Text, { children: "Starting preview server..." }) }));
|
|
52
|
+
}
|
|
@@ -29,9 +29,9 @@ export default function Publish({ cli }) {
|
|
|
29
29
|
// Check if the user is authenticated
|
|
30
30
|
useEffect(() => {
|
|
31
31
|
async function checkAuth() {
|
|
32
|
-
const token = getToken(cli.flags.
|
|
32
|
+
const token = getToken(cli.flags.stage);
|
|
33
33
|
if (token) {
|
|
34
|
-
const isValid = await checkToken(token, cli.flags.
|
|
34
|
+
const isValid = await checkToken(token, cli.flags.stage);
|
|
35
35
|
if (isValid) {
|
|
36
36
|
setIsAuthenticated(true);
|
|
37
37
|
}
|
|
@@ -86,12 +86,12 @@ export default function Publish({ cli }) {
|
|
|
86
86
|
formData.append('file', blob, 'dist.zip');
|
|
87
87
|
formData.append('visibility', visibility);
|
|
88
88
|
// Send the file to the server
|
|
89
|
-
const res = await fetch(cli.flags.
|
|
89
|
+
const res = await fetch(cli.flags.stage
|
|
90
90
|
? 'https://localhost:8080/api/app/publish'
|
|
91
91
|
: 'https://pulse-editor.com/api/app/publish', {
|
|
92
92
|
method: 'POST',
|
|
93
93
|
headers: {
|
|
94
|
-
Authorization: `Bearer ${getToken(cli.flags.
|
|
94
|
+
Authorization: `Bearer ${getToken(cli.flags.stage)}`,
|
|
95
95
|
},
|
|
96
96
|
body: formData,
|
|
97
97
|
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Text } from 'ink';
|
|
3
|
+
import { useEffect } from 'react';
|
|
4
|
+
import { execa } from 'execa';
|
|
5
|
+
export default function Start({ cli }) {
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
// Start dev server
|
|
8
|
+
execa('npm run start', {
|
|
9
|
+
stdio: 'inherit',
|
|
10
|
+
shell: true,
|
|
11
|
+
});
|
|
12
|
+
}, []);
|
|
13
|
+
return (_jsx(_Fragment, { children: _jsx(Text, { children: "Starting prod server..." }) }));
|
|
14
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Text } from 'ink';
|
|
3
|
+
import { useEffect } from 'react';
|
|
4
|
+
import { execa } from 'execa';
|
|
5
|
+
export default function Start({ cli }) {
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
async function startProdServer() {
|
|
8
|
+
// Start prod server
|
|
9
|
+
await execa('tsx node_modules/@pulse-editor/cli/dist/lib/server/express.js', {
|
|
10
|
+
stdio: 'inherit',
|
|
11
|
+
shell: true,
|
|
12
|
+
env: {
|
|
13
|
+
NODE_OPTIONS: '--import=tsx',
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
startProdServer();
|
|
18
|
+
}, []);
|
|
19
|
+
return (_jsx(_Fragment, { children: _jsx(Text, { children: "Starting prod server..." }) }));
|
|
20
|
+
}
|
package/dist/lib/cli-flags.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export declare const flags: {
|
|
|
9
9
|
type: "string";
|
|
10
10
|
shortFlag: string;
|
|
11
11
|
};
|
|
12
|
-
|
|
12
|
+
stage: {
|
|
13
13
|
type: "boolean";
|
|
14
14
|
default: false;
|
|
15
15
|
};
|
|
@@ -21,5 +21,9 @@ export declare const flags: {
|
|
|
21
21
|
type: "string";
|
|
22
22
|
shortFlag: string;
|
|
23
23
|
};
|
|
24
|
+
target: {
|
|
25
|
+
type: "string";
|
|
26
|
+
shortFlag: string;
|
|
27
|
+
};
|
|
24
28
|
};
|
|
25
29
|
export type Flags = typeof flags;
|
package/dist/lib/cli-flags.js
CHANGED
|
@@ -13,7 +13,7 @@ export const flags = defineFlags({
|
|
|
13
13
|
type: 'string',
|
|
14
14
|
shortFlag: 'f',
|
|
15
15
|
},
|
|
16
|
-
|
|
16
|
+
stage: {
|
|
17
17
|
type: 'boolean',
|
|
18
18
|
default: false,
|
|
19
19
|
},
|
|
@@ -25,4 +25,8 @@ export const flags = defineFlags({
|
|
|
25
25
|
type: 'string',
|
|
26
26
|
shortFlag: 'v',
|
|
27
27
|
},
|
|
28
|
+
target: {
|
|
29
|
+
type: 'string',
|
|
30
|
+
shortFlag: 't',
|
|
31
|
+
},
|
|
28
32
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getDepsBinPath(name: string): string;
|
package/dist/lib/deps.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// Get deps bin at node_modules/@pulse-editor/cli/node_modules
|
|
2
|
+
export function getDepsBinPath(name) {
|
|
3
|
+
const workingDir = process.cwd();
|
|
4
|
+
return `${workingDir}/node_modules/@pulse-editor/cli/node_modules/.bin/${process.platform === 'win32' ? `${name}.cmd` : name}`;
|
|
5
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function cleanDist(): Promise<void>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { execa } from 'execa';
|
|
2
|
+
import { getDepsBinPath } from './deps.js';
|
|
3
|
+
export async function cleanDist() {
|
|
4
|
+
console.log('♻️ Cleaning dist directory...');
|
|
5
|
+
await execa(`${getDepsBinPath('rimraf')} dist`, {
|
|
6
|
+
shell: true,
|
|
7
|
+
});
|
|
8
|
+
console.log('✅ Cleaned dist directory.');
|
|
9
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getDepsBinPath(name: string): string;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// Get deps bin at node_modules/@pulse-editor/cli/node_modules
|
|
2
|
+
export function getDepsBinPath(name) {
|
|
3
|
+
const workingDir = process.cwd();
|
|
4
|
+
return `${workingDir}/node_modules/@pulse-editor/cli/node_modules/.bin/${process.platform === 'win32' ? `${name}.cmd` : name}`;
|
|
5
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function bin(name: string): string;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
4
|
+
const __dirname = path.dirname(__filename);
|
|
5
|
+
export function bin(name) {
|
|
6
|
+
return path.join(__dirname, 'node_modules', '.bin', process.platform === 'win32' ? `${name}.cmd` : name);
|
|
7
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This is a local dev server for "npm run dev" and "npm run preview".
|
|
3
|
+
*/
|
|
4
|
+
import express from 'express';
|
|
5
|
+
import cors from 'cors';
|
|
6
|
+
import dotenv from 'dotenv';
|
|
7
|
+
import livereload from 'livereload';
|
|
8
|
+
import connectLivereload from 'connect-livereload';
|
|
9
|
+
import { networkInterfaces } from 'os';
|
|
10
|
+
import { pipeline, Readable } from 'stream';
|
|
11
|
+
import { promisify } from 'util';
|
|
12
|
+
import { readConfigFile } from './utils.js';
|
|
13
|
+
import path from 'path';
|
|
14
|
+
import { pathToFileURL } from 'url';
|
|
15
|
+
dotenv.config({
|
|
16
|
+
quiet: true,
|
|
17
|
+
});
|
|
18
|
+
const isPreview = process.env?.['PREVIEW'];
|
|
19
|
+
const isDev = process.env?.['NODE_ENV'];
|
|
20
|
+
const workspaceId = process.env?.['WORKSPACE_ID'];
|
|
21
|
+
const pulseConfig = await readConfigFile();
|
|
22
|
+
if (isDev || isPreview) {
|
|
23
|
+
const livereloadServer = livereload.createServer({
|
|
24
|
+
// @ts-expect-error override server options
|
|
25
|
+
host: '0.0.0.0',
|
|
26
|
+
});
|
|
27
|
+
livereloadServer.watch('dist');
|
|
28
|
+
livereloadServer.server.once('connection', () => {
|
|
29
|
+
console.log('✅ LiveReload connected');
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
const app = express();
|
|
33
|
+
app.use(cors());
|
|
34
|
+
// Inject the client-side livereload script into HTML responses
|
|
35
|
+
app.use(
|
|
36
|
+
// The port might not be right here for the ingress.
|
|
37
|
+
// I need this route to be exposed
|
|
38
|
+
connectLivereload({
|
|
39
|
+
// @ts-expect-error override server options
|
|
40
|
+
host: workspaceId
|
|
41
|
+
? `${workspaceId}.workspace.pulse-editor.com"`
|
|
42
|
+
: undefined,
|
|
43
|
+
port: workspaceId ? 443 : 35729,
|
|
44
|
+
}));
|
|
45
|
+
app.use(express.json());
|
|
46
|
+
// Log each request to the console
|
|
47
|
+
app.use((req, res, next) => {
|
|
48
|
+
console.log(`✅ [${req.method}] Received: ${req.url}`);
|
|
49
|
+
return next();
|
|
50
|
+
});
|
|
51
|
+
// Serve backend
|
|
52
|
+
app.use(`/${pulseConfig.id}/${pulseConfig.version}/server`, express.static('dist/server'));
|
|
53
|
+
// Catch backend function calls
|
|
54
|
+
app.all(/^\/server-function\/(.*)/, async (req, res) => {
|
|
55
|
+
const func = req.params[0];
|
|
56
|
+
const url = `${req.protocol}://${req.get('host')}${req.originalUrl}`;
|
|
57
|
+
// Convert Express req -> Fetch Request
|
|
58
|
+
const request = new Request(url, {
|
|
59
|
+
method: req.method,
|
|
60
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
61
|
+
headers: req.headers,
|
|
62
|
+
body: ['GET', 'HEAD'].includes(req.method)
|
|
63
|
+
? null
|
|
64
|
+
: JSON.stringify(req.body),
|
|
65
|
+
});
|
|
66
|
+
const dir = path.resolve('node_modules/@pulse-editor/cli/dist/lib/server/preview/backend/load-remote.cjs');
|
|
67
|
+
const fileUrl = pathToFileURL(dir).href;
|
|
68
|
+
const { loadAndCall } = await import(fileUrl);
|
|
69
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
70
|
+
const response = await loadAndCall(func, request, pulseConfig.id, 'http://localhost:3030', pulseConfig.version);
|
|
71
|
+
const streamPipeline = promisify(pipeline);
|
|
72
|
+
// If loadAndCall returns a Response (Fetch API Response)
|
|
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);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
res.end();
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
if (isPreview) {
|
|
84
|
+
/* Preview mode */
|
|
85
|
+
app.use(express.static('dist/client'));
|
|
86
|
+
app.listen(3030, '0.0.0.0');
|
|
87
|
+
}
|
|
88
|
+
else if (isDev) {
|
|
89
|
+
/* Dev mode */
|
|
90
|
+
app.use(`/${pulseConfig.id}/${pulseConfig.version}`, express.static('dist'));
|
|
91
|
+
app.listen(3030, '0.0.0.0');
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
/* Production mode */
|
|
95
|
+
app.use(`/${pulseConfig.id}/${pulseConfig.version}`, express.static('dist'));
|
|
96
|
+
app.listen(3030, '0.0.0.0', () => {
|
|
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
|
+
|
|
103
|
+
✨ Try it out in the Pulse Editor and let the magic happen! 🚀`);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
function getLocalNetworkIP() {
|
|
107
|
+
const interfaces = networkInterfaces();
|
|
108
|
+
for (const iface of Object.values(interfaces)) {
|
|
109
|
+
if (!iface)
|
|
110
|
+
continue;
|
|
111
|
+
for (const config of iface) {
|
|
112
|
+
if (config.family === 'IPv4' && !config.internal) {
|
|
113
|
+
return config.address; // Returns the first non-internal IPv4 address
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return 'localhost'; // Fallback
|
|
118
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function loadAndCall(func: string, req: Request): Promise<Response>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/* This folder contains temporary code to be moved to a different package in the future. */
|
|
2
|
+
// @ts-expect-error ignore ts error
|
|
3
|
+
import { createInstance } from '@module-federation/runtime';
|
|
4
|
+
import { performReload } from '@module-federation/node/utils';
|
|
5
|
+
import { readConfigFile } from '../../utils.js';
|
|
6
|
+
export async function loadAndCall(func, req) {
|
|
7
|
+
const pulseConfig = await readConfigFile();
|
|
8
|
+
await performReload(true);
|
|
9
|
+
// here we assign the return value of the init() function, which can be used to do some more complex
|
|
10
|
+
// things with the module federation runtime
|
|
11
|
+
const instance = createInstance({
|
|
12
|
+
name: 'preview_host',
|
|
13
|
+
remotes: [
|
|
14
|
+
{
|
|
15
|
+
name: pulseConfig.id + '_server',
|
|
16
|
+
entry: `http://localhost:3030/.server-function/remoteEntry.js`,
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
});
|
|
20
|
+
const loadedFunc = (await instance.loadRemote(`${pulseConfig.id}_server/${func}`)).default;
|
|
21
|
+
const res = await loadedFunc(req);
|
|
22
|
+
return res;
|
|
23
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const { createInstance } = require("@module-federation/runtime");
|
|
2
|
+
|
|
3
|
+
async function loadAndCall(func, req, appId, origin, version) {
|
|
4
|
+
// here we assign the return value of the init() function, which can be used to do some more complex
|
|
5
|
+
// things with the module federation runtime
|
|
6
|
+
const instance = createInstance({
|
|
7
|
+
name: "server_function_runner",
|
|
8
|
+
remotes: [
|
|
9
|
+
{
|
|
10
|
+
name: appId + "_server",
|
|
11
|
+
entry: `${origin}/${appId}/${version}/server/remoteEntry.js`,
|
|
12
|
+
},
|
|
13
|
+
],
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const loadedFunc = (await instance.loadRemote(`${appId}_server/${func}`))
|
|
17
|
+
.default;
|
|
18
|
+
|
|
19
|
+
const res = await loadedFunc(req);
|
|
20
|
+
return res;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = { loadAndCall };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +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>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/* This folder contains temporary code to be moved to a different package in the future. */
|
|
3
|
+
import ReactDOM from 'react-dom/client';
|
|
4
|
+
// @ts-expect-error ignore ts error for now
|
|
5
|
+
import Main from "../../../../../src/main.tsx";
|
|
6
|
+
function Preview() {
|
|
7
|
+
return _jsx(Main, {});
|
|
8
|
+
}
|
|
9
|
+
const root = ReactDOM.createRoot(document.getElementById('root'));
|
|
10
|
+
root.render(_jsx(Preview, {}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function readConfigFile(): Promise<any>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
export async function readConfigFile() {
|
|
3
|
+
// Read pulse.config.json from dist/client
|
|
4
|
+
// Wait until dist/client/pulse.config.json exists
|
|
5
|
+
while (true) {
|
|
6
|
+
try {
|
|
7
|
+
await fs.access('dist/client/pulse.config.json');
|
|
8
|
+
break;
|
|
9
|
+
}
|
|
10
|
+
catch (err) {
|
|
11
|
+
// Wait for 100ms before trying again
|
|
12
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
const data = await fs.readFile('dist/client/pulse.config.json', 'utf-8');
|
|
16
|
+
return JSON.parse(data);
|
|
17
|
+
}
|
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.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"bin": {
|
|
6
6
|
"pulse": "dist/cli.js"
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
"node": ">=16"
|
|
11
11
|
},
|
|
12
12
|
"scripts": {
|
|
13
|
-
"build": "
|
|
14
|
-
"dev": "
|
|
13
|
+
"build": "tsx build.ts",
|
|
14
|
+
"dev": "tsx watch --include \"./source/**/*\" build.ts",
|
|
15
15
|
"test": "prettier --check . && xo && ava",
|
|
16
16
|
"link": "npm link"
|
|
17
17
|
},
|
|
@@ -19,29 +19,44 @@
|
|
|
19
19
|
"dist"
|
|
20
20
|
],
|
|
21
21
|
"dependencies": {
|
|
22
|
+
"@module-federation/enhanced": "0.19.1",
|
|
23
|
+
"@module-federation/node": "2.7.17",
|
|
24
|
+
"@module-federation/runtime": "0.19.1",
|
|
25
|
+
"concurrently": "^9.2.1",
|
|
26
|
+
"connect-livereload": "^0.6.1",
|
|
27
|
+
"cors": "^2.8.5",
|
|
28
|
+
"cross-env": "^10.1.0",
|
|
22
29
|
"execa": "^9.6.0",
|
|
23
|
-
"
|
|
30
|
+
"express": "^5.1.0",
|
|
31
|
+
"ink": "^6.5.0",
|
|
24
32
|
"ink-select-input": "^6.2.0",
|
|
25
33
|
"ink-spinner": "^5.0.0",
|
|
26
34
|
"ink-text-input": "^6.0.0",
|
|
27
|
-
"meow": "^
|
|
28
|
-
"openid-client": "^6.
|
|
29
|
-
"react": "^19.
|
|
35
|
+
"meow": "^14.0.0",
|
|
36
|
+
"openid-client": "^6.8.1",
|
|
37
|
+
"react": "^19.2.0",
|
|
38
|
+
"rimraf": "^6.1.0",
|
|
39
|
+
"tsx": "^4.20.6"
|
|
30
40
|
},
|
|
31
41
|
"devDependencies": {
|
|
32
|
-
"@sindresorhus/tsconfig": "^8.0
|
|
33
|
-
"@types/
|
|
42
|
+
"@sindresorhus/tsconfig": "^8.1.0",
|
|
43
|
+
"@types/connect-livereload": "^0.6.3",
|
|
44
|
+
"@types/cors": "^2.8.19",
|
|
45
|
+
"@types/express": "^5.0.5",
|
|
46
|
+
"@types/livereload": "^0.9.5",
|
|
47
|
+
"@types/react": "^19.2.5",
|
|
34
48
|
"@vdemedes/prettier-config": "^2.0.1",
|
|
35
49
|
"ava": "^6.4.1",
|
|
36
|
-
"chalk": "^5.
|
|
37
|
-
"eslint-config-xo-react": "^0.
|
|
50
|
+
"chalk": "^5.6.2",
|
|
51
|
+
"eslint-config-xo-react": "^0.29.0",
|
|
38
52
|
"eslint-plugin-react": "^7.37.5",
|
|
39
|
-
"eslint-plugin-react-hooks": "^
|
|
53
|
+
"eslint-plugin-react-hooks": "^7.0.1",
|
|
40
54
|
"ink-testing-library": "^4.0.0",
|
|
55
|
+
"livereload": "^0.10.3",
|
|
41
56
|
"prettier": "^3.6.2",
|
|
42
57
|
"ts-node": "^10.9.2",
|
|
43
|
-
"typescript": "^5.9.
|
|
44
|
-
"xo": "^1.2.
|
|
58
|
+
"typescript": "^5.9.3",
|
|
59
|
+
"xo": "^1.2.3"
|
|
45
60
|
},
|
|
46
61
|
"ava": {
|
|
47
62
|
"extensions": {
|