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

Adding fingerprint to compiled css #694

Closed
wants to merge 14 commits into from
Closed

Conversation

gedeagas
Copy link
Contributor

Motivation

As stated here #439 fingerprinting the CSS file is necessary when the website use CDN. It is also necessary to fingerprint our static file because in PWAs they cache the content via paths.

Have you read the Contributing Guidelines on pull requests?

Yes

@docusaurus-bot
Copy link
Contributor

docusaurus-bot commented May 23, 2018

Deploy preview for docusaurus-preview ready!

Built with commit 59525aa

https://deploy-preview-694--docusaurus-preview.netlify.com

@gedeagas
Copy link
Contributor Author

gedeagas commented May 23, 2018

This is still a WIP.

I am not sure if adding a hash.txt file on the build folder is the right and cleanest approach in order to supply the CSS hash string to the test suites

Copy link
Contributor

@yangshun yangshun left a comment

Choose a reason for hiding this comment

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

@gedeagas Thanks for the PR! However, I don't agree with this approach that a random hash is generated on every build. I think the better approach is to generate the hash based on file contents instead so that if the file contents doesn't change, the hash remains the same and loaded assets can be cached.

@@ -88,10 +88,14 @@ test('Generated table of contents', function() {
});

test('Concatenated CSS files', function() {
let hash = fs.readFileSync(
buildDir + '/' + siteConfig.projectName + '/hash.txt',
Copy link
Contributor

Choose a reason for hiding this comment

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

What is there a need for hash.txt?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the hash.txt file is needed in order to supply the CSS hash string to the test suites. but this is only a temporary fix because as I say in my previous comment adding a hash.txt file on the build folder is not the right nor cleanest approach to this. Do you have any suggestion on 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.

lib/core/Head.js Outdated
<link
rel="stylesheet"
href={
this.props.config.baseUrl + 'css/' + this.props.hash + '.main.css'
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't it more common to have main.[hash].css? At least that's the webpack convention.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Got it


commander.option('--skip-image-compression').parse(process.argv);

// create secure crypto hash for css
var secureHash = crypto.randomBytes(10).toString('hex');
Copy link
Contributor

Choose a reason for hiding this comment

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

Use const

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Got it

@gedeagas
Copy link
Contributor Author

Will try to implement hashing based on the content later today. Thank you for your suggestion @yangshun

@gedeagas
Copy link
Contributor Author

How do you think @yangshun ?

lib/core/Head.js Outdated
rel="stylesheet"
href={this.props.config.baseUrl + 'css/main.css'}
/>
{this.props.hash ? (
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like we can save few lines of code by removing double re-use of <link ....

Example:

href = {`${this.props.config.baseUrl}css/main${this.props.hash ? this.props.hash : ''}.css`}

or ( i prefer this )

href = {this.props.config.baseUrl + 'css/main.' + this.props.hash ? this.props.hash : '' + '.css'}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

woah, this not crossed my mind before @endiliey. i think your second option is what we should do

Copy link
Contributor

@endiliey endiliey Jun 5, 2018

Choose a reason for hiding this comment

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

Actually @yangshun told me template literal string is easier to read

this.props.hash ?
`${this.props.config.baseUrl}css/main${this.props.hash}.css` :
`${this.props.config.baseUrl}css/main.css` :

@JoelMarcey
Copy link
Contributor

@gedeagas Hi! I think we are just waiting for the suggested change and a rebase since there are some conflicts now.

Copy link
Contributor

@endiliey endiliey left a comment

Choose a reason for hiding this comment

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

Please make the suggested changes (refactor), resolve the merge conflicts & provide a test plan whereby the same build(no changes in file content) produces the same hash and different build produces different hash

@gedeagas
Copy link
Contributor Author

Hi @JoelMarcey, I will be working on this today. Sorry for the delay. crazy week at the office.

Got it @endiliey

@endiliey endiliey force-pushed the master branch 2 times, most recently from dbb6441 to a5c13f2 Compare June 18, 2018 16:45
endiliey
endiliey previously approved these changes Jun 18, 2018
Copy link
Contributor

@endiliey endiliey left a comment

Choose a reason for hiding this comment

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

I patched & refactored @gedeagas PR. It should be working now. I tested with 5 different build, and the same build(no changes in file content) produces the same hash and different build produces different hash

Test Plan

  1. No change on main.css
    Result: main.1ac8d6325f8699c1b86a3b61854ad4e82f880891.css
  2. No change on main.css
    Result: main.1ac8d6325f8699c1b86a3b61854ad4e82f880891.css
  3. Change main.css
    Result: main.0a037522d71b06ecf631742f19a4acd6fbd04ce6.css
footer .social {
  padding: 15px 0;
}
  1. Change main.css
    Result: main.d06b7994781743bfed253162cc0a59c3c7e0452d.css
footer .social {
  padding: 25px 0;
}
  1. Change main.css back to original
    Result: main.1ac8d6325f8699c1b86a3b61854ad4e82f880891.css

Our css is now fingerprinted 😄
fingerprint

@endiliey endiliey dismissed their stale review June 18, 2018 17:11

Edit review

@yangshun
Copy link
Contributor

yangshun commented Jun 18, 2018

One thing we need to test is whether the separate css works. For example, Prettier has a separate playground that hardcodes the CSS path there. Will their code break when we introduce this?

@endiliey
Copy link
Contributor

endiliey commented Jun 18, 2018

By right it should work because the implementation only fingerprint the compiled css from Docusaurus main.css and all css in website/static/css

const secureHash = fingerprintCss(join(CWD, 'static', 'css', '*.css'));

Example:
Our prism.css is defined separately so it's not even hashed & used separately
separate

But just to be safe, I'll test on prettier tmrw

@endiliey
Copy link
Contributor

endiliey commented Jun 19, 2018

@yangshun

Just tried prettier website, serving the build with python http server. Seems that separate css work as per normal
separate css

Only compiled css (compiled to main.css) get hashed
maincss

Edit: On a side note, let's not rush to merge this 😄

@yangshun
Copy link
Contributor

yangshun commented Jun 19, 2018 via email

@endiliey
Copy link
Contributor

Yes, if the playground page referenced main.css (without hash). It would fail.

Without this PR

build/prettier/
├── css
│   └── main.css

With this PR it will be something like

build/prettier/
├── css
│   └── main.0cfe7d770fe94c50109893d6354ad47842c8c1fb.css

It's a given that static html page like this didn't know the hash and won't be able to reference main.css.

All other pages (docs, blogs, *.js) won't have a problem because we inject the main.css (with hash) in Head.js

However, a way for static HTML pages to use our own Docusaurus site styles, header and footer is to set wrapPagesHTML to true (https://docusaurus.io/docs/en/site-config#optional-fields)
https://github.com/facebook/Docusaurus/blob/169a5e6ded1d742feabf13878d559dd80fd19384/lib/server/generate.js#L564-L576
Then this one will be injected with the main.css (with hash)

@JoelMarcey
Copy link
Contributor

So if we landed this, then Prettier would have to update their site or the playground would break? Just making sure I understand that correctly.

@endiliey
Copy link
Contributor

@JoelMarcey it won't break prettier's website because they never reference Docusaurus main.css on their static HTML page. I think we are talking about corner cases where people reference Docusaurus css files directly in .html instead of using proper wrapPagesHTML

@endiliey
Copy link
Contributor

endiliey commented Jun 19, 2018

I just checked https://docusaurus.io/docs/en/custom-pages#adding-static-pages

Static .html files can also be used, but they will not include Docusaurus' header, footer, or styles by default. These can be added to the static directory in the same way as other static assets. Alternatively, they can be placed in the pages directory and would be served as-is instead of being rendered from React.
If you wish to use Docusaurus' stylesheet, you can access it at ${baseUrl}css/main.css.
You can set the $wrapPagesHTML site config option in order to wrap raw HTML fragments with the Docusaurus site styling, header and footer.

Seems that this will break someone's else code 😂
A very simple solution is that we also generate main.css (without hash) for backward compatibilities

build/prettier/
├── css
│   └── main.0cfe7d770fe94c50109893d6354ad47842c8c1fb.css
│   └── main.css

@yangshun
Copy link
Contributor

A very simple solution is that we also generate main.css (without hash) for backward compatibilities

I thought of that too. But doesn't seem to be an elegant solution. If this feature is not that important shall we hold off for now?

@endiliey
Copy link
Contributor

endiliey commented Jun 19, 2018

I agree, it defeats the purpose of fingerprinting. I think this feature is not that important (sorry @gedeagas 😢). It might become important if we focus on building offline version of Docusaurus. Let's put this on hold

@endiliey endiliey added status: don't merge yet This PR is completed, but we are still waiting for other things to be ready. do-not-reap proposal This issue is a proposal, usually non-trivial change and removed Major Change labels Jun 19, 2018
@parano
Copy link
Contributor

parano commented Jul 30, 2018

For those who deploy to CDN and still looking for a workaround, we currently use the following lines in our deployment script to add fingerprint to CSS, before pushing to S3:

GIT_ROOT=$(git rev-parse --show-toplevel)
GIT_HASH=$(git rev-parse HEAD)

cd $GIT_ROOT/website/build/
mv css/main.css css/main-$GIT_HASH.css
find ./ -name '*.html' -print0 | xargs -0 sed -i '' 's/\"\/css\/main.css\"/\"\/css\/main-'$GIT_HASH'.css\"/g'

hope it's helpful

@endiliey
Copy link
Contributor

Closing this in favor of v2 implementation

@endiliey endiliey closed this Aug 22, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed Signed Facebook CLA proposal This issue is a proposal, usually non-trivial change status: don't merge yet This PR is completed, but we are still waiting for other things to be ready.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants