Repository: theseanl/tscc Branch: master Commit: 7c0a7a5194e5 Files: 185 Total size: 459.6 KB Directory structure: gitextract_d855frd2/ ├── .circleci/ │ └── config.yml ├── .editorconfig ├── .github/ │ └── dependabot.yml ├── .gitignore ├── .vscode/ │ └── settings.json ├── LICENSE ├── README.md ├── jest.config.js ├── lerna.json ├── package.json ├── packages/ │ ├── rollup-plugin-tscc/ │ │ ├── .npmignore │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── MultiMap.ts │ │ │ ├── goog_shim_mixin.ts │ │ │ ├── index.ts │ │ │ ├── merge_chunks.ts │ │ │ ├── sort_chunks.ts │ │ │ └── spec/ │ │ │ ├── ITsccSpecRollupFacade.ts │ │ │ └── TsccSpecRollupFacade.ts │ │ ├── test/ │ │ │ ├── __snapshots__/ │ │ │ │ └── golden_test.ts.snap │ │ │ ├── golden_test.ts │ │ │ ├── merge_chunks.ts │ │ │ ├── sample/ │ │ │ │ ├── external-modules/ │ │ │ │ │ ├── entry.js │ │ │ │ │ └── tscc.spec.json │ │ │ │ ├── external-modules-in-many-module-build/ │ │ │ │ │ ├── common/ │ │ │ │ │ │ └── index.js │ │ │ │ │ ├── dependent.js │ │ │ │ │ ├── entry.js │ │ │ │ │ └── tscc.spec.json │ │ │ │ ├── goog-shim/ │ │ │ │ │ ├── dependent.js │ │ │ │ │ ├── entry.js │ │ │ │ │ ├── tscc.spec.json │ │ │ │ │ └── tscc.spec.module.json │ │ │ │ └── many-module-build/ │ │ │ │ ├── common.js │ │ │ │ ├── dir/ │ │ │ │ │ └── dependency.js │ │ │ │ ├── entry.js │ │ │ │ ├── tscc.spec.json │ │ │ │ └── tscc.spec.module.json │ │ │ └── sort_chunks.ts │ │ ├── third_party/ │ │ │ └── closure_library/ │ │ │ ├── README.md │ │ │ ├── goog_shim.js │ │ │ └── reflect_shim.js │ │ └── tsconfig.json │ ├── tscc/ │ │ ├── .npmignore │ │ ├── Makefile │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── default_libs.ts │ │ │ ├── external_module_support.ts │ │ │ ├── graph/ │ │ │ │ ├── Cache.ts │ │ │ │ ├── ClosureDependencyGraph.ts │ │ │ │ ├── ISourceNode.ts │ │ │ │ ├── TypescriptDependencyGraph.ts │ │ │ │ └── source_node_factory.ts │ │ │ ├── log/ │ │ │ │ ├── Logger.ts │ │ │ │ └── spinner.ts │ │ │ ├── main.ts │ │ │ ├── shared/ │ │ │ │ ├── PartialMap.ts │ │ │ │ ├── array_utils.ts │ │ │ │ ├── escape_goog_identifier.ts │ │ │ │ ├── sourcemap_splice.ts │ │ │ │ └── vinyl_utils.ts │ │ │ ├── spawn_compiler.ts │ │ │ ├── spec/ │ │ │ │ ├── ITsccSpecWithTS.ts │ │ │ │ └── TsccSpecWithTS.ts │ │ │ ├── transformer/ │ │ │ │ ├── decorator_property_transformer.ts │ │ │ │ ├── dts_requiretype_transformer.ts │ │ │ │ ├── goog_namespace_transformer.ts │ │ │ │ ├── rest_property_transformer.ts │ │ │ │ ├── transformer_utils.ts │ │ │ │ └── ts_helper_transformer.ts │ │ │ ├── tscc.ts │ │ │ └── tsickle_patches/ │ │ │ ├── facade.ts │ │ │ ├── patch_tsickle_decorator_transformer.ts │ │ │ └── patch_tsickle_module_resolver.ts │ │ ├── test/ │ │ │ ├── e2e/ │ │ │ │ ├── __snapshots__/ │ │ │ │ │ └── golden_test.ts.snap │ │ │ │ ├── golden_test.ts │ │ │ │ └── sample/ │ │ │ │ ├── case_1/ │ │ │ │ │ ├── a.ts │ │ │ │ │ ├── ab.ts │ │ │ │ │ ├── b.ts │ │ │ │ │ ├── bb.ts │ │ │ │ │ ├── bc.ts │ │ │ │ │ ├── c.ts │ │ │ │ │ ├── cc.ts │ │ │ │ │ ├── tscc.spec.json │ │ │ │ │ └── tsconfig.json │ │ │ │ ├── case_10_unsafe_module_name_and_script_dts/ │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── script_dts.d.ts │ │ │ │ │ ├── tscc.spec.json │ │ │ │ │ └── tsconfig.json │ │ │ │ ├── case_11_referencing_goog/ │ │ │ │ │ ├── dependent.ts │ │ │ │ │ ├── entry.ts │ │ │ │ │ ├── tscc.spec.json │ │ │ │ │ └── tsconfig.json │ │ │ │ ├── case_12_using_jsFiles/ │ │ │ │ │ ├── entry.ts │ │ │ │ │ ├── jsfile.d.ts │ │ │ │ │ ├── jsfile.js │ │ │ │ │ ├── tscc.spec.json │ │ │ │ │ └── tsconfig.json │ │ │ │ ├── case_13_chunk_format_global/ │ │ │ │ │ ├── tscc.spec.json │ │ │ │ │ └── tsconfig.json │ │ │ │ ├── case_14_chunk_format_module/ │ │ │ │ │ ├── tscc.spec.json │ │ │ │ │ └── tsconfig.json │ │ │ │ ├── case_2_sourcemaps/ │ │ │ │ │ ├── a.ts │ │ │ │ │ ├── tscc.spec.json │ │ │ │ │ └── tsconfig.json │ │ │ │ ├── case_3_sourcemaps_with_decorators/ │ │ │ │ │ ├── a.ts │ │ │ │ │ ├── tscc.spec.json │ │ │ │ │ └── tsconfig.json │ │ │ │ ├── case_4_external/ │ │ │ │ │ ├── a.ts │ │ │ │ │ ├── tscc.spec.json │ │ │ │ │ └── tsconfig.json │ │ │ │ ├── case_5_object_spread/ │ │ │ │ │ ├── a.ts │ │ │ │ │ ├── tscc.spec.json │ │ │ │ │ └── tsconfig.json │ │ │ │ ├── case_6_type_only_references/ │ │ │ │ │ ├── a.ts │ │ │ │ │ ├── b.ts │ │ │ │ │ ├── c.ts │ │ │ │ │ ├── d.ts │ │ │ │ │ ├── tscc.spec.json │ │ │ │ │ └── tsconfig.json │ │ │ │ ├── case_7_lodash_style_external_module_declaration/ │ │ │ │ │ ├── a.ts │ │ │ │ │ ├── tscc.spec.json │ │ │ │ │ └── tsconfig.json │ │ │ │ ├── case_8_dts_requiretype/ │ │ │ │ │ ├── imported.d.ts │ │ │ │ │ ├── module.ts │ │ │ │ │ ├── transtively_imported.d.ts │ │ │ │ │ ├── tscc.spec.json │ │ │ │ │ └── tsconfig.json │ │ │ │ └── case_9_ts_in_node_modules/ │ │ │ │ ├── index.ts │ │ │ │ ├── tscc.spec.json │ │ │ │ └── tsconfig.json │ │ │ ├── graph/ │ │ │ │ ├── ClosureDependencyGraph.ts │ │ │ │ ├── TypescriptDependencyGraph.ts │ │ │ │ └── source_node_factory.ts │ │ │ ├── main.ts │ │ │ ├── sample/ │ │ │ │ ├── decorator/ │ │ │ │ │ └── decorates.ts │ │ │ │ ├── dts_requiretype/ │ │ │ │ │ ├── entry.ts │ │ │ │ │ └── required.d.ts │ │ │ │ ├── goog_module.js │ │ │ │ ├── rest/ │ │ │ │ │ ├── case_1.ts │ │ │ │ │ └── combined_with_decorators.ts │ │ │ │ ├── tsconfig.json │ │ │ │ └── tsdepsgraph/ │ │ │ │ ├── a.ts │ │ │ │ ├── ab.d.ts │ │ │ │ ├── b.d.ts │ │ │ │ ├── bb.d.ts │ │ │ │ ├── entry.ts │ │ │ │ └── tsconfig.json │ │ │ ├── shared/ │ │ │ │ ├── escape_goog_identifier.ts │ │ │ │ └── sourcemap_splice.ts │ │ │ ├── spec/ │ │ │ │ ├── TsccSpecWithTS.ts │ │ │ │ └── sample/ │ │ │ │ ├── empty.ts │ │ │ │ ├── nested_directory/ │ │ │ │ │ ├── empty.ts │ │ │ │ │ └── tsconfig.json │ │ │ │ └── tsconfig.1.json │ │ │ ├── transformer/ │ │ │ │ ├── __snapshots__/ │ │ │ │ │ └── transformers.ts.snap │ │ │ │ └── transformers.ts │ │ │ ├── tsconfig.test_files.json │ │ │ └── tsickle_patches/ │ │ │ └── patch_tsickle_module_resolver.ts │ │ ├── third_party/ │ │ │ ├── closure_library/ │ │ │ │ ├── base.d.ts │ │ │ │ ├── base.js │ │ │ │ ├── reflect.d.ts │ │ │ │ └── reflect.js │ │ │ └── tsickle/ │ │ │ ├── closure_externs.js │ │ │ └── third_party/ │ │ │ └── tslib/ │ │ │ ├── LICENSE.txt │ │ │ ├── README.google │ │ │ ├── externs.js │ │ │ └── tslib.js │ │ └── tsconfig.json │ └── tscc-spec/ │ ├── .npmignore │ ├── index.ts │ ├── package.json │ ├── src/ │ │ ├── ITsccSpec.ts │ │ ├── ITsccSpecJSON.ts │ │ ├── TsccSpec.ts │ │ └── shared/ │ │ └── Graph.ts │ ├── test/ │ │ ├── TsccSpec.ts │ │ ├── sample/ │ │ │ ├── invalid_json.json │ │ │ ├── spec_with_relative_external.json │ │ │ ├── tscc.spec.json │ │ │ └── unsupported_spec.json │ │ └── shared/ │ │ └── Graph.ts │ └── tsconfig.json └── tsconfig.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .circleci/config.yml ================================================ cache_key: &cache_key tscc-{{ checksum "yarn.lock" }} save_cache: &save_cache save_cache: key: *cache_key paths: - "node_modules" version: 2 jobs: build: parallelism: 3 docker: - image: circleci/openjdk:9-node working_directory: ~/tscc steps: - checkout - run: name: update node command: | curl -sSL "https://nodejs.org/dist/v14.17.3/node-v14.17.3-linux-x64.tar.xz" | sudo tar --strip-components=2 -xJ -C /usr/local/bin/ node-v14.17.3-linux-x64/bin/node - restore_cache: key: *cache_key - run: yarn install - run: yarn lerna bootstrap - run: name: run tests command: yarn jest --ci --runInBand --reporters=default --reporters=jest-junit environment: JEST_JUNIT_OUTPUT: "reports/junit/results.xml" - run: name: compile typescript command: yarn lerna exec -- -- tsc --noEmit - *save_cache - store_test_results: path: reports/junit - store_artifacts: path: reports/junit ================================================ FILE: .editorconfig ================================================ root = true [*] end_of_line = lf insert_final_newline = true [*.{js,jsx,ts,tsx,css,html,json}] charset = utf-8 indent_style = tab indent_size = 4 trim_trailing_whitespace = true max_line_length = 100 [*.md] trim_trailing_whitespace = false ================================================ FILE: .github/dependabot.yml ================================================ version: 2 updates: - package-ecosystem: npm directory: "/" schedule: interval: weekly open-pull-requests-limit: 20 ignore: - dependency-name: typescript versions: - "> 3.9.7" - dependency-name: tslib versions: - 2.1.0 ================================================ FILE: .gitignore ================================================ /node_modules /packages/*/node_modules dist lerna-debug.log junit.xml .tscc_temp yarn-error.log .vscode/**/*.log ================================================ FILE: .vscode/settings.json ================================================ { "editor.rulers": [ 100 ], "editor.formatOnSave": true, "javascript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": false, "javascript.format.insertSpaceAfterOpeningAndBeforeClosingEmptyBraces": false, "typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": false, "typescript.format.insertSpaceAfterOpeningAndBeforeClosingEmptyBraces": false, "jest.jestCommandLine": "jest" } ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2019 Sean Lee Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # TSCC [![tscc npm version](https://img.shields.io/npm/v/@tscc/tscc.svg?style=popout&color=blue&label=%40tscc%2Ftscc)](https://www.npmjs.com/package/@tscc/tscc) [![rollup-plugin-tscc npm version](https://img.shields.io/npm/v/@tscc/rollup-plugin-tscc.svg?style=popout&color=blue&label=%40tscc%2Frollup-plugin-tscc)](https://www.npmjs.com/package/@tscc/rollup-plugin-tscc) [![CircleCI](https://circleci.com/gh/theseanl/tscc.svg?style=svg)](https://circleci.com/gh/theseanl/tscc) TSCC is a collection of tools to seamlessly bundle and minify Typescript project using Closure Compiler. ## Migrate It is easy to migrate an existing Typescript project to TSCC. To get a sense of what it is like, check out [todomvc apps](https://github.com/theseanl/todomvc/) forked from the original [tastejs/todomvc](https://github.com/tastejs/todomvc) and modified to use TSCC. --- ## Features - Automatically configures settings for [tsickle](https://github.com/angular/tsickle) and [closure compiler](https://github.com/google/closure-compiler), and wires up tsickle js outputs and sourcemaps to closure compiler, sorted in accordence with dependency information, as required by closure compiler. - Provides an alternative [rollup](https://rollupjs.org) build using `rollup-plugin-tscc` plugin, emulating the chunking behaviour of closure compiler to get the same set of output files as what closure compiler would produce. - External module support: looks up `require`d nodejs modules and wires them so that externs are generated, and transforms any code that uses externally imported variables. Think of it as an analogue of ["external" option of webpack](https://webpack.js.org/configuration/externals/#externals) or ["globals" option of rollup](https://rollupjs.org/guide/en/#outputglobals) for closure compiler. ## Background Closure is a wonderful system of tools. The closure compiler is the best javascript minifier and bundler, but it is known to be very difficult to use. Documentations are scarce, and integration with external tools is not well-established. Tsickle is another wonderful tool. It has finally made it ergonomic to write codes that can naturally be consumed by closure compiler — it transforms `.ts` files to well-annotated `.js` files, which would otherwise not even be consumed by the compiler and automatically generates [externs](https://developers.google.com/closure/compiler/docs/api-tutorial3#externs) file from declaration files. However, as in closure compiler, one has to be careful with setting it up, otherwise strange bugs can occur which are not actively worked on as of now. Also, it only performs transpilation — there is no tool to feed transpiled files to closure compiler, which is another painful part. TSCC aims to encapsulate these tools in a minimal, ergonomic API, and provides a faster and easier alternative bundling method using [Rollup](https://rollupjs.org) via a companion rollup-plugin-tscc plugin, which is great for rapid development. TSCC can be used as a drop-in replacement for rollup in existing projects using rollup, after moving chunk information in `rollup.config.js` to [`tscc.spec.json`](#tscc-spec-files). TSCC spec file is a single source of truth for all of your module bundling information. ## Getting started Suppose that we have a project with the following directory structure. ``` my_project ├───tsconfig.json ├───rollup.config.js └───src ├───Components │ ... └───app.ts ``` 1. Install tscc cli: ``` yarn global add @tscc/tscc ``` 2. Create a [_spec file_](#tscc-spec-files) `tscc.spec.json` next to `tsconfig.json`. ```jsonc { "modules": { "out": "src/app.ts" // entry file path } } ``` 3. Execute at the project root: ``` tscc ``` In order to setup an alternative rollup build, 1. In your project's directory, install `@tscc/rollup-plugin-tscc` by executing: ``` yarn add -D @tscc/rollup-plugin-tscc ``` 2. Import `rollup-plugin-tscc` plugin in rollup config file. ```js // rollup.config.js import tscc from '@tscc/rollup-plugin-tscc'; export default { output: { ..., dir: '.' }, plugins: [ tscc(), typescript() ] } ``` 3. Execute `rollup` at the project root. ## Usage ### Command line @tscc/tscc package provides a single command line interface `tscc`. ``` tscc [--help] [--clean] [--spec ] [-- [-- ]] ``` It will compile & bundle typescript sourced based on module spec described in `spec_file`. Alternatively, one can provide the spec file's key-value pairs via command line arguments, see below. Note that one have to provide a spec file or at least a value for a 'module' - if both are omitted, it will assume that the spec file's path is implicitly set as the current working directory. Arguments passed after the first `--`, if exists, will be passed to the typescript compiler as one would pass to `tsc`, and arguments after the second `--` will be passed to the closure compiler. e.g. `tscc --spec src -- --target ES5 -- --assume_function_wrapper`. Note that `tsc` assumes that the project root (`--project`) is the current working directory when it is omitted, but `tscc` assumes that it is the containing directory of the spec file. ### JS API Simply provide contents of spec file as an argument: ```js import tscc from 'tscc'; tscc({ modules: { bundle: 'entry.ts' }, prefix: 'dist/' // ... }).then(() => console.log('Done')); ``` The default export `tscc` function accepts up to 3 arguments. ```ts tscc( json_content_of_spec_file_or_path_to_spec_file, path_to_search_for_tsconfig?:string, typescript_compiler_option_override? ):Promise ``` The first argument is either a string representing the path of the spec file or a JSON content of the spec file. The JSON object can additionally have `specFile` property, whose value is a path to a spec file. TSCC will lookup tscc spec file at that path and merge its contents. ```js tscc({ /* Contents of spec JSON */ specFile: "path_to_spec_file.json" }) // To load spec file from the path and override it. ``` The second argument should be self-explanatory; the third argument is what would you put in tsconfig.json's "compilerOption" key, it will override those provided with the second argument. ### Usage with rollup @tscc/rollup-plugin-tscc package provides a rollup plugin which will provide chunking information in your spec file to rollup. Install `rollup-plugin-tscc` by executing `yarn add -D @tscc/rollup-plugin-tscc`. ```js // rollup.config.js const tscc = require('@tscc/rollup-plugin-tscc'); module.exports = { output: { dir: '.' // output.dir option will be provided by the plugin, but // rollup currently requires something to be present in order to // work properly. }, // ... plugins: [ tscc({ /* Contents of spec JSON */ // or, specFile: "path_to_spec_file.json" }) ] }; ``` Then it will provide the information in your spec file to rollup, and post-process code-splitting chunks produced by rollup to match the behavior of Closure Compiler, so that you can use `rollup` build interchangeably with `tscc` build. The plugin will control the `input`, `output.dir`, `output.entryFileNames`, `output.chunkFileNames` option. Note that it does not transpile TS to JS, one has to provide another plugin manually, such as [rollup-plugin-typescript2](https://github.com/ezolenko/rollup-plugin-typescript2). ## Tscc spec files Tscc spec file is a single source of truth of your bundling information. It describes each of output bundle's entry file and dependencies among them. It also describes which modules imported in your source shall be treated as an external module and aliased with which global variable. ```js { modules, /* required */ external, prefix, chunkFormat, compilerFlags, jsFiles, debug } ``` ### `modules` ```jsonc "modules": { "index": "index.ts", "dependent_a": { "entry": "dependent_a_entry_file.ts", "dependencies": [ "index" ], "extraSources": [ "css_renaming_map.js" ] } } ``` `modules` option is a key-value pair of module name and module's specification. If a specification only consists of a entry file name, it can simply be a string representing the entry file's path, which is sufficient for most of build situation where no code splitting is applied. In general, module's specification consists of `entry`, `dependencies`, and `extraSources`. `dependencies` is an array of module names that this module depends on. It can be omitted if empty. Module names specified here will be provided to closure compiler via `--chunk` flags, so check out a more detailed description of it in [Closure Compiler repo](https://github.com/google/closure-compiler/wiki/JS-Modules#code-splitting-output-modules). `extraSources` is an array of file names, which are not included in the Typescript project or not reacheable from a specified entry file via `import`s, but still needed to be provided to the closure compiler, such as css renaming maps generated by [Closure Stylesheets](https://github.com/google/closure-stylesheets). It can be omitted if empty. A module's name is an identifier to be used as a output chunk's name. To control the output directory, use `prefix` option. CLI equivalent is `--module :::`. ### `external` ```jsonc { "external": { "react": "React", "react-dom": "ReactDOM", } } ``` It is mostly identical to the [`output.global` option](https://rollupjs.org/guide/en#core-functionality) of rollup. It is a key-value pair, where key is a module name whose content will not be included in the bundle output, and value is a name of a global variable to which an `module.exports` of the module will be aliased. If a module name is a relative path, the file it _resolves to_ will be treated as an external module. This is similar to rollup's case, but is different in that only relative path can be used and absolute paths can't. Such paths are resolved in the same convention how other relative paths in the spec file are resolved, see `prefix` option description. CLI equivalent is `--external :`. ### `prefix` ```jsonc "prefix": "dist/" // or, "prefix": { "rollup": "dev/", "cc": "dist/" } ``` It is a name that will be prepended to the output chunk's name. It is prepended _as is_, which means that if no trailing path separator was provided, it will modify the output file's name. If it is a relative path starting from the current directory ("."), it will be resolved relative to the spec file's location. Otherwise, any relative path will be resolved relative to the current working directory, and absolute paths are used as is. CLI equivalent is `--prefix dist/` (or `--prefix.rollup dev/ --prefix.cc dist/`). ### `chunkFormat` ```jsonc "chunkFormat": "global" /* default */ | "module" ``` It is a value of `"global"` or `"module"` designating the output chunks' format. In case of `"global"`, which is the default behavior if this key isn't specified, output chunks will be plain Javascript that is suitable for `