extractorUtils.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  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(attribute => node.attr(attribute) && node.attr(attribute).value().trim());
  75. };
  76. /**
  77. * Helper method to remove all attributes of a noce
  78. *
  79. * @param node
  80. */
  81. const removeAllAttributes = node => {
  82. node.attrs().forEach(attr => {
  83. attr.remove();
  84. });
  85. };
  86. /**
  87. * Helper method to find a fragment with specified anchorId within a topic. If the anchor ID isn't found it will return
  88. * the closes possible parent.
  89. *
  90. * @param topic
  91. * @param anchorId
  92. * @return {[DocFragment]}
  93. */
  94. const findFragmentInTopic = (topic, anchorId) => {
  95. if (!anchorId) {
  96. return topic.fragment;
  97. }
  98. const splitIds = anchorId.split('/');
  99. const findDeep = (fragments, id) => {
  100. let foundFragment = undefined;
  101. fragments.some(fragment => {
  102. if (fragment.id === id) {
  103. foundFragment = fragment;
  104. return true;
  105. }
  106. foundFragment = findDeep(fragment.children, id);
  107. return foundFragment;
  108. });
  109. return foundFragment;
  110. };
  111. let fragmentsToSearch = [topic.fragment];
  112. let result = undefined;
  113. while (splitIds.length) {
  114. result = findDeep(fragmentsToSearch, splitIds.shift());
  115. if (!result) {
  116. break;
  117. }
  118. fragmentsToSearch = result.children;
  119. }
  120. if (!result) {
  121. console.log("%s: Could not find id '%s' in ref '%s'", LOG_NAME, anchorId, topic.ref);
  122. return topic.fragment;
  123. }
  124. return result;
  125. };
  126. /**
  127. * @typedef {Object} FragmentSearchResult
  128. * @property {boolean} partOfTree - Whether or not the found fragment is part of the main tree
  129. * @property {DocFragment} [fragment]
  130. */
  131. /**
  132. *
  133. * @param {DitamapParseResult[]} parseResults
  134. * @param {string} ref
  135. * @param {string} [anchorId]
  136. * @return {FragmentSearchResult}
  137. */
  138. const findFragment = (parseResults, ref, anchorId) => {
  139. const result = { partOfTree: true, fragment: undefined };
  140. parseResults.some(parseResult => {
  141. const topic = parseResult.topicIndex[ref];
  142. if (topic) {
  143. result.fragment = findFragmentInTopic(topic, anchorId);
  144. } else {
  145. result.partOfTree = false;
  146. }
  147. return result.fragment;
  148. });
  149. return result;
  150. };
  151. module.exports = {
  152. readFile: readFile,
  153. getParentFolder: getParentFolder,
  154. checkArguments: checkArguments,
  155. hasAttributes: hasAttributes,
  156. removeAllAttributes: removeAllAttributes,
  157. findFragment: findFragment
  158. };
  159. /* eslint-enable no-restricted-syntax */