Skip to content

fix(build): preserve transitive external versions - #2887

Open
james-elicx wants to merge 7 commits into
mainfrom
codex/fix-externals-transitive
Open

fix(build): preserve transitive external versions#2887
james-elicx wants to merge 7 commits into
mainfrom
codex/fix-externals-transitive

Conversation

@james-elicx

Copy link
Copy Markdown
Member

Summary

  • mirrors Next.js baseResolveCheck behavior for serverExternalPackages by comparing importer-relative and project-root package resolution
  • bundles nested package instances when relocation would otherwise collapse them to the root version
  • applies the same merged default and explicit external set, including transpile-package exclusions
  • limits the resolver to server environments and native-filtered bare package imports

Failure mapping

Fixes the non-cache externals-transitive failure from Actions run 31439707085, job 93624401572:

  • test/e2e/externals-transitive/externals-transitive.test.ts
  • before: depB resolved lodash 3.10.1 instead of its nested 4.17.21
  • after: exact Next.js v16.2.6 targeted deploy suite passes 1/1

Validation

  • exact Next.js targeted E2E: 1 suite, 1 test passed
  • local production-build regression proves both an explicit external and default pg-style external preserve distinct root/nested versions
  • adjacent external-package suites: 6/6 passed
  • vp check on changed files
  • vp run vinext#build
  • independent review loop: no findings

Refs #1503.

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@pkg-pr-new

pkg-pr-new Bot commented Aug 11, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@vinext/cloudflare@2887
npm i https://pkg.pr.new/create-vinext-app@2887
npm i https://pkg.pr.new/@vinext/types@2887
npm i https://pkg.pr.new/vinext@2887

commit: d5dfeb9

@github-actions

Copy link
Copy Markdown
Contributor
Example Preview Production Original
app-router-cloudflare preview production
pages-router-cloudflare preview production
app-router-playground preview production original
realworld-api-rest preview production
nextra-docs-template preview production
benchmarks preview production
hackernews preview production original
workers-cache preview production
web preview production

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@ask-bonk ask-bonk Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: transitive external version preservation

Reviewed the three files. The overall approach is sound and faithfully mirrors Next.js baseResolveCheck (resolve from importer, resolve from root, bundle when they differ). The new E2E test is a good port of the Next.js suite and passes locally. A few findings below, ordered by severity.

1. fs.realpathSync output is used as a Vite module id without toSlash — Windows id-invariant violation (medium)

packages/vinext/src/plugins/transitive-externals.ts:11-17

function realpath(resolvedPath: string): string {
  try {
    return fs.realpathSync(resolvedPath);
  } catch {
    return resolvedPath;
  }
}

The result of realpath() is returned as the resolved id from resolveId (compareTransitiveExternalResolutions returns importerRealpath at line 43, which flows out of the handler at lines 147/152). Everywhere else in this codebase, fs.realpathSync / fs.realpath.native output is wrapped in toSlash(...) before it becomes a module id, precisely because on Windows it returns backslash separators and the repo enforces canonical forward-slash ids (see plugins/og-asset-ownership.ts:6-9, plugins/import-meta-url.ts:304, plugins/extensionless-dynamic-import.ts:656, index.ts:1460). Returning a backslash id here would break the forward-slash id invariant that route logic, startsWith/split("/"), and generated import specifiers rely on.

Additionally, the codebase canonicalizes ids with fs.realpathSync.native (see utils/commonjs-loader.ts:25, server/prod-server.ts:118) because Rolldown keys canonical module ids off realpathSync.native. This plugin uses the non-native fs.realpathSync. On case-insensitive or otherwise-normalizing filesystems the non-native variant can yield a different string than what Rolldown will canonicalize to, which risks a duplicate-module id for the very copy we are trying to bundle distinctly.

Suggested fix: import toSlash from pathslash and use fs.realpathSync.native, e.g. return toSlash(fs.realpathSync.native(resolvedPath)) (with the same toSlash(path.resolve(...)) fallback pattern used elsewhere). The equality comparison at line 43 stays correct either way since both sides go through the same helper, but the returned id must be canonical.

