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

feat(compiler): Allow disabling the injected hydration stylesheet #2989

Merged
merged 9 commits into from
Aug 27, 2021

Conversation

splitinfinities
Copy link
Contributor

@splitinfinities splitinfinities commented Aug 4, 2021

Pull request checklist

Please check if your PR fulfills the following requirements:

  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been reviewed and added / updated if needed (for bug fixes / features)
  • Build (npm run build) was run locally and any changes were pushed
  • Tests (npm run test.karma.prod) was run locally and passed

Pull request type

Please check the type of change your PR introduces:

  • Bugfix
  • Feature
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • Documentation content changes
  • Other (please describe):

What is the current behavior?

GitHub Issue Number: #2245
Internal ticket: STENCIL-23

What is the new behavior?

This change provides a new stencil.config.ts option called invisiblePrehydration, which is defaulted true, and can be set false.

Ionic has an opinion to visually hide components while they are not hydrated by automatically injecting a stylesheet into the head of a document containing a css ruleset setting visibility: hidden on each of compiled components, only until they have a hydrated class.

This lets consumers opt-out of that Ionic opinion, so that they can use their own fallback styles, and more closely resemble the HTML Spec around Shadow DOM, Slots, and Dehydrated Web Components.

Does this introduce a breaking change?

  • Yes
  • No

Testing

  1. Check this branch out.
  2. Link the branch with npm link, or build then pack the branch.
  3. Create a new component library with npm init stencil, installing the local copy of stencil with either npm link @stencil/core --force, or npm install <path to stencil repo>/stencil-core-<version>.tgz
  4. Edit the stencil.config.ts file, and add a new line for invisiblePrehydration: false
  5. Run npm start, and when the server starts up, you should not see a style tag automatically injected into the head of the document. You can view source on that html page to see that by default, there is no style tag sent over the wire.
  6. Shut the server down, change the value to true, and you will see the style tag added.

Other information

@splitinfinities splitinfinities self-assigned this Aug 11, 2021
@splitinfinities splitinfinities requested a review from a team August 11, 2021 13:52
@splitinfinities splitinfinities marked this pull request as ready for review August 11, 2021 13:52
Copy link
Contributor Author

@splitinfinities splitinfinities left a comment

Choose a reason for hiding this comment

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

I have some changes and notes listed below

src/client/polyfills/css-shim/load-link-styles.ts Outdated Show resolved Hide resolved
src/compiler/app-core/app-data.ts Outdated Show resolved Hide resolved
src/compiler/prerender/prerender-optimize.ts Outdated Show resolved Hide resolved
test/karma/package.json Show resolved Hide resolved
test/karma/test-app/util.ts Show resolved Hide resolved
test/karma/test-invisible-prehydration/package.json Outdated Show resolved Hide resolved
@@ -155,6 +155,7 @@ export const updateBuildConditionals = (config: Config, b: BuildConditionals) =>
b.scriptDataOpts = config.extras.scriptDataOpts;
b.shadowDomShim = config.extras.shadowDomShim;
b.attachStyles = true;
b.invisiblePrehydration = typeof config?.invisiblePrehydration === 'undefined' ? true : config?.invisiblePrehydration;
Copy link
Member

Choose a reason for hiding this comment

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

Looking at the rest of this file, it looks like we don't use optional chaining for accessing the config. Is there a case you saw where we pass undefined to this function? Or can we simplify this to

Suggested change
b.invisiblePrehydration = typeof config?.invisiblePrehydration === 'undefined' ? true : config?.invisiblePrehydration;
b.invisiblePrehydration = typeof config.invisiblePrehydration === 'undefined' ? true : config.invisiblePrehydration;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm good with that. I'll try it out locally and if it breaks for some reason I'll revert it and let you know.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Worked great!

@@ -158,7 +158,7 @@ export const bootstrapLazy = (lazyBundles: d.LazyBundlesRuntimeData, options: d.
})
);

