-
Notifications
You must be signed in to change notification settings - Fork 2k
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
SSR The config module #11352
SSR The config module #11352
Conversation
1f3dc51
to
74a0d6c
Compare
client/config/index.js
Outdated
@@ -0,0 +1,11 @@ | |||
let config; | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
todo: add comment or readme explaining whats going on
Cool idea! Thanks for working on it. It would be great to improve config handling, in particular in the context of unit tests. I'll double check this proposal on Monday. There might be security concern with this approach, because at least on desktop there are secret keys loaded in the server build. This code would probably expose those keys to the client. It needs to be verified. |
9fa2c85
to
e9c32ff
Compare
client/config/index.js
Outdated
config.isEnabled = window.isEnabled; | ||
} else { | ||
throw new Error( 'config not available' ); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can flip this conditional and then use a constant value…
/**
* Manages config flags for various deployment builds
* @module config/index
*/
if ( 'undefined' === typeof window || ! window.config ) {
throw new ReferenceError( `No configuration was found: please see client/config/README.md for more information` );
}
/**
* @typedef {Object} CalypsoConfig
* @property {Function} anyEnabled whether one of a set of properties is enabled
* @property {Function} isEnabled whether a specific property is enabled
*/
export default config = {
...window.config,
anyEnabled: window.anyEnabled,
isEnabled: window.isEnabled,
};
Another thing to think about that may or may not come into play here is that Object.assign()
sets values while destructuring assignment defines them. These are small semantic differences and if we have any properties defined on config
then we may want to make sure we carry those over (and not run or evaluate them while copying).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the docs :). I'm definitely going to put those in. Also TIL about ReferenceError!
Another thing to think about that may or may not come into play here is that Object.assign() sets values while destructuring assignment defines them.
window.config
is a function here. I don't think spread works for those...does it? I usually prefer spread/object literal when possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
window.config is a function here. I don't think spread works for those
Just tested it out in the online babel repl
a = () => 5;
b = { ...a }
console.log( a() );
console.log( b() );
transpiled to:
"use strict";
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
a = function a() {
return 5;
};
b = _extends({}, a);
console.log(a());
console.log(b());
which printed out:
Uncaught TypeError: b is not a function
at <anonymous>:1:1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
absolutely - I didn't realize/think about that it was a function. that's too bad, but thanks for verifying that and adjusting accordingly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@samouri thanks for making this change. If we can get this ready to go it's going to be a major boon for development.
I'm not that familiar with this part of the build process so I don't have much to offer. However, I would definitely implore you to spend some time understanding the scope of the improvements you are bringing and the system they change and then write up some guides for the benefit of others (README.md is fair enough)
This would significantly help further efforts to improve the build process.
👍
I spun this up locally in docker and ran some e2e tests against it, couldn't find any issues. |
One problem with this that comes to mind is that we won't have the correct build time config for each environment. For example, when building the sections we use |
i think @ehg is right, I can't access devdocs in the docker instance for some reason even though I'm testing out putting EDIT: its this line https://github.com/Automattic/wp-calypso/pull/11352/files#diff-3254677a7917c6c01f55212f86c57fbfR42 Since it builds with |
A solution i came up with that I think may be pretty valid is:
The client can request which bundles it needs. There is one big constraint that this idea would create though:
|
related is existing work on async loaders and changing the way we request bundles. if we had a more flexible and robust system for loading code asynchronously this would be less of a concern I would imagine since we wouldn't be tied to the sections. by the way, the section bundles have been serving us incredibly well and props to those who made them work the way they do! |
Dockerfile
Outdated
@@ -39,11 +39,7 @@ RUN npm install --production || npm install --production | |||
COPY . /calypso | |||
|
|||
# Build javascript bundles for each environment and change ownership |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment above becomes no longer valid.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed.
Dockerfile
Outdated
CALYPSO_ENV=stage make build-stage && \ | ||
CALYPSO_ENV=production make build-production && \ | ||
chown -R nobody /calypso | ||
RUN CALYPSO_ENV=production make build && chown -R nobody /calypso |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This need to be further investigated, because we have 3 ways of detecting env in client and server code:
config( 'env )
NODE_ENV
CALYPSO_ENV
secondary: true, | ||
enableLoggedOut: true | ||
} ); | ||
sections.push( { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that we need to remove those feature flag checks from files where sections definition are located. At the same time I think we should move existing logic to entry point of each individual section. This means I'm suggesting to bring back those file guard to the first file which isn't generated by Webpack, to make sure we don't change any existing logic. We can remove some flags in separate PRs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think both are required in this PR:
- removing feature flag checks for build (so that all bundles are generated)
- adding feature flag checks to controller files (so that routes inaccessible in a specific env stay inaccessible)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this mean we'll be generating a bundle for devdocs regardless of config settings?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mtias yes, the bundle will be generated regardless of config (this should be bringing us to a single build)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure generating these bundles if they are disabled at the config level is a good path. It makes extending Calypso and using it to build things harder.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does it differ from using feature flags nested deeper into a chunk? I think it is a very good simplification that bring lots of benefits simplifying build process and drastically reducing its time.
However we need to sync it with extensions. I double checked it and it turns out that our code depends on env_id
(https://github.com/Automattic/wp-calypso/blob/master/client/sections.js#L17). We plan to use production
env for all non-development envs when preparing build targeted for Docker, which means it won't work as expected for staging
, wpcalypso
and horizon
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mtias: I'm not sure how generating /not-generating bundles can affect extensibility.
Whether or not the client requests the bundle is up to it. For example, theres no reason the devdocs
bundle can't exist on the server for NODE_ENV=production
, as long as the client doesn't register the /devdocs
routes via page
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gziolo I'll fix that now. My current solution will be to move that check to the individual index.js and hopefully eventually we can autowrap the extensions's default exports with an env_id
check
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just wanted to summarize what we need to be aware of when working on this PR. Calypso code depends on the following constructs: As far as I understand there was intent to have:
This is how I read it from config files. All of those different expressions are compared in many places against We also need to keep in mind that we are using here Webpack DefinePlugin which is able to replace all code occurrences like Much better in code explanation how
I hope this helps reviewing this PR :) |
One more important note. We need to make sure we don't break Desktop app introducing those changes. I don't remember how config was consumed in https://github.com/Automattic/wp-desktop but I have a feeling that it could have it's own flavors :) |
server/config/index.js
Outdated
'<%= anyEnabled %>' | ||
) ( { | ||
data: JSON.stringify( clientData ), | ||
config: config.toString(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wouldn't worry about duplicating the config module on the client if I were you, I think we should avoid serializing functions here and depending on several globals.
1 global object containing only the raw configuration should be enough for our needs:
window.configData = <%= data %>;
Moreover, we could easily share the config module between the client and the server.
function loadConfig( configData = null ) {
data = configData || window && window.configData;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree on this one. At the moment we have this code inlined in HTML file:
function config(key) {
if (key in data) {
return data[key];
}
throw new Error('config key `' + key + '` does not exist');
}function isEnabled(feature) {
return !!data.features[feature];
}function anyEnabled() {
var args = Array.prototype.slice.call(arguments);
return args.some(function (feature) {
return isEnabled(feature);
});
}
We should rather keep config API code in the JS file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just tested this out locally. While I'd rather rely on the same module in both client/server, I couldn't figure out how to ensure webpack would exclude the serverConfig from the bundle (without changing what this file exports)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I branched off a PR to test this out: #11454
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
server/bundler/bin/bundler.js
Outdated
@@ -114,7 +114,7 @@ webpack( webpackConfig, function( error, stats ) { | |||
} ).map( function( chunk ) { | |||
return path.join( process.cwd(), 'public', chunk.file ); | |||
} ); | |||
files.push( path.join( process.cwd(), 'public', 'vendor.js' ) ); | |||
|
|||
files.unshift( path.join( process.cwd(), 'public', 'vendor.js' ) ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We usually rebase to avoid those merge commits ;)
git rebase origin/master
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this may not have been the best place to test out the resolve conflicts feature of GitHub. Is there anything different (or worse) about this method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
one at the front, one at the end…
Looks good so far, good job on this @samouri :) I think this might need extensive testing before we are confident about a merge but I'm optimist, don't hesitate to keep pinging people to have a look! |
secondary: true | ||
} ); | ||
} | ||
sections.push( { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@scottsweb It looks like you created scaffolding for VIP section. It doesn't look like it is actively developed. Do you use those pages or would it be good idea to remove those files?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are not currently using those files and any further development there is at least 3 months out. If you would like to remove them then please do. I imagine a great deal has changed in Calypso since they were put together :) cc @gziolo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for letting me know. I'll open a follow up PR once this one gets merged ;)
…plate literals 1. use es6 template literal instead of lodash template 2. remove anyEnabled from config api since it is never used 3. CALYPSO_ENV --> config( 'env_id' ) 4. docs changes thanks @withinboredom / slight doc updates 5. move extensions check to individual extension instead of in sections.js so it happens at runtime
…erences Build: Move env check for extensions to bundler loader
…, combine server/config with client/lib/config
9670e46
to
baae88d
Compare
@@ -157,7 +150,7 @@ const sections = [ | |||
name: 'themes', | |||
paths: [ '/design' ], | |||
module: 'my-sites/themes', | |||
enableLoggedOut: config.isEnabled( 'manage/themes/logged-out' ), | |||
enableLoggedOut: true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since manage/themes/logged-out
is true in every environment configuration I think that modifying this should be harmless. Is this true?
cc @seear
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct. We should also remove manage/themes/logged-out
from the config files if we set this to true, in case anyone tries to turn that flag off in the future expecting it to have any effect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Darn, I missed this comment earlier. I'll follow up with a PR after this lands
sections.push( { | ||
name: 'post-editor', | ||
paths: editorPaths, | ||
paths: [ '/post', '/page', '/edit' ], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since manage/custom-post-types is true in every environment configuration I think that modifying this should produce no changes
cc @aduth
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since manage/custom-post-types is true in every environment configuration I think that modifying this should produce no changes
Noticing I hadn't replied here. Yes, this should be fine, but we should probably also remove the config flag altogether if we're starting to make assumptions here that it's enabled in all environments.
]; | ||
|
||
envs.forEach( env => { | ||
process.env.NODE_ENV = env; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice to revert process.env.NODE_ENV
to value set before this test suite is executed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By the way we use only two values for NODE_ENV: development
and production
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think thats what Line 54 is doing right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yea, that whole section should be modifying CALYPSO_ENV
not NODE_ENV
. Its a completely separate issue to this PR though, so i'd rather fix that in a smaller future pr
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I missed that, sorry :)
2nd comment is still valid though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's correct as it is in my opinion to use NODE_ENV
. The idea here is take advantage of dead code elimination and never execute this check in non-development envs. @dmsnell can you confirm?
group: 'sites' | ||
} ); | ||
} | ||
sections.push( { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
manage/drafts
isn't enabled on production and staging. We need to add this check in my-sites/drafts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I looked into it, and its already there :)
https://github.com/Automattic/wp-calypso/blob/master/client/my-sites/drafts/index.js
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I missed that ;)
|
||
if ( config.isEnabled( 'reader' ) ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's safe to remove. We don't have the same check inside all reader
modules, but it is set to true
for all envs.
There are a couple action items for after this lands:
Calypso.live
wp-desktopThere is nothing fundamental about this PR that is incompatible with |
} ), | ||
obj = {}; | ||
|
||
keys.forEach( function( key ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@samouri I think we need to bring it back when we expose clientData
in server/config/parser.js
. It performs filtering based on keys loaded from client.json
file.
One subtle change from this pull request is that it's no longer possible to use |
@aduth, if it is important to keep |
It's not my use-case, so I can't really speak to whether it's important. Just passing along an observed change from @jmosky who'd expected to be able to use debug logging in staging. p1490989187543272-slack-calypso |
It's extremely useful to be able to see what events fire as I go through WP.com. It doesn't matter to me whether I have to go to https://calypso.live/?branch=master vs. just launching the debugging tool in staging. Also, I think this might be related: #12277 . |
i'll whip up a pr that allows debug statements to go through in all stages except production with a runtime check to |
Calypso currently has different configuration for its different stages and the way that we handle it causes some inefficiencies. We have almost the same exact modules for wpcalypso, horizon, staging, and production. The only difference between them is the
config
module. This single module difference essentially forces us to separate out bundles for each environment and call the bundler 4 times within a docker build.This PR seeks to move the
config
module to be sent by the server as part of the initial payload which:bundle-wpcalypso.js
,bundle-production.js
, etc.Speed tests on my machine:
/try/framework/ssr-config
master
To Test
related issues this should solve as well: