Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(hydrate): prevent dead code elimination of patch dom implementation #4966

Merged
merged 1 commit into from
Nov 15, 2023

Conversation

sean-perkins
Copy link
Contributor

@sean-perkins sean-perkins commented Oct 23, 2023

What is the current behavior?

In certain environments, such as Angular Universal (with Angular 16 or newer), the baseURI patch code will be eliminated from the final bundle. Javascript optimizers will treat the code as deadcode and eliminate it.

This causes issues in Ionic Framework: ionic-team/ionic-framework#28077

GitHub Issue Number: N/A

What is the new behavior?

  • baseURI patched implementation is assigned, so that it is not eliminated from the bundle.
  • Ionic Framework (v7) works with Angular Universal with Angular 16 or newer

Does this introduce a breaking change?

  • Yes
  • No

Testing

Other information

This guidance was provided directly by the Angular team.

@github-actions
Copy link
Contributor

github-actions bot commented Oct 23, 2023

--strictNullChecks error report

Typechecking with --strictNullChecks resulted in 1367 errors on this branch.

That's the same number of errors on main, so at least we're not creating new ones!

reports and statistics

Our most error-prone files
Path Error Count
src/dev-server/index.ts 37
src/mock-doc/serialize-node.ts 36
src/dev-server/server-process.ts 32
src/compiler/build/build-stats.ts 23
src/compiler/style/test/optimize-css.spec.ts 23
src/testing/puppeteer/puppeteer-element.ts 23
src/compiler/output-targets/dist-lazy/generate-lazy-module.ts 22
src/compiler/prerender/prerender-main.ts 22
src/runtime/client-hydrate.ts 19
src/screenshot/connector-base.ts 19
src/runtime/vdom/vdom-render.ts 18
src/compiler/config/test/validate-paths.spec.ts 16
src/dev-server/request-handler.ts 15
src/compiler/prerender/prerender-optimize.ts 14
src/compiler/sys/stencil-sys.ts 14
src/compiler/transpile/transpile-module.ts 14
src/runtime/vdom/vdom-annotations.ts 14
src/sys/node/node-sys.ts 14
src/compiler/build/build-finish.ts 13
src/compiler/prerender/prerender-queue.ts 13
Our most common errors
Typescript Error Code Count
TS2345 403
TS2322 384
TS18048 310
TS18047 100
TS2722 38
TS2532 34
TS2531 23
TS2454 14
TS2352 13
TS2769 10
TS2790 10
TS2538 8
TS2416 6
TS2344 5
TS2493 3
TS2488 2
TS18046 2
TS2684 1
TS2430 1

Unused exports report

There are 14 unused exports on this PR. That's the same number of errors on main, so at least we're not creating new ones!

Unused exports
File Line Identifier
src/runtime/bootstrap-lazy.ts 21 setNonce
src/screenshot/screenshot-fs.ts 18 readScreenshotData
src/testing/testing-utils.ts 198 withSilentWarn
src/utils/index.ts 145 CUSTOM
src/utils/index.ts 269 normalize
src/utils/index.ts 7 escapeRegExpSpecialCharacters
src/compiler/app-core/app-data.ts 25 BUILD
src/compiler/app-core/app-data.ts 115 Env
src/compiler/app-core/app-data.ts 117 NAMESPACE
src/compiler/fs-watch/fs-watch-rebuild.ts 123 updateCacheFromRebuild
src/compiler/types/validate-primary-package-output-target.ts 61 satisfies
src/compiler/types/validate-primary-package-output-target.ts 61 Record
src/testing/puppeteer/puppeteer-declarations.ts 485 WaitForEventOptions
src/compiler/sys/fetch/write-fetch-success.ts 7 writeFetchSuccessSync

@sean-perkins
Copy link
Contributor Author

Let me know if we have any concerns to this approach. Happy to run through the Angular Universal testing I did locally. I also have additional comments to the issue in the linked framework issue.

@sean-perkins sean-perkins marked this pull request as ready for review October 26, 2023 14:58
@sean-perkins sean-perkins requested a review from a team as a code owner October 26, 2023 14:58
Copy link
Contributor

