Skip to content

Commit 031b566

Browse files
authored
feat(typescript): better error when tslib is not installed (#793)
* feat(typescript): better error when tslib is not installed * test: trim snapshot file path
1 parent a927ed8 commit 031b566

File tree

15 files changed

+473
-262
lines changed

15 files changed

+473
-262
lines changed

.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ module.exports = {
3131
tsx: 'never'
3232
}
3333
],
34+
'import/prefer-default-export': 'off',
3435
'import/no-namespace': 'off',
3536
'import/no-named-export': 'off',
3637
'no-redeclare': 'off',

package.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,12 @@
2626
"@ava/babel": "^1.0.1",
2727
"@typescript-eslint/eslint-plugin": "^4.9.0",
2828
"@typescript-eslint/parser": "^4.9.0",
29-
"ava": "^3.13.0",
29+
"ava": "^3.15.0",
3030
"chalk": "^4.1.0",
3131
"codecov-lite": "^1.0.3",
3232
"del-cli": "^3.0.1",
3333
"eslint-config-rollup": "^1.0.0",
3434
"esm": "^3.2.25",
35-
"execa": "^4.0.3",
3635
"globby": "^11.0.1",
3736
"husky": "^4.2.5",
3837
"lint-staged": "^10.5.2",
@@ -42,7 +41,6 @@
4241
"prettier-plugin-package": "^1.3.0",
4342
"ts-node": "^8.10.2",
4443
"tsconfig-paths": "^3.9.0",
45-
"tslib": "^2.0.0",
4644
"typescript": "^3.9.7",
4745
"yaml": "^1.10.0"
4846
},

packages/typescript/src/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import * as path from 'path';
22

3-
import { Plugin, SourceDescription } from 'rollup';
3+
import { Plugin, RollupOptions, SourceDescription } from 'rollup';
44
import type { Watch } from 'typescript';
55

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

