UNPKG

5.92 kBPlain TextView Raw
1import * as ts from 'typescript';
2import { createFilter } from 'rollup-pluginutils';
3import * as path from 'path';
4import {
5 existsSync,
6 readFileSync,
7 statSync,
8} from 'fs';
9import assign from 'object-assign';
10import compareVersions from 'compare-versions';
11
12import { endsWith } from './string';
13import fixExportClass from './fixExportClass';
14
15interface Options {
16 tsconfig?: boolean;
17 include?: stringstring[];
18 exclude?: stringstring[];
19 typescript?: typeof ts;
20 module?: string;
21}
22
23const resolveHost = {
24 fileExists ( filePath: string ): boolean {
25 try {
26 return statSync( filePath ).isFile();
27 } catch ( err ) {
28 return false;
29 }
30 }
31};
32
33function goodErrors ( diagnostic: ts.Diagnostic ): boolean {
34 // All errors except `Cannot compile modules into 'es6' when targeting 'ES5' or lower.`
35 return diagnostic.code !== 1204;
36}
37
38function getDefaultOptions(): any {
39 return {
40 noEmitHelpers: true,
41 module: 'es2015',
42 sourceMap: true
43 };
44}
45
46// Gratefully lifted from 'look-up', due to problems using it directly:
47// https://github.com/jonschlinkert/look-up/blob/master/index.js
48// MIT Licenced
49function findFile( cwd: string, filename: string ): string {
50 let fp = cwd ? ( cwd + '/' + filename ) : filename;
51
52 if ( existsSync( fp ) ) {
53 return fp;
54 }
55
56 const segs = cwd.split( path.sep );
57 let len = segs.length;
58
59 while ( len-- ) {
60 cwd = segs.slice( 0, len ).join( '/' );
61 fp = cwd + '/' + filename;
62 if ( existsSync( fp ) ) {
63 return fp;
64 }
65 }
66 return null;
67}
68
69function compilerOptionsFromTsConfig( typescript: typeof ts ): ts.CompilerOptions {
70 const cwd = process.cwd();
71
72 const tsconfig = typescript.readConfigFile( findFile( cwd, 'tsconfig.json' ), path => readFileSync( path, 'utf8' ) );
73
74 if ( !tsconfig.config || !tsconfig.config.compilerOptions ) return {};
75
76 return tsconfig.config.compilerOptions;
77}
78
79// Set `sourceMap` to `inlineSourceMap` if it's a boolean,
80// under the assumption that both are never specified simultaneously.
81function fixSourceMapOption( options: any ) {
82 if ( typeof options.inlineSourceMap === 'boolean' ) {
83 options.sourceMap = options.inlineSourceMap;
84 delete options.inlineSourceMap;
85 }
86}
87
88export default function typescript ( options: Options ) {
89 options = assign( {}, options || {} );
90
91 const filter = createFilter(
92 options.include || [ '*.ts+(|x)', '**/*.ts+(|x)' ],
93 options.exclude || [ '*.d.ts', '**/*.d.ts' ] );
94
95 delete options.include;
96 delete options.exclude;
97
98 // Allow users to override the TypeScript version used for transpilation.
99 const typescript: typeof ts = options.typescript || ts;
100
101 delete options.typescript;
102
103 // Load options from `tsconfig.json` unless explicitly asked not to.
104 const tsconfig = options.tsconfig === false ? {} :
105 compilerOptionsFromTsConfig( typescript );
106
107 delete options.tsconfig;
108
109 // Since Rollup handles the source maps; we equate the
110 // `sourceMap` and `inlineSourceMap` options.
111 fixSourceMapOption( tsconfig );
112 fixSourceMapOption( options );
113
114 // Merge all options.
115 options = assign( tsconfig, getDefaultOptions(), options );
116
117 // Verify that we're targeting ES2015 modules.
118 if ( options.module !== 'es2015' && options.module !== 'es6' ) {
119 throw new Error( `rollup-plugin-typescript: The module kind should be 'es2015', found: '${ options.module }'` );
120 }
121
122 const parsed = typescript.convertCompilerOptionsFromJson( options, process.cwd() );
123
124 if ( parsed.errors.length ) {
125 parsed.errors.forEach( error => console.error( `rollup-plugin-typescript: ${ error.messageText }` ) );
126
127 throw new Error( `rollup-plugin-typescript: Couldn't process compiler options` );
128 }
129
130 const compilerOptions = parsed.options;
131
132 return {
133 resolveId ( importee: string, importer: string ): string {
134 // Handle the special `typescript-helpers` import itself.
135 if ( importee === 'typescript-helpers' ) {
136 return path.resolve( __dirname, '../src/typescript-helpers.js' );
137 }
138
139 if ( !importer ) return null;
140
141 var result: ts.ResolvedModuleWithFailedLookupLocations;
142
143 if ( compareVersions( typescript.version, '1.8.0' ) < 0 ) {
144 // Suppress TypeScript warnings for function call.
145 result = (typescript as any).nodeModuleNameResolver( importee, importer, resolveHost );
146 } else {
147 result = typescript.nodeModuleNameResolver( importee, importer, compilerOptions, resolveHost );
148 }
149
150 if ( result.resolvedModule && result.resolvedModule.resolvedFileName ) {
151 if ( endsWith( result.resolvedModule.resolvedFileName, '.d.ts' ) ) {
152 return null;
153 }
154
155 return result.resolvedModule.resolvedFileName;
156 }
157
158 return null;
159 },
160
161 transform ( code: string, id: string ): { code: string, map: any } {
162 if ( !filter( id ) ) return null;
163
164 const transformed = typescript.transpileModule( fixExportClass( code, id ), {
165 fileName: id,
166 reportDiagnostics: true,
167 compilerOptions
168 });
169
170 const diagnostics = transformed.diagnostics.filter( goodErrors );
171 let fatalError = false;
172
173 diagnostics.forEach( diagnostic => {
174 var message = typescript.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
175
176 if ( diagnostic.file ) {
177 const { line, character } = diagnostic.file.getLineAndCharacterOfPosition( diagnostic.start );
178
179 console.error( `${diagnostic.file.fileName}(${line + 1},${character + 1}): error TS${diagnostic.code}: ${message}` );
180 } else {
181 console.error( `Error: ${message}` );
182 }
183
184 if ( diagnostic.category === ts.DiagnosticCategory.Error ) {
185 fatalError = true;
186 }
187 });
188
189 if ( fatalError ) {
190 throw new Error( `There were TypeScript errors transpiling "${id}"` );
191 }
192
193 return {
194 // Always append an import for the helpers.
195 code: transformed.outputText +
196 `\nimport { __extends, __decorate, __metadata, __param, __awaiter } from 'typescript-helpers';`,
197
198 // Rollup expects `map` to be an object so we must parse the string
199 map: JSON.parse(transformed.sourceMapText)
200 };
201 }
202 };
203}