Ver código fonte

Import URL Linting to ensure tree shaking

sebavan 6 anos atrás
pai
commit
0dff3115bf

+ 1 - 1
.travis.yml

@@ -22,7 +22,7 @@ jobs:
   - env: JOB=DocumentationCheck
     script: gulp typedoc-check
   - env: JOB=Styling
-    script: gulp tsLint
+    script: gulp fullLint
   - env: JOB=Build
     script: gulp typescript-all
   - env: JOB=Tests

+ 2 - 0
Tools/Config/config.js

@@ -97,6 +97,7 @@ config.modules.map(function(module) {
             srcDirectory.replace(/\\/g, "/") + "/**/*.vertex.ts",
             srcDirectory.replace(/\\/g, "/") + "/**/ShadersInclude/*.ts",
         ];
+        const tsGlob = srcDirectory.replace(/\\/g, "/") + "/**/*.ts*";
 
         for (let library of settings.libraries) {
             const entryPath = path.join(srcDirectory, library.entry);
@@ -109,6 +110,7 @@ config.modules.map(function(module) {
         settings.computed.srcDirectory = srcDirectory;
         settings.computed.shaderGlob = shaderGlob;
         settings.computed.shaderTSGlob = shaderTSGlob;
+        settings.computed.tsGlob = tsGlob;
     }
 });
 

+ 12 - 2
Tools/Gulp/gulpfile.js

@@ -6,6 +6,7 @@ require("./tasks/gulpTasks-viewerLibraries");
 require("./tasks/gulpTasks-libraries");
 require("./tasks/gulpTasks-librariesES6");
 require("./tasks/gulpTasks-tsLint");
+require("./tasks/gulpTasks-importLint");
 require("./tasks/gulpTasks-netlify");
 require("./tasks/gulpTasks-whatsNew");
 require("./tasks/gulpTasks-localRun");
@@ -19,11 +20,20 @@ require("./tasks/gulpTasks-npmPackages");
 
 /**
  * Full TsLint.
- * Back Compat Only, now included in typescript-libraries-tsLint.
  */
 gulp.task("tsLint", gulp.series("typescript-libraries-tsLint"));
 
 /**
+ * Full ImportLint.
+ */
+gulp.task("importLint", gulp.series("typescript-libraries-importLint"));
+
+/**
+ * Full Lint.
+ */
+gulp.task("fullLint", gulp.series("tsLint", "importLint"));
+
+/**
  * Validate compile the code and check the comments and style case convention through typedoc
  */
 gulp.task("typedoc-check", gulp.series("core", "gui", "loaders", "serializers", "typedoc-generate", "typedoc-validate"));
@@ -51,4 +61,4 @@ gulp.task("npmPackages", gulp.series("npmPackages-all"));
 /**
  * The default task, concat and min the main BJS files.
  */
-gulp.task("default", gulp.series("tsLint", "typescript-all", "intellisense", "typedoc-all", "tests-all"));
+gulp.task("default", gulp.series("tsLint", "importLint", "typescript-all", "intellisense", "typedoc-all", "tests-all"));

+ 193 - 0
Tools/Gulp/helpers/gulp-validateImports.js

@@ -0,0 +1,193 @@
+// Gulp Tools
+var fs = require("fs");
+var path = require("path");
+var through = require('through2');
+var PluginError = require('plugin-error');
+var colorConsole = require("../../NodeHelpers/colorConsole");
+
+var config = require("../../Config/config");
+
+const indexExlclusion = ["States", "EmitterTypes"];
+
+var validateImports = function(data, fileLocation, options) {
+    var str = "" + data;
+    var errors = [];
+
+    // Start process by extracting all lines.
+    let lines = str.split('\n');
+
+    // Let's go line by line and check if we have special folder replacements
+    // Replaces declare module '...'; by declare module 'babylonjs/...'; for instance
+    for (let index = 0; index < lines.length; index++) {
+        let line = lines[index];
+        let module = null, externalModule = null;
+
+        // Find Imports.
+        if (line.indexOf("import") > -1) {
+            let regexTypeImport = new RegExp(`import .* from ['"](.*)['"];`, "g");
+            let match = regexTypeImport.exec(line);
+            if (match) {
+                module = match[1];
+            }
+            else {
+                let regexSideEffectImport = new RegExp(`import \\(*['"](.*)['"]\\)*;`, "g");
+                let matchSideEffects = regexSideEffectImport.exec(line);
+                if (matchSideEffects) {
+                    module = matchSideEffects[1];
+                }
+                else {
+                    continue;
+                }
+            }
+
+            // Checks if line is about external module
+            if (options.externals) {
+                for (let ext in options.externals) {
+                    if (line.indexOf(ext) > -1) {
+                        externalModule = ext;
+                        break;
+                    }
+                }
+            }
+
+            // Check Relative.
+            if (!externalModule && !module.startsWith(".")) {
+                errors.push(`Line ${index} Imports needs to be relative. ${module}`);
+            }
+
+            // Check if path is correct internal.
+            if (!externalModule) {
+                const directory = path.dirname(fileLocation);
+                let internalModulePath = path.join(directory, module + ".ts");
+                if (!fs.existsSync(internalModulePath)) {
+                    let internalModulePath = path.join(directory, module + ".tsx");
+                    if (!fs.existsSync(internalModulePath)) {
+                        if (fileLocation.indexOf("legacy") > -1 || fileLocation.indexOf("index") > -1) {
+                            let internalModulePath = path.join(directory, module + "index.ts");
+                            if (!fs.existsSync(internalModulePath)) {
+                                let internalModulePath = path.join(directory, module + "/index.ts");
+                                if (!fs.existsSync(internalModulePath)) {
+                                    errors.push(`Line ${index} Imports needs to be full path. ${module}`);
+                                }
+                            }
+                        }
+                        else {
+                            errors.push(`Line ${index} Imports ${module} needs to be full path.`);
+                        }
+                    }
+                }
+
+                if (internalModulePath.indexOf("index.") > -1) {
+                    if (fileLocation.indexOf("legacy") === -1) {
+                        let excluded = false;
+                        for (let exclusion of indexExlclusion) {
+                            if (internalModulePath.indexOf(exclusion) > -1) {
+                                excluded = true;
+                                break;
+                            }
+                        }
+                        if (!excluded) {
+                            errors.push(`Line ${index} Imports ${module} should not be from index for tree shaking.`);
+                        }
+                    }
+                }
+            }
+            else {
+                const mapping = {
+                    "babylonjs": "core",
+                    "babylonjs-loaders": "loaders",
+                    "babylonjs-serializers": "serializers",
+                    "babylonjs-gui": "gui",
+                };
+
+                const splitter = module.indexOf("/");
+                const baseModule = module.substring(0, splitter);
+                if (mapping[baseModule]) {
+                    const configName = mapping[baseModule];
+
+                    const directory = config[configName].computed.srcDirectory;
+                    module = module.substring(splitter);
+
+                    let internalModulePath = path.join(directory, module + ".ts");
+                    if (!fs.existsSync(internalModulePath)) {
+                        let internalModulePath = path.join(directory, module + ".tsx");
+                        if (!fs.existsSync(internalModulePath)) {
+                            if (fileLocation.indexOf("legacy") > -1 || fileLocation.indexOf("index") > -1) {
+                                let internalModulePath = path.join(directory, module + "index.ts");
+                                if (!fs.existsSync(internalModulePath)) {
+                                    let internalModulePath = path.join(directory, module + "/index.ts");
+                                    if (!fs.existsSync(internalModulePath)) {
+                                        errors.push(`Line ${index} Imports needs to be full path. ${module}`);
+                                    }
+                                }
+                            }
+                            else {
+                                errors.push(`Line ${index} Imports ${module} needs to be full path.`);
+                            }
+                        }
+                    }
+
+                    if (internalModulePath.indexOf("index.") > -1) {
+                        if (fileLocation.indexOf("legacy") === -1) {
+                            let excluded = false;
+                            for (let exclusion of indexExlclusion) {
+                                if (internalModulePath.indexOf(exclusion) > -1) {
+                                    excluded = true;
+                                    break;
+                                }
+                            }
+                            if (!excluded) {
+                                errors.push(`Line ${index} Imports ${module} should not be from index for tree shaking.`);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    return errors;
+}
+
+function gulpValidateImports(options) {
+    var globalErrors = [];
+
+    return through.obj(function (file, enc, cb) {
+        if (file.isNull()) {
+            cb(null, file);
+            return;
+        }
+        if (file.isStream()) {
+            cb(new PluginError("Validate imports", "Streaming not supported."));
+        }
+        
+        let data = file.contents.toString();
+        let result = validateImports(data, file.path, options);
+
+        if (result.length > 0) {
+            for (let error of result) {
+                globalErrors.push({
+                    message: error,
+                    path: file.path
+                });
+            }
+        }
+
+        return cb();
+    }, 
+    function endStream(cb) {
+        if (globalErrors.length > 0) {
+            for (let error of globalErrors) {
+                colorConsole.error(error.message + " " + error.path);
+            }
+            colorConsole.error(`Import validation failed with ${globalErrors.length} errors.`);
+
+            var finalMessage = new PluginError('gulp-validateImports', `gulp-validateImports: ${globalErrors.length} errors found.`);
+            this.emit('error', finalMessage);
+        }
+
+        cb();
+    });
+}
+
+module.exports = gulpValidateImports;

+ 44 - 0
Tools/Gulp/tasks/gulpTasks-importLint.js

@@ -0,0 +1,44 @@
+// Import Dependencies.
+var gulp = require("gulp");
+var filter = require('gulp-filter');
+
+// Helpers
+var validateImports = require("../helpers/gulp-validateImports");
+
+// Read the full config.
+var config = require("../../Config/config.js");
+
+/*
+ * ImportLint all typescript files from the src directory.
+ */
+var importLintLibrary = function(settings) {
+    const fxFilter = filter(['**', '!**/*.fragment.ts', '!**/*.vertex.ts', '!**/ShadersInclude/**'], { restore: false });
+    return gulp.src(settings.computed.tsGlob)
+        .pipe(fxFilter)
+        .pipe(validateImports({
+            externals: settings.build.umd.processDeclaration.classMap
+        }));
+}
+
+const lintModules = config.modules.filter((module) => module != "inspector");
+
+/**
+ * Dynamic module linting for library (mat, post processes, ...).
+ */
+lintModules.map(function(module) {
+    // Task will be like moduleName-importLint
+    gulp.task(module + "-importLint", function() {
+        var settings = config[module];
+
+        return importLintLibrary(settings, false);
+    });
+});
+
+/**
+ * Full Librairies importLint.
+ */
+gulp.task("typescript-libraries-importLint",
+    gulp.series(lintModules.map((module) => {
+        return module + "-importLint";
+    })
+));

+ 1 - 1
Tools/Gulp/tasks/gulpTasks-tsLint.js

@@ -24,7 +24,7 @@ var config = require("../../Config/config.js");
  */
 var tsLintExternalLibrary = function(settings) {
     const fxFilter = filter(['**', '!**/*.fragment.ts', '!**/*.vertex.ts', '!**/ShadersInclude/**'], { restore: false });
-    return gulp.src((settings.computed.srcDirectory) + "/**/*.ts")
+    return gulp.src(settings.computed.tsGlob)
         .pipe(fxFilter)
         .pipe(gulpTslint(tsLintConfig))
         .pipe(gulpTslint.report());

+ 1 - 1
gui/src/2D/controls/displayGrid.ts

@@ -1,4 +1,4 @@
-import { Control } from ".";
+import { Control } from "./control";
 
 /** Class used to render a grid  */
 export class DisplayGrid extends Control {

+ 2 - 1
gui/src/2D/controls/radioButton.ts

@@ -2,7 +2,8 @@ import { Observable } from "babylonjs/Misc/observable";
 import { Vector2 } from "babylonjs/Maths/math";
 
 import { Control } from "./control";
-import { StackPanel, TextBlock } from ".";
+import { StackPanel } from "./stackPanel";
+import { TextBlock } from "./textBlock";
 
 /**
  * Class used to create radio button controls

+ 1 - 1
gui/src/2D/controls/sliders/scrollBar.ts

@@ -1,6 +1,6 @@
 import { Vector2 } from "babylonjs/Maths/math";
 import { BaseSlider } from "./baseSlider";
-import { Control } from "..";
+import { Control } from "../control";
 import { Measure } from "../../measure";
 
 /**

+ 0 - 1
inspector/src/inspector.ts

@@ -1,4 +1,3 @@
-
 import * as React from "react";
 import * as ReactDOM from "react-dom";
 

+ 2 - 1
loaders/src/glTF/1.0/glTFMaterialsCommonExtension.ts

@@ -1,4 +1,5 @@
-import { GLTFLoaderExtension, GLTFLoaderBase } from ".";
+import { GLTFLoaderExtension } from "./glTFLoaderExtension";
+import { GLTFLoaderBase } from "./glTFLoader";
 
 import { IGLTFRuntime, IGLTFMaterial } from "./glTFLoaderInterfaces";