lucid-package 0.0.3 → 0.0.6
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/README.md +70 -1
- package/assets/images/developer-menu.png +0 -0
- package/package.json +5 -2
- package/src/editorextension.js +11 -6
- package/src/index.js +25 -1
- package/src/package.d.ts +1 -11
- package/src/package.js +22 -15
- package/src/packagemanifest.d.ts +15 -0
- package/src/packagemanifest.js +8 -0
- package/src/shapelibrary.d.ts +4 -0
- package/src/shapelibrary.js +200 -0
- package/templates/package/manifest.json +2 -1
- package/templates/shapelibrary/images/README +1 -0
- package/templates/shapelibrary/library.manifest +16 -0
- package/templates/shapelibrary/shapes/first.shape +8 -0
package/README.md
CHANGED
|
@@ -2,7 +2,76 @@
|
|
|
2
2
|
|
|
3
3
|
## Description
|
|
4
4
|
|
|
5
|
-
CLI for creating and managing
|
|
5
|
+
CLI for creating and managing extension packages for the products of Lucid Software.
|
|
6
|
+
|
|
7
|
+
## Getting started
|
|
8
|
+
|
|
9
|
+
### Step 1: Install `lucid-package` and create a new package
|
|
10
|
+
|
|
11
|
+
An extension package is a set of Lucid product extensions that is installable by a Lucid
|
|
12
|
+
account admin for use by all of their users. In order to start building an extension, you
|
|
13
|
+
need to create a package to contain it.
|
|
14
|
+
|
|
15
|
+
In a directory that will contain your extension packages:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
npm install lucid-package
|
|
19
|
+
npx lucid-package create my-new-package-name
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Step 2: Add an editor extension to your new package
|
|
23
|
+
|
|
24
|
+
An editor extension is a piece of custom code that executes inside a Lucid product such
|
|
25
|
+
as Lucidchart or Lucidspark. The `lucid-package` CLI provides a quick-start template to
|
|
26
|
+
help you get up and running immediately.
|
|
27
|
+
|
|
28
|
+
To add a new extension to your package:
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
cd my-new-package-name
|
|
32
|
+
npx lucid-package create-editor-extension my-extension-name
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Step 3: Serve your extension code in debug mode to test it
|
|
36
|
+
|
|
37
|
+
You don't need to package, upload, and install your extension in order to run it and
|
|
38
|
+
make sure it works. The following command will start `webpack` on your code in `--watch`
|
|
39
|
+
mode, and start up a local HTTP server that Lucid products can connect to, to load your
|
|
40
|
+
latest code.
|
|
41
|
+
|
|
42
|
+
To start the debug server:
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
npx lucid-package test-editor-extension my-extension-name
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
You can then enable loading of your local plugin in the Developer menu in Lucidchart
|
|
49
|
+
by clicking `Load local extension`. The page will refresh and your extension code will run.
|
|
50
|
+
|
|
51
|
+
The main entry point to your new editor extension is in `editorextensions/my-extension-name/src/extension.ts`.
|
|
52
|
+
Experiment by changing code in that file and refreshing your browser tab to reload it.
|
|
53
|
+
|
|
54
|
+
For all published extensions, and by default for this debug server as well, your code
|
|
55
|
+
runs in a sandboxed JavaScript VM for security. However, this makes debugging difficult.
|
|
56
|
+
If you turn on the `Debug local extensions (no sandbox)` option in the Developer menu,
|
|
57
|
+
your code will be run via a scoped `eval`, allowing you to use the standard browser
|
|
58
|
+
debugging tools to examine and step through your code.
|
|
59
|
+
|
|
60
|
+
We recommend that you do all final validation of your extension with the normal
|
|
61
|
+
sandbox enabled, however, as you may have inadvertently used features not allowed in
|
|
62
|
+
the sandbox that won't work once you release your extension.
|
|
63
|
+
|
|
64
|
+
### Step 4: Prepare your package for upload
|
|
65
|
+
|
|
66
|
+
Once your editor extension works the way you want, you can package it for upload to the
|
|
67
|
+
Lucid developer dashboard:
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
npx lucid-package package
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
This will create the file `package.zip` which is ready for upload to the [Lucid developer
|
|
74
|
+
dashboard](https://lucid.app/developer).
|
|
6
75
|
|
|
7
76
|
## License
|
|
8
77
|
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lucid-package",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -19,11 +19,14 @@
|
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"argparse": "^2.0.1",
|
|
22
|
+
"chokidar": "^3.5.3",
|
|
22
23
|
"express": "^4.17.1",
|
|
24
|
+
"hjson": "^3.2.2",
|
|
23
25
|
"jszip": "^3.7.1",
|
|
24
26
|
"password-prompt": "^1.1.2",
|
|
25
27
|
"ts-loader": "^9.2.6",
|
|
26
28
|
"webpack": "^5.64.4",
|
|
27
|
-
"webpack-cli": "^4.9.1"
|
|
29
|
+
"webpack-cli": "^4.9.1",
|
|
30
|
+
"ws": "^8.5.0"
|
|
28
31
|
}
|
|
29
32
|
}
|
package/src/editorextension.js
CHANGED
|
@@ -6,16 +6,17 @@ const filesystemutil_1 = require("./filesystemutil");
|
|
|
6
6
|
const package_1 = require("./package");
|
|
7
7
|
const express = require("express");
|
|
8
8
|
const child_process = require("child_process");
|
|
9
|
+
const shapelibrary_1 = require("./shapelibrary");
|
|
9
10
|
const WebPackCLI = require('webpack-cli');
|
|
10
11
|
async function createEditorExtension(name, isInternalTesting) {
|
|
11
12
|
console.log('Creating empty editor extension in editorextensions/' + name);
|
|
12
13
|
(0, filesystemutil_1.copyFolderRecursiveSync)(__dirname + '/../templates/editorextension', 'editorextensions/' + name);
|
|
13
14
|
await (0, package_1.modifyManifest)((manifest) => {
|
|
14
|
-
manifest
|
|
15
|
-
name: name,
|
|
16
|
-
title: name,
|
|
17
|
-
codePath: `editorextensions/${name}/bin/extension.js`,
|
|
18
|
-
scopes: ['READ', 'DOWNLOAD'],
|
|
15
|
+
manifest['extensions'].push({
|
|
16
|
+
'name': name,
|
|
17
|
+
'title': name,
|
|
18
|
+
'codePath': `editorextensions/${name}/bin/extension.js`,
|
|
19
|
+
'scopes': ['READ', 'DOWNLOAD'],
|
|
19
20
|
});
|
|
20
21
|
});
|
|
21
22
|
console.log(`Installing dependencies`);
|
|
@@ -50,13 +51,17 @@ async function debugEditorExtension(name) {
|
|
|
50
51
|
const cli = new WebPackCLI();
|
|
51
52
|
cli.run(['node', 'webpack', '--watch']);
|
|
52
53
|
const app = express();
|
|
53
|
-
app.
|
|
54
|
+
app.use((req, res, next) => {
|
|
54
55
|
res.header('Access-Control-Allow-Origin', '*');
|
|
55
56
|
res.header('Access-Control-Allow-Methods', 'GET, OPTIONS');
|
|
57
|
+
next();
|
|
58
|
+
});
|
|
59
|
+
app.get('/extension.js', async (req, res) => {
|
|
56
60
|
res.send((await fs.readFile('bin/extension.js')).toString());
|
|
57
61
|
});
|
|
58
62
|
app.listen(9900, () => {
|
|
59
63
|
console.log('Listening at http://localhost:9900/extension.js');
|
|
60
64
|
});
|
|
65
|
+
(0, shapelibrary_1.debugShapeLibraries)('../..');
|
|
61
66
|
}
|
|
62
67
|
exports.debugEditorExtension = debugEditorExtension;
|
package/src/index.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const editorextension_1 = require("./editorextension");
|
|
4
4
|
const package_1 = require("./package");
|
|
5
5
|
const argparse_1 = require("argparse");
|
|
6
|
+
const shapelibrary_1 = require("./shapelibrary");
|
|
6
7
|
class LucidSuiteExtensionCLI {
|
|
7
8
|
run(args) {
|
|
8
9
|
const parser = new argparse_1.ArgumentParser({
|
|
@@ -35,9 +36,16 @@ class LucidSuiteExtensionCLI {
|
|
|
35
36
|
});
|
|
36
37
|
buildExtensionParser.add_argument('name');
|
|
37
38
|
const testExtensionParser = subparsers.add_parser('test-editor-extension', {
|
|
38
|
-
help: 'Compile an editor extension in debug mode, and serve it at localhost:9900. Watch for changes to the code and recompile as needed',
|
|
39
|
+
help: 'Compile an editor extension in debug mode, and serve it at localhost:9900, along with any shape libraries in this package at localhost:9901. Watch for changes to the code and recompile as needed',
|
|
39
40
|
});
|
|
40
41
|
testExtensionParser.add_argument('name');
|
|
42
|
+
const testShapeLibraries = subparsers.add_parser('test-shape-libraries', {
|
|
43
|
+
help: 'Serve any shape libraries in this package at localhost:9901. Watch for changes to the code and live-update as needed',
|
|
44
|
+
});
|
|
45
|
+
const createShapeLibrary = subparsers.add_parser('create-shape-library', {
|
|
46
|
+
help: 'Create a new shape library as part of the current package',
|
|
47
|
+
});
|
|
48
|
+
createShapeLibrary.add_argument('name');
|
|
41
49
|
const parsed = parser.parse_args(args);
|
|
42
50
|
//For internal development, creating and operating on a "test" package via bazel
|
|
43
51
|
const isInternalTesting = !!parsed['chdir'];
|
|
@@ -88,6 +96,22 @@ class LucidSuiteExtensionCLI {
|
|
|
88
96
|
console.error('Not currently in a Lucid extensibility package folder');
|
|
89
97
|
}
|
|
90
98
|
break;
|
|
99
|
+
case 'create-shape-library':
|
|
100
|
+
if ((0, package_1.currentlyInPackage)()) {
|
|
101
|
+
(0, shapelibrary_1.createEmptyShapeLibrary)(parsed['name']);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
console.error('Not currently in a Lucid extensibility package folder');
|
|
105
|
+
}
|
|
106
|
+
break;
|
|
107
|
+
case 'test-shape-libraries':
|
|
108
|
+
if ((0, package_1.currentlyInPackage)()) {
|
|
109
|
+
(0, shapelibrary_1.debugShapeLibraries)();
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
console.error('Not currently in a Lucid extensibility package folder');
|
|
113
|
+
}
|
|
114
|
+
break;
|
|
91
115
|
case 'test-editor-extension':
|
|
92
116
|
if ((0, package_1.currentlyInPackage)()) {
|
|
93
117
|
(0, editorextension_1.debugEditorExtension)(parsed['name']);
|
package/src/package.d.ts
CHANGED
|
@@ -1,16 +1,6 @@
|
|
|
1
|
+
import { PackageManifest } from './packagemanifest';
|
|
1
2
|
export declare function createEmptyPackage(root: string): Promise<void>;
|
|
2
3
|
export declare function currentlyInPackage(): boolean;
|
|
3
|
-
declare type PackageManifest = {
|
|
4
|
-
version: string;
|
|
5
|
-
extensions: {
|
|
6
|
-
name: string;
|
|
7
|
-
title: string;
|
|
8
|
-
codePath: string;
|
|
9
|
-
scopes: string[];
|
|
10
|
-
}[];
|
|
11
|
-
};
|
|
12
|
-
export declare function readManifest(): Promise<PackageManifest>;
|
|
13
4
|
export declare function modifyManifest(callback: (manifest: PackageManifest) => void | Promise<void>): Promise<void>;
|
|
14
5
|
export declare function updateAllExtensionSDK(): Promise<void>;
|
|
15
6
|
export declare function writePackage(): Promise<void>;
|
|
16
|
-
export {};
|
package/src/package.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.writePackage = exports.updateAllExtensionSDK = exports.modifyManifest = exports.
|
|
3
|
+
exports.writePackage = exports.updateAllExtensionSDK = exports.modifyManifest = exports.currentlyInPackage = exports.createEmptyPackage = void 0;
|
|
4
4
|
const fs = require("fs/promises");
|
|
5
5
|
const fsOld = require("fs");
|
|
6
6
|
const filesystemutil_1 = require("./filesystemutil");
|
|
7
7
|
const editorextension_1 = require("./editorextension");
|
|
8
8
|
const child_process = require("child_process");
|
|
9
9
|
const JSZip = require("jszip");
|
|
10
|
+
const shapelibrary_1 = require("./shapelibrary");
|
|
11
|
+
const packagemanifest_1 = require("./packagemanifest");
|
|
10
12
|
async function createEmptyPackage(root) {
|
|
11
13
|
console.log('Creating empty Lucid Suite package in ' + root);
|
|
12
14
|
(0, filesystemutil_1.copyFolderRecursiveSync)(__dirname + '/../templates/package', root);
|
|
@@ -17,19 +19,15 @@ function currentlyInPackage() {
|
|
|
17
19
|
}
|
|
18
20
|
exports.currentlyInPackage = currentlyInPackage;
|
|
19
21
|
const allScopes = new Set(['DOWNLOAD', 'NETWORK', 'READ', 'SHOW_MODAL', 'WRITE']);
|
|
20
|
-
async function readManifest() {
|
|
21
|
-
return JSON.parse((await fs.readFile('manifest.json')).toString());
|
|
22
|
-
}
|
|
23
|
-
exports.readManifest = readManifest;
|
|
24
22
|
async function modifyManifest(callback) {
|
|
25
|
-
const manifest = await readManifest();
|
|
23
|
+
const manifest = await (0, packagemanifest_1.readManifest)();
|
|
26
24
|
await callback(manifest);
|
|
27
25
|
await fs.writeFile('manifest.json', JSON.stringify(manifest, undefined, 2));
|
|
28
26
|
}
|
|
29
27
|
exports.modifyManifest = modifyManifest;
|
|
30
28
|
async function updateAllExtensionSDK() {
|
|
31
|
-
for (const extension of (await readManifest()).extensions) {
|
|
32
|
-
const parts = extension
|
|
29
|
+
for (const extension of (await (0, packagemanifest_1.readManifest)()).extensions) {
|
|
30
|
+
const parts = extension['codePath'].split('/');
|
|
33
31
|
if (parts[0] === 'editorextensions') {
|
|
34
32
|
await (0, editorextension_1.updateExtensionSDK)(parts[1]);
|
|
35
33
|
}
|
|
@@ -40,19 +38,19 @@ async function writePackage() {
|
|
|
40
38
|
const zip = new JSZip();
|
|
41
39
|
//Increment the patch number automatically on each build
|
|
42
40
|
await modifyManifest(async (manifest) => {
|
|
43
|
-
const parts = manifest
|
|
41
|
+
const parts = manifest['version'].split('.');
|
|
44
42
|
parts[parts.length - 1] = String(parseInt(parts[parts.length - 1], 10) + 1);
|
|
45
|
-
manifest
|
|
43
|
+
manifest['version'] = parts.join('.');
|
|
46
44
|
//For each extension, compile it and add it to the archive
|
|
47
|
-
for (const extension of manifest
|
|
45
|
+
for (const extension of manifest['extensions']) {
|
|
48
46
|
//Check that the scopes are valid
|
|
49
|
-
if (!extension
|
|
50
|
-
console.error(`Invalid scope specified for extension ${extension
|
|
47
|
+
if (!extension['scopes'].every((one) => allScopes.has(one))) {
|
|
48
|
+
console.error(`Invalid scope specified for extension ${extension['name']}: ${extension['scopes']
|
|
51
49
|
.filter((one) => !allScopes.has(one))
|
|
52
50
|
.join(', ')}`);
|
|
53
51
|
process.exit(1);
|
|
54
52
|
}
|
|
55
|
-
const parts = extension
|
|
53
|
+
const parts = extension['codePath'].split('/');
|
|
56
54
|
if (parts[0] === 'editorextensions') {
|
|
57
55
|
//Can't just call the method, as the WebpackCLI class does process.exit(2) :facepalm:
|
|
58
56
|
console.log(`Compiling editor extension ${parts[1]}`);
|
|
@@ -60,7 +58,16 @@ async function writePackage() {
|
|
|
60
58
|
console.log(cmd);
|
|
61
59
|
console.log(child_process.execSync(cmd).toString());
|
|
62
60
|
}
|
|
63
|
-
zip.file(extension
|
|
61
|
+
zip.file(extension['codePath'], await fs.readFile(extension['codePath']));
|
|
62
|
+
}
|
|
63
|
+
//Do the same for each shape library
|
|
64
|
+
for (const library of manifest['shapeLibraries']) {
|
|
65
|
+
const parts = library['lcszPath'].split('/');
|
|
66
|
+
if (parts[0] === 'shapelibraries') {
|
|
67
|
+
console.log(`Building shape library ${parts[1]}`);
|
|
68
|
+
await (0, shapelibrary_1.buildShapeLibrary)(parts[1].replace('.lcsz', ''));
|
|
69
|
+
}
|
|
70
|
+
zip.file(library['lcszPath'], await fs.readFile(library['lcszPath']));
|
|
64
71
|
}
|
|
65
72
|
//Add the manifest itself
|
|
66
73
|
zip.file('manifest.json', JSON.stringify(manifest, undefined, 2));
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare type PackageManifest = {
|
|
2
|
+
'version': string;
|
|
3
|
+
'extensions': {
|
|
4
|
+
'name': string;
|
|
5
|
+
'title': string;
|
|
6
|
+
'codePath': string;
|
|
7
|
+
'scopes': string[];
|
|
8
|
+
}[];
|
|
9
|
+
'shapeLibraries': {
|
|
10
|
+
'name': string;
|
|
11
|
+
'title': string;
|
|
12
|
+
'lcszPath': string;
|
|
13
|
+
}[];
|
|
14
|
+
};
|
|
15
|
+
export declare function readManifest(path?: string): Promise<PackageManifest>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.readManifest = void 0;
|
|
4
|
+
const fs = require("fs/promises");
|
|
5
|
+
async function readManifest(path = './') {
|
|
6
|
+
return JSON.parse((await fs.readFile(path + 'manifest.json')).toString());
|
|
7
|
+
}
|
|
8
|
+
exports.readManifest = readManifest;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function createEmptyShapeLibrary(name: string): Promise<void>;
|
|
2
|
+
export declare function getShapeListJson(name: string, packagePath: string): Promise<string>;
|
|
3
|
+
export declare function buildShapeLibrary(name: string): Promise<void>;
|
|
4
|
+
export declare function debugShapeLibraries(packagePath?: string): Promise<void>;
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.debugShapeLibraries = exports.buildShapeLibrary = exports.getShapeListJson = exports.createEmptyShapeLibrary = void 0;
|
|
4
|
+
const fs = require("fs/promises");
|
|
5
|
+
const filesystemutil_1 = require("./filesystemutil");
|
|
6
|
+
const package_1 = require("./package");
|
|
7
|
+
const JSZip = require("jszip");
|
|
8
|
+
const path = require("path");
|
|
9
|
+
const express = require("express");
|
|
10
|
+
const packagemanifest_1 = require("./packagemanifest");
|
|
11
|
+
const http = require("http");
|
|
12
|
+
const hjson = require('hjson');
|
|
13
|
+
const ws = require('ws');
|
|
14
|
+
const chokidar = require('chokidar');
|
|
15
|
+
async function createEmptyShapeLibrary(name) {
|
|
16
|
+
console.log('Creating shape library in shapelibraries/' + name);
|
|
17
|
+
(0, filesystemutil_1.copyFolderRecursiveSync)(__dirname + '/../templates/shapelibrary', 'shapelibraries/' + name);
|
|
18
|
+
await (0, package_1.modifyManifest)((manifest) => {
|
|
19
|
+
manifest['shapeLibraries'].push({
|
|
20
|
+
'name': name,
|
|
21
|
+
'title': name,
|
|
22
|
+
'lcszPath': `shapelibraries/${name}.lcsz`,
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
exports.createEmptyShapeLibrary = createEmptyShapeLibrary;
|
|
27
|
+
const defaultNameMap = new Map([
|
|
28
|
+
['rounding', 'Rounding'],
|
|
29
|
+
['fillColor', 'FillColor'],
|
|
30
|
+
['strokeColor', 'LineColor'],
|
|
31
|
+
['strokeWidth', 'LineWidth'],
|
|
32
|
+
['opacity', 'Opacity'],
|
|
33
|
+
['rotation', 'Rotation'],
|
|
34
|
+
]);
|
|
35
|
+
//Produce JSON equivalent to the document service's
|
|
36
|
+
// /shapeLibraries/:name/shapes endpoint
|
|
37
|
+
async function getShapeListJson(name, packagePath) {
|
|
38
|
+
const rawManifest = await fs.readFile(packagePath + `/shapelibraries/${name}/library.manifest`);
|
|
39
|
+
const manifest = hjson.parse(rawManifest.toString());
|
|
40
|
+
return JSON.stringify(await Promise.all(manifest['shapes'].map(async (shapeManifest, index) => {
|
|
41
|
+
var _a;
|
|
42
|
+
const rawShapeData = await fs.readFile(packagePath + `/shapelibraries/${name}/shapes/${shapeManifest['shape']}.shape`);
|
|
43
|
+
const shapeData = hjson.parse(rawShapeData.toString());
|
|
44
|
+
const properties = {
|
|
45
|
+
'Base': { 'x': shapeManifest['defaults']['width'] / 2, 'y': shapeManifest['defaults']['height'] / 2 },
|
|
46
|
+
'BoundingBox': {
|
|
47
|
+
'x': 0,
|
|
48
|
+
'y': 0,
|
|
49
|
+
'w': shapeManifest['defaults']['width'],
|
|
50
|
+
'h': shapeManifest['defaults']['height'],
|
|
51
|
+
},
|
|
52
|
+
'DefaultSize': {
|
|
53
|
+
'w': shapeManifest['defaults']['width'],
|
|
54
|
+
'h': shapeManifest['defaults']['height'],
|
|
55
|
+
},
|
|
56
|
+
'Stencil': {
|
|
57
|
+
'lcszVersion': '1',
|
|
58
|
+
'name': shapeManifest['name'],
|
|
59
|
+
'i18n': {},
|
|
60
|
+
'sourcePackage': {
|
|
61
|
+
'packageId': '__local__',
|
|
62
|
+
'version': '0.0.0',
|
|
63
|
+
'library': name,
|
|
64
|
+
'shape': shapeManifest['shape'],
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
//Property type enum is case-insensitive during upload; replicate that here
|
|
69
|
+
if (shapeData['properties']) {
|
|
70
|
+
for (const property of shapeData['properties']) {
|
|
71
|
+
if (typeof property['type'] == 'string') {
|
|
72
|
+
property['type'] = property['type'].toLowerCase();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
for (var key in shapeData) {
|
|
77
|
+
properties['Stencil'][key] = shapeData[key];
|
|
78
|
+
}
|
|
79
|
+
const imageMap = properties['Stencil']['images'];
|
|
80
|
+
if (imageMap) {
|
|
81
|
+
const finalImageMap = {};
|
|
82
|
+
for (const key in imageMap) {
|
|
83
|
+
if (imageMap[key]['type'] == 'url') {
|
|
84
|
+
finalImageMap[key] = imageMap[key]['path'];
|
|
85
|
+
}
|
|
86
|
+
else if (imageMap[key]['type'] == 'file') {
|
|
87
|
+
finalImageMap[key] =
|
|
88
|
+
'http://localhost:9901/shapeLibraries/' +
|
|
89
|
+
encodeURIComponent(name) +
|
|
90
|
+
'/images/' +
|
|
91
|
+
encodeURIComponent(imageMap[key]['path']);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
properties['Stencil']['images'] = finalImageMap;
|
|
95
|
+
}
|
|
96
|
+
for (var key in shapeManifest['defaults']) {
|
|
97
|
+
if (key !== 'width' && key !== 'height') {
|
|
98
|
+
const outName = (_a = defaultNameMap.get(key)) !== null && _a !== void 0 ? _a : key;
|
|
99
|
+
properties[outName] = shapeManifest['defaults'][key];
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
'class': 'CustomBlock',
|
|
104
|
+
'created': new Date().toString(),
|
|
105
|
+
'name': shapeManifest['name'],
|
|
106
|
+
'order': index,
|
|
107
|
+
'properties': JSON.stringify(properties),
|
|
108
|
+
'shapeLibrary': 'http://localhost:9901/shapeLibraries/' + encodeURIComponent(name),
|
|
109
|
+
'uri': `http://localhost:9901/shapeLibraries/${encodeURIComponent(name)}/shapes/${encodeURIComponent(shapeManifest['shape'])}`,
|
|
110
|
+
};
|
|
111
|
+
})));
|
|
112
|
+
}
|
|
113
|
+
exports.getShapeListJson = getShapeListJson;
|
|
114
|
+
async function buildShapeLibrary(name) {
|
|
115
|
+
const zip = new JSZip();
|
|
116
|
+
const addToZip = async (source) => {
|
|
117
|
+
console.log(source);
|
|
118
|
+
const stat = await fs.lstat(source);
|
|
119
|
+
if (stat.isDirectory()) {
|
|
120
|
+
const dir = await fs.readdir(source);
|
|
121
|
+
for (const file of dir) {
|
|
122
|
+
await addToZip(path.join(source, file));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
zip.file(source.replace(`shapelibraries/${name}/`, ''), await fs.readFile(source));
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
await addToZip(`shapelibraries/${name}`);
|
|
130
|
+
const zipBytes = await zip.generateAsync({ type: 'uint8array' });
|
|
131
|
+
console.log(`Writing file ${name}.lcsz`);
|
|
132
|
+
await fs.writeFile(`shapelibraries/${name}.lcsz`, zipBytes);
|
|
133
|
+
}
|
|
134
|
+
exports.buildShapeLibrary = buildShapeLibrary;
|
|
135
|
+
async function debugShapeLibraries(packagePath = '.') {
|
|
136
|
+
const app = express();
|
|
137
|
+
app.use((req, res, next) => {
|
|
138
|
+
res.header('Access-Control-Allow-Origin', '*');
|
|
139
|
+
res.header('Access-Control-Allow-Methods', 'GET, OPTIONS');
|
|
140
|
+
next();
|
|
141
|
+
});
|
|
142
|
+
//Having a version number on the shape library URL that changes each time the user updates the code
|
|
143
|
+
//allows us to refresh the toolbox in the editor mid-editor-session. Unless the URL changes for the
|
|
144
|
+
//shape library, the toolbox doesn't update.
|
|
145
|
+
let shapeLibraryVersion = Date.now();
|
|
146
|
+
app.get('/shapeLibraries', async (req, res) => {
|
|
147
|
+
var _a;
|
|
148
|
+
const manifest = await (0, packagemanifest_1.readManifest)(packagePath + '/');
|
|
149
|
+
const libraries = (_a = manifest['shapeLibraries']) !== null && _a !== void 0 ? _a : [];
|
|
150
|
+
res.send(JSON.stringify(libraries.map((libraryManifest) => {
|
|
151
|
+
return {
|
|
152
|
+
'uri': 'http://localhost:9901/shapeLibraries/' +
|
|
153
|
+
encodeURIComponent(libraryManifest['name']) +
|
|
154
|
+
'?version=' +
|
|
155
|
+
shapeLibraryVersion,
|
|
156
|
+
'user': '',
|
|
157
|
+
'created': new Date().toISOString(),
|
|
158
|
+
'name': libraryManifest['title'],
|
|
159
|
+
'size': 0,
|
|
160
|
+
'shapes': 'http://localhost:9901/shapeLibraries/' +
|
|
161
|
+
encodeURIComponent(libraryManifest['name']) +
|
|
162
|
+
'/shapes',
|
|
163
|
+
'shared': false,
|
|
164
|
+
'team-edit': false,
|
|
165
|
+
'encryption': '',
|
|
166
|
+
};
|
|
167
|
+
})));
|
|
168
|
+
});
|
|
169
|
+
app.get('/shapeLibraries/:libraryName/shapes', async (req, res) => {
|
|
170
|
+
//Find all the shapes in the given shape library, and produce JSON for the client
|
|
171
|
+
res.send(await getShapeListJson(req.params.libraryName, packagePath));
|
|
172
|
+
});
|
|
173
|
+
app.get('/shapeLibraries/:libraryName/images/:image', async (req, res) => {
|
|
174
|
+
const oldcwd = process.cwd();
|
|
175
|
+
process.chdir(packagePath);
|
|
176
|
+
//Find all the shapes in the given shape library, and produce JSON for the client
|
|
177
|
+
res.sendFile('shapelibraries/' + req.params.libraryName + '/images/' + req.params.image, {
|
|
178
|
+
root: process.cwd(),
|
|
179
|
+
});
|
|
180
|
+
process.chdir(oldcwd);
|
|
181
|
+
});
|
|
182
|
+
app.get('/shapeLibraries/');
|
|
183
|
+
const server = http.createServer(app);
|
|
184
|
+
//Watch for any shape library changes, and inform watchers that they need to refresh them.
|
|
185
|
+
const wss = new ws.Server({ server, path: '/shapeLibraries/changes' });
|
|
186
|
+
const sockets = [];
|
|
187
|
+
wss.on('connection', (ws) => {
|
|
188
|
+
sockets.push(ws);
|
|
189
|
+
});
|
|
190
|
+
chokidar.watch(packagePath + '/shapelibraries').on('all', () => {
|
|
191
|
+
shapeLibraryVersion = Date.now();
|
|
192
|
+
for (const socket of sockets) {
|
|
193
|
+
socket.send('refresh');
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
server.listen(9901, () => {
|
|
197
|
+
console.log('Listening at http://localhost:9901/shapeLibraries');
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
exports.debugShapeLibraries = debugShapeLibraries;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Images used in this shape library should be placed in this directory, and file paths referenced in the image map of any shape are relative to this location.
|