• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

evolvedbinary / jdita / a7766ea3-980a-4afe-844e-9324f17748d0

18 Apr 2024 01:08PM UTC coverage: 84.65% (-1.2%) from 85.85%
a7766ea3-980a-4afe-844e-9324f17748d0

Pull #136

circleci

marmoure
Revert "[doc] update `serializeToXML` info in the README"

This reverts commit 0fcee1b26.
Pull Request #136: Feature "XDita Serialization of the AST" (#2731)

426 of 539 branches covered (79.04%)

Branch coverage included in aggregate %.

49 of 76 new or added lines in 6 files covered. (64.47%)

13 existing lines in 1 file now uncovered.

1096 of 1259 relevant lines covered (87.05%)

20.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.67
/packages/lwdita-xdita/utils.ts
1
import { BasicValue, OrArray, ChildTypes, ChildType} from "./classes";
2
import fs from 'fs';
1✔
3

4
/**
5
 * has - Check if an array has a value
6
 *
7
 * @param array - Array
8
 * @param value - Value
9
 * @returns Boolean
10
 */
11
export function has<T>(array: Array<T>, value: T): boolean {
1✔
12
    return array.indexOf(value) >= 0;
359✔
13
}
14

15
/**
16
 * isOrUndefined - Check if a value is undefined or not
17
 *
18
 * @param check - Function to check the value
19
 * @param value - Value
20
 * @returns - Boolean
21
 */
22
export function isOrUndefined<T extends BasicValue>(check: (value?: BasicValue) => boolean, value?: BasicValue): value is T {
1✔
23
    return typeof value === 'undefined' || check(value);
406✔
24
}
25

26
/**
27
 * splitTypenames - Converts a string to an array of strings
28
 * it splits the string by `|`
29
 *
30
 * @privateRemarks
31
 * This is only used in tests
32
 *
33
 * @param value - string
34
 * @returns - String[]
35
 */
36
export function splitTypenames(value: string): string[] {
1✔
37
    if (value[0] !== '(') {
21✔
38
        return value.split('|');
6✔
39
    }
40

41
    // if the string starts with `(` and ends with `)` then remove them
42
    const last = value.slice(-1);
15✔
43
    return has(['+', '*', '?'], last)
15✔
44
        ? value.slice(1, -2).split('|').map(type => type + last)
50!
45
        : value.slice(1, -1).split('|');
46
}
47

48
/**
49
 * childTypeToString - Convert a child type to a string
50
 *
51
 * @param type - ChildType object
52
 * @param getNodeName - Get node name function
53
 * @param nodeGroups - Node groups
54
 * @returns - string
55
 */
56
function childTypeToString(type: ChildType, nodeGroups: Record<string, string[]>, getNodeName?: (nodeName: string) => string): string {
57
    return (type.isGroup
43✔
58
        ? nodeGroups[type.name].length === 1
43✔
59
            ? (getNodeName
4!
60
                ? nodeGroups[type.name].map(getNodeName)
4!
61
                : nodeGroups[type.name]
62
            ).join('|')
63
            : '(' + (getNodeName
64
                ? nodeGroups[type.name].map(getNodeName)
×
65
                : nodeGroups[type.name]
66
            ).join('|') + ')'
67
        : getNodeName ? getNodeName(type.name) : type.name
39✔
68
    ) + (type.single
69
        ? type.required ? '' : '?'
68✔
70
        : type.required ? '+' : '*');
18✔
71
}
72

73
/**
74
 * customChildTypesToString - Serialize a child type object to a string with a custom function to get the node name
75
 *
76
 * @param type - ChildType Array
77
 * @param nodeGroups - Node groups
78
 * @param getNodeName - function to get the node name
79
 * @param topLevel - start of the document
80
 * @returns string - Serialized ChildType Array
81
 */
82
export function customChildTypesToString(type: ChildTypes, nodeGroups: Record<string, string[]>, getNodeName?: (nodeName: string) => string, topLevel = true): string {
1✔
83
    if (Array.isArray(type)) {
52✔
84
        const types = type.map(subType => customChildTypesToString(subType, nodeGroups, getNodeName, false)).join('|');
37✔
85
        return topLevel || type.length === 1 ? types : '(' + types + ')';
9✔
86
    } else {
87
        return childTypeToString(type, nodeGroups, getNodeName)
43✔
88
    }
89
}
90

