state-machine-cat 12.0.18 → 12.0.19

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 CHANGED
@@ -518,7 +518,7 @@ eat -> sleep [color="blue" width=3.5] : belly full;
518
518
 
519
519
  ... would yield this diagram:
520
520
 
521
- <img width="659" alt="colored states and transitions" src="https://raw.githubusercontent.com/sverweij/state-machine-cat/main/docs/pics/10colored_states_and_transitions.png">
521
+ <img width="488" alt="colored states and transitions" src="https://raw.githubusercontent.com/sverweij/state-machine-cat/main/docs/pics/10colored_states_and_transitions.png">
522
522
 
523
523
  What does 'experimental' mean?
524
524
 
package/dist/cli/cli.mjs CHANGED
@@ -1,12 +1,8 @@
1
- import { readFileSync } from "node:fs";
2
1
  import { parseArgs } from "node:util";
3
- import satisfies from "semver/functions/satisfies.js";
2
+ import { version } from "../version.mjs";
4
3
  import { formatError, displayLicense, transform } from "./actions.mjs";
5
4
  import normalize from "./normalize.mjs";
6
5
  import validations from "./validations.mjs";
7
- const $package = JSON.parse(
8
- readFileSync(new URL("../../package.json", import.meta.url), "utf8"),
9
- );
10
6
  const HELP_TEXT = `Usage: smcat [options] [infile]
11
7
 
12
8
  Write beautiful state charts - https://github.com/sverweij/state-machine-cat
@@ -130,31 +126,20 @@ function parseArguments(pArguments) {
130
126
  );
131
127
  return { values: camelizeObject(values), positionals };
132
128
  }
133
- function assertNodeVersion(pCurrentNodeVersion, pSupportedEngines) {
134
- if (!satisfies(pCurrentNodeVersion, pSupportedEngines)) {
135
- throw new Error(
136
- `\nERROR: your node version (${pCurrentNodeVersion}) is not recent enough.\n` +
137
- ` state-machine-cat is supported on node ${pSupportedEngines}\n\n`,
138
- );
139
- }
140
- }
141
129
  export default async function cli(pArguments = process.argv, pOptions) {
142
130
  const lOptions = {
143
- currentNodeVersion: process.versions.node,
144
- supportedEngines: $package.engines.node,
145
131
  outStream: process.stdout,
146
132
  errorStream: process.stderr,
147
133
  ...pOptions,
148
134
  };
149
135
  try {
150
- assertNodeVersion(lOptions.currentNodeVersion, lOptions.supportedEngines);
151
136
  const { values, positionals } = parseArguments(pArguments.slice(2));
152
137
  if (values.help) {
153
138
  lOptions.outStream.write(HELP_TEXT, "utf8");
154
139
  return;
155
140
  }
156
141
  if (values.version) {
157
- lOptions.outStream.write(`${$package.version}\n`, "utf8");
142
+ lOptions.outStream.write(`${version}\n`, "utf8");
158
143
  return;
159
144
  }
160
145
  if (values.license) {
@@ -1,6 +1,6 @@
1
1
  import fastxml from "fast-xml-parser";
2
2
  import he from "he";
3
- import traverse from "traverse";
3
+ import traverse from "neotraverse";
4
4
  import utl from "../../transform/utl.mjs";
5
5
  import parserHelpers from "../parser-helpers.mjs";
6
6
  import { castArray } from "./utl.mjs";
@@ -16,6 +16,7 @@ import {
16
16
  normalizeState,
17
17
  stateNote,
18
18
  } from "./utl.mjs";
19
+ let gRenderedTransitions = new Set();
19
20
  function initial(pState, pIndent) {
20
21
  const lActiveAttribute = pState.active ? " penwidth=3.0" : "";
21
22
  return `${pIndent} "${pState.name}" [shape=circle style=filled class="${pState.class}" color="${pState.color}" fillcolor="${pState.color}" fixedsize=true height=0.15 label=""${lActiveAttribute}]${pState.noteText}`;
@@ -164,13 +165,28 @@ const STATE_TYPE2FUNCTION = new Map([
164
165
  ]);
165
166
  function state(pState, pIndent, pOptions, pModel) {
166
167
  const lState = normalizeState(pState, pOptions, pIndent);
168
+ const lCandidateTransitions = pModel.findTransitionsToSiblings(
169
+ pState.name,
170
+ gRenderedTransitions,
171
+ );
172
+ lCandidateTransitions.forEach((pTransition) => {
173
+ gRenderedTransitions.add(pTransition.id);
174
+ });
175
+ const lTransitions = transitions(
176
+ lCandidateTransitions,
177
+ pIndent,
178
+ pOptions,
179
+ pModel,
180
+ );
167
181
  return (
168
182
  (STATE_TYPE2FUNCTION.get(pState.type) ?? regular)(
169
183
  lState,
170
184
  pIndent,
171
185
  pOptions,
172
186
  pModel,
173
- ) + "\n"
187
+ ) +
188
+ lTransitions +
189
+ "\n"
174
190
  );
175
191
  }
176
192
  function states(pStates, pIndent, pOptions, pModel) {
@@ -233,19 +249,23 @@ export default function renderDot(pStateMachine, pOptions = {}, pIndent = "") {
233
249
  const lNodeAttributes = buildNodeAttributes(pOptions.dotNodeAttrs || []);
234
250
  const lEdgeAttributes = buildEdgeAttributes(pOptions.dotEdgeAttrs || []);
235
251
  const lModel = new StateMachineModel(pStateMachine);
252
+ gRenderedTransitions = new Set();
236
253
  const lStates = states(pStateMachine.states, pIndent, pOptions, lModel);
237
- const lTransitions = transitions(
238
- lModel.flattenedTransitions,
254
+ const lRemainingTransitions = transitions(
255
+ lModel.flattenedTransitions.filter(
256
+ (pTransition) => !gRenderedTransitions.has(pTransition.id),
257
+ ),
239
258
  pIndent,
240
259
  pOptions,
241
260
  lModel,
242
261
  );
262
+ gRenderedTransitions = new Set();
243
263
  return `digraph "state transitions" {
244
264
  ${lGraphAttributes}
245
265
  node [${lNodeAttributes}]
246
266
  edge [${lEdgeAttributes}]
247
267
 
248
- ${lStates}${lTransitions}
268
+ ${lStates}${lRemainingTransitions}
249
269
  }
250
270
  `;
251
271
  }
@@ -42,7 +42,8 @@ export function isVertical(pDirection) {
42
42
  export function isCompositeSelf(pStateMachineModel, pTransition) {
43
43
  return (
44
44
  pTransition.from === pTransition.to &&
45
- pStateMachineModel.findStateByName(pTransition.from).statemachine &&
45
+ (pStateMachineModel.findStateByName(pTransition.from)?.statemachine ??
46
+ false) &&
46
47
  pTransition.type !== "internal"
47
48
  );
48
49
  }
@@ -94,7 +95,7 @@ export function getTransitionPorts(pOptions, pModel, pTransition) {
94
95
  if (isVertical(lDirection)) {
95
96
  lTailPorts = ' tailport="e" headport="e"';
96
97
  lHeadPorts = ' tailport="w"';
97
- } else if (pModel.findStateByName(pTransition.from).hasParent) {
98
+ } else if (pModel.findStateByName(pTransition.from)?.parent ?? false) {
98
99
  lTailPorts = ' tailport="n" headport="n"';
99
100
  lHeadPorts = ' tailport="s"';
100
101
  }
@@ -1,21 +1,18 @@
1
- function flattenStates(pStates, pHasParent = false) {
2
- let lReturnValue = [];
1
+ function flattenStatesToMap(pStates, pMap, pParent = "") {
3
2
  pStates
4
3
  .filter((pState) => Boolean(pState.statemachine))
5
4
  .forEach((pState) => {
6
5
  if (Object.hasOwn(pState.statemachine, "states")) {
7
- lReturnValue = lReturnValue.concat(
8
- flattenStates(pState.statemachine.states, true),
9
- );
6
+ flattenStatesToMap(pState.statemachine.states, pMap, pState.name);
10
7
  }
11
8
  });
12
- return lReturnValue.concat(
13
- pStates.map((pState) => ({
9
+ pStates.forEach((pState) =>
10
+ pMap.set(pState.name, {
14
11
  name: pState.name,
15
12
  type: pState.type,
16
13
  statemachine: Boolean(pState.statemachine),
17
- hasParent: pHasParent,
18
- })),
14
+ parent: pParent,
15
+ }),
19
16
  );
20
17
  }
21
18
  function flattenTransitions(pStateMachine) {
@@ -38,17 +35,18 @@ export default class StateMachineModel {
38
35
  _flattenedTransitions;
39
36
  _flattenedStates;
40
37
  constructor(pStateMachine) {
41
- this._flattenedStates = flattenStates(pStateMachine.states || []);
38
+ this._flattenedStates = new Map();
39
+ flattenStatesToMap(pStateMachine.states ?? [], this._flattenedStates);
42
40
  this._flattenedTransitions = flattenTransitions(pStateMachine);
43
41
  }
44
42
  get flattenedTransitions() {
45
43
  return this._flattenedTransitions;
46
44
  }
47
45
  findStateByName(pName) {
48
- return this._flattenedStates.find((pState) => pState.name === pName);
46
+ return this._flattenedStates.get(pName);
49
47
  }
50
48
  findStatesByTypes(pTypes) {
51
- return this._flattenedStates.filter((pState) =>
49
+ return Array.from(this._flattenedStates.values()).filter((pState) =>
52
50
  pTypes.includes(pState.type),
53
51
  );
54
52
  }
@@ -70,4 +68,13 @@ export default class StateMachineModel {
70
68
  (pTransition) => pTransition.to === pToStateName,
71
69
  );
72
70
  }
71
+ findTransitionsToSiblings(pStateName, pExcludeIds) {
72
+ return this._flattenedTransitions.filter(
73
+ (pTransition) =>
74
+ !pExcludeIds.has(pTransition.id) &&
75
+ pTransition.from === pStateName &&
76
+ this._flattenedStates.get(pTransition.to)?.parent ===
77
+ this._flattenedStates.get(pStateName)?.parent,
78
+ );
79
+ }
73
80
  }
package/dist/version.mjs CHANGED
@@ -1 +1 @@
1
- export const version = "12.0.18";
1
+ export const version = "12.0.19";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "state-machine-cat",
3
- "version": "12.0.18",
3
+ "version": "12.0.19",
4
4
  "description": "write beautiful state charts",
5
5
  "main": "./dist/index.mjs",
6
6
  "module": "./dist/index.mjs",
@@ -44,8 +44,7 @@
44
44
  "ajv": "8.17.1",
45
45
  "fast-xml-parser": "4.5.1",
46
46
  "he": "1.2.0",
47
- "semver": "^7.6.2",
48
- "traverse": "0.6.8"
47
+ "neotraverse": "0.6.18"
49
48
  },
50
49
  "engines": {
51
50
  "node": "^18.17||>=20"