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

Rewrite project to make it smaller, faster and more understandable #72

Merged
merged 15 commits into from
Jun 18, 2021

Conversation

Lodin
Copy link
Collaborator

@Lodin Lodin commented Apr 29, 2021

This PR introduces a new approach for initializing and supporting the Constructible Style Sheets. It aims to make code smaller, faster and more clear to understand.

Approach

Now we have two logic entrypoints associated with the specific class:

  • The CSSStyleSheet implementation (associated with ConstructedStyleSheet class).
  • The adoptedStyleSheets property (associated with Location class).

Details

  • The iframe element is not used anymore. Instead, the in-memory HTML document created by document.implementation.createHTMLDocument() is used. Thanks to that improvement, we no longer need to rely on the document loading status; in-memory document can be created even in <head>.
  • Following the performance idea, all CSS rules (including ones added by replace and replaceSync) are done via CSSStyleSheet.insertRule.
  • Each object that can have the adoptedStyleSheets property (Document and ShadowRoot) is wrapped with the associated Location class instance that watches internal DOM tree and connects (initialzies) each added element; it also disconnects each removed element.
  • ConstructedStyleSheet no more reveals the internal stylesheet; instead, it imitates the CSSStyleSheet behavior.
  • The adoptedStyleSheets property works closer to specification and doesn't allow adding non-constructable style sheet.
  • For better type-safety, the Typescript support added for both core and tests.
  • Code is written in ES5 to avoid including Babel helpers and reduce the general size of the polyfill.
  • Private properties are emulated via WeakMap. For ConstructedStyleSheet it allows avoiding access to internal properties by the end user; for Location it reduces the size of the polyfill when minified.
  • Added commands for Terser to inline some functions; it also allows reducing the size of the build.
  • The husky package is replaced with simple-git-hooks due to license and performance issues.
  • All dependencies are updated to the latest versions.
  • IE11 regression fixed.
  • Added size-limit package for checking the project size with limit of 2 kB.
  • @rollup/plugin-typescript is used only because babel does not support declare class A {} along with function A() {} which is necessary for correct typings of internal classes. @rollup/plugin-typescript only removes types. For tests, to add coverage @rollup/plugin-babel is used.

Size

  • Old package size (by size-limit): 2.12 kB.
  • New package size (by size-limit): 1.82 kB.

Special thanks

Special thanks to @platosha for suggesting the idea of document.implementation.createHTMLDocument().

Breaking changes?

In theory, it shouldn't affect the users because both versions comply the specification of Constructible Style Sheets. Howerver, I would suggest to release it as beta first to get the initial user feedback.

I would also suggest to merge #34 first. With it, we will be able to run tests in all browsers including Safari and IE11.

@codecov-commenter
Copy link

codecov-commenter commented Apr 29, 2021

Codecov Report

Merging #72 (1ae8460) into master (b793dd7) will increase coverage by 90.86%.
The diff coverage is 90.86%.

Impacted file tree graph

@@             Coverage Diff             @@
##           master      #72       +/-   ##
===========================================
+ Coverage        0   90.86%   +90.86%     
===========================================
  Files           0        5        +5     
  Lines           0      186      +186     
  Branches        0       33       +33     
===========================================
+ Hits            0      169      +169     
- Misses          0        4        +4     
- Partials        0       13       +13     
Impacted Files Coverage Δ
src/shared.ts 50.00% <50.00%> (ø)
src/index.ts 80.00% <80.00%> (ø)
src/Location.ts 91.95% <91.95%> (ø)
src/ConstructedStyleSheet.ts 93.75% <93.75%> (ø)
src/utils.ts 93.75% <93.75%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update b793dd7...1ae8460. Read the comment docs.

@calebdwilliams
Copy link
Owner

Nice, I’ve actually been meaning to make a ton of these changes but haven’t had the time.

I definitely think this constitutes a breaking change so well increment the major version before releasing. There’s a lot here, so it might take me a few days to put it through it’s paces, but my first skim looked good. Not entirely sure about all ES5, but I’ll let it sink in.

Copy link
Owner

@calebdwilliams calebdwilliams left a comment

Choose a reason for hiding this comment

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

Just a cursory look at this. I'll dive in deeper a bit later.