91
/**
92
 * childTypesToString - Serialize a child type object to a string
93
 *
94
 * @param type - ChildType Array
95
 * @param nodeGroups - Node groups
96
 * @param topLevel - start of the document
97
 * @returns string - Serialized ChildType Array
98
 */
99
export function childTypesToString(type: ChildTypes, nodeGroups: Record<string, string[]>, topLevel = true): string {
1✔
100
    return customChildTypesToString(type, nodeGroups, undefined, topLevel);
14✔
101
}
102

103
/**
104
 * stringToChildTypes - Convert the array list of string to child objects
105
 *
106
 * @remarks
107
 * `?` - optional
108
 * `+` - required
109
 * `*` - optional and multiple
110
 * `%` - group
111
 *
112
 * @param value - String or Array of strings
113
 * @param topLevel - Entry of the document
114
 * @returns Array of ChildType objects , @see {@link ChildType}
115
 */
116
export function stringToChildTypes(value: OrArray<string>, topLevel = true): ChildTypes[] {
1✔
117
    if (typeof value === 'string') {
391✔
118
        if (value === '') {
250✔
119
            return [];
2✔
120
        }
121
        if (value.indexOf('|') < 0) {
248✔
122
            const last = value.slice(-1);
229✔
123
            const result: ChildType = has(['+', '*', '?'], last)
229✔
124
            ? {
229✔
125
                name: value.slice(0, -1),
126
                single: last === '?',
127
                required: last === '+',
128
                isGroup: false,
129
            } : {
130
                name: value,
131
                single: true,
132
                required: true,
133
                isGroup: false,
134
            };
135
            if (result.name[0] === '%') {
229✔
136
                result.name = result.name.substr(1);
72✔
137
                result.isGroup = true;
72✔
138
            }
139
            return topLevel && !Array.isArray(result) ? [ result ] : result as unknown as ChildTypes[];
229✔
140
        } else {
141
            return stringToChildTypes(splitTypenames(value), false);
19✔
142
        }
143
    } else {
144
        return value.map(subType => stringToChildTypes(subType, false)).filter(type => !Array.isArray(type) || type.length > 0);
235✔
145
    }
146
}
147

148
/**
149
 * acceptsNodeName - Check whether a child type accepts a node name
150
 *
151
 * @param value - Node name
152
 * @param childType - String or an ChildType object or an array ChildType objects
153
 * @param nodeGroups - Node groups
154
 * @returns ChildType | undefined - returns the ChildType oject if it's accepted or undefined if it's not
155
 */
156
export function acceptsNodeName(value: string, childType: ChildTypes, nodeGroups: Record<string, string[]>): ChildType | undefined {
1✔
157
    // if child type is an array
158
    if (Array.isArray(childType)) {
364✔
159
        let result: ChildType | undefined;
160
        childType.some(type => {
100✔
161
            // if any of the children in the array accepts the node name then return true
162
            result = acceptsNodeName(value, type, nodeGroups);
156✔
163
            if (result) {
156✔
164
                return true;
96✔
165
            }
166
        });
167
        return result;
100✔
168
    } else {
169
        // if child type is not a group
170
        // then check if the child type name is equal to the value
171
        // if it's a group check if the value is in the group
172
        return !childType.isGroup
264✔
173
            ? (childType.name === value ? childType : undefined)
413✔
174
            : (has(nodeGroups[childType.name], value) ? childType : undefined);
115✔
175
    }
176
}
177

178
/**
179
 * isChildTypeSingle - check if the child belongs to a group of elements eg: `list-blocks` or `all-line`
180
 *
181
 * @param childType - String or an ChildType object or an array ChildType objects
182
 * @returns Boolean - Whether the child is a group or not
183
 */
184
export function isChildTypeSingle(childType: string | ChildType | ChildTypes): boolean {
1✔
185
    // if it's an Array
186
    if (Array.isArray(childType)) {
89✔
187
        let result = true;
32✔
188
        // if any of the children in the array is not a single type then return false
189
        childType.some(type => {
32✔
190
            result = isChildTypeSingle(type);
48✔
191
            return !result;
48✔
192
        });
193
        return result;
32✔
194
    } else {
195
        // it's a string
196
        if (typeof childType === 'string') {
57!
197
            // parse the string using `stringToChildTypes` and check if it's a single type
198
            // single type can be denoted by the lack of `%` in the beginning of the string
199
            return isChildTypeSingle(stringToChildTypes(childType));
×
200
        }
201
        // if the oject is already parsed then return the single property
202
        return !!childType.single;
57✔
203
    }
204
}
205

206
/**
207
 * isChildTypeRequired - Check if a child is required
208
 *
209
 * the required property is denoted by `+` or the lack of `?` in the end of the string
210
 * This means the child must be present in the node in the order specified by the parent node
211
 *
212
 * @param childType -  String or an ChildType object or an array ChildType objects
213
 * @returns Boolean - Whether the child is required or not
214
 */
215
export function isChildTypeRequired(childType: string | ChildType | ChildTypes): boolean {
1✔
216
    // console.log(childType);
217
    // if it's an Array
218
    if (Array.isArray(childType)) {
8!
219
        let result = true;
×
220
        // if one of the children in the array is required then return true
221
        childType.some(type => {
×
222
            result = !isChildTypeRequired(type);
×
223
            return !result;
×
224
        });
225
        return result;
×
226
    } else {
227
        if (typeof childType === 'string') {
8!
228
            // if it's a string parse it and check if it's required
229
            return isChildTypeRequired(stringToChildTypes(childType));
×
230
        }
231
        // if the oject is already parsed then return the required property
232
        return !!childType.required;
8✔
233
    }
234
}
235

236
/**
237
 * childTypesArray - Check whether the child types is an array or not and return an array
238
 *
239
 * @param childTypes - ChildType array or ChildType object
240
 * @returns - ChildType array
241
 */
242
export function childTypesArray(childTypes: ChildTypes): ChildTypes[] {
1✔
243
    return Array.isArray(childTypes) ? childTypes : [childTypes];
×
244
}
245

246
/**
247
 * areFieldsValid - Attribute validator
248
 *
249
 * @param fields - Array of attribute names
250
 * @param value - Object of attribute values
251
 * @param validations - Validation functions
252
 * @returns Boolean - Whether the attributes are valid or not
253
 */
254
export function areFieldsValid(fields: string[], value: Record<string, BasicValue>, ...validations: ((field: string, value: BasicValue) => boolean)[]): boolean {
1✔
255
    for (const field of fields) {
42✔
256
        let valid = false;
322✔
257
        for (const validation of validations) {
322✔
258
            if (validation(field, value[field])) {
322!
259
                valid = true;
322✔
260
                break;
322✔
261
            }
262
        }
263
        if (!valid) {
322!
264
            return false;
×
265
        }
266
    }
267
    return true;
42✔
268
}
269

270
/**
271
 * Store Xml documents in the file system.
272
 *
273
 * @privateRemarks
274
 * TODO:  Checkout which platform this is running on, We are not sure yet if this would be running in the browser?
275
 * TODO: Get file name info from input XML
276
 * TODO: Check if the parser has support for reading the XML & Doctype Declaration,
277
 * if it can then, set the values from the XML Declaration and the Doctype Declaration
278
 * as properties on the root node of the AST tree.
279
 *
280
 * @param xml - The xml input string to store
281
 * @param path - The path in the filesystem to store the output array
282
 */
283
export function storeOutputXML(xml: string, path: string): void {
1✔
284
    // This is a static & generic XML & DOCTYPE Declaration for the output XML,
285
    // see above's TODO for a future implementation
NEW
286
    const header = `<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE topic PUBLIC "-//OASIS//DTD LIGHTWEIGHT DITA Topic//EN" "lw-topic.dtd">\n`
×
NEW
287
    xml = header + xml;
×
NEW
288
    fs.writeFileSync(path,xml)
×
289
}
290

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc