Skip to content

Commit ac46ec1

Browse files
NotWoodsLarsDenBakker
authored andcommitted
feat(typescript): Move to BuilderProgram API (rollup#217)
* Move to BuilderProgram API * Update tests for new API * Use include/exclude for files * Clean up refactored code * Change tslib to be a path instead of source * Update overriding test * Work on supporting project references * Working declaration files * Stop watching after build is done * Add warnings for sourceMap flag * Add incremental build support * Add rebuild test * Separate module resolution * Work on project reference support * Fix incremental test * Fix path normalization * Cleanup * Update override options * Skip project reference test * Try skipping incremental build * Try no cache * Try serial * Posix paths
1 parent 8f22b12 commit ac46ec1

File tree

51 files changed

+2885
-467
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+2885
-467
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ pnpm-debug.log
1313
!packages/node-resolve/test/fixtures/**/node_modules
1414
!packages/commonjs/test/**/node_modules
1515
!packages/typescript/test/fixtures/**/node_modules
16+
!packages/typescript/test/fixtures/**/dist

packages/typescript/README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ Overrides the injected TypeScript helpers with a custom version.
108108

109109
```js
110110
typescript({
111-
tslib: fs.readFileSync(require.resolve('some-fork-of-tslib'))
111+
tslib: require.resolve('some-fork-of-tslib')
112112
});
113113
```
114114

@@ -134,8 +134,6 @@ Declaration files are automatically included if they are listed in the `files` f
134134

135135
These compiler options are ignored by Rollup:
136136

137-
- `declaration`, `declarationMap`: This plugin currently cannot emit declaration files.
138-
- `incremental`, `tsBuildInfoFile`: This plugin currently does not support incremental compilation using Typescript.
139137
- `noEmitHelpers`, `importHelpers`: The `tslib` helper module always must be used.
140138
- `noEmit`, `emitDeclarationOnly`: Typescript needs to emit code for the plugin to work with.
141139
- `noResolve`: Preventing Typescript from resolving code may break compilation

packages/typescript/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"ci:coverage": "nyc pnpm run test && nyc report --reporter=text-lcov > coverage.lcov",
2020
"ci:lint": "pnpm run build && pnpm run lint",
2121
"ci:lint:commits": "commitlint --from=${CIRCLE_BRANCH} --to=${CIRCLE_SHA1}",
22-
"ci:test": "pnpm run test -- --verbose",
22+
"ci:test": "pnpm run test -- --verbose --serial",
2323
"lint": "pnpm run lint:js && pnpm run lint:docs && pnpm run lint:package",
2424
"lint:docs": "prettier --single-quote --write README.md",
2525
"lint:js": "eslint --fix --cache src test --ext .js,.ts",

packages/typescript/src/diagnostics/emit.ts

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,49 @@ import diagnosticToWarning from './toWarning';
66
// `Cannot compile modules into 'es6' when targeting 'ES5' or lower.`
77
const CANNOT_COMPILE_ESM = 1204;
88

9+
/**
10+
* Emit a Rollup warning or error for a Typescript type error.
11+
*/
12+
export function emitDiagnostic(
13+
ts: typeof import('typescript'),
14+
context: PluginContext,
15+
host: DiagnosticsHost,
16+
diagnostic: import('typescript').Diagnostic
17+
) {
18+
if (diagnostic.code === CANNOT_COMPILE_ESM) return;
19+
20+
const { noEmitOnError } = host.getCompilationSettings();
21+
22+
// Build a Rollup warning object from the diagnostics object.
23+
const warning = diagnosticToWarning(ts, host, diagnostic);
24+
25+
// Errors are fatal. Otherwise emit warnings.
26+
if (noEmitOnError && diagnostic.category === ts.DiagnosticCategory.Error) {
27+
context.error(warning);
28+
} else {
29+
context.warn(warning);
30+
}
31+
}
32+
33+
export function buildDiagnosticReporter(
34+
ts: typeof import('typescript'),
35+
context: PluginContext,
36+
host: DiagnosticsHost
37+
): import('typescript').DiagnosticReporter {
38+
return function reportDiagnostics(diagnostic) {
39+
emitDiagnostic(ts, context, host, diagnostic);
40+
};
41+
}
42+
943
/**
1044
* For each type error reported by Typescript, emit a Rollup warning or error.
1145
*/
12-
export default function emitDiagnostics(
46+
export function emitDiagnostics(
1347
ts: typeof import('typescript'),
1448
context: PluginContext,
1549
host: DiagnosticsHost,
1650
diagnostics: readonly import('typescript').Diagnostic[] | undefined
1751
) {
1852
if (!diagnostics) return;
19-
const { noEmitOnError } = host.getCompilationSettings();
20-
21-
diagnostics
22-
.filter((diagnostic) => diagnostic.code !== CANNOT_COMPILE_ESM)
23-
.forEach((diagnostic) => {
24-
// Build a Rollup warning object from the diagnostics object.
25-
const warning = diagnosticToWarning(ts, host, diagnostic);
26-
27-
// Errors are fatal. Otherwise emit warnings.
28-
if (noEmitOnError && diagnostic.category === ts.DiagnosticCategory.Error) {
29-
context.error(warning);
30-
} else {
31-
context.warn(warning);
32-
}
33-
});
53+
diagnostics.forEach(buildDiagnosticReporter(ts, context, host));
3454
}

packages/typescript/src/documentRegistry.ts

Lines changed: 0 additions & 26 deletions
This file was deleted.

packages/typescript/src/host.ts

Lines changed: 0 additions & 109 deletions
This file was deleted.

packages/typescript/src/index.ts

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

3-
import { Plugin } from 'rollup';
3+
import { Plugin, SourceDescription } from 'rollup';
44

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

7-
import emitDiagnostics from './diagnostics/emit';
87
import createFormattingHost from './diagnostics/host';
9-
import getDocumentRegistry from './documentRegistry';
10-
import createHost from './host';
11-
import { emitParsedOptionsErrors, getPluginOptions, parseTypescriptConfig } from './options';
12-
import typescriptOutputToRollupTransformation from './outputToRollupTransformation';
13-
import { TSLIB_ID } from './tslib';
8+
import createModuleResolver from './moduleResolution';
9+
import getPluginOptions from './options/plugin';
10+
import { emitParsedOptionsErrors, parseTypescriptConfig } from './options/tsconfig';
11+
import { validatePaths, validateSourceMap } from './options/validate';
12+
import findTypescriptOutput from './outputFile';
13+
import createWatchProgram from './watchProgram';
1414

1515
export default function typescript(options: RollupTypescriptOptions = {}): Plugin {
1616
const { filter, tsconfig, compilerOptions, tslib, typescript: ts } = getPluginOptions(options);
17+
const emittedFiles = new Map<string, string>();
18+
const declarationFiles = new Set<string>();
1719

1820
const parsedOptions = parseTypescriptConfig(ts, tsconfig, compilerOptions);
21+
parsedOptions.fileNames = parsedOptions.fileNames.filter(filter);
22+
1923
const formatHost = createFormattingHost(ts, parsedOptions.options);
20-
const host = createHost(ts, parsedOptions);
21-
const services = ts.createLanguageService(host, getDocumentRegistry(ts, process.cwd()));
24+
const resolveModule = createModuleResolver(ts, formatHost);
25+
26+
let program: import('typescript').Watch<unknown> | null = null;
27+
28+
function normalizePath(fileName: string) {
29+
return fileName.split(path.win32.sep).join(path.posix.sep);
30+
}
2231

2332
return {
2433
name: 'typescript',
2534

2635
buildStart() {
2736
emitParsedOptionsErrors(ts, this, parsedOptions);
37+
38+
program = createWatchProgram(ts, this, {
39+
formatHost,
40+
resolveModule,
41+
parsedOptions,
42+
writeFile(fileName, data) {
43+
emittedFiles.set(fileName, data);
44+
}
45+
});
46+
},
47+
48+
buildEnd() {
49+
if (process.env.ROLLUP_WATCH !== 'true') {
50+
// ESLint doesn't understand optional chaining
51+
// eslint-disable-next-line
52+
program?.close();
53+
}
54+
},
55+
56+
renderStart(outputOptions) {
57+
validateSourceMap(this, parsedOptions.options, outputOptions, parsedOptions.autoSetSourceMap);
58+
validatePaths(ts, this, parsedOptions.options, outputOptions);
2859
},
2960

3061
resolveId(importee, importer) {
3162
if (importee === 'tslib') {
32-
return TSLIB_ID;
63+
return tslib;
3364
}
3465

3566
if (!importer) return null;
3667

3768
// Convert path from windows separators to posix separators
38-
const containingFile = importer.split(path.win32.sep).join(path.posix.sep);
69+
const containingFile = normalizePath(importer);
3970

40-
const resolved = host.resolveModuleNames([importee], containingFile);
41-
const resolvedFile = resolved[0]?.resolvedFileName;
71+
const resolved = resolveModule(importee, containingFile);
4272

43-
if (resolvedFile) {
44-
if (resolvedFile.endsWith('.d.ts')) return null;
45-
return resolvedFile;
73+
if (resolved) {
74+
if (resolved.extension === '.d.ts') return null;
75+
return resolved.resolvedFileName;
4676
}
4777

4878
return null;
4979
},
5080

5181
load(id) {
52-
if (id === TSLIB_ID) {
53-
return tslib;
54-
}
55-
return null;
56-
},
57-
58-
transform(code, id) {
5982
if (!filter(id)) return null;
6083

61-
host.addFile(id, code);
62-
const output = services.getEmitOutput(id);
84+
const output = findTypescriptOutput(ts, parsedOptions, id, emittedFiles);
85+
output.declarations.forEach((declaration) => declarationFiles.add(declaration));
6386

64-
if (output.emitSkipped) {
65-
// Emit failed, print all diagnostics for this file
66-
const allDiagnostics = ([] as import('typescript').Diagnostic[])
67-
.concat(services.getSyntacticDiagnostics(id))
68-
.concat(services.getSemanticDiagnostics(id));
69-
emitDiagnostics(ts, this, formatHost, allDiagnostics);
87+
return output.code ? (output as SourceDescription) : null;
88+
},
7089

71-
throw new Error(`Couldn't compile ${id}`);
90+
generateBundle(outputOptions) {
91+
for (const id of declarationFiles) {
92+
const code = emittedFiles.get(id);
93+
if (code) {
94+
this.emitFile({
95+
type: 'asset',
96+
fileName: normalizePath(path.relative(outputOptions.dir!, id)),
97+
source: code
98+
});
99+
}
72100
}
73101

74-
return typescriptOutputToRollupTransformation(output.outputFiles);
75-
},
76-
77-
generateBundle() {
78-
const program = services.getProgram();
79-
if (program == null) return;
80-
emitDiagnostics(ts, this, formatHost, ts.getPreEmitDiagnostics(program));
102+
const tsBuildInfoPath = ts.getTsBuildInfoEmitOutputFilePath(parsedOptions.options);
103+
if (tsBuildInfoPath) {
104+
this.emitFile({
105+
type: 'asset',
106+
fileName: normalizePath(path.relative(outputOptions.dir!, tsBuildInfoPath)),
107+
source: emittedFiles.get(tsBuildInfoPath)
108+
});
109+
}
81110
}
82111
};
83112
}

0 commit comments

Comments
 (0)