Skip to content

Commit

Permalink
fix: allow composite projects
Browse files Browse the repository at this point in the history
We need to propagate the tsconfig.json file to composite project dependents as an input.
This was in rules_nodejs but we lost it.

Also correct the pathing for the tsbuildinfo output, which hadn't been tested in rules_ts yet.

Copied the three composite projects from rules_nodejs tests, so we have that coverage now.

Fixes #163
  • Loading branch information
alexeagle committed Oct 12, 2022
1 parent f6c0dc9 commit 08e4467
Show file tree
Hide file tree
Showing 21 changed files with 239 additions and 0 deletions.
7 changes: 7 additions & 0 deletions WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,10 @@ npm_translate_lock(
load("@npm//:repositories.bzl", "npm_repositories")

npm_repositories()

load("@aspect_rules_jasmine//jasmine:repositories.bzl", "LATEST_VERSION", "rules_jasmine_repositories")

rules_jasmine_repositories(
name = "jasmine",
jasmine_version = LATEST_VERSION,
)
3 changes: 3 additions & 0 deletions examples/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
"dependencies": {
"@babel/cli": "7.18.9",
"@babel/core": "7.18.9",
"@babel/parser": "7.18.9",
"@babel/preset-typescript": "7.18.6",
"@babel/types": "7.18.6",
"@types/jasmine": "*",
"@types/node": "18.6.1",
"date-fns": "2.29.1",
"typescript": "4.8.2"
Expand Down
18 changes: 18 additions & 0 deletions examples/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions examples/project_references/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
load("@aspect_bazel_lib//lib:copy_to_bin.bzl", "copy_to_bin")
load("@aspect_rules_ts//ts:defs.bzl", "ts_config")

package(default_visibility = ["//examples/project_references:__subpackages__"])

ts_config(
name = "tsconfig",
src = "tsconfig-base.json",
)

copy_to_bin(
name = "tsconfig-base",
srcs = ["tsconfig-base.json"],
)
29 changes: 29 additions & 0 deletions examples/project_references/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# TypeScript Project References

TypeScript has its own feature for breaking up a large TS program into multiple compilation units:

> Project references are a new feature in TypeScript 3.0 that allow you to structure your TypeScript programs into smaller pieces.
> By doing this, you can greatly improve build times, enforce logical separation between components, and organize your code in new and better ways.
> We’re also introducing a new mode for tsc, the --build flag, that works hand in hand with project references to enable faster TypeScript builds.
See documentation: https://www.typescriptlang.org/docs/handbook/project-references.html

This works with rules_ts, as this example demonstrates.
However, Project References don't provide any benefit in Bazel, since we already knew how to compile
projects independently and have them reference each other.
The reason you'd set it up this way is to allow `tsc --build` to work outside of Bazel, allowing
both legacy and Bazel workflows to coexist in the same codebase.

## Known issues

TypeScript writes a .tsbuildinfo output file for composite projects. This is intended to make a
subsequent compilation of that project faster, by loading that file and reusing information from
a previous execution of `tsc` to enable incremental builds.
Under Bazel, the `.tsbuildinfo` file is produced, but the result is ignored, because Bazel does not
permit the output of an action to be reused as an input to a subsequent spawn of the same action,
as that would be non-hermetic.

## Dependency graph

`app` depends on `lib_b` which depends on `lib_a`. Each one is marked with `composite = True` so
TypeScript knows to look for the referenced project by resolving to the other `tsconfig.json` file.
10 changes: 10 additions & 0 deletions examples/project_references/app/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
load("@aspect_rules_ts//ts:defs.bzl", "ts_project")

ts_project(
name = "compile",
composite = True,
declaration = True,
extends = "//examples/project_references:tsconfig-base",
tsconfig = "tsconfig.json",
deps = ["//examples/project_references/lib_b"],
)
11 changes: 11 additions & 0 deletions examples/project_references/app/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// This import ought to be disallowed by a "strict dependencies" feature.
// We didn't tell TypeScript that app depends on lib_a, so the import should not resolve.
// In theory, we could make a custom compiler just for Bazel (like ts_library from google3)
// to implement a strict dependencies feature, but the rules_ts philosophy is to be a thin starlark
// wrapper around TypeScript's tsc.
// See https://github.com/microsoft/TypeScript/issues/36743
import { a } from '../lib_a'
import { sayHello } from '../lib_b'

sayHello('world')
console.error(a)
4 changes: 4 additions & 0 deletions examples/project_references/app/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "../tsconfig-base.json",
"references": [{ "path": "../lib_b/tsconfig.json" }]
}
27 changes: 27 additions & 0 deletions examples/project_references/lib_a/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
load("@aspect_rules_ts//ts:defs.bzl", "ts_config", "ts_project")

package(default_visibility = ["//examples/project_references:__subpackages__"])

ts_config(
name = "config",
src = "tsconfig.json",
deps = [
"tsconfig-extended.json",
"//examples/project_references:tsconfig",
],
)

ts_project(
name = "lib_a",
composite = True,
tsconfig = "config",
# Intentionally not syncing this option from tsconfig, to test validator suppression
# source_map = True,
validate = False,
# Use @babel/parser since the package.json is required to resolve "typings" field
# Repro of #2044
deps = [
"//examples:node_modules/@babel/parser",
"//examples:node_modules/@babel/types",
],
)
3 changes: 3 additions & 0 deletions examples/project_references/lib_a/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export declare const a: string
import { DecoratorsPluginOptions } from '@babel/parser'
export declare const o: DecoratorsPluginOptions
5 changes: 5 additions & 0 deletions examples/project_references/lib_a/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const a: string = 'hello'

// Repro rules_nodejs#2044
import { DecoratorsPluginOptions } from '@babel/parser'
export const o: DecoratorsPluginOptions = {}
4 changes: 4 additions & 0 deletions examples/project_references/lib_a/tsconfig-extended.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// This file is an extra hop of indirection, regression test for rules_nodejs#1754
{
"extends": "../tsconfig-base"
}
6 changes: 6 additions & 0 deletions examples/project_references/lib_a/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"extends": "./tsconfig-extended.json",
"compilerOptions": {
"sourceMap": true
}
}
40 changes: 40 additions & 0 deletions examples/project_references/lib_b/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
load("@aspect_rules_jasmine//jasmine:defs.bzl", "jasmine_test")
load("@aspect_rules_ts//ts:defs.bzl", "ts_project")

