Repository: tradingview/lightweight-charts
Pull request #436 (2020-05-15 12:27:06)

Introduced ts-transformer-properties-rename instead of ts-transformer-minify-privates
It allows to minify everything aren't exported to the public space, so it reduces bundle size a lot (by several KB which is several % in min.gz)

**Type of PR:** enhancement

**Overview of change:**

There are several "big" changes:

1. To make prod build we started to use non-composite project (see below).
1. Replaced `ts-transformer-minify-privates` with `ts-transformer-properties-rename` to rename properties while compiling
1. Changed "workflow". Scripts `tsc-all` and `tsc-all-watch` have been dropped.
	Previously these scripts were used to verify that everything is compiled successfully.
	Now, to ensure this, you should use `tsc-verify` script - it does pretty similar to `tsc-all`, but the difference is meaning of the script (see below).
1. `d.ts` bundle right now bundled from `d.ts` files (previously it takes `.ts` files directly) and from non-composite build (see below).
1. Slightly changed `verify` script and now it should be a bit faster (in my env it's 55s vs 65s).
1. Pre-commit hook uses `tsc-verify` script so it might take more time than usual because you won't compile the library with composite project because we use non-composite one (see 1).
1. A bit refactored tsconfigs.
	- We have `tsconfig.options.json` instead of `tsconfig.base.json` with all options, without `plugins` and `outDir` specified.
	- We have `tsconfig.composite.base.json` - the base config for composite sub-projects (it has specified `outDir` option and set `composite` to `true`, so you don't need to set it in every config).
	- We have `tsconfig.prod.json` - it's _non-composite_ config to make a prod build. It has `plugins` option for `ttsc` and `stripInternals` for `dts-bundle-generator`.
1. Structure of `lib` output folder has been a bit changed - previously we had `lib/src` and `lib/tests` where we had all compiled js/dts code. Now there are `lib/prod` and `lib/composite` - output folders for different builds (prod and composite ones).
1. I have disabled `no-redundant-jsdoc` tslint rule because it failed with error on `/** @public */` comments ¯\_(ツ)_/¯
1. `ts-transformer-strip-const-enums` added in #432 now detects whether `const enum` is exported from entry point (`src/index.ts`) and it allows us strip some additional `const enum`s.

**Composite vs non-composite**

Composite projects are great solution - it allows you split your project on different areas and compile them "independently" with some references between them. Another cool features of composite projects are built-in support to detect cyclic dependencies between projects and detecting whether you're able to import module from other project or not. This helps us several (2-3) times while develop and disallow, for instance, import a module from api level in model or even gui sub-project. That's why we don't want to opt out them back.

In opposite, non-composite project has the whole program information while compilation - which symbols are exported _from entry point_, which aren't (composite projects know about current compiled project and don't know about project which will use this project).

So, to make kind of "whole program optimization" we should use non-composite projects, but to be sure about correct imports/dependencies and structure we should use composite ones.

That's why this PR exist in the way you can see it.

To build the library we use non-composite project, but to verify that everything is ok - composite ones.

I hope I didn't miss something 🙂 

timocov: ![image](https://user-images.githubusercontent.com/3112183/82050830-7ab8b000-96c1-11ea-9d08-b30f8d7e20ac.png)

We make the library even smaller than it was before all changes in 3.0. Good point for blog post though 😂
timocov: We need to build locally (and in e2e tests) with non-composite projects, because we want to get _the same state_ before and after minifying so we'll be sure that everything is fine locally (we don't need to build production build for that like with google closure compiler).
timocov: Ah, goodbye debugging production build 😂 

![image](https://user-images.githubusercontent.com/3112183/82051946-7ab9af80-96c3-11ea-956a-be039bb89859.png)


Patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
index c99c1645e..236dfb62d 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -80,12 +80,9 @@ jobs:
     executor: node-latest-executor
     steps:
       - checkout-with-deps
-      - run: npm run tsc-all
-
-      # make sure that the project is compiled with non-composite project
-      # it doesn't output anything, but is used by IDE or tools
-      - run: ./node_modules/.bin/tsc -p tsconfig.json
-
+      # make sure that the project is compiled successfully with composite projects
+      # so we don't have cyclic deps between projects and wrong imports
+      - run: npm run tsc-verify
       - run: npm run build:prod
       - persist_to_workspace:
           root: ./
diff --git a/BUILDING.md b/BUILDING.md
index c9acade08..af4372dcf 100644
--- a/BUILDING.md
+++ b/BUILDING.md
@@ -6,8 +6,7 @@ The minimal supported version of [NodeJS](https://nodejs.org/) for development i
 
 - `npm run tsc` - compiles the source code only (excluding tests)
 - `npm run tsc-watch` - runs the TypeScript compiler in the watch mode for source code (same as `tsc`, but in the watch mode)
-- `npm run tsc-all` - compiles everything (source code and tests)
-- `npm run tsc-all-watch` - runs the TypeScript compiler in watch mode for source code and tests (same as `tsc-all`, but in watch mode)
+- `npm run tsc-verify` - compiles everything (source code and tests) with composite projects config to ensure that no invalid imports or cyclic deps are found
 
 ## Bundling
 
diff --git a/dts-config.js b/dts-config.js
index 52707c189..cf93f5167 100644
--- a/dts-config.js
+++ b/dts-config.js
@@ -3,11 +3,11 @@
 /** @type import('dts-bundle-generator/config-schema').BundlerConfig */
 const config = {
 	compilationOptions: {
-		preferredConfigPath: './tsconfig.json',
+		preferredConfigPath: './tsconfig.prod.json',
 	},
 	entries: [
 		{
-			filePath: './src/index.ts',
+			filePath: './lib/prod/src/index.d.ts',
 			outFile: './dist/typings.d.ts',
 			output: {
 				sortNodes: true,
diff --git a/package.json b/package.json
index 178db7c0a..526ae4bd0 100644
--- a/package.json
+++ b/package.json
@@ -56,7 +56,7 @@
     "rollup-plugin-replace": "~2.2.0",
     "rollup-plugin-terser": "~5.3.0",
     "ts-node": "~8.10.0",
-    "ts-transformer-minify-privates": "~0.3.0",
+    "ts-transformer-properties-rename": "~0.1.0",
     "ts-transformer-strip-const-enums": "~1.0.1",
     "tslib": "1.12.0",
     "tslint": "6.1.1",
@@ -72,10 +72,9 @@
     "install-hooks": "node scripts/githooks/install.js",
     "clean": "rimraf lib/ dist/",
     "bundle-dts": "tsc --noEmit --allowJs dts-config.js && dts-bundle-generator --config dts-config.js",
-    "tsc": "ttsc -b src/tsconfig.composite.json",
+    "tsc": "ttsc -p tsconfig.prod.json",
     "tsc-watch": "npm run tsc -- --watch --preserveWatchOutput",
-    "tsc-all": "ttsc -b tsconfig.composite.json",
-    "tsc-all-watch": "npm run tsc-all -- --watch --preserveWatchOutput",
+    "tsc-verify": "tsc -b tsconfig.composite.json",
     "lint": "npm-run-all -p lint:**",
     "lint:eslint": "eslint --format=unix --ext .js ./",
     "lint:tslint": "tslint --config tslint.json --project tsconfig.json",
@@ -87,7 +86,7 @@
     "build:watch": "npm-run-all tsc -p tsc-watch rollup-watch",
     "build:prod": "cross-env NODE_ENV=production npm run build",
     "codechecks": "codechecks",
-    "verify": "npm-run-all clean -p tsc-all lint check-markdown-links -s build:prod test codechecks",
+    "verify": "npm-run-all clean -p lint check-markdown-links build:prod -p tsc-verify test codechecks",
     "test": "mocha tests/unittests/**/*.spec.ts"
   }
 }
diff --git a/rollup.config.js b/rollup.config.js
index 84c4dee42..0a561573d 100644
--- a/rollup.config.js
+++ b/rollup.config.js
@@ -50,7 +50,7 @@ function getConfig(inputFile, type, isProd) {
 				mangle: {
 					module: (type === 'module'),
 					properties: {
-						regex: /^_private_/,
+						regex: /^_(private|internal)_/,
 					},
 				},
 			}),
@@ -62,14 +62,14 @@ function getConfig(inputFile, type, isProd) {
 }
 
 const configs = [
-	getConfig('./lib/src/index.js', 'module', false),
-	getConfig('./lib/src/standalone.js', 'standalone', false),
+	getConfig('./lib/prod/src/index.js', 'module', false),
+	getConfig('./lib/prod/src/standalone.js', 'standalone', false),
 ];
 
 if (process.env.NODE_ENV === 'production') {
 	configs.push(
-		getConfig('./lib/src/index.js', 'module', true),
-		getConfig('./lib/src/standalone.js', 'standalone', true)
+		getConfig('./lib/prod/src/index.js', 'module', true),
+		getConfig('./lib/prod/src/standalone.js', 'standalone', true)
 	);
 }
 
diff --git a/scripts/check-dts-changes.sh b/scripts/check-dts-changes.sh
index 619cd16d4..f26556fb6 100755
--- a/scripts/check-dts-changes.sh
+++ b/scripts/check-dts-changes.sh
@@ -23,6 +23,8 @@ function generate_dts_for_rev {
   npm install
 
   print_step "Generate dts to $dts_filename"
+  npm run clean
+  npm run tsc
   npm run bundle-dts
   mv ./dist/typings.d.ts $dts_filename
 }
diff --git a/scripts/githooks/pre-commit/lint.js b/scripts/githooks/pre-commit/lint.js
index 5c3575d0e..29f143920 100755
--- a/scripts/githooks/pre-commit/lint.js
+++ b/scripts/githooks/pre-commit/lint.js
@@ -89,7 +89,7 @@ function lintFiles(files) {
 	// tsc & tslint
 	const tsFiles = filterByExt(files, '.ts');
 	if (tsFiles.length !== 0) {
-		hasErrors = run('npm run tsc-all') || hasErrors;
+		hasErrors = run('npm run tsc-verify') || hasErrors;
 
 		// we won't run tslint for all files
 		// because it's slow as fuck (18s for all project)
diff --git a/src/api/series-api.ts b/src/api/series-api.ts
index 785271239..1307cc5dc 100644
--- a/src/api/series-api.ts
+++ b/src/api/series-api.ts
@@ -14,7 +14,7 @@ import {
 	SeriesPartialOptionsMap,
 	SeriesType,
 } from '../model/series-options';
-import { Logical, Range, TimePointIndex } from '../model/time-data';
+import { Logical, Range, TimePoint, TimePointIndex } from '../model/time-data';
 import { TimeScaleVisibleRange } from '../model/time-scale-visible-range';
 
 import { IPriceScaleApiProvider } from './chart-api';
@@ -138,7 +138,7 @@ export class SeriesApi<TSeriesType extends SeriesType> implements ISeriesApi<TSe
 	}
 
 	public setMarkers(data: SeriesMarker<Time>[]): void {
-		const convertedMarkers = data.map((marker: SeriesMarker<Time>) => ({
+		const convertedMarkers = data.map<SeriesMarker<TimePoint>>((marker: SeriesMarker<Time>) => ({
 			...marker,
 			time: convertTime(marker.time),
 		}));
diff --git a/src/api/tsconfig.composite.json b/src/api/tsconfig.composite.json
index a1679c550..5d75db3a5 100644
--- a/src/api/tsconfig.composite.json
+++ b/src/api/tsconfig.composite.json
@@ -1,8 +1,5 @@
 {
-	"extends": "../../tsconfig.base.json",
-	"compilerOptions": {
-		"composite": true
-	},
+	"extends": "../../tsconfig.composite.base.json",
 	"references": [
 		{ "path": "../formatters/tsconfig.composite.json" },
 		{ "path": "../gui/tsconfig.composite.json" },
diff --git a/src/formatters/tsconfig.composite.json b/src/formatters/tsconfig.composite.json
index 987b8f837..601ac47b4 100644
--- a/src/formatters/tsconfig.composite.json
+++ b/src/formatters/tsconfig.composite.json
@@ -1,8 +1,5 @@
 {
-	"extends": "../../tsconfig.base.json",
-	"compilerOptions": {
-		"composite": true
-	},
+	"extends": "../../tsconfig.composite.base.json",
 	"references": [
 		{ "path": "../helpers/tsconfig.composite.json" }
 	],
diff --git a/src/gui/tsconfig.composite.json b/src/gui/tsconfig.composite.json
index 63558a8b3..b4ba456c6 100644
--- a/src/gui/tsconfig.composite.json
+++ b/src/gui/tsconfig.composite.json
@@ -1,8 +1,5 @@
 {
-	"extends": "../../tsconfig.base.json",
-	"compilerOptions": {
-		"composite": true
-	},
+	"extends": "../../tsconfig.composite.base.json",
 	"references": [
 		{ "path": "../tsconfig.model.json" }
 	],
diff --git a/src/helpers/color.ts b/src/helpers/color.ts
index 237ccdf52..5517ed21b 100644
--- a/src/helpers/color.ts
+++ b/src/helpers/color.ts
@@ -28,6 +28,7 @@ type AlphaComponent = Nominal<number, 'AlphaComponent'>;
 export type Rgb = [RedComponent, GreenComponent, BlueComponent];
 type Rgba = [RedComponent, GreenComponent, BlueComponent, AlphaComponent];
 
+/** @public */
 const namedColorRgbHexStrings: Record<string, string> = {
 	aliceblue: '#f0f8ff',
 	antiquewhite: '#faebd7',
diff --git a/src/helpers/tsconfig.composite.json b/src/helpers/tsconfig.composite.json
index a4a4c0d76..13506b9ba 100644
--- a/src/helpers/tsconfig.composite.json
+++ b/src/helpers/tsconfig.composite.json
@@ -1,9 +1,5 @@
 {
-	"extends": "../../tsconfig.base.json",
-	"compilerOptions": {
-		"composite": true
-	},
-	"references": [],
+	"extends": "../../tsconfig.composite.base.json",
 	"include": [
 		"./**/*.ts"
 	]
diff --git a/src/model/series-data.ts b/src/model/series-data.ts
index b26f22d31..cef1c1246 100644
--- a/src/model/series-data.ts
+++ b/src/model/series-data.ts
@@ -20,6 +20,7 @@ export const enum SeriesPlotIndex {
 	Color = 4,
 }
 
+/** @public */
 const barFunctions = {
 	open: (bar: Bar['value']) => bar[SeriesPlotIndex.Open] as BarPrice,
 
diff --git a/src/model/series.ts b/src/model/series.ts
index b3286eae3..6b4de5502 100644
--- a/src/model/series.ts
+++ b/src/model/series.ts
@@ -284,7 +284,7 @@ export class Series<T extends SeriesType = SeriesType> extends PriceDataSource i
 	}
 
 	public setMarkers(data: SeriesMarker<TimePoint>[]): void {
-		this._markers = data.map((item: SeriesMarker<TimePoint>) => ({ ...item }));
+		this._markers = data.map<SeriesMarker<TimePoint>>((item: SeriesMarker<TimePoint>) => ({ ...item }));
 		this._recalculateMarkers();
 		const sourcePane = this.model().paneForSource(this);
 		this._markersPaneView.update('data');
@@ -564,7 +564,7 @@ export class Series<T extends SeriesType = SeriesType> extends PriceDataSource i
 			return;
 		}
 
-		this._indexedMarkers = this._markers.map((marker: SeriesMarker<TimePoint>, index: number) => ({
+		this._indexedMarkers = this._markers.map<InternalSeriesMarker<TimePointIndex>>((marker: SeriesMarker<TimePoint>, index: number) => ({
 			time: ensureNotNull(timeScalePoints.indexOf(marker.time.timestamp, true)),
 			position: marker.position,
 			shape: marker.shape,
diff --git a/src/tsconfig.composite.json b/src/tsconfig.composite.json
index cd2bca798..8ca4c042d 100644
--- a/src/tsconfig.composite.json
+++ b/src/tsconfig.composite.json
@@ -1,8 +1,5 @@
 {
-	"extends": "../tsconfig.base.json",
-	"compilerOptions": {
-		"composite": true
-	},
+	"extends": "../tsconfig.composite.base.json",
 	"references": [
 		{ "path": "./api/tsconfig.composite.json" },
 		{ "path": "./formatters/tsconfig.composite.json" },
diff --git a/src/tsconfig.model.json b/src/tsconfig.model.json
index 5008f9074..aac88ae53 100644
--- a/src/tsconfig.model.json
+++ b/src/tsconfig.model.json
@@ -1,8 +1,5 @@
 {
-	"extends": "../tsconfig.base.json",
-	"compilerOptions": {
-		"composite": true
-	},
+	"extends": "../tsconfig.composite.base.json",
 	"references": [
 		{ "path": "./formatters/tsconfig.composite.json" },
 		{ "path": "./helpers/tsconfig.composite.json" }
diff --git a/src/views/pane/series-markers-pane-view.ts b/src/views/pane/series-markers-pane-view.ts
index 592cd5f21..face72612 100644
--- a/src/views/pane/series-markers-pane-view.ts
+++ b/src/views/pane/series-markers-pane-view.ts
@@ -150,7 +150,7 @@ export class SeriesMarkersPaneView implements IUpdatablePaneView {
 		const timeScale = this._model.timeScale();
 		const seriesMarkers = this._series.indexedMarkers();
 		if (this._dataInvalidated) {
-			this._data.items = seriesMarkers.map((marker: InternalSeriesMarker<TimePointIndex>) => ({
+			this._data.items = seriesMarkers.map<SeriesMarkerRendererDataItem>((marker: InternalSeriesMarker<TimePointIndex>) => ({
 				time: marker.time,
 				x: 0 as Coordinate,
 				y: 0 as Coordinate,
diff --git a/tsconfig.composite.base.json b/tsconfig.composite.base.json
new file mode 100644
index 000000000..b676e474a
--- /dev/null
+++ b/tsconfig.composite.base.json
@@ -0,0 +1,9 @@
+{
+	"extends": "./tsconfig.options.json",
+	"compilerOptions": {
+		"composite": true,
+		"outDir": "lib/composite"
+	},
+	"references": [],
+	"include": []
+}
diff --git a/tsconfig.composite.json b/tsconfig.composite.json
index e1efa04ee..515dc2b84 100644
--- a/tsconfig.composite.json
+++ b/tsconfig.composite.json
@@ -1,11 +1,7 @@
 {
-	"extends": "./tsconfig.base.json",
-	"compilerOptions": {
-		"composite": true
-	},
+	"extends": "./tsconfig.composite.base.json",
 	"references": [
 		{ "path": "./src/tsconfig.composite.json" },
 		{ "path": "./tests/unittests/tsconfig.composite.json" }
-	],
-	"include": []
+	]
 }
diff --git a/tsconfig.json b/tsconfig.json
index 4e1ef1b9b..c0a5df387 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,8 +1,7 @@
 {
-	"extends": "./tsconfig.base.json",
+	"extends": "./tsconfig.options.json",
 	"compilerOptions": {
-		"noEmit": true,
-		"stripInternal": true,
+		"noEmit": true
 	},
 	"include": [
 		"src/**/*.ts",
diff --git a/tsconfig.base.json b/tsconfig.options.json
similarity index 82%
rename from tsconfig.base.json
rename to tsconfig.options.json
index 9fd4335d0..c7944abdd 100644
--- a/tsconfig.base.json
+++ b/tsconfig.options.json
@@ -18,11 +18,6 @@
 		"noFallthroughCasesInSwitch": true,
 		"noImplicitReturns": true,
 		"noUnusedLocals": true,
-		"outDir": "lib",
-		"plugins": [
-			{ "transform": "ts-transformer-minify-privates" },
-			{ "transform": "ts-transformer-strip-const-enums" }
-		],
 		"preserveConstEnums": true,
 		"resolveJsonModule": true,
 		"rootDir": "./",
diff --git a/tsconfig.prod.json b/tsconfig.prod.json
new file mode 100644
index 000000000..78de39546
--- /dev/null
+++ b/tsconfig.prod.json
@@ -0,0 +1,14 @@
+{
+	"extends": "./tsconfig.options.json",
+	"compilerOptions": {
+		"outDir": "lib/prod",
+		"plugins": [
+			{ "transform": "ts-transformer-strip-const-enums", "entrySourceFiles": ["./src/index.ts"] },
+			{ "transform": "ts-transformer-properties-rename", "entrySourceFiles": ["./src/index.ts"] }
+		],
+		"stripInternal": true
+	},
+	"include": [
+		"src/**/*.ts"
+	]
+}
diff --git a/tslint.json b/tslint.json
index ad80e3168..56657ed8c 100644
--- a/tslint.json
+++ b/tslint.json
@@ -131,7 +131,7 @@
 		"no-parameter-properties": true,
 		"no-parameter-reassignment": false,
 		"no-promise-as-boolean": false,
-		"no-redundant-jsdoc": true,
+		"no-redundant-jsdoc": false,
 		"no-reference": false,
 		"no-reference-import": true,
 		"no-require-imports": false,


Test patch:
diff --git a/tests/e2e/memleaks/helpers/get-test-cases.ts b/tests/e2e/memleaks/helpers/get-test-cases.ts
index 53d640ab3..0bdb286c7 100644
--- a/tests/e2e/memleaks/helpers/get-test-cases.ts
+++ b/tests/e2e/memleaks/helpers/get-test-cases.ts
@@ -22,7 +22,7 @@ function isTestCaseFile(filePath: string): boolean {
 export function getTestCases(): TestCase[] {
 	return fs.readdirSync(testCasesDir)
 		.filter(isTestCaseFile)
-		.map((testCaseFile: string) => ({
+		.map<TestCase>((testCaseFile: string) => ({
 			name: extractTestCaseName(testCaseFile) as string,
 			caseContent: fs.readFileSync(path.join(testCasesDir, testCaseFile), { encoding: 'utf-8' }),
 		}));
diff --git a/tests/e2e/tsconfig.json b/tests/e2e/tsconfig.json
index 08c133b45..2be6ad5b8 100644
--- a/tests/e2e/tsconfig.json
+++ b/tests/e2e/tsconfig.json
@@ -1,5 +1,5 @@
 {
-	"extends": "../../tsconfig.base.json",
+	"extends": "../../tsconfig.options.json",
 	"compilerOptions": {
 		"esModuleInterop": true,
 		"lib": [
diff --git a/tests/unittests/tsconfig.composite.json b/tests/unittests/tsconfig.composite.json
index eafb9d364..87b99777e 100644
--- a/tests/unittests/tsconfig.composite.json
+++ b/tests/unittests/tsconfig.composite.json
@@ -1,7 +1,6 @@
 {
-	"extends": "../../tsconfig.base.json",
+	"extends": "../../tsconfig.composite.base.json",
 	"compilerOptions": {
-		"composite": true,
 		"module": "commonjs"
 	},
 	"references": [