gulp-processModuleDeclarationToNamespace.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. // Gulp Tools
  2. var fs = require("fs");
  3. var processData = function(data, packageName, options) {
  4. var str = "" + data;
  5. // Start process by extracting all lines.
  6. let lines = str.split('\n');
  7. // Let's go line by line and check if we have special folder replacements
  8. // Replaces declare module 'babylonjs'; by declare module BABYLON for instance
  9. for (var index = 0; index < lines.length; index++) {
  10. var namespace = options.moduleName;
  11. var regex = /declare module ["'](.*)["'] {/g;
  12. if (options.moduleSpecifics) {
  13. var match = regex.exec(lines[index]);
  14. if (!match) {
  15. continue;
  16. }
  17. var module = match[1];
  18. options.moduleSpecifics.forEach(function(specific) {
  19. if (module.indexOf(specific.path) > -1) {
  20. namespace = specific.namespace;
  21. }
  22. });
  23. }
  24. lines[index] = lines[index].replace(regex, `declare module ${namespace} {`);
  25. }
  26. // Replace module augmentation blocks
  27. for (var index = 0; index < lines.length; index++) {
  28. var namespace = options.moduleName;
  29. var regex = /\smodule ["'](.*)["'] {/g;
  30. var match = regex.exec(lines[index]);
  31. if (!match) {
  32. continue;
  33. }
  34. lines[index] = "";
  35. // Find matching closing curly }
  36. var opened = 0;
  37. for (let endIndex = index; endIndex < lines.length; endIndex++) {
  38. let scanLine = lines[endIndex].trim();
  39. if (scanLine.length === 0) {
  40. continue;
  41. }
  42. // Skip comments
  43. if (scanLine[0] === "*" || scanLine[0] === "/") {
  44. continue;
  45. }
  46. // Count open curly
  47. if (scanLine.indexOf("{") != -1) {
  48. opened++;
  49. }
  50. // And closing ones
  51. if (scanLine.indexOf("}") != -1) {
  52. opened--;
  53. // until the closing module
  54. if (opened < 0) {
  55. lines[endIndex] = "";
  56. index = endIndex;
  57. break;
  58. }
  59. }
  60. }
  61. }
  62. // Recreate the file.
  63. str = lines.join('\n');
  64. // First let s deal with internal aliased imports.
  65. if (options.moduleSpecifics) {
  66. // Find all imported classes and aliased classes.
  67. var babylonRegex = new RegExp(`import {(.*)} from ['"](.*)['"];`, "g");
  68. var match = babylonRegex.exec(str);
  69. let aliasedClasses = new Set();
  70. while (match != null) {
  71. if (match[1]) {
  72. match[1].split(",").forEach(element => {
  73. // Filter only aliased classes
  74. if (element.indexOf(" as ") > -1) {
  75. aliasedClasses.add(element.trim() + " as " + match[2]);
  76. }
  77. });
  78. }
  79. match = babylonRegex.exec(str);
  80. }
  81. str = str.replace(babylonRegex, '');
  82. // For every aliased.
  83. aliasedClasses.forEach(cls => {
  84. const tokens = cls.split(" as ");
  85. const className = tokens[0];
  86. const alias = tokens[1];
  87. const package = tokens[2];
  88. // Use the default module name.
  89. let namespace = options.moduleName;
  90. // If they are part of a specific module.
  91. options.moduleSpecifics.forEach(function(specific) {
  92. if (package.indexOf(specific.path) > -1) {
  93. namespace = specific.namespace;
  94. }
  95. });
  96. // Replace
  97. const rg = new RegExp(`([ <])(${alias})([^\\w])`, "g")
  98. str = str.replace(rg, `$1${namespace}.${className}$3`);
  99. });
  100. }
  101. // Let s clean up all the import * from BABYLON or the package itself as we know it is part of
  102. // the same namespace... Should be
  103. str = str.replace("import * as BABYLON from 'babylonjs';", "");
  104. let regexp = new RegExp(`import {(.*)} from ['"]${packageName}(.*)['"];`, 'g');
  105. str = str.replace(regexp, '');
  106. // Let s clean other chosen imports from the mix.
  107. if (options.importsToRemove) {
  108. while (options.importsToRemove.length) {
  109. let remove = options.importsToRemove.pop();
  110. str = str.replace(new RegExp(`import '${remove}';`), '');
  111. }
  112. }
  113. // Find all other imported classes (Part of BABYLON or Loaders for instance)
  114. // and suffix them by the namespace.
  115. if ((options.classMap)) {
  116. // Replace import { foo, bar } from ...
  117. Object.keys(options.classMap).forEach(package => {
  118. var babylonRegex = new RegExp(`import {(.*)} from ['"](${package})[\/'"](.*);`, "g");
  119. var match = babylonRegex.exec(str);
  120. let classes = new Set();
  121. while (match != null) {
  122. if (match[1]) {
  123. match[1].split(",").forEach(element => {
  124. classes.add(element.trim());
  125. });
  126. }
  127. match = babylonRegex.exec(str);
  128. }
  129. str = str.replace(babylonRegex, '');
  130. classes.forEach(cls => {
  131. let className = cls;
  132. let alias = cls;
  133. // Deal with import { foo as A, bar as B } from ...
  134. if (cls.indexOf(" as ") > -1) {
  135. const tokens = cls.split(" as ");
  136. className = tokens[0];
  137. alias = tokens[1];
  138. }
  139. // !!! Be carefull
  140. // Could cause issues if this appears in several import scope
  141. // with different aliases.
  142. // !!! Be carefull multiline not managed.
  143. // False is a special case to remove all the lines.
  144. if (options.classMap[package] === false) {
  145. const rg = new RegExp(`.*[ <]${alias}[^\\w].*`, "g")
  146. str = str.replace(rg, "");
  147. }
  148. // Else replace with the namespace prefix.
  149. else {
  150. const rg = new RegExp(`([ <])(${alias})([^\\w])`, "g")
  151. str = str.replace(rg, `$1${options.classMap[package]}.${className}$3`);
  152. }
  153. });
  154. });
  155. // Replace import * as ...
  156. Object.keys(options.classMap).forEach(package => {
  157. var babylonRegex = new RegExp(`import \\* as (.*) from ['"](${package})['"];`, "g");
  158. var match = babylonRegex.exec(str);
  159. let localNamespace = "";
  160. if (match && match[1]) {
  161. localNamespace = match[1].trim();
  162. str = str.replace(babylonRegex, '');
  163. let rg = new RegExp(`([ <])(${localNamespace}.)([A-Za-z])`, "g")
  164. str = str.replace(rg, `$1${options.classMap[package]}.$3`);
  165. }
  166. });
  167. }
  168. // Clean up named export.
  169. str = str.replace(/export {(.*)};/g, '');
  170. // Clean up left import.
  171. str = str.replace(/import (.*);/g, "");
  172. // Clean up export * from.
  173. str = str.split("\n").filter(line => line.trim()).filter(line => line.indexOf("export * from") === -1).join("\n");
  174. // Remove empty module declaration
  175. var cleanEmptyNamespace = function(str, moduleName) {
  176. let emptyDeclareRegexp = new RegExp("declare module " + moduleName + " {\\s*}\\s*", "gm");
  177. str = str.replace(emptyDeclareRegexp, "");
  178. return str;
  179. }
  180. str = cleanEmptyNamespace(str, options.moduleName);
  181. // Remove empty module declaration of specific modules
  182. if (options.moduleSpecifics) {
  183. options.moduleSpecifics.forEach(function(specific) {
  184. str = cleanEmptyNamespace(str, specific.namespace);
  185. });
  186. }
  187. // Remove Empty Lines
  188. str = str.replace(/^\s*$/gm, "");
  189. // Remove Inlined Import
  190. str = str.replace(/import\("[A-Za-z0-9\/]*"\)\./g, "");
  191. return str;
  192. }
  193. module.exports = function(fileLocation, packageName, options, cb) {
  194. options = options || { };
  195. fs.readFile(fileLocation, function(err, data) {
  196. if (err) throw err;
  197. data += "";
  198. if (options.replacements) {
  199. for (let replacement of options.replacements) {
  200. data = data.replace(replacement.from, replacement.to);
  201. }
  202. }
  203. if (options.prependText) {
  204. data = options.prependText + '\n' + data.toString();
  205. }
  206. var newData = "";
  207. if (options) {
  208. newData = processData(data, packageName, options);
  209. var namespaceData = newData;
  210. fs.writeFileSync(fileLocation.replace('.module', ''), namespaceData);
  211. }
  212. if (options.doNotAppendNamespace) {
  213. fs.writeFileSync(fileLocation, data);
  214. }
  215. else {
  216. fs.writeFileSync(fileLocation, data + "\n" + newData);
  217. }
  218. cb && cb();
  219. });
  220. }