diff --git a/lib/visitors/ATPEDependencies.js b/lib/visitors/ATPEDependencies.js new file mode 100644 index 0000000..22bb9ca --- /dev/null +++ b/lib/visitors/ATPEDependencies.js @@ -0,0 +1,101 @@ +/* + * Copyright 2015 Amadeus s.a.s. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var vm = require("vm"); +var grunt = require('../grunt').grunt(); + +var evalJsonString = function (code) { + // note that JSON.parse is too strict compared to the Aria Templates eval + return vm.runInNewContext("(" + code + ")"); +}; + +var classpathToLogicalPath = function (classpath, extension) { + return classpath.split(".").join("/") + extension; +}; + +var getJsonDependencies = function (json) { + var dependencies = []; + + var processModules = function (modulesMap) { + if (!modulesMap) { + return; + } + for (var key in modulesMap) { + if (modulesMap.hasOwnProperty(key)) { + var curModule = modulesMap[key]; + dependencies.push(classpathToLogicalPath(curModule.classpath, ".js")); + } + } + }; + + var processPlaceholder = function (placeholder) { + if (typeof placeholder == "object") { + if (placeholder.constructor.isArray) { + placeholder.forEach(processPlaceholder); + } else { + dependencies.push(classpathToLogicalPath(placeholder.template, ".tpl")); + } + } + }; + + if (json.pageComposition) { + // page definition + processModules(json.pageComposition.modules); + var placeholdersMap = json.pageComposition.placeholders; + for (var placeholder in placeholdersMap) { + if (placeholdersMap.hasOwnProperty(placeholder)) { + processPlaceholder(placeholdersMap[placeholder]); + } + } + dependencies.push(classpathToLogicalPath(json.pageComposition.template, ".tpl")); + } else if (json.containerId) { + // site configuration + processModules(json.commonModules); + } + return dependencies; +}; + +var ATPEDependencies = function (cfg) { + cfg = cfg || {}; + this.files = cfg.files || ['**/*.json']; + this.mustExist = cfg.hasOwnProperty('mustExist') ? cfg.mustExist : true; + this.externalDependencies = cfg.hasOwnProperty('externalDependencies') ? cfg.externalDependencies : []; +}; + +ATPEDependencies.prototype.computeDependencies = function (packaging, inputFile) { + if (!inputFile.isMatch(this.files)) { + return; + } + + var mustExist = this.mustExist; + var externalDependencies = this.externalDependencies; + var textContent = inputFile.getTextContent(); + try { + var jsonContent = evalJsonString(textContent); + var dependencies = getJsonDependencies(jsonContent); + dependencies.forEach(function (dependency) { + var correspondingFile = packaging.getSourceFile(dependency); + if (correspondingFile) { + inputFile.addDependency(correspondingFile); + } else if (mustExist && !grunt.file.isMatch(externalDependencies, [dependency])) { + grunt.log.error(inputFile.logicalPath.yellow + " depends on " + dependency + " which cannot be found."); + } + }); + } catch (error) { + grunt.log.error("ATPEDependencies: error when processing " + inputFile.logicalPath.yellow + ": " + error); + } +}; + +module.exports = ATPEDependencies; \ No newline at end of file diff --git a/lib/visitors/DependenciesGraph.js b/lib/visitors/DependenciesGraph.js new file mode 100644 index 0000000..a43e840 --- /dev/null +++ b/lib/visitors/DependenciesGraph.js @@ -0,0 +1,75 @@ +/* + * Copyright 2015 Amadeus s.a.s. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var grunt = require('../grunt').grunt(); +var path = require('path'); + +var normalizePath = function (filePath) { + return path.normalize(filePath).split(path.sep).join('/'); // only forward slashes for the web +}; + +var stringifyForDot = function (string) { + return '"' + string.replace(/"/g, "\"") + '"'; +}; + +var DependenciesGraph = function (cfg) { + cfg = cfg || {}; + this.sourceFiles = cfg.sourceFiles || ['**/*']; // source files to take into account in the graph + this.graphFile = cfg.graphFile || 'dependencies.gv'; // the graph file to write + this.graphFileEncoding = cfg.graphFileEncoding || null; // encoding of the graph file + this.outputDirectory = cfg.outputDirectory || null; // output directory of the graph file; if not specified, the + // global one is used +}; + +DependenciesGraph.prototype.onAfterBuild = function (packaging) { + var sourceFiles = packaging.sourceFiles; + var sourceFilesPatterns = this.sourceFiles; + var cache = {}; + var output = ["digraph dependencies {\n"]; + + var processSourceFile = function (sourceFile) { + var logicalPath = sourceFile.logicalPath; + if (cache.hasOwnProperty(logicalPath)) { + return cache[logicalPath]; + } + if (!sourceFile.isMatch(sourceFilesPatterns)) { + cache[logicalPath] = false; + return false; + } + cache[logicalPath] = true; + var dependencies = sourceFile.getDependencies().filter(processSourceFile); + var stringifiedName = stringifyForDot(normalizePath(sourceFile.logicalPath)); + output.push(stringifiedName, ";\n"); + dependencies.forEach(function (curDep) { + output.push(stringifiedName, " -> ", stringifyForDot(normalizePath(curDep.logicalPath)), ";\n"); + }); + return true; + }; + + for (var file in sourceFiles) { + if (sourceFiles.hasOwnProperty(file)) { + processSourceFile(sourceFiles[file]); + } + } + + output.push("}\n"); + + var outputGraphFile = path.join(this.outputDirectory || packaging.outputDirectory, this.graphFile); + grunt.file.write(outputGraphFile, output.join(""), { + encoding : this.graphFileEncoding || grunt.file.defaultEncoding + }); +}; + +module.exports = DependenciesGraph;