├── .gitignore ├── .npmignore ├── .prettierrc.js ├── .travis.yml ├── CHANGELOG.md ├── README.md ├── jest.config.js ├── lerna.json ├── package.json ├── packages ├── parcel-plugin │ ├── TTypescriptAsset.js │ ├── index.js │ └── package.json ├── ttypescript-examples │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── gen-transpiled.js │ │ ├── test.ts │ │ ├── transformers │ │ │ └── transformer.ts │ │ └── tsconfig.json │ └── tsconfig.json └── ttypescript │ ├── CHANGELOG.md │ ├── README.md │ ├── __tests__ │ ├── PluginCreator.spec.ts │ ├── assets │ │ ├── tsconfig.json │ │ ├── tsnode.js │ │ └── tsnode.ts │ ├── configs.ts │ ├── helpers.ts │ ├── loadTypeScript │ │ ├── loadTypeScript.spec.ts │ │ └── mocks │ │ │ ├── oldVersion.js │ │ │ ├── requireCache.js │ │ │ ├── runInThisContextOnce.js │ │ │ ├── selfRequire.js │ │ │ └── simple.js │ ├── transforms │ │ ├── before.ts │ │ ├── safely.ts │ │ ├── transform-advanced.ts │ │ ├── transform-simple.ts │ │ └── transformers.ts │ ├── tsconfig.json │ ├── tsnode.spec.ts │ ├── tsplugins.spec.ts │ ├── ttsc.spec.ts │ └── typescript.spec.ts │ ├── bin │ ├── tsc │ └── tsserver │ ├── package.json │ ├── src │ ├── PluginCreator.ts │ ├── cancelationToken.ts │ ├── loadTypescript.ts │ ├── patchCreateProgram.ts │ ├── tsc.ts │ ├── tsserver.ts │ ├── tsserverlibrary.ts │ ├── typescript.ts │ ├── typescriptServices.ts │ ├── typingsInstaller.ts │ └── watchGuard.ts │ └── tsconfig.json ├── tsconfig.base.json └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | package-lock.json 4 | lib 5 | dist 6 | *.log 7 | .DS_STORE 8 | .vscode -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | example 4 | src 5 | tslint.json 6 | tsconfig.json 7 | .travis.yml 8 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | printWidth: 120, 3 | tabWidth: 4, 4 | singleQuote: true, 5 | trailingComma: 'es5', 6 | jsxBracketSameLine: true, 7 | }; 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - node 4 | deploy: 5 | on: 6 | tags: true 7 | api_key: 8 | secure: ioRzbbWBfaWUWlraJjEJBghP4QJADPtWsXwYPoxPTOj8j00xyUz2UsSjMWD97B6NULcdEZkiHgCgNayI0udU3vOoej2qif/5cuQHwU28m/QHLKaes2hRcMis7FVBE85agO/QUGW3bRqZfdy2pwXbM4IGQ8bL5/QD1KBF84gyPMF5x7ab2qwxu+ivEuxizU3+w74kANfdAHAP5C9wyBoPiQgrjt7j2O+Q1G9qZsnI1xdEOxRQF7a+SaC15f0YL79z0cQGMLeUOffius9zqx3rK6uUBf4L4GuvlnHTrivqIFC+07/81pPAmewrm1BOcB/Ms8fiHcwrR8wBnAcLp9/JTLkZZUdeQcvpkW4x0/7LLwv2RbcBh4b+dEl27EZCyL+rwXt6nbGGFOoODJtJUdo9kM2lkXZ3ucKu8Z6rxTDI5rm4+GdmULu7QH2ilbEb8CESWY0W8/sZEbhTPKGj2yWeppO4vwFukXsP1fkDwBmh0RiNT7qaNJDg25Vzq3rEHhitQsaOQUCvcoF15JL+KcLVvoo5VTZxspCvFXy9HLoTQMk/cSvZCZp99NTEJQT7XsmEQaPUIMkEIoFtPllwqZFLraBLNJA17s5n+T6pmRzqIixUGUy6739eLJ9pkLnNwg4wUjGaQdH4+CKxVfqKEHw+AQu4nlXw5otDf5ewRJ4Z/R4= 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deprecation Notice 2 | 3 | > **`ttypescript` is deprecated**. It currently works with TS below 5.0, but it will not be updated. 4 | 5 | For TypeScript 5+, please use [ts-patch](https://github.com/nonara/ts-patch). 6 | 7 | --- 8 | 9 | [![npm version](https://badge.fury.io/js/ttypescript.svg)](https://badge.fury.io/js/ttypescript) [![Build Status](https://travis-ci.org/cevek/ttypescript.svg?branch=master)](https://travis-ci.org/cevek/ttypescript) 10 | 11 | # ttypescript 12 | 13 | ## What it is 14 | Currently TypeScript doesn't support custom transformers in the tsconfig.json, but supports it programmatically. 15 | 16 | And there is no way to compile your files using custom transformers using `tsc` command. 17 | 18 | TTypescript (Transformer TypeScript) solves this problem by patching on the fly the compile module to use transformers from `tsconfig.json`. 19 | 20 | 29 | Instead of tsc and tsserver, use ttsc and ttsserver wrappers. This wrappers try to use locally installed typescript first. 30 | 31 | No version lock-ins - typescript used as peer dependency. 32 | 33 | ## How to install 34 | 35 | ``` 36 | npm i ttypescript -D 37 | ``` 38 | 39 | ttypescript uses your installed `typescript` in your `node_modules` 40 | 41 | ## How to use 42 | 43 | ### tsconfig.json 44 | 45 | Set a transformer path to the `tsconfig.json` in `compilerOptions` section `plugin` array: 46 | ``` 47 | { 48 | "compilerOptions": { 49 | "plugins": [ 50 | { "transform": "transformer-module" }, 51 | ] 52 | } 53 | } 54 | ``` 55 | 56 | plugin entries described in `PluginConfig`: 57 | 58 | ```ts 59 | export interface PluginConfig { 60 | /** 61 | * Path to transformer or transformer module name 62 | */ 63 | transform?: string; 64 | 65 | /** 66 | * The optional name of the exported transform plugin in the transform module. 67 | */ 68 | import?: string; 69 | 70 | /** 71 | * Plugin entry point format type, default is program 72 | */ 73 | type?: 'program' | 'config' | 'checker' | 'raw' | 'compilerOptions'; 74 | 75 | /** 76 | * Should transformer applied after all ones 77 | */ 78 | after?: boolean; 79 | 80 | /** 81 | * Should transformer applied for d.ts files, supports from TS2.9 82 | */ 83 | afterDeclarations?: boolean; 84 | /** 85 | * any other properties provided to the transformer as config argument 86 | * */ 87 | [options: string]: any; 88 | } 89 | ``` 90 | 91 | You just need to add the `transform` block with optional `import`, `type`, `after`, `afterDeclarations` and plugin-related options. 92 | 93 | `transform` can accept npm module or local file path (.ts or .js) related to `tsconfig.json` 94 | 95 | ### PluginConfig.type 96 | Because currently transformers can run only programmatically, most of them use factory wrapper with different signatures. 97 | For the possible to work with any of them you can specify `type` in the plugin config. 98 | 99 | By default will be used a `program` type. 100 | 115 | #### program 116 | If the transformer has a factory signature using `program` as first argument: 117 | ```ts 118 | (program: ts.Program, config?: PluginConfig) => ts.TransformerFactory 119 | where 120 | ts.TransformerFactory = (context: ts.TransformationContext) => (sourceFile: ts.SourceFile) => ts.SourceFile 121 | ``` 122 | Plugin config entry: `{ "transform": "transformer-module" }`. 123 | 124 | 125 | #### config 126 | For the signature with transformer's config: 127 | ```ts 128 | (config: PluginConfig) => ts.TransformerFactory 129 | ``` 130 | Plugin config entry: `{ "transform": "transformer-module", type: "config" }`. 131 | 132 | #### checker 133 | For the signature with ts.TypeChecker: 134 | ```ts 135 | (checker: ts.TypeChecker, config?: PluginConfig) => ts.TransformerFactory 136 | ``` 137 | Plugin config entry: `{ "transform": "transformer-module", type: "checker" }`. 138 | 139 | #### raw 140 | For the signature without factory wrapper: 141 | ```ts 142 | ts.TransformerFactory 143 | ``` 144 | Plugin config entry: `{ "transform": "transformer-module", type: "raw" }`. 145 | 146 | #### compilerOptions 147 | ```ts 148 | (compilerOpts: ts.CompilerOptions, config?: PluginConfig) => ts.TransformerFactory 149 | ``` 150 | Plugin config entry: `{ "transform": "transformer-module", type: "compilerOptions" }`. 151 | 152 | ```json 153 | { 154 | "compilerOptions": { 155 | "plugins": [ 156 | { "transform": "transformer-module", "someOption1": 123, "someOption2": 321 }, 157 | { "transform": "./transformers/my-transformer.ts" }, 158 | { "transform": "transformer-module", "after": true }, 159 | { "transform": "transformer-module", "afterDeclarations": true }, 160 | { "transform": "transformer-module", "type": "ls" } 161 | ] 162 | }, 163 | } 164 | ``` 165 | 166 | ### Command line 167 | 168 | Like usual `tsc`, all arguments work the same way. 169 | ``` 170 | ttsc 171 | ``` 172 | 173 | ### ts-node 174 | 175 | ``` 176 | ts-node --compiler ttypescript index.ts 177 | or 178 | ts-node -C ttypescript index.ts 179 | ``` 180 | 181 | ### Parcel 182 | 183 | Just install a parcel plugin 184 | ``` 185 | npm i parcel-plugin-ttypescript 186 | ``` 187 | 188 | 189 | ### Webpack 190 | ```js 191 | { 192 | test: /\.(ts|tsx)$/, 193 | loader: require.resolve('awesome-typescript-loader'), 194 | // or 195 | loader: require.resolve('ts-loader'), 196 | options: { 197 | compiler: 'ttypescript' 198 | } 199 | } 200 | ``` 201 | 202 | ### Rollup 203 | ```js 204 | // rollup.config.js 205 | import ttypescript from 'ttypescript' 206 | import tsPlugin from 'rollup-plugin-typescript2' 207 | 208 | export default { 209 | // ... 210 | plugins: [ 211 | // ... 212 | tsPlugin({ 213 | typescript: ttypescript 214 | }) 215 | ] 216 | } 217 | ``` 218 | 219 | ### VS Code 220 | If you want to compile your project with VS Code task runner you need to overwrite the config `typescript.tsdk` to path of the installed `ttypescript`: 221 | ``` 222 | "typescript.tsdk": "/usr/local/lib/node_modules/ttypescript/lib", 223 | or 224 | "typescript.tsdk": "node_modules/ttypescript/lib", 225 | ``` 226 | 227 | ### Jest, ts-jest 228 | ```js 229 | module.exports = { 230 | // [...] 231 | globals: { 232 | 'ts-jest': { 233 | compiler: 'ttypescript' 234 | } 235 | } 236 | }; 237 | ``` 238 | or in `package.json` 239 | ```json 240 | { 241 | "jest": { 242 | "globals": { 243 | "ts-jest": { 244 | "compiler": "ttypescript" 245 | } 246 | } 247 | } 248 | } 249 | ``` 250 | 251 | ## Transformers 252 | 253 | You can use transformers written in ts or js 254 | 255 | ```ts 256 | // transformer1-module 257 | import * as ts from 'typescript'; 258 | export default function(program: ts.Program, pluginOptions: {}) { 259 | return (ctx: ts.TransformationContext) => { 260 | return (sourceFile: ts.SourceFile) => { 261 | function visitor(node: ts.Node): ts.Node { 262 | // if (ts.isCallExpression(node)) { 263 | // return ts.createLiteral('call'); 264 | // } 265 | return ts.visitEachChild(node, visitor, ctx); 266 | } 267 | return ts.visitEachChild(sourceFile, visitor, ctx); 268 | }; 269 | }; 270 | } 271 | 272 | ``` 273 | 274 | Examples of transformers: 275 | 276 | [`{ "transform": "ts-nameof", type: "raw"}`](https://github.com/dsherret/ts-nameof) 277 | 278 | [`{ "transform": "ts-optchain/transform" }`](https://github.com/rimeto/ts-optchain) 279 | 280 | [`{ "transform": "ts-transform-asset" }`](https://github.com/slune-org/ts-transform-asset) 281 | 282 | [`{ "transform": "ts-transform-auto-require" }`](https://github.com/slune-org/ts-transform-auto-require) 283 | 284 | [`{ "transform": "ts-transform-css-modules/dist/transform", type: "config" }`](https://github.com/longlho/ts-transform-css-modules) 285 | 286 | [`{ "transform": "ts-transform-graphql-tag/dist/transformer" }`](https://github.com/firede/ts-transform-graphql-tag) 287 | 288 | [`{ "transform": "ts-transform-img/dist/transform", type: "config" }`](https://github.com/longlho/ts-transform-img) 289 | 290 | [`{ "transform": "ts-transform-react-intl/dist/transform", import: "transform", type: "config" }`](https://github.com/longlho/ts-transform-react-intl) 291 | 292 | [`{ "transform": "ts-transformer-enumerate/transformer" }`](https://github.com/kimamula/ts-transformer-enumerate) 293 | 294 | [`{ "transform": "ts-transformer-keys/transformer" }`](https://github.com/kimamula/ts-transformer-keys) 295 | 296 | [`{ "transform": "ts-transformer-minify-privates" }`](https://github.com/timocov/ts-transformer-minify-privates) 297 | 298 | [`{ "transform": "typescript-is/lib/transform-inline/transformer" }`](https://github.com/woutervh-/typescript-is) 299 | 300 | [`{ "transform": "typescript-plugin-styled-components", type: "config" }`](https://github.com/Igorbek/typescript-plugin-styled-components#ttypescript-compiler) 301 | 302 | [`{ "transform": "typescript-transform-jsx" }`](https://github.com/LeDDGroup/typescript-transform-jsx) 303 | 304 | [`{ "transform": "typescript-transform-macros" }`](https://github.com/LeDDGroup/typescript-transform-macros) 305 | 306 | [`{ "transform": "typescript-transform-paths" }`](https://github.com/LeDDGroup/typescript-transform-paths) 307 | 308 | [`{ "transform": "@zerollup/ts-transform-paths" }`](https://github.com/zerkalica/zerollup/tree/master/packages/ts-transform-paths) 309 | 310 | [`{ "transform": "@zoltu/typescript-transformer-append-js-extension" }`](https://github.com/Zoltu/typescript-transformer-append-js-extension) 311 | 312 | [`{ "transform": "@magic-works/ttypescript-browser-like-import-transformer" }`](https://github.com/Jack-Works/ttypescript-browser-like-import-transformer) 313 | 314 | [`{ "transform": "typescript-transform-react-jsx-source" }`](https://github.com/alexgorbatchev/typescript-transform-react-jsx-source) 315 | 316 | [`{ "transform": "ts-transformer-remove-named-export" }`](https://github.com/dominguezcelada/ts-transformer-remove-named-export) 317 | 318 | [`{ "transform": "ts-transformer-export-default-name" }`](https://github.com/jirutka/ts-transformer-export-default-name) 319 | 320 | [`{ "transform": "ts-transformer-inline-file/transformer" }`](https://github.com/jirutka/ts-transformer-inline-file) 321 | 322 | [`{ "transform": "ts-transformer-strip-const-enums", "entrySourceFiles": ["./src/index.ts" }`](https://github.com/timocov/ts-transformer-strip-const-enums) 323 | 324 | [`{ "transform": "ts-transformer-properties-rename", "entrySourceFiles": ["./src/index.ts"] }`](https://github.com/timocov/ts-transformer-properties-rename) 325 | 326 | [`{ "transform": "tsc-progress", "name": "TSC", "color": "green" }`](https://github.com/JiangWeixian/tsc-progress) 327 | 328 | [Tutorial how to write a typescript transformer](https://dev.doctorevidence.com/how-to-write-a-typescript-transform-plugin-fc5308fdd943) 329 | 330 | - [Tutorial how to write a typescript transformer](https://dev.doctorevidence.com/how-to-write-a-typescript-transform-plugin-fc5308fdd943) 331 | - [Transformer framework](https://github.com/slune-org/simple-ts-transform) 332 | - [Unit testing transformer compiler](https://github.com/slune-org/ts-transform-test-compiler) 333 | 334 | ## Example 335 | An example project is in the [example](./packages/ttypescript-examples) directory 336 | 337 | ## License 338 | MIT License 339 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json"], 3 | moduleNameMapper: { 4 | "^ttypescript/lib/(.*?)$": "/packages/ttypescript/src/$1", 5 | "^ttypescript$": "/packages/ttypescript/src/typescript", 6 | "^ttypescript-(.*?)$": "/packages/ttypescript-$1/src" 7 | }, 8 | rootDir: __dirname, 9 | testURL: 'http://localhost', 10 | testMatch: [ 11 | "/packages/*/__tests__/**/*spec.@(js|ts)?(x)", 12 | ], 13 | transform: { 14 | "^.+\\.tsx?$": "ts-jest" 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "lerna": "2.11.0", 3 | "command": { 4 | "init": { 5 | "exact": true 6 | }, 7 | "publish": { 8 | "message": "chore(release): %s [ci skip]" 9 | } 10 | }, 11 | "packages": [ 12 | "packages/*" 13 | ], 14 | "version": "1.5.5" 15 | } 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ttypescript-build", 3 | "version": "1.4.1", 4 | "description": "Over TypeScript tool to use custom transformers in the tsconfig.json", 5 | "private": true, 6 | "scripts": { 7 | "clean": "rm -rf packages/*/dist packages/*/lib", 8 | "clean.all": "lerna clean --yes && rm -rf package-lock.json packages/*/package-lock.json node_modules packages/*/lib packages/*/dist", 9 | "debug": "node --inspect-brk ./node_modules/jest/bin/jest --runInBand --no-cache --no-watchman", 10 | "postinstall": "lerna bootstrap --hoist", 11 | "build": "lerna exec --scope=ttypescript -- tsc", 12 | "test": "npm run build && jest", 13 | "test.watch": "npm run build && jest --watch", 14 | "publish": "npm test && lerna publish --conventional-commits --yes" 15 | }, 16 | "author": "cevek", 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/cevek/ttypescript.git" 20 | }, 21 | "homepage": "https://github.com/cevek/ttypescript/#readme", 22 | "license": "MIT", 23 | "devDependencies": { 24 | "@types/jest": ">=23.3.11", 25 | "@types/node": ">=10.12.18", 26 | "@types/resolve": ">=0.0.8", 27 | "jest": ">=23.6.0", 28 | "lerna": ">=3.8.1", 29 | "ts-jest": ">=23.10.5", 30 | "ts-nameof": ">=2.0.0", 31 | "ts-node": ">=8.0.2", 32 | "ts-transform-css-modules": ">=0.3.3", 33 | "ts-transform-img": ">=0.3.2", 34 | "ts-transform-react-intl": ">=0.4.0", 35 | "ts-transformer-enumerate": ">=0.3.1", 36 | "ts-transformer-keys": ">=0.3.4", 37 | "typescript": ">=3.2.2" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/parcel-plugin/TTypescriptAsset.js: -------------------------------------------------------------------------------- 1 | const vm = require('vm'); 2 | const { createRequire, createRequireFromPath } = require('module'); 3 | const tsAssetPath = require.resolve('parcel-bundler/src/assets/TypeScriptAsset.js'); 4 | const original = require('fs').readFileSync(tsAssetPath, 'utf8'); 5 | const patchedAsset = original.replace(/'typescript'/g, "'ttypescript'").replace(/this\.relativeName/g, 'this.name'); 6 | const factory = vm.runInThisContext( 7 | `(function (exports, require, module, __filename, __dirname) {${patchedAsset}\n});`, 8 | { filename: this.filename, lineOffset: 0, displayErrors: true } 9 | ); 10 | const _require = createRequire ? createRequire(tsAssetPath) : createRequireFromPath(tsAssetPath); 11 | factory(module.exports, _require, module, __filename, __dirname); 12 | -------------------------------------------------------------------------------- /packages/parcel-plugin/index.js: -------------------------------------------------------------------------------- 1 | module.exports = (bundler) => { 2 | bundler.addAssetType('ts', require.resolve('./TTypescriptAsset')); 3 | bundler.addAssetType('tsx', require.resolve('./TTypescriptAsset')); 4 | }; 5 | -------------------------------------------------------------------------------- /packages/parcel-plugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "parcel-plugin-ttypescript", 3 | "version": "1.0.2", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": {}, 7 | "keywords": [], 8 | "author": "", 9 | "license": "ISC", 10 | "peerDependencies": { 11 | "parcel-bundler": "*" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/ttypescript-examples/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | 7 | ## [1.5.3](https://github.com/cevek/ttypescript/compare/v1.5.2...v1.5.3) (2018-07-31) 8 | 9 | 10 | 11 | 12 | **Note:** Version bump only for package ttypescript-examples 13 | 14 | 15 | ## [1.5.2](https://github.com/cevek/ttypescript/compare/v1.5.1...v1.5.2) (2018-07-30) 16 | 17 | 18 | 19 | 20 | **Note:** Version bump only for package ttypescript-examples 21 | 22 | 23 | ## [1.5.1](https://github.com/cevek/ttypescript/compare/v1.5.0...v1.5.1) (2018-07-30) 24 | 25 | 26 | 27 | 28 | **Note:** Version bump only for package ttypescript-examples 29 | 30 | 31 | # [1.5.0](https://github.com/cevek/ttypescript/compare/v1.4.6...v1.5.0) (2018-07-30) 32 | 33 | 34 | 35 | 36 | **Note:** Version bump only for package ttypescript-examples 37 | 38 | 39 | ## [1.4.6](https://github.com/cevek/ttypescript/compare/v1.4.5...v1.4.6) (2018-06-01) 40 | 41 | 42 | ### Bug Fixes 43 | 44 | * Remove dev deps from packages ([7255e11](https://github.com/cevek/ttypescript/commit/7255e11)) 45 | 46 | 47 | 48 | 49 | 50 | ## [1.4.5](https://github.com/cevek/ttypescript/compare/v1.4.4...v1.4.5) (2018-06-01) 51 | 52 | 53 | ### Bug Fixes 54 | 55 | * Add support ts 2.9 ([1a738f7](https://github.com/cevek/ttypescript/commit/1a738f7)) 56 | 57 | 58 | 59 | 60 | 61 | ## [1.4.4](https://github.com/cevek/ttypescript/compare/v1.4.3...v1.4.4) (2018-05-31) 62 | 63 | 64 | 65 | 66 | **Note:** Version bump only for package ttypescript-examples 67 | 68 | 69 | ## [1.4.3](https://github.com/cevek/ttypescript/compare/v1.4.2...v1.4.3) (2018-05-17) 70 | 71 | 72 | 73 | 74 | **Note:** Version bump only for package ttypescript-examples 75 | 76 | 77 | ## [1.4.2](https://github.com/cevek/ttypescript/compare/v1.4.1...v1.4.2) (2018-05-17) 78 | 79 | 80 | 81 | 82 | **Note:** Version bump only for package ttypescript-examples 83 | 84 | 85 | ## [1.4.1](https://github.com/cevek/ttypescript/compare/v1.4.0...v1.4.1) (2018-05-15) 86 | 87 | 88 | 89 | 90 | **Note:** Version bump only for package ttypescript-examples 91 | -------------------------------------------------------------------------------- /packages/ttypescript-examples/README.md: -------------------------------------------------------------------------------- 1 | # ttypescript-examples 2 | 3 | ## Install 4 | ``` 5 | npm i ttypescript -D 6 | ``` 7 | 8 | ## Run 9 | ``` 10 | npx ts-node -C ttypescript test.ts 11 | ``` 12 | Or 13 | ``` 14 | npx ttsc 15 | ``` 16 | -------------------------------------------------------------------------------- /packages/ttypescript-examples/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ttypescript-examples", 3 | "version": "1.5.3", 4 | "description": "TTypescript examples", 5 | "scripts": { 6 | "build": "cd src && ts-node -C ttypescript test.ts", 7 | "build.ttsc": "cd src && ttsc" 8 | }, 9 | "private": true, 10 | "author": "cevek", 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/cevek/ttypescript.git" 14 | }, 15 | "license": "MIT", 16 | "homepage": "https://github.com/cevek/ttypescript/packages/ttypescript-examples/#readme", 17 | "keywords": [ 18 | "typescript", 19 | "wrapper", 20 | "ttypescript", 21 | "examples", 22 | "plugin", 23 | "config" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /packages/ttypescript-examples/src/gen-transpiled.js: -------------------------------------------------------------------------------- 1 | const ts = require('ttypescript') 2 | const fs = require('fs') 3 | const path = require('path') 4 | 5 | const res = ts.transpileModule( 6 | fs.readFileSync(path.join(__dirname, 'test.ts')).toString(), 7 | require('./tsconfig.json') 8 | ) 9 | 10 | console.log(res.outputText) 11 | -------------------------------------------------------------------------------- /packages/ttypescript-examples/src/test.ts: -------------------------------------------------------------------------------- 1 | const a = { b: 1 }; 2 | declare function safely(a: any): void; 3 | 4 | function abc() { 5 | const c = safely(a.b); 6 | } 7 | console.log(abc.toString()); 8 | -------------------------------------------------------------------------------- /packages/ttypescript-examples/src/transformers/transformer.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | 3 | export interface MyPluginOptions { 4 | some?: string 5 | } 6 | 7 | export default function myTransformerPlugin(program: ts.Program, opts: MyPluginOptions) { 8 | return { 9 | before(ctx: ts.TransformationContext) { 10 | return (sourceFile: ts.SourceFile) => { 11 | function visitor(node: ts.Node): ts.Node { 12 | if (ts.isCallExpression(node) && node.expression.getText() === 'safely') { 13 | const target = node.arguments[0] 14 | if (ts.isPropertyAccessExpression(target)) { 15 | return ts.createBinary( 16 | target.expression, 17 | ts.SyntaxKind.AmpersandAmpersandToken, 18 | target 19 | ) 20 | } 21 | } 22 | return ts.visitEachChild(node, visitor, ctx) 23 | } 24 | return ts.visitEachChild(sourceFile, visitor, ctx) 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/ttypescript-examples/src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "declaration": true, 5 | "module": "commonjs", 6 | "strict": true, 7 | "outDir": "../dist", 8 | "plugins": [ 9 | { 10 | "transform": "./transformers/transformer.ts" 11 | } 12 | ] 13 | }, 14 | "exclude": ["transformers/**/*"] 15 | } 16 | -------------------------------------------------------------------------------- /packages/ttypescript-examples/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "declaration": true, 5 | "module": "commonjs", 6 | "strict": true, 7 | "outDir": "dist", 8 | "plugins": [ 9 | { 10 | "transform": "./src/transformers/transformer.ts" 11 | } 12 | ] 13 | }, 14 | "include": ["src/**/*", "node_modules/ttypescript/**/*"], 15 | "exclude": ["src/transformers/**/*"] 16 | } 17 | -------------------------------------------------------------------------------- /packages/ttypescript/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | -------------------------------------------------------------------------------- /packages/ttypescript/README.md: -------------------------------------------------------------------------------- 1 | 2 | [![npm version](https://badge.fury.io/js/ttypescript.svg)](https://badge.fury.io/js/ttypescript) [![Build Status](https://travis-ci.org/cevek/ttypescript.svg?branch=master)](https://travis-ci.org/cevek/ttypescript) 3 | 4 | # ttypescript 5 | 6 | ## What it is 7 | Currently TypeScript doesn't support custom transformers in the tsconfig.json, but supports it programmatically. 8 | 9 | And there is no way to compile your files using custom transformers using `tsc` command. 10 | 11 | TTypescript (Transformer TypeScript) solves this problem by patching on the fly the compile module to use transformers from `tsconfig.json`. 12 | 13 | 22 | Instead of tsc and tsserver, use ttsc and ttsserver wrappers. This wrappers try to use locally installed typescript first. 23 | 24 | No version lock-ins - typescript used as peer dependency. 25 | 26 | ## How to install 27 | 28 | ``` 29 | npm i ttypescript -D 30 | ``` 31 | 32 | ttypescript uses your installed `typescript` in your `node_modules` 33 | 34 | ## How to use 35 | 36 | ### tsconfig.json 37 | 38 | Set a transformer path to the `tsconfig.json` in `compilerOptions` section `plugin` array: 39 | ``` 40 | { 41 | "compilerOptions": { 42 | "plugins": [ 43 | { "transform": "transformer-module" }, 44 | ] 45 | } 46 | } 47 | ``` 48 | 49 | plugin entries described in `PluginConfig`: 50 | 51 | ```ts 52 | export interface PluginConfig { 53 | /** 54 | * Path to transformer or transformer module name 55 | */ 56 | transform?: string; 57 | 58 | /** 59 | * The optional name of the exported transform plugin in the transform module. 60 | */ 61 | import?: string; 62 | 63 | /** 64 | * Plugin entry point format type, default is program 65 | */ 66 | type?: 'program' | 'config' | 'checker' | 'raw' | 'compilerOptions'; 67 | 68 | /** 69 | * Should transformer applied after all ones 70 | */ 71 | after?: boolean; 72 | 73 | /** 74 | * Should transformer applied for d.ts files, supports from TS2.9 75 | */ 76 | afterDeclarations?: boolean; 77 | /** 78 | * any other properties provided to the transformer as config argument 79 | * */ 80 | [options: string]: any; 81 | } 82 | ``` 83 | 84 | You just need to add the `transform` block with optional `import`, `type`, `after`, `afterDeclarations` and plugin-related options. 85 | 86 | `transform` can accept npm module or local file path (.ts or .js) related to `tsconfig.json` 87 | 88 | ### PluginConfig.type 89 | Because currently transformers can run only programmatically, most of them use factory wrapper with different signatures. 90 | For the possible to work with any of them you can specify `type` in the plugin config. 91 | 92 | By default will be used a `program` type. 93 | 108 | #### program 109 | If the transformer has a factory signature using `program` as first argument: 110 | ```ts 111 | (program: ts.Program, config?: PluginConfig) => ts.TransformerFactory 112 | where 113 | ts.TransformerFactory = (context: ts.TransformationContext) => (sourceFile: ts.SourceFile) => ts.SourceFile 114 | ``` 115 | Plugin config entry: `{ "transform": "transformer-module" }`. 116 | 117 | 118 | #### config 119 | For the signature with transformer's config: 120 | ```ts 121 | (config: PluginConfig) => ts.TransformerFactory 122 | ``` 123 | Plugin config entry: `{ "transform": "transformer-module", type: "config" }`. 124 | 125 | #### checker 126 | For the signature with ts.TypeChecker: 127 | ```ts 128 | (checker: ts.TypeChecker, config?: PluginConfig) => ts.TransformerFactory 129 | ``` 130 | Plugin config entry: `{ "transform": "transformer-module", type: "checker" }`. 131 | 132 | #### raw 133 | For the signature without factory wrapper: 134 | ```ts 135 | ts.TransformerFactory 136 | ``` 137 | Plugin config entry: `{ "transform": "transformer-module", type: "raw" }`. 138 | 139 | #### compilerOptions 140 | ```ts 141 | (compilerOpts: ts.CompilerOptions, config?: PluginConfig) => ts.TransformerFactory 142 | ``` 143 | Plugin config entry: `{ "transform": "transformer-module", type: "compilerOptions" }`. 144 | 145 | ```json 146 | { 147 | "compilerOptions": { 148 | "plugins": [ 149 | { "transform": "transformer-module", "someOption1": 123, "someOption2": 321 }, 150 | { "transform": "./transformers/my-transformer.ts" }, 151 | { "transform": "transformer-module", "after": true }, 152 | { "transform": "transformer-module", "afterDeclarations": true }, 153 | { "transform": "transformer-module", "type": "ls" } 154 | ] 155 | }, 156 | } 157 | ``` 158 | 159 | ### Command line 160 | 161 | Like usual `tsc`, all arguments work the same way. 162 | ``` 163 | ttsc 164 | ``` 165 | 166 | ### ts-node 167 | 168 | ``` 169 | ts-node --compiler ttypescript index.ts 170 | or 171 | ts-node -C ttypescript index.ts 172 | ``` 173 | 174 | ### Parcel 175 | 176 | Just install a parcel plugin 177 | ``` 178 | npm i parcel-plugin-ttypescript 179 | ``` 180 | 181 | 182 | ### Webpack 183 | ```js 184 | { 185 | test: /\.(ts|tsx)$/, 186 | loader: require.resolve('awesome-typescript-loader'), 187 | // or 188 | loader: require.resolve('ts-loader'), 189 | options: { 190 | compiler: 'ttypescript' 191 | } 192 | } 193 | ``` 194 | 195 | ### Rollup 196 | ```js 197 | // rollup.config.js 198 | import ttypescript from 'ttypescript' 199 | import tsPlugin from 'rollup-plugin-typescript2' 200 | 201 | export default { 202 | // ... 203 | plugins: [ 204 | // ... 205 | tsPlugin({ 206 | typescript: ttypescript 207 | }) 208 | ] 209 | } 210 | ``` 211 | 212 | ### VS Code 213 | If you want to compile your project with VS Code task runner you need to overwrite the config `typescript.tsdk` to path of the installed `ttypescript`: 214 | ``` 215 | "typescript.tsdk": "/usr/local/lib/node_modules/ttypescript/lib", 216 | or 217 | "typescript.tsdk": "node_modules/ttypescript/lib", 218 | ``` 219 | 220 | ### Jest, ts-jest 221 | ```js 222 | module.exports = { 223 | // [...] 224 | globals: { 225 | 'ts-jest': { 226 | compiler: 'ttypescript' 227 | } 228 | } 229 | }; 230 | ``` 231 | or in `package.json` 232 | ```json 233 | { 234 | "jest": { 235 | "globals": { 236 | "ts-jest": { 237 | "compiler": "ttypescript" 238 | } 239 | } 240 | } 241 | } 242 | ``` 243 | 244 | ## Transformers 245 | 246 | You can use transformers written in ts or js 247 | 248 | ```ts 249 | // transformer1-module 250 | import * as ts from 'typescript'; 251 | export default function(program: ts.Program, pluginOptions: {}) { 252 | return (ctx: ts.TransformationContext) => { 253 | return (sourceFile: ts.SourceFile) => { 254 | function visitor(node: ts.Node): ts.Node { 255 | // if (ts.isCallExpression(node)) { 256 | // return ts.createLiteral('call'); 257 | // } 258 | return ts.visitEachChild(node, visitor, ctx); 259 | } 260 | return ts.visitEachChild(sourceFile, visitor, ctx); 261 | }; 262 | }; 263 | } 264 | 265 | ``` 266 | 267 | Examples of transformers 268 | 269 | [`{ "transform": "ts-optchain/transform" }`](https://github.com/rimeto/ts-optchain) 270 | 271 | [`{transform: "typescript-is/lib/transform-inline/transformer"}`](https://github.com/woutervh-/typescript-is) 272 | 273 | [`{transform: "ts-transformer-keys/transformer"}`](https://github.com/kimamula/ts-transformer-keys) 274 | 275 | [`{transform: "ts-transformer-enumerate/transformer"}`](https://github.com/kimamula/ts-transformer-enumerate) 276 | 277 | [`{transform: "ts-transform-graphql-tag/dist/transformer"}`](https://github.com/firede/ts-transform-graphql-tag) 278 | 279 | [`{transform: "ts-transform-img/dist/transform", type: "config"}`](https://github.com/longlho/ts-transform-img) 280 | 281 | [`{transform: "ts-transform-css-modules/dist/transform", type: "config"}`](https://github.com/longlho/ts-transform-css-modules) 282 | 283 | [`{transform: "ts-transform-react-intl/dist/transform", import: "transform", type: "config"}`](https://github.com/longlho/ts-transform-react-intl) 284 | 285 | [`{transform: "ts-nameof", type: "raw"}`](https://github.com/dsherret/ts-nameof) 286 | 287 | [`{transform: "typescript-transform-jsx" }`](https://github.com/LeDDGroup/typescript-transform-jsx) 288 | 289 | [`{transform: "typescript-transform-paths" }`](https://github.com/LeDDGroup/typescript-transform-paths) 290 | 291 | [`{transform: "typescript-transform-macros" }`](https://github.com/LeDDGroup/typescript-transform-macros) 292 | 293 | [`{transform: "ts-transformer-minify-privates" }`](https://github.com/timocov/ts-transformer-minify-privates) 294 | 295 | [`{transform: "typescript-plugin-styled-components", type: "config"}`](https://github.com/Igorbek/typescript-plugin-styled-components#ttypescript-compiler) 296 | 297 | [`{ "transform": "@zoltu/typescript-transformer-append-js-extension" }`](https://github.com/Zoltu/typescript-transformer-append-js-extension) 298 | 299 | 300 | [Tutorial how to write a typescript transformer](https://dev.doctorevidence.com/how-to-write-a-typescript-transform-plugin-fc5308fdd943) 301 | 302 | 303 | ## Example 304 | An example project is in the [example](./packages/ttypescript-examples) directory 305 | 306 | ## License 307 | MIT License 308 | -------------------------------------------------------------------------------- /packages/ttypescript/__tests__/PluginCreator.spec.ts: -------------------------------------------------------------------------------- 1 | import { PluginConfig, PluginCreator } from 'ttypescript/lib/PluginCreator'; 2 | import { createTransformers } from './helpers'; 3 | import { advancedTransformer } from './transforms/transform-advanced'; 4 | import { simpleTransformer } from './transforms/transform-simple'; 5 | import * as ts from 'typescript'; 6 | 7 | describe('PluginCreator', () => { 8 | it('should be initialized with empty config', () => { 9 | const pluginCreator = new PluginCreator(ts, []); 10 | 11 | expect(pluginCreator).toBeInstanceOf(PluginCreator); 12 | }); 13 | 14 | it('should throw error if wrong config entry given', () => { 15 | const config = [ 16 | { 17 | someGarbage: 123, 18 | }, 19 | ] as any; 20 | 21 | expect(() => new PluginCreator(ts, config)).toThrow(); 22 | }); 23 | 24 | it('should initialize default transformer in before group', () => { 25 | const config: PluginConfig[] = [ 26 | { 27 | transform: './transforms/transform-simple.ts', 28 | }, 29 | ]; 30 | 31 | expect(createTransformers(config)).toEqual({ 32 | after: [], 33 | afterDeclarations: [], 34 | before: [simpleTransformer], 35 | }); 36 | }); 37 | 38 | it('should initialize default transformer in after group', () => { 39 | const config: PluginConfig[] = [ 40 | { 41 | transform: './transforms/transform-simple.ts', 42 | after: true, 43 | }, 44 | ]; 45 | 46 | expect(createTransformers(config)).toEqual({ 47 | after: [simpleTransformer], 48 | afterDeclarations: [], 49 | before: [], 50 | }); 51 | }); 52 | 53 | it('should initialize advanced transformer in after group', () => { 54 | const config: PluginConfig[] = [ 55 | { 56 | transform: './transforms/transform-advanced.ts', 57 | }, 58 | ]; 59 | 60 | expect(createTransformers(config)).toEqual({ 61 | after: [advancedTransformer], 62 | afterDeclarations: [], 63 | before: [], 64 | }); 65 | }); 66 | it('should provide custom config', () => { 67 | const config: PluginConfig[] = [{ transform: './transforms/transform-advanced.ts', some: 1, bla: 2 } as any]; 68 | 69 | expect(createTransformers(config)).toEqual({ 70 | after: [advancedTransformer], 71 | afterDeclarations: [], 72 | before: [], 73 | }); 74 | }); 75 | }); 76 | -------------------------------------------------------------------------------- /packages/ttypescript/__tests__/assets/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["es2015"], 5 | "module": "commonjs", 6 | "plugins": [{ "transform": "../transforms/before.ts" }] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/ttypescript/__tests__/assets/tsnode.js: -------------------------------------------------------------------------------- 1 | function type() { 2 | return ''; 3 | } 4 | var x = "{ abc: 1; }"; 5 | console.log(x); 6 | -------------------------------------------------------------------------------- /packages/ttypescript/__tests__/assets/tsnode.ts: -------------------------------------------------------------------------------- 1 | 2 | function type() { 3 | return ''; 4 | } 5 | const x = type<{ abc: 1 }>(); 6 | console.log(x); 7 | -------------------------------------------------------------------------------- /packages/ttypescript/__tests__/configs.ts: -------------------------------------------------------------------------------- 1 | import { normalize } from "path"; 2 | 3 | export const configs = { 4 | tsNodePath: normalize(__dirname + '/../../../node_modules/ts-node/dist/bin.js'), 5 | typescriptFromLibPath: normalize(__dirname + '/../lib/typescript.js'), 6 | tscFromLibPath: normalize(__dirname + '/../lib/tsc.js') 7 | } -------------------------------------------------------------------------------- /packages/ttypescript/__tests__/helpers.ts: -------------------------------------------------------------------------------- 1 | import { PluginConfig, PluginCreator } from 'ttypescript/lib/PluginCreator'; 2 | import * as ts from 'typescript'; 3 | 4 | export function createTransformers(config: PluginConfig[]): ts.CustomTransformers { 5 | const pluginCreator = new PluginCreator(ts, config, __dirname); 6 | const host = { program: {} as ts.Program }; 7 | return pluginCreator.createTransformers(host); 8 | } 9 | -------------------------------------------------------------------------------- /packages/ttypescript/__tests__/loadTypeScript/loadTypeScript.spec.ts: -------------------------------------------------------------------------------- 1 | import resolve = require('resolve'); 2 | import path = require('path'); 3 | import vm = require('vm'); 4 | 5 | import { loadTypeScript } from 'ttypescript/lib/loadTypescript'; 6 | 7 | describe('loadTypeScript', () => { 8 | 9 | const originalResolveSync = resolve.sync; 10 | 11 | beforeEach(() => { 12 | jest.spyOn(resolve, 'sync').mockImplementation((id: string, opts?: resolve.SyncOpts) => { 13 | if (opts && opts.basedir === 'mocks') { 14 | return path.join(__dirname, 'mocks', path.basename(id) + '.js'); 15 | } 16 | return originalResolveSync(id, opts); 17 | }); 18 | }); 19 | 20 | afterEach(() => { 21 | jest.restoreAllMocks(); 22 | jest.resetModules(); 23 | }); 24 | 25 | it('throws if module does not exists', () => { 26 | expect(() => { 27 | loadTypeScript('not_existing', { folder: 'mocks' }); 28 | }).toThrow(); 29 | }); 30 | 31 | it('throws if an old version of typescript is used', () => { 32 | expect(() => { 33 | loadTypeScript('oldVersion', { folder: 'mocks' }); 34 | }).toThrow(); 35 | }); 36 | 37 | it('initializes createProgram and has valid versionMajorMinor', () => { 38 | const ts: any = loadTypeScript('simple', { folder: 'mocks' }); 39 | expect(ts.versionMajorMinor).toBe('99.0'); 40 | expect(typeof ts.createProgram).toBe('function'); 41 | }); 42 | 43 | it('can require itself without stack overflow', () => { 44 | const ts: any = loadTypeScript('selfRequire', { folder: 'mocks' }); 45 | expect(ts.versionMajorMinor).toBe('99.0'); 46 | expect(ts.selfRequire.versionMajorMinor).toBe('99.0'); 47 | expect(ts.selfRequire.selfRequire).toBe(ts.selfRequire); 48 | }); 49 | 50 | it('should pass correct arguments', () => { 51 | const ts: any = loadTypeScript('simple', { folder: 'mocks' }); 52 | // expect(ts.args.module).toBeInstanceOf(Module); 53 | expect(typeof ts.args.require).toBe('function'); 54 | expect(ts.args.this).toBe(ts.args.exports); 55 | expect(ts.args.exports).toBe(ts.args.module.exports); 56 | expect(ts.args.__filename).toBe(path.join(__dirname, 'mocks', 'simple.js')); 57 | expect(ts.args.__dirname).toBe(path.join(__dirname, 'mocks')); 58 | }); 59 | 60 | it('returns always a different instance, calling vm.runInThisContext only once', () => { 61 | const runInThisContextSpy = jest.spyOn(vm, 'runInThisContext'); 62 | const ts1: any = loadTypeScript('runInThisContextOnce', { folder: 'mocks' }); 63 | const ts2: any = loadTypeScript('runInThisContextOnce', { folder: 'mocks' }); 64 | expect(ts1.versionMajorMinor).toBe('100.0'); 65 | expect(ts2.versionMajorMinor).toBe('100.0'); 66 | expect(ts1 !== ts2).toBe(true); 67 | expect(runInThisContextSpy).toBeCalledTimes(1); 68 | }); 69 | 70 | it('does not alter standard require pristine typescript', () => { 71 | loadTypeScript('simple', { folder: 'mocks' }); 72 | const required = require(path.join(__dirname, 'mocks', 'simple.js')); 73 | expect(required.createProgram).toBeUndefined(); 74 | }); 75 | 76 | it('registers a pristine lazy module in require.cache, running vm.runInThisContext only once', () => { 77 | const runInThisContextSpy = jest.spyOn(vm, 'runInThisContext'); 78 | const ts: any = loadTypeScript('requireCache', { folder: 'mocks' }); 79 | const cached = ts.cachedModule; 80 | // expect(cached).toBeInstanceOf(Module); 81 | expect(cached.exports.versionMajorMinor).toBe('99.0'); 82 | expect(cached.loaded).toBe(true); 83 | expect(cached.exports).toBe(cached.exports); 84 | expect(cached.exports.createProgram).toBeUndefined(); 85 | expect(runInThisContextSpy).toBeCalledTimes(1); 86 | }); 87 | }); 88 | -------------------------------------------------------------------------------- /packages/ttypescript/__tests__/loadTypeScript/mocks/oldVersion.js: -------------------------------------------------------------------------------- 1 | exports.versionMajorMinor = '1.1'; 2 | -------------------------------------------------------------------------------- /packages/ttypescript/__tests__/loadTypeScript/mocks/requireCache.js: -------------------------------------------------------------------------------- 1 | exports.versionMajorMinor = '99.0'; 2 | exports.cachedModule = require.cache[__filename]; 3 | -------------------------------------------------------------------------------- /packages/ttypescript/__tests__/loadTypeScript/mocks/runInThisContextOnce.js: -------------------------------------------------------------------------------- 1 | exports.versionMajorMinor = '100.0'; 2 | -------------------------------------------------------------------------------- /packages/ttypescript/__tests__/loadTypeScript/mocks/selfRequire.js: -------------------------------------------------------------------------------- 1 | exports.versionMajorMinor = '99.0'; 2 | exports.selfRequire = module.require(__filename); 3 | -------------------------------------------------------------------------------- /packages/ttypescript/__tests__/loadTypeScript/mocks/simple.js: -------------------------------------------------------------------------------- 1 | exports.versionMajorMinor = '99.0'; 2 | exports.args = { 'this':this, exports, require, module, __filename, __dirname }; 3 | -------------------------------------------------------------------------------- /packages/ttypescript/__tests__/transforms/before.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | export default function(program: ts.Program) { 3 | const checker = program.getTypeChecker(); 4 | return (ctx: ts.TransformationContext) => (sourceFile: ts.SourceFile) => { 5 | function visitor(node: ts.Node): ts.Node { 6 | if ( 7 | ts.isCallExpression(node) && 8 | ts.isIdentifier(node.expression) && 9 | node.expression.getText() === 'type' && 10 | node.typeArguments && 11 | node.typeArguments[0] 12 | ) { 13 | const type = checker.getTypeFromTypeNode(node.typeArguments[0]); 14 | return ts.createLiteral(checker.typeToString(type)); 15 | } 16 | return ts.visitEachChild(node, visitor, ctx); 17 | } 18 | return ts.visitEachChild(sourceFile, visitor, ctx); 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /packages/ttypescript/__tests__/transforms/safely.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | 3 | export interface MyPluginOptions { 4 | some?: string 5 | } 6 | 7 | export default function myTransformerPlugin(program: ts.Program, opts?: MyPluginOptions) { 8 | return { 9 | before(ctx: ts.TransformationContext) { 10 | return (sourceFile: ts.SourceFile) => { 11 | function visitor(node: ts.Node): ts.Node { 12 | if (ts.isCallExpression(node) && node.expression.getText() === 'safely') { 13 | const target = node.arguments[0] 14 | if (ts.isPropertyAccessExpression(target)) { 15 | return ts.createBinary( 16 | target.expression, 17 | ts.SyntaxKind.AmpersandAmpersandToken, 18 | target 19 | ) 20 | } 21 | } 22 | return ts.visitEachChild(node, visitor, ctx) 23 | } 24 | return ts.visitEachChild(sourceFile, visitor, ctx) 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/ttypescript/__tests__/transforms/transform-advanced.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript' 2 | 3 | export function advancedTransformer(ctx: ts.TransformationContext) { 4 | return (sourceFile: ts.SourceFile) => { 5 | return sourceFile 6 | } 7 | } 8 | 9 | export default function transform1(program: ts.Program) { 10 | return { 11 | after: advancedTransformer 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/ttypescript/__tests__/transforms/transform-simple.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript' 2 | 3 | export function simpleTransformer(ctx: ts.TransformationContext) { 4 | return (sourceFile: ts.SourceFile) => { 5 | return sourceFile 6 | } 7 | } 8 | 9 | export default function transform1(program: ts.Program) { 10 | return simpleTransformer 11 | } 12 | -------------------------------------------------------------------------------- /packages/ttypescript/__tests__/transforms/transformers.ts: -------------------------------------------------------------------------------- 1 | export const transformers = { 2 | before: { 3 | transform: __dirname + '/before.ts', 4 | source: `declare function type(): string; var x = type<{ abc: 1 }>();`, 5 | out: `var x = "{ abc: 1 }"\n` 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /packages/ttypescript/__tests__/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "noEmit": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/ttypescript/__tests__/tsnode.spec.ts: -------------------------------------------------------------------------------- 1 | import { execSync } from 'child_process'; 2 | import { configs } from './configs'; 3 | 4 | describe('ts-node', () => { 5 | it('should transform', () => { 6 | const result = execSync('node ' + configs.tsNodePath + ' -C ' + configs.typescriptFromLibPath + ' tsnode.ts', { 7 | cwd: __dirname + '/assets/', 8 | maxBuffer: 1e8, 9 | }); 10 | expect(result.toString()).toBe('{ abc: 1; }\n'); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /packages/ttypescript/__tests__/tsplugins.spec.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'ttypescript'; 2 | 3 | describe('tsplugins', () => { 4 | it('should skip ts plugin without errors', () => { 5 | const res = ts.transpileModule('var a = 1;', { 6 | compilerOptions: { 7 | plugins: [{ name: 'foobar' }], 8 | }, 9 | }); 10 | expect(res.outputText).toBe('var a = 1;\n'); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /packages/ttypescript/__tests__/ttsc.spec.ts: -------------------------------------------------------------------------------- 1 | import { execSync } from 'child_process'; 2 | import { readFileSync } from 'fs'; 3 | import { configs } from './configs'; 4 | 5 | const expectCode = ` 6 | function type() { 7 | return ''; 8 | } 9 | var x = "{ abc: 1; }"; 10 | console.log(x); 11 | `; 12 | describe('ttsc', () => { 13 | it('should transform code', () => { 14 | execSync('node ' + configs.tscFromLibPath, { 15 | cwd: __dirname + '/assets/', 16 | maxBuffer: 1e8, 17 | }); 18 | expect(readFileSync(__dirname + '/assets/tsnode.js', 'utf8').trim()).toBe(expectCode.trim()); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /packages/ttypescript/__tests__/typescript.spec.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as path from 'path'; 3 | import * as ts from 'ttypescript'; 4 | const exampleDir = path.resolve(__dirname, '..', '..', 'ttypescript-examples', 'src'); 5 | 6 | describe('typescript', () => { 7 | it('should apply transformer from legacy config', () => { 8 | const content = fs.readFileSync(path.join(exampleDir, 'test.ts')).toString(); 9 | const res = ts.transpileModule(content, { 10 | compilerOptions: { 11 | plugins: [ 12 | { 13 | customTransformers: { 14 | before: [__dirname + '/transforms/safely.ts'], 15 | }, 16 | }, 17 | ] as any, 18 | }, 19 | }); 20 | 21 | const result = `var a = { b: 1 }; 22 | function abc() { 23 | var c = a && a.b; 24 | } 25 | console.log(abc.toString()); 26 | `; 27 | 28 | expect(res.outputText).toEqual(result); 29 | }); 30 | 31 | it('should apply transformer from default config', () => { 32 | const content = fs.readFileSync(path.join(exampleDir, 'test.ts')).toString(); 33 | 34 | const res = ts.transpileModule(content, { 35 | compilerOptions: { 36 | plugins: [ 37 | { 38 | transform: __dirname + '/transforms/safely.ts', 39 | }, 40 | ] as any, 41 | }, 42 | }); 43 | 44 | const result = `var a = { b: 1 }; 45 | function abc() { 46 | var c = a && a.b; 47 | } 48 | console.log(abc.toString()); 49 | `; 50 | 51 | expect(res.outputText).toEqual(result); 52 | }); 53 | 54 | it('should merge transformers', () => { 55 | const content = fs.readFileSync(path.join(exampleDir, 'test.ts')).toString(); 56 | const customTransformer = jest.fn(sf => sf) 57 | 58 | const res = ts.transpileModule(content, { 59 | compilerOptions: { 60 | plugins: [ 61 | { 62 | transform: __dirname + '/transforms/safely.ts', 63 | }, 64 | ] as any, 65 | }, 66 | transformers: { 67 | before: [() => customTransformer] 68 | }, 69 | }); 70 | 71 | const result = `var a = { b: 1 }; 72 | function abc() { 73 | var c = a && a.b; 74 | } 75 | console.log(abc.toString()); 76 | `; 77 | 78 | expect(res.outputText).toEqual(result); 79 | expect(customTransformer).toHaveBeenCalled() 80 | }); 81 | 82 | it('should run 3rd party transformers', () => { 83 | const res = ts.transpileModule('var x = 1;', { 84 | compilerOptions: { 85 | plugins: [ 86 | { transform: 'ts-transformer-keys/transformer' }, 87 | { transform: 'ts-transformer-enumerate/transformer' }, 88 | // { transform: 'ts-transform-graphql-tag/dist/transformer' }, 89 | { transform: 'ts-transform-img/dist/transform', type: 'config' }, 90 | { transform: 'ts-transform-css-modules/dist/transform', type: 'config' }, 91 | { transform: 'ts-transform-react-intl/dist/transform', type: 'config', import: "transform" }, 92 | { transform: 'ts-nameof', type: 'raw' }, 93 | ] as any, 94 | }, 95 | }); 96 | 97 | const result = `var x = 1;\n`; 98 | expect(res.outputText).toEqual(result); 99 | }); 100 | }); 101 | -------------------------------------------------------------------------------- /packages/ttypescript/bin/tsc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | require('../lib/tsc.js') 3 | -------------------------------------------------------------------------------- /packages/ttypescript/bin/tsserver: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | require('../lib/tsserver.js') 3 | -------------------------------------------------------------------------------- /packages/ttypescript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ttypescript", 3 | "bin": { 4 | "ttsc": "./bin/tsc", 5 | "ttsserver": "./bin/tsserver" 6 | }, 7 | "version": "1.5.15", 8 | "description": "Over TypeScript tool to use custom transformers in the tsconfig.json", 9 | "main": "lib/typescript.js", 10 | "files": [ 11 | "*.md", 12 | "bin", 13 | "lib", 14 | "ts-node-config" 15 | ], 16 | "author": "cevek", 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/cevek/ttypescript.git" 20 | }, 21 | "homepage": "https://github.com/cevek/ttypescript/tree/master/packages/ttypescript", 22 | "keywords": [ 23 | "typescript", 24 | "wrapper", 25 | "transform", 26 | "transformer", 27 | "plugin", 28 | "config" 29 | ], 30 | "license": "MIT", 31 | "dependencies": { 32 | "resolve": ">=1.9.0" 33 | }, 34 | "devDependencies": { 35 | "ts-node": ">=8.0.2", 36 | "typescript": ">=3.2.2" 37 | }, 38 | "peerDependencies": { 39 | "ts-node": ">=8.0.2", 40 | "typescript": ">=3.2.2" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/ttypescript/src/PluginCreator.ts: -------------------------------------------------------------------------------- 1 | import * as resolve from 'resolve'; 2 | import * as ts from 'typescript'; 3 | import { inspect } from 'util'; 4 | import { addDiagnosticFactory } from './patchCreateProgram'; 5 | 6 | export interface PluginConfig { 7 | /** 8 | * Language Server TypeScript Plugin name 9 | */ 10 | name?: string; 11 | /** 12 | * Path to transformer or transformer module name 13 | */ 14 | transform?: string; 15 | 16 | /** 17 | * The optional name of the exported transform plugin in the transform module. 18 | */ 19 | import?: string; 20 | 21 | /** 22 | * Plugin entry point format type, default is program 23 | */ 24 | type?: 'ls' | 'program' | 'config' | 'checker' | 'raw' | 'compilerOptions'; 25 | 26 | /** 27 | * Should transformer applied after all ones 28 | */ 29 | after?: boolean; 30 | 31 | /** 32 | * Should transformer applied for d.ts files, supports from TS2.9 33 | */ 34 | afterDeclarations?: boolean; 35 | } 36 | 37 | export interface TransformerBasePlugin { 38 | before?: ts.TransformerFactory; 39 | after?: ts.TransformerFactory; 40 | afterDeclarations?: ts.TransformerFactory; 41 | } 42 | export type TransformerList = Required; 43 | 44 | export type TransformerPlugin = TransformerBasePlugin | ts.TransformerFactory; 45 | 46 | export type LSPattern = (ls: ts.LanguageService, config: {}) => TransformerPlugin; 47 | export type ProgramPattern = ( 48 | program: ts.Program, 49 | config: {}, 50 | helpers?: { ts: typeof ts; addDiagnostic: (diag: ts.Diagnostic) => void } 51 | ) => TransformerPlugin; 52 | export type CompilerOptionsPattern = (compilerOpts: ts.CompilerOptions, config: {}) => TransformerPlugin; 53 | export type ConfigPattern = (config: {}) => TransformerPlugin; 54 | export type TypeCheckerPattern = (checker: ts.TypeChecker, config: {}) => TransformerPlugin; 55 | export type RawPattern = ( 56 | context: ts.TransformationContext, 57 | program: ts.Program, 58 | config: {} 59 | ) => ts.Transformer; 60 | export type PluginFactory = 61 | | LSPattern 62 | | ProgramPattern 63 | | ConfigPattern 64 | | CompilerOptionsPattern 65 | | TypeCheckerPattern 66 | | RawPattern; 67 | 68 | function createTransformerFromPattern({ 69 | typescript, 70 | factory, 71 | config, 72 | program, 73 | ls, 74 | }: { 75 | typescript: typeof ts; 76 | factory: PluginFactory; 77 | config: PluginConfig; 78 | program: ts.Program; 79 | ls?: ts.LanguageService; 80 | }): TransformerBasePlugin { 81 | const { transform, after, afterDeclarations, name, type, ...cleanConfig } = config; 82 | if (!transform) throw new Error('Not a valid config entry: "transform" key not found'); 83 | let ret: TransformerPlugin; 84 | switch (config.type) { 85 | case 'ls': 86 | if (!ls) throw new Error(`Plugin ${transform} need a LanguageService`); 87 | ret = (factory as LSPattern)(ls, cleanConfig); 88 | break; 89 | case 'config': 90 | ret = (factory as ConfigPattern)(cleanConfig); 91 | break; 92 | case 'compilerOptions': 93 | ret = (factory as CompilerOptionsPattern)(program.getCompilerOptions(), cleanConfig); 94 | break; 95 | case 'checker': 96 | ret = (factory as TypeCheckerPattern)(program.getTypeChecker(), cleanConfig); 97 | break; 98 | case undefined: 99 | case 'program': 100 | ret = (factory as ProgramPattern)(program, cleanConfig, { 101 | ts: typescript, 102 | addDiagnostic: addDiagnosticFactory(program), 103 | }); 104 | break; 105 | case 'raw': 106 | ret = (ctx: ts.TransformationContext) => (factory as RawPattern)(ctx, program, cleanConfig); 107 | break; 108 | default: 109 | return never(config.type); 110 | } 111 | if (typeof ret === 'function') { 112 | if (after) return { after: ret }; 113 | else if (afterDeclarations) { 114 | return { afterDeclarations: ret as ts.TransformerFactory }; 115 | } else return { before: ret }; 116 | } 117 | return ret; 118 | } 119 | 120 | function never(n: never): never { 121 | throw new Error('Unexpected type: ' + n); 122 | } 123 | 124 | let tsNodeIncluded = false; 125 | // to fix recursion bug, see usage below 126 | const requireStack: string[] = []; 127 | /** 128 | * @example 129 | * 130 | * new PluginCreator([ 131 | * {transform: '@zerollup/ts-transform-paths', someOption: '123'}, 132 | * {transform: '@zerollup/ts-transform-paths', type: 'ls', someOption: '123'}, 133 | * {transform: '@zerollup/ts-transform-paths', type: 'ls', after: true, someOption: '123'} 134 | * ]).createTransformers({ program }) 135 | */ 136 | export class PluginCreator { 137 | constructor( 138 | private typescript: typeof ts, 139 | private configs: PluginConfig[], 140 | private resolveBaseDir: string = process.cwd() 141 | ) { 142 | this.validateConfigs(configs); 143 | } 144 | 145 | mergeTransformers(into: TransformerList, source: ts.CustomTransformers | TransformerBasePlugin) { 146 | const slice = (input: T | T[]) => (Array.isArray(input) ? input.slice() : [input]); 147 | if (source.before) { 148 | into.before.push(...slice(source.before)); 149 | } 150 | if (source.after) { 151 | into.after.push(...slice(source.after)); 152 | } 153 | if (source.afterDeclarations) { 154 | into.afterDeclarations.push(...slice(source.afterDeclarations)); 155 | } 156 | return this; 157 | } 158 | 159 | createTransformers( 160 | params: { program: ts.Program } | { ls: ts.LanguageService }, 161 | customTransformers?: ts.CustomTransformers 162 | ) { 163 | const chain: TransformerList = { 164 | before: [], 165 | after: [], 166 | afterDeclarations: [], 167 | }; 168 | let ls; 169 | let program; 170 | if ('ls' in params) { 171 | ls = params.ls; 172 | program = ls.getProgram()!; 173 | } else { 174 | program = params.program; 175 | } 176 | for (const config of this.configs) { 177 | if (!config.transform) { 178 | continue; 179 | } 180 | const factory = this.resolveFactory(config.transform, config.import); 181 | // if recursion 182 | if (factory === undefined) continue; 183 | const transformer = createTransformerFromPattern({ 184 | typescript: this.typescript, 185 | factory, 186 | config, 187 | program, 188 | ls, 189 | }); 190 | this.mergeTransformers(chain, transformer); 191 | } 192 | 193 | // if we're given some custom transformers, they must be chained at the end 194 | if (customTransformers) { 195 | this.mergeTransformers(chain, customTransformers); 196 | } 197 | 198 | return chain; 199 | } 200 | 201 | private resolveFactory(transform: string, importKey: string = 'default'): PluginFactory | undefined { 202 | if ( 203 | !tsNodeIncluded && 204 | transform.match(/\.tsx?$/) && 205 | (module.parent!.parent === null || 206 | module.parent!.parent!.parent === null || 207 | module.parent!.parent!.parent!.id.split(/[\/\\]/).indexOf('ts-node') === -1) 208 | ) { 209 | require('ts-node').register({ 210 | transpileOnly: true, 211 | skipProject: true, 212 | compilerOptions: { 213 | target: 'ES2018', 214 | jsx: 'react', 215 | esModuleInterop: true, 216 | module: 'commonjs', 217 | }, 218 | }); 219 | tsNodeIncluded = true; 220 | } 221 | 222 | const modulePath = resolve.sync(transform, { basedir: this.resolveBaseDir }); 223 | // in ts-node occurs error cause recursion: 224 | // ts-node file.ts -> createTransformers -> require transformer.ts 225 | // -> createTransformers -> require transformer.ts -> ... 226 | // this happens cause ts-node uses to compile transformers the same config included this transformer 227 | // so this stack checks that if we already required this file we are in the reqursion 228 | if (requireStack.indexOf(modulePath) > -1) return; 229 | 230 | requireStack.push(modulePath); 231 | const commonjsModule: PluginFactory | { [key: string]: PluginFactory } = require(modulePath); 232 | requireStack.pop(); 233 | 234 | const factoryModule = typeof commonjsModule === 'function' ? { default: commonjsModule } : commonjsModule; 235 | 236 | const factory = factoryModule[importKey]; 237 | if (!factory) { 238 | throw new Error( 239 | `tsconfig.json > plugins: "${transform}" does not have an export "${importKey}": ` + 240 | inspect(factoryModule) 241 | ); 242 | } 243 | 244 | if (typeof factory !== 'function') { 245 | throw new Error( 246 | `tsconfig.json > plugins: "${transform}" export "${importKey}" is not a plugin: "${inspect(factory)}"` 247 | ); 248 | } 249 | 250 | return factory; 251 | } 252 | 253 | private validateConfigs(configs: PluginConfig[]) { 254 | for (const config of configs) { 255 | if (!config.name && !config.transform) { 256 | throw new Error('tsconfig.json plugins error: transform must be present'); 257 | } 258 | } 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /packages/ttypescript/src/cancelationToken.ts: -------------------------------------------------------------------------------- 1 | module.exports = require('typescript/lib/cancellationToken'); 2 | -------------------------------------------------------------------------------- /packages/ttypescript/src/loadTypescript.ts: -------------------------------------------------------------------------------- 1 | import * as TS from 'typescript'; 2 | type ts = typeof TS; 3 | import { readFileSync } from 'fs'; 4 | import { sync as resolveSync } from 'resolve'; 5 | import { patchCreateProgram } from './patchCreateProgram'; 6 | import { dirname } from 'path'; 7 | import { runInThisContext } from 'vm'; 8 | import Module = require('module'); 9 | 10 | export function loadTypeScript( 11 | filename: string, 12 | { folder = __dirname, forceConfigLoad = false }: { folder?: string; forceConfigLoad?: boolean } = {} 13 | ): ts { 14 | const libFilename = resolveSync('typescript/lib/' + filename, { basedir: folder }); 15 | 16 | if (!require.cache[libFilename]) { 17 | require.cache[libFilename] = TSModuleFactory(libFilename); 18 | } 19 | 20 | const ts = TSModuleFactory(libFilename).exports; 21 | const [major, minor] = ts.versionMajorMinor.split('.'); 22 | if (+major < 3 && +minor < 7) { 23 | throw new Error('ttypescript supports typescript from 2.7 version'); 24 | } 25 | 26 | return patchCreateProgram(ts, forceConfigLoad); 27 | } 28 | 29 | type TypeScriptFactory = (exports: ts, require: NodeRequire, module: Module, filename: string, dirname: string) => void; 30 | const typeScriptFactoryCache = new Map(); 31 | 32 | function TSModuleFactory(filename: string) { 33 | let factory = typeScriptFactoryCache.get(filename); 34 | if (!factory) { 35 | const code = readFileSync(filename, 'utf8'); 36 | factory = runInThisContext(`(function (exports, require, module, __filename, __dirname) {${code}\n});`, { 37 | filename: filename, 38 | lineOffset: 0, 39 | displayErrors: true, 40 | }) as TypeScriptFactory; 41 | typeScriptFactoryCache.set(filename, factory); 42 | } 43 | 44 | const newModule = new Module(filename, module); 45 | factory.call(newModule, newModule.exports, require, newModule, filename, dirname(filename)); 46 | return newModule; 47 | } 48 | -------------------------------------------------------------------------------- /packages/ttypescript/src/patchCreateProgram.ts: -------------------------------------------------------------------------------- 1 | import { dirname } from 'path'; 2 | import * as ts from 'typescript'; 3 | import { PluginConfig, PluginCreator } from './PluginCreator'; 4 | import { Diagnostic } from 'typescript/lib/tsserverlibrary'; 5 | 6 | declare module 'typescript' { 7 | interface CreateProgramOptions { 8 | rootNames: ReadonlyArray; 9 | options: ts.CompilerOptions; 10 | projectReferences?: ReadonlyArray; 11 | host?: ts.CompilerHost; 12 | oldProgram?: ts.Program; 13 | configFileParsingDiagnostics?: ReadonlyArray; 14 | } 15 | interface ProjectReference { 16 | path: string; 17 | originalPath?: string; 18 | prepend?: boolean; 19 | circular?: boolean; 20 | } 21 | } 22 | 23 | export const transformerErrors = new WeakMap(); 24 | export function addDiagnosticFactory(program: ts.Program) { 25 | return (diag: ts.Diagnostic) => { 26 | const arr = transformerErrors.get(program) || []; 27 | arr.push(diag); 28 | transformerErrors.set(program, arr); 29 | }; 30 | } 31 | 32 | export function patchCreateProgram(tsm: typeof ts, forceReadConfig = false, projectDir = process.cwd()) { 33 | const originCreateProgram = tsm.createProgram as any; 34 | 35 | function createProgram(createProgramOptions: ts.CreateProgramOptions): ts.Program; 36 | function createProgram( 37 | rootNames: ReadonlyArray, 38 | options: ts.CompilerOptions, 39 | host?: ts.CompilerHost, 40 | oldProgram?: ts.Program, 41 | configFileParsingDiagnostics?: ReadonlyArray 42 | ): ts.Program; 43 | function createProgram( 44 | rootNamesOrOptions: ReadonlyArray | ts.CreateProgramOptions, 45 | options?: ts.CompilerOptions, 46 | host?: ts.CompilerHost, 47 | oldProgram?: ts.Program, 48 | configFileParsingDiagnostics?: ReadonlyArray 49 | ): ts.Program { 50 | let rootNames; 51 | let createOpts: ts.CreateProgramOptions | undefined; 52 | if (!Array.isArray(rootNamesOrOptions)) { 53 | createOpts = rootNamesOrOptions as ts.CreateProgramOptions; 54 | } 55 | if (createOpts) { 56 | rootNames = createOpts.rootNames; 57 | options = createOpts.options; 58 | host = createOpts.host; 59 | oldProgram = createOpts.oldProgram; 60 | configFileParsingDiagnostics = createOpts.configFileParsingDiagnostics; 61 | } else { 62 | options = options!; 63 | rootNames = rootNamesOrOptions as ReadonlyArray; 64 | } 65 | 66 | if (forceReadConfig) { 67 | const info = getConfig(tsm, options, rootNames, projectDir); 68 | options = info.compilerOptions; 69 | if (createOpts) { 70 | createOpts.options = options; 71 | } 72 | projectDir = info.projectDir; 73 | } 74 | const program: ts.Program = createOpts 75 | ? originCreateProgram(createOpts) 76 | : originCreateProgram(rootNames, options, host, oldProgram, configFileParsingDiagnostics); 77 | 78 | const plugins = preparePluginsFromCompilerOptions(options.plugins); 79 | const pluginCreator = new PluginCreator(tsm, plugins, projectDir); 80 | 81 | const originEmit = program.emit; 82 | 83 | /** 84 | * The emit method has the following declaration: 85 | * https://github.com/microsoft/TypeScript/blob/bfc55b5762443c37ecdef08a3b5a4e057b4d1e85/src/compiler/builderPublic.ts#L101 86 | * The declaration specifies 5 arguments, but it's not true. Sometimes the emit method takes 6 arguments. 87 | */ 88 | program.emit = function newEmit( 89 | targetSourceFile?: ts.SourceFile, 90 | writeFile?: ts.WriteFileCallback, 91 | cancellationToken?: ts.CancellationToken, 92 | emitOnlyDtsFiles?: boolean, 93 | customTransformers?: ts.CustomTransformers, 94 | arg?: boolean 95 | ): ts.EmitResult { 96 | const mergedTransformers = pluginCreator.createTransformers({ program }, customTransformers); 97 | const result: ts.EmitResult = (originEmit as any)( 98 | targetSourceFile, 99 | writeFile, 100 | cancellationToken, 101 | emitOnlyDtsFiles, 102 | mergedTransformers, 103 | arg 104 | ); 105 | // todo: doesn't work with 3.7 106 | // result.diagnostics = [...result.diagnostics, ...transformerErrors.get(program)!]; 107 | return result; 108 | }; 109 | return program; 110 | } 111 | tsm.createProgram = createProgram; 112 | return tsm; 113 | } 114 | 115 | function getConfig( 116 | tsm: typeof ts, 117 | compilerOptions: ts.CompilerOptions, 118 | rootFileNames: ReadonlyArray, 119 | defaultDir: string 120 | ) { 121 | if (compilerOptions.configFilePath === undefined) { 122 | const dir = rootFileNames.length > 0 ? dirname(rootFileNames[0]) : defaultDir; 123 | const tsconfigPath = tsm.findConfigFile(dir, tsm.sys.fileExists); 124 | if (tsconfigPath) { 125 | const projectDir = dirname(tsconfigPath); 126 | const config = readConfig(tsm, tsconfigPath, dirname(tsconfigPath)); 127 | compilerOptions = { ...config.options, ...compilerOptions }; 128 | return { 129 | projectDir, 130 | compilerOptions, 131 | }; 132 | } 133 | } 134 | return { 135 | projectDir: dirname(compilerOptions.configFilePath as string), 136 | compilerOptions, 137 | }; 138 | } 139 | 140 | function readConfig(tsm: typeof ts, configFileNamePath: string, projectDir: string) { 141 | const result = tsm.readConfigFile(configFileNamePath, tsm.sys.readFile); 142 | if (result.error) { 143 | throw new Error('tsconfig.json error: ' + result.error.messageText); 144 | } 145 | return tsm.parseJsonConfigFileContent(result.config, tsm.sys, projectDir, undefined, configFileNamePath); 146 | } 147 | 148 | function preparePluginsFromCompilerOptions(plugins: any): PluginConfig[] { 149 | if (!plugins) return []; 150 | // old transformers system 151 | if (plugins.length === 1 && plugins[0].customTransformers) { 152 | const { before = [], after = [] } = plugins[0].customTransformers as { before: string[]; after: string[] }; 153 | return [ 154 | ...before.map((item: string) => ({ transform: item })), 155 | ...after.map((item: string) => ({ transform: item, after: true })), 156 | ]; 157 | } 158 | return plugins; 159 | } 160 | -------------------------------------------------------------------------------- /packages/ttypescript/src/tsc.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as resolve from 'resolve'; 3 | import { loadTypeScript } from './loadTypescript'; 4 | import { dirname } from 'path'; 5 | import { runInThisContext } from 'vm'; 6 | 7 | const ts = loadTypeScript('typescript', { folder: process.cwd(), forceConfigLoad: true }); 8 | const tscFileName = resolve.sync('typescript/lib/tsc', { basedir: process.cwd() }); 9 | const [major, minor]: [number, number] = 10 | ts.version.split(".").map( 11 | (str: string) => Number(str) 12 | ) as [number, number]; 13 | 14 | const commandLineTsCode = fs 15 | .readFileSync(tscFileName, 'utf8') 16 | .replace( 17 | major >= 4 && minor >= 9 18 | ? /^[\s\S]+(\(function \(ts\) {\s+var StatisticType;[\s\S]+)$/ 19 | : /^[\s\S]+(\(function \(ts\) \{\s+function countLines[\s\S]+)$/, 20 | '$1' 21 | ); 22 | 23 | const globalCode = (fs.readFileSync(tscFileName, 'utf8').match(/^([\s\S]*?)var ts;/) || ['', ''])[1]; 24 | runInThisContext( 25 | `(function (exports, require, module, __filename, __dirname, ts) {${globalCode}${commandLineTsCode}\n});`, 26 | { 27 | filename: tscFileName, 28 | lineOffset: 0, 29 | displayErrors: true, 30 | } 31 | ).call(ts, ts, require, { exports: ts }, tscFileName, dirname(tscFileName), ts); 32 | -------------------------------------------------------------------------------- /packages/ttypescript/src/tsserver.ts: -------------------------------------------------------------------------------- 1 | module.exports = require('typescript/lib/tsserver'); 2 | -------------------------------------------------------------------------------- /packages/ttypescript/src/tsserverlibrary.ts: -------------------------------------------------------------------------------- 1 | import * as TS from 'typescript'; 2 | import * as TSSL from 'typescript/lib/tsserverlibrary'; 3 | import { loadTypeScript } from './loadTypescript'; 4 | 5 | const ts = (loadTypeScript('tsserverlibrary') as unknown) as typeof TSSL; 6 | export = ts; 7 | -------------------------------------------------------------------------------- /packages/ttypescript/src/typescript.ts: -------------------------------------------------------------------------------- 1 | import * as TS from 'typescript'; 2 | import { loadTypeScript } from './loadTypescript'; 3 | 4 | const ts = loadTypeScript('typescript'); 5 | export = ts; -------------------------------------------------------------------------------- /packages/ttypescript/src/typescriptServices.ts: -------------------------------------------------------------------------------- 1 | import * as TS from 'typescript'; 2 | import { loadTypeScript } from './loadTypescript'; 3 | 4 | const ts = loadTypeScript('typescriptServices'); 5 | export = ts; 6 | -------------------------------------------------------------------------------- /packages/ttypescript/src/typingsInstaller.ts: -------------------------------------------------------------------------------- 1 | module.exports = require('typescript/lib/typingsInstaller'); 2 | -------------------------------------------------------------------------------- /packages/ttypescript/src/watchGuard.ts: -------------------------------------------------------------------------------- 1 | module.exports = require('typescript/lib/watchGuard'); 2 | -------------------------------------------------------------------------------- /packages/ttypescript/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "outDir": "lib" 5 | }, 6 | "exclude": ["__tests__", "lib"] 7 | } 8 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "declaration": true, 5 | "module": "commonjs", 6 | "strict": true, 7 | "baseUrl": "packages", 8 | "lib": ["es2015"], 9 | "paths": { 10 | "ttypescript/lib/*": ["ttypescript/src/*"], 11 | "ttypescript": ["ttypescript/src/typescript"], 12 | "ttypescript-*": ["ttypescript-*/src"] 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "include": ["packages/**/*.ts"], 4 | "exclude": ["packages/*/__tests__/**"] 5 | } 6 | --------------------------------------------------------------------------------