The same applies to the CJS fallback: resolveTransitiveExternal (line 57) returns createRequire(importer).resolve(request), whose value is a native-separator path fed into the same compareTransitiveExternalResolutions → returned id.

2. No coverage / handling for the ESM-vs-CJS resolution difference (low, parity gap)

Next.js baseResolveCheck bundles when either the resolved path differs or the module format differs: if (baseRes !== res || isEsm !== baseIsEsm). This PR only compares paths, not the resolved format. If the importer and root resolve to the same file but Vite would pick different export conditions (import vs require) for that request, Next.js bundles and vinext would leave it external. This is likely a rare edge and may be acceptable, but it is a deliberate divergence from the referenced baseResolveCheck; worth a code comment noting the intentional gap so it is not mistaken for full parity.

3. Test only exercises POSIX symlink layout (low)

tests/externals-transitive.test.ts builds the fixture with node:path (correct per AGENTS.md) and relies on junction/symlinks. It runs on every platform, but the assertion path (returned realpath id) would exercise the Windows backslash behavior from finding #1. If finding #1 is fixed, this test would validate the conversion on a Windows runner; if not, it may surface the id-invariant issue there. Consider confirming behavior on Windows CI or gating/annotating accordingly.

Nits

  • transitive-externals.ts:141-156: the handler returns an IIFE-wrapped async closure. Returning an async handler (or making the whole handler async) reads more directly; minor.
  • The comment at line 74 says it "Mirrors Next.js's baseResolveCheck" — given finding #2, tightening this to "the path-equality portion of" would set accurate expectations.

What looks good

  • Correct handling of the "root can't resolve" case (returns importer realpath → bundle), matching Next.js's baseRes = null branch.
  • Proper gating: filters by the bare-specifier regex, checks externalPackages.has before any resolution, and skips \0/query/non-absolute importers — keeps the common request path cheap per the perf guidance.
  • Environment scoping via applyToEnvironment (non-client only) and per-environment resolver caching through createIdResolver.
  • The nitroTraceDepsFromServerExternalsresolvedServerExternalPackages rename is consistent across all use sites.

@ask-bonk

ask-bonk Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Review posted to PR #2887. Summary of findings:

Review Summary

The PR's approach is sound — it correctly mirrors Next.js baseResolveCheck (resolve from importer, resolve from root, bundle when they differ), and the ported E2E test passes locally. I found one medium issue and two low-severity items.

Medium — fs.realpathSync output used as a Vite module id without toSlash:
In transitive-externals.ts:11-17, the realpath() result is returned as the resolved module id. Everywhere else in the codebase, fs.realpathSync/realpath.native output is wrapped in toSlash() because on Windows it yields backslash separators, violating the repo's forward-slash id invariant. It also uses non-native fs.realpathSync, whereas Rolldown canonicalizes ids via realpathSync.native — a potential duplicate-module mismatch on normalizing filesystems. The CJS fallback (resolveTransitiveExternal) has the same problem via createRequire().resolve. Fix: toSlash(fs.realpathSync.native(...)).

Low — ESM/CJS format parity gap: Next.js baseResolveCheck also bundles when isEsm !== baseIsEsm; this PR only compares paths. Likely acceptable but should be documented as an intentional divergence.

Low — test only exercises POSIX symlink layout and depends on the returned realpath id; worth confirming Windows behavior (tied to the medium finding).

Plus nits on the IIFE-wrapped async handler and the "mirrors baseResolveCheck" comment accuracy. Gating, environment scoping, resolver caching, the root-unresolvable branch, and the variable rename all look correct.

github run

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Performance benchmarks

Compared d5dfeb9 against base d558d62 using alternating same-runner rounds. Next.js was unchanged and skipped.

0 improved · 0 regressed · 6 within ±1.5%

