refactor(next/core): reorganize next.js custom transforms for next-swc/turbopack (#60400)

<!-- Thanks for opening a PR! Your contribution is much appreciated.
To make sure your PR is handled as smoothly as possible we request that
you follow the checklist sections below.
Choose the right checklist for the change(s) that you're making:

## For Contributors

### Improving Documentation

- Run `pnpm prettier-fix` to fix formatting issues before opening the
PR.
- Read the Docs Contribution Guide to ensure your contribution follows
the docs guidelines:
https://nextjs.org/docs/community/contribution-guide

### Adding or Updating Examples

- The "examples guidelines" are followed from our contributing doc
https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md
- Make sure the linting passes by running `pnpm build && pnpm lint`. See
https://github.com/vercel/next.js/blob/canary/contributing/repository/linting.md

### Fixing a bug

- Related issues linked using `fixes #number`
- Tests added. See:
https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs
- Errors have a helpful link attached, see
https://github.com/vercel/next.js/blob/canary/contributing.md

### Adding a feature

- Implements an existing feature request or RFC. Make sure the feature
request has been accepted for implementation before opening a PR. (A
discussion must be opened, see
https://github.com/vercel/next.js/discussions/new?category=ideas)
- Related issues/discussions are linked using `fixes #number`
- e2e tests added
(https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs)
- Documentation added
- Telemetry added. In case of a feature if it's used or not.
- Errors have a helpful link attached, see
https://github.com/vercel/next.js/blob/canary/contributing.md


## For Maintainers

- Minimal description (aim for explaining to someone not on the team to
understand the PR)
- When linking to a Slack thread, you might want to share details of the
conclusion
- Link both the Linear (Fixes NEXT-xxx) and the GitHub issues
- Add review comments if necessary to explain to the reviewer the logic
behind a change



-->

### What?

This PR refactors organization for the rust side packages to build
`next-swc`.

### Why?

We had some historical legacy around package structures, have ambiguous
name for `core` / `next-core`. One contains swc transform visitor for
the next.js, and the other one is new for turbopack's core next.js
features. In addition to that, there was a package dependency chain
prevents to use `core` in the turobpack / next-swc both, so each time
porting a transformer into turbopack it requires to extract new
dependency to be imported in the both place.

PR touches its organization - while PR is large to touch various files,
the crux is summarized at
2cedd06ea5
:

1. `core` becomes `next-custom-transforms`, also this becomes an
agnostic pkg can be imported in turbopack / wasm / next-swc
2. simplify dependency chain to import next-custom-transforms, organized
as below

```mermaid
flowchart TD
    C(next-custom-transforms) --> A(napi)
    C(next-custom-transforms) --> B(wasm)
    D(next-core) --> A(napi)
    E(next-build) --> A(napi)
    F(next-api) --> A(napi)
    C(next-custom-transforms) --> D
    D(next-core) --> F(next-api)
    D(next-core) --> E(next-build)
```

`impl CustomTransformer` for the each transform still lives in
`next-core`, so turbopack specific dependency is isolated under
`next-core/build/api`.


Closes PACK-2201
Closes PACK-2202

---------

Co-authored-by: hrmny <8845940+ForsakenHarmony@users.noreply.github.com>
This commit is contained in:
OJ Kwon 2024-01-09 12:23:47 -08:00 committed by GitHub
parent 7018a65de4
commit 46370d816b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
708 changed files with 612 additions and 2085 deletions

View file

@ -119,29 +119,6 @@ jobs:
mold: 'yes'
secrets: inherit
test-cargo-integration:
name: test cargo integration
needs: ['changes', 'build-next']
if: ${{ needs.changes.outputs.docs-only == 'false' }}
uses: ./.github/workflows/build_reusable.yml
with:
needsNextest: 'yes'
needsRust: 'yes'
skipNativeBuild: 'yes'
afterBuild: xvfb-run turbo run test-cargo-integration
test-cargo-bench:
name: test cargo benchmarks
needs: ['changes', 'build-next']
if: ${{ needs.changes.outputs.docs-only == 'false' || needs.changes.outputs.is-release == 'false' }}
uses: ./.github/workflows/build_reusable.yml
with:
needsRust: 'yes'
skipNativeBuild: 'yes'
afterBuild: xvfb-run turbo run test-cargo-bench
rust-check:
name: rust check
needs: ['changes', 'build-next']
@ -373,8 +350,6 @@ jobs:
'test-ppr-prod',
'test-ppr-integration',
'test-cargo-unit',
'test-cargo-integration',
'test-cargo-bench',
'rust-check',
'test-next-swc-wasm',
'test-turbopack-dev',

67
Cargo.lock generated
View file

@ -3390,7 +3390,6 @@ dependencies = [
"indexmap 1.9.3",
"indoc",
"next-core",
"next-swc",
"once_cell",
"serde",
"serde_json",
@ -3442,11 +3441,7 @@ dependencies = [
"lazy_static",
"mime",
"mime_guess",
"next-swc",
"next-transform-dynamic",
"next-transform-font",
"next-transform-react-server-components",
"next-transform-strip-page-exports",
"next-custom-transforms",
"once_cell",
"qstring",
"regex",
@ -3461,19 +3456,16 @@ dependencies = [
]
[[package]]
name = "next-swc"
name = "next-custom-transforms"
version = "0.0.0"
dependencies = [
"anyhow",
"chrono",
"convert_case 0.5.0",
"easy-error",
"either",
"fxhash",
"hex",
"next-transform-dynamic",
"next-transform-font",
"next-transform-react-server-components",
"next-visitor-cjs-finder",
"once_cell",
"pathdiff",
"react_remove_properties",
@ -3483,6 +3475,7 @@ dependencies = [
"serde",
"serde_json",
"sha1 0.10.5",
"swc_core",
"tracing",
"turbopack-binding",
"walkdir",
@ -3502,7 +3495,7 @@ dependencies = [
"next-api",
"next-build",
"next-core",
"next-swc",
"next-custom-transforms",
"once_cell",
"sentry",
"serde",
@ -3519,54 +3512,6 @@ dependencies = [
"urlencoding",
]
[[package]]
name = "next-transform-dynamic"
version = "0.1.0"
dependencies = [
"pathdiff",
"swc_core",
"testing",
]
[[package]]
name = "next-transform-font"
version = "0.1.0"
dependencies = [
"rustc-hash",
"serde",
"serde_json",
"swc_core",
]
[[package]]
name = "next-transform-react-server-components"
version = "0.1.0"
dependencies = [
"anyhow",
"next-visitor-cjs-finder",
"regex",
"serde",
"serde_json",
"swc_core",
]
[[package]]
name = "next-transform-strip-page-exports"
version = "0.1.0"
dependencies = [
"rustc-hash",
"swc_core",
"testing",
"tracing",
]
[[package]]
name = "next-visitor-cjs-finder"
version = "0.1.0"
dependencies = [
"swc_core",
]
[[package]]
name = "node-file-trace"
version = "0.1.0"
@ -8795,7 +8740,7 @@ dependencies = [
"console_error_panic_hook",
"getrandom",
"js-sys",
"next-swc",
"next-custom-transforms",
"once_cell",
"parking_lot_core 0.8.0",
"path-clean",

View file

@ -3,17 +3,12 @@ resolver = "2"
members = [
"scripts/send-trace-to-jaeger",
"packages/next-swc/crates/core",
"packages/next-swc/crates/napi",
"packages/next-swc/crates/wasm",
"packages/next-swc/crates/next-api",
"packages/next-swc/crates/next-build",
"packages/next-swc/crates/next-core",
"packages/next-swc/crates/next-transform-font",
"packages/next-swc/crates/next-transform-dynamic",
"packages/next-swc/crates/next-transform-strip-page-exports",
"packages/next-swc/crates/next-transform-react-server-components",
"packages/next-swc/crates/next-visitor-cjs-finder",
"packages/next-swc/crates/next-custom-transforms",
]
[workspace.lints.clippy]
@ -32,12 +27,7 @@ opt-level = 3
next-api = { path = "packages/next-swc/crates/next-api", default-features = false }
next-build = { path = "packages/next-swc/crates/next-build", default-features = false }
next-core = { path = "packages/next-swc/crates/next-core", default-features = false }
next-swc = { path = "packages/next-swc/crates/core" }
next-transform-font = { path = "packages/next-swc/crates/next-transform-font" }
next-transform-dynamic = { path = "packages/next-swc/crates/next-transform-dynamic" }
next-transform-strip-page-exports = { path = "packages/next-swc/crates/next-transform-strip-page-exports" }
next-transform-react-server-components = { path = "packages/next-swc/crates/next-transform-react-server-components" }
next-visitor-cjs-finder = { path = "packages/next-swc/crates/next-visitor-cjs-finder" }
next-custom-transforms = { path = "packages/next-swc/crates/next-custom-transforms" }
# SWC crates
swc_core = { version = "0.87.16", features = [

View file

@ -24,3 +24,35 @@ Build the binary to integrate with next.js
```
pnpm build-native
```
Build wasm bindings to integrate with next.js
```
pnpm build-wasm
```
### Package hierarchies
`@next/swc` consist of multiple rust packages to enable features. See below for the high level hierarchies.
```mermaid
flowchart TD
C(next-custom-transforms) --> A(napi)
C(next-custom-transforms) --> B(wasm)
D(next-core) --> A(napi)
E(next-build) --> A(napi)
F(next-api) --> A(napi)
C(next-custom-transforms) --> D
D(next-core) --> F(next-api)
D(next-core) --> E(next-build)
```
- `next-custom-transforms`: provides next-swc specific SWC transform visitors. Turbopack, and the plain next-swc bidnings (`transform`) use these transforms. Since this is a bottom package can be imported in any place (turbopack / next-swc / wasm), it is important package do not contain specific dependencies. For example, using Turbopack's VC in this package will cause build failures to wasm bindings.
- `next-core`: Implements Turbopack features for the next.js core functionality. This is also the place where Turbopack-specific transform providers (implementing `CustomTransformer`) lives, which wraps swc's transformer in the `next-custom-transforms`.
- `next-api`: Binding interface to the next.js provides a proper next.js functionaility using `next-core`.
- `napi` / `wasm`: The actual binding interfaces, napi for the node.js and wasm for the wasm. Note wasm bindings cannot import packages using turbopack's feature.
#### To add new swc transforms
1. Implements a new visitor in `next-custom-transforms`. It is highly encouraged to use `VisitMut` instead of `Fold` for the performance reasons.
2. Implements a new `CustomTransformer` under `packages/next-swc/crates/next-core/src/next_shared/transforms` to make Turbopack's ecma transform plugin, then adjust corresponding rules in `packages/next-swc/crates/next-core/src/(next_client|next_server)/context.rs`.

View file

@ -1,6 +0,0 @@
x NEXT_RSC_ERR_INVALID_API: getServerSideProps
,-[input.js:1:1]
1 | export function getServerSideProps() {}
: ^^^^^^^^^^^^^^^^^^
`----

View file

@ -1,6 +0,0 @@
x NEXT_RSC_ERR_INVALID_API: getStaticProps
,-[input.js:1:1]
1 | export function getStaticProps() {}
: ^^^^^^^^^^^^^^
`----

View file

@ -1,7 +0,0 @@
x NEXT_RSC_ERR_CLIENT_IMPORT: server-only
,-[input.js:8:1]
8 |
9 | import 'server-only'
: ^^^^^^^^^^^^^^^^^^^^
`----

View file

@ -1,7 +0,0 @@
x NEXT_RSC_ERR_CLIENT_DIRECTIVE
,-[input.js:3:1]
3 | // prettier-ignore
4 | 'use client'
: ^^^^^^^^^^^^
`----

View file

@ -1,7 +0,0 @@
x NEXT_RSC_ERR_SERVER_IMPORT: client-only
,-[input.js:8:1]
8 |
9 | import 'client-only'
: ^^^^^^^^^^^^^^^^^^^^
`----

View file

@ -1,7 +0,0 @@
x NEXT_RSC_ERR_CLIENT_DIRECTIVE
,-[input.js:5:1]
5 | // prettier-ignore
6 | 'use client'
: ^^^^^^^^^^^^
`----

View file

@ -1,6 +0,0 @@
x NEXT_RSC_ERR_INVALID_API: getServerSideProps
,-[input.js:1:1]
1 | export function getServerSideProps() {}
: ^^^^^^^^^^^^^^^^^^
`----

View file

@ -1,6 +0,0 @@
x NEXT_RSC_ERR_INVALID_API: getStaticProps
,-[input.js:1:1]
1 | export function getStaticProps() {}
: ^^^^^^^^^^^^^^
`----

View file

@ -1,106 +0,0 @@
x NEXT_RSC_ERR_REACT_API: useState
,-[input.js:1:1]
1 | import { useState } from 'react'
: ^^^^^^^^
`----
x NEXT_RSC_ERR_REACT_API: createContext
,-[input.js:2:1]
2 |
3 | import { createContext } from 'react'
: ^^^^^^^^^^^^^
`----
x NEXT_RSC_ERR_REACT_API: useEffect
,-[input.js:4:1]
4 |
5 | import { useEffect, useImperativeHandle } from 'react'
: ^^^^^^^^^
`----
x NEXT_RSC_ERR_REACT_API: useImperativeHandle
,-[input.js:4:1]
4 |
5 | import { useEffect, useImperativeHandle } from 'react'
: ^^^^^^^^^^^^^^^^^^^
`----
x NEXT_RSC_ERR_REACT_API: Component
,-[input.js:7:1]
7 | import {
8 | Component,
: ^^^^^^^^^
9 | createFactory,
`----
x NEXT_RSC_ERR_REACT_API: createFactory
,-[input.js:8:1]
8 | Component,
9 | createFactory,
: ^^^^^^^^^^^^^
10 | PureComponent,
`----
x NEXT_RSC_ERR_REACT_API: PureComponent
,-[input.js:9:1]
9 | createFactory,
10 | PureComponent,
: ^^^^^^^^^^^^^
11 | useDeferredValue,
`----
x NEXT_RSC_ERR_REACT_API: useDeferredValue
,-[input.js:10:1]
10 | PureComponent,
11 | useDeferredValue,
: ^^^^^^^^^^^^^^^^
12 | useInsertionEffect,
`----
x NEXT_RSC_ERR_REACT_API: useInsertionEffect
,-[input.js:11:1]
11 | useDeferredValue,
12 | useInsertionEffect,
: ^^^^^^^^^^^^^^^^^^
13 | useLayoutEffect,
`----
x NEXT_RSC_ERR_REACT_API: useLayoutEffect
,-[input.js:12:1]
12 | useInsertionEffect,
13 | useLayoutEffect,
: ^^^^^^^^^^^^^^^
14 | useReducer,
`----
x NEXT_RSC_ERR_REACT_API: useReducer
,-[input.js:13:1]
13 | useLayoutEffect,
14 | useReducer,
: ^^^^^^^^^^
15 | useRef,
`----
x NEXT_RSC_ERR_REACT_API: useRef
,-[input.js:14:1]
14 | useReducer,
15 | useRef,
: ^^^^^^
16 | useSyncExternalStore,
`----
x NEXT_RSC_ERR_REACT_API: useSyncExternalStore
,-[input.js:15:1]
15 | useRef,
16 | useSyncExternalStore,
: ^^^^^^^^^^^^^^^^^^^^
17 | } from 'react'
`----
x NEXT_RSC_ERR_REACT_API: experimental_useOptimistic
,-[input.js:18:1]
18 |
19 | import { experimental_useOptimistic as useOptimistic } from 'react'
: ^^^^^^^^^^^^^^^^^^^^^^^^^^
`----

View file

@ -1,34 +0,0 @@
x NEXT_RSC_ERR_REACT_API: findDOMNode
,-[input.js:1:1]
1 | import { findDOMNode, flushSync, unstable_batchedUpdates } from 'react-dom'
: ^^^^^^^^^^^
`----
x NEXT_RSC_ERR_REACT_API: flushSync
,-[input.js:1:1]
1 | import { findDOMNode, flushSync, unstable_batchedUpdates } from 'react-dom'
: ^^^^^^^^^
`----
x NEXT_RSC_ERR_REACT_API: unstable_batchedUpdates
,-[input.js:1:1]
1 | import { findDOMNode, flushSync, unstable_batchedUpdates } from 'react-dom'
: ^^^^^^^^^^^^^^^^^^^^^^^
`----
x NEXT_RSC_ERR_REACT_API: useFormStatus
,-[input.js:3:1]
3 | import {
4 | useFormStatus,
: ^^^^^^^^^^^^^^^^^^^^^^^^^^
5 | useFormState,
`----
x NEXT_RSC_ERR_REACT_API: useFormState
,-[input.js:4:1]
4 | useFormStatus,
5 | useFormState,
: ^^^^^^^^^^^^^^^^^^^^^^^^^
6 | } from 'react-dom'
`----

View file

@ -1,14 +0,0 @@
x NEXT_RSC_ERR_SERVER_IMPORT: react-dom/server
,-[input.js:8:1]
8 |
9 | import 'react-dom/server'
: ^^^^^^^^^^^^^^^^^^^^^^^^^
`----
x NEXT_RSC_ERR_SERVER_IMPORT: react-dom/client
,-[input.js:10:1]
10 |
11 | import 'react-dom/client'
: ^^^^^^^^^^^^^^^^^^^^^^^^^
`----

View file

@ -1,2 +0,0 @@
;
/*#__PURE__*/ console.log("test!");

View file

@ -1,13 +0,0 @@
// This is a comment.
'use strict'
/**
* This is a comment.
*/
import 'client-only'
export default function () {
return null
}

View file

@ -1,8 +0,0 @@
// This is a comment.
'use strict';
/**
* This is a comment.
*/ import 'client-only';
export default function() {
return null;
}

View file

@ -1,5 +0,0 @@
export function getServerSideProps() {}
export default function () {
return null
}

View file

@ -1,4 +0,0 @@
export function getServerSideProps() {}
export default function() {
return null;
}

View file

@ -1,5 +0,0 @@
export function getStaticProps() {}
export default function () {
return null
}

View file

@ -1,4 +0,0 @@
export function getStaticProps() {}
export default function() {
return null;
}

View file

@ -1,13 +0,0 @@
// This is a comment.
'use strict'
/**
* This is a comment.
*/
import 'server-only'
export default function () {
return null
}

View file

@ -1,8 +0,0 @@
// This is a comment.
'use strict';
/**
* This is a comment.
*/ import 'server-only';
export default function() {
return null;
}

View file

@ -1,8 +0,0 @@
import 'react'
// prettier-ignore
'use client'
export default function () {
return null
}

View file

@ -1,4 +0,0 @@
import 'react';
export default function() {
return null;
}

View file

@ -1,13 +0,0 @@
// This is a comment.
'use strict'
/**
* This is a comment.
*/
import 'client-only'
export default function () {
return null
}

View file

@ -1,8 +0,0 @@
// This is a comment.
'use strict';
/**
* This is a comment.
*/ import 'client-only';
export default function() {
return null;
}

View file

@ -1,6 +0,0 @@
export default function () {
return null
}
// prettier-ignore
'use client'

View file

@ -1,3 +0,0 @@
export default function() {
return null;
}

View file

@ -1,5 +0,0 @@
export function getServerSideProps() {}
export default function () {
return null
}

View file

@ -1,4 +0,0 @@
export function getServerSideProps() {}
export default function() {
return null;
}

View file

@ -1,5 +0,0 @@
export function getStaticProps() {}
export default function () {
return null
}

View file

@ -1,4 +0,0 @@
export function getStaticProps() {}
export default function() {
return null;
}

View file

@ -1,21 +0,0 @@
import { useState } from 'react'
import { createContext } from 'react'
import { useEffect, useImperativeHandle } from 'react'
import {
Component,
createFactory,
PureComponent,
useDeferredValue,
useInsertionEffect,
useLayoutEffect,
useReducer,
useRef,
useSyncExternalStore,
} from 'react'
export default function () {
return null
}

View file

@ -1,7 +0,0 @@
import { useState } from 'react';
import { createContext } from 'react';
import { useEffect, useImperativeHandle } from 'react';
import { Component, createFactory, PureComponent, useDeferredValue, useInsertionEffect, useLayoutEffect, useReducer, useRef, useSyncExternalStore } from 'react';
export default function() {
return null;
}

View file

@ -1,7 +0,0 @@
import { findDOMNode, flushSync, unstable_batchedUpdates } from 'react-dom'
import { useFormStatus, useFormState } from 'react-dom'
export default function () {
return null
}

View file

@ -1,6 +0,0 @@
import { findDOMNode, flushSync, unstable_batchedUpdates } from 'react-dom';
import { useFormStatus, useFormState } from 'react-dom';
export default function() {
return null;
}

View file

@ -1,15 +0,0 @@
// This is a comment.
'use strict'
/**
* This is a comment.
*/
import 'react-dom/server'
import 'react-dom/client'
export default function () {
return null
}

View file

@ -1,9 +0,0 @@
// This is a comment.
'use strict';
/**
* This is a comment.
*/ import 'react-dom/server';
import 'react-dom/client';
export default function() {
return null;
}

View file

@ -1 +0,0 @@
<div>children</div>; '<>hello</>'

View file

@ -1,24 +0,0 @@
function _tagged_template_literal(strings, raw) {
if (!raw) {
raw = strings.slice(0);
}
return Object.freeze(Object.defineProperties(strings, {
raw: {
value: Object.freeze(raw)
}
}));
}
function _templateObject() {
var data = _tagged_template_literal([
"\n color: red;\n"
]);
_templateObject = function _templateObject() {
return data;
};
return data;
}
import styled from "styled-components";
export var foo = styled.input.withConfig({
displayName: "input__foo",
componentId: "sc-21a13c03-0"
})(_templateObject());

View file

@ -17,7 +17,7 @@ plugin = [
"turbopack-binding/__swc_core_binding_napi_plugin",
"turbopack-binding/__swc_core_binding_napi_plugin_filesystem_cache",
"turbopack-binding/__swc_core_binding_napi_plugin_shared_runtime",
"next-swc/plugin",
"next-custom-transforms/plugin",
"next-core/plugin",
]
sentry_native_tls = ["sentry", "sentry/native-tls", "native-tls"]
@ -54,7 +54,7 @@ napi = { version = "2", default-features = false, features = [
"error_anyhow",
] }
napi-derive = "2"
next-swc = { workspace = true }
next-custom-transforms = { workspace = true }
next-api = { workspace = true }
next-build = { workspace = true }
next-core = { workspace = true }

View file

@ -37,7 +37,7 @@ use std::{
use anyhow::{anyhow, bail, Context as _};
use fxhash::FxHashSet;
use napi::bindgen_prelude::*;
use next_swc::{custom_before_pass, TransformOptions};
use next_custom_transforms::chain_transforms::{custom_before_pass, TransformOptions};
use turbopack_binding::swc::core::{
base::{try_with_handler, Compiler, TransformOutput},
common::{comments::SingleThreadedComments, errors::ColorConfig, FileName, Mark, GLOBALS},

View file

@ -22,7 +22,6 @@ workspace = true
[dependencies]
anyhow = { workspace = true, features = ["backtrace"] }
futures = { workspace = true }
next-swc = { workspace = true }
indexmap = { workspace = true }
indoc = { workspace = true }
next-core = { workspace = true }

View file

@ -1,4 +1,4 @@
use std::{io::Write, iter::once};
use std::{collections::BTreeMap, io::Write, iter::once};
use anyhow::{bail, Context, Result};
use indexmap::{map::Entry, IndexMap};
@ -6,13 +6,13 @@ use next_core::{
next_manifests::{ActionLayer, ActionManifestWorkerEntry, ServerReferenceManifest},
util::{get_asset_prefix_from_pathname, NextRuntime},
};
use next_swc::server_actions::parse_server_actions;
use tracing::Instrument;
use turbo_tasks::{
graph::{GraphTraversal, NonDeterministic},
TryFlatJoinIterExt, Value, ValueToString, Vc,
};
use turbopack_binding::{
swc::core::{common::comments::Comments, ecma::ast::Program},
turbo::tasks_fs::{rope::RopeBuilder, File, FileSystemPath},
turbopack::{
core::{
@ -267,6 +267,30 @@ async fn get_referenced_modules(
.map(|modules| modules.clone_value().into_iter().map(move |m| (layer, m)))
}
/// Parses the Server Actions comment for all exported action function names.
///
/// Action names are stored in a leading BlockComment prefixed by
/// `__next_internal_action_entry_do_not_use__`.
pub fn parse_server_actions<C: Comments>(
program: &Program,
comments: C,
) -> Option<BTreeMap<String, String>> {
let byte_pos = match program {
Program::Module(m) => m.span.lo,
Program::Script(s) => s.span.lo,
};
comments.get_leading(byte_pos).and_then(|comments| {
comments.iter().find_map(|c| {
c.text
.split_once("__next_internal_action_entry_do_not_use__")
.and_then(|(_, actions)| match serde_json::from_str(actions) {
Ok(v) => Some(v),
Err(_) => None,
})
})
})
}
/// Inspects the comments inside [module] looking for the magic actions comment.
/// If found, we return the mapping of every action's hashed id to the name of
/// the exported action function. If not, we return a None.

View file

@ -18,7 +18,7 @@ async-trait = { workspace = true }
base64 = "0.21.0"
const_format = "0.2.30"
lazy-regex = "3.0.1"
next-swc = { workspace = true }
next-custom-transforms = { workspace = true }
once_cell = { workspace = true }
qstring = { workspace = true }
regex = { workspace = true }
@ -60,10 +60,6 @@ turbopack-binding = { workspace = true, features = [
] }
turbo-tasks = { workspace = true }
turbo-tasks-fs = { workspace = true }
next-transform-strip-page-exports = { workspace = true }
next-transform-font = { workspace = true }
next-transform-dynamic = { workspace = true }
next-transform-react-server-components = { workspace = true }
swc_core = { workspace = true, features = [
"ecma_ast",

View file

@ -1,5 +1,5 @@
use anyhow::Result;
use next_transform_strip_page_exports::ExportFilter;
use next_custom_transforms::transforms::strip_page_exports::ExportFilter;
use turbo_tasks::Vc;
use turbopack_binding::turbopack::turbopack::module_options::ModuleRule;

View file

@ -1,5 +1,5 @@
use anyhow::Result;
use next_transform_strip_page_exports::ExportFilter;
use next_custom_transforms::transforms::strip_page_exports::ExportFilter;
use turbo_tasks::Vc;
use turbopack_binding::turbopack::turbopack::module_options::ModuleRule;

View file

@ -2,7 +2,7 @@ use std::path::PathBuf;
use anyhow::Result;
use async_trait::async_trait;
use next_transform_dynamic::{next_dynamic, NextDynamicMode};
use next_custom_transforms::transforms::dynamic::{next_dynamic, NextDynamicMode};
use swc_core::{
common::{util::take::Take, FileName},
ecma::{

View file

@ -1,5 +1,6 @@
use anyhow::Result;
use async_trait::async_trait;
use next_custom_transforms::transforms::fonts::*;
use swc_core::ecma::{ast::Program, atoms::JsWord, visit::VisitMutWith};
use turbo_tasks::Vc;
use turbopack_binding::turbopack::{
@ -37,7 +38,7 @@ struct NextJsFont {
#[async_trait]
impl CustomTransformer for NextJsFont {
async fn transform(&self, program: &mut Program, ctx: &TransformContext<'_>) -> Result<()> {
let mut next_font = next_transform_font::next_font_loaders(next_transform_font::Config {
let mut next_font = next_font_loaders(Config {
font_loaders: self.font_loaders.clone(),
relative_file_path_from_root: ctx.file_name_str.into(),
});

View file

@ -2,7 +2,7 @@ use std::path::PathBuf;
use anyhow::Result;
use async_trait::async_trait;
use next_transform_react_server_components::{server_components, Config, Options};
use next_custom_transforms::transforms::react_server_components::*;
use swc_core::{
common::{util::take::Take, FileName},
ecma::{

View file

@ -1,6 +1,8 @@
use anyhow::Result;
use async_trait::async_trait;
use next_transform_strip_page_exports::{next_transform_strip_page_exports, ExportFilter};
use next_custom_transforms::transforms::strip_page_exports::{
next_transform_strip_page_exports, ExportFilter,
};
use swc_core::{
common::util::take::Take,
ecma::{

View file

@ -1,6 +1,6 @@
use anyhow::Result;
use async_trait::async_trait;
use next_swc::server_actions::{server_actions, Config};
use next_custom_transforms::transforms::server_actions::{server_actions, Config};
use swc_core::{
common::FileName,
ecma::{ast::Program, visit::VisitMutWith},

View file

@ -1,6 +1,6 @@
[package]
edition = "2018"
name = "next-swc"
name = "next-custom-transforms"
version = "0.0.0"
publish = false
@ -18,18 +18,14 @@ either = "1"
fxhash = "0.2.1"
hex = "0.4.3"
once_cell = { workspace = true }
pathdiff = "0.2.0"
pathdiff = { workspace = true }
regex = "1.5"
rustc-hash = "1"
serde = "1"
serde_json = "1"
rustc-hash = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true, features = ["preserve_order"] }
sha1 = "0.10.1"
tracing = { version = "0.1.37" }
next-visitor-cjs-finder = { workspace = true }
next-transform-dynamic = { workspace = true }
next-transform-font = { workspace = true }
next-transform-react-server-components = { workspace = true }
anyhow = { workspace = true }
turbopack-binding = { workspace = true, features = [
"__swc_core",
@ -40,6 +36,8 @@ turbopack-binding = { workspace = true, features = [
"__swc_transform_modularize_imports",
"__swc_transform_relay",
] }
# To allow quote! macro works
swc_core = { workspace = true, features = ["ecma_quote"]}
react_remove_properties = "0.19.0"
remove_console = "0.20.0"

View file

@ -1,44 +1,7 @@
/*
Copyright (c) 2017 The swc Project Developers
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
// TODO(alexkirsz) Remove once the diagnostic is fixed.
#![allow(rustc::untranslatable_diagnostic_trivial)]
#![recursion_limit = "2048"]
#![deny(clippy::all)]
#![feature(box_patterns)]
use std::{cell::RefCell, path::PathBuf, rc::Rc, sync::Arc};
use either::Either;
use fxhash::FxHashSet;
use next_transform_dynamic::{next_dynamic, NextDynamicMode};
use next_transform_font::next_font_loaders;
use next_visitor_cjs_finder::contains_cjs;
use serde::Deserialize;
use turbopack_binding::swc::{
core::{
@ -55,18 +18,12 @@ use turbopack_binding::swc::{
custom_transform::modularize_imports,
};
pub mod amp_attributes;
pub mod cjs_optimizer;
pub mod disallow_re_export_all_in_page;
mod import_analyzer;
pub mod named_import_transform;
pub mod next_ssg;
pub mod optimize_barrel;
pub mod optimize_server_react;
pub mod page_config;
pub mod pure;
pub mod server_actions;
pub mod shake_exports;
use crate::transforms::{
cjs_finder::contains_cjs,
dynamic::{next_dynamic, NextDynamicMode},
fonts::next_font_loaders,
react_server_components,
};
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
@ -99,7 +56,7 @@ pub struct TransformOptions {
pub prefer_esm: bool,
#[serde(default)]
pub server_components: Option<next_transform_react_server_components::Config>,
pub server_components: Option<react_server_components::Config>,
#[serde(default)]
pub styled_jsx: Option<turbopack_binding::swc::custom_transform::styled_jsx::visitor::Config>,
@ -125,7 +82,7 @@ pub struct TransformOptions {
pub relay: Option<serde_json::Value>,
#[serde(default)]
pub shake_exports: Option<shake_exports::Config>,
pub shake_exports: Option<crate::transforms::shake_exports::Config>,
#[serde(default)]
pub emotion: Option<turbopack_binding::swc::custom_transform::emotion::EmotionOptions>,
@ -134,22 +91,22 @@ pub struct TransformOptions {
pub modularize_imports: Option<modularize_imports::Config>,
#[serde(default)]
pub auto_modularize_imports: Option<named_import_transform::Config>,
pub auto_modularize_imports: Option<crate::transforms::named_import_transform::Config>,
#[serde(default)]
pub optimize_barrel_exports: Option<optimize_barrel::Config>,
pub optimize_barrel_exports: Option<crate::transforms::optimize_barrel::Config>,
#[serde(default)]
pub font_loaders: Option<next_transform_font::Config>,
pub font_loaders: Option<crate::transforms::fonts::Config>,
#[serde(default)]
pub server_actions: Option<server_actions::Config>,
pub server_actions: Option<crate::transforms::server_actions::Config>,
#[serde(default)]
pub cjs_require_optimizer: Option<cjs_optimizer::Config>,
pub cjs_require_optimizer: Option<crate::transforms::cjs_optimizer::Config>,
#[serde(default)]
pub optimize_server_react: Option<optimize_server_react::Config>,
pub optimize_server_react: Option<crate::transforms::optimize_server_react::Config>,
}
pub fn custom_before_pass<'a, C: Comments + 'a>(
@ -189,10 +146,10 @@ where
};
chain!(
disallow_re_export_all_in_page::disallow_re_export_all_in_page(opts.is_page_file),
crate::transforms::disallow_re_export_all_in_page::disallow_re_export_all_in_page(opts.is_page_file),
match &opts.server_components {
Some(config) if config.truthy() =>
Either::Left(next_transform_react_server_components::server_components(
Either::Left(react_server_components::server_components(
file.name.clone(),
config.clone(),
comments.clone(),
@ -223,10 +180,10 @@ where
None => Either::Right(noop()),
},
Optional::new(
next_ssg::next_ssg(eliminated_packages),
crate::transforms::next_ssg::next_ssg(eliminated_packages),
!opts.disable_next_ssg
),
amp_attributes::amp_attributes(),
crate::transforms::amp_attributes::amp_attributes(),
next_dynamic(
opts.is_development,
opts.is_server_compiler,
@ -234,7 +191,7 @@ where
Some(config) if config.truthy() => match config {
// Always enable the Server Components mode for both
// server and client layers.
next_transform_react_server_components::Config::WithOptions(config) => config.is_react_server_layer,
react_server_components::Config::WithOptions(config) => config.is_react_server_layer,
_ => false,
},
_ => false,
@ -245,7 +202,7 @@ where
opts.pages_dir.clone()
),
Optional::new(
page_config::page_config(opts.is_development, opts.is_page_file),
crate::transforms::page_config::page_config(opts.is_development, opts.is_page_file),
!opts.disable_page_config
),
relay_plugin,
@ -263,19 +220,19 @@ where
_ => Either::Right(noop()),
},
match &opts.shake_exports {
Some(config) => Either::Left(shake_exports::shake_exports(config.clone())),
Some(config) => Either::Left(crate::transforms::shake_exports::shake_exports(config.clone())),
None => Either::Right(noop()),
},
match &opts.auto_modularize_imports {
Some(config) => Either::Left(named_import_transform::named_import_transform(config.clone())),
Some(config) => Either::Left(crate::transforms::named_import_transform::named_import_transform(config.clone())),
None => Either::Right(noop()),
},
match &opts.optimize_barrel_exports {
Some(config) => Either::Left(optimize_barrel::optimize_barrel(config.clone())),
Some(config) => Either::Left(crate::transforms::optimize_barrel::optimize_barrel(config.clone())),
_ => Either::Right(noop()),
},
match &opts.optimize_server_react {
Some(config) => Either::Left(optimize_server_react::optimize_server_react(config.clone())),
Some(config) => Either::Left(crate::transforms::optimize_server_react::optimize_server_react(config.clone())),
_ => Either::Right(noop()),
},
opts.emotion
@ -309,7 +266,7 @@ where
None => Either::Right(noop()),
},
match &opts.server_actions {
Some(config) => Either::Left(server_actions::server_actions(
Some(config) => Either::Left(crate::transforms::server_actions::server_actions(
&file.name,
config.clone(),
comments.clone(),
@ -318,11 +275,11 @@ where
},
match &opts.cjs_require_optimizer {
Some(config) => {
Either::Left(cjs_optimizer::cjs_optimizer(config.clone(), SyntaxContext::empty().apply_mark(unresolved_mark)))
Either::Left(crate::transforms::cjs_optimizer::cjs_optimizer(config.clone(), SyntaxContext::empty().apply_mark(unresolved_mark)))
},
None => Either::Right(noop()),
},
pure::pure_magic(comments),
crate::transforms::pure::pure_magic(comments),
)
}

View file

@ -0,0 +1,37 @@
/*
Copyright (c) 2017 The swc Project Developers
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
// TODO(alexkirsz) Remove once the diagnostic is fixed.
#![allow(rustc::untranslatable_diagnostic_trivial)]
#![recursion_limit = "2048"]
#![deny(clippy::all)]
#![feature(box_patterns)]
#![feature(arbitrary_self_types)]
pub mod chain_transforms;
pub mod transforms;

View file

@ -1,4 +1,4 @@
use swc_core::ecma::{
use turbopack_binding::swc::core::ecma::{
ast::*,
visit::{Visit, VisitWith},
};

View file

@ -1,10 +1,8 @@
// TODO(alexkirsz) Remove once the diagnostic is fixed.
#![allow(rustc::untranslatable_diagnostic_trivial)]
use std::path::{Path, PathBuf};
use pathdiff::diff_paths;
use swc_core::{
use swc_core::quote;
use turbopack_binding::swc::core::{
common::{errors::HANDLER, FileName, Span, DUMMY_SP},
ecma::{
ast::{
@ -16,7 +14,6 @@ use swc_core::{
utils::{private_ident, quote_ident, ExprFactory},
visit::{Fold, FoldWith},
},
quote,
};
/// Creates a SWC visitor to transform `next/dynamic` calls to have the

View file

@ -1,4 +1,4 @@
use swc_core::{
use turbopack_binding::swc::core::{
common::errors::HANDLER,
ecma::{
ast::*,

View file

@ -1,5 +1,5 @@
use serde_json::Value;
use swc_core::{
use turbopack_binding::swc::core::{
common::{errors::HANDLER, Spanned, DUMMY_SP},
ecma::{
ast::*,

View file

@ -1,9 +1,6 @@
// TODO(alexkirsz) Remove once the diagnostic is fixed.
#![allow(rustc::untranslatable_diagnostic_trivial)]
use rustc_hash::FxHashSet;
use serde::Deserialize;
use swc_core::{
use turbopack_binding::swc::core::{
common::{collections::AHashMap, BytePos, Spanned},
ecma::{
ast::{Id, ModuleItem},

View file

@ -0,0 +1,17 @@
pub mod amp_attributes;
pub mod cjs_finder;
pub mod cjs_optimizer;
pub mod disallow_re_export_all_in_page;
pub mod dynamic;
pub mod fonts;
pub mod import_analyzer;
pub mod named_import_transform;
pub mod next_ssg;
pub mod optimize_barrel;
pub mod optimize_server_react;
pub mod page_config;
pub mod pure;
pub mod react_server_components;
pub mod server_actions;
pub mod shake_exports;
pub mod strip_page_exports;

View file

@ -6,7 +6,7 @@ use turbopack_binding::swc::core::{
},
};
use crate::import_analyzer::ImportMap;
use crate::transforms::import_analyzer::ImportMap;
pub fn pure_magic<C>(comments: C) -> impl Fold
where

View file

@ -1,11 +1,8 @@
#![feature(arbitrary_self_types)]
use std::{collections::HashMap, path::PathBuf};
use next_visitor_cjs_finder::contains_cjs;
use regex::Regex;
use serde::Deserialize;
use swc_core::{
use turbopack_binding::swc::core::{
common::{
comments::{Comment, CommentKind, Comments},
errors::HANDLER,
@ -19,6 +16,8 @@ use swc_core::{
},
};
use super::cjs_finder::contains_cjs;
#[derive(Clone, Debug, Deserialize)]
#[serde(untagged)]
pub enum Config {

View file

@ -63,27 +63,6 @@ pub fn server_actions<C: Comments>(
})
}
/// Parses the Server Actions comment for all exported action function names.
///
/// Action names are stored in a leading BlockComment prefixed by
/// `__next_internal_action_entry_do_not_use__`.
pub fn parse_server_actions<C: Comments>(program: &Program, comments: C) -> Option<ActionsMap> {
let byte_pos = match program {
Program::Module(m) => m.span.lo,
Program::Script(s) => s.span.lo,
};
comments.get_leading(byte_pos).and_then(|comments| {
comments.iter().find_map(|c| {
c.text
.split_once("__next_internal_action_entry_do_not_use__")
.and_then(|(_, actions)| match serde_json::from_str(actions) {
Ok(v) => Some(v),
Err(_) => None,
})
})
})
}
/// Serializes the Server Actions into a magic comment prefixed by
/// `__next_internal_action_entry_do_not_use__`.
fn generate_server_actions_comment(actions: ActionsMap) -> String {

View file

@ -2,11 +2,12 @@
//! https://github.com/vercel/next.js/blob/f7fecf00cb40c2f784387ff8ccc5e213b8bdd9ca/packages/next-swc/crates/core/src/next_ssg.rs
//!
//! This version adds support for eliminating client-side exports only.
//! [TODO] may consolidate into next_ssg
use std::{cell::RefCell, mem::take, rc::Rc};
use rustc_hash::{FxHashMap, FxHashSet};
use swc_core::{
use turbopack_binding::swc::core::{
common::{
errors::HANDLER,
pass::{Repeat, Repeated},
@ -949,7 +950,8 @@ impl Fold for NextSsg {
match e {
Expr::Assign(assign_expr) => {
let mut retain = true;
let left = self.within_lhs_of_var(true, |this| assign_expr.left.fold_with(this));
let left =
self.within_lhs_of_var(true, |this| assign_expr.left.clone().fold_with(this));
let right = self.within_lhs_of_var(false, |this| {
if let PatOrExpr::Pat(pat) = &left {
@ -958,7 +960,7 @@ impl Fold for NextSsg {
this.mark_as_candidate(&assign_expr.right);
}
}
assign_expr.right.fold_with(this)
assign_expr.right.clone().fold_with(this)
});
if retain {
@ -983,13 +985,13 @@ impl Fold for NextSsg {
/// This method make `name` of [VarDeclarator] to [Pat::Invalid] if it
/// should be removed.
fn fold_var_declarator(&mut self, d: VarDeclarator) -> VarDeclarator {
let name = self.within_lhs_of_var(true, |this| d.name.fold_with(this));
let name = self.within_lhs_of_var(true, |this| d.name.clone().fold_with(this));
let init = self.within_lhs_of_var(false, |this| {
if name.is_invalid() {
this.mark_as_candidate(&d.init);
}
d.init.fold_with(this)
d.init.clone().fold_with(this)
});
VarDeclarator { name, init, ..d }

View file

@ -1,15 +1,16 @@
use std::path::PathBuf;
use next_swc::{
use next_custom_transforms::transforms::{
disallow_re_export_all_in_page::disallow_re_export_all_in_page,
dynamic::{next_dynamic, NextDynamicMode},
fonts::{next_font_loaders, Config as FontLoaderConfig},
next_ssg::next_ssg,
react_server_components::server_components,
server_actions::{
server_actions, {self},
},
strip_page_exports::{next_transform_strip_page_exports, ExportFilter},
};
use next_transform_dynamic::{next_dynamic, NextDynamicMode};
use next_transform_font::{next_font_loaders, Config as FontLoaderConfig};
use turbopack_binding::swc::{
core::{
common::{chain, FileName, Mark},
@ -88,17 +89,16 @@ fn next_ssg_errors(input: PathBuf) {
#[fixture("tests/errors/react-server-components/server-graph/**/input.js")]
fn react_server_components_server_graph_errors(input: PathBuf) {
use next_custom_transforms::transforms::react_server_components::{Config, Options};
let output = input.parent().unwrap().join("output.js");
test_fixture(
syntax(),
&|tr| {
server_components(
FileName::Real(PathBuf::from("/some-project/src/layout.js")),
next_swc::react_server_components::Config::WithOptions(
next_swc::react_server_components::Options {
is_react_server_layer: true,
},
),
Config::WithOptions(Options {
is_react_server_layer: true,
}),
tr.comments.as_ref().clone(),
None,
)
@ -114,17 +114,16 @@ fn react_server_components_server_graph_errors(input: PathBuf) {
#[fixture("tests/errors/react-server-components/client-graph/**/input.js")]
fn react_server_components_client_graph_errors(input: PathBuf) {
use next_custom_transforms::transforms::react_server_components::{Config, Options};
let output = input.parent().unwrap().join("output.js");
test_fixture(
syntax(),
&|tr| {
server_components(
FileName::Real(PathBuf::from("/some-project/src/page.js")),
next_swc::react_server_components::Config::WithOptions(
next_swc::react_server_components::Options {
is_react_server_layer: false,
},
),
Config::WithOptions(Options {
is_react_server_layer: false,
}),
tr.comments.as_ref().clone(),
None,
)
@ -160,6 +159,7 @@ fn next_font_loaders_errors(input: PathBuf) {
#[fixture("tests/errors/server-actions/server-graph/**/input.js")]
fn react_server_actions_server_errors(input: PathBuf) {
use next_custom_transforms::transforms::react_server_components::{Config, Options};
let output = input.parent().unwrap().join("output.js");
test_fixture(
syntax(),
@ -168,11 +168,9 @@ fn react_server_actions_server_errors(input: PathBuf) {
resolver(Mark::new(), Mark::new(), false),
server_components(
FileName::Real(PathBuf::from("/app/item.js")),
next_swc::react_server_components::Config::WithOptions(
next_swc::react_server_components::Options {
is_react_server_layer: true
},
),
Config::WithOptions(Options {
is_react_server_layer: true
},),
tr.comments.as_ref().clone(),
None,
),
@ -197,6 +195,7 @@ fn react_server_actions_server_errors(input: PathBuf) {
#[fixture("tests/errors/server-actions/client-graph/**/input.js")]
fn react_server_actions_client_errors(input: PathBuf) {
use next_custom_transforms::transforms::react_server_components::{Config, Options};
let output = input.parent().unwrap().join("output.js");
test_fixture(
syntax(),
@ -205,11 +204,9 @@ fn react_server_actions_client_errors(input: PathBuf) {
resolver(Mark::new(), Mark::new(), false),
server_components(
FileName::Real(PathBuf::from("/app/item.js")),
next_swc::react_server_components::Config::WithOptions(
next_swc::react_server_components::Options {
is_react_server_layer: false
},
),
Config::WithOptions(Options {
is_react_server_layer: false
},),
tr.comments.as_ref().clone(),
None,
),
@ -231,3 +228,20 @@ fn react_server_actions_client_errors(input: PathBuf) {
},
);
}
#[fixture("tests/errors/strip-page-exports/**/input.js")]
fn next_transform_strip_page_exports_errors(input: PathBuf) {
let output = input.parent().unwrap().join("output.js");
test_fixture(
syntax(),
&|_tr| {
next_transform_strip_page_exports(ExportFilter::StripDataExports, Default::default())
},
&input,
&output,
FixtureTestConfig {
allow_error: true,
..Default::default()
},
);
}

Some files were not shown because too many files have changed in this diff Show more