diff --git a/examples/BUILD.bazel b/examples/BUILD.bazel index 1b31481ce7..b5228ea44e 100644 --- a/examples/BUILD.bazel +++ b/examples/BUILD.bazel @@ -64,6 +64,9 @@ example_integration_test( name = "examples_react_webpack", # TODO: add some tests in the example bazel_commands = ["build ..."], + npm_packages = { + "//packages/typescript:npm_package": "@bazel/typescript", + }, # TODO(alexeagle): somehow this is broken by the new node-patches based node_patches script # ERROR: D:/temp/tmp-6900sejcsrcttpdb/BUILD.bazel:37:1: output 'app.bundle.js' was not created tags = ["no-bazelci-windows"], diff --git a/examples/react_webpack/BUILD.bazel b/examples/react_webpack/BUILD.bazel index ffeb3a3891..86bb368625 100644 --- a/examples/react_webpack/BUILD.bazel +++ b/examples/react_webpack/BUILD.bazel @@ -1,7 +1,7 @@ load("@npm//http-server:index.bzl", "http_server") load("@npm//sass:index.bzl", "sass") -load("@npm//typescript:index.bzl", "tsc") load("@npm//webpack-cli:index.bzl", webpack = "webpack_cli") +load("@npm_bazel_typescript//:index.bzl", "ts_project") sass( name = "styles", @@ -13,27 +13,29 @@ sass( data = ["styles.scss"], ) -tsc( - name = "compile", - outs = ["index.js"], - args = [ - "$(execpath index.tsx)", - "$(execpath types.d.ts)", - "--outDir", - "$(RULEDIR)", - "--lib", - "es2015,dom", - "--jsx", - "react", - ], - data = [ +ts_project( + name = "tsconfig", + srcs = [ "index.tsx", "types.d.ts", + ], + # The tsconfig.json file doesn't enable "declaration" or "composite" + # so tsc won't produce any .d.ts outputs. We need to tell Bazel not + # to expect those outputs being produced. + declaration = False, + deps = [ "@npm//@types", "@npm//csstype", ], ) +# If you don't want to write //:tsconfig as the label for the TypeScript compilation, +# use an alias like this, so you can write //:compile_ts instead. +alias( + name = "compile_ts", + actual = "tsconfig", +) + webpack( name = "bundle", outs = ["app.bundle.js"], diff --git a/examples/react_webpack/WORKSPACE b/examples/react_webpack/WORKSPACE index 0022821781..a9086fba54 100644 --- a/examples/react_webpack/WORKSPACE +++ b/examples/react_webpack/WORKSPACE @@ -19,3 +19,7 @@ yarn_install( package_json = "//:package.json", yarn_lock = "//:yarn.lock", ) + +load("@npm//:install_bazel_dependencies.bzl", "install_bazel_dependencies") + +install_bazel_dependencies() diff --git a/examples/react_webpack/package.json b/examples/react_webpack/package.json index 97710aace2..add1df42c7 100644 --- a/examples/react_webpack/package.json +++ b/examples/react_webpack/package.json @@ -4,6 +4,7 @@ "@bazel/bazelisk": "^1.3.0", "@bazel/buildifier": "^0.29.0", "@bazel/ibazel": "^0.12.2", + "@bazel/typescript": "^1.4.1", "@types/react": "^16.9.5", "@types/react-dom": "^16.9.1", "css-loader": "^3.2.0", diff --git a/examples/react_webpack/tsconfig.json b/examples/react_webpack/tsconfig.json new file mode 100644 index 0000000000..0ece51d45f --- /dev/null +++ b/examples/react_webpack/tsconfig.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "jsx": "react", + "lib": ["ES2015", "DOM"] + } +} \ No newline at end of file diff --git a/packages/typescript/src/index.bzl b/packages/typescript/src/index.bzl index d1b620ee73..e45c0c5cb0 100644 --- a/packages/typescript/src/index.bzl +++ b/packages/typescript/src/index.bzl @@ -19,6 +19,7 @@ Users should not load files under "/internal" load("//internal:build_defs.bzl", _ts_library = "ts_library_macro") load("//internal:ts_config.bzl", _ts_config = "ts_config") +load("//internal:ts_project.bzl", _ts_project = "ts_project_macro") load("//internal:ts_repositories.bzl", _ts_setup_workspace = "ts_setup_workspace") load("//internal/devserver:ts_devserver.bzl", _ts_devserver = "ts_devserver_macro") @@ -26,4 +27,5 @@ ts_setup_workspace = _ts_setup_workspace ts_library = _ts_library ts_config = _ts_config ts_devserver = _ts_devserver +ts_project = _ts_project # If adding rules here also add to index.docs.bzl diff --git a/packages/typescript/src/index.docs.bzl b/packages/typescript/src/index.docs.bzl index a44d119049..2ec770c3d7 100644 --- a/packages/typescript/src/index.docs.bzl +++ b/packages/typescript/src/index.docs.bzl @@ -20,12 +20,14 @@ So this is a copy of index.bzl with macro indirection removed. load("//internal:build_defs.bzl", _ts_library = "ts_library") load("//internal:ts_config.bzl", _ts_config = "ts_config") +load("//internal:ts_project.bzl", _ts_project = "ts_project_macro") load("//internal:ts_repositories.bzl", _ts_setup_workspace = "ts_setup_workspace") load("//internal/devserver:ts_devserver.bzl", _ts_devserver = "ts_devserver") ts_setup_workspace = _ts_setup_workspace ts_library = _ts_library ts_config = _ts_config +ts_project = _ts_project ts_devserver = _ts_devserver # DO NOT ADD MORE rules here unless they appear in the generated docsite. # Run yarn stardoc to re-generate the docsite. diff --git a/packages/typescript/src/internal/BUILD.bazel b/packages/typescript/src/internal/BUILD.bazel index daf761f170..839caa36a6 100644 --- a/packages/typescript/src/internal/BUILD.bazel +++ b/packages/typescript/src/internal/BUILD.bazel @@ -47,6 +47,7 @@ filegroup( srcs = [ "build_defs.bzl", "ts_config.bzl", + "ts_project.bzl", "ts_repositories.bzl", "//internal/devserver:package_contents", ], diff --git a/packages/typescript/src/internal/ts_project.bzl b/packages/typescript/src/internal/ts_project.bzl new file mode 100644 index 0000000000..567ccd6c32 --- /dev/null +++ b/packages/typescript/src/internal/ts_project.bzl @@ -0,0 +1,231 @@ +"ts_project rule" + +load("@build_bazel_rules_nodejs//:providers.bzl", "DeclarationInfo", "NpmPackageInfo", "run_node") + +_ATTRS = { + # NB: no restriction on extensions here, because tsc sometimes adds type-check support + # for more file kinds (like require('some.json')) and also + # if you swap out the `compiler` attribute (like with ngtsc) + # that compiler might allow more sources than tsc does. + "srcs": attr.label_list(allow_files = True, mandatory = True), + "extends": attr.label_list(allow_files = [".json"]), + "tsc": attr.label(default = Label("@npm//typescript/bin:tsc"), executable = True, cfg = "host"), + "tsconfig": attr.label(mandatory = True, allow_single_file = [".json"]), + "deps": attr.label_list(providers = [DeclarationInfo]), +} + +# tsc knows how to produce the following kinds of output files. +# NB: the macro `ts_project_macro` will set these outputs based on user +# telling us which settings are enabled in the tsconfig for this project. +_OUTPUTS = { + "js_outs": attr.output_list(), + "map_outs": attr.output_list(), + "typing_maps_outs": attr.output_list(), + "typings_outs": attr.output_list(), +} + +_TsConfigInfo = provider( + doc = """Passes tsconfig.json files to downstream compilations so that TypeScript can read them. + This is needed to support Project References""", + fields = { + "tsconfigs": "depset of tsconfig.json files", + }, +) + +def _ts_project_impl(ctx): + arguments = ctx.actions.args() + arguments.add_all([ + "-p", + ctx.file.tsconfig.short_path, + "--outDir", + "/".join([ctx.bin_dir.path, ctx.label.package]), + ]) + if len(ctx.outputs.typings_outs) > 0: + arguments.add_all([ + "--declarationDir", + "/".join([ctx.bin_dir.path, ctx.label.package]), + ]) + + # When users report problems, we can ask them to re-build with + # --define=VERBOSE_LOGS=1 + # so anything that's useful to diagnose rule failures belongs here + if "VERBOSE_LOGS" in ctx.var.keys(): + arguments.add_all([ + # What files were in the ts.Program + "--listFiles", + # Did tsc write all outputs to the place we expect to find them? + "--listEmittedFiles", + # Why did module resolution fail? + "--traceResolution", + # Why was the build slow? + "--diagnostics", + "--extendedDiagnostics", + # Prefer to show gory details rather than save space in console + "--noErrorTruncation", + ]) + + deps_depsets = [] + for dep in ctx.attr.deps: + if _TsConfigInfo in dep: + deps_depsets.append(dep[_TsConfigInfo].tsconfigs) + if NpmPackageInfo in dep: + # TODO: we could maybe filter these to be tsconfig.json or *.d.ts only + # we don't expect tsc wants to read any other files from npm packages. + deps_depsets.append(dep[NpmPackageInfo].sources) + elif DeclarationInfo in dep: + deps_depsets.append(dep[DeclarationInfo].transitive_declarations) + + inputs = ctx.files.srcs + depset(transitive = deps_depsets).to_list() + [ctx.file.tsconfig] + if ctx.attr.extends: + inputs.extend(ctx.files.extends) + outputs = ctx.outputs.js_outs + ctx.outputs.map_outs + ctx.outputs.typings_outs + ctx.outputs.typing_maps_outs + + if len(outputs) == 0: + return [] + + run_node( + ctx, + inputs = inputs, + arguments = [arguments], + outputs = outputs, + executable = "tsc", + progress_message = "Compiling TypeScript project %s" % ctx.file.tsconfig.short_path, + ) + + runtime_files = depset(ctx.outputs.js_outs + ctx.outputs.map_outs) + typings_files = ctx.outputs.typings_outs + [s for s in ctx.files.srcs if s.path.endswith(".d.ts")] + + return [ + DeclarationInfo( + declarations = depset(typings_files), + transitive_declarations = depset(typings_files, transitive = [ + dep[DeclarationInfo].transitive_declarations + for dep in ctx.attr.deps + ]), + ), + DefaultInfo( + files = runtime_files, + runfiles = ctx.runfiles( + transitive_files = runtime_files, + collect_default = True, + ), + ), + _TsConfigInfo(tsconfigs = depset([ctx.file.tsconfig], transitive = [ + dep[_TsConfigInfo].tsconfigs + for dep in ctx.attr.deps + if _TsConfigInfo in dep + ])), + ] + +ts_project = rule( + implementation = _ts_project_impl, + attrs = dict(_ATTRS, **_OUTPUTS), +) + +def _out_paths(srcs, ext): + return [f[:f.rindex(".")] + ext for f in srcs if not f.endswith(".d.ts")] + +def ts_project_macro( + name = "tsconfig", + srcs = None, + deps = [], + extends = None, + declaration = True, + source_map = False, + declaration_map = False, + emit_declaration_only = False, + **kwargs): + """Compiles one TypeScript project using `tsc -p` + + Unlike `ts_library`, this rule is the thinnest possible layer of Bazel awareness on top + of the TypeScript compiler. It shifts the burden of configuring TypeScript into the tsconfig.json file. + TODO(alexeagle): update https://github.com/bazelbuild/rules_nodejs/blob/master/docs/TypeScript.md#alternatives + # to describe the trade-offs between the two rules. + + Any code that works with `tsc` should work with `ts_project` with a few caveats: + + - Bazel requires that the `outDir` (and `declarationDir`) be set to + `bazel-out/[arch]/bin/path/to/package` + so we override whatever settings appear in your tsconfig. + - Bazel expects that each output is produced by a single rule. + Thus if you have two `ts_project` rules with overlapping sources (the same .ts file + appears in more than one) then you get an error if you try to build both together. + Worse, if you build them separately then the output directory will contain whichever + one you happened to build most recently. This is highly discouraged. + + > Note, in order for TypeScript to find referenced projects in the bazel-out folder, + > we recommend that the base tsconfig contain a rootDirs section that includes all + > possible locations they may appear. + > + > We hope this will not be needed in some future release of TypeScript. + > Follow https://github.com/microsoft/TypeScript/issues/37257 for more info. + > + > For example, if the base tsconfig file relative to the workspace root is + > `path/to/tsconfig.json` then you should configure like: + > + > ``` + > "compilerOptions": { + > "rootDirs": [ + > ".", + > "../../bazel-out/darwin-fastbuild/bin/path/to", + > "../../bazel-out/k8-fastbuild/bin/path/to", + > "../../bazel-out/x64_windows-fastbuild/bin/path/to", + > "../../bazel-out/darwin-dbg/bin/path/to", + > "../../bazel-out/k8-dbg/bin/path/to", + > "../../bazel-out/x64_windows-dbg/bin/path/to", + > ] + > } + > ``` + + This rule requires that the name match the tsconfig file. + The reason is that it makes BUILD file generation (autoconfig) much simpler. + If you want to use a different label, you can use a Bazel `alias` rule to give it an additional name. + + If your dependencies are not installed into a workspace called `npm`, or if you want to + use a custom TypeScript compiler binary, you can pass `tsc = "@some_wksp//path/to:tsc_bin"` + to this rule to override the compiler. + + Args: + name: The basename (no `.json` extension) of the tsconfig file that should be compiled. + srcs: List of labels of TypeScript source files to be provided to the compiler. + + If absent, defaults to `**/*.ts` (all TypeScript files in the package). + deps: List of labels of other rules that produce TypeScript typings (.d.ts files) + extends: List of labels of tsconfig file(s) referenced in `extends` section of tsconfig. + + Must include any tsconfig files "chained" by extends clauses. + declaration: if the `declaration` or `composite` bit are set in the tsconfig. + Instructs Bazel to expect a `.d.ts` output for each `.ts` source. + source_map: if the `sourceMap` bit is set in the tsconfig. + Instructs Bazel to expect a `.js.map` output for each `.ts` source. + declaration_map: if the `declarationMap` bit is set in the tsconfig. + Instructs Bazel to expect a `.d.ts.map` output for each `.ts` source. + emit_declaration_only: if the `emitDeclarationOnly` bit is set in the tsconfig. + Instructs Bazel *not* to expect `.js` outputs for `.ts` sources. + """ + + if srcs == None: + srcs = native.glob(["**/*.ts"]) + + # Bazel's `name` property is arbitrarily controlled by the user. + # But here we constrain it to match the tsconfig file. + # The reason for doing this is so that BUILD file generation can be trivial. + # If you know the path of a referenced tsconfig file, you know what label to use + # to include it in the deps of another rule. + # If we allowed users to control it, then BUILD file generation would need + # heuristic semantics to do bazel query and try to figure out which label to use + # to satisfy a project references dependency. + tsconfig = name + ".json" + + ts_project( + name = name, + srcs = srcs, + deps = deps, + tsconfig = tsconfig, + extends = extends, + js_outs = _out_paths(srcs, ".js") if not emit_declaration_only else [], + map_outs = _out_paths(srcs, ".js.map") if source_map and not emit_declaration_only else [], + typings_outs = _out_paths(srcs, ".d.ts") if declaration else [], + typing_maps_outs = _out_paths(srcs, ".d.ts.map") if declaration_map else [], + **kwargs + ) diff --git a/packages/typescript/test/ts_project/BUILD b/packages/typescript/test/ts_project/BUILD new file mode 100644 index 0000000000..96b6172fd3 --- /dev/null +++ b/packages/typescript/test/ts_project/BUILD @@ -0,0 +1 @@ +exports_files(["tsconfig-base.json"]) diff --git a/packages/typescript/test/ts_project/a/BUILD.bazel b/packages/typescript/test/ts_project/a/BUILD.bazel new file mode 100644 index 0000000000..0b122a74ae --- /dev/null +++ b/packages/typescript/test/ts_project/a/BUILD.bazel @@ -0,0 +1,9 @@ +load("@npm_bazel_typescript//:index.bzl", "ts_project") + +ts_project( + name = "tsconfig", # This will use ./tsconfig.json + srcs = [":a.ts"], + extends = ["//packages/typescript/test/ts_project:tsconfig-base.json"], + source_map = True, + visibility = ["//packages/typescript/test:__subpackages__"], +) diff --git a/packages/typescript/test/ts_project/a/a.ts b/packages/typescript/test/ts_project/a/a.ts new file mode 100644 index 0000000000..a668b7e336 --- /dev/null +++ b/packages/typescript/test/ts_project/a/a.ts @@ -0,0 +1 @@ +export const a: string = 'hello'; diff --git a/packages/typescript/test/ts_project/a/tsconfig.json b/packages/typescript/test/ts_project/a/tsconfig.json new file mode 100644 index 0000000000..8fa4bcb5e0 --- /dev/null +++ b/packages/typescript/test/ts_project/a/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../tsconfig-base.json", + "compilerOptions": { + "sourceMap": true, + // WORKAROUND https://github.com/microsoft/TypeScript/issues/37378 + // When running the action with standalone strategy (no sandbox), + // tsc will see the .ts source files + // from our dependencies. It then tries to do an up-to-date check on the outputs. + // We are forced to have outDir in the tsconfig in this case (on windows) + // so that tsc can know where to look for the .d.ts outputs + // Error looks like + // (05:54:49) ERROR: C:/b/bk-windows-b4qr/bazel/rules-nodejs-nodejs/packages/typescript/test/ts_project/b/BUILD.bazel:6:1: Compiling TypeScript project packages/typescript/test/ts_project/b/tsconfig.json failed (Exit 2): tsc.bat failed: error executing command + // cd C:/b/uuxnwop3/execroot/build_bazel_rules_nodejs + // SET COMPILATION_MODE=fastbuild + // bazel-out/host/bin/external/npm/typescript/bin/tsc.bat -p packages/typescript/test/ts_project/b/tsconfig.json --outDir bazel-out/x64_windows-fastbuild/bin/packages/typescript/test/ts_project/b --declarationDir bazel-out/x64_windows-fastbuild/bin/packages/typescript/test/ts_project/b --bazel_node_modules_manifest=bazel-out/x64_windows-fastbuild/bin/packages/typescript/test/ts_project/b/_tsconfig.module_mappings.json --nobazel_patch_module_resolver + // Execution platform: @local_config_platform//:host + // packages/typescript/test/ts_project/b/b.ts(1,17): error TS6305: Output file 'C:/b/uuxnwop3/execroot/build_bazel_rules_nodejs/packages/typescript/test/ts_project/a/a.d.ts' has not been built from source file 'C:/b/uuxnwop3/execroot/build_bazel_rules_nodejs/packages/typescript/test/ts_project/a/a.ts'. + "outDir": "../../../../../bazel-out/x64_windows-fastbuild/bin/packages/typescript/test/ts_project/a", + } +} \ No newline at end of file diff --git a/packages/typescript/test/ts_project/b/BUILD.bazel b/packages/typescript/test/ts_project/b/BUILD.bazel new file mode 100644 index 0000000000..c32f3e286a --- /dev/null +++ b/packages/typescript/test/ts_project/b/BUILD.bazel @@ -0,0 +1,28 @@ +load("@npm_bazel_jasmine//:index.from_src.bzl", "jasmine_node_test") +load("@npm_bazel_typescript//:index.bzl", "ts_project") + +package(default_visibility = ["//packages/typescript/test:__subpackages__"]) + +ts_project( + name = "tsconfig", # This will use ./tsconfig.json + srcs = [":b.ts"], + extends = ["//packages/typescript/test/ts_project:tsconfig-base.json"], + deps = ["//packages/typescript/test/ts_project/a:tsconfig"], +) + +ts_project( + name = "tsconfig-test", # This will use ./tsconfig-test.json + testonly = True, + srcs = [":b.spec.ts"], + extends = ["//packages/typescript/test/ts_project:tsconfig-base.json"], + deps = [ + ":tsconfig", + "@npm//@types/jasmine", + ], +) + +jasmine_node_test( + name = "test", + srcs = ["b.spec.js"], + data = [":tsconfig"], +) diff --git a/packages/typescript/test/ts_project/b/b.spec.ts b/packages/typescript/test/ts_project/b/b.spec.ts new file mode 100644 index 0000000000..e72c0a1c3d --- /dev/null +++ b/packages/typescript/test/ts_project/b/b.spec.ts @@ -0,0 +1,10 @@ +import {sayHello} from './b'; + +describe('b', () => { + it('should say hello', () => { + let captured: string = ''; + console.log = (s: string) => captured = s; + sayHello(' world'); + expect(captured).toBe('hello world'); + }); +}); diff --git a/packages/typescript/test/ts_project/b/b.ts b/packages/typescript/test/ts_project/b/b.ts new file mode 100644 index 0000000000..1879b29fa3 --- /dev/null +++ b/packages/typescript/test/ts_project/b/b.ts @@ -0,0 +1,5 @@ +import {a} from '../a/a'; + +export function sayHello(f: string) { + console.log(a + f); +} diff --git a/packages/typescript/test/ts_project/b/tsconfig-test.json b/packages/typescript/test/ts_project/b/tsconfig-test.json new file mode 100644 index 0000000000..288ba2408c --- /dev/null +++ b/packages/typescript/test/ts_project/b/tsconfig-test.json @@ -0,0 +1,7 @@ +{ + "extends": "../tsconfig-base.json", + "references": [ + {"path": "./"} + ], + "include": ["*.spec.ts"] +} diff --git a/packages/typescript/test/ts_project/b/tsconfig.json b/packages/typescript/test/ts_project/b/tsconfig.json new file mode 100644 index 0000000000..4746a3c94f --- /dev/null +++ b/packages/typescript/test/ts_project/b/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../tsconfig-base.json", + "references": [ + {"path": "../a"} + ], + "compilerOptions": { + // WORKAROUND - See comment in ../a/tsconfig.json + "outDir": "../../../../../bazel-out/x64_windows-fastbuild/bin/packages/typescript/test/ts_project/b" + }, + "exclude": ["*.spec.ts"] +} diff --git a/packages/typescript/test/ts_project/c/BUILD.bazel b/packages/typescript/test/ts_project/c/BUILD.bazel new file mode 100644 index 0000000000..29d38649f1 --- /dev/null +++ b/packages/typescript/test/ts_project/c/BUILD.bazel @@ -0,0 +1,8 @@ +load("@npm_bazel_typescript//:index.bzl", "ts_project") + +ts_project( + name = "tsconfig", # This will use ./tsconfig.json + srcs = [":c.ts"], + extends = ["//packages/typescript/test/ts_project:tsconfig-base.json"], + deps = ["//packages/typescript/test/ts_project/b:tsconfig"], +) diff --git a/packages/typescript/test/ts_project/c/c.ts b/packages/typescript/test/ts_project/c/c.ts new file mode 100644 index 0000000000..5c4b70280b --- /dev/null +++ b/packages/typescript/test/ts_project/c/c.ts @@ -0,0 +1,5 @@ +import {a} from '../a/a'; // SHOULD FAIL HERE per https://github.com/microsoft/TypeScript/issues/36743 +import {sayHello} from '../b/b'; + +sayHello('world'); +console.error(a); diff --git a/packages/typescript/test/ts_project/c/tsconfig.json b/packages/typescript/test/ts_project/c/tsconfig.json new file mode 100644 index 0000000000..0c7bf6c73c --- /dev/null +++ b/packages/typescript/test/ts_project/c/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../tsconfig-base.json", + "references": [ + {"path": "../b/tsconfig.json"} + ] +} \ No newline at end of file diff --git a/packages/typescript/test/ts_project/tsconfig-base.json b/packages/typescript/test/ts_project/tsconfig-base.json new file mode 100644 index 0000000000..11a249c71d --- /dev/null +++ b/packages/typescript/test/ts_project/tsconfig-base.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "strict": true, + "lib": [ + "ES2015", + "ES2016.Array.Include", + "DOM" + ], + "module": "commonjs", + "target": "ES2015", + "composite": true, + "rootDirs": [ + ".", + "../../../../bazel-out/darwin-fastbuild/bin/packages/typescript/test/ts_project", + "../../../../bazel-out/k8-fastbuild/bin/packages/typescript/test/ts_project", + "../../../../bazel-out/x64_windows-fastbuild/bin/packages/typescript/test/ts_project", + "../../../../bazel-out/darwin-dbg/bin/packages/typescript/test/ts_project", + "../../../../bazel-out/k8-dbg/bin/packages/typescript/test/ts_project", + "../../../../bazel-out/x64_windows-dbg/bin/packages/typescript/test/ts_project", + ], + } +} \ No newline at end of file