Skip to content

Commit

Permalink
feat(typescript): allow override of forced noEmit and `emitDeclarat…
Browse files Browse the repository at this point in the history
…ionOnly` compiler options (#1242)

* add documentation

* add noForceEmit option and apply in code

* fix that issue with | undefined

* tests WIP

* MR feedback for README

* fix tests
  • Loading branch information
Harris-Miller committed Aug 23, 2022
1 parent 160959d commit a21db7d
Show file tree
Hide file tree
Showing 12 changed files with 124 additions and 10 deletions.
10 changes: 10 additions & 0 deletions packages/typescript/README.md
Expand Up @@ -214,6 +214,15 @@ typescript({
});
```

### `noForceEmit`

Type: `Boolean`<br>
Default: `false`

Earlier version of `@rollup/plugin-typescript` required that the `compilerOptions` `noEmit` and `emitDeclarationOnly` both false to guarantee that source code was fed into the next plugin/output. This is no longer true. This option disables the plugin forcing the values of those options and instead defers to the values set in `tsconfig.json`.

`noForceEmit` can be very useful if you use with `@rollup/plugin-babel` and `@babel/preset-typescript`. Having `@rollup/plugin-typescript` only do typechecking / declarations with `"emitDeclarationOnly": true` while deferring to `@rollup/plugin-babel` for transpilation can dramatically reduce `rollup` build times for large projects.

### Typescript compiler options

Some of Typescript's [CompilerOptions](https://www.typescriptlang.org/docs/handbook/compiler-options.html) affect how Rollup builds files.
Expand All @@ -238,6 +247,7 @@ These compiler options are ignored by Rollup:

- `noEmitHelpers`, `importHelpers`: The `tslib` helper module always must be used.
- `noEmit`, `emitDeclarationOnly`: Typescript needs to emit code for the plugin to work with.
- _Note: While this was true for early iterations of `@rollup/plugin-typescript`, it is no longer. To override this behavior, and defer to `tsconfig.json` for these options, see the [`noForceEmit`](#noForceEmit) option_
- `noResolve`: Preventing Typescript from resolving code may break compilation

### Importing CommonJS
Expand Down
3 changes: 2 additions & 1 deletion packages/typescript/src/index.ts
Expand Up @@ -25,6 +25,7 @@ export default function typescript(options: RollupTypescriptOptions = {}): Plugi
filterRoot,
include,
outputToFilesystem,
noForceEmit,
transformers,
tsconfig,
tslib,
Expand All @@ -34,7 +35,7 @@ export default function typescript(options: RollupTypescriptOptions = {}): Plugi
const emittedFiles = new Map<string, string>();
const watchProgramHelper = new WatchProgramHelper();

const parsedOptions = parseTypescriptConfig(ts, tsconfig, compilerOptions);
const parsedOptions = parseTypescriptConfig(ts, tsconfig, compilerOptions, noForceEmit);
const filter = createFilter(include || ['*.ts+(|x)', '**/*.ts+(|x)'], exclude, {
resolve: filterRoot ?? parsedOptions.options.rootDir
});
Expand Down
8 changes: 5 additions & 3 deletions packages/typescript/src/options/interfaces.ts
Expand Up @@ -13,13 +13,15 @@ export const DEFAULT_COMPILER_OPTIONS: PartialCompilerOptions = {
skipLibCheck: true
};

export const OVERRIDABLE_EMIT_COMPILER_OPTIONS: Partial<CompilerOptions> = {
noEmit: false,
emitDeclarationOnly: false
};

export const FORCED_COMPILER_OPTIONS: Partial<CompilerOptions> = {
// Always use tslib
noEmitHelpers: true,
importHelpers: true,
// Typescript needs to emit the code for us to work with
noEmit: false,
emitDeclarationOnly: false,
// Preventing Typescript from resolving code may break compilation
noResolve: false
};
2 changes: 2 additions & 0 deletions packages/typescript/src/options/plugin.ts
Expand Up @@ -19,6 +19,7 @@ export const getPluginOptions = (options: RollupTypescriptOptions) => {
exclude,
include,
filterRoot,
noForceEmit,
transformers,
tsconfig,
tslib,
Expand All @@ -34,6 +35,7 @@ export const getPluginOptions = (options: RollupTypescriptOptions) => {
include,
exclude,
filterRoot,
noForceEmit: noForceEmit || false,
tsconfig,
compilerOptions: { ...extra, ...compilerOptions } as PartialCompilerOptions,
typescript: typescript || defaultTs,
Expand Down
12 changes: 9 additions & 3 deletions packages/typescript/src/options/tsconfig.ts
Expand Up @@ -21,6 +21,7 @@ import {
DEFAULT_COMPILER_OPTIONS,
EnumCompilerOptions,
FORCED_COMPILER_OPTIONS,
OVERRIDABLE_EMIT_COMPILER_OPTIONS,
PartialCompilerOptions
} from './interfaces';
import { normalizeCompilerOptions, makePathsAbsolute } from './normalize';
Expand All @@ -38,6 +39,10 @@ export interface TypeScriptConfig {
compileOnSave?: boolean | undefined;
}

function makeForcedCompilerOptions(noForceEmit: boolean) {
return { ...FORCED_COMPILER_OPTIONS, ...(noForceEmit ? {} : OVERRIDABLE_EMIT_COMPILER_OPTIONS) };
}

/**
* Finds the path to the tsconfig file relative to the current working directory.
* @param relativePath Relative tsconfig path given by the user.
Expand Down Expand Up @@ -110,7 +115,8 @@ const configCache = new Map() as import('typescript').Map<ExtendedConfigCacheEnt
export function parseTypescriptConfig(
ts: typeof import('typescript'),
tsconfig: RollupTypescriptOptions['tsconfig'],
compilerOptions: PartialCompilerOptions
compilerOptions: PartialCompilerOptions,
noForceEmit: boolean
): TypeScriptConfig {
/* eslint-disable no-undefined */
const cwd = process.cwd();
Expand All @@ -136,7 +142,7 @@ export function parseTypescriptConfig(
},
ts.sys,
basePath,
{ ...compilerOptions, ...FORCED_COMPILER_OPTIONS },
{ ...compilerOptions, ...makeForcedCompilerOptions(noForceEmit) },
tsConfigPath,
undefined,
undefined,
Expand All @@ -154,7 +160,7 @@ export function parseTypescriptConfig(
},
ts.sys,
basePath,
FORCED_COMPILER_OPTIONS,
makeForcedCompilerOptions(noForceEmit),
tsConfigPath,
undefined,
undefined,
Expand Down
Expand Up @@ -391,6 +391,11 @@
"signature": "ef91066d2057cca50511e3af2d4aa54e07913a15f9cee1748f256139318eb413",
"affectsGlobalScope": false
},
"../../../../../node_modules/.pnpm/@babel+types@7.14.5/node_modules/@babel/types/lib/index.d.ts": {
"version": "a17ba25a194979a88bfd925e849d76b63f68c4160b69a99c5d723eac5ffddaf0",
"signature": "a17ba25a194979a88bfd925e849d76b63f68c4160b69a99c5d723eac5ffddaf0",
"affectsGlobalScope": false
},
"../../../../../node_modules/.pnpm/@babel+parser@7.12.3/node_modules/@babel/parser/typings/babel-parser.d.ts": {
"version": "8678956904af215fe917b2df07b6c54f876fa64eb1f8a158e4ff38404cef3ff4",
"signature": "8678956904af215fe917b2df07b6c54f876fa64eb1f8a158e4ff38404cef3ff4",
Expand Down Expand Up @@ -744,15 +749,15 @@
"configFilePath": "./tsconfig.json",
"noEmitHelpers": true,
"importHelpers": true,
"noResolve": false,
"noEmit": false,
"emitDeclarationOnly": false,
"noResolve": false,
"sourceMap": true,
"inlineSources": true
},
"referencedMap": {
"../../../../../node_modules/.pnpm/@babel+parser@7.12.3/node_modules/@babel/parser/typings/babel-parser.d.ts": [
"../../../../../node_modules/.pnpm/@babel+types@7.12.1/node_modules/@babel/types/lib/index.d.ts"
"../../../../../node_modules/.pnpm/@babel+types@7.14.5/node_modules/@babel/types/lib/index.d.ts"
],
"../../../../../node_modules/.pnpm/@types+babel__core@7.1.10/node_modules/@types/babel__core/index.d.ts": [
"../../../../../node_modules/.pnpm/@babel+parser@7.12.3/node_modules/@babel/parser/typings/babel-parser.d.ts",
Expand Down Expand Up @@ -1154,7 +1159,7 @@
},
"exportedModulesMap": {
"../../../../../node_modules/.pnpm/@babel+parser@7.12.3/node_modules/@babel/parser/typings/babel-parser.d.ts": [
"../../../../../node_modules/.pnpm/@babel+types@7.12.1/node_modules/@babel/types/lib/index.d.ts"
"../../../../../node_modules/.pnpm/@babel+types@7.14.5/node_modules/@babel/types/lib/index.d.ts"
],
"../../../../../node_modules/.pnpm/@types+babel__core@7.1.10/node_modules/@types/babel__core/index.d.ts": [
"../../../../../node_modules/.pnpm/@babel+parser@7.12.3/node_modules/@babel/parser/typings/babel-parser.d.ts",
Expand Down Expand Up @@ -1557,6 +1562,7 @@
"semanticDiagnosticsPerFile": [
"../../../../../node_modules/.pnpm/@babel+parser@7.12.3/node_modules/@babel/parser/typings/babel-parser.d.ts",
"../../../../../node_modules/.pnpm/@babel+types@7.12.1/node_modules/@babel/types/lib/index.d.ts",
"../../../../../node_modules/.pnpm/@babel+types@7.14.5/node_modules/@babel/types/lib/index.d.ts",
"../../../../../node_modules/.pnpm/@types+babel__core@7.1.10/node_modules/@types/babel__core/index.d.ts",
"../../../../../node_modules/.pnpm/@types+babel__generator@7.6.2/node_modules/@types/babel__generator/index.d.ts",
"../../../../../node_modules/.pnpm/@types+babel__template@7.0.3/node_modules/@types/babel__template/index.d.ts",
Expand Down
@@ -0,0 +1,3 @@
const answer = 42;

export { answer };
@@ -0,0 +1,7 @@
{
"compilerOptions": {
"emitDeclarationOnly": true,
"declaration": true,
"outDir": "dist"
}
}
3 changes: 3 additions & 0 deletions packages/typescript/test/fixtures/noForceEmit/noEmit/main.ts
@@ -0,0 +1,3 @@
const answer = 42;

export { answer };
@@ -0,0 +1,7 @@
{
"compilerOptions": {
"declaration": true,
"noEmit": true,
"outDir": "dist"
}
}
63 changes: 63 additions & 0 deletions packages/typescript/test/test.js
Expand Up @@ -1208,3 +1208,66 @@ test.serial('works when code is in src directory', async (t) => {
['index.js', 'index.d.ts']
);
});

test.serial('noForceEmit option defers to tsconfig.json for emitDeclarationOnly', async (t) => {
const input = 'fixtures/noForceEmit/emitDeclarationOnly/main.ts';
const warnings = [];
const bundle = await rollup({
input,
plugins: [
typescript({
tsconfig: 'fixtures/noForceEmit/emitDeclarationOnly/tsconfig.json',
noForceEmit: true
})
],
onwarn(warning) {
warnings.push(warning);
}
});
// generate a single output bundle, in which case, declaration files were not correctly emitted
const output = await getCode(
bundle,
{ format: 'esm', file: 'fixtures/noForceEmit/emitDeclarationOnly/dist/main.js' },
true
);

t.deepEqual(
output.map((out) => out.fileName),
// original file is passed through, main.d.ts is emitted
['main.js', 'main.d.ts']
);
t.is(warnings.length, 0);
// test that NO transpilation happened
const originalCode = fs.readFileSync(path.join(__dirname, input), 'utf8');
t.is(output[0].code, originalCode);
});

test.serial('noForceEmit option defers to tsconfig.json for noEmit', async (t) => {
const input = 'fixtures/noForceEmit/noEmit/main.ts';
const warnings = [];
const bundle = await rollup({
input,
plugins: [
typescript({ tsconfig: 'fixtures/noForceEmit/noEmit/tsconfig.json', noForceEmit: true })
],
onwarn(warning) {
warnings.push(warning);
}
});
// generate a single output bundle, in which case, declaration files were not correctly emitted
const output = await getCode(
bundle,
{ format: 'esm', file: 'fixtures/noForceEmit/noEmit/dist/main.js' },
true
);

t.deepEqual(
output.map((out) => out.fileName),
// no `main.d.ts`, main.js is passed through
['main.js']
);
t.is(warnings.length, 0);
// test that NO transpilation happened
const originalCode = fs.readFileSync(path.join(__dirname, input), 'utf8');
t.is(output[0].code, originalCode);
});
4 changes: 4 additions & 0 deletions packages/typescript/types/index.d.ts
Expand Up @@ -83,6 +83,10 @@ export interface RollupTypescriptPluginOptions {
* Pass additional compiler options to the plugin.
*/
compilerOptions?: PartialCompilerOptions;
/**
* Override force setting of `noEmit` and `emitDeclarationOnly` and use what is defined in `tsconfig.json`
*/
noForceEmit?: boolean;
}

export interface FlexibleCompilerOptions extends CompilerOptions {
Expand Down

0 comments on commit a21db7d

Please sign in to comment.