diff --git a/packages/babel/README.md b/packages/babel/README.md index 3b573fbec..5c004e70a 100644 --- a/packages/babel/README.md +++ b/packages/babel/README.md @@ -94,7 +94,7 @@ A [picomatch pattern](https://github.com/micromatch/picomatch), or array of patt ### `filter` -Type: (id: string) => boolean
+Type: (id: string, code: string) => boolean
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. diff --git a/packages/babel/package.json b/packages/babel/package.json index 45bdda3b7..aeda64e99 100644 --- a/packages/babel/package.json +++ b/packages/babel/package.json @@ -55,7 +55,7 @@ "peerDependencies": { "@babel/core": "^7.0.0", "@types/babel__core": "^7.1.9", - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + "rollup": "^2.0.0||^3.0.0||^4.0.0" }, "peerDependenciesMeta": { "rollup": { diff --git a/packages/babel/src/index.js b/packages/babel/src/index.js index 125e6305a..322f8ada0 100644 --- a/packages/babel/src/index.js +++ b/packages/babel/src/index.js @@ -5,7 +5,7 @@ import { BUNDLED, HELPERS } from './constants.js'; import bundledHelpersPlugin from './bundledHelpersPlugin.js'; import preflightCheck from './preflightCheck.js'; import transformCode from './transformCode.js'; -import { addBabelPlugin, escapeRegExpCharacters, warnOnce, stripQuery } from './utils.js'; +import { addBabelPlugin, escapeRegExpCharacters, warnOnce } from './utils.js'; const unpackOptions = ({ extensions = babel.DEFAULT_EXTENSIONS, @@ -37,7 +37,7 @@ const warnAboutDeprecatedHelpersOption = ({ deprecatedOption, suggestion }) => { ); }; -const unpackInputPluginOptions = ({ skipPreflightCheck = false, ...rest }, rollupVersion) => { +const unpackInputPluginOptions = ({ skipPreflightCheck = false, ...rest }) => { if ('runtimeHelpers' in rest) { warnAboutDeprecatedHelpersOption({ deprecatedOption: 'runtimeHelpers', @@ -63,8 +63,7 @@ const unpackInputPluginOptions = ({ skipPreflightCheck = false, ...rest }, rollu supportsStaticESM: true, supportsDynamicImport: true, supportsTopLevelAwait: true, - // todo: remove version checks for 1.20 - 1.25 when we bump peer deps - supportsExportNamespaceFrom: !rollupVersion.match(/^1\.2[0-5]\./), + supportsExportNamespaceFrom: true, ...rest.caller } }); @@ -110,77 +109,76 @@ function createBabelInputPluginFactory(customCallback = returnObject) { overrides ); - let babelHelpers; - let babelOptions; - let filter; - let skipPreflightCheck; - return { - name: 'babel', - - options() { - // todo: remove options hook and hoist declarations when version checks are removed - let exclude; - let include; - let extensions; - let customFilter; - - ({ - exclude, - extensions, - babelHelpers, - include, - filter: customFilter, - skipPreflightCheck, - ...babelOptions - } = unpackInputPluginOptions(pluginOptionsWithOverrides, this.meta.rollupVersion)); + const { + exclude, + extensions, + babelHelpers, + include, + filter: customFilter, + skipPreflightCheck, + ...babelOptions + } = unpackInputPluginOptions(pluginOptionsWithOverrides); + + const extensionRegExp = new RegExp( + `(${extensions.map(escapeRegExpCharacters).join('|')})(\\?.*)?(#.*)?$` + ); + if (customFilter && (include || exclude)) { + throw new Error('Could not handle include or exclude with custom filter together'); + } + const userDefinedFilter = + typeof customFilter === 'function' ? customFilter : createFilter(include, exclude); + const filter = (id, code) => extensionRegExp.test(id) && userDefinedFilter(id, code); - const extensionRegExp = new RegExp( - `(${extensions.map(escapeRegExpCharacters).join('|')})$` - ); - if (customFilter && (include || exclude)) { - throw new Error('Could not handle include or exclude with custom filter together'); - } - const userDefinedFilter = - typeof customFilter === 'function' ? customFilter : createFilter(include, exclude); - filter = (id) => extensionRegExp.test(stripQuery(id).bareId) && userDefinedFilter(id); + const helpersFilter = { id: new RegExp(`^${escapeRegExpCharacters(HELPERS)}$`) }; - return null; - }, + return { + name: 'babel', - resolveId(id) { - if (id !== HELPERS) { - return null; + resolveId: { + filter: helpersFilter, + handler(id) { + if (id !== HELPERS) { + return null; + } + return id; } - return id; }, - load(id) { - if (id !== HELPERS) { - return null; + load: { + filter: helpersFilter, + handler(id) { + if (id !== HELPERS) { + return null; + } + return babel.buildExternalHelpers(null, 'module'); } - return babel.buildExternalHelpers(null, 'module'); }, - transform(code, filename) { - if (!filter(filename)) return null; - if (filename === HELPERS) return null; - - return transformCode( - code, - { ...babelOptions, filename }, - overrides, - customOptions, - this, - async (transformOptions) => { - if (!skipPreflightCheck) { - await preflightCheck(this, babelHelpers, transformOptions); + transform: { + filter: { + id: extensionRegExp + }, + async handler(code, filename) { + if (!(await filter(filename, code))) return null; + if (filename === HELPERS) return null; + + return transformCode( + code, + { ...babelOptions, filename }, + overrides, + customOptions, + this, + async (transformOptions) => { + if (!skipPreflightCheck) { + await preflightCheck(this, babelHelpers, transformOptions); + } + + return babelHelpers === BUNDLED + ? addBabelPlugin(transformOptions, bundledHelpersPlugin) + : transformOptions; } - - return babelHelpers === BUNDLED - ? addBabelPlugin(transformOptions, bundledHelpersPlugin) - : transformOptions; - } - ); + ); + } } }; }; diff --git a/packages/babel/test/as-input-plugin.mjs b/packages/babel/test/as-input-plugin.mjs index c7d145558..4b8d9495f 100644 --- a/packages/babel/test/as-input-plugin.mjs +++ b/packages/babel/test/as-input-plugin.mjs @@ -122,6 +122,23 @@ console.log("the answer is ".concat(foo())); ); }); +test('does not babelify excluded code with code-based filter', async (t) => { + const filter = (id, code) => code.includes('the answer is'); + const code = await generate('exclusions/main.js', { filter }); + // eslint-disable-next-line no-template-curly-in-string + t.false(code.includes('${foo()}')); + t.true(code.includes('=> 42')); + t.is( + code, + `'use strict'; + +const foo = () => 42; + +console.log("the answer is ".concat(foo())); +` + ); +}); + test('does babelify included code with custom filter', async (t) => { const filter = createFilter('**/foo.js', [], { resolve: DIRNAME @@ -143,6 +160,25 @@ console.log(\`the answer is \${foo()}\`); ); }); +test('does babelify excluded code with code-based filter', async (t) => { + const filter = (id, code) => !code.includes('the answer is'); + const code = await generate('exclusions/main.js', { filter }); + // eslint-disable-next-line no-template-curly-in-string + t.true(code.includes('${foo()}')); + t.false(code.includes('=> 42')); + t.is( + code, + `'use strict'; + +var foo = function foo() { + return 42; +}; + +console.log(\`the answer is \${foo()}\`); +` + ); +}); + test('can not pass include or exclude when custom filter specified', async (t) => { const filter = createFilter('**/foo.js', [], { resolve: DIRNAME diff --git a/packages/babel/types/index.d.ts b/packages/babel/types/index.d.ts index 20288d8a0..76cf6af8a 100644 --- a/packages/babel/types/index.d.ts +++ b/packages/babel/types/index.d.ts @@ -1,5 +1,5 @@ import type { Plugin, PluginContext, TransformPluginContext } from 'rollup'; -import type { FilterPattern, CreateFilter } from '@rollup/pluginutils'; +import type { FilterPattern } from '@rollup/pluginutils'; import type * as babelCore from '@babel/core'; export interface RollupBabelInputPluginOptions @@ -23,7 +23,7 @@ export interface RollupBabelInputPluginOptions * const filter = createFilter(include, exclude, {}); * @default undefined; */ - filter?: ReturnType; + filter?: (id: string, code: string) => Promise; /** * 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. * @default ['.js', '.jsx', '.es6', '.es', '.mjs']