Scenario Framework Baseline Current Change
Client bundle size (gzip) vinext 135.2 KB 135.2 KB ⚫ -0.0%
Client entry size (gzip) vinext 122.7 KB 122.7 KB ⚫ -0.0%
Dev server cold start vinext 2.27 s 2.26 s ⚫ -0.3%
Production build time vinext 2.37 s 2.39 s ⚫ +1.0%
RSC entry closure size (gzip) vinext 114.2 KB 114.2 KB ⚫ +0.0%
Server bundle size (gzip) vinext 193.0 KB 193.0 KB ⚫ -0.0%

View detailed results and traces

🟢 improvement · 🔴 regression · ⚫ change below 1.5% · paired base/head

@ask-bonk

ask-bonk Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Re-review: transitive external version preservation

The three prior findings from the last /bigbonk pass are all resolved in 8bc5851:

  • toSlash + realpathSync.nativerealpath() now returns toSlash(fs.realpathSync.native(...)) with a toSlash(...) fallback, so returned ids are canonical forward-slash and match Rolldown's native canonicalization. Fixed.
  • ESM/CJS parity comment — the doc comment now scopes itself to "the path-equality portion of baseResolveCheck" and explains why Vite's up-front import/require resolver selection replaces Next.js's format comparison. Fixed.
  • Windows id-invariant on the test — now moot given the toSlash fix; the returned id is canonical on every platform.

The core approach still looks correct (resolve-from-importer vs resolve-from-root, bundle on divergence), and the E2E test passes locally. One new finding.

1. Plugin runs in dev — should be gated to apply: "build" (medium)

packages/vinext/src/plugins/transitive-externals.ts:99-104

The plugin has no apply gate and applyToEnvironment only excludes the client environment, so its enforce: "pre" resolveId hook runs in dev (vite dev) as well as build. resolvedServerExternalPackages is populated in the shared config hook (index.ts:2588), which fires for both serve and build, so the hook is live during dev.

That is a problem specific to what this plugin does. serverExternalPackages are added to ssr.external for all commands (index.ts:3049-3053), and the comment right above that code is explicit about why: these packages "import Node-specific entry points via conditional exports (like file-type ...). Without externalizing them, Vite's optimizer picks the wrong export condition and the build fails with MISSING_EXPORT errors." Native addons (pg, sharp, etc.) are externalized for the same reason.

Because this hook is enforce: "pre" and returns an absolute resolved id, it short-circuits that externalization for any such package whenever the importer and root resolve to different copies — pulling the nested copy into Vite's transform graph instead of leaving it external. In a production build that is the intended fix (bundle the nested copy so relocation can't collapse versions). In dev there is no output relocation: Node's resolver already loads the correct nested copy natively, so forcing the package into the Vite graph is at best redundant overhead on the dev request path and at worst reintroduces the exact wrong-export-condition / native-binding breakage that dev externalization exists to prevent.

The plugin's own doc comment frames the concern purely in build terms ("relocating the server output cannot collapse both imports"), and the adjacent server-externals sibling createServerExternalsManifestPlugin() — placed one entry above this one in the plugin array — already uses apply: "build" for the same reason.

Suggested fix: add apply: "build" to the returned plugin object. If dev coverage is desired later, it should be a deliberate, separately-tested decision rather than an implicit side effect of the shared config hook.

