@pulse-editor/cli 0.1.1-beta.0 → 0.1.1-beta.10

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.
Files changed (44) hide show
  1. package/dist/app.js +7 -2
  2. package/dist/cli.js +1 -0
  3. package/dist/components/commands/build.d.ts +5 -0
  4. package/dist/components/commands/build.js +77 -0
  5. package/dist/components/commands/clean.d.ts +5 -0
  6. package/dist/components/commands/clean.js +10 -0
  7. package/dist/components/commands/create.js +8 -4
  8. package/dist/components/commands/dev.d.ts +5 -0
  9. package/dist/components/commands/dev.js +54 -0
  10. package/dist/components/commands/help.js +2 -1
  11. package/dist/components/commands/login.js +5 -5
  12. package/dist/components/commands/logout.js +1 -1
  13. package/dist/components/commands/preview copy.d.ts +5 -0
  14. package/dist/components/commands/preview copy.js +14 -0
  15. package/dist/components/commands/preview.d.ts +5 -0
  16. package/dist/components/commands/preview.js +57 -0
  17. package/dist/components/commands/publish.js +4 -4
  18. package/dist/components/commands/start copy.d.ts +5 -0
  19. package/dist/components/commands/start copy.js +14 -0
  20. package/dist/components/commands/start.d.ts +5 -0
  21. package/dist/components/commands/start.js +20 -0
  22. package/dist/lib/cli-flags.d.ts +5 -1
  23. package/dist/lib/cli-flags.js +5 -1
  24. package/dist/lib/deps.d.ts +1 -0
  25. package/dist/lib/deps.js +5 -0
  26. package/dist/lib/execa-utils/clean.d.ts +1 -0
  27. package/dist/lib/execa-utils/clean.js +9 -0
  28. package/dist/lib/execa-utils/deps.d.ts +1 -0
  29. package/dist/lib/execa-utils/deps.js +14 -0
  30. package/dist/lib/manual.js +38 -0
  31. package/dist/lib/node_module_bin.d.ts +1 -0
  32. package/dist/lib/node_module_bin.js +7 -0
  33. package/dist/lib/server/express.d.ts +4 -0
  34. package/dist/lib/server/express.js +118 -0
  35. package/dist/lib/server/preview/backend/index.d.ts +1 -0
  36. package/dist/lib/server/preview/backend/index.js +23 -0
  37. package/dist/lib/server/preview/backend/load-remote.cjs +23 -0
  38. package/dist/lib/server/preview/frontend/index.d.ts +1 -0
  39. package/dist/lib/server/preview/frontend/index.html +11 -0
  40. package/dist/lib/server/preview/frontend/index.js +10 -0
  41. package/dist/lib/server/utils.d.ts +1 -0
  42. package/dist/lib/server/utils.js +17 -0
  43. package/package.json +39 -17
  44. package/readme.md +28 -0
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.dev) {
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.dev && (_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 !== 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."] })] })))] }));
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
  }
package/dist/cli.js CHANGED
@@ -15,6 +15,7 @@ ${Object.entries(commandsManual)
15
15
  .join('')}
16
16
  Examples
17
17
  pulse help publish
18
+
18
19
  `, {
19
20
  importMeta: import.meta,
20
21
  flags: flags,
@@ -0,0 +1,5 @@
1
+ import { Result } from 'meow';
2
+ import { Flags } from '../../lib/cli-flags.js';
3
+ export default function Build({ cli }: {
4
+ cli: Result<Flags>;
5
+ }): import("react/jsx-runtime").JSX.Element;
@@ -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,5 @@
1
+ import { Result } from 'meow';
2
+ import { Flags } from '../../lib/cli-flags.js';
3
+ export default function Clean({ cli }: {
4
+ cli: Result<Flags>;
5
+ }): import("react/jsx-runtime").JSX.Element;
@@ -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,5 @@
1
+ import { Result } from 'meow';
2
+ import { Flags } from '../../lib/cli-flags.js';
3
+ export default function Dev({ cli }: {
4
+ cli: Result<Flags>;
5
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,54 @@
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'), [
38
+ '--prefix',
39
+ 'none',
40
+ '"npx webpack --mode development --watch"',
41
+ '"tsx watch --clear-screen=false node_modules/@pulse-editor/cli/dist/lib/server/express.js"',
42
+ ], {
43
+ stdio: 'inherit',
44
+ shell: true,
45
+ env: {
46
+ NODE_OPTIONS: '--import=tsx',
47
+ NODE_ENV: 'development',
48
+ },
49
+ });
50
+ }
51
+ startDevServer();
52
+ }, []);
53
+ return (_jsx(_Fragment, { children: _jsx(Text, { children: "Starting dev server..." }) }));
54
+ }
@@ -4,5 +4,6 @@ import { commandsManual } from '../../lib/manual.js';
4
4
  import Header from '../header.js';
5
5
  export default function Help({ cli }) {
6
6
  const subCommand = cli.input[1];
7
- return (_jsx(_Fragment, { children: subCommand ? (_jsx(Text, { children: commandsManual[subCommand] })) : (_jsxs(_Fragment, { children: [_jsx(Header, {}), _jsx(Text, { children: cli.help })] })) }));
7
+ return (_jsx(_Fragment, { children: subCommand ? (_jsx(Text, { children: commandsManual[subCommand]?.trimEnd() ??
8
+ `No help found for command: ${subCommand}` })) : (_jsxs(_Fragment, { children: [_jsx(Header, {}), _jsx(Text, { children: cli.help })] })) }));
8
9
  }
@@ -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.dev);
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.dev).then(isValid => {
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.dev) &&
72
- getToken(cli.flags.dev) !== 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 => {
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.dev);
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.dev);
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,5 @@
1
+ import { Result } from 'meow';
2
+ import { Flags } from '../../lib/cli-flags.js';
3
+ export default function Preview({ cli }: {
4
+ cli: Result<Flags>;
5
+ }): import("react/jsx-runtime").JSX.Element;
@@ -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,5 @@
1
+ import { Result } from 'meow';
2
+ import { Flags } from '../../lib/cli-flags.js';
3
+ export default function Preview({ cli }: {
4
+ cli: Result<Flags>;
5
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,57 @@
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'), [
41
+ '--prefix',
42
+ 'none',
43
+ '"npx webpack --mode development --watch"',
44
+ '"tsx watch --clear-screen=false node_modules/@pulse-editor/cli/dist/lib/server/express.js"',
45
+ ], {
46
+ stdio: 'inherit',
47
+ shell: true,
48
+ env: {
49
+ NODE_OPTIONS: '--import=tsx',
50
+ PREVIEW: 'true',
51
+ },
52
+ });
53
+ }
54
+ startPreviewServer();
55
+ }, []);
56
+ return (_jsx(_Fragment, { children: _jsx(Text, { children: "Starting preview server..." }) }));
57
+ }
@@ -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.dev);
32
+ const token = getToken(cli.flags.stage);
33
33
  if (token) {
34
- const isValid = await checkToken(token, cli.flags.dev);
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.dev
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.dev)}`,
94
+ Authorization: `Bearer ${getToken(cli.flags.stage)}`,
95
95
  },