if (BUILD.hydratedClass || BUILD.hydratedAttribute) {
if (BUILD.invisiblePrehydration && (BUILD.hydratedClass || BUILD.hydratedAttribute)) {
Copy link
Member

Choose a reason for hiding this comment

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

For my own understanding, is this value always true, correct? IE This build flag just enables the feature? Or do the build conditionals get merged back into BUILD (and if so, where?)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Correct, invisiblePrehydration as a concept is true until you opt-out by setting that in consumers stencil.config.ts. I need to do more research on this BUILD concept, but I believe it's the latter right now. brb.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Interestingly topical to the rollup plugin discussion around source maps I posted a bit ago.

These get flattened within the appendBuildConditionals during the rollup plugin process.

So the BUILD that would be used in runtime is produced there.

Copy link
Member

Choose a reason for hiding this comment

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

TIL 👍 👍 👍

src/declarations/stencil-public-compiler.ts Show resolved Hide resolved
@@ -13,6 +13,7 @@ export const config: Config = {
outputTargets: [
{
type: 'www',
empty: false,
Copy link
Member

Choose a reason for hiding this comment

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

For my own information, what does empty do (in general)? I don't think we have that documented on the stencil site ATM. Does it just empty the output directory if set to true?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

empty: true (which is the default) on the www target will remove the directory prior to a build being run. This stop that from happening. Important because I wanted the sibling build to empty the directory, since it's the first build task in this process. That way, nothing in previous build processes get removed and are accessible for tests.

@@ -0,0 +1,10 @@
{
Copy link
Member

Choose a reason for hiding this comment

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

FYI - the other packages in test/karma use the older version of lock files (v1) so the can be run in an npm 6/node 12 environment. This shouldn't really matter since you're not npm installing anything for this, but something to keep in mind (and I imagine when Node 12 support is dropped (in a distant breaking change)) we'll want to upgrade those v1 ones to v2

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@splitinfinities: ADR this^^

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added in #3028

"test-app/global.ts",
"test-prehydrated-styles/components.d.ts",
"test-prehydrated-styles/**/*.spec.ts",
"test-prehydrated-styles/**/*.tsx"
Copy link
Member

Choose a reason for hiding this comment

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

For my own understanding, why do we need the top level tsconfig file to handle the transpilation too? I thought that was being handled bu the test-prehydrated-styles/tsconfig.json setup

Copy link
Member

Choose a reason for hiding this comment

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

Actually, does this directory exist? I don't see it added in this PR

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a typo, it should be prehydration. And actually... not sure if it's needed since the tests pass. I'm going to review this.

Copy link
Member

Choose a reason for hiding this comment

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

@splitinfinities whatever came of that investigation? Do we need this?

Copy link
Contributor Author

@splitinfinities splitinfinities Aug 27, 2021

Choose a reason for hiding this comment

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

Because this directory is built, then those built files are relocated, and all the tests associated are placed in the test-app directory, no we do not need this chunk. Deleted!

test/karma/test-invisible-prehydration/tsconfig.json Outdated Show resolved Hide resolved
Comment on lines 7 to 18
{
type: 'www',
dir: '../www',
empty: false,
indexHtml: 'prehydrated-styles.html',
serviceWorker: null,
},
{
type: 'www',
empty: false,
indexHtml: 'prehydrated-styles.html',
serviceWorker: null,
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure I fully understand why we need both of these output targets - would you mind explaining the usage of each to me?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My intent here was to allow for local debugging if necessary, and for placing the build content within the testable directory. It may seem clunky, and it is, but it's not as bad as it seems since this is only for putting the built files where the tests were already running.

Copy link
Member

Choose a reason for hiding this comment

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

I've got a good amount of brain fog ATM - but don't we have that ability (local debugging) from running npm start in the karma directory?

Copy link
Member

Choose a reason for hiding this comment

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

Hmmm so it looks like we don't serve the HTML in the local dev server, just the minified JS. Maybe that's what you were getting at?
Screen Shot 2021-08-25 at 8 56 44 AM

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I see what I did now. Originally, I was trying to allow tests within this directory. After trial and error, I kept this output target, but later I had moved the html file to the test-app directory, to work better with the karma automation.

Looking at this now, I think it's best to nix the local output target. I don't think there's much value in allowing these to be run directly, but if there was value, I would relocate these to the /test directory.

@splitinfinities
Copy link
Contributor Author

Added ionic-team/stencil-site#761 for the stencil site


it('the style element will not be placed in the head', async () => {
tearDownStylesScripts();
await setupDom('/invisible-prehydration-default/index.html', 1000);
Copy link
Member

@rwaskiewicz rwaskiewicz Aug 25, 2021

Choose a reason for hiding this comment

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

Double checking, is 1000 the best value here? I'm OK with it if it's the only way to avoid a flaky test, but waiting a full second for a test if we don't need it isn't ideal (this may apply to the other tests for prehyrdation as well)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I reduced each of these to 350, which seemed to be the most stable across the 6 tests I ran.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ahhh, maybe that's because of my beefy processor. Seems I'm introducing 3 new potentially flakey tests... what's your perspective on that?

Copy link
Member

Choose a reason for hiding this comment

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

Let's bring it back up to 1000 for now, I tried locally w/500 and got just-okay consistency


it('the style element will not be placed in the head', async () => {
tearDownStylesScripts();
let app = await setupDom('/invisible-prehydration-false/index.html', 1000);
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
let app = await setupDom('/invisible-prehydration-false/index.html', 1000);
await setupDom('/invisible-prehydration-false/index.html', 1000);


it('the style element will not be placed in the head', async () => {
tearDownStylesScripts();
let app = await setupDom('/invisible-prehydration-true/index.html', 1000);
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
let app = await setupDom('/invisible-prehydration-true/index.html', 1000);
await setupDom('/invisible-prehydration-true/index.html', 1000);

@@ -26,7 +26,8 @@
"paths": {
"@stencil/core": ["../../internal"],
"@stencil/core/internal": ["../../internal"],
"@stencil/core/testing": ["../../testing"]
"@stencil/core/testing": ["../../testing"],
"@test-invisible-prehydration": ["./test-invisible-prehydration"]
Copy link
Member

Choose a reason for hiding this comment

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

Do we use this path anywhere in the code? I don't see @test-invisible-prehydration anywhere in this PR

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is not, nixed

@@ -35,7 +36,10 @@
"test-app/**/*.tsx",
"test-app/**/*.ts",
"test-app/util.ts",
"test-app/global.ts"
"test-app/global.ts",
"test-invisible-prehydration/components.d.ts",
Copy link
Member

Choose a reason for hiding this comment

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

I think these would be covered by the tsconfig file in the test-invisible-prehydration directory and can be removed

@splitinfinities splitinfinities requested review from rwaskiewicz and a team August 27, 2021 16:36
@splitinfinities splitinfinities force-pushed the STENCIL-23-allow-disabling-hydration-stylesheets branch from 22f15be to b53904a Compare August 27, 2021 16:37
@splitinfinities
Copy link
Contributor Author

Rebased with the leading branch, back up for review!

@rwaskiewicz rwaskiewicz merged commit a3d2928 into master Aug 27, 2021
@rwaskiewicz rwaskiewicz deleted the STENCIL-23-allow-disabling-hydration-stylesheets branch August 27, 2021 17:11
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.

None yet

2 participants