fetch_docs.js 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. // Licensed to the Apache Software Foundation (ASF) under one
  2. // or more contributor license agreements. See the NOTICE file
  3. // distributed with this work for additional information
  4. // regarding copyright ownership. The ASF 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,
  12. // software distributed under the License is distributed on an
  13. // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  14. // KIND, either express or implied. See the License for the
  15. // specific language governing permissions and limitations
  16. // under the License.
  17. 'use strict';
  18. var fs = require('fs');
  19. var fse = require('fs-extra');
  20. var https = require('https');
  21. var path = require('path');
  22. var child_process = require('child_process');
  23. var yaml = require('js-yaml');
  24. var optimist = require('optimist');
  25. var util = require('./util');
  26. // constants
  27. var DEFAULT_REPO_PATH = 'README.md';
  28. var DEFAULT_VERSION_NAME = 'dev';
  29. var DEFAULT_LANGUAGE_NAME = 'en';
  30. var THIS_FILE = path.basename(__filename);
  31. var WARNING_COMMENT = '<!-- WARNING: This file is generated. See ' + THIS_FILE + '. -->\n\n';
  32. // helpers
  33. function isPluginName (packageName) {
  34. return packageName.match(/cordova-plugin-.*/);
  35. }
  36. function getRepoFileURI (repoName, commit, filePath) {
  37. return 'https://raw.githubusercontent.com/' + repoName + '/' + commit + '/' + filePath;
  38. }
  39. function getRepoEditURI (repoName, commit, filePath) {
  40. return 'https://github.com/' + repoName + '/blob/' + commit + '/' + filePath;
  41. }
  42. function getLatestRelease (packageName) {
  43. var latestRelease = child_process.execSync('npm info ' + packageName + ' dist-tags.latest');
  44. return latestRelease.toString().trim();
  45. }
  46. function packageNameFromRepoName (repoName) {
  47. return repoName.split('/')[1];
  48. }
  49. function getFetchedFileConfig (entry) {
  50. // get entry components
  51. var srcConfig = entry.src;
  52. var destConfig = entry.dest;
  53. // validate entry
  54. if (!srcConfig) {
  55. console.error("entry '" + entry.toString() + "' missing 'src'");
  56. return;
  57. }
  58. if (!srcConfig.repoName) {
  59. console.error("entry '" + entry.toString() + "' missing 'repoName' in 'src'");
  60. return;
  61. }
  62. if (!srcConfig.repoName) {
  63. console.error("entry '" + entry.toString() + "' missing 'repoName' in 'src'");
  64. return;
  65. }
  66. if (!destConfig) {
  67. console.error("entry '" + entry.toString() + "' missing 'dest'");
  68. return;
  69. }
  70. if (!destConfig.path) {
  71. console.error("entry '" + entry.toString() + "' missing 'path' in 'dest'");
  72. return;
  73. }
  74. // complete src config
  75. if (!srcConfig.packageName) {
  76. srcConfig.packageName = packageNameFromRepoName(srcConfig.repoName);
  77. }
  78. if (!srcConfig.path) {
  79. srcConfig.path = DEFAULT_REPO_PATH;
  80. }
  81. if (!srcConfig.commit) {
  82. srcConfig.commit = getLatestRelease(srcConfig.packageName);
  83. }
  84. // make front matter
  85. var frontMatter = {
  86. edit_link: getRepoEditURI(srcConfig.repoName, srcConfig.commit, srcConfig.path),
  87. title: srcConfig.packageName
  88. };
  89. // set special front matter values for plugins
  90. if (isPluginName(srcConfig.packageName)) {
  91. frontMatter.plugin_name = srcConfig.packageName;
  92. frontMatter.plugin_version = srcConfig.commit;
  93. }
  94. // set returned values
  95. var fetchedFileConfig = {
  96. frontMatter: frontMatter,
  97. downloadURI: getRepoFileURI(srcConfig.repoName, srcConfig.commit, srcConfig.path),
  98. savePath: destConfig.path
  99. };
  100. return fetchedFileConfig;
  101. }
  102. function getFrontMatter (text) {
  103. var frontMatterString = util.getFrontMatterString(text);
  104. if (frontMatterString !== null) {
  105. return yaml.load(frontMatterString);
  106. }
  107. return {};
  108. }
  109. function setFrontMatter (text, frontMatter, options) {
  110. var frontMatterString = yaml.dump(frontMatter, options);
  111. return util.setFrontMatterString(text, frontMatterString);
  112. }
  113. function dumpEntries (downloadPrefix, entries) {
  114. entries.forEach(function (entry) {
  115. // validate entry's dest config
  116. if (!entry.dest) {
  117. console.error("entry '" + entry.toString() + "' missing 'dest'");
  118. return;
  119. }
  120. if (!entry.dest.path) {
  121. console.error("entry '" + entry.toString() + "' missing 'path' in 'dest'");
  122. return;
  123. }
  124. // print the save path for the entry
  125. if (entry.dest && entry.dest.path) {
  126. var filePath = path.join(downloadPrefix, entry.dest.path);
  127. console.log(filePath);
  128. // error out on invalid entries
  129. } else {
  130. console.error('Invalid dest: ' + entry);
  131. process.exit(1);
  132. }
  133. });
  134. }
  135. function downloadEntries (downloadPrefix, entries) {
  136. entries.forEach(function (entry) {
  137. // verify and process entry
  138. var fetchedFileConfig = getFetchedFileConfig(entry);
  139. if (!fetchedFileConfig) {
  140. process.exit(1);
  141. }
  142. // get info for fetching
  143. var fetchURI = fetchedFileConfig.downloadURI;
  144. var outFilePath = path.join(downloadPrefix, fetchedFileConfig.savePath);
  145. var outFileDir = path.dirname(outFilePath);
  146. // create directory for the file if it doesn't exist
  147. if (!fs.existsSync(outFileDir)) {
  148. fse.mkdirsSync(outFileDir);
  149. }
  150. console.log(fetchURI + ' -> ' + outFilePath);
  151. // open the file for writing
  152. var outFile = fs.createWriteStream(outFilePath);
  153. // open an HTTP request for the file
  154. https.get(fetchURI, function (response) {
  155. if (response.statusCode !== 200) {
  156. console.error('Failed to download ' + fetchURI + ': got ' + response.statusCode);
  157. process.exit(1);
  158. }
  159. // read in the response
  160. var fileContents = '';
  161. response.setEncoding('utf8');
  162. response.on('data', function (data) {
  163. fileContents += data;
  164. });
  165. // process the response when it finishes
  166. response.on('end', function () {
  167. // merge new front matter and file's own front matter (if it had any)
  168. //
  169. // NOTE:
  170. // fileFrontMatter's properties should override those of newFrontMatter
  171. var newFrontMatter = fetchedFileConfig.frontMatter;
  172. var fileFrontMatter = getFrontMatter(fileContents);
  173. var mergedFrontMatter = util.mergeObjects(newFrontMatter, fileFrontMatter);
  174. // add a warning and set the merged file matter in the file
  175. var contentsOnly = util.stripFrontMatter(fileContents);
  176. contentsOnly = WARNING_COMMENT + contentsOnly;
  177. var augmentedContents = setFrontMatter(contentsOnly, mergedFrontMatter);
  178. // write out the file
  179. outFile.end(augmentedContents);
  180. }).on('error', function (e) {
  181. console.error(e);
  182. });
  183. }); // http request
  184. }); // entries
  185. }
  186. // main
  187. function main () {
  188. // get args
  189. var argv = optimist
  190. .usage('Usage: $0 [options]')
  191. .demand('config')
  192. .demand('docsRoot')
  193. .string('version')
  194. .string('language')
  195. .boolean('dump')
  196. .describe('config', '.yml file listing fetched files')
  197. .describe('docsRoot', 'docs root directory')
  198. .describe('version', 'version in which to save the downloaded files').default('version', DEFAULT_VERSION_NAME)
  199. .describe('language', 'language in which to save the downloaded files').default('language', DEFAULT_LANGUAGE_NAME)
  200. .describe('dump', 'only print the downloaded files')
  201. .argv;
  202. var configFile = argv.config;
  203. var docsRoot = argv.docsRoot;
  204. var targetVersion = argv.version;
  205. var targetLanguage = argv.language;
  206. var printOnly = argv.dump;
  207. var downloadPrefix = path.join(docsRoot, targetLanguage, targetVersion);
  208. // validate args
  209. if (!fs.existsSync(configFile)) {
  210. console.error("Config file doesn't exist.");
  211. process.exit();
  212. }
  213. if (!fs.existsSync(docsRoot)) {
  214. console.error("Docs root doesn't exist.");
  215. process.exit();
  216. }
  217. // get config
  218. var fetchConfig = fs.readFileSync(configFile);
  219. var configEntries = yaml.load(fetchConfig);
  220. // just dump entries if --dump was passed
  221. if (printOnly === true) {
  222. dumpEntries(downloadPrefix, configEntries);
  223. // otherwise, fetch them
  224. } else {
  225. downloadEntries(downloadPrefix, configEntries);
  226. }
  227. }
  228. main();