From 6da0d22d1b242251143cec0afcfa95f9e3015dde Mon Sep 17 00:00:00 2001 From: Paulo Veiga Date: Wed, 29 Dec 2021 18:35:58 +0000 Subject: [PATCH] Merged in feature/browser_export (pull request #20) Introduce typescript * Configure typescript support for jets * Fix cycle dependency * Configure typescript * Migrate mindplot to typescript * Merge branch 'develop' into feature/browser_export --- packages/mindplot/babel.config.json | 6 +- packages/mindplot/jest.config.js | 16 +-- packages/mindplot/package.json | 6 +- packages/mindplot/src/components/Designer.js | 4 +- packages/mindplot/src/components/Topic.js | 22 ++-- .../mindplot/src/components/TopicFeature.js | 36 +----- .../src/components/export/Exporter.ts | 7 + .../src/components/export/TxtExporter.ts | 10 ++ .../components/model/FeatureModelFactory.ts | 37 ++++++ .../model/{IMindmap.js => IMindmap.ts} | 78 +++-------- .../model/{Mindmap.js => Mindmap.ts} | 62 ++++----- .../src/components/model/NodeModel.js | 4 +- .../persistence/XMLSerializerBeta.js | 8 +- .../persistence/XMLSerializerPela.js | 6 +- packages/mindplot/src/index.js | 4 +- .../test/unit/TxTExportTestSuite.test.ts | 9 ++ packages/mindplot/tsconfig.json | 13 ++ packages/mindplot/webpack.common.js | 10 +- packages/mindplot/webpack.prod.js | 2 +- yarn.lock | 122 ++++++++++++++---- 20 files changed, 275 insertions(+), 187 deletions(-) create mode 100644 packages/mindplot/src/components/export/Exporter.ts create mode 100644 packages/mindplot/src/components/export/TxtExporter.ts create mode 100644 packages/mindplot/src/components/model/FeatureModelFactory.ts rename packages/mindplot/src/components/model/{IMindmap.js => IMindmap.ts} (66%) rename packages/mindplot/src/components/model/{Mindmap.js => Mindmap.ts} (78%) create mode 100644 packages/mindplot/test/unit/TxTExportTestSuite.test.ts create mode 100644 packages/mindplot/tsconfig.json diff --git a/packages/mindplot/babel.config.json b/packages/mindplot/babel.config.json index 8d99cd1d..386fd8fa 100644 --- a/packages/mindplot/babel.config.json +++ b/packages/mindplot/babel.config.json @@ -7,8 +7,12 @@ "targets": { "esmodules": true } - } + }, + "@babel/preset-typescript" ] ], + "plugins": [ + "@babel/plugin-proposal-class-properties" + ], "sourceType": "module" } diff --git a/packages/mindplot/jest.config.js b/packages/mindplot/jest.config.js index 82e518eb..a3831d95 100644 --- a/packages/mindplot/jest.config.js +++ b/packages/mindplot/jest.config.js @@ -1,23 +1,11 @@ -// Sync object -/** @type {import('@jest/types').Config.InitialOptions} */ const config = { testEnvironment: 'jsdom', verbose: true, - moduleFileExtensions: ['js'], + preset: 'ts-jest', + moduleFileExtensions: ['js', 'ts'], transform: { '^.+\\.js?$': 'babel-jest', }, - moduleNameMapper: { - '^@libraries(.*)$': '../../libraries$1', - '^@commands(.*)$': '/src/components/commands$1', - '^@layout(.*)$': '/src/components/layout$1', - '^@libs(.*)$': '/src/components/libraries$1', - '^@model(.*)$': '/src/components/model$1', - '^@persistence(.*)$': '/src/components/persistence$1', - '^@util(.*)$': '/src/components/util$1', - '^@widget(.*)$': '/src/components/widget$1', - '^@components(.*)$': '/src/components$1', - }, }; module.exports = config; diff --git a/packages/mindplot/package.json b/packages/mindplot/package.json index 18d4e076..6fc1c59f 100644 --- a/packages/mindplot/package.json +++ b/packages/mindplot/package.json @@ -26,7 +26,7 @@ "lint": "eslint src", "playground": "webpack serve --config webpack.playground.js", "cy:run": "cypress run", - "test:unit": "jest ./test/unit/*.js", + "test:unit": "jest ./test/unit/*.ts ./test/unit/*.js", "test:integration": "start-server-and-test playground http-get://localhost:8081 cy:run", "test": "yarn test:unit && yarn test:integration" }, @@ -41,7 +41,9 @@ "devDependencies": { "@babel/core": "^7.14.6", "@babel/preset-env": "^7.14.7", + "@babel/preset-typescript": "^7.16.5", "@babel/register": "^7.16.0", + "@types/jest": "^27.0.3", "babel-loader": "^8.2.2", "clean-webpack-plugin": "^4.0.0-alpha.0", "compression-webpack-plugin": "^9.2.0", @@ -62,6 +64,8 @@ "mocha": "^9.1.3", "nodemon": "^2.0.12", "start-server-and-test": "^1.14.0", + "ts-jest": "^27.1.2", + "ts-loader": "^9.2.6", "webpack": "^5.44.0", "webpack-bundle-analyzer": "^4.5.0", "webpack-cli": "^4.7.2", diff --git a/packages/mindplot/src/components/Designer.js b/packages/mindplot/src/components/Designer.js index a4d164e9..a6b55aa8 100644 --- a/packages/mindplot/src/components/Designer.js +++ b/packages/mindplot/src/components/Designer.js @@ -37,7 +37,7 @@ import RelationshipPivot from './RelationshipPivot'; import Relationship from './Relationship'; import TopicEventDispatcher, { TopicEvent } from './TopicEventDispatcher'; -import TopicFeature from './TopicFeature'; +import TopicFeatureFactory from './TopicFeature'; import { create } from './NodeGraphUtils'; @@ -952,7 +952,7 @@ class Designer extends Events { addIconType(iconType) { const topicsIds = this.getModel().filterTopicsIds(); if (topicsIds.length > 0) { - this._actionDispatcher.addFeatureToTopic(topicsIds[0], TopicFeature.Icon.id, { + this._actionDispatcher.addFeatureToTopic(topicsIds[0], TopicFeatureFactory.Icon.id, { id: iconType, }); } diff --git a/packages/mindplot/src/components/Topic.js b/packages/mindplot/src/components/Topic.js index 9b8646ad..834bf966 100644 --- a/packages/mindplot/src/components/Topic.js +++ b/packages/mindplot/src/components/Topic.js @@ -31,7 +31,7 @@ import { import NodeGraph from './NodeGraph'; import TopicConfig from './TopicConfig'; import TopicStyle from './TopicStyle'; -import TopicFeature from './TopicFeature'; +import TopicFeatureFactory from './TopicFeature'; import ConnectionLine from './ConnectionLine'; import IconGroup from './IconGroup'; import FadeEffect from './util/FadeEffect'; @@ -311,8 +311,8 @@ class Topic extends NodeGraph { const model = this.getModel(); const featuresModel = model.getFeatures(); featuresModel.forEach((f) => { - const icon = TopicFeature.createIcon(this, f, this.isReadOnly()); - result.addIcon(icon, f.getType() === TopicFeature.Icon.id && !this.isReadOnly()); + const icon = TopicFeatureFactory.createIcon(this, f, this.isReadOnly()); + result.addIcon(icon, f.getType() === TopicFeatureFactory.Icon.id && !this.isReadOnly()); }); return result; @@ -331,10 +331,10 @@ class Topic extends NodeGraph { const model = this.getModel(); model.addFeature(featureModel); - const result = TopicFeature.createIcon(this, featureModel, this.isReadOnly()); + const result = TopicFeatureFactory.createIcon(this, featureModel, this.isReadOnly()); iconGroup.addIcon( result, - featureModel.getType() === TopicFeature.Icon.id && !this.isReadOnly(), + featureModel.getType() === TopicFeatureFactory.Icon.id && !this.isReadOnly(), ); this._adjustShapes(); @@ -764,7 +764,7 @@ class Topic extends NodeGraph { const model = this.getModel(); const editorModel = { getValue() { - const notes = model.findFeatureByType(TopicFeature.Note.id); + const notes = model.findFeatureByType(TopicFeatureFactory.Note.id); let result; if (notes.length > 0) result = notes[0].getText(); @@ -773,7 +773,7 @@ class Topic extends NodeGraph { setValue(value) { const dispatcher = ActionDispatcher.getInstance(); - const notes = model.findFeatureByType(TopicFeature.Note.id); + const notes = model.findFeatureByType(TopicFeatureFactory.Note.id); if (!$defined(value)) { const featureId = notes[0].getId(); dispatcher.removeFeatureFromTopic(topicId, featureId); @@ -782,7 +782,7 @@ class Topic extends NodeGraph { text: value, }); } else { - dispatcher.addFeatureToTopic(topicId, TopicFeature.Note.id, { + dispatcher.addFeatureToTopic(topicId, TopicFeatureFactory.Note.id, { text: value, }); } @@ -800,7 +800,7 @@ class Topic extends NodeGraph { const editorModel = { getValue() { // @param {mindplot.model.LinkModel[]} links - const links = model.findFeatureByType(TopicFeature.Link.id); + const links = model.findFeatureByType(TopicFeatureFactory.Link.id); let result; if (links.length > 0) result = links[0].getUrl(); @@ -809,7 +809,7 @@ class Topic extends NodeGraph { setValue(value) { const dispatcher = ActionDispatcher.getInstance(); - const links = model.findFeatureByType(TopicFeature.Link.id); + const links = model.findFeatureByType(TopicFeatureFactory.Link.id); if (!$defined(value)) { const featureId = links[0].getId(); dispatcher.removeFeatureFromTopic(topicId, featureId); @@ -818,7 +818,7 @@ class Topic extends NodeGraph { url: value, }); } else { - dispatcher.addFeatureToTopic(topicId, TopicFeature.Link.id, { + dispatcher.addFeatureToTopic(topicId, TopicFeatureFactory.Link.id, { url: value, }); } diff --git a/packages/mindplot/src/components/TopicFeature.js b/packages/mindplot/src/components/TopicFeature.js index 1a9b5674..d7186010 100644 --- a/packages/mindplot/src/components/TopicFeature.js +++ b/packages/mindplot/src/components/TopicFeature.js @@ -24,53 +24,25 @@ import LinkIcon from './LinkIcon'; import NoteModel from './model/NoteModel'; import NoteIcon from './NoteIcon'; -const TopicFeature = { +const TopicFeatureFactory = { /** the icon object */ Icon: { id: IconModel.FEATURE_TYPE, - model: IconModel, icon: ImageIcon, }, /** the link object */ Link: { id: LinkModel.FEATURE_TYPE, - model: LinkModel, icon: LinkIcon, }, /** the note object */ Note: { id: NoteModel.FEATURE_TYPE, - model: NoteModel, icon: NoteIcon, }, - /** - * @param id the feature metadata id - * @return {Boolean} returns true if the given id is contained in the metadata array - */ - isSupported(id) { - return TopicFeature._featuresMetadataById.some((elem) => elem.id === id); - }, - - /** - * @param type - * @param attributes - * @throws will throw an error if type is null or undefined - * @throws will throw an error if attributes is null or undefined - * @return {mindplot.model.FeatureModel} a new instance of the feature model subclass matching - * the topic feature - */ - createModel(type, attributes) { - $assert(type, 'type can not be null'); - $assert(attributes, 'attributes can not be null'); - - const { model: Model } = TopicFeature._featuresMetadataById - .filter((elem) => elem.id === type)[0]; - return new Model(attributes); - }, - /** * @param {mindplot.Topic} topic * @param {mindplot.model.FeatureModel} model @@ -83,12 +55,12 @@ const TopicFeature = { $assert(topic, 'topic can not be null'); $assert(model, 'model can not be null'); - const { icon: Icon } = TopicFeature._featuresMetadataById + const { icon: Icon } = TopicFeatureFactory._featuresMetadataById .filter((elem) => elem.id === model.getType())[0]; return new Icon(topic, model, readOnly); }, }; -TopicFeature._featuresMetadataById = [TopicFeature.Icon, TopicFeature.Link, TopicFeature.Note]; +TopicFeatureFactory._featuresMetadataById = [TopicFeatureFactory.Icon, TopicFeatureFactory.Link, TopicFeatureFactory.Note]; -export default TopicFeature; +export default TopicFeatureFactory; diff --git a/packages/mindplot/src/components/export/Exporter.ts b/packages/mindplot/src/components/export/Exporter.ts new file mode 100644 index 00000000..4086a7ee --- /dev/null +++ b/packages/mindplot/src/components/export/Exporter.ts @@ -0,0 +1,7 @@ +import Mindmap from '../model/Mindmap'; + +interface Exporter { + export(mindplot: Mindmap): string; +} + +export default Exporter; \ No newline at end of file diff --git a/packages/mindplot/src/components/export/TxtExporter.ts b/packages/mindplot/src/components/export/TxtExporter.ts new file mode 100644 index 00000000..2b45e548 --- /dev/null +++ b/packages/mindplot/src/components/export/TxtExporter.ts @@ -0,0 +1,10 @@ +import Exporter from "./Exporter"; +import Mindmap from "../model/Mindmap"; + + +class TextExporter implements Exporter { + export(mindplot: Mindmap): string { + return "some value"; + } +} +export default TextExporter; \ No newline at end of file diff --git a/packages/mindplot/src/components/model/FeatureModelFactory.ts b/packages/mindplot/src/components/model/FeatureModelFactory.ts new file mode 100644 index 00000000..8f871c09 --- /dev/null +++ b/packages/mindplot/src/components/model/FeatureModelFactory.ts @@ -0,0 +1,37 @@ +import { $assert } from '@wisemapping/core-js'; +import IconModel from './IconModel'; +import LinkModel from './LinkModel'; +import NoteModel from './NoteModel'; +import FeatureModel from './FeatureModel'; + +class FeatureModelFactory { + + private static modelById = [{ + id: IconModel.FEATURE_TYPE, + model: IconModel, + }, { + id: LinkModel.FEATURE_TYPE, + model: LinkModel, + }, { + id: NoteModel.FEATURE_TYPE, + model: NoteModel, + }] as const; + + static createModel(type: string, attributes): FeatureModel { + $assert(type, 'type can not be null'); + $assert(attributes, 'attributes can not be null'); + + const { model: Model } = FeatureModelFactory.modelById + .filter((elem) => elem.id === type)[0]; + return new Model(attributes); + } + /** + * @param id the feature metadata id + * @return {Boolean} returns true if the given id is contained in the metadata array + */ + static isSupported(type: string): boolean { + return FeatureModelFactory.modelById.some((elem) => elem.id === type); + } +}; + +export default FeatureModelFactory; diff --git a/packages/mindplot/src/components/model/IMindmap.js b/packages/mindplot/src/components/model/IMindmap.ts similarity index 66% rename from packages/mindplot/src/components/model/IMindmap.js rename to packages/mindplot/src/components/model/IMindmap.ts index f71f7865..3533554d 100644 --- a/packages/mindplot/src/components/model/IMindmap.js +++ b/packages/mindplot/src/components/model/IMindmap.ts @@ -18,68 +18,35 @@ * limitations under the License. */ import { $assert } from '@wisemapping/core-js'; +import NodeModel from './NodeModel'; +import RelationshipModel from './RelationshipModel'; -class IMindmap { - getCentralTopic() { +abstract class IMindmap { + getCentralTopic(): NodeModel { return this.getBranches()[0]; } - /** @abstract */ - getDescription() { - throw new Error('Unsupported operation'); - } + abstract getDescription(): string; - /** @abstract */ - setDescription(value) { - throw new Error('Unsupported operation'); - } + abstract setDescription(value: string); - /** @abstract */ - getId() { - throw new Error('Unsupported operation'); - } + abstract getId(): string - /** @abstract */ - setId(id) { - throw new Error('Unsupported operation'); - } + abstract setId(id: string); - /** @abstract */ - getVersion() { - throw new Error('Unsupported operation'); - } + abstract getVersion(): string; - /** @abstract */ - setVersion(version) { - throw new Error('Unsupported operation'); - } + abstract setVersion(version: string): void; - /** @abstract */ - addBranch(nodeModel) { - throw new Error('Unsupported operation'); - } + abstract addBranch(nodeModel: NodeModel): void; - /** @abstract */ - getBranches() { - throw new Error('Unsupported operation'); - } + abstract getBranches(): Array; - /** @abstract */ - removeBranch(node) { - throw new Error('Unsupported operation'); - } + abstract removeBranch(node: NodeModel): void; - /** @abstract */ - getRelationships() { - throw new Error('Unsupported operation'); - } + abstract getRelationships(): Array; - /** - * @param parent - * @param child - * @throws will throw an error if child already has a connection to a parent node - */ - connect(parent, child) { + connect(parent: NodeModel, child: NodeModel): void { // Child already has a parent ? $assert(!child.getParent(), 'Child model seems to be already connected'); @@ -95,7 +62,7 @@ class IMindmap { * @throws will throw an error if child is null or undefined * @throws will throw an error if child's parent cannot be found */ - disconnect(child) { + disconnect(child: NodeModel): void { const parent = child.getParent(); $assert(child, 'Child can not be null.'); $assert(parent, 'Child model seems to be already connected'); @@ -114,18 +81,11 @@ class IMindmap { throw new Error('Unsupported operation'); } - /** @abstract */ - createRelationship(fromNode, toNode) { - throw new Error('Unsupported operation'); - } + abstract createRelationship(fromNode: NodeModel, toNode: NodeModel): void; - /** @abstract */ - addRelationship(rel) { - throw new Error('Unsupported operation'); - } + abstract addRelationship(rel: RelationshipModel): void; - /** @abstract */ - deleteRelationship(relationship) { + deleteRelationship(relationship: RelationshipModel): void { throw new Error('Unsupported operation'); } diff --git a/packages/mindplot/src/components/model/Mindmap.js b/packages/mindplot/src/components/model/Mindmap.ts similarity index 78% rename from packages/mindplot/src/components/model/Mindmap.js rename to packages/mindplot/src/components/model/Mindmap.ts index 5de44f18..01b3401f 100644 --- a/packages/mindplot/src/components/model/Mindmap.js +++ b/packages/mindplot/src/components/model/Mindmap.ts @@ -23,43 +23,51 @@ import RelationshipModel from './RelationshipModel'; import ModelCodeName from '../persistence/ModelCodeName'; class Mindmap extends IMindmap { - constructor(id, version) { + _description: string; + _version: string; + _id: string; + _branches: Array; + _relationships: Array; + + constructor(id: string, version: string = ModelCodeName.TANGO) { super(); $assert(id, 'Id can not be null'); + $assert($defined(version), 'Version can not be null'); + this._branches = []; this._description = null; this._relationships = []; - this._version = $defined(version) ? version : ModelCodeName.TANGO; + this._version = version; this._id = id; } /** */ - getDescription() { + getDescription(): string { return this._description; } /** */ - setDescription(value) { + setDescription(value: string) { this._description = value; } /** */ - getId() { + getId(): string { return this._id; } /** */ - setId(id) { + setId(id: string) { this._id = id; } /** */ - getVersion() { + getVersion(): string { return this._version; } /** */ - setVersion(version) { + setVersion(version: string): void { this._version = version; } @@ -68,7 +76,7 @@ class Mindmap extends IMindmap { * @throws will throw an error if nodeModel is null, undefined or not a node model object * @throws will throw an error if */ - addBranch(nodeModel) { + addBranch(nodeModel: NodeModel): void { $assert(nodeModel && nodeModel.isNodeModel(), 'Add node must be invoked with model objects'); const branches = this.getBranches(); if (branches.length === 0) { @@ -84,18 +92,16 @@ class Mindmap extends IMindmap { /** * @param nodeModel */ - removeBranch(nodeModel) { + removeBranch(nodeModel: NodeModel): void { $assert(nodeModel && nodeModel.isNodeModel(), 'Remove node must be invoked with model objects'); this._branches = this._branches.filter((b) => b !== nodeModel); } - /** */ getBranches() { return this._branches; } - /** */ - getRelationships() { + getRelationships(): Array { return this._relationships; } @@ -103,7 +109,7 @@ class Mindmap extends IMindmap { * @param node * @return {Boolean} true if node already exists */ - hasAlreadyAdded(node) { + hasAlreadyAdded(node: any) { let result = false; // Check in not connected nodes. @@ -121,7 +127,7 @@ class Mindmap extends IMindmap { * @param id * @return the node model created */ - createNode(type = INodeModel.MAIN_TOPIC_TYPE, id) { + createNode(type = INodeModel.MAIN_TOPIC_TYPE, id: number) { return new NodeModel(type, this, id); } @@ -132,7 +138,7 @@ class Mindmap extends IMindmap { * @throws will throw an error if target node is null or undefined * @return the relationship model created */ - createRelationship(sourceNodeId, targetNodeId) { + createRelationship(sourceNodeId: any, targetNodeId: any) { $assert($defined(sourceNodeId), 'from node cannot be null'); $assert($defined(targetNodeId), 'to node cannot be null'); @@ -142,18 +148,18 @@ class Mindmap extends IMindmap { /** * @param relationship */ - addRelationship(relationship) { + addRelationship(relationship: RelationshipModel) { this._relationships.push(relationship); } /** * @param relationship */ - deleteRelationship(relationship) { + deleteRelationship(relationship: RelationshipModel) { this._relationships = this._relationships.filter((r) => r !== relationship); } - findNodeById(id) { + findNodeById(id: any) { let result = null; for (let i = 0; i < this._branches.length; i++) { const branch = this._branches[i]; @@ -164,17 +170,13 @@ class Mindmap extends IMindmap { } return result; } + + static buildEmpty = (mapId: string) => { + const result = new Mindmap(mapId); + const node = result.createNode(INodeModel.CENTRAL_TOPIC_TYPE, 0); + result.addBranch(node); + return result; + }; } -/** - * @param mapId - * @return an empty mindmap with central topic only - */ -Mindmap.buildEmpty = (mapId) => { - const result = new Mindmap(mapId); - const node = result.createNode(INodeModel.CENTRAL_TOPIC_TYPE, 0); - result.addBranch(node); - return result; -}; - export default Mindmap; diff --git a/packages/mindplot/src/components/model/NodeModel.js b/packages/mindplot/src/components/model/NodeModel.js index 3a301e11..af440777 100644 --- a/packages/mindplot/src/components/model/NodeModel.js +++ b/packages/mindplot/src/components/model/NodeModel.js @@ -18,7 +18,7 @@ import { $assert, $defined } from '@wisemapping/core-js'; import cloneDeep from 'lodash/cloneDeep'; import INodeModel from './INodeModel'; -import TopicFeature from '../TopicFeature'; +import FeatureModelFactory from './FeatureModelFactory'; class NodeModel extends INodeModel { constructor(type, mindmap, id) { @@ -40,7 +40,7 @@ class NodeModel extends INodeModel { * @return {mindplot.model.FeatureModel} the created feature model */ createFeature(type, attributes) { - return TopicFeature.createModel(type, attributes); + return FeatureModelFactory.createModel(type, attributes); } /** diff --git a/packages/mindplot/src/components/persistence/XMLSerializerBeta.js b/packages/mindplot/src/components/persistence/XMLSerializerBeta.js index 1453174a..af96a417 100644 --- a/packages/mindplot/src/components/persistence/XMLSerializerBeta.js +++ b/packages/mindplot/src/components/persistence/XMLSerializerBeta.js @@ -20,7 +20,7 @@ import { import ModelCodeName from './ModelCodeName'; import Mindmap from '../model/Mindmap'; import INodeModel from '../model/INodeModel'; -import TopicFeature from '../TopicFeature'; +import FeatureModelFactory from '../model/FeatureModelFactory'; class XMLSerializerBeta { toXML(mindmap) { @@ -306,16 +306,16 @@ class XMLSerializerBeta { _deserializeIcon(domElem) { let icon = domElem.getAttribute('id'); icon = icon.replace('images/', 'icons/legacy/'); - return TopicFeature.createModel(TopicFeature.Icon.id, { id: icon }); + return FeatureModelFactory.createModel(FeatureModelFactory.Icon.id, { id: icon }); } _deserializeLink(domElem) { - return TopicFeature.createModel(TopicFeature.Link.id, { url: domElem.getAttribute('url') }); + return FeatureModelFactory.createModel(FeatureModelFactory.Link.id, { url: domElem.getAttribute('url') }); } _deserializeNote(domElem) { const text = domElem.getAttribute('text'); - return TopicFeature.createModel(TopicFeature.Note.id, { text: text == null ? ' ' : text }); + return FeatureModelFactory.createModel(FeatureModelFactory.Note.id, { text: text == null ? ' ' : text }); } } diff --git a/packages/mindplot/src/components/persistence/XMLSerializerPela.js b/packages/mindplot/src/components/persistence/XMLSerializerPela.js index cd718437..b258954c 100644 --- a/packages/mindplot/src/components/persistence/XMLSerializerPela.js +++ b/packages/mindplot/src/components/persistence/XMLSerializerPela.js @@ -19,8 +19,8 @@ import { $assert, $defined, createDocument } from '@wisemapping/core-js'; import { Point } from '@wisemapping/web2d'; import Mindmap from '../model/Mindmap'; import INodeModel, { TopicShape } from '../model/INodeModel'; -import TopicFeature from '../TopicFeature'; import ConnectionLine from '../ConnectionLine'; +import FeatureModelFactory from '../model/FeatureModelFactory'; class XMLSerializerPela { toXML(mindmap) { @@ -371,7 +371,7 @@ class XMLSerializerPela { if (child.tagName === 'topic') { const childTopic = this._deserializeNode(child, mindmap); childTopic.connectTo(topic); - } else if (TopicFeature.isSupported(child.tagName)) { + } else if (FeatureModelFactory.isSupported(child.tagName)) { // Load attributes ... const namedNodeMap = child.attributes; const attributes = {}; @@ -388,7 +388,7 @@ class XMLSerializerPela { // Create a new element .... const featureType = child.tagName; - const feature = TopicFeature.createModel(featureType, attributes); + const feature = FeatureModelFactory.createModel(featureType, attributes); topic.addFeature(feature); } else if (child.tagName === 'text') { const nodeText = XMLSerializerPela._deserializeNodeText(child); diff --git a/packages/mindplot/src/index.js b/packages/mindplot/src/index.js index d4cd1edd..d40bccb0 100644 --- a/packages/mindplot/src/index.js +++ b/packages/mindplot/src/index.js @@ -22,6 +22,7 @@ import Mindmap from './components/model/Mindmap'; import PersistenceManager from './components/PersistenceManager'; import Designer from './components/Designer'; import LocalStorageManager from './components/LocalStorageManager'; +import TxtExporter from './components/export/TxtExporter'; import Menu from './components/widget/Menu'; @@ -36,4 +37,5 @@ export { LocalStorageManager, Menu, DesignerBuilder, -}; \ No newline at end of file + TxtExporter, +}; diff --git a/packages/mindplot/test/unit/TxTExportTestSuite.test.ts b/packages/mindplot/test/unit/TxTExportTestSuite.test.ts new file mode 100644 index 00000000..4f48ef01 --- /dev/null +++ b/packages/mindplot/test/unit/TxTExportTestSuite.test.ts @@ -0,0 +1,9 @@ +import TxtExporter from '../../src/components/export/TxtExporter'; +import Mindmap from '../../src/components/model/Mindmap'; + +test('adds 1 + 2 to equal 3', () => { + const m = new Mindmap("some map"); + + const exporter = new TxtExporter(); + console.log(exporter.export(m)); +}); diff --git a/packages/mindplot/tsconfig.json b/packages/mindplot/tsconfig.json new file mode 100644 index 00000000..71cc099f --- /dev/null +++ b/packages/mindplot/tsconfig.json @@ -0,0 +1,13 @@ + { + "compilerOptions": { + "outDir": "./dist/", + "sourceMap": true, + "noImplicitAny": false, + "module": "es6", + "moduleResolution": "Node", + "target": "es6", + "allowJs": true, + "esModuleInterop": true + }, + "exclude": ["node_modules"] + } \ No newline at end of file diff --git a/packages/mindplot/webpack.common.js b/packages/mindplot/webpack.common.js index a0937453..188df7a0 100644 --- a/packages/mindplot/webpack.common.js +++ b/packages/mindplot/webpack.common.js @@ -22,11 +22,17 @@ module.exports = { module: { rules: [ { - use: ['babel-loader'], test: /.(js$)/, + use: ['babel-loader'], exclude: [ /node_modules/, ], + enforce: 'pre', + }, + { + test: /\.(ts)$/, + use: 'ts-loader', + exclude: '/node_modules/', }, { test: /\.(png|svg|jpg|jpeg|gif)$/i, @@ -38,7 +44,7 @@ module.exports = { alias: { '@libraries': path.resolve(__dirname, '../../libraries/'), }, - extensions: ['.js', '.json'], + extensions: ['.js', '.ts', '.json'], }, plugins: [new CleanWebpackPlugin()], }; diff --git a/packages/mindplot/webpack.prod.js b/packages/mindplot/webpack.prod.js index dd017903..82c807b1 100644 --- a/packages/mindplot/webpack.prod.js +++ b/packages/mindplot/webpack.prod.js @@ -8,7 +8,7 @@ const prodConfig = { }, plugins: [ new CompressionPlugin({ - test: /\.js(\?.*)?$/i + test: /\.js(\?.*)?$/i, }), ], }; diff --git a/yarn.lock b/yarn.lock index f6ad5189..e0f4260e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -187,6 +187,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9" integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ== +"@babel/helper-plugin-utils@^7.16.5": + version "7.16.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.5.tgz#afe37a45f39fce44a3d50a7958129ea5b1a5c074" + integrity sha512-59KHWHXxVA9K4HNF4sbHCf+eJeFe0Te/ZFGqBT4OjXhrwvA04sGfaEGsVTdsjoszq0YTP49RC9UKe5g8uN2RwQ== + "@babel/helper-remap-async-to-generator@^7.16.0", "@babel/helper-remap-async-to-generator@^7.16.4": version "7.16.4" resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.4.tgz#5d7902f61349ff6b963e07f06a389ce139fbfe6e" @@ -526,6 +531,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" +"@babel/plugin-syntax-typescript@^7.16.0": + version "7.16.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.5.tgz#f47a33e4eee38554f00fb6b2f894fa1f5649b0b3" + integrity sha512-/d4//lZ1Vqb4mZ5xTep3dDK888j7BGM/iKqBmndBaoYAFPlPKrGU608VVBz5JeyAb6YQDjRu1UKqj86UhwWVgw== + dependencies: + "@babel/helper-plugin-utils" "^7.16.5" + "@babel/plugin-syntax-typescript@^7.7.2": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.0.tgz#2feeb13d9334cc582ea9111d3506f773174179bb" @@ -766,6 +778,15 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" +"@babel/plugin-transform-typescript@^7.16.1": + version "7.16.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.16.1.tgz#cc0670b2822b0338355bc1b3d2246a42b8166409" + integrity sha512-NO4XoryBng06jjw/qWEU2LhcLJr1tWkhpMam/H4eas/CDKMX/b2/Ylb6EI256Y7+FVPCawwSM1rrJNOpDiz+Lg== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.16.0" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-typescript" "^7.16.0" + "@babel/plugin-transform-unicode-escapes@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.0.tgz#1a354064b4c45663a32334f46fa0cf6100b5b1f3" @@ -872,6 +893,15 @@ "@babel/types" "^7.4.4" esutils "^2.0.2" +"@babel/preset-typescript@^7.16.5": + version "7.16.5" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.16.5.tgz#b86a5b0ae739ba741347d2f58c52f52e63cf1ba1" + integrity sha512-lmAWRoJ9iOSvs3DqOndQpj8XqXkzaiQs50VG/zESiI9D3eoZhGriU675xNCr0UwvsuXrhMAGvyk1w+EVWF3u8Q== + dependencies: + "@babel/helper-plugin-utils" "^7.16.5" + "@babel/helper-validator-option" "^7.14.5" + "@babel/plugin-transform-typescript" "^7.16.1" + "@babel/register@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.16.0.tgz#f5d2aa14df37cf7146b9759f7c53818360f24ec6" @@ -2523,6 +2553,14 @@ dependencies: "@types/istanbul-lib-report" "*" +"@types/jest@^27.0.3": + version "27.0.3" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.0.3.tgz#0cf9dfe9009e467f70a342f0f94ead19842a783a" + integrity sha512-cmmwv9t7gBYt7hNKH5Spu7Kuu/DotGa+Ff+JGRKZ4db5eh8PnKS4LuebJ3YLUoyOyIHraTGyULn23YtEAm0VSg== + dependencies: + jest-diff "^27.0.0" + pretty-format "^27.0.0" + "@types/json-schema@*", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.7", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": version "7.0.9" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" @@ -3848,6 +3886,13 @@ browserslist@^4.14.5, browserslist@^4.17.5, browserslist@^4.18.1: node-releases "^2.0.1" picocolors "^1.0.0" +bs-logger@0.x: + version "0.2.6" + resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" + integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== + dependencies: + fast-json-stable-stringify "2.x" + bser@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" @@ -5517,7 +5562,7 @@ enhanced-resolve@^4.0.0: memory-fs "^0.5.0" tapable "^1.0.0" -enhanced-resolve@^5.8.3: +enhanced-resolve@^5.0.0, enhanced-resolve@^5.8.3: version "5.8.3" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz#6d552d465cce0423f5b3d718511ea53826a7b2f0" integrity sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA== @@ -6332,7 +6377,7 @@ fast-glob@^3.1.1, fast-glob@^3.2.4, fast-glob@^3.2.7: merge2 "^1.3.0" micromatch "^4.0.4" -fast-json-stable-stringify@^2.0.0: +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== @@ -8298,7 +8343,7 @@ jest-config@^27.4.4: pretty-format "^27.4.2" slash "^3.0.0" -jest-diff@^27.4.2: +jest-diff@^27.0.0, jest-diff@^27.4.2: version "27.4.2" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.4.2.tgz#786b2a5211d854f848e2dcc1e324448e9481f36f" integrity sha512-ujc9ToyUZDh9KcqvQDkk/gkbf6zSaeEg9AiBxtttXW59H/AcqEYp1ciXAtJp+jXWva5nAf/ePtSsgWwE5mqp4Q== @@ -8589,7 +8634,7 @@ jest-snapshot@^27.4.4: pretty-format "^27.4.2" semver "^7.3.2" -jest-util@^27.4.2: +jest-util@^27.0.0, jest-util@^27.4.2: version "27.4.2" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.4.2.tgz#ed95b05b1adfd761e2cda47e0144c6a58e05a621" integrity sha512-YuxxpXU6nlMan9qyLuxHaMMOzXAl5aGZWCSzben5DhLHemYQxCc4YK+4L3ZrCutT8GPQ+ui9k5D8rUJoDioMnA== @@ -8798,6 +8843,13 @@ json3@^3.3.3: resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81" integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA== +json5@2.x, json5@^2.1.2: + version "2.2.0" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" + integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== + dependencies: + minimist "^1.2.5" + json5@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" @@ -8805,13 +8857,6 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" -json5@^2.1.2: - version "2.2.0" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" - integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== - dependencies: - minimist "^1.2.5" - jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -9220,6 +9265,11 @@ lodash.ismatch@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37" integrity sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc= +lodash.memoize@4.x: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= + lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" @@ -9394,7 +9444,7 @@ make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: dependencies: semver "^6.0.0" -make-error@^1.1.1: +make-error@1.x, make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== @@ -11002,7 +11052,7 @@ pretty-format@^26.6.2: ansi-styles "^4.0.0" react-is "^17.0.1" -pretty-format@^27.4.2: +pretty-format@^27.0.0, pretty-format@^27.4.2: version "27.4.2" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.4.2.tgz#e4ce92ad66c3888423d332b40477c87d1dac1fb8" integrity sha512-p0wNtJ9oLuvgOQDEIZ9zQjZffK7KtyR6Si0jnXULIDwrlNF8Cuir3AZP0hHv0jmKuNN/edOnbMjnzd4uTcmWiw== @@ -12050,18 +12100,18 @@ semver@7.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== -semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: +semver@7.x, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: version "7.3.5" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== dependencies: lru-cache "^6.0.0" +semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + send@0.17.1: version "0.17.1" resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" @@ -13187,6 +13237,20 @@ trim-newlines@^3.0.0: resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== +ts-jest@^27.1.2: + version "27.1.2" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.1.2.tgz#5991d6eb3fd8e1a8d4b8f6de3ec0a3cc567f3151" + integrity sha512-eSOiJOWq6Hhs6Khzk5wKC5sgWIXgXqOCiIl1+3lfnearu58Hj4QpE5tUhQcA3xtZrELbcvAGCsd6HB8OsaVaTA== + dependencies: + bs-logger "0.x" + fast-json-stable-stringify "2.x" + jest-util "^27.0.0" + json5 "2.x" + lodash.memoize "4.x" + make-error "1.x" + semver "7.x" + yargs-parser "20.x" + ts-loader@^8.0.11: version "8.3.0" resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-8.3.0.tgz#83360496d6f8004fab35825279132c93412edf33" @@ -13198,6 +13262,16 @@ ts-loader@^8.0.11: micromatch "^4.0.0" semver "^7.3.4" +ts-loader@^9.2.6: + version "9.2.6" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.2.6.tgz#9937c4dd0a1e3dbbb5e433f8102a6601c6615d74" + integrity sha512-QMTC4UFzHmu9wU2VHZEmWWE9cUajjfcdcws+Gh7FhiO+Dy0RnR1bNz0YCHqhI0yRowCE9arVnNxYHqELOy9Hjw== + dependencies: + chalk "^4.1.0" + enhanced-resolve "^5.0.0" + micromatch "^4.0.0" + semver "^7.3.4" + ts-node@^9.0.0: version "9.1.1" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.1.1.tgz#51a9a450a3e959401bda5f004a72d54b936d376d" @@ -14133,6 +14207,11 @@ yargs-parser@20.2.4: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== +yargs-parser@20.x, yargs-parser@^20.2.2, yargs-parser@^20.2.3: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + yargs-parser@^13.1.2: version "13.1.2" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" @@ -14149,11 +14228,6 @@ yargs-parser@^15.0.1: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^20.2.2, yargs-parser@^20.2.3: - version "20.2.9" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" - integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== - yargs-parser@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-8.1.0.tgz#f1376a33b6629a5d063782944da732631e966950"