diff --git a/packages/sucrase/README.md b/packages/sucrase/README.md
index 680e5a7b2..b639c11c8 100644
--- a/packages/sucrase/README.md
+++ b/packages/sucrase/README.md
@@ -55,10 +55,15 @@ Then call `rollup` either via the [CLI](https://www.rollupjs.org/guide/en/#comma
The following [Sucrase options](https://github.com/alangpierce/sucrase#transforms) may be passed as options for this plugin:
+- `disableESTransforms`
- `enableLegacyBabel5ModuleInterop`
- `enableLegacyTypeScriptModuleInterop`
+- `injectCreateRequireForImportRequire`
- `jsxFragmentPragma`
+- `jsxImportSource`
- `jsxPragma`
+- `jsxRuntime`
+- `preserveDynamicImport`
- `production`
- `transforms`
diff --git a/packages/sucrase/src/index.js b/packages/sucrase/src/index.js
index dfeb9a765..cefe1cf4c 100644
--- a/packages/sucrase/src/index.js
+++ b/packages/sucrase/src/index.js
@@ -13,7 +13,7 @@ export default function sucrase(opts = {}) {
// eslint-disable-next-line consistent-return
resolveId(importee, importer) {
if (importer && /^[./]/.test(importee)) {
- const resolved = path.resolve(importer ? path.dirname(importer) : process.cwd(), importee);
+ const resolved = path.resolve(path.dirname(importer), importee);
// resolve in the same order that TypeScript resolves modules
const resolvedFilenames = [
`${resolved}.ts`,
@@ -42,8 +42,12 @@ export default function sucrase(opts = {}) {
const result = transform(code, {
transforms: opts.transforms,
+ jsxRuntime: opts.jsxRuntime,
+ jsxImportSource: opts.jsxImportSource,
jsxPragma: opts.jsxPragma,
jsxFragmentPragma: opts.jsxFragmentPragma,
+ preserveDynamicImport: opts.preserveDynamicImport,
+ injectCreateRequireForImportRequire: opts.injectCreateRequireForImportRequire,
enableLegacyTypeScriptModuleInterop: opts.enableLegacyTypeScriptModuleInterop,
enableLegacyBabel5ModuleInterop: opts.enableLegacyBabel5ModuleInterop,
production: opts.production,
diff --git a/packages/sucrase/test/fixtures/inject-create-require/main.ts b/packages/sucrase/test/fixtures/inject-create-require/main.ts
new file mode 100644
index 000000000..6dc8b4b3c
--- /dev/null
+++ b/packages/sucrase/test/fixtures/inject-create-require/main.ts
@@ -0,0 +1,3 @@
+import foo = require('foo');
+
+export default foo;
diff --git a/packages/sucrase/test/fixtures/jsx-import-source/example.jsx b/packages/sucrase/test/fixtures/jsx-import-source/example.jsx
new file mode 100644
index 000000000..87153ec31
--- /dev/null
+++ b/packages/sucrase/test/fixtures/jsx-import-source/example.jsx
@@ -0,0 +1 @@
+export default () =>
hello world
diff --git a/packages/sucrase/test/fixtures/jsx-import-source/main.js b/packages/sucrase/test/fixtures/jsx-import-source/main.js
new file mode 100644
index 000000000..9d03081dd
--- /dev/null
+++ b/packages/sucrase/test/fixtures/jsx-import-source/main.js
@@ -0,0 +1,4 @@
+/* eslint-disable import/extensions */
+import example from './example.jsx';
+
+t.snapshot(example.toString());
diff --git a/packages/sucrase/test/fixtures/jsx-runtime/example.jsx b/packages/sucrase/test/fixtures/jsx-runtime/example.jsx
new file mode 100644
index 000000000..87153ec31
--- /dev/null
+++ b/packages/sucrase/test/fixtures/jsx-runtime/example.jsx
@@ -0,0 +1 @@
+export default () => hello world
diff --git a/packages/sucrase/test/fixtures/jsx-runtime/main.js b/packages/sucrase/test/fixtures/jsx-runtime/main.js
new file mode 100644
index 000000000..9d03081dd
--- /dev/null
+++ b/packages/sucrase/test/fixtures/jsx-runtime/main.js
@@ -0,0 +1,4 @@
+/* eslint-disable import/extensions */
+import example from './example.jsx';
+
+t.snapshot(example.toString());
diff --git a/packages/sucrase/test/fixtures/preserve-dynamic-import/main.js b/packages/sucrase/test/fixtures/preserve-dynamic-import/main.js
new file mode 100644
index 000000000..edb2ee72e
--- /dev/null
+++ b/packages/sucrase/test/fixtures/preserve-dynamic-import/main.js
@@ -0,0 +1,3 @@
+export default function loadModule(name) {
+ return import(name);
+}
diff --git a/packages/sucrase/test/snapshots/test.js.md b/packages/sucrase/test/snapshots/test.js.md
index cabbf6a3d..7ca1a8db1 100644
--- a/packages/sucrase/test/snapshots/test.js.md
+++ b/packages/sucrase/test/snapshots/test.js.md
@@ -8,13 +8,13 @@ Generated by [AVA](https://avajs.dev).
> Snapshot 1
- '() => React.createElement(\'div\', { id: "foo", __self: undefined, __source: {fileName: _jsxFileName, lineNumber: 1}}, "hello world" )'
+ '() => React.createElement(\'div\', { id: "foo", __self: this, __source: {fileName: _jsxFileName, lineNumber: 1}}, "hello world" )'
## converts jsx with custom jsxPragma
> Snapshot 1
- '() => FakeReactCreateElement(\'div\', { id: "foo", __self: undefined, __source: {fileName: _jsxFileName, lineNumber: 1}}, "hello world" )'
+ '() => FakeReactCreateElement(\'div\', { id: "foo", __self: this, __source: {fileName: _jsxFileName, lineNumber: 1}}, "hello world" )'
## converts typescript
@@ -54,20 +54,20 @@ Generated by [AVA](https://avajs.dev).
> Snapshot 1
- '() => React.createElement(\'div\', { id: "foo", __self: undefined, __source: {fileName: _jsxFileName$4, lineNumber: 1}}, "hello world" )'
+ '() => React.createElement(\'div\', { id: "foo", __self: this, __source: {fileName: _jsxFileName$4, lineNumber: 1}}, "hello world" )'
> Snapshot 2
- '() => React.createElement(\'div\', { id: "example-b", __self: undefined, __source: {fileName: _jsxFileName$3, lineNumber: 1}}, "hello world (a second time)" )'
+ '() => React.createElement(\'div\', { id: "example-b", __self: this, __source: {fileName: _jsxFileName$3, lineNumber: 1}}, "hello world (a second time)" )'
> Snapshot 3
- '() => React.createElement(\'div\', { id: "foo", __self: undefined, __source: {fileName: _jsxFileName$2, lineNumber: 1}}, "hello world" )'
+ '() => React.createElement(\'div\', { id: "foo", __self: this, __source: {fileName: _jsxFileName$2, lineNumber: 1}}, "hello world" )'
> Snapshot 4
- '() => React.createElement(\'div\', { id: "foo", __self: undefined, __source: {fileName: _jsxFileName$1, lineNumber: 1}}, "hello world" )'
+ '() => React.createElement(\'div\', { id: "foo", __self: this, __source: {fileName: _jsxFileName$1, lineNumber: 1}}, "hello world" )'
> Snapshot 5
- '() => React.createElement(\'div\', { id: "foo", __self: undefined, __source: {fileName: _jsxFileName, lineNumber: 1}}, "hello world" )'
+ '() => React.createElement(\'div\', { id: "foo", __self: this, __source: {fileName: _jsxFileName, lineNumber: 1}}, "hello world" )'
diff --git a/packages/sucrase/test/snapshots/test.js.snap b/packages/sucrase/test/snapshots/test.js.snap
index 4ab40afdd..d3be49ecc 100644
Binary files a/packages/sucrase/test/snapshots/test.js.snap and b/packages/sucrase/test/snapshots/test.js.snap differ
diff --git a/packages/sucrase/test/test.js b/packages/sucrase/test/test.js
index 3d3e77ef0..39fa962f9 100644
--- a/packages/sucrase/test/test.js
+++ b/packages/sucrase/test/test.js
@@ -14,42 +14,60 @@ require('source-map-support').install();
process.chdir(__dirname);
-test('converts jsx', async (t) => {
- const bundle = await rollup({
- input: 'fixtures/jsx/main.js',
- plugins: [
- sucrase({
- transforms: ['jsx']
- })
- ]
+function getBundle(input, sucraseOptions, rollupOptions) {
+ return rollup({
+ /**
+ * Explicitly set Rollup's top-level `this` context to silence build-time warnings about `this` being undefined in ES modules.
+ * This is a bundler-level concern only and does not affect the plugin's transform behavior, which runs before bundling.
+ */
+ context: 'this',
+ input,
+ plugins: [sucrase(sucraseOptions)],
+ ...rollupOptions
});
+}
+
+test('calls without options', async (t) => {
+ const plugin = sucrase();
+ t.is(plugin.name, 'sucrase');
+});
+
+test('does not transform files excluded by filter', async (t) => {
+ const plugin = sucrase({ exclude: '**/*.ts', transforms: ['typescript'] });
+ const result = plugin.transform('const x: number = 1;', 'foo.ts');
+ t.is(result, null);
+});
+
+test('converts jsx', async (t) => {
+ const bundle = await getBundle('fixtures/jsx/main.js', { transforms: ['jsx'] });
t.plan(1);
return testBundle(t, bundle);
});
test('converts jsx with custom jsxPragma', async (t) => {
- const bundle = await rollup({
- input: 'fixtures/jsx/main.js',
- plugins: [
- sucrase({
- transforms: ['jsx'],
- jsxPragma: 'FakeReactCreateElement'
- })
- ]
+ const bundle = await getBundle('fixtures/jsx/main.js', {
+ transforms: ['jsx'],
+ jsxPragma: 'FakeReactCreateElement'
});
t.plan(1);
return testBundle(t, bundle);
});
+test('converts jsx with jsxRuntime automatic', async (t) => {
+ const bundle = await getBundle(
+ 'fixtures/jsx-runtime/main.js',
+ { transforms: ['jsx'], jsxRuntime: 'automatic' },
+ { external: ['react/jsx-dev-runtime'] }
+ );
+ const { output } = await bundle.generate({ format: 'cjs', exports: 'auto' });
+ const [{ code }] = output;
+ // Check that the code uses the automatic runtime instead of React.createElement
+ t.regex(code, /require\(['"]react\/jsx-dev-runtime['"]\)/);
+ t.notRegex(code, /React\.createElement/);
+});
+
test('converts typescript', async (t) => {
- const bundle = await rollup({
- input: 'fixtures/typescript/main.js',
- plugins: [
- sucrase({
- transforms: ['typescript']
- })
- ]
- });
+ const bundle = await getBundle('fixtures/typescript/main.js', { transforms: ['typescript'] });
t.plan(4);
return testBundle(t, bundle);
});
@@ -60,9 +78,7 @@ if (process.platform !== 'win32') {
const bundle = await rollup({
input: 'fixtures/typescript-with-aliases/main.js',
plugins: [
- sucrase({
- transforms: ['typescript']
- }),
+ sucrase({ transforms: ['typescript'] }),
alias({
entries: [
{
@@ -80,13 +96,8 @@ if (process.platform !== 'win32') {
}
test('resolves typescript directory imports', async (t) => {
- const bundle = await rollup({
- input: 'fixtures/typescript-resolve-directory/main.js',
- plugins: [
- sucrase({
- transforms: ['typescript']
- })
- ]
+ const bundle = await getBundle('fixtures/typescript-resolve-directory/main.js', {
+ transforms: ['typescript']
});
t.plan(2);
@@ -94,15 +105,46 @@ test('resolves typescript directory imports', async (t) => {
});
test('converts typescript jsx ("tsx")', async (t) => {
- const bundle = await rollup({
- input: 'fixtures/typescript-with-tsx/main.js',
- plugins: [
- sucrase({
- transforms: ['typescript', 'jsx']
- })
- ]
+ const bundle = await getBundle('fixtures/typescript-with-tsx/main.js', {
+ transforms: ['typescript', 'jsx']
});
t.plan(5);
return testBundle(t, bundle);
});
+
+test('converts jsx with jsxImportSource', async (t) => {
+ const bundle = await getBundle(
+ 'fixtures/jsx-import-source/main.js',
+ { transforms: ['jsx'], jsxRuntime: 'automatic', jsxImportSource: 'preact' },
+ { external: ['preact/jsx-dev-runtime'] }
+ );
+ const { output } = await bundle.generate({ format: 'cjs', exports: 'auto' });
+ const [{ code }] = output;
+ t.regex(code, /require\(['"]preact\/jsx-dev-runtime['"]\)/);
+ t.notRegex(code, /['"]react\/jsx-dev-runtime['"]/);
+});
+
+test('preserveDynamicImport keeps import() expression', async (t) => {
+ const bundle = await getBundle('fixtures/preserve-dynamic-import/main.js', {
+ transforms: ['imports'],
+ preserveDynamicImport: true
+ });
+ const { output } = await bundle.generate({ format: 'es' });
+ const [{ code }] = output;
+ t.regex(code, /import\(/);
+});
+
+test('injectCreateRequireForImportRequire emits createRequire', async (t) => {
+ const bundle = await getBundle(
+ 'fixtures/inject-create-require/main.ts',
+ {
+ transforms: ['typescript'],
+ injectCreateRequireForImportRequire: true
+ },
+ { external: ['foo', 'module'] }
+ );
+ const { output } = await bundle.generate({ format: 'es' });
+ const [{ code }] = output;
+ t.regex(code, /createRequire/);
+});
diff --git a/packages/sucrase/test/types.ts b/packages/sucrase/test/types.ts
index e4e7f2c66..336a141b0 100644
--- a/packages/sucrase/test/types.ts
+++ b/packages/sucrase/test/types.ts
@@ -16,6 +16,10 @@ const config: RollupOptions = {
enableLegacyTypeScriptModuleInterop: true,
jsxFragmentPragma: 'React.fragment',
jsxPragma: 'React',
+ jsxRuntime: 'automatic',
+ jsxImportSource: 'preact',
+ preserveDynamicImport: true,
+ injectCreateRequireForImportRequire: true,
production: true,
disableESTransforms: true,
transforms: ['jsx']
diff --git a/packages/sucrase/types/index.d.ts b/packages/sucrase/types/index.d.ts
index 3b2147b1a..8f7143cd7 100644
--- a/packages/sucrase/types/index.d.ts
+++ b/packages/sucrase/types/index.d.ts
@@ -6,8 +6,12 @@ interface RollupSucraseOptions
extends Pick<
SucraseOptions,
| 'transforms'
+ | 'jsxRuntime'
+ | 'jsxImportSource'
| 'jsxPragma'
| 'jsxFragmentPragma'
+ | 'preserveDynamicImport'
+ | 'injectCreateRequireForImportRequire'
| 'enableLegacyTypeScriptModuleInterop'
| 'enableLegacyBabel5ModuleInterop'
| 'production'