96
96
  body: formData,
97
97
  });
@@ -0,0 +1,5 @@
1
+ import { Result } from 'meow';
2
+ import { Flags } from '../../lib/cli-flags.js';
3
+ export default function Start({ cli }: {
4
+ cli: Result<Flags>;
5
+ }): import("react/jsx-runtime").JSX.Element;
@@ -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,5 @@
1
+ import { Result } from 'meow';
2
+ import { Flags } from '../../lib/cli-flags.js';
3
+ export default function Start({ cli }: {
4
+ cli: Result<Flags>;
5
+ }): import("react/jsx-runtime").JSX.Element;
@@ -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
+ }
@@ -9,7 +9,7 @@ export declare const flags: {
9
9
  type: "string";
10
10
  shortFlag: string;
11
11
  };
12
- dev: {
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;
@@ -13,7 +13,7 @@ export const flags = defineFlags({
13
13
  type: 'string',
14
14
  shortFlag: 'f',
15
15
  },
16
- dev: {
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;
@@ -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,14 @@
1
+ import fs from 'fs';
2
+ // Get deps bin at node_modules/@pulse-editor/cli/node_modules
3
+ export function getDepsBinPath(name) {
4
+ const workingDir = process.cwd();
5
+ // Check if the package is installed in the node_modules, if yes, just return "npx <name>"
6
+ if (fs.existsSync(`${workingDir}/node_modules/${name}`)) {
7
+ return `npx ${name}`;
8
+ }
9
+ // Check if the package exists in node_modules/@pulse-editor/cli/node_modules
10
+ else if (fs.existsSync(`${workingDir}/node_modules/@pulse-editor/cli/node_modules/${name}`)) {
11
+ return `${workingDir}/node_modules/@pulse-editor/cli/node_modules/.bin/${process.platform === 'win32' ? `${name}.cmd` : name}`;
12
+ }
13
+ throw new Error(`Dependency ${name} not found.`);
14
+ }
@@ -1,5 +1,6 @@
1
1
  const help = `\
2
2
  help [command] Show help for a command.
3
+
3
4
  `;
4
5
  const chat = `\
5
6
  chat [message] (WIP) Chat with the Pulse Editor AI assistant.
@@ -22,9 +23,11 @@ const login = `\
22
23
  `;
23
24
  const logout = `\
24
25
  logout Logout from the Pulse Editor Platform.
26
+
25
27
  `;
26
28
  const publish = `\
27
29
  publish Publish Pulse Editor Extension in current directory to the Pulse Editor Platform.
30
+
28
31
  `;
29
32
  const create = `\
30
33
  create Create a new Pulse App using the starter template.
@@ -38,6 +41,36 @@ const create = `\
38
41
  --visibility, -v [visibility]
39
42
  The visibility of the new project. Options are private,
40
43
  public, and unlisted.
44
+
45
+ `;
46
+ const preview = `\
47
+ preview Build the Pulse App in development mode and
48
+ start a preview server accessible via browser
49
+ with live reloading.
50
+
51
+ `;
52
+ const dev = `\
53
+ dev Build the Pulse App in development mode and
54
+ start a local development server for Pulse Editor
55
+ to load the app from, with live reloading.
56
+
57
+ `;
58
+ const build = `\
59
+ build Build the Pulse App for production deployment.
60
+ Flags:
61
+ --target, -t [target]
62
+ The build target. Options are 'client', 'server', or
63
+ unspecified (both client and server).
64
+
65
+ `;
66
+ const start = `\
67
+ start Build the Pulse App in production mode and
68
+ start a local server for Pulse Editor to load the app from.
69
+
70
+ `;
71
+ const clean = `\
72
+ clean Clean the dist/ directory.
73
+
41
74
  `;
42
75
  export const commandsManual = {
43
76
  help,
@@ -46,4 +79,9 @@ export const commandsManual = {
46
79
  logout,
47
80
  publish,
48
81
  create,
82
+ preview,
83
+ dev,
84
+ build,
85
+ start,
86
+ clean,
49
87
  };
@@ -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,4 @@
1
+ /**
2
+ * This is a local dev server for "npm run dev" and "npm run preview".
3
+ */
4
+ export {};
@@ -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.0",
3
+ "version": "0.1.1-beta.10",
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": "tsc",
14
- "dev": "tsc --watch",
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,51 @@
19
19
  "dist"
20
20
  ],
21
21
  "dependencies": {
22
- "execa": "^9.6.0",
23
- "ink": "^6.1.0",
22
+ "concurrently": "^9.2.1",
23
+ "connect-livereload": "^0.6.1",
24
+ "cors": "^2.8.5",
25
+ "cross-env": "^10.1.0",
26
+ "dotenv": "^17.2.3",
27
+ "execa": "^9.6.1",
28
+ "express": "^5.2.1",
29
+ "ink": "^6.5.1",
24
30
  "ink-select-input": "^6.2.0",
25
31
  "ink-spinner": "^5.0.0",
26
32
  "ink-text-input": "^6.0.0",
27
- "meow": "^13.2.0",
28
- "openid-client": "^6.6.3",
29
- "react": "^19.1.1"
33
+ "livereload": "^0.10.3",
34
+ "meow": "^14.0.0",
35
+ "openid-client": "^6.8.1",
36
+ "rimraf": "^6.1.2",
37
+ "tsx": "^4.21.0"
30
38
  },
31
39
  "devDependencies": {
32
- "@sindresorhus/tsconfig": "^8.0.1",
33
- "@types/react": "^19.1.9",
40
+ "@module-federation/enhanced": "0.21.6",
41
+ "@module-federation/node": "2.7.25",
42
+ "@module-federation/runtime": "0.21.6",
43
+ "@sindresorhus/tsconfig": "^8.1.0",
44
+ "@types/connect-livereload": "^0.6.3",
45
+ "@types/cors": "^2.8.19",
46
+ "@types/express": "^5.0.6",
47
+ "@types/livereload": "^0.9.5",
48
+ "@types/react": "^19.2.7",
49
+ "@types/react-dom": "^19.2.3",
34
50
  "@vdemedes/prettier-config": "^2.0.1",
35
51
  "ava": "^6.4.1",
36
- "chalk": "^5.5.0",
37
- "eslint-config-xo-react": "^0.28.0",
52
+ "chalk": "^5.6.2",
53
+ "eslint-config-xo-react": "^0.29.0",
38
54
  "eslint-plugin-react": "^7.37.5",
39
- "eslint-plugin-react-hooks": "^5.2.0",
55
+ "eslint-plugin-react-hooks": "^7.0.1",
40
56
  "ink-testing-library": "^4.0.0",
41
- "prettier": "^3.6.2",
57
+ "prettier": "^3.7.4",
58
+ "react": "19.2.0",
59
+ "react-dom": "19.2.0",
42
60
  "ts-node": "^10.9.2",
43
- "typescript": "^5.9.2",
44
- "xo": "^1.2.1"
61
+ "typescript": "^5.9.3",
62
+ "xo": "^1.2.3"
63
+ },
64
+ "peerDependencies": {
65
+ "react": "19.2.0",
66
+ "react-dom": "19.2.0"
45
67
  },
46
68
  "ava": {
47
69
  "extensions": {
@@ -60,4 +82,4 @@
60
82
  }
61
83
  },
62
84
  "prettier": "@vdemedes/prettier-config"
63
- }
85
+ }
package/readme.md CHANGED
@@ -17,6 +17,7 @@ npm run link
17
17
 
18
18
  Commands
19
19
  help [command] Show help for a command.
20
+
20
21
  chat [message] (WIP) Chat with the Pulse Editor AI assistant.
21
22
 
22
23
  Flags:
@@ -33,13 +34,40 @@ npm run link
33
34
  Login using a browser flow.
34
35
 
35
36
  logout Logout from the Pulse Editor Platform.
37
+
36
38
  publish Publish Pulse Editor Extension in current directory to the Pulse Editor Platform.
39
+
37
40
  create Create a new Pulse App using the starter template.
38
41
  Flags:
39
42
  --framework, -f [framework]
40
43
  The framework to use for the new app.
41
44
  Currently available options: react.
42
45
  Future options: vue, angular, etc.
46
+ --name, -n [project-name]
47
+ The name of the new project.
48
+ --visibility, -v [visibility]
49
+ The visibility of the new project. Options are private,
50
+ public, and unlisted.
51
+
52
+ preview Build the Pulse App in development mode and
53
+ start a preview server accessible via browser
54
+ with live reloading.
55
+
56
+ dev Build the Pulse App in development mode and
57
+ start a local development server for Pulse Editor
58
+ to load the app from, with live reloading.
59
+
60
+ build Build the Pulse App for production deployment.
61
+ Flags:
62
+ --target, -t [target]
63
+ The build target. Options are 'client', 'server', or
64
+ unspecified (both client and server).
65
+
66
+ start Build the Pulse App in production mode and
67
+ start a local server for Pulse Editor to load the app from.
68
+
69
+ clean Clean the dist/ directory.
70
+
43
71
 
44
72
  Examples
45
73
  pulse help publish