├── LICENSE.md ├── README.md ├── index.js └── package.json /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2018 Sven Schannak 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Adobe XD JSON Wrapper 2 | 3 | Adobe XD is a great tool to create interactive prototypes for user experiences. Because they have just started to develop their plugin platform we often have to use some workarounds. Due to the current implementation it is not possible to just use ```JSON.stringify``` on their nodes, artboards or documents. 4 | 5 | This library offers methods to wrap them und use ```JSON.stringify``` on them. 6 | 7 | ## Installation 8 | 9 | ```bash 10 | yarn add xd-json-wrapper 11 | ``` 12 | or 13 | 14 | ```bash 15 | npm install xd-json-wrapper 16 | ``` 17 | 18 | ## Available Methods 19 | 20 | ### getXDWrapper 21 | 22 | This wrapper wraps any node object on an artboard and makes it suitable for json. It also returns all parent elements. 23 | 24 | #### Example 25 | ```javascript 26 | import { getXDWrapper } = "xd-json-wrapper"; 27 | 28 | const node = // use an actual node from an artboard in XD here 29 | const wrappedNode = getXDWrapper(node); 30 | 31 | const json = wrappedNode.toJSON(); 32 | // or 33 | JSON.stringify(wrappedNode); 34 | ``` 35 | 36 | ### getArtboardAsJSON 37 | 38 | This wrapper wraps any artboard object and makes it suitable for json. It als returns all children nodes. 39 | 40 | #### Example 41 | ```javascript 42 | import { getArtboardAsJSON } = "xd-json-wrapper"; 43 | 44 | const artboard = // use an actual node from an artboard in XD here 45 | const wrappedArtboard = getArtboardAsJSON(artboard); 46 | 47 | const json = wrappedArtboard.toJSON(); 48 | // or 49 | JSON.stringify(wrappedArtboard); 50 | ``` 51 | 52 | ### getDocumentAsJSON 53 | 54 | This wrapper wraps the complete XD document and returns a list of all artboards and their childrend nodes. 55 | 56 | #### Example 57 | ```javascript 58 | import { getDocumentAsJSON } = "xd-json-wrapper"; 59 | 60 | const documentRoot = // use an actual node from an artboard in XD here 61 | const wrappedArtboard = getDocumentAsJSON(documentRoot); 62 | 63 | const json = documentRoot.toJSON(); 64 | // or 65 | JSON.stringify(documentRoot); 66 | ``` 67 | 68 | # Contributors 69 | The initial project was created at the Tel Aviv Design Tools Hackathon in 2018. 70 | Members of the group were: 71 | 72 | * @svschannak 73 | 74 | # MIT License 75 | 76 | Copyright (c) 2018 Sven Schannak 77 | 78 | Permission is hereby granted, free of charge, to any person obtaining a copy 79 | of this software and associated documentation files (the "Software"), to deal 80 | in the Software without restriction, including without limitation the rights 81 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 82 | copies of the Software, and to permit persons to whom the Software is 83 | furnished to do so, subject to the following conditions: 84 | 85 | The above copyright notice and this permission notice shall be included in all 86 | copies or substantial portions of the Software. 87 | 88 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 89 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 90 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 91 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 92 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 93 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 94 | SOFTWARE. -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | class XDMatrixWrapper { 2 | constructor(xdNode) { 3 | this.xdNode = xdNode; 4 | } 5 | 6 | toJSON() { 7 | let result = {}; 8 | 9 | const node = this.xdNode; 10 | return { 11 | type: node.constructor.name, 12 | 13 | ...result 14 | }; 15 | } 16 | } 17 | 18 | class XDColorWrapper { 19 | constructor(xdNode) { 20 | this.xdNode = xdNode; 21 | } 22 | 23 | toJSON() { 24 | let result = {}; 25 | 26 | const node = this.xdNode; 27 | return { 28 | type: node.constructor.name, 29 | 30 | a: node.a, 31 | r: node.r, 32 | g: node.g, 33 | b: node.b, 34 | ...result 35 | }; 36 | } 37 | } 38 | 39 | class XDLinearGradientFillWrapper { 40 | constructor(xdNode) { 41 | this.xdNode = xdNode; 42 | } 43 | 44 | toJSON() { 45 | let result = {}; 46 | 47 | const node = this.xdNode; 48 | return { 49 | type: node.constructor.name, 50 | 51 | colorStops: node.colorStops, 52 | startX: node.startX, 53 | startY: node.startY, 54 | endX: node.endX, 55 | endY: node.endY, 56 | ...result 57 | }; 58 | } 59 | } 60 | 61 | class XDRadialGradientFillWrapper { 62 | constructor(xdNode) { 63 | this.xdNode = xdNode; 64 | } 65 | 66 | toJSON() { 67 | let result = {}; 68 | 69 | const node = this.xdNode; 70 | return { 71 | type: node.constructor.name, 72 | 73 | ...result 74 | }; 75 | } 76 | } 77 | 78 | class XDImageFillWrapper { 79 | constructor(xdNode) { 80 | this.xdNode = xdNode; 81 | } 82 | 83 | toJSON() { 84 | let result = {}; 85 | 86 | const node = this.xdNode; 87 | return { 88 | type: node.constructor.name, 89 | 90 | SCALE_STRETCH: node.SCALE_STRETCH, 91 | SCALE_COVER: node.SCALE_COVER, 92 | scaleBehaviour: node.scaleBehaviour, 93 | mimeType: node.mimeType, 94 | isLinkedContent: node.isLinkedContent, 95 | naturalWidth: node.naturalWidth, 96 | naturalHeight: node.naturalHeight, 97 | ...result 98 | }; 99 | } 100 | } 101 | 102 | class XDShadowWrapper { 103 | constructor(xdNode) { 104 | this.xdNode = xdNode; 105 | } 106 | 107 | toJSON() { 108 | let result = {}; 109 | 110 | const node = this.xdNode; 111 | return { 112 | type: node.constructor.name, 113 | 114 | x: node.x, 115 | y: node.y, 116 | blur: node.blur, 117 | color: node.color, 118 | visible: node.visible, 119 | ...result 120 | }; 121 | } 122 | } 123 | 124 | class XDBlurWrapper { 125 | constructor(xdNode) { 126 | this.xdNode = xdNode; 127 | } 128 | 129 | toJSON() { 130 | let result = {}; 131 | 132 | const node = this.xdNode; 133 | return { 134 | type: node.constructor.name, 135 | 136 | blurAmount: node.blurAmount, 137 | brightnessAmount: node.brightnessAmount, 138 | fillOpacity: node.fillOpacity, 139 | isBackgroundEffect: node.isBackgroundEffect, 140 | visible: node.visible, 141 | ...result 142 | }; 143 | } 144 | } 145 | 146 | class XDSceneNodeWrapper { 147 | constructor(xdNode) { 148 | this.xdNode = xdNode; 149 | } 150 | 151 | toJSON() { 152 | let result = {}; 153 | 154 | const node = this.xdNode; 155 | return { 156 | type: node.constructor.name, 157 | 158 | guid: node.guid, 159 | parent: node.parent, 160 | children: node.children, 161 | isInArtworkTree: node.isInArtworkTree, 162 | isContainer: node.isContainer, 163 | selected: node.selected, 164 | visible: node.visible, 165 | opacity: node.opacity, 166 | transform: node.transform, 167 | translation: node.translation, 168 | rotation: node.rotation, 169 | globalBounds: node.globalBounds, 170 | localBounds: node.localBounds, 171 | boundsInParent: node.boundsInParent, 172 | topLeftInParent: node.topLeftInParent, 173 | localCenterPoint: node.localCenterPoint, 174 | globalDrawBounds: node.globalDrawBounds, 175 | name: node.name, 176 | hasDefaultName: node.hasDefaultName, 177 | locked: node.locked, 178 | markedForExport: node.markedForExport, 179 | hasLinkedContent: node.hasLinkedContent, 180 | ...result 181 | }; 182 | } 183 | } 184 | 185 | class XDGraphicsNodeWrapper { 186 | constructor(xdNode) { 187 | this.xdNode = xdNode; 188 | this.parentNodeWrapper = new XDSceneNodeWrapper(this.xdNode); 189 | } 190 | 191 | toJSON() { 192 | let result = {}; 193 | 194 | if (this.parentNodeWrapper) { 195 | result = this.parentNodeWrapper.toJSON(); 196 | } 197 | 198 | const node = this.xdNode; 199 | return { 200 | type: node.constructor.name, 201 | 202 | fill: node.fill, 203 | fillEnabled: node.fillEnabled, 204 | stroke: node.stroke, 205 | strokeEnabled: node.strokeEnabled, 206 | strokeWidth: node.strokeWidth, 207 | strokePosition: node.strokePosition, 208 | strokeEndCaps: node.strokeEndCaps, 209 | strokeJoins: node.strokeJoins, 210 | strokeMiterLimit: node.strokeMiterLimit, 211 | strokeDashArray: node.strokeDashArray, 212 | strokeDashOffset: node.strokeDashOffset, 213 | shadow: node.shadow, 214 | blur: node.blur, 215 | pathData: node.pathData, 216 | hasLinkedGraphicFill: node.hasLinkedGraphicFill, 217 | ...result 218 | }; 219 | } 220 | } 221 | 222 | class XDArtboardWrapper { 223 | constructor(xdNode) { 224 | this.xdNode = xdNode; 225 | this.parentNodeWrapper = new XDGraphicsNodeWrapper(this.xdNode); 226 | } 227 | 228 | toJSON() { 229 | let result = {}; 230 | 231 | if (this.parentNodeWrapper) { 232 | result = this.parentNodeWrapper.toJSON(); 233 | } 234 | 235 | const node = this.xdNode; 236 | return { 237 | type: node.constructor.name, 238 | 239 | width: node.width, 240 | height: node.height, 241 | viewportHeight: node.viewportHeight, 242 | ...result 243 | }; 244 | } 245 | } 246 | 247 | class XDRectangleWrapper { 248 | constructor(xdNode) { 249 | this.xdNode = xdNode; 250 | this.parentNodeWrapper = new XDGraphicsNodeWrapper(this.xdNode); 251 | } 252 | 253 | toJSON() { 254 | let result = {}; 255 | 256 | if (this.parentNodeWrapper) { 257 | result = this.parentNodeWrapper.toJSON(); 258 | } 259 | 260 | const node = this.xdNode; 261 | return { 262 | type: node.constructor.name, 263 | 264 | width: node.width, 265 | height: node.height, 266 | cornerRadii: node.cornerRadii, 267 | hasRoundedCorners: node.hasRoundedCorners, 268 | effectiveCornerRadii: node.effectiveCornerRadii, 269 | ...result 270 | }; 271 | } 272 | } 273 | 274 | class XDEllipseWrapper { 275 | constructor(xdNode) { 276 | this.xdNode = xdNode; 277 | this.parentNodeWrapper = new XDGraphicsNodeWrapper(this.xdNode); 278 | } 279 | 280 | toJSON() { 281 | let result = {}; 282 | 283 | if (this.parentNodeWrapper) { 284 | result = this.parentNodeWrapper.toJSON(); 285 | } 286 | 287 | const node = this.xdNode; 288 | return { 289 | type: node.constructor.name, 290 | 291 | radiusX: node.radiusX, 292 | radiusY: node.radiusY, 293 | isCircle: node.isCircle, 294 | ...result 295 | }; 296 | } 297 | } 298 | 299 | class XDLineWrapper { 300 | constructor(xdNode) { 301 | this.xdNode = xdNode; 302 | this.parentNodeWrapper = new XDGraphicsNodeWrapper(this.xdNode); 303 | } 304 | 305 | toJSON() { 306 | let result = {}; 307 | 308 | if (this.parentNodeWrapper) { 309 | result = this.parentNodeWrapper.toJSON(); 310 | } 311 | 312 | const node = this.xdNode; 313 | return { 314 | type: node.constructor.name, 315 | 316 | start: node.start, 317 | end: node.end, 318 | ...result 319 | }; 320 | } 321 | } 322 | 323 | class XDPathWrapper { 324 | constructor(xdNode) { 325 | this.xdNode = xdNode; 326 | this.parentNodeWrapper = new XDGraphicsNodeWrapper(this.xdNode); 327 | } 328 | 329 | toJSON() { 330 | let result = {}; 331 | 332 | if (this.parentNodeWrapper) { 333 | result = this.parentNodeWrapper.toJSON(); 334 | } 335 | 336 | const node = this.xdNode; 337 | return { 338 | type: node.constructor.name, 339 | 340 | pathData: node.pathData, 341 | ...result 342 | }; 343 | } 344 | } 345 | 346 | class XDBooleanGroupWrapper { 347 | constructor(xdNode) { 348 | this.xdNode = xdNode; 349 | this.parentNodeWrapper = new XDGraphicsNodeWrapper(this.xdNode); 350 | } 351 | 352 | toJSON() { 353 | let result = {}; 354 | 355 | if (this.parentNodeWrapper) { 356 | result = this.parentNodeWrapper.toJSON(); 357 | } 358 | 359 | const node = this.xdNode; 360 | return { 361 | type: node.constructor.name, 362 | 363 | pathOp: node.pathOp, 364 | ...result 365 | }; 366 | } 367 | } 368 | 369 | class XDTextWrapper { 370 | constructor(xdNode) { 371 | this.xdNode = xdNode; 372 | this.parentNodeWrapper = new XDGraphicsNodeWrapper(this.xdNode); 373 | } 374 | 375 | toJSON() { 376 | let result = {}; 377 | 378 | if (this.parentNodeWrapper) { 379 | result = this.parentNodeWrapper.toJSON(); 380 | } 381 | 382 | const node = this.xdNode; 383 | return { 384 | type: node.constructor.name, 385 | 386 | text: node.text, 387 | styleRanges: node.styleRanges, 388 | flipY: node.flipY, 389 | textAlign: node.textAlign, 390 | lineSpacing: node.lineSpacing, 391 | areaBox: node.areaBox, 392 | clippedByArea: node.clippedByArea, 393 | ...result 394 | }; 395 | } 396 | } 397 | 398 | class XDGroupWrapper { 399 | constructor(xdNode) { 400 | this.xdNode = xdNode; 401 | this.parentNodeWrapper = new XDSceneNodeWrapper(this.xdNode); 402 | } 403 | 404 | toJSON() { 405 | let result = {}; 406 | 407 | if (this.parentNodeWrapper) { 408 | result = this.parentNodeWrapper.toJSON(); 409 | } 410 | 411 | const node = this.xdNode; 412 | return { 413 | type: node.constructor.name, 414 | 415 | mask: node.mask, 416 | ...result 417 | }; 418 | } 419 | } 420 | 421 | class XDSymbolInstanceWrapper { 422 | constructor(xdNode) { 423 | this.xdNode = xdNode; 424 | this.parentNodeWrapper = new XDSceneNodeWrapper(this.xdNode); 425 | } 426 | 427 | toJSON() { 428 | let result = {}; 429 | 430 | if (this.parentNodeWrapper) { 431 | result = this.parentNodeWrapper.toJSON(); 432 | } 433 | 434 | const node = this.xdNode; 435 | return { 436 | type: node.constructor.name, 437 | 438 | symbolId: node.symbolId, 439 | ...result 440 | }; 441 | } 442 | } 443 | 444 | class XDRepeatGridWrapper { 445 | constructor(xdNode) { 446 | this.xdNode = xdNode; 447 | this.parentNodeWrapper = new XDSceneNodeWrapper(this.xdNode); 448 | } 449 | 450 | toJSON() { 451 | let result = {}; 452 | 453 | if (this.parentNodeWrapper) { 454 | result = this.parentNodeWrapper.toJSON(); 455 | } 456 | 457 | const node = this.xdNode; 458 | return { 459 | type: node.constructor.name, 460 | 461 | width: node.width, 462 | height: node.height, 463 | numColumns: node.numColumns, 464 | numRows: node.numRows, 465 | paddingX: node.paddingX, 466 | paddingY: node.paddingY, 467 | cellSize: node.cellSize, 468 | ...result 469 | }; 470 | } 471 | } 472 | 473 | class XDLinkedGraphicWrapper { 474 | constructor(xdNode) { 475 | this.xdNode = xdNode; 476 | this.parentNodeWrapper = new XDSceneNodeWrapper(this.xdNode); 477 | } 478 | 479 | toJSON() { 480 | let result = {}; 481 | 482 | if (this.parentNodeWrapper) { 483 | result = this.parentNodeWrapper.toJSON(); 484 | } 485 | 486 | const node = this.xdNode; 487 | return { 488 | type: node.constructor.name, 489 | 490 | ...result 491 | }; 492 | } 493 | } 494 | 495 | class XDRootNodeWrapper { 496 | constructor(xdNode) { 497 | this.xdNode = xdNode; 498 | this.parentNodeWrapper = new XDSceneNodeWrapper(this.xdNode); 499 | } 500 | 501 | toJSON() { 502 | let result = {}; 503 | 504 | if (this.parentNodeWrapper) { 505 | result = this.parentNodeWrapper.toJSON(); 506 | } 507 | 508 | const node = this.xdNode; 509 | return { 510 | type: node.constructor.name, 511 | 512 | ...result 513 | }; 514 | } 515 | } 516 | 517 | const WRAPPER_ASSIGNMENTS = { 518 | Matrix: XDMatrixWrapper, 519 | Color: XDColorWrapper, 520 | LinearGradientFill: XDLinearGradientFillWrapper, 521 | RadialGradientFill: XDRadialGradientFillWrapper, 522 | ImageFill: XDImageFillWrapper, 523 | Shadow: XDShadowWrapper, 524 | Blur: XDBlurWrapper, 525 | SceneNode: XDSceneNodeWrapper, 526 | GraphicsNode: XDGraphicsNodeWrapper, 527 | Artboard: XDArtboardWrapper, 528 | Rectangle: XDRectangleWrapper, 529 | Ellipse: XDEllipseWrapper, 530 | Line: XDLineWrapper, 531 | Path: XDPathWrapper, 532 | BooleanGroup: XDBooleanGroupWrapper, 533 | Text: XDTextWrapper, 534 | Group: XDGroupWrapper, 535 | SymbolInstance: XDSymbolInstanceWrapper, 536 | RepeatGrid: XDRepeatGridWrapper, 537 | LinkedGraphic: XDLinkedGraphicWrapper, 538 | RootNode: XDRootNodeWrapper 539 | }; 540 | 541 | function getXDWrapper(node) { 542 | const wrapperName = WRAPPER_ASSIGNMENTS[node.constructor.name]; 543 | if (wrapperName !== undefined) { 544 | return new wrapperName(node); 545 | } 546 | 547 | return undefined; 548 | } 549 | 550 | function getArtboardAsJSON(artboard) { 551 | const children = []; 552 | 553 | artboard.children.forEach(node => { 554 | if (getXDWrapper(node) !== undefined) { 555 | children.push(getXDWrapper(node)); 556 | } 557 | }); 558 | 559 | return children; 560 | } 561 | 562 | function getDocumentAsJSON(documentRoot) { 563 | const children = []; 564 | 565 | documentRoot.children.forEach(artboard => { 566 | children.push(getArtboardAsJSON(artboard)); 567 | }); 568 | 569 | return children; 570 | } 571 | 572 | module.exports = { 573 | getXDWrapper: getXDWrapper, 574 | getArtboardAsJSON: getArtboardAsJSON, 575 | getDocumentAsJSON: getDocumentAsJSON 576 | }; 577 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xd-json-wrapper", 3 | "version": "1.0.0", 4 | "description": "Wrapper for XD Elements, Artboard and document to convert to json", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/svschannak/xd-json-wrapper.git" 12 | }, 13 | "keywords": [ 14 | "adobe", 15 | "xd", 16 | "json", 17 | "helper", 18 | "wrapper" 19 | ], 20 | "author": "Sven Schannak", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/svschannak/xd-json-wrapper/issues" 24 | }, 25 | "homepage": "https://github.com/svschannak/xd-json-wrapper#readme" 26 | } 27 | --------------------------------------------------------------------------------