All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.javascript.jscomp.FindModuleDependencies Maven / Gradle / Ivy

/*
 * Copyright 2017 The Closure Compiler Authors.
 *
 * 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.
 */

package com.google.javascript.jscomp;

import static com.google.common.base.Preconditions.checkArgument;

import com.google.common.collect.ImmutableMap;
import com.google.javascript.jscomp.CompilerInput.ModuleType;
import com.google.javascript.jscomp.deps.DependencyInfo.Require;
import com.google.javascript.jscomp.deps.ModuleLoader;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;

/**
 * Find and update any direct dependencies of an input. Used to walk the dependency graph and
 * support a strict depth-first dependency ordering. Marks an input as providing its module name.
 *
 * 

Discovers dependencies from: * *

    *
  • goog.require calls *
  • ES6 import statements *
  • CommonJS require statements *
* *

The order of dependency references is preserved so that a deterministic depth-first ordering * can be achieved. */ public class FindModuleDependencies implements NodeTraversal.ScopedCallback { private final AbstractCompiler compiler; private final boolean supportsEs6Modules; private final boolean supportsCommonJsModules; private ModuleType moduleType = ModuleType.NONE; private Scope dynamicImportScope = null; private final ImmutableMap inputPathByWebpackId; FindModuleDependencies( AbstractCompiler compiler, boolean supportsEs6Modules, boolean supportsCommonJsModules, ImmutableMap inputPathByWebpackId) { this.compiler = compiler; this.supportsEs6Modules = supportsEs6Modules; this.supportsCommonJsModules = supportsCommonJsModules; this.inputPathByWebpackId = inputPathByWebpackId; } private static class FindGoogProvideOrGoogModule extends NodeTraversal.AbstractPreOrderCallback { private boolean found; boolean isFound() { return found; } @Override public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { if (found) { return false; } // Shallow traversal, since we don't need to inspect within functions or expressions. if (parent == null || NodeUtil.isControlStructure(parent) || NodeUtil.isStatementBlock(parent)) { if (n.isExprResult()) { Node maybeGetProp = n.getFirstFirstChild(); if (maybeGetProp != null && (maybeGetProp.matchesQualifiedName("goog.provide") || maybeGetProp.matchesQualifiedName("goog.module"))) { found = true; return false; } } return true; } return false; } } public void process(Node root) { checkArgument(root.isScript()); if (Es6RewriteModules.isEs6ModuleRoot(root)) { moduleType = ModuleType.ES6; } CompilerInput input = compiler.getInput(root.getInputId()); // The "goog" namespace isn't always specifically required. // The deps parser will pick up any access to a `goog.foo()` call // and add "goog" as a dependency. If "goog" is a dependency of the // file we add it here to the ordered requires so that it's always // first. if (input.getRequires().contains(Require.BASE)) { input.addOrderedRequire(Require.BASE); } NodeTraversal.traverse(compiler, root, this); if (moduleType == ModuleType.ES6) { convertToEs6Module(root, true); } else if (moduleType == ModuleType.NONE && inputPathByWebpackId != null && inputPathByWebpackId.containsValue(input.getPath().toString())) { moduleType = ModuleType.IMPORTED_SCRIPT; } input.addProvide(input.getPath().toModuleName()); input.setJsModuleType(moduleType); input.setHasFullParseDependencyInfo(true); } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (supportsCommonJsModules && n.isFunction() && ProcessCommonJSModules.isCommonJsDynamicImportCallback( n, compiler.getOptions().moduleResolutionMode)) { if (dynamicImportScope == null) { dynamicImportScope = t.getScope(); } } return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { ModuleLoader.ResolutionMode resolutionMode = compiler.getOptions().moduleResolutionMode; if (parent == null || NodeUtil.isControlStructure(parent) || NodeUtil.isStatementBlock(parent)) { if (n.isExprResult()) { Node maybeGetProp = n.getFirstFirstChild(); if (maybeGetProp != null && (maybeGetProp.matchesQualifiedName("goog.provide") || maybeGetProp.matchesQualifiedName("goog.module"))) { moduleType = ModuleType.GOOG; return; } } } if (supportsEs6Modules && n.isExport()) { moduleType = ModuleType.ES6; if (n.getBooleanProp(Node.EXPORT_DEFAULT)) { // export default } else if (n.hasTwoChildren()) { // export * from 'moduleIdentifier'; // export {x, y as z} from 'moduleIdentifier'; addEs6ModuleImportToGraph(t, n); } } else if (supportsEs6Modules && n.isImport()) { moduleType = ModuleType.ES6; addEs6ModuleImportToGraph(t, n); } else if (supportsCommonJsModules) { if (moduleType != ModuleType.GOOG && ProcessCommonJSModules.isCommonJsExport(t, n, resolutionMode)) { moduleType = ModuleType.COMMONJS; } else if (ProcessCommonJSModules.isCommonJsImport(n, resolutionMode)) { String path = ProcessCommonJSModules.getCommonJsImportPath(n, resolutionMode); ModuleLoader.ModulePath modulePath = t.getInput() .getPath() .resolveJsModule(path, n.getSourceFileName(), n.getLineno(), n.getCharno()); if (modulePath != null) { if (dynamicImportScope != null || ProcessCommonJSModules.isCommonJsDynamicImportCallback( NodeUtil.getEnclosingFunction(n), resolutionMode)) { t.getInput().addDynamicRequire(modulePath.toModuleName()); } else { t.getInput().addOrderedRequire(Require.commonJs(modulePath.toModuleName(), path)); } } } // TODO(ChadKillingsworth) add require.ensure support } if (parent != null && (parent.isExprResult() || !t.inGlobalHoistScope()) && n.isCall() && n.getFirstChild().matchesQualifiedName("goog.require") && n.getSecondChild() != null && n.getSecondChild().isString()) { String namespace = n.getSecondChild().getString(); if (namespace.startsWith("goog.")) { t.getInput().addOrderedRequire(Require.BASE); } t.getInput().addOrderedRequire(Require.googRequireSymbol(namespace)); } } @Override public void enterScope(NodeTraversal t) {} @Override public void exitScope(NodeTraversal t) { if (t.getScope() == dynamicImportScope) { dynamicImportScope = null; } } /** Adds an es6 module from an import node (import or export statement) to the graph. */ private void addEs6ModuleImportToGraph(NodeTraversal t, Node n) { String moduleName = getEs6ModuleNameFromImportNode(t, n); if (moduleName.startsWith("goog.")) { t.getInput().addOrderedRequire(Require.BASE); } t.getInput().addOrderedRequire(Require.es6Import(moduleName, n.getLastChild().getString())); } /** Get the module name from an import node (import or export statement). */ private String getEs6ModuleNameFromImportNode(NodeTraversal t, Node n) { String importName = n.getLastChild().getString(); boolean isNamespaceImport = importName.startsWith("goog:"); if (isNamespaceImport) { // Allow importing Closure namespace objects (e.g. from goog.provide or goog.module) as // import ... from 'goog:my.ns.Object'. // These are rewritten to plain namespace object accesses. return importName.substring("goog:".length()); } else { ModuleLoader.ModulePath modulePath = t.getInput() .getPath() .resolveJsModule(importName, n.getSourceFileName(), n.getLineno(), n.getCharno()); if (modulePath == null) { // The module loader issues an error // Fall back to assuming the module is a file path modulePath = t.getInput().getPath().resolveModuleAsPath(importName); } return modulePath.toModuleName(); } } private boolean convertToEs6Module(Node root, boolean skipGoogProvideModuleCheck) { if (Es6RewriteModules.isEs6ModuleRoot(root)) { return true; } if (!skipGoogProvideModuleCheck) { FindGoogProvideOrGoogModule finder = new FindGoogProvideOrGoogModule(); NodeTraversal.traverse(compiler, root, finder); if (finder.isFound()) { return false; } } Node moduleNode = new Node(Token.MODULE_BODY).srcref(root); moduleNode.addChildrenToBack(root.removeChildren()); root.addChildToBack(moduleNode); return true; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy