Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

swc transpiler and new --transpiler option to use third-party transpilers #1160

Merged
merged 22 commits into from Feb 27, 2021
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
19990d4
WIP experimental swc compiler
cspotcode Nov 22, 2020
1551509
Fix optional peer dep
cspotcode Nov 23, 2020
fcd27cc
wip
cspotcode Nov 27, 2020
77436c3
properly merge all of ts onto exports object
cspotcode Dec 10, 2020
f77e1b1
fix clobbering of code because swc does not append a //# sourcemap co…
cspotcode Dec 10, 2020
c82c820
More changes:
cspotcode Jan 17, 2021
b13a631
Merge remote-tracking branch 'origin/master' into ab/swc-compiler
cspotcode Jan 17, 2021
78b49e3
Fix bug in swc loading to allow swc API instance to be passed to factory
cspotcode Jan 17, 2021
230f638
lint fixes
cspotcode Jan 17, 2021
f1b1be0
Fix typo in createTypescriptCompiler function name
cspotcode Jan 17, 2021
ab67165
Switch from hacky overloading the "compiler" config to implementing a…
cspotcode Feb 23, 2021
c1548cf
Merge remote-tracking branch 'origin/master' into ab/swc-compiler
cspotcode Feb 23, 2021
369d43a
fix package.json files array and add --transpiler CLI flag
cspotcode Feb 23, 2021
7103b68
make --transpiler imply --transpile-only and add tests
cspotcode Feb 23, 2021
026b4c1
fixes
cspotcode Feb 23, 2021
d72dbd0
add missing test files
cspotcode Feb 23, 2021
52082fd
add @swc/core dep to tests
cspotcode Feb 23, 2021
7a855ed
add some jsdoc to new transpiler api surface
cspotcode Feb 23, 2021
6fe8249
change transpiler options to be specified as "transpiler: [name, {/*o…
cspotcode Feb 27, 2021
14671a3
Merge remote-tracking branch 'origin/master' into ab/swc-compiler
cspotcode Feb 27, 2021
c3c5a3e
fix
cspotcode Feb 27, 2021
1a06089
cleanup comments
cspotcode Feb 27, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions compilers/swc-experimental.js
@@ -0,0 +1 @@
module.exports = require('../dist/compilers/swc')
103 changes: 103 additions & 0 deletions package-lock.json

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

