Skip to content

Commit e0e8143

Browse files
committed
feat: node extension scaffolding
1 parent 9957229 commit e0e8143

File tree

8 files changed

+824
-37
lines changed

8 files changed

+824
-37
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules
2+
node/node_modules
3+
.idea

README.md

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
11
# Extension Template
22

3-
Quick start and create a new extension by using this template. Follow the below 4 steps:
3+
Quick start and create a new `node.js extension` by using this template. This
4+
template extension works in browser as well as desktop builds.
45

5-
Refer this [link](https://github.com/phcode-dev/phoenix/wiki/How-To-Write-Extensions-And-Themes) for detailed extension docs after following the below steps:
6+
In desktop builds, there is an additional capability to execute node.js code. This
7+
is helpful if you want to extend the functionality of Phoenix Code using the
8+
vast npm library.
9+
10+
For creating extensions that do not need node, follow this link:
11+
https://github.com/phcode-dev/extension-template
12+
13+
Follow the below 4 steps to start using this template:
14+
15+
Refer this
16+
[link](https://github.com/phcode-dev/phoenix/wiki/How-To-Write-Extensions-And-Themes)
17+
for detailed extension docs after following the below steps:
618

719
## step 1
820

9-
Login with github: https://github.com/login
21+
Login with GitHub: https://github.com/login
1022

1123
## step 2
1224

@@ -15,26 +27,35 @@ Create a new repository using this template.
1527

1628
## step 3
1729

18-
* Clone your GitHub Repo created from `step 2`. See [this link](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository) to lean how to clone a repository from GitHub.
30+
- Clone your GitHub Repo created from `step 2`. See
31+
[this link](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository)
32+
to lean how to clone a repository from GitHub.
1933

2034
## step 4
2135

22-
* Go to https://create.phcode.dev .This is a special development centric website of phcode.dev which shows non minified js/css files in the browser developer tools.
23-
* Open the cloned folder and select `Debug > Load Project As Extension`
36+
- Go to https://create.phcode.dev .This is a special development centric
37+
website of phcode.dev which shows non minified js/css files in the browser
38+
developer tools.
39+
- Open the cloned folder and select `Debug > Load Project As Extension`
2440

2541
![image](https://user-images.githubusercontent.com/5336369/224746152-0416a862-891a-4fe1-b9dd-09add25a6cc0.png)
2642

27-
28-
* You can now make code changes to your extension and select `Debug> Reload With Extensions` to test the new code changes.
29-
* When you are done developing the extension/theme, select `Debug> Unload Project As Extension` to unload the extension.
43+
- You can now make code changes to your extension and select
44+
`Debug> Reload With Extensions` to test the new code changes.
45+
- When you are done developing the extension/theme, select
46+
`Debug> Unload Project As Extension` to unload the extension.
3047

3148
![image](https://user-images.githubusercontent.com/5336369/224747590-556dff1d-5b29-41c3-88a0-3ce72ab643d0.png)
3249

3350
# Detailed Documentation
3451

35-
Please go to https://github.com/phcode-dev/phoenix/wiki/How-To-Write-Extensions-And-Themes for more documentation/community support links.
52+
Please go to
53+
https://github.com/phcode-dev/phoenix/wiki/How-To-Write-Extensions-And-Themes
54+
for more documentation/community support links.
3655

3756
# Publishing your extension to the repository
3857

39-
Once you have built your extension/theme, you can publish the extension to phcode.dev extension repository in a single step directly from this repo.
40-
Please see publish section in this link for more details: https://github.com/phcode-dev/phoenix/wiki/How-To-Write-Extensions-And-Themes
58+
Once you have built your extension/theme, you can publish the extension to
59+
phcode.dev extension repository in a single step directly from this repo. Please
60+
see publish section in this link for more details:
61+
https://github.com/phcode-dev/phoenix/wiki/How-To-Write-Extensions-And-Themes

main.js

Lines changed: 65 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
1+
/**
2+
* A simple extension that downloads an image and then converts it to grey scale using `sharp` node.js lib via
3+
* Phoenix Code node.js Extension. Extension can be activated using menu: `file->Download Image & Greyscale`.
4+
* */
5+
16
/*global define, brackets, $ */
27

38
// See detailed docs in https://github.com/phcode-dev/phoenix/wiki/How-To-Write-Extensions-And-Themes
49
// A good place to look for code examples for extensions: https://github.com/phcode-dev/phoenix/tree/main/src/extensions/default
510

6-
// A simple extension that adds an entry in "file menu> hello world"
11+
712
define(function (require, exports, module) {
813
"use strict";
914

@@ -12,32 +17,78 @@ define(function (require, exports, module) {
1217
DefaultDialogs = brackets.getModule("widgets/DefaultDialogs"),
1318
Dialogs = brackets.getModule("widgets/Dialogs"),
1419
CommandManager = brackets.getModule("command/CommandManager"),
15-
Menus = brackets.getModule("command/Menus");
20+
Menus = brackets.getModule("command/Menus"),
21+
NodeConnector = brackets.getModule("NodeConnector");
22+
23+
const nodeConnector = NodeConnector.createNodeConnector(
24+
"your-extension-id-1",
25+
exports
26+
);
27+
28+
async function fetchImage() {
29+
const imageUrl = "https://picsum.photos/536/354";
30+
const response = await fetch(imageUrl);
31+
32+
if (!response.ok) {
33+
throw new Error(
34+
`Failed to fetch image (status ${response.status})`
35+
);
36+
}
37+
38+
return response.arrayBuffer();
39+
}
1640

1741
// Function to run when the menu item is clicked
18-
function handleHelloWorld() {
19-
Dialogs.showModalDialog(
20-
DefaultDialogs.DIALOG_ID_INFO,
21-
"hello",
22-
"world"
23-
);
42+
async function handleHelloWorld() {
43+
let html = "<b>Image conversion failed</b>";
44+
try {
45+
alert("downloading image...");
46+
// Fetch the image and get its array buffer
47+
const imageArrayBuffer = await fetchImage();
48+
49+
// Call the nodeConnector to convert the image to grayscale
50+
const { buffer, success } = await nodeConnector.execPeer(
51+
"convertToGreyScale",
52+
{ imageName: "imageName" },
53+
imageArrayBuffer
54+
);
55+
56+
if (!success) {
57+
alert("Image conversion failed in Node.");
58+
return;
59+
}
60+
61+
// Construct HTML with the grayscale image array buffer
62+
// For example, you can use the buffer as a base64 data URL
63+
html = `<img src="data:image/jpeg;base64,${Buffer.from(buffer).toString("base64")}">`;
64+
} catch (error) {
65+
console.error("Error:", error);
66+
}
67+
Dialogs.showModalDialog(DefaultDialogs.DIALOG_ID_INFO, "Image to greyscale with node.js", html);
2468
}
25-
26-
// First, register a command - a UI-less object associating an id to a handler
27-
var MY_COMMAND_ID = "helloworld.sayhello"; // package-style naming to avoid collisions
28-
CommandManager.register("Hello World", MY_COMMAND_ID, handleHelloWorld);
69+
70+
// First, register a command - a UI-less object associating an id to a handler
71+
var MY_COMMAND_ID = "helloworld.imageConvert"; // package-style naming to avoid collisions
72+
CommandManager.register("Download Image & Greyscale", MY_COMMAND_ID, handleHelloWorld);
2973

3074
// Then create a menu item bound to the command
3175
// The label of the menu item is the name we gave the command (see above)
3276
var menu = Menus.getMenu(Menus.AppMenuBar.FILE_MENU);
3377
menu.addMenuItem(MY_COMMAND_ID);
34-
78+
3579
// We could also add a key binding at the same time:
3680
//menu.addMenuItem(MY_COMMAND_ID, "Ctrl-Alt-W");
3781
// (Note: "Ctrl" is automatically mapped to "Cmd" on Mac)
38-
82+
3983
// Initialize extension once shell is finished initializing.
4084
AppInit.appReady(function () {
85+
// nb: Please enable `Debug menu> Phoenix code diagnostic tools> enable detailed logs` to view all console logs.`
4186
console.log("hello world");
87+
88+
if (Phoenix.isNativeApp) {
89+
// you can also execute nodejs code in dekstop builds
90+
// below code will execute the function `echoTest` defined in `node/index.js`
91+
nodeConnector.execPeer("echoTest", "yo!").then(console.log);
92+
}
4293
});
4394
});

node/README.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
## Setting up node extensions
2+
3+
In package.json, add the following section
4+
5+
```json
6+
{
7+
"nodeConfig": {
8+
"nodeIsRequired": false,
9+
"main": "node/index.js",
10+
"npmInstall": "node/"
11+
}
12+
}
13+
```
14+
15+
#### `nodeConfig` Object
16+
17+
The `nodeConfig` object indicates that this is a Node extension.
18+
19+
#### `nodeIsRequired` Field
20+
21+
- Set this field to `true` if the extension relies on Node and won't function
22+
without it.
23+
- If set to `false` or omitted, the extension can still be loaded in browser
24+
versions of Phoenix code without Node support, But will use node in native
25+
builds.
26+
- It will be shown in the extension manager dialog in browser builds as well.
27+
28+
#### `main` Field
29+
30+
- Specifies the main entry point for the Node.js component of the extension.
31+
- Should point to the main JavaScript file for the Node part of the extension.
32+
- Example: `"main": "node/index.js"`
33+
34+
#### `npmInstall` Field (Optional)
35+
36+
- Specifies the path to run `npm install` when the user installs the extension
37+
from the extension manager.
38+
- It's advisable not to package `node_modules` inside the extension. Only the
39+
package lock file should be distributed.
40+
- Example: `"npmInstall": "node/"`
41+
42+
## Communicating between node.js and Phoenix Code
43+
44+
### NodeConnector-API
45+
46+
create
47+
[NodeConnector-API](https://github.com/phcode-dev/phoenix/wiki/NodeConnector-API)
48+
to call functions and send events between your node.js and Phoenix Code
49+
extension components.
50+
51+
- This is available as a global object `global.createNodeConnector`. See above
52+
link for API docs.
53+
- Another API -
54+
[EventDispatcher-API](https://github.com/phcode-dev/phoenix/wiki/EventDispatcher-API)
55+
is also available in the global context as `global.EventDispatcher` for
56+
event trigger/listen within node.

node/index.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/**
2+
* This is an optional node extension that is available only in desktop builds. This code will be run in node. You can
3+
* bring in any third party node modules using package.json in folder `node/package.json`
4+
*
5+
* nb: Please note that you should not package `node_modules` folder when you care creating the extension zip file.
6+
* Zip `node/package-lock.json`, but not node modules.
7+
*
8+
* To communicate between this node file and the phoenix extension use: NodeConnector-API -
9+
* See. https://github.com/phcode-dev/phoenix/wiki/NodeConnector-API for detailed docs.
10+
**/
11+
console.log("hello world node extension");
12+
13+
const sharp = require("sharp");
14+
15+
const extnNodeConnector = global.createNodeConnector(
16+
"your-extension-id-1",
17+
exports
18+
);
19+
20+
async function echoTest(name) {
21+
return "hello from node " + name;
22+
}
23+
24+
async function convertToGreyScale(imageName, imageArrayBuffer) {
25+
try {
26+
// Convert the image buffer to grayscale using sharp
27+
const outputBuffer = await sharp(imageArrayBuffer)
28+
.grayscale() // Convert to grayscale
29+
.toBuffer(); // Convert to buffer
30+
31+
// Return the output buffer
32+
return {
33+
success: true,
34+
buffer: outputBuffer // a single binary array buffer can be transmitted, it should use key buffer
35+
};
36+
} catch (error) {
37+
throw new Error("Error converting image to black and white:", error);
38+
}
39+
}
40+
41+
exports.echoTest = echoTest;
42+
exports.convertToGreyScale = convertToGreyScale;

0 commit comments

Comments
 (0)