package.json Outdated
"typecheck": "tsc --noEmit"
},
"repository": {
"type": "git",
"url": "git+https://github.com/calebdwilliams/adoptedStyleSheets.git"
},
"author": "Caleb D. Williams <[email protected]>",
"authors": [
Copy link
Owner

Choose a reason for hiding this comment

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

I don't technically think this is valid in package.json. Can you revert but add a contributors? You certainly deserve credit and I'm more than happy to give it where it's due; really appreciate the effort here.

Copy link
Collaborator Author

@Lodin Lodin May 7, 2021

Choose a reason for hiding this comment

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

Oh right. I replaced it with contributors instead.

clearRules(basic);

if (sanitized) {
basic.insertRule(sanitized, 0);
Copy link
Owner

Choose a reason for hiding this comment

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

Unless I'm missing something this will fail if there is more than one rule in the stylesheet. Changing the test to

headingStyles
  .replace(
    ` h1 {
    color: tomato;
}  body { background: mediumaquamarine; }`,
  )
  .then(sheet => {
    console.log(`${sheet} styles have been replaced.`);
    console.assert(sheet === headingStyles, 'not the same', 'yass');
  });

will throw, hence the original innerHTML call. We could potentially parse the thing with a regexp and see if insertRule is faster, but I doubt it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good catch, thanks! I replaced it with the basic.ownerNode.textContent = sanitized. It will do all the parsing job for us and still allow using restyleAdopter with insertRule approach.

src/ConstructedStyleSheet.ts Show resolved Hide resolved
src/Location.ts Show resolved Hide resolved
src/Location.ts Outdated
removedSheets.forEach(function (sheet) {
// Type Note: any removed sheet is already initialized, so there cannot be
// missing adopter for this location.
/*#__INLINE__*/ removeNode(getAdopterByLocation(sheet, self)!);
Copy link
Owner

Choose a reason for hiding this comment

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

Are these /*#__INLINE__*/ comments necessary?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

They are not but it would allow Terser to understand that these functions can be inlined. I don't want to do that manually because of readability but it would be nice if we give user a chance to reduce some bytes during the minification.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Just checked: you're right, they are unnecessary; Terser does inlining based on amount of function calls. So, I removed them.

src/index.ts Show resolved Hide resolved
}

/*#__PURE__*/
export function insertAllRules(from: CSSStyleSheet, to: CSSStyleSheet): void {
Copy link
Owner

Choose a reason for hiding this comment

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

Probably want to use this above instead of this.insertRule. I still wonder about the performance about these two and if it might cause unwanted flash of styles. Should this be in a requestAnimationFrame? Again, I'm mostly just thinking out loud. I will test these either this weekend or early next week.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I tried both current and requestAnimationFrame approaches and don't see any specific difference in visualization and the flame chart. Could you test it as well? I think I would add requestAnimationFrame call anyway just to be sure it works at correct time.

@calebdwilliams
Copy link
Owner

@Lodin Also, I really think modern syntax like let/const and some (though not all) arrow functions could help the readability here. We can set the TS target to ES5 with no problems here to utilize those things. If nothing else it provides information to the developer what the intent of the variables are and TypeScript will compile those away nicely.

@Lodin
Copy link
Collaborator Author

Lodin commented May 7, 2021

Also, I really think modern syntax like let/const and some (though not all) arrow functions could help the readability here.

Well, I'm ok with it. Just thought that if we try to avoid any compilation artifacts it may be better to use ES5 approach as much as possible. However, I agree with your arguments.

Lodin added 3 commits May 7, 2021 14:46
* Fix issue with multiple rules replacement.
* Use let/const and arrow functions wherever possible.
* Use "contributors" in package.json
@calebdwilliams
Copy link
Owner

@Lodin, let me know when you're happy with this PR and I'll do a final review.

@Lodin
Copy link
Collaborator Author

Lodin commented May 14, 2021

@calebdwilliams, it's ready, actually. I'm not going to do further changes before your review.

@calebdwilliams
Copy link
Owner

@Lodin, I'm ready to merge/publish this, but I would like to do it as a major version/prerelease. In order to do that, I'd prefer to keep it in a different branch for a bit first. Would you mind setting that up so I can merge/release? Great work here, thanks for all your contributions to this project!

@Lodin
Copy link
Collaborator Author

Lodin commented Jun 16, 2021

@calebdwilliams, great news! What the branch would you like to use then? Also, what the version should it have?

@calebdwilliams
Copy link
Owner

I’d keep it simple like 3.0-rc or something like that (I’m not picky). I’ll take care of the version number when I publish it.

@Lodin
Copy link
Collaborator Author

Lodin commented Jun 17, 2021

Looks like I can re-direct the PR only if the branch exists. Could you please create a 3.0-rc branch then?

@calebdwilliams
Copy link
Owner

@Lodin done

@Lodin Lodin changed the base branch from master to 3.0-rc June 18, 2021 06:00
@Lodin
Copy link
Collaborator Author

Lodin commented Jun 18, 2021

@calebdwilliams, I have re-directed the PR to the 3.0-rc branch

@calebdwilliams calebdwilliams merged commit ad64cf5 into calebdwilliams:3.0-rc Jun 18, 2021
@calebdwilliams
Copy link
Owner

@Lodin this has been released as [email protected].

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.

3 participants