16 changes: 15 additions & 1 deletion package.json
Expand Up @@ -20,7 +20,8 @@
"./esm": "./esm.mjs",
"./esm.mjs": "./esm.mjs",
"./esm/transpile-only": "./esm/transpile-only.mjs",
"./esm/transpile-only.mjs": "./esm/transpile-only.mjs"
"./esm/transpile-only.mjs": "./esm/transpile-only.mjs",
"./compilers/swc-experimental": "./compilers/swc-experimental.js"
},
"types": "dist/index.d.ts",
"bin": {
Expand All @@ -30,6 +31,7 @@
"ts-node-transpile-only": "dist/bin-transpile.js"
},
"files": [
"compilers/",
"dist/",
"dist-raw/",
"register/",
Expand Down Expand Up @@ -80,6 +82,8 @@
},
"homepage": "https://github.com/TypeStrong/ts-node",
"devDependencies": {
"@swc/core": ">=1.2.45",
"@swc/wasm": ">=1.2.45",
"@types/chai": "^4.0.4",
"@types/diff": "^4.0.2",
"@types/lodash": "^4.14.151",
Expand Down Expand Up @@ -108,8 +112,18 @@
"util.promisify": "^1.0.1"
},
"peerDependencies": {
"@swc/core": ">=1.2.45",
"@swc/wasm": ">=1.2.45",
"typescript": ">=2.7"
},
"peerDependenciesMeta": {
"@swc/core": {
"optional": true
},
"@swc/wasm": {
"optional": true
}
},
"dependencies": {
"arg": "^4.1.0",
"create-require": "^1.1.0",
Expand Down
95 changes: 95 additions & 0 deletions src/compilers/swc.ts
@@ -0,0 +1,95 @@
import type * as ts from 'typescript'
import type * as swcWasm from '@swc/wasm'
import type * as swcTypes from '@swc/core'
import type { TSCommon } from '..'
export interface Options {
/** TypeScript compiler to wrap */
compiler?: string | TSCommon
/**
* swc compiler to use for compilation
* Set to '@swc/wasm' to use swc's WASM compiler
* Default: '@swc/core'
*/
swc?: string | typeof swcWasm
}

export function createTypescriptCompiler (options: Options = {}) {
const { swc, compiler = 'typescript' } = options
const compilerInstance = typeof compiler === 'string' ? require(compiler) as TSCommon : compiler
let swcInstance: typeof swcWasm
if (typeof swc === 'string') {
swcInstance = require(swc) as typeof swcWasm
} else if (swc == null) { // tslint:disable-line
let swcResolved
try {
swcResolved = require.resolve('@swc/core')
} catch (e) {
try {
swcResolved = require.resolve('@swc/wasm')
} catch (e) {
throw new Error('swc compiler requires either @swc/core or @swc/wasm to be installed as dependencies')
}
}
swcInstance = require(swcResolved) as typeof swcWasm
} else {
swcInstance = swc
}

const version = `${ compilerInstance.version }-tsnode-${ require('../../package').version }-swc`

const transpileModule: TSCommon['transpileModule'] = (input: string, transpileOptions: ts.TranspileOptions): ts.TranspileOutput => {
const compilerOptions = transpileOptions.compilerOptions!
const { fileName } = transpileOptions
const { esModuleInterop, sourceMap, importHelpers, experimentalDecorators, emitDecoratorMetadata, target, jsxFactory, jsxFragmentFactory } = compilerOptions
const options: swcTypes.Options = {
filename: fileName,
sourceMaps: sourceMap,
// isModule: true,
module: {
type: 'commonjs',
noInterop: !esModuleInterop
},
swcrc: false,
jsc: {
externalHelpers: importHelpers,
parser: {
syntax: 'typescript',
tsx: fileName!.endsWith('.tsx') || fileName!.endsWith('.jsx'),
decorators: experimentalDecorators,
dynamicImport: true
},
target: targetMapping.get(target!) ?? 'es3',
transform: {
decoratorMetadata: emitDecoratorMetadata,
legacyDecorator: true,
react: {
throwIfNamespace: false,
development: false,
useBuiltins: false,
pragma: jsxFactory!,
pragmaFrag: jsxFragmentFactory!
}
}
}
}
const { code, map } = swcInstance.transformSync(input, options)
return { outputText: code, sourceMapText: map }
}
return {
...compilerInstance,
version,
transpileModule
}
}

const targetMapping = new Map<ts.ScriptTarget, swcTypes.JscTarget>()
targetMapping.set(/* ts.ScriptTarget.ES3 */ 0, 'es3')
targetMapping.set(/* ts.ScriptTarget.ES5 */ 1, 'es5')
targetMapping.set(/* ts.ScriptTarget.ES2015 */ 2, 'es2015')
targetMapping.set(/* ts.ScriptTarget.ES2016 */ 3, 'es2016')
targetMapping.set(/* ts.ScriptTarget.ES2017 */ 4, 'es2017')
targetMapping.set(/* ts.ScriptTarget.ES2018 */ 5, 'es2018')
targetMapping.set(/* ts.ScriptTarget.ES2019 */ 6, 'es2019')
targetMapping.set(/* ts.ScriptTarget.ES2020 */ 7, 'es2019')
targetMapping.set(/* ts.ScriptTarget.ESNext */ 99, 'es2019')
targetMapping.set(/* ts.ScriptTarget.Latest */ 99, 'es2019')
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

99 is set twice - intentional?

36 changes: 30 additions & 6 deletions src/index.ts
Expand Up @@ -98,6 +98,18 @@ export interface TSCommon {
parseJsonConfigFileContent: typeof _ts.parseJsonConfigFileContent
formatDiagnostics: typeof _ts.formatDiagnostics
formatDiagnosticsWithColorAndContext: typeof _ts.formatDiagnosticsWithColorAndContext

createDocumentRegistry: typeof _ts.createDocumentRegistry
JsxEmit: typeof _ts.JsxEmit
createModuleResolutionCache: typeof _ts.createModuleResolutionCache
resolveModuleName: typeof _ts.resolveModuleName
resolveModuleNameFromCache: typeof _ts.resolveModuleNameFromCache
resolveTypeReferenceDirective: typeof _ts.resolveTypeReferenceDirective
createIncrementalCompilerHost: typeof _ts.createIncrementalCompilerHost
createSourceFile: typeof _ts.createSourceFile
getDefaultLibFileName: typeof _ts.getDefaultLibFileName
createIncrementalProgram: typeof _ts.createIncrementalProgram
createEmitAndSemanticDiagnosticsBuilderProgram: typeof _ts.createEmitAndSemanticDiagnosticsBuilderProgram
}

/**
Expand All @@ -112,6 +124,10 @@ namespace TSInternal {
export type GetCanonicalFileName = (fileName: string) => string
}

export interface TSCompilerFactory {
createTypescriptCompiler (options?: any): TSCommon
}

/**
* Export the current version.
*/
Expand Down Expand Up @@ -437,7 +453,14 @@ export function create (rawOptions: CreateOptions = {}): Service {
*/
function loadCompiler (name: string | undefined) {
const compiler = require.resolve(name || 'typescript', { paths: [cwd, __dirname] })
const ts: typeof _ts = require(compiler)
const tsInstanceOrFactory: TSCommon | TSCompilerFactory = require(compiler)
let ts: TSCommon
// tslint:disable-next-line
if (typeof (tsInstanceOrFactory as TSCompilerFactory).createTypescriptCompiler === 'function') {
ts = (tsInstanceOrFactory as TSCompilerFactory).createTypescriptCompiler()
} else {
ts = tsInstanceOrFactory as TSCommon
}
return { compiler, ts }
}

Expand Down Expand Up @@ -1180,10 +1203,11 @@ type SourceOutput = [string, string]
*/
function updateOutput (outputText: string, fileName: string, sourceMap: string, getExtension: (fileName: string) => string) {
const base64Map = Buffer.from(updateSourceMap(sourceMap, fileName), 'utf8').toString('base64')
const sourceMapContent = `data:application/json;charset=utf-8;base64,${base64Map}`
const sourceMapLength = `${basename(fileName)}.map`.length + (getExtension(fileName).length - extname(fileName).length)

return outputText.slice(0, -sourceMapLength) + sourceMapContent
const sourceMapContent = `//# sourceMappingURL=data:application/json;charset=utf-8;base64,${base64Map}`
// Expected form: `//# sourceMappingURL=foo.js.map` for input file foo.tsx
const sourceMapLength = /*//# sourceMappingURL=*/ 21 + /*foo.tsx*/ basename(fileName).length - /*.tsx*/ extname(fileName).length + /*.js*/ getExtension(fileName).length + /*.map*/ 4
// Only rewrite if existing directive exists, to support compilers that do not append a sourcemap directive
return (outputText.slice(-sourceMapLength, -sourceMapLength + 21) === '//# sourceMappingURL=' ? outputText.slice(0, -sourceMapLength) : outputText) + sourceMapContent
}

/**
Expand All @@ -1209,7 +1233,7 @@ function filterDiagnostics (diagnostics: readonly _ts.Diagnostic[], ignore: numb
*
* Reference: https://github.com/microsoft/TypeScript/blob/fcd9334f57d85b73dd66ad2d21c02e84822f4841/src/services/utilities.ts#L705-L731
*/
function getTokenAtPosition (ts: typeof _ts, sourceFile: _ts.SourceFile, position: number): _ts.Node {
function getTokenAtPosition (ts: TSCommon, sourceFile: _ts.SourceFile, position: number): _ts.Node {
let current: _ts.Node = sourceFile

outer: while (true) {
Expand Down