@rwaskiewicz rwaskiewicz left a comment

Choose a reason for hiding this comment

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

@sean-perkins Thanks!

Can you expound on

Installed the dev-build in the user's test application and confirmed the application renders
Additionally verified that the baseURI code ends up in the final bundle

and the steps you took to do that? Ideally, if we were to be going through git blame in the future and wanted to figure out how this was tested, it'd be ideal if there were steps that someone on the team could use to reproduce the error/fix

@@ -38,7 +38,8 @@ export function patchDomImplementation(doc: any, opts: d.HydrateFactoryOptions)
}

try {
doc.baseURI;
// Assigning the baseURI prevents JavaScript optimizers from treating this as dead code
win.__stencil_baseURI = doc.baseURI;
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need to assign to win here to avoid dead code elimination? I.E. would something like:

Suggested change
win.__stencil_baseURI = doc.baseURI;
const __stencil_baseURI = doc.baseURI;

(with maybe an eslint disable rule pragma above it for unused variables) prevent us from elimination of this code without adding something to global?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Test reproduction steps would follow the feedback here: ionic-team/ionic-framework#28077 (comment)

Only with the modifications of:

  1. Install the provided Ionic dev-build
  2. Run against Angular 16 (the issue reporters originally reproduction app)
  3. Validate that:
try {
  win.__stencil_baseURI = e2.baseURI;
} catch {
  Object.defineProperty(e2, "baseURI", {
    get() {
      const t3 = e2.querySelector("base[href]");
      return t3
        ? new URL(t3.getAttribute("href"), r.location.href).href
        : r.location.href;
    },
  });
}

is in the patchDomImplementation function in the compiled bundle.

I do not know if a local assignment of an unused constant would get treated as a side-effect. The Angular team was not certain to which underlying package update between Angular 15 → Angular 16 had an impact on the analysis and removal of dead code. The recommendation from them was assigning to win after looking at Stencil's implementation, but if there is some performance impact to assigning to mock window we can discuss that.

Copy link
Contributor

Choose a reason for hiding this comment

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

Okay, that all sounds reasonable to me - can you help me understand where I'd find that code snippet? So far, I've:

  1. Downloaded the reproduction case linked above
  2. Installed your dev build
  3. Verified we're using Angular 16
  4. Run npm run build, but I'm not sure where I should be looking for patchDomImplementation. I'm sure I'm missing something, but I'm not sure what exactly.

For context, I put together a branch of Stencil with the suggested change to try it out, created a dev build of Stencil and then used that to create a dev build of Framework. I wanted to try this out myself, but first want to verify that that code snippet you provided with the existing dev build (so I could tell if/what my build changed)

Choose a reason for hiding this comment

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

Hope I'm not stepping on any toes pitching in ... (in that case, apologies).

Run the command npm run build:ssr to build the SSR app. Open the generated dist/server/main.js, optionally format it, and look for function patchDomImplementation in it.

I can find the code snippet, with one difference. I have r.__stencil_baseURI=e2.baseURI instead of win.__stencil_baseURI=e2.baseURI

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Apologies for the delay testing this, I created a new build with your dev-build for using a const: 7.5.6-dev.11700071366.1395b143 (Framework dev-build).

This does not resolve the problem. Bundler still optimizes that bit of code for removal.

@rwaskiewicz rwaskiewicz added the Awaiting Reply This PR or Issue needs a reply from the original reporter. label Oct 27, 2023
@rwaskiewicz rwaskiewicz removed the Awaiting Reply This PR or Issue needs a reply from the original reporter. label Nov 15, 2023
@rwaskiewicz rwaskiewicz added this pull request to the merge queue Nov 15, 2023
Merged via the queue into stenciljs:main with commit 5e36057 Nov 15, 2023
@rwaskiewicz rwaskiewicz added the Resolution: Refine This PR is marked for Jira refinement. We're not working on it - we're talking it through. label Nov 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Resolution: Refine This PR is marked for Jira refinement. We're not working on it - we're talking it through.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants