| # Copyright 2017 The Bazel Authors. All rights reserved. |
| # |
| # 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. |
| |
| """Helpers for configuring the TypeScript compiler. |
| """ |
| |
| load(":common/module_mappings.bzl", "get_module_mappings") |
| |
| _DEBUG = False |
| |
| def create_tsconfig(ctx, files, srcs, |
| devmode_manifest=None, tsickle_externs=None, type_blacklisted_declarations=[], |
| out_dir=None, disable_strict_deps=False, allowed_deps=depset(), |
| extra_root_dirs=[], module_path_prefixes=None, module_roots=None): |
| """Creates an object representing the TypeScript configuration to run the compiler under Bazel. |
| |
| Args: |
| ctx: the skylark execution context |
| files: Labels of all TypeScript compiler inputs |
| srcs: Immediate sources being compiled, as opposed to transitive deps. |
| devmode_manifest: path to the manifest file to write for --target=es5 |
| tsickle_externs: path to write tsickle-generated externs.js. |
| type_blacklisted_declarations: types declared in these files will never be |
| mentioned in generated .d.ts. |
| out_dir: directory for generated output. Default is ctx.bin_dir |
| disable_strict_deps: whether to disable the strict deps check |
| allowed_deps: the set of files that code in srcs may depend on (strict deps) |
| extra_root_dirs: Extra root dirs to be passed to tsc_wrapped. |
| module_path_prefixes: additional locations to resolve modules |
| module_roots: standard locations to resolve modules |
| |
| Returns: |
| A nested dict that corresponds to a tsconfig.json structure |
| """ |
| outdir_path = out_dir if out_dir != None else ctx.configuration.bin_dir.path |
| # Callers can choose the filename for the tsconfig, but it must always live |
| # in the output directory corresponding with the label where it's declared. |
| tsconfig_dir = "/".join([p |
| for p in [ |
| ctx.bin_dir.path, |
| ctx.label.workspace_root, |
| ctx.label.package |
| ] + ctx.label.name.split("/")[:-1] |
| # Skip empty path segments (eg. workspace_root when in same repo) |
| if p]) |
| workspace_path = "/".join([".."] * len(tsconfig_dir.split("/"))) |
| if module_path_prefixes == None: |
| module_path_prefixes = [ |
| "", |
| ctx.configuration.genfiles_dir.path + "/", |
| ctx.configuration.bin_dir.path + "/" |
| ] |
| if module_roots == None: |
| base_path_mappings = ["%s/*" % p for p in [ |
| ".", |
| ctx.configuration.genfiles_dir.path, |
| ctx.configuration.bin_dir.path |
| ]] |
| |
| node_modules_mappings = [] |
| if (hasattr(ctx.attr, "node_modules")): |
| node_modules_mappings.append("/".join([p for p in [ |
| ctx.attr.node_modules.label.workspace_root, |
| ctx.attr.node_modules.label.package, |
| "node_modules", |
| "*"] if p])) |
| # TypeScript needs to look up ambient types from a 'node_modules' |
| # directory, but when Bazel manages the dependencies, this directory |
| # isn't in the project so TypeScript won't find it. |
| # We can add it to the path mapping to make this lookup work. |
| # See https://github.com/bazelbuild/rules_typescript/issues/179 |
| node_modules_mappings.append("/".join([p for p in [ |
| ctx.attr.node_modules.label.workspace_root, |
| ctx.attr.node_modules.label.package, |
| "node_modules", |
| "@types", |
| "*"] if p])) |
| |
| module_roots = { |
| "*": node_modules_mappings, |
| ctx.workspace_name + "/*": base_path_mappings, |
| } |
| module_mappings = get_module_mappings(ctx.label, ctx.attr, srcs = srcs) |
| |
| for name, path in module_mappings.items(): |
| # Each module name maps to the immediate path, to resolve "index(.d).ts", |
| # or module mappings that directly point to files (like index.d.ts). |
| module_roots[name] = ["%s%s" % (p, path.replace(".d.ts", "")) for p in module_path_prefixes] |
| if not path.endswith(".d.ts"): |
| # If not just mapping to a single .d.ts file, include a path glob that |
| # maps the entire module root. |
| module_roots["{}/*".format(name)] = ["%s%s/*" % (p, path) for p in module_path_prefixes] |
| |
| # Options for running the TypeScript compiler under Bazel. |
| # See javascript/typescript/compiler/tsc_wrapped.ts:BazelOptions. |
| # Unlike compiler_options, the paths here are relative to the rootDir, |
| # not the location of the tsconfig.json file. |
| bazel_options = { |
| "workspaceName": ctx.workspace_name, |
| "target": str(ctx.label), |
| "package": ctx.label.package, |
| "tsickle": tsickle_externs != None, |
| "tsickleGenerateExterns": getattr(ctx.attr, "generate_externs", True), |
| "tsickleExternsPath": tsickle_externs.path if tsickle_externs else "", |
| "untyped": not getattr(ctx.attr, "tsickle_typed", False), |
| "typeBlackListPaths": [f.path for f in type_blacklisted_declarations], |
| |
| "es5Mode": devmode_manifest != None, |
| "manifest": devmode_manifest if devmode_manifest else "", |
| # Explicitly tell the compiler which sources we're interested in (emitting |
| # and type checking). |
| "compilationTargetSrc": [s.path for s in srcs], |
| "addDtsClutzAliases": getattr(ctx.attr, "add_dts_clutz_aliases", False), |
| "typeCheckDependencies": getattr(ctx.attr, "internal_testing_type_check_dependencies", False), |
| "expectedDiagnostics": getattr(ctx.attr, "expected_diagnostics", []), |
| } |
| |
| if disable_strict_deps: |
| bazel_options["disableStrictDeps"] = disable_strict_deps |
| else: |
| bazel_options["allowedStrictDeps"] = [f.path for f in allowed_deps] |
| |
| if hasattr(ctx.attr, "module_name") and ctx.attr.module_name: |
| bazel_options["moduleName"] = ctx.attr.module_name |
| if hasattr(ctx.attr, "module_root") and ctx.attr.module_root: |
| bazel_options["moduleRoot"] = ctx.attr.module_root |
| |
| if "TYPESCRIPT_WORKER_CACHE_SIZE_MB" in ctx.var: |
| max_cache_size_mb = int(ctx.var["TYPESCRIPT_WORKER_CACHE_SIZE_MB"]) |
| if max_cache_size_mb < 0: |
| fail("TYPESCRIPT_WORKER_CACHE_SIZE_MB set to a negative value (%d)." % max_cache_size_mb) |
| bazel_options["maxCacheSizeMb"] = max_cache_size_mb |
| |
| # Keep these options in sync with those in playground/playground.ts. |
| compiler_options = { |
| # De-sugar to this language level |
| "target": "es5" if devmode_manifest or ctx.attr.runtime == "nodejs" else "es6", |
| # Has no effect in closure/ES2015 mode. Always true just for simplicity. |
| "downlevelIteration": True, |
| |
| # Do not type-check the lib.*.d.ts. |
| # We think this shouldn't be necessary but haven't figured out why yet |
| # and builds are faster with the setting on. |
| # See http://b/30709121 |
| "skipDefaultLibCheck": True, |
| |
| "moduleResolution": "node", |
| |
| "outDir": "/".join([workspace_path, outdir_path]), |
| |
| # We must set a rootDir to avoid TypeScript emit paths varying |
| # due computeCommonSourceDirectory behavior. |
| # TypeScript requires the rootDir be a parent of all sources in |
| # files[], so it must be set to the workspace_path. |
| "rootDir": workspace_path, |
| |
| # Path handling for resolving modules, see specification at |
| # https://github.com/Microsoft/TypeScript/issues/5039 |
| # Paths where we attempt to load relative references. |
| # Longest match wins |
| # |
| # tsc_wrapped also uses this property to strip leading paths |
| # to produce a flattened output tree, see |
| # https://github.com/Microsoft/TypeScript/issues/8245 |
| "rootDirs": ["/".join([workspace_path, e]) for e in extra_root_dirs] + [ |
| workspace_path, |
| "/".join([workspace_path, ctx.configuration.genfiles_dir.path]), |
| "/".join([workspace_path, ctx.configuration.bin_dir.path]), |
| ], |
| |
| # Root for non-relative module names |
| "baseUrl": workspace_path, |
| |
| # "short name" mappings for npm packages, such as "@angular/core" |
| "paths": module_roots, |
| |
| # Inline const enums. |
| "preserveConstEnums": False, |
| |
| # permit `@Decorator` syntax and allow runtime reflection on their types. |
| "experimentalDecorators": True, |
| "emitDecoratorMetadata": True, |
| |
| # Interpret JSX as React calls (until someone asks for something different) |
| "jsx": "react", |
| |
| "noEmitOnError": False, |
| "declaration": True, |
| |
| # We don't support this compiler option (See github #32), so |
| # always emit declaration files in the same location as outDir. |
| "declarationDir": "/".join([workspace_path, outdir_path]), |
| "stripInternal": True, |
| |
| # Embed source maps and sources in .js outputs |
| "inlineSourceMap": True, |
| "inlineSources": True, |
| # Implied by inlineSourceMap: True |
| "sourceMap": False, |
| } |
| |
| if hasattr(ctx.attr, "node_modules"): |
| compiler_options["typeRoots"] = ["/".join([p for p in [ |
| workspace_path, |
| ctx.attr.node_modules.label.workspace_root, |
| ctx.attr.node_modules.label.package, |
| "node_modules", |
| "@types"] if p])] |
| |
| if _DEBUG: |
| compiler_options["traceResolution"] = True |
| compiler_options["diagnostics"] = True |
| |
| return { |
| "compilerOptions": compiler_options, |
| "bazelOptions": bazel_options, |
| "files": [workspace_path + "/" + f.path for f in files], |
| "compileOnSave": False, |
| } |