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

com.google.javascript.jscomp.modules.ModuleMetadataMap Maven / Gradle / Ivy

Go to download

Closure Compiler is a JavaScript optimizing compiler. It parses your JavaScript, analyzes it, removes dead code and rewrites and minimizes what's left. It also checks syntax, variable references, and types, and warns about common JavaScript pitfalls. It is used in many of Google's JavaScript apps, including Gmail, Google Web Search, Google Maps, and Google Docs.

There is a newer version: v20240317
Show newest version
/*
 * Copyright 2018 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.modules;

import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultiset;
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.javascript.jscomp.deps.ModuleLoader.ModulePath;
import com.google.javascript.rhino.Node;
import java.util.Map;
import org.jspecify.nullness.Nullable;

/**
 * Contains metadata around modules (or scripts) that is useful for checking imports / requires.
 *
 * 

TODO(johnplaisted): There's an opportunity for reuse here in ClosureRewriteModules, which * would involve putting this in some common location. Currently this is only used as a helper class * for Es6RewriteModules. CompilerInput already has some (not all) of this information but it is not * always populated. It may also be ideal to include CommonJS here too as ES6 modules can import * them. That would allow decoupling of how these modules are written; right now Es6RewriteModule * only checks this for goog.requires and goog: imports, not for ES6 path imports. */ public final class ModuleMetadataMap { /** Various types of Javascript modules and scripts that can be found in the JS Compiler. */ public enum ModuleType { ES6_MODULE("an ES6 module"), /** A goog.module that does not declare a legacy namespace. */ GOOG_MODULE("a goog.module"), /** A goog.module that declares a legacy namespace with goog.module.declareLegacyNamespace. */ LEGACY_GOOG_MODULE("a goog.module"), COMMON_JS("a CommonJS module"), // The following two cases are not actually modules, but are useful to include in the map. GOOG_PROVIDE("a script file that contains at least one goog.provide"), SCRIPT("a script file that does not contain a goog.provide"); public final String description; ModuleType(String description) { this.description = description; } } /** * Map from module path to module. These modules represent files and thus will contain all goog * namespaces that are in the file. These are not the same modules in modulesByGoogNamespace. */ private final ImmutableMap modulesByPath; /** * Map from Closure namespace to module. These modules represent just the single namespace and * thus each module has only one goog namespace in its {@link ModuleMetadata#googNamespaces()}. * These are not the same modules in modulesByPath. */ private final ImmutableMap modulesByGoogNamespace; private final ImmutableSet moduleMetadata; public ModuleMetadataMap( Map modulesByPath, Map modulesByGoogNamespace) { this.modulesByPath = ImmutableMap.copyOf(modulesByPath); this.modulesByGoogNamespace = ImmutableMap.copyOf(modulesByGoogNamespace); this.moduleMetadata = ImmutableSet.builder() .addAll(modulesByPath.values()) .addAll(modulesByGoogNamespace.values()) .build(); } /** * Struct containing basic information about a module/script including its type and goog * namespaces. */ @AutoValue public abstract static class ModuleMetadata { public abstract ModuleType moduleType(); public boolean isEs6Module() { return moduleType() == ModuleType.ES6_MODULE; } public boolean isGoogModule() { return isNonLegacyGoogModule() || isLegacyGoogModule(); } public boolean isNonLegacyGoogModule() { return moduleType() == ModuleType.GOOG_MODULE; } public boolean isLegacyGoogModule() { return moduleType() == ModuleType.LEGACY_GOOG_MODULE; } public boolean isGoogProvide() { return moduleType() == ModuleType.GOOG_PROVIDE; } public boolean isCommonJs() { return moduleType() == ModuleType.COMMON_JS; } public boolean isNonProvideScript() { return moduleType() == ModuleType.SCRIPT; } /** Whether this is a module (with it's own local scope). */ public boolean isModule() { switch (moduleType()) { case GOOG_PROVIDE: case SCRIPT: return false; case COMMON_JS: case ES6_MODULE: case GOOG_MODULE: case LEGACY_GOOG_MODULE: return true; } throw new AssertionError(moduleType()); } /** * AST node that represents the root of this module. * *

May be null if this is a synthetic piece of metadata - e.g. in a test, or something used * as a fallback. */ public abstract @Nullable Node rootNode(); /** * Whether this file uses Closure Library at all. Note that a file could use Closure Library * even without calling goog.provide/module/require - there are some primitives in base.js that * can be used without being required like goog.isArray. * *

If this is true this indicates the base.js is needed and is not part of this script - it * is an EXTERNAL dependencym otherwise false. If this is also false if Closure Library is part * of this script - e.g. a bundle with base.js. So something could be a "goog.provide'd file", * but not use Closure if the bundle already contains Closure. */ public abstract boolean usesClosure(); /** Whether goog.setTestOnly was called. */ public abstract boolean isTestOnly(); /** * Closure namespaces that this file is associated with. Created by goog.provide, goog.module, * and goog.declareModuleId. * *

This is a multiset as it does not warn on duplicate namespaces, but will still encapsulate * that information with this multiset. */ public abstract ImmutableMultiset googNamespaces(); /** * Closure namespaces this file strongly requires, i.e., arguments to goog.require calls. * *

This is a multiset as it does not warn on duplicate namespaces, but will still encapsulate * that information with this multiset. */ public abstract ImmutableMultiset stronglyRequiredGoogNamespaces(); /** * Closure namespaces this file dynamically require, i.e., arguments to goog.requireDynamic() * calls. * *

This is a multiset as it does not warn on duplicate namespaces, but will still encapsulate * that information with this multiset. */ public abstract ImmutableMultiset dynamicallyRequiredGoogNamespaces(); /** * Closure namespaces this file weakly requires, i.e., arguments to goog.requireType calls. * *

This is a multiset as it does not warn on duplicate namespaces, but will still encapsulate * that information with this multiset. */ public abstract ImmutableMultiset weaklyRequiredGoogNamespaces(); /** Raw text of all ES6 import specifiers (includes "export from" as well). */ public abstract ImmutableMultiset es6ImportSpecifiers(); public abstract ImmutableList nestedModules(); public abstract @Nullable ModulePath path(); public static Builder builder() { return new AutoValue_ModuleMetadataMap_ModuleMetadata.Builder(); } // Use reference equality to prevent bad HashSet performance on GWT. // GatherModuleMetadata is guaranteed to create exactly one ModuleMetadata instance for each // input module. @Override public final boolean equals(Object other) { return super.equals(other); } @Override public final int hashCode() { return super.hashCode(); } /** Builder for {@link ModuleMetadata}. */ @AutoValue.Builder public abstract static class Builder { public abstract ModuleMetadata build(); public abstract ImmutableMultiset.Builder googNamespacesBuilder(); @CanIgnoreReturnValue public Builder addGoogNamespace(String namespace) { googNamespacesBuilder().add(namespace); return this; } public abstract ImmutableMultiset.Builder stronglyRequiredGoogNamespacesBuilder(); public abstract ImmutableMultiset.Builder dynamicallyRequiredGoogNamespacesBuilder(); public abstract ImmutableMultiset.Builder weaklyRequiredGoogNamespacesBuilder(); public abstract ImmutableMultiset.Builder es6ImportSpecifiersBuilder(); public abstract ImmutableList.Builder nestedModulesBuilder(); public abstract Builder path(@Nullable ModulePath value); public abstract Builder usesClosure(boolean value); public abstract Builder isTestOnly(boolean value); public abstract ModuleType moduleType(); public abstract Builder moduleType(ModuleType value); public abstract Builder rootNode(@Nullable Node root); } } /** * @return map from module path to module. These modules represent files and thus {@link * ModuleMetadata#googNamespaces()} contains all Closure namespaces in the file. These are not * the same modules from ModuleMetadataMap#getModulesByGoogNamespace(). It is not valid to * call ModuleRenaming#getGlobalName(ModuleMetadata, String) on {@link * ModuleType#GOOG_PROVIDE} modules from this map that have more than one Closure namespace as * it is ambiguous. */ public ImmutableMap getModulesByPath() { return modulesByPath; } /** * @return map from Closure namespace to module. These modules represent the Closure namespace and * thus {@link ModuleMetadata#googNamespaces()} will have size 1. As a result, it is valid to * call ModuleRenaming#getGlobalName(ModuleMetadata, String) on these modules. These are not * the same modules from {@link ModuleMetadataMap#getModulesByPath()}. */ public ImmutableMap getModulesByGoogNamespace() { return modulesByGoogNamespace; } /** * The set of all modules across both maps. * *

{@code goog.loadModule} calls have no associated path, and non-Closure modules have no * namespaces. */ public ImmutableSet getAllModuleMetadata() { return moduleMetadata; } public static ModuleMetadataMap emptyForTesting() { return new ModuleMetadataMap(ImmutableMap.of(), ImmutableMap.of()); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy