extractorUtils.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. // Licensed to Cloudera, Inc. under one
  2. // or more contributor license agreements. See the NOTICE file
  3. // distributed with this work for additional information
  4. // regarding copyright ownership. Cloudera, Inc. licenses this file
  5. // to you under the Apache License, Version 2.0 (the
  6. // "License"); you may not use this file except in compliance
  7. // with the License. You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. /* eslint-disable no-restricted-syntax */
  17. const fs = require('fs');
  18. const LOG_NAME = 'extractorUtils.js';
  19. /**
  20. * Utility function to read a text file using UTF-8 encoding
  21. *
  22. * @param {string} path
  23. * @return {Promise} - A promise, fulfilled with the file contents or rejected
  24. */
  25. const readFile = path =>
  26. new Promise((resolve, reject) => {
  27. fs.readFile(path, 'utf8', (err, contents) => {
  28. if (err) {
  29. console.log("%s: Could not read file '%s'", LOG_NAME, path);
  30. console.log(err);
  31. reject(err);
  32. } else {
  33. resolve(contents);
  34. }
  35. });
  36. });
  37. /**
  38. * Returns the parent folder of a file path
  39. *
  40. * @param {string} path
  41. * @return {string}
  42. */
  43. const getParentFolder = path => path.substring(0, path.lastIndexOf('/') + 1);
  44. /**
  45. * Checks arguments given on the command line, breaks the program if required ones are missing.
  46. *
  47. * @param {Object} program - See command lib
  48. */
  49. const checkArguments = program => {
  50. if (!program.folder) {
  51. console.log('\n No folder supplied!');
  52. program.help();
  53. }
  54. if (!program.ditamap) {
  55. console.log('\n No ditamap file supplied!');
  56. program.help();
  57. }
  58. if (!program.output) {
  59. console.log('\n No output path supplied!');
  60. program.help();
  61. }
  62. };
  63. /**
  64. * Helper method to check if a node has attributes that aren't empty
  65. *
  66. * @param node
  67. * @param {string|string[]} attributes
  68. * @return {boolean}
  69. */
  70. const hasAttributes = (node, attributes) => {
  71. if (typeof attributes === 'string') {
  72. attributes = [attributes];
  73. }
  74. return attributes.every(
  75. attribute =>
  76. node.attr(attribute) &&
  77. node
  78. .attr(attribute)
  79. .value()
  80. .trim()
  81. );
  82. };
  83. /**
  84. * Helper method to remove all attributes of a noce
  85. *
  86. * @param node
  87. */
  88. const removeAllAttributes = node => {
  89. node.attrs().forEach(attr => {
  90. attr.remove();
  91. });
  92. };
  93. /**
  94. * Helper method to find a fragment with specified anchorId within a topic. If the anchor ID isn't found it will return
  95. * the closes possible parent.
  96. *
  97. * @param topic
  98. * @param anchorId
  99. * @return {[DocFragment]}
  100. */
  101. const findFragmentInTopic = (topic, anchorId) => {
  102. if (!anchorId) {
  103. return topic.fragment;
  104. }
  105. const splitIds = anchorId.split('/');
  106. const findDeep = (fragments, id) => {
  107. let foundFragment = undefined;
  108. fragments.some(fragment => {
  109. if (fragment.id === id) {
  110. foundFragment = fragment;
  111. return true;
  112. }
  113. foundFragment = findDeep(fragment.children, id);
  114. return foundFragment;
  115. });
  116. return foundFragment;
  117. };
  118. let fragmentsToSearch = [topic.fragment];
  119. let result = undefined;
  120. while (splitIds.length) {
  121. result = findDeep(fragmentsToSearch, splitIds.shift());
  122. if (!result) {
  123. break;
  124. }
  125. fragmentsToSearch = result.children;
  126. }
  127. if (!result) {
  128. console.log("%s: Could not find id '%s' in ref '%s'", LOG_NAME, anchorId, topic.ref);
  129. return topic.fragment;
  130. }
  131. return result;
  132. };
  133. /**
  134. * @typedef {Object} FragmentSearchResult
  135. * @property {boolean} partOfTree - Whether or not the found fragment is part of the main tree
  136. * @property {DocFragment} [fragment]
  137. */
  138. /**
  139. *
  140. * @param {DitamapParseResult[]} parseResults
  141. * @param {string} ref
  142. * @param {string} [anchorId]
  143. * @return {FragmentSearchResult}
  144. */
  145. const findFragment = (parseResults, ref, anchorId) => {
  146. const result = { partOfTree: true, fragment: undefined };
  147. parseResults.some(parseResult => {
  148. const topic = parseResult.topicIndex[ref];
  149. if (topic) {
  150. result.fragment = findFragmentInTopic(topic, anchorId);
  151. } else {
  152. result.partOfTree = false;
  153. }
  154. return result.fragment;
  155. });
  156. return result;
  157. };
  158. module.exports = {
  159. readFile: readFile,
  160. getParentFolder: getParentFolder,
  161. checkArguments: checkArguments,
  162. hasAttributes: hasAttributes,
  163. removeAllAttributes: removeAllAttributes,
  164. findFragment: findFragment
  165. };
  166. /* eslint-enable no-restricted-syntax */