2. No dev coverage (low, follows from #1)

tests/externals-transitive.test.ts exercises only the production path (createBuilder().buildApp() + startProdServer). That is the right place to prove the fix, but it means the dev behavior in #1 is untested in either direction. If you add apply: "build", no dev test is needed; if you intentionally keep it running in dev, add a dev-server test asserting a native-export-conditions external (e.g. a file-type-style node-only export) still resolves correctly with a nested copy present.

What looks good

  • Canonicalization fix is correct and consistent with the rest of the codebase.
  • Gating stays cheap: bare-specifier regex filter, externalPackages.has short-circuit, and \0/query/non-absolute importer skips before any resolver call.
  • Per-environment createIdResolver caching and the import-vs-require resolver split match ResolveIdFn's (environment, id, importer) contract.
  • The null root-resolution branch (bundle the importer-only copy) mirrors Next.js's baseRes = null case.
  • resolvedServerExternalPackages rename is consistent across all use sites, and the closure defers reading until after config populates it.

@ask-bonk

ask-bonk Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Review posted to PR #2887.

Summary

The three findings from the previous /bigbonk pass were all addressed in commit 8bc5851:

  • realpath() now uses toSlash(fs.realpathSync.native(...)) — canonical ids, matches Rolldown.
  • Doc comment scoped to "the path-equality portion of baseResolveCheck" and explains the ESM/CJS divergence.
  • The Windows id-invariant test concern is moot given the toSlash fix.

I found one new medium issue:

Plugin runs in dev; should be gated to apply: "build". createTransitiveExternalsPlugin has no apply gate and only excludes the client environment, so its enforce: "pre" resolveId hook fires during vite dev too (resolvedServerExternalPackages is set in the shared config hook). Because it returns an absolute resolved id, it short-circuits the serverExternalPackages externalization — the same externalization the code (index.ts:3049-3053) explicitly relies on to avoid wrong-export-condition (file-type) and native-addon (pg, sharp) failures. Relocation only happens in production builds, and the adjacent createServerExternalsManifestPlugin() already uses apply: "build". Fix: add apply: "build".

Plus a low follow-on note that the test only covers the production path (which is correct), so dev behavior is untested either way.

Everything else — canonicalization, cheap gating, resolver caching, the null-root branch, and the resolvedServerExternalPackages rename — looks correct. The targeted E2E test passes locally and vp check is clean on the changed plugin.

github run

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@ask-bonk ask-bonk Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review: transitive external version preservation (b3a51d6)

The medium finding from the previous /bigbonk pass — the plugin running in dev — is resolved. Commit b3a51d6 adds apply: "build" to createTransitiveExternalsPlugin (transitive-externals.ts:101), so the enforce: "pre" resolveId hook no longer fires during vite dev. This is consistent with the adjacent createServerExternalsManifestPlugin() (server-externals-manifest.ts:83), and correct: dev/preview serve native resolution or pre-built output, so there is no output relocation to guard against there.

I re-verified the full change and the earlier findings; everything checks out. tests/externals-transitive.test.ts passes locally (2.4s), and vp check is clean on both changed source files.

Verified correct

  • apply: "build" + configResolved orderingroot and resolvedServerExternalPackages are both populated in the main plugin's config hook (index.ts:1976, index.ts:2588), which runs before this plugin's configResolved snapshots them. No stale/undefined read.
  • Externalization override — Vite's SSR externalization matches on the bare specifier before resolution; returning an absolute resolved id from enforce: "pre" demotes it out of the external check and into the bundle. The test empirically confirms depB's nested shared-version@4.17.21 and nested pg@9.0.0 are preserved distinctly from the root copies.
  • Default externals covered — the test exercises both a user-listed external (shared-version) and a default external (pg), so the merged default+explicit set is proven.
  • Canonicalizationrealpath() returns toSlash(fs.realpathSync.native(...)) with a toSlash(...) fallback; ids are canonical forward-slash and match Rolldown's native canonicalization (prior finding, still good).
  • Cheap gating — bare-specifier regex filter, externalPackages.has short-circuit, and \0/query/non-absolute importer skips all run before any resolver call.
  • Resolver split — per-environment createIdResolver caching and the import-vs-require selection match the (environment, id, importer) ResolveIdFn contract; this.environment is captured at handler-call time inside the returned Promise.
  • RenamenitroTraceDepsFromServerExternalsresolvedServerExternalPackages is complete with no dangling references.

Low (optional, unchanged from prior passes)

  • ESM/CJS format divergence is documented (transitive-externals.ts:82-85) as an intentional gap vs Next.js's isEsm !== baseIsEsm comparison — fine as-is.
  • Dev is now correctly untested because it is gated off; no dev test needed given apply: "build".

No blocking issues. LGTM.

@ask-bonk

ask-bonk Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Review posted to PR #2887.

Summary

The PR is in good shape. The medium finding from the previous /bigbonk pass — the plugin's resolveId hook firing during vite dev — was resolved in commit b3a51d6, which adds apply: "build" to createTransitiveExternalsPlugin. This matches the adjacent createServerExternalsManifestPlugin() and is correct, since dev/preview use native resolution or pre-built output with no output relocation to guard against.

I re-verified the whole change:

  • Test passes locally (2.4s) and vp check is clean on both changed source files.
  • configResolved ordering is saferoot and resolvedServerExternalPackages are populated in the main plugin's config hook before this plugin's configResolved reads them.
  • Externalization override works — returning an absolute resolved id from enforce: "pre" demotes the package out of the bare-specifier external check into the bundle; the test proves nested shared-version@4.17.21 and pg@9.0.0 are preserved distinctly.
  • Both user-listed (shared-version) and default (pg) externals are covered by the test.
  • Canonicalization (toSlash(fs.realpathSync.native(...))), cheap gating, resolver import/require split, and the resolvedServerExternalPackages rename all check out.

No blocking issues found. The two low-severity items (ESM/CJS format divergence, dev being untested) are both already correctly documented or made moot by the apply: "build" gate.

github run

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@ask-bonk

ask-bonk Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

@james-elicx Bonk workflow failed. Check the logs for details.

View workflow run · To retry, trigger Bonk again.

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@ask-bonk

ask-bonk Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

UnknownError: ProviderInitError

github run

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@ask-bonk ask-bonk Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: transitive external version preservation (d5dfeb9)

I re-reviewed the full change independently. The approach faithfully mirrors the path-equality portion of Next.js's baseResolveCheck — resolve from the importer, resolve from the project root, and bundle the importer's nested copy when they diverge so output relocation cannot collapse two distinct package versions to the root copy. Verified locally:

  • tests/externals-transitive.test.ts passes (2.4s).
  • vp check is clean on transitive-externals.ts and the test.

Prior findings — all resolved

  • Canonicalizationrealpath() returns toSlash(fs.realpathSync.native(...)) with a toSlash(...) fallback (transitive-externals.ts:12-18). Ids are canonical forward-slash and match Rolldown's native canonicalization. Both sides of the equality at line 44 go through the same helper, so the comparison is symmetric while the returned id is canonical.
  • ESM/CJS format divergence — documented as an intentional gap (transitive-externals.ts:82-86): Vite selects an import- or require-configured resolver up front rather than comparing isEsm !== baseIsEsm. Fine as-is.
  • apply: "build" gatingtransitive-externals.ts:102 gates the enforce: "pre" resolveId hook to builds, matching the sibling createServerExternalsManifestPlugin(). Correct: dev/preview use native resolution or pre-built output, so there is no output relocation to guard against.

Independently verified correct

  • configResolved ordering is saferoot (index.ts:1976) and resolvedServerExternalPackages (index.ts:2588) are both populated in the main plugin's config hook, which runs before this plugin's configResolved snapshots them via the getRoot/getExternalPackages closures. No stale/undefined read; the handler also guards every resolver var.
  • Externalization override — Vite's SSR externalization matches the bare specifier before resolution; returning an absolute resolved id from an enforce: "pre" hook demotes the request out of the external check and into the bundle. The test empirically proves depB's nested shared-version@4.17.21 and pg@9.0.0 stay distinct from the root 3.10.1/8.0.0.
  • Default + explicit externals both covered — the test exercises a user-listed external (shared-version) and a default external (pg), so the merged default+explicit set from mergeServerExternalPackages is proven.
  • Cheap gating — the BARE_PACKAGE_SPECIFIER_RE filter (whose [^?#]* subpath excludes queried sources), the externalPackages.has short-circuit, and the \0/query/non-absolute-importer skips all run before any resolver call, keeping the hot path light per the perf guidance.
  • Resolver split — per-environment createIdResolver import/require caching, the moduleMode(resolveOptions.kind) selection, and the require-only Node createRequire fallback (never used for ESM, avoiding require-only export selection) match the ResolveIdFn contract. this.environment is read at handler-call time inside the returned Promise.
  • null root-resolution branch — returning the importer realpath when the root cannot resolve (compareTransitiveExternalResolutions line 43; CJS fallback lines 63-71) mirrors Next.js's baseRes = null case: an importer-only package must be bundled or it will not exist beside a relocated server bundle.
  • No plugin conflictcreateServerExternalsManifestPlugin() is a writeBundle/enforce: "post" collector, not a resolver, so it does not race this hook.
  • RenamenitroTraceDepsFromServerExternalsresolvedServerExternalPackages is complete across all use sites (index.ts declaration, config assignment, Nitro traceDeps merge) with no dangling references.

Nits (non-blocking)

  • transitive-externals.ts:149 returns an IIFE-wrapped async closure. An async handler reads more directly, but the IIFE preserves the cheap synchronous guard/filter path before any await, which is a reasonable tradeoff — leave as-is.

No blocking issues. LGTM.

@ask-bonk

ask-bonk Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Review posted to PR #2887.

Summary

I independently re-reviewed the whole change (d5dfeb9). The PR is well-implemented with no blocking issues — the approach correctly mirrors the path-equality portion of Next.js's baseResolveCheck, and I verified the test passes (2.4s) and vp check is clean on the changed files.

All three prior /bigbonk findings are resolved:

  • Canonicalization via toSlash(fs.realpathSync.native(...))
  • ESM/CJS format divergence documented as intentional
  • apply: "build" gating (matches the sibling manifest plugin)

Independently verified correct:

  • configResolved ordering is safe (root and resolvedServerExternalPackages set in the main plugin's config hook before this plugin's configResolved reads them)
  • Externalization override works (absolute id from enforce: "pre" demotes the request into the bundle; test proves nested shared-version@4.17.21/pg@9.0.0 stay distinct)
  • Both default (pg) and explicit (shared-version) externals covered
  • Cheap gating (bare-specifier regex excludes queried sources, .has short-circuit, \0/query/non-absolute skips)
  • Resolver import/require split and Node createRequire fallback (require-only, avoiding wrong-export selection)
  • null root-resolution branch mirrors Next.js's baseRes = null case
  • No conflict with the writeBundle-based createServerExternalsManifestPlugin()
  • Rename is complete with no dangling references

Only a non-blocking nit about the IIFE-wrapped async handler, which is actually a reasonable tradeoff since it preserves the cheap synchronous guard path before any await. LGTM.

github run

@james-elicx
james-elicx marked this pull request as ready for review August 11, 2026 03:56
@james-elicx

Copy link
Copy Markdown
Member Author

NO FINDINGS — full independent closure review of exact head d5dfeb963b396b71edac017a631b09e6a6c28903.

I reviewed the complete diff and the queried-importer follow-up. The build-only, non-client resolver strips Vite query/hash suffixes from absolute importer ids, compares canonical importer-relative and project-root package instances, and returns an absolute id only when the nested copy differs so Rolldown bundles that copy instead of collapsing it to the root external. Same-instance packages remain external. The package filter, import/require resolver split, symlink realpath handling, missing-root fallback, Nitro trace-dependency rename, and dev/preview/build boundaries do not expose a correctness or regression gap.

Validation on this exact SHA:

  • Required upstream test/e2e/externals-transitive/externals-transitive.test.ts — 1/1 passed, retry 0/0.
  • Local tests/externals-transitive.test.ts — 1/1 passed, covering a queried importer plus distinct explicit shared-version and default pg versions in production.
  • Targeted vp check — clean across all 3 changed files.
  • Final Big Bonk review is attached to this exact SHA and reports no blocking issues.
  • All CI is green, the PR is mergeable, the remote head matches, and the worktree remained clean.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant