extractorUtils.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  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. const fs = require('fs');
  17. const LOG_NAME = 'extractorUtils.js';
  18. /**
  19. * Utility function to read a text file using UTF-8 encoding
  20. *
  21. * @param {string} path
  22. * @return {Promise} - A promise, fulfilled with the file contents or rejected
  23. */
  24. const readFile = path =>
  25. new Promise((resolve, reject) => {
  26. fs.readFile(path, 'utf8', (err, contents) => {
  27. if (err) {
  28. console.log("%s: Could not read file '%s'", LOG_NAME, path);
  29. console.log(err);
  30. reject(err);
  31. } else {
  32. resolve(contents);
  33. }
  34. });
  35. });
  36. /**
  37. * Returns the parent folder of a file path
  38. *
  39. * @param {string} path
  40. * @return {string}
  41. */
  42. const getParentFolder = path => path.substring(0, path.lastIndexOf('/') + 1);
  43. /**
  44. * Checks arguments given on the command line, breaks the program if required ones are missing.
  45. *
  46. * @param {Object} program - See command lib
  47. */
  48. const checkArguments = program => {
  49. if (!program.folder) {
  50. console.log('\n No folder supplied!');
  51. program.help();
  52. }
  53. if (!program.ditamap) {
  54. console.log('\n No ditamap file supplied!');
  55. program.help();
  56. }
  57. if (!program.output) {
  58. console.log('\n No output path supplied!');
  59. program.help();
  60. }
  61. };
  62. /**
  63. * Helper method to check if a node has attributes that aren't empty
  64. *
  65. * @param node
  66. * @param {string|string[]} attributes
  67. * @return {boolean}
  68. */
  69. const hasAttributes = (node, attributes) => {
  70. if (typeof attributes === 'string') {
  71. attributes = [attributes];
  72. }
  73. return attributes.every(
  74. attribute =>
  75. node.attr(attribute) &&
  76. node
  77. .attr(attribute)
  78. .value()
  79. .trim()
  80. );
  81. };
  82. /**
  83. * Helper method to remove all attributes of a noce
  84. *
  85. * @param node
  86. */
  87. const removeAllAttributes = node => {
  88. node.attrs().forEach(attr => {
  89. attr.remove();
  90. });
  91. };
  92. /**
  93. * Helper method to find a fragment with specified anchorId within a topic. If the anchor ID isn't found it will return
  94. * the closes possible parent.
  95. *
  96. * @param topic
  97. * @param anchorId
  98. * @return {[DocFragment]}
  99. */
  100. const findFragmentInTopic = (topic, anchorId) => {
  101. if (!anchorId) {
  102. return topic.fragment;
  103. }
  104. const splitIds = anchorId.split('/');
  105. const findDeep = (fragments, id) => {
  106. let foundFragment = undefined;
  107. fragments.some(fragment => {
  108. if (fragment.id === id) {
  109. foundFragment = fragment;
  110. return true;
  111. }
  112. foundFragment = findDeep(fragment.children, id);
  113. return foundFragment;
  114. });
  115. return foundFragment;
  116. };
  117. let fragmentsToSearch = [topic.fragment];
  118. let result = undefined;
  119. while (splitIds.length) {
  120. result = findDeep(fragmentsToSearch, splitIds.shift());
  121. if (!result) {
  122. break;
  123. }
  124. fragmentsToSearch = result.children;
  125. }
  126. if (!result) {
  127. console.log("%s: Could not find id '%s' in ref '%s'", LOG_NAME, anchorId, topic.ref);
  128. return topic.fragment;
  129. }
  130. return result;
  131. };
  132. /**
  133. * @typedef {Object} FragmentSearchResult
  134. * @property {boolean} partOfTree - Whether or not the found fragment is part of the main tree
  135. * @property {DocFragment} [fragment]
  136. */
  137. /**
  138. *
  139. * @param {DitamapParseResult[]} parseResults
  140. * @param {string} ref
  141. * @param {string} [anchorId]
  142. * @return {FragmentSearchResult}
  143. */
  144. const findFragment = (parseResults, ref, anchorId) => {
  145. const result = { partOfTree: true, fragment: undefined };
  146. parseResults.some(parseResult => {
  147. const topic = parseResult.topicIndex[ref];
  148. if (topic) {
  149. result.fragment = findFragmentInTopic(topic, anchorId);
  150. } else {
  151. result.partOfTree = false;
  152. }
  153. return result.fragment;
  154. });
  155. return result;
  156. };
  157. module.exports = {
  158. readFile: readFile,
  159. getParentFolder: getParentFolder,
  160. checkArguments: checkArguments,
  161. hasAttributes: hasAttributes,
  162. removeAllAttributes: removeAllAttributes,
  163. findFragment: findFragment
  164. };