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

fix(typescript): Use this.warn for ts errors #129

Merged
merged 2 commits into from
Jan 1, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 29 additions & 53 deletions packages/typescript/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,93 +1,69 @@
import * as fs from 'fs';

import { Plugin } from 'rollup';
import * as ts from 'typescript';
import { createFilter } from '@rollup/pluginutils';
import resolveId from 'resolve';
import { Plugin } from 'rollup';
import * as defaultTs from 'typescript';

import { RollupTypescriptOptions } from '../types';

import { getDefaultOptions, readTsConfig, adjustCompilerOptions } from './options';
import {
adjustCompilerOptions,
getDefaultOptions,
parseCompilerOptions,
readTsConfig,
validateModuleType
} from './options';
import resolveHost from './resolveHost';

const TSLIB_ID = '\0tslib';

export default function typescript(options: RollupTypescriptOptions = {}): Plugin {
let opts = Object.assign({}, options);
const opts = Object.assign({}, options);

const filter = createFilter(
opts.include || ['*.ts+(|x)', '**/*.ts+(|x)'],
opts.exclude || ['*.d.ts', '**/*.d.ts']
);

delete opts.include;
delete opts.exclude;

// Allow users to override the TypeScript version used for transpilation and tslib version used for helpers.
const tsRuntime: typeof import('typescript') = opts.typescript || ts;
const ts: typeof import('typescript') = opts.typescript || defaultTs;
delete opts.typescript;

const tslib =
opts.tslib ||
fs.readFileSync(resolveId.sync('tslib/tslib.es6.js', { basedir: __dirname }), 'utf-8');

delete opts.typescript;
delete opts.tslib;

// Load options from `tsconfig.json` unless explicitly asked not to.
const tsConfig =
opts.tsconfig === false ? { compilerOptions: {} } : readTsConfig(tsRuntime, opts.tsconfig);

opts.tsconfig === false ? { compilerOptions: {} } : readTsConfig(ts, opts.tsconfig);
delete opts.tsconfig;

// Since the CompilerOptions aren't designed for the Rollup
// use case, we'll adjust them for use with Rollup.
tsConfig.compilerOptions = adjustCompilerOptions(tsConfig.compilerOptions);
opts = adjustCompilerOptions(opts);

opts = Object.assign(tsConfig.compilerOptions, getDefaultOptions(), opts);
Object.assign(tsConfig.compilerOptions, getDefaultOptions(), adjustCompilerOptions(opts));

// Verify that we're targeting ES2015 modules.
const moduleType = (opts.module as string).toUpperCase();
if (
moduleType !== 'ES2015' &&
moduleType !== 'ES6' &&
moduleType !== 'ESNEXT' &&
moduleType !== 'COMMONJS'
) {
throw new Error(
`@rollup/plugin-typescript: The module kind should be 'ES2015' or 'ESNext, found: '${opts.module}'`
);
}

const parsed = tsRuntime.convertCompilerOptionsFromJson(opts, process.cwd());

if (parsed.errors.length) {
parsed.errors.forEach((error) =>
// eslint-disable-next-line
console.error(`@rollup/plugin-typescript: ${error.messageText}`)
);

throw new Error(`@rollup/plugin-typescript: Couldn't process compiler options`);
}

// let typescript load inheritance chain if there are base configs
const extendedConfig = tsConfig.extends
? tsRuntime.parseJsonConfigFileContent(tsConfig, tsRuntime.sys, process.cwd(), parsed.options)
: null;

if (extendedConfig && extendedConfig.errors.length) {
extendedConfig.errors.forEach((error) =>
// eslint-disable-next-line
console.error(`@rollup/plugin-typescript: ${error.messageText}`)
);

throw new Error(`@rollup/plugin-typescript: Couldn't process compiler options`);
}

const compilerOptions = extendedConfig ? extendedConfig.options : parsed.options;
validateModuleType(tsConfig.compilerOptions.module);

const { options: compilerOptions, errors } = parseCompilerOptions(ts, tsConfig);

return {
name: 'typescript',

buildStart() {
if (errors.length > 0) {
errors.forEach((error) => this.warn(`@rollup/plugin-typescript: ${error.messageText}`));

this.error(`@rollup/plugin-typescript: Couldn't process compiler options`);
}
},

resolveId(importee, importer) {
if (importee === 'tslib') {
return TSLIB_ID;
Expand All @@ -96,7 +72,7 @@ export default function typescript(options: RollupTypescriptOptions = {}): Plugi
if (!importer) return null;
const containingFile = importer.split('\\').join('/');

const result = tsRuntime.nodeModuleNameResolver(
const result = ts.nodeModuleNameResolver(
importee,
containingFile,
compilerOptions,
Expand Down Expand Up @@ -124,7 +100,7 @@ export default function typescript(options: RollupTypescriptOptions = {}): Plugi
transform(code, id) {
if (!filter(id)) return null;

const transformed = tsRuntime.transpileModule(code, {
const transformed = ts.transpileModule(code, {
fileName: id,
reportDiagnostics: true,
compilerOptions
Expand All @@ -138,7 +114,7 @@ export default function typescript(options: RollupTypescriptOptions = {}): Plugi
let fatalError = false;

diagnostics.forEach((diagnostic) => {
const message = tsRuntime.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');

if (diagnostic.file) {
const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(
Expand Down
39 changes: 31 additions & 8 deletions packages/typescript/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,26 +33,21 @@ function findFile(cwd: string, filename: string): string | null {
return null;
}

export function readTsConfig(
typescript: typeof import('typescript'),
tsconfigPath: string | undefined
) {
export function readTsConfig(ts: typeof import('typescript'), tsconfigPath: string | undefined) {
if (tsconfigPath && !existsSync(tsconfigPath)) {
throw new Error(`Could not find specified tsconfig.json at ${tsconfigPath}`);
}
const existingTsConfig = tsconfigPath || findFile(process.cwd(), 'tsconfig.json');
if (!existingTsConfig) {
return {};
}
const tsconfig = typescript.readConfigFile(existingTsConfig, (path) =>
readFileSync(path, 'utf8')
);
const tsconfig = ts.readConfigFile(existingTsConfig, (path) => readFileSync(path, 'utf8'));

if (!tsconfig.config || !tsconfig.config.compilerOptions) return { compilerOptions: {} };
return tsconfig.config;
}

export function adjustCompilerOptions(options) {
export function adjustCompilerOptions(options: any) {
const opts = Object.assign({}, options);
// Set `sourceMap` to `inlineSourceMap` if it's a boolean
// under the assumption that both are never specified simultaneously.
Expand All @@ -72,3 +67,31 @@ export function adjustCompilerOptions(options) {
delete opts.tsBuildInfoFile;
return opts;
}

export function parseCompilerOptions(ts: typeof import('typescript'), tsConfig: any) {
const parsed = ts.convertCompilerOptionsFromJson(tsConfig.compilerOptions, process.cwd());

// let typescript load inheritance chain if there are base configs
const extendedConfig = tsConfig.extends
? ts.parseJsonConfigFileContent(tsConfig, ts.sys, process.cwd(), parsed.options)
: null;

return {
options: extendedConfig?.options || parsed.options,
errors: parsed.errors.concat(extendedConfig?.errors || [])
};
}

/**
* Verify that we're targeting ES2015 modules.
* @param moduleType `tsConfig.compilerOptions.module`
*/
export function validateModuleType(moduleType: string) {
const esModuleTypes = new Set(['ES2015', 'ES6', 'ESNEXT', 'COMMONJS']);

if (!esModuleTypes.has(moduleType.toUpperCase())) {
throw new Error(
`@rollup/plugin-typescript: The module kind should be 'ES2015' or 'ESNext, found: '${moduleType}'`
);
}
}
14 changes: 11 additions & 3 deletions packages/typescript/test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -273,15 +273,23 @@ test('throws if tsconfig cannot be found', async (t) => {
);
});

test('should throw on bad options', (t) => {
t.throws(
test('should throw on bad options', async (t) => {
const warnings = [];
await t.throwsAsync(
() =>
rollup({
input: 'does-not-matter.ts',
plugins: [typescript({ foo: 'bar' })]
plugins: [typescript({ foo: 'bar' })],
onwarn(warning) {
warnings.push(warning);
}
}),
"@rollup/plugin-typescript: Couldn't process compiler options"
);

t.is(warnings.length, 1);
t.is(warnings[0].plugin, 'typescript');
t.is(warnings[0].message, `@rollup/plugin-typescript: Unknown compiler option 'foo'.`);
});

test('prevents errors due to conflicting `sourceMap`/`inlineSourceMap` options', async (t) => {
Expand Down