@udt/parser-utils 0.1.0 → 0.2.0

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.
@@ -19,10 +19,6 @@ interface TestDesignToken {
19
19
 
20
20
  interface TestParentContext {
21
21
  dummyData?: number;
22
-
23
- // Used to pass in a mock addChild() function for
24
- // mockParseGroupData() to use for testing
25
- addChild?: AddChildFn<TestGroup, TestDesignToken>;
26
22
  }
27
23
 
28
24
  interface ParseDataCall {
@@ -53,7 +49,7 @@ const mockIsDesignTokenData = vi.fn<IsDesignTokenDataFn>(
53
49
  );
54
50
 
55
51
  const mockParseGroupData = vi.fn<
56
- ParseGroupDataFn<TestGroup, TestDesignToken, TestParentContext>
52
+ ParseGroupDataFn<TestGroup, TestParentContext>
57
53
  >((_data, path, contextFromParent) => {
58
54
  parseDataCalls.push({
59
55
  type: "group",
@@ -65,12 +61,18 @@ const mockParseGroupData = vi.fn<
65
61
  },
66
62
  // pass through contextFromParent
67
63
  contextForChildren: contextFromParent,
68
-
69
- // pass through addChild
70
- addChild: contextFromParent?.addChild,
71
64
  };
72
65
  });
73
66
 
67
+ const mockAddChildToGroup = vi.fn<AddChildFn<TestGroup, TestDesignToken>>(
68
+ (_parent, name, _child) => {
69
+ parseDataCalls.push({
70
+ type: "addChild",
71
+ name,
72
+ });
73
+ }
74
+ );
75
+
74
76
  const mockParseDesignTokenData = vi.fn<
75
77
  ParseDesignTokenDataFn<TestDesignToken, TestParentContext>
76
78
  >((_data, path, _contextFromParent) => {
@@ -81,20 +83,23 @@ const mockParseDesignTokenData = vi.fn<
81
83
  return result;
82
84
  });
83
85
 
86
+ const defaultParserConfig: ParserConfig<
87
+ TestDesignToken,
88
+ TestGroup,
89
+ TestParentContext
90
+ > = {
91
+ isDesignTokenData: mockIsDesignTokenData,
92
+ groupPropsToExtract: [],
93
+ parseGroupData: mockParseGroupData,
94
+ parseDesignTokenData: mockParseDesignTokenData,
95
+ };
96
+
84
97
  describe("parseData()", () => {
85
- const parserConfig: ParserConfig<
86
- TestDesignToken,
87
- TestGroup,
88
- TestParentContext
89
- > = {
90
- isDesignTokenData: mockIsDesignTokenData,
91
- groupPropsToExtract: [],
92
- parseGroupData: mockParseGroupData,
93
- parseDesignTokenData: mockParseDesignTokenData,
94
- };
98
+ let parserConfig: ParserConfig<TestDesignToken, TestGroup, TestParentContext>;
95
99
 
96
100
  beforeEach(() => {
97
101
  // Reset stuff
102
+ parserConfig = defaultParserConfig;
98
103
  parseDataCalls = [];
99
104
  parserConfig.groupPropsToExtract = [];
100
105
  mockIsDesignTokenData.mockClear();
@@ -103,14 +108,14 @@ describe("parseData()", () => {
103
108
  });
104
109
 
105
110
  describe("parsing an empty group object", () => {
106
- let parsedGroupOrToken: TestGroup | TestDesignToken;
111
+ let parsedGroupOrToken: TestGroup | TestDesignToken | undefined;
107
112
 
108
113
  beforeEach(() => {
109
114
  parsedGroupOrToken = parseData({}, parserConfig);
110
115
  });
111
116
 
112
117
  it("returns a group", () => {
113
- expect(parsedGroupOrToken.type).toBe("group");
118
+ expect(parsedGroupOrToken?.type).toBe("group");
114
119
  });
115
120
 
116
121
  it("calls isDesignTokenData function once", () => {
@@ -141,14 +146,14 @@ describe("parseData()", () => {
141
146
  stuff: 123,
142
147
  notAGroup: {},
143
148
  };
144
- let parsedGroupOrToken: TestGroup | TestDesignToken;
149
+ let parsedGroupOrToken: TestGroup | TestDesignToken | undefined;
145
150
 
146
151
  beforeEach(() => {
147
152
  parsedGroupOrToken = parseData(testTokenData, parserConfig);
148
153
  });
149
154
 
150
155
  it("returns a design token", () => {
151
- expect(parsedGroupOrToken.type).toBe("token");
156
+ expect(parsedGroupOrToken?.type).toBe("token");
152
157
  });
153
158
 
154
159
  it("does not call parseGroupData function", () => {
@@ -320,32 +325,24 @@ describe("parseData()", () => {
320
325
  });
321
326
  });
322
327
 
323
- describe("using addChild functions", () => {
328
+ describe("using addChildToGroup function", () => {
324
329
  const testData = {
325
330
  tokenA: { value: 1 },
326
331
  tokenB: { value: 2 },
327
332
  groupC: { tokenX: { value: 99 }, tokenY: { value: 100 } },
328
333
  };
329
- const mockAddChild = vi.fn<AddChildFn<TestGroup, TestDesignToken>>(
330
- (name, _child) => {
331
- parseDataCalls.push({
332
- type: "addChild",
333
- name,
334
- });
335
- }
336
- );
337
- const testContext: TestParentContext = { addChild: mockAddChild };
338
334
 
339
335
  beforeEach(() => {
340
- mockAddChild.mockClear();
341
- parseData(testData, parserConfig, testContext);
336
+ mockAddChildToGroup.mockClear();
337
+ parserConfig.addChildToGroup = mockAddChildToGroup;
338
+ parseData(testData, parserConfig);
342
339
  });
343
340
 
344
- it("calls addChild for every child of every group", () => {
341
+ it("calls addChildToGroup for every child of every group", () => {
345
342
  // Root group contains 3 children: "tokenA", "tokenB" and "groupC"
346
343
  // "groupC" contains 2 children: "tokenX" and "tokenY"
347
344
  // 3 + 2 = 5
348
- expect(mockAddChild).toHaveBeenCalledTimes(5);
345
+ expect(mockAddChildToGroup).toHaveBeenCalledTimes(5);
349
346
  });
350
347
 
351
348
  it("adds a nested group to its parent before parsing its children", () => {
package/src/parseData.ts CHANGED
@@ -39,12 +39,14 @@ export type ParseDesignTokenDataFn<ParsedDesignToken, T> = (
39
39
 
40
40
  /**
41
41
  * A function that adds a parsed group or design token
42
- * as a child of a parsed group.
42
+ * as a child of the given parsed group.
43
43
  *
44
+ * @param parent The parent group to add a child to
44
45
  * @param name The name of the child group or design token
45
- * @param child The group or desing token to add
46
+ * @param child The group or design token to add
46
47
  */
47
48
  export type AddChildFn<ParsedGroup, ParsedDesignToken> = (
49
+ parent: ParsedGroup,
48
50
  name: string,
49
51
  child: ParsedGroup | ParsedDesignToken
50
52
  ) => void;
@@ -52,25 +54,15 @@ export type AddChildFn<ParsedGroup, ParsedDesignToken> = (
52
54
  /**
53
55
  * The return value of a `ParseGroupDataFn`.
54
56
  */
55
- export interface ParseGroupResult<ParsedGroup, ParsedDesignToken, T> {
57
+ export interface ParseGroupResult<ParsedGroup, T> {
56
58
  /**
57
59
  * The parsed representation of the group.
58
60
  *
59
- * May be `undefined` if there is no useful result
60
- * to return from `parseData()` - e.g. if just
61
- * logging group info or something like that.
61
+ * May be omitted if there is no useful result to
62
+ * return and we only need to pass along context
63
+ * data.
62
64
  */
63
- group: ParsedGroup;
64
-
65
- /**
66
- * Optional function that will add other parsed groups
67
- * or design tokens as children of this parsed group.
68
- *
69
- * Intended for cases where the parsed representation
70
- * of a group needs to contain its children. If not
71
- * needed, this property can be omitted.
72
- */
73
- addChild?: AddChildFn<ParsedGroup, ParsedDesignToken>;
65
+ group?: ParsedGroup;
74
66
 
75
67
  /**
76
68
  * Optional context data to be passed into the
@@ -96,15 +88,14 @@ export interface ParseGroupResult<ParsedGroup, ParsedDesignToken, T> {
96
88
  * parsed the group containing this group.
97
89
  *
98
90
  * @returns The parsed representation of the group and,
99
- * optionally, a function to add child groups or
100
- * design tokens to it and some context data to
101
- * pass down when child data is parsed.
91
+ * optionally, some context data to pass down
92
+ * when child data is parsed.
102
93
  */
103
- export type ParseGroupDataFn<ParsedGroup, ParsedDesignToken, T> = (
94
+ export type ParseGroupDataFn<ParsedGroup, T> = (
104
95
  data: PlainObject,
105
96
  path: string[],
106
97
  contextFromParent?: T
107
- ) => ParseGroupResult<ParsedGroup, ParsedDesignToken, T>;
98
+ ) => ParseGroupResult<ParsedGroup, T>;
108
99
 
109
100
  export interface ParserConfig<ParsedDesignToken, ParsedGroup, T> {
110
101
  /**
@@ -132,7 +123,7 @@ export interface ParserConfig<ParsedDesignToken, ParsedGroup, T> {
132
123
  * path, and should parse that data into whatever structure
133
124
  * is desired.
134
125
  */
135
- parseGroupData: ParseGroupDataFn<ParsedGroup, ParsedDesignToken, T>;
126
+ parseGroupData?: ParseGroupDataFn<ParsedGroup, T>;
136
127
 
137
128
  /**
138
129
  * Function which is called for each design token
@@ -143,6 +134,16 @@ export interface ParserConfig<ParsedDesignToken, ParsedGroup, T> {
143
134
  * desired.
144
135
  */
145
136
  parseDesignTokenData: ParseDesignTokenDataFn<ParsedDesignToken, T>;
137
+
138
+ /**
139
+ * Optional function that will add parsed groups
140
+ * or design tokens as children of another parsed group.
141
+ *
142
+ * Intended for cases where the parsed representation
143
+ * of a group needs to contain its children. If not
144
+ * needed, this property can be omitted.
145
+ */
146
+ addChildToGroup?: AddChildFn<ParsedGroup, ParsedDesignToken>;
146
147
  }
147
148
 
148
149
  /**
@@ -192,8 +193,8 @@ function parseDataImpl<ParsedDesignToken, ParsedGroup, T>(
192
193
  config: ParserConfig<ParsedDesignToken, ParsedGroup, T>,
193
194
  contextFromParent?: T,
194
195
  path: string[] = [],
195
- addToParent?: AddChildFn<ParsedGroup, ParsedDesignToken>
196
- ): ParsedDesignToken | ParsedGroup {
196
+ parentGroup?: ParsedGroup
197
+ ): ParsedDesignToken | ParsedGroup | undefined {
197
198
  if (!isPlainObject(data)) {
198
199
  throw new InvalidDataError(path, data);
199
200
  }
@@ -203,38 +204,46 @@ function parseDataImpl<ParsedDesignToken, ParsedGroup, T>(
203
204
  groupPropsToExtract,
204
205
  parseGroupData,
205
206
  parseDesignTokenData,
207
+ addChildToGroup,
206
208
  } = config;
207
209
 
208
- let groupOrToken: ParsedGroup | ParsedDesignToken;
210
+ let groupOrToken: ParsedGroup | ParsedDesignToken | undefined = undefined;
209
211
  if (isDesignTokenData(data)) {
210
212
  // looks like a token
211
213
  groupOrToken = parseDesignTokenData(data, path, contextFromParent);
212
- if (addToParent && path.length > 0) {
213
- addToParent(path[path.length - 1], groupOrToken);
214
+ if (addChildToGroup && path.length > 0 && parentGroup !== undefined) {
215
+ addChildToGroup(parentGroup, path[path.length - 1], groupOrToken);
214
216
  }
215
217
  } else {
216
218
  // must be a group
217
- const { extracted: groupData, remainingProps: childNames } =
218
- extractProperties(data, groupPropsToExtract);
219
- const { group, addChild, contextForChildren } = parseGroupData(
220
- groupData,
221
- path,
222
- contextFromParent
219
+ const { extracted: groupData, rest: children } = extractProperties(
220
+ data,
221
+ groupPropsToExtract
223
222
  );
224
223
 
225
- groupOrToken = group;
224
+ let contextForChildren: T | undefined;
225
+ if (parseGroupData) {
226
+ const parseResult = parseGroupData(groupData, path, contextFromParent);
227
+ contextForChildren = parseResult.contextForChildren;
228
+ groupOrToken = parseResult.group;
229
+ }
226
230
 
227
- if (addToParent && path.length > 0) {
228
- addToParent(path[path.length - 1], groupOrToken);
231
+ if (
232
+ addChildToGroup &&
233
+ path.length > 0 &&
234
+ parentGroup !== undefined &&
235
+ groupOrToken !== undefined
236
+ ) {
237
+ addChildToGroup(parentGroup, path[path.length - 1], groupOrToken);
229
238
  }
230
239
 
231
- for (const childName of childNames) {
240
+ for (const childName in children) {
232
241
  parseDataImpl(
233
- data[childName],
242
+ children[childName],
234
243
  config,
235
244
  contextForChildren,
236
245
  [...path, childName],
237
- addChild
246
+ groupOrToken
238
247
  );
239
248
  }
240
249
  }
@@ -266,6 +275,6 @@ export function parseData<ParsedDesignToken, ParsedGroup, T>(
266
275
  data: unknown,
267
276
  config: ParserConfig<ParsedDesignToken, ParsedGroup, T>,
268
277
  contextFromParent?: T
269
- ): ParsedDesignToken | ParsedGroup {
278
+ ): ParsedDesignToken | ParsedGroup | undefined {
270
279
  return parseDataImpl(data, config, contextFromParent);
271
280
  }