Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Internal change #1525

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
207 changes: 4 additions & 203 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,207 +1,8 @@
# Tsickle - TypeScript to Closure Translator [![Build Status](https://github.com/angular/tsickle/actions/workflows/node.js.yml/badge.svg)](https://github.com/angular/tsickle/actions/workflows/node.js.yml)
# Tsickle - TypeScript to Closure Translator

Tsickle converts TypeScript code into a form acceptable to the [Closure
Compiler]. This allows using TypeScript to transpile your sources, and then
using Closure Compiler to bundle and optimize them, while taking advantage of
type information in Closure Compiler.
The tsickle repository is unsupported and will not be updated. It has been [officially unsupported](https://github.com/angular/tsickle/commit/3bf8d97c6bd53a920eb4f9b4d18cb584ead48e5a) since November 2022, has not seen any new commits to master since December 2023, and will be frozen starting May 2024. We generally cannot recommend using tsickle to integrate with Closure Compiler outside of the Google ecosystem due to the complexity of the toolchain.

Check notice on line 3 in README.md

View check run for this annotation

In Solidarity / Inclusive Language

Match Found

Please consider an alternative to `master`. Possibilities include: `primary`, `main`, `leader`, `active`, `writer`
Raw output
/master/gi

[closure compiler]: https://github.com/google/closure-compiler/
This repository will remain publicly available to fork.

## What conversion means
Previous version of this README can be found [here](https://github.com/angular/tsickle/blob/3bf8d97c6bd53a920eb4f9b4d18cb584ead48e5a/README.md).

A (non-exhaustive) list of the sorts of transformations Tsickle applies:

- inserts Closure-compatible JSDoc annotations on functions/classes/etc
- converts ES6 modules into `goog.module` modules
- generates externs.js from TypeScript d.ts (and `declare`, see below)
- declares types for class member variables
- translates `export * from ...` into a form Closure accepts
- converts TypeScript enums into a form Closure accepts
- reprocesses all jsdoc to strip Closure-invalid tags

In general the goal is that you write valid TypeScript and Tsickle handles
making it valid Closure Compiler code.

## Warning: unsupported

Google uses tsickle internally to minify its apps (including those using
Angular) using Closure Compiler. We have little experience using tsickle in the
other JavaScript ecosystems that are seen outside of Google, and there is
generally no support for using it from our side.

## Usage

Tsickle is a library, designed to be used by a larger program that interacts
with TypeScript and the Closure compiler.

Some known clients are:

1. Within Google we use tsickle inside the [Bazel build
system](https://bazel.build/). That code is published as
open source as part of [Bazel's nodejs/TypeScript
build rules](https://bazelbuild.github.io/rules_nodejs/).
1. [tscc](https://github.com/theseanl/tscc) wraps tsickle and
closure compiler, and interops with rollup.
1. We publish a simple demo program in the `demo/` subdirectory.

## Design details

### Output format

Tsickle is designed to do whatever is necessary to make the code acceptable by
Closure compiler. We view its output as a necessary intermediate form for
communicating to the Closure compiler, and not something for humans. This means
the tsickle output may be kind of ugly to read. Its only real use is to pass it
on to the compiler.

For one example, the syntax of types tsickle produces are specific to Closure.
The type `{!Foo}` means "Foo, excluding null" and a type alias becomes a `var`
statement that is tagged with `@typedef`.

Tsickle emits modules using Closure's `goog.module` module system. This system
is similar to but different from ES modules, and was supported by Closure before
the ES module system was finalized.

### Differences from TypeScript

Closure and TypeScript are not identical. Tsickle hides most of the
differences, but users must still be aware of some differences.

#### `declare`

Any declaration in a `.d.ts` file, as well as any declaration tagged with
`declare ...`, is intepreted by Tsickle as a name that should be preserved
through Closure compilation (i.e. not renamed into something shorter). Use it
any time the specific string names of your fields are significant. That would
most often happen when the object either coming from outside your program, or
being passed out of the program.

Example:

declare interface JSONResult {
username: string;
}
let r = JSON.parse(input) as JSONResult;
console.log(r.username);

By adding `declare` to the interface (or if it were in a `.d.ts` file), Tsickle
will inform Closure that it must use exactly the field name `.username` (and not
e.g. `.a`) in the output JS. This matters for this example because the input
JSON probably uses the string `'username'` and not whatever name Closure would
invent for it. (Note: `declare` on an interface has no additional meaning in
pure TypeScript.)

#### Exporting decorators

An exporting decorator is a decorator that has `@ExportDecoratedItems` in its
JSDoc.

The names of elements that have an exporting decorator are preserved through
the Closure compilation process by applying an `@export` tag to them.

Example:

/** @ExportDecoratedItems */
function myDecorator() {
// ...
}

@myDecorator()
class DoNotRenameThisClass { ... }

## Development

### Dependencies

- nodejs. Install from your operating system's package manger, by following
instructions on https://nodejs.org/en/, or by using
[NVM](https://github.com/nvm-sh/nvm)
- yarn. Install from your operating system's package manager or by following
[instructions on yarnpkg.com](https://yarnpkg.com/en/docs/install).

### One-time setup

Run `yarn` to install dependencies.

### Build & Test commands

- `yarn build` builds the code base.
- Run `tsc --watch` for an interactive, incremental, and continuous build.
- `yarn lint` checks for lint.
- `yarn test` runs unit tests, e2e tests and checks for lint (but make sure to
`yarn build` first or run tsc!). Set the `TESTBRIDGE_TEST_ONLY` environment
variable to filter what golden tests to run.

### TypeScript AST help

https://astexplorer.net/ and https://ts-ast-viewer.com/ are convenient tools to
visualize and inspect a TypeScript AST.

### Debugging

You can debug tests by passing `--node_options=--inspect` or
`--node_options=--inspect-brk` (to suspend execution directly after startup).

For example, to debug a specific golden test:

```shell
TESTBRIDGE_TEST_ONLY=my_golden_test node --inspect-brk=4332 ./node_modules/.bin/jasmine out/test/*.js
```

Then open [about:inspect] in Chrome and choose "about:inspect". Chrome will
launch a debugging session on any node process that starts with a debugger
listening on one of the listed ports. The tsickle tests and Chrome both default
to `localhost:9229`, so things should work out of the box.

The break in specific code locations you can add `debugger;` statements in the
source code.

### Updating Goldens

Run `UPDATE_GOLDENS=y yarn test` to have the test suite update the goldens in
`test_files/...`.

### Environment variables

Set the environment variable `TESTBRIDGE_TEST_ONLY=<REGEX>` to limit the golden
tests (found in `test_files/...`) to only run tests with a name matching the
regex.

### Releasing

On a new branch, run:

```
# tsickle releases are all minor releases for now, see npm help version.
$ npm version minor
```

This will update the version in `package.json`, commit the changes, and
create a git tag.

Push the branch, open a pull request, get it reviewed, and wait for it to be merged.

Checkout and pull the latest version from master:

```
$ git checkout master && git pull
```

Check if the tag exists. If not, re-tag the commit and push the tag.

```
$ git tag
# Does this show the tag already? If not, proceed with:
$ git tag v0.32.0 && git push origin v0.32.0 # but use correct version
```

Once the versioned tag is pushed to GitHub the release (as found on
https://github.com/angular/tsickle/releases) will be implicitly created.

From the master branch run:

```
npm config set registry https://wombat-dressing-room.appspot.com
npm login
npm publish # runs a clean build & test automatically
```
2 changes: 1 addition & 1 deletion demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"dependencies": {
"minimist": "^1.2.3",
"tsickle": "file:../",
"typescript": "5.2.2"
"typescript": "5.4.2"
},
"devDependencies": {
"@types/minimist": "1.2.0",
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"out/src/*"
],
"peerDependencies": {
"typescript": "~5.1.5"
"typescript": "~5.4.2"
},
"devDependencies": {
"@types/diff-match-patch": "^1.0.32",
Expand All @@ -28,7 +28,7 @@
"source-map-support": "^0.5.19",
"tslib": "^2.2.0",
"tslint": "^6.1.3",
"typescript": "5.2.2"
"typescript": "5.4.2"
},
"scripts": {
"build": "tsc",
Expand Down
5 changes: 4 additions & 1 deletion src/annotator_host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ export interface AnnotatorHost {
* prefix to scope symbols in externs file (see externs.ts).
*/
export function moduleNameAsIdentifier(
host: AnnotatorHost, fileName: string, context = ''): string {
host: AnnotatorHost,
fileName: string,
context = '',
): string {
return host.pathToModuleName(context, fileName).replace(/\./g, '$');
}
15 changes: 11 additions & 4 deletions src/cli_support.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ export function assertAbsolute(fileName: string) {
* import and generates a googmodule module name for the imported module.
*/
export function pathToModuleName(
rootModulePath: string, context: string, fileName: string): string {
rootModulePath: string,
context: string,
fileName: string,
): string {
fileName = fileName.replace(/(\.d)?\.[tj]s$/, '');

if (fileName[0] === '.') {
Expand All @@ -37,7 +40,9 @@ export function pathToModuleName(
// TODO(evanm): various tests assume they can import relative paths like
// 'foo/bar' and have them interpreted as root-relative; preserve that here.
// Fix this by removing the next line.
if (!path.isAbsolute(fileName)) fileName = path.join(rootModulePath, fileName);
if (!path.isAbsolute(fileName)) {
fileName = path.join(rootModulePath, fileName);
}

// TODO(evanm): various tests assume they can pass in a 'fileName' like
// 'goog:foo.bar' and have this function do something reasonable.
Expand All @@ -50,8 +55,10 @@ export function pathToModuleName(
}

// Replace characters not supported by goog.module.
const moduleName =
fileName.replace(/\/|\\/g, '.').replace(/^[^a-zA-Z_$]/, '_').replace(/[^a-zA-Z0-9._$]/g, '_');
const moduleName = fileName
.replace(/\/|\\/g, '.')
.replace(/^[^a-zA-Z_$]/, '_')
.replace(/[^a-zA-Z0-9._$]/g, '_');

return moduleName;
}
4 changes: 2 additions & 2 deletions src/closure_externs.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ var ReadonlySet;
* @template T
* @extends {IThenable<T>}
*/
function PromiseLike() {};
function PromiseLike() {}

/** @typedef {function(new:Promise)} */
var PromiseConstructor;
Expand Down Expand Up @@ -94,7 +94,7 @@ var TemplateStringsArray;
var RegExpMatchArray;

/** @record */
function ImportMeta() {};
function ImportMeta() {}

// Representations for TS' EventMap objects.
// These are types that contain a mapping from event names to event object
Expand Down
Loading
Loading