gulp-validateImports.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. // Gulp Tools
  2. var fs = require("fs");
  3. var path = require("path");
  4. var through = require('through2');
  5. var PluginError = require('plugin-error');
  6. var colorConsole = require("../../NodeHelpers/colorConsole");
  7. var config = require("../../Config/config");
  8. const indexExlclusion = ["States", "EmitterTypes"];
  9. const forbiddenImports = ["Meshes/meshBuilder"];
  10. const mapping = { };
  11. config.modules.forEach(moduleName => {
  12. mapping[config[moduleName].build.umd.packageName] = moduleName;
  13. });
  14. var validatePath = function(fileLocation, directory, module, lineNumber, errors) {
  15. let internalModulePath = path.join(directory, module + ".ts");
  16. // Check .ts path.
  17. if (!fs.existsSync(internalModulePath)) {
  18. let internalModulePath = path.join(directory, module + ".tsx");
  19. // Check .tsx path.
  20. if (!fs.existsSync(internalModulePath)) {
  21. // If not found, check index.ts for legacy and index files.
  22. if (fileLocation.indexOf("legacy") > -1 || fileLocation.indexOf("index") > -1) {
  23. let internalModulePath = path.join(directory, module, "index.ts");
  24. if (!fs.existsSync(internalModulePath)) {
  25. errors.push(`Line ${lineNumber} Export from folder only allowes if index is present. ${module}`);
  26. }
  27. }
  28. else {
  29. errors.push(`Line ${lineNumber} Imports ${module} needs to be full path (not from directory) for tree shaking.`);
  30. }
  31. }
  32. }
  33. if (internalModulePath.indexOf("index.") > -1) {
  34. if (fileLocation.indexOf("legacy") === -1) {
  35. let excluded = false;
  36. for (let exclusion of indexExlclusion) {
  37. if (internalModulePath.indexOf(exclusion) > -1) {
  38. excluded = true;
  39. break;
  40. }
  41. }
  42. if (!excluded) {
  43. errors.push(`Line ${lineNumber} Imports ${module} should not be from index for tree shaking.`);
  44. }
  45. }
  46. }
  47. for (let forbiddenImport of forbiddenImports) {
  48. if (module.endsWith(forbiddenImport)) {
  49. errors.push(`Line ${lineNumber} Imports ${module} is forbidden for tree shaking.`);
  50. }
  51. }
  52. }
  53. var validateImports = function(data, fileLocation, options) {
  54. var str = "" + data;
  55. var errors = [];
  56. // Start process by extracting all lines.
  57. let lines = str.split('\n');
  58. // Let's go line by line and check if we have special folder replacements
  59. // Replaces declare module '...'; by declare module 'babylonjs/...'; for instance
  60. for (let index = 0; index < lines.length; index++) {
  61. let line = lines[index];
  62. let module = null, externalModule = null;
  63. // Find Imports.
  64. if (line.indexOf("import") > -1) {
  65. let regexTypeImport = new RegExp(`import .* from ['"](.*)['"];`, "g");
  66. let match = regexTypeImport.exec(line);
  67. if (match) {
  68. module = match[1];
  69. }
  70. else {
  71. let regexSideEffectImport = new RegExp(`import \\(*['"](.*)['"]\\)*;`, "g");
  72. let matchSideEffects = regexSideEffectImport.exec(line);
  73. if (matchSideEffects) {
  74. module = matchSideEffects[1];
  75. }
  76. else {
  77. continue;
  78. }
  79. }
  80. // Checks if line is about external module
  81. if (options.externals) {
  82. for (let ext in options.externals) {
  83. if (line.indexOf(ext) > -1) {
  84. externalModule = ext;
  85. break;
  86. }
  87. }
  88. }
  89. // Check if path is correct internal.
  90. if (externalModule) {
  91. const splitter = module.indexOf("/");
  92. const baseModule = module.substring(0, splitter);
  93. if (mapping[baseModule]) {
  94. const configName = mapping[baseModule];
  95. const directory = config[configName].computed.srcDirectory;
  96. module = module.substring(splitter);
  97. validatePath(fileLocation, directory, module, index + 1, errors);
  98. }
  99. }
  100. else {
  101. // Check Relative.
  102. if (!module.startsWith(".")) {
  103. errors.push(`Line ${index + 1} Import ${module} needs to be relative.`);
  104. }
  105. else {
  106. const directory = path.dirname(fileLocation);
  107. validatePath(fileLocation, directory, module, index + 1, errors);
  108. }
  109. }
  110. }
  111. }
  112. return errors;
  113. }
  114. function gulpValidateImports(options) {
  115. var globalErrors = [];
  116. return through.obj(function (file, enc, cb) {
  117. if (file.isNull()) {
  118. cb(null, file);
  119. return;
  120. }
  121. if (file.isStream()) {
  122. cb(new PluginError("Validate imports", "Streaming not supported."));
  123. }
  124. let data = file.contents.toString();
  125. let result = validateImports(data, file.path, options);
  126. if (result.length > 0) {
  127. for (let error of result) {
  128. globalErrors.push({
  129. message: error,
  130. path: file.path
  131. });
  132. }
  133. }
  134. return cb();
  135. },
  136. function endStream(cb) {
  137. if (globalErrors.length > 0) {
  138. for (let error of globalErrors) {
  139. colorConsole.error(error.message + " " + error.path);
  140. }
  141. colorConsole.error(`Import validation failed with ${globalErrors.length} errors.`);
  142. var finalMessage = new PluginError('gulp-validateImports', `gulp-validateImports: ${globalErrors.length} errors found.`);
  143. this.emit('error', finalMessage);
  144. }
  145. cb();
  146. });
  147. }
  148. module.exports = gulpValidateImports;