Skip to content

Commit 204c3db

Browse files
committed
feat(babel): improve filtering capability & performance
- drops support for rollup 1.x, which allows simplifying the implementation - implements 'plugin hook filters' for improved performance under rolldown - passes a new `code` argument to custom filter functions, so that consumers can add more advanced filtering logic - allows custom filter functions to be async
1 parent c8e78c8 commit 204c3db

File tree

5 files changed

+103
-69
lines changed

5 files changed

+103
-69
lines changed

packages/babel/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ A [picomatch pattern](https://github.com/micromatch/picomatch), or array of patt
9494

9595
### `filter`
9696

97-
Type: (id: string) => boolean<br>
97+
Type: (id: string, code: string) => boolean<br>
9898

9999
Custom [filter function](https://github.com/rollup/plugins/tree/master/packages/pluginutils#createfilter) can be used to determine whether or not certain modules should be operated upon.
100100

packages/babel/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
"peerDependencies": {
5656
"@babel/core": "^7.0.0",
5757
"@types/babel__core": "^7.1.9",
58-
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
58+
"rollup": "^2.0.0||^3.0.0||^4.0.0"
5959
},
6060
"peerDependenciesMeta": {
6161
"rollup": {

packages/babel/src/index.js

Lines changed: 63 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { BUNDLED, HELPERS } from './constants.js';
55
import bundledHelpersPlugin from './bundledHelpersPlugin.js';
66
import preflightCheck from './preflightCheck.js';
77
import transformCode from './transformCode.js';
8-
import { addBabelPlugin, escapeRegExpCharacters, warnOnce, stripQuery } from './utils.js';
8+
import { addBabelPlugin, escapeRegExpCharacters, warnOnce } from './utils.js';
99

1010
const unpackOptions = ({
1111
extensions = babel.DEFAULT_EXTENSIONS,
@@ -37,7 +37,7 @@ const warnAboutDeprecatedHelpersOption = ({ deprecatedOption, suggestion }) => {
3737
);
3838
};
3939

40-
const unpackInputPluginOptions = ({ skipPreflightCheck = false, ...rest }, rollupVersion) => {
40+
const unpackInputPluginOptions = ({ skipPreflightCheck = false, ...rest }) => {
4141
if ('runtimeHelpers' in rest) {
4242
warnAboutDeprecatedHelpersOption({
4343
deprecatedOption: 'runtimeHelpers',
@@ -63,8 +63,7 @@ const unpackInputPluginOptions = ({ skipPreflightCheck = false, ...rest }, rollu
6363
supportsStaticESM: true,
6464
supportsDynamicImport: true,
6565
supportsTopLevelAwait: true,
66-
// todo: remove version checks for 1.20 - 1.25 when we bump peer deps
67-
supportsExportNamespaceFrom: !rollupVersion.match(/^1\.2[0-5]\./),
66+
supportsExportNamespaceFrom: true,
6867
...rest.caller
6968
}
7069
});
@@ -110,77 +109,76 @@ function createBabelInputPluginFactory(customCallback = returnObject) {
110109
overrides
111110
);
112111

113-
let babelHelpers;
114-
let babelOptions;
115-
let filter;
116-
let skipPreflightCheck;
117-
return {
118-
name: 'babel',
119-
120-
options() {
121-
// todo: remove options hook and hoist declarations when version checks are removed
122-
let exclude;
123-
let include;
124-
let extensions;
125-
let customFilter;
126-
127-
({
128-
exclude,
129-
extensions,
130-
babelHelpers,
131-
include,
132-
filter: customFilter,
133-
skipPreflightCheck,
134-
...babelOptions
135-
} = unpackInputPluginOptions(pluginOptionsWithOverrides, this.meta.rollupVersion));
112+
const {
113+
exclude,
114+
extensions,
115+
babelHelpers,
116+
include,
117+
filter: customFilter,
118+
skipPreflightCheck,
119+
...babelOptions
120+
} = unpackInputPluginOptions(pluginOptionsWithOverrides);
121+
122+
const extensionRegExp = new RegExp(
123+
`(${extensions.map(escapeRegExpCharacters).join('|')})(\\?.*)?(#.*)?$`
124+
);
125+
if (customFilter && (include || exclude)) {
126+
throw new Error('Could not handle include or exclude with custom filter together');
127+
}
128+
const userDefinedFilter =
129+
typeof customFilter === 'function' ? customFilter : createFilter(include, exclude);
130+
const filter = (id, code) => extensionRegExp.test(id) && userDefinedFilter(id, code);
136131

137-
const extensionRegExp = new RegExp(
138-
`(${extensions.map(escapeRegExpCharacters).join('|')})$`
139-
);
140-
if (customFilter && (include || exclude)) {
141-
throw new Error('Could not handle include or exclude with custom filter together');
142-
}
143-
const userDefinedFilter =
144-
typeof customFilter === 'function' ? customFilter : createFilter(include, exclude);
145-
filter = (id) => extensionRegExp.test(stripQuery(id).bareId) && userDefinedFilter(id);
132+
const helpersFilter = { id: new RegExp(`^${escapeRegExpCharacters(HELPERS)}$`) };
146133

147-
return null;
148-
},
134+
return {
135+
name: 'babel',
149136

150-
resolveId(id) {
151-
if (id !== HELPERS) {
152-
return null;
137+
resolveId: {
138+
filter: helpersFilter,
139+
handler(id) {
140+
if (id !== HELPERS) {
141+
return null;
142+
}
143+
return id;
153144
}
154-
return id;
155145
},
156146

157-
load(id) {
158-
if (id !== HELPERS) {
159-
return null;
147+
load: {
148+
filter: helpersFilter,
149+
handler(id) {
150+
if (id !== HELPERS) {
151+
return null;
152+
}
153+
return babel.buildExternalHelpers(null, 'module');
160154
}
161-
return babel.buildExternalHelpers(null, 'module');
162155
},
163156

164-
transform(code, filename) {
165-
if (!filter(filename)) return null;
166-
if (filename === HELPERS) return null;
167-
168-
return transformCode(
169-
code,
170-
{ ...babelOptions, filename },
171-
overrides,
172-
customOptions,
173-
this,
174-
async (transformOptions) => {
175-
if (!skipPreflightCheck) {
176-
await preflightCheck(this, babelHelpers, transformOptions);
157+
transform: {
158+
filter: {
159+
id: extensionRegExp
160+
},
161+
async handler(code, filename) {
162+
if (!(await filter(filename, code))) return null;
163+
if (filename === HELPERS) return null;
164+
165+
return transformCode(
166+
code,
167+
{ ...babelOptions, filename },
168+
overrides,
169+
customOptions,
170+
this,
171+
async (transformOptions) => {
172+
if (!skipPreflightCheck) {
173+
await preflightCheck(this, babelHelpers, transformOptions);
174+
}
175+
176+
return babelHelpers === BUNDLED
177+
? addBabelPlugin(transformOptions, bundledHelpersPlugin)
178+
: transformOptions;
177179
}
178-
179-
return babelHelpers === BUNDLED
180-
? addBabelPlugin(transformOptions, bundledHelpersPlugin)
181-
: transformOptions;
182-
}
183-
);
180+
);
181+
}
184182
}
185183
};
186184
};

packages/babel/test/as-input-plugin.mjs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,23 @@ console.log("the answer is ".concat(foo()));
122122
);
123123
});
124124

125+
test('does not babelify excluded code with code-based filter', async (t) => {
126+
const filter = (id, code) => code.includes('the answer is');
127+
const code = await generate('exclusions/main.js', { filter });
128+
// eslint-disable-next-line no-template-curly-in-string
129+
t.false(code.includes('${foo()}'));
130+
t.true(code.includes('=> 42'));
131+
t.is(
132+
code,
133+
`'use strict';
134+
135+
const foo = () => 42;
136+
137+
console.log("the answer is ".concat(foo()));
138+
`
139+
);
140+
});
141+
125142
test('does babelify included code with custom filter', async (t) => {
126143
const filter = createFilter('**/foo.js', [], {
127144
resolve: DIRNAME
@@ -143,6 +160,25 @@ console.log(\`the answer is \${foo()}\`);
143160
);
144161
});
145162

163+
test('does babelify excluded code with code-based filter', async (t) => {
164+
const filter = (id, code) => !code.includes('the answer is');
165+
const code = await generate('exclusions/main.js', { filter });
166+
// eslint-disable-next-line no-template-curly-in-string
167+
t.true(code.includes('${foo()}'));
168+
t.false(code.includes('=> 42'));
169+
t.is(
170+
code,
171+
`'use strict';
172+
173+
var foo = function foo() {
174+
return 42;
175+
};
176+
177+
console.log(\`the answer is \${foo()}\`);
178+
`
179+
);
180+
});
181+
146182
test('can not pass include or exclude when custom filter specified', async (t) => {
147183
const filter = createFilter('**/foo.js', [], {
148184
resolve: DIRNAME

packages/babel/types/index.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { Plugin, PluginContext, TransformPluginContext } from 'rollup';
2-
import type { FilterPattern, CreateFilter } from '@rollup/pluginutils';
2+
import type { FilterPattern } from '@rollup/pluginutils';
33
import type * as babelCore from '@babel/core';
44

55
export interface RollupBabelInputPluginOptions
@@ -23,7 +23,7 @@ export interface RollupBabelInputPluginOptions
2323
* const filter = createFilter(include, exclude, {});
2424
* @default undefined;
2525
*/
26-
filter?: ReturnType<CreateFilter>;
26+
filter?: (id: string, code: string) => Promise<boolean>;
2727
/**
2828
* An array of file extensions that Babel should transpile. If you want to transpile TypeScript files with this plugin it's essential to include .ts and .tsx in this option.
2929
* @default ['.js', '.jsx', '.es6', '.es', '.mjs']

0 commit comments

Comments
 (0)