88
import createFormattingHost from './diagnostics/host';
99
import createModuleResolver from './moduleResolution';
10-
import getPluginOptions from './options/plugin';
10+
import { getPluginOptions } from './options/plugin';
1111
import { emitParsedOptionsErrors, parseTypescriptConfig } from './options/tsconfig';
1212
import { validatePaths, validateSourceMap } from './options/validate';
1313
import findTypescriptOutput, { getEmittedFile } from './outputFile';
@@ -44,10 +44,10 @@ export default function typescript(options: RollupTypescriptOptions = {}): Plugi
4444
return {
4545
name: 'typescript',
4646

47-
buildStart() {
47+
buildStart(rollupOptions: RollupOptions) {
4848
emitParsedOptionsErrors(ts, this, parsedOptions);
4949

50-
preflight(parsedOptions, this);
50+
preflight({ config: parsedOptions, context: this, rollupOptions, tslib });
5151

5252
// Fixes a memory leak https://github.com/rollup/plugins/issues/322
5353
if (!program) {

packages/typescript/src/options/plugin.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { createFilter } from '@rollup/pluginutils';
22
import * as defaultTs from 'typescript';
33

44
import { RollupTypescriptOptions, PartialCompilerOptions } from '../../types';
5-
import getTsLibPath from '../tslib';
5+
import { getTsLibPath } from '../tslib';
66

77
/**
88
* Separate the Rollup plugin options from the Typescript compiler options,
@@ -14,7 +14,7 @@ import getTsLibPath from '../tslib';
1414
* - `typescript`: Instance of Typescript library (possibly custom).
1515
* - `tslib`: ESM code from the tslib helper library (possibly custom).
1616
*/
17-
export default function getPluginOptions(options: RollupTypescriptOptions) {
17+
export const getPluginOptions = (options: RollupTypescriptOptions) => {
1818
const {
1919
cacheDir,
2020
exclude,
@@ -37,4 +37,4 @@ export default function getPluginOptions(options: RollupTypescriptOptions) {
3737
tslib: tslib || getTsLibPath(),
3838
transformers
3939
};
40-
}
40+
};
Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,34 @@
1-
import { PluginContext } from 'rollup';
1+
import { PluginContext, RollupOptions } from 'rollup';
22
import { ModuleKind } from 'typescript';
33

44
import { TypeScriptConfig } from './options/tsconfig';
5+
// import { resolveIdAsync } from './tslib';
56

6-
const moduleError = `
7-
Rollup requires that TypeScript produces ES Modules. Unfortunately your configuration specifies a
7+
interface PreflightOptions {
8+
config: TypeScriptConfig;
9+
context: PluginContext;
10+
rollupOptions: RollupOptions;
11+
tslib: any;
12+
}
13+
14+
const pluginName = '@rollup/plugin-typescript';
15+
const moduleErrorMessage = `
16+
${pluginName}: Rollup requires that TypeScript produces ES Modules. Unfortunately your configuration specifies a
817
"module" other than "esnext". Unless you know what you're doing, please change "module" to "esnext"
918
in the target tsconfig.json file or plugin options.`.replace(/\n/g, '');
1019

20+
const tsLibErrorMessage = `${pluginName}: Could not find module 'tslib', which is required by this plugin. Is it installed?`;
21+
1122
let undef;
1223
const validModules = [ModuleKind.ES2015, ModuleKind.ES2020, ModuleKind.ESNext, undef];
1324

1425
// eslint-disable-next-line import/prefer-default-export
15-
export const preflight = (config: TypeScriptConfig, context: PluginContext) => {
26+
export const preflight = ({ config, context, rollupOptions, tslib }: PreflightOptions) => {
1627
if (!validModules.includes(config.options.module)) {
17-
context.warn(`@rollup/plugin-typescript: ${moduleError}`);
28+
context.warn(moduleErrorMessage);
29+
}
30+
31+
if (!rollupOptions.preserveModules && tslib === null) {
32+
context.error(tsLibErrorMessage);
1833
}
1934
};

packages/typescript/src/tslib.ts

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,27 @@
1-
import resolveId, { AsyncOpts } from 'resolve';
1+
import resolve, { SyncOpts } from 'resolve';
22

3-
const resolveIdAsync = (file: string, opts: AsyncOpts) =>
4-
new Promise<string>((fulfil, reject) =>
5-
resolveId(file, opts, (err, contents) => (err || typeof contents === 'undefined' ? reject(err) : fulfil(contents)))
6-
);
3+
// const resolveIdAsync = (file: string, opts: AsyncOpts) =>
4+
// new Promise<string>((fulfil, reject) =>
5+
// resolveId(file, opts, (err, contents) =>
6+
// err || typeof contents === 'undefined' ? reject(err) : fulfil(contents)
7+
// )
8+
// );
9+
10+
const resolveId = (file: string, opts: SyncOpts) => resolve.sync(file, opts);
711

812
/**
913
* Returns code asynchronously for the tslib helper library.
1014
*/
11-
export default function getTsLibPath() {
12-
return resolveIdAsync('tslib/tslib.es6.js', { basedir: __dirname });
13-
}
15+
export const getTsLibPath = () => {
16+
// Note: This isn't preferable, but we've no other way to test this bit. Removing the tslib devDep
17+
// during the test run doesn't work due to the nature of the pnpm flat node_modules, and
18+
// other workspace dependencies that depenend upon tslib.
19+
try {
20+
// eslint-disable-next-line no-underscore-dangle
21+
return resolveId(process.env.__TSLIB_TEST_PATH__ || 'tslib/tslib.es6.js', {
22+
basedir: __dirname
23+
});
24+
} catch (_) {
25+
return null;
26+
}
27+
};
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Snapshot report for `test/tslib.ts`
2+
3+
The actual snapshot is saved in `tslib.ts.snap`.
4+
5+
Generated by [AVA](https://avajs.dev).
6+
7+
## fails on bad tslib path
8+
9+
> Snapshot 1
10+
11+
Error {
12+
code: 'ENOENT',
13+
errno: -2,
14+
path: 'fixtures/joker/tslib.js',
15+
syscall: 'open',
16+
watchFiles: [
17+
'packages/typescript/test/fixtures/overriding-tslib/main.ts',
18+
'fixtures/joker/tslib.js',
19+
],
20+
message: 'Could not load fixtures/joker/tslib.js (imported by fixtures/overriding-tslib/main.ts): ENOENT: no such file or directory, open \'fixtures/joker/tslib.js\'',
21+
}
22+
23+
## fails without tslib installed
24+
25+
> Snapshot 1
26+
27+
Error {
28+
code: 'PLUGIN_ERROR',
29+
hook: 'buildStart',
30+
plugin: 'typescript',
31+
message: '@rollup/plugin-typescript: Could not find module \'tslib\', which is required by this plugin. Is it installed?',
32+
}
574 Bytes
Binary file not shown.

packages/typescript/test/test.js

Lines changed: 2 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,14 @@ const test = require('ava');
66
const { rollup, watch } = require('rollup');
77
const ts = require('typescript');
88

9-
const { getCode, testBundle } = require('../../../util/test');
9+
const { evaluateBundle, getCode, onwarn } = require('../../../util/test');
1010

1111
const typescript = require('..');
1212

1313
test.beforeEach(() => process.chdir(__dirname));
1414

1515
const outputOptions = { format: 'esm' };
1616

17-
async function evaluateBundle(bundle) {
18-
const { module } = await testBundle(null, bundle);
19-
return module.exports;
20-
}
21-
22-
function onwarn(warning) {
23-
// eslint-disable-next-line no-console
24-
console.warn(warning.toString());
25-
}
26-
2717
test.serial('runs code through typescript', async (t) => {
2818
const bundle = await rollup({
2919
input: 'fixtures/basic/main.ts',
@@ -263,7 +253,7 @@ test.serial('ignore type errors if noEmitOnError is false', async (t) => {
263253

264254
t.true(code.includes(`console.log('hello world')`));
265255

266-
t.is(warnings.length, 1);
256+
t.is(warnings.length, 2);
267257

268258
t.is(warnings[0].code, 'PLUGIN_WARNING');
269259
t.is(warnings[0].plugin, 'typescript');
@@ -333,38 +323,6 @@ test.serial('supports overriding the TypeScript version', async (t) => {
333323
t.is(result, 1337);
334324
});
335325

336-
test.serial('supports overriding tslib with a custom path', async (t) => {
337-
const bundle = await rollup({
338-
input: 'fixtures/overriding-tslib/main.ts',
339-
plugins: [
340-
typescript({
341-
tsconfig: 'fixtures/overriding-tslib/tsconfig.json',
342-
tslib: 'fixtures/overriding-tslib/tslib.js'
343-
})
344-
],
345-
onwarn
346-
});
347-
const code = await evaluateBundle(bundle);
348-
349-
t.is(code.myParent.baseMethod(), 'base method');
350-
});
351-
352-
test.serial('supports overriding tslib with a custom path in a promise', async (t) => {
353-
const bundle = await rollup({
354-
input: 'fixtures/overriding-tslib/main.ts',
355-
plugins: [
356-
typescript({
357-
tsconfig: 'fixtures/overriding-tslib/tsconfig.json',
358-
tslib: Promise.resolve('fixtures/overriding-tslib/tslib.js')
359-
})
360-
],
361-
onwarn
362-
});
363-
const code = await evaluateBundle(bundle);
364-
365-
t.is(code.myParent.baseMethod(), 'base method');
366-
});
367-
368326
test.serial('should not resolve .d.ts files', async (t) => {
369327
const bundle = await rollup({
370328
input: 'fixtures/dts/main.ts',
@@ -521,19 +479,6 @@ test.serial('should throw on bad options', async (t) => {
521479
]);
522480
});
523481

524-
test.serial('creates _tslib.js file when preserveModules is used', async (t) => {
525-
const bundle = await rollup({
526-
input: 'fixtures/preserve-modules/main.ts',
527-
plugins: [typescript({ tsconfig: 'fixtures/preserve-modules/tsconfig.json' })],
528-
preserveModules: true,
529-
onwarn
530-
});
531-
532-
const files = await getCode(bundle, { format: 'es' }, true);
533-
t.true(files[0].fileName.includes('main.js'), files[0].fileName);
534-
t.true(files[1].fileName.includes('tslib.es6.js'), files[1].fileName);
535-
});
536-
537482
test.serial('should handle re-exporting types', async (t) => {
538483
const bundle = await rollup({
539484
input: 'fixtures/reexport-type/main.ts',
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"extends": "../tsconfig.json",
3+
"include": ["."]
4+
}

0 commit comments

Comments
 (0)