package(default_visibility = ["//examples/project_references:__subpackages__"])

ts_project(
name = "lib_b",
srcs = ["index.ts"],
# just a test for the pass-through args attribute
args = ["--emitBOM"],
composite = True,
declaration = True,
extends = "//examples/project_references:tsconfig-base",
deps = ["//examples/project_references/lib_a"],
)

ts_project(
name = "transpile_test",
testonly = True,
srcs = [":index.spec.ts"],
composite = True,
declaration = True,
extends = "//examples/project_references:tsconfig-base",
tsconfig = "tsconfig-test.json",
deps = [
":lib_b",
"//examples:node_modules/@types/jasmine",
"//examples:node_modules/@types/node",
],
)

jasmine_test(
name = "test",
args = ["*.spec.js"],
chdir = package_name(),
data = [
"index.spec.js",
":lib_b",
],
)
15 changes: 15 additions & 0 deletions examples/project_references/lib_b/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { sayHello } from './'

describe('b', () => {
it('should say hello', () => {
let captured: string = ''
console.log = (s: string) => (captured = s)
sayHello(' world')
expect(captured).toBe('hello world')
})
it('should include byte-order mark since that was passed in args attr', () => {
expect(
require('fs').readFileSync(require.resolve('./index'), 'utf-8')[0]
).toBe('\ufeff')
})
})
5 changes: 5 additions & 0 deletions examples/project_references/lib_b/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { a } from '../lib_a'

export function sayHello(f: string) {
console.log(a + f)
}
8 changes: 8 additions & 0 deletions examples/project_references/lib_b/tsconfig-test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"extends": "../tsconfig-base.json",
"compilerOptions": {
"types": ["jasmine", "node"]
},
"references": [{ "path": "./" }],
"include": ["*.spec.ts"]
}
5 changes: 5 additions & 0 deletions examples/project_references/lib_b/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": "../tsconfig-base.json",
"references": [{ "path": "../lib_a" }],
"exclude": ["*.spec.ts"]
}
10 changes: 10 additions & 0 deletions examples/project_references/tsconfig-base.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"compilerOptions": {
"strict": true,
"declaration": true,
"lib": ["ES2015", "ES2016.Array.Include", "DOM"],
"module": "commonjs",
"target": "ES2015",
"composite": true
}
}
8 changes: 8 additions & 0 deletions internal_deps.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,11 @@ def rules_ts_internal_deps():
"https://github.com/bazelbuild/stardoc/releases/download/0.5.2/stardoc-0.5.2.tar.gz",
],
)

maybe(
http_archive,
name = "aspect_rules_jasmine",
sha256 = "938a2818100fd89e7600a45b7ba4fcd4114c11c5b5741db30ff7c6e0dcb2ea4b",
strip_prefix = "rules_jasmine-0.1.0",
url = "https://github.com/aspect-build/rules_jasmine/archive/refs/tags/v0.1.0.tar.gz",
)
7 changes: 7 additions & 0 deletions ts/private/ts_project.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,13 @@ def _ts_project_impl(ctx):

inputs = srcs_inputs[:]
for dep in ctx.attr.deps:
# When TypeScript builds a composite project, our compilation will want to read the tsconfig.json of
# composite projects we reference.
# Otherwise we'd get an error like
# examples/project_references/lib_b/tsconfig.json(5,9): error TS6053:
# File 'execroot/aspect_rules_ts/bazel-out/k8-fastbuild/bin/examples/project_references/lib_a/tsconfig.json' not found.
if ctx.attr.composite and TsConfigInfo in dep:
inputs.extend(dep[TsConfigInfo].deps.to_list())
if ValidOptionsInfo in dep:
inputs.append(dep[ValidOptionsInfo].marker)

Expand Down

0 comments on commit 08e4467

Please sign in to comment.