-
Notifications
You must be signed in to change notification settings - Fork 948
Replace Platform injection with source preprocessor #3206
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
Conversation
5f799e6
to
9fb9e1f
Compare
Binary Size ReportAffected SDKs
Test Logs
|
f3296f5
to
a7dc96f
Compare
I just realized that we might also be able to do this if we publish an internal Update: I don't think this is possible due to the sheer number of internal dependencies that |
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.
LGTM with a few nits.
This isn't a crazy idea at all--it's very similar to the link-time substitution we do for platform-dependent code in the iOS codebase.
One concern: should the CDN builds be uber builds that work anywhere? As it stands it seems like they become forcibly browser. This, in turn, means that users have to take on the complexity of choosing the right build for their platform. Would we want to make it so that if you take the CDN build it will run anywhere, to keep things simple for hackathons and other get-up-and-running-quickly scenarios where you may not want to invest in setting up a bundler?
export function loadConnection( | ||
databaseInfo: DatabaseInfo | ||
): Promise<Connection> { | ||
return isNode() |
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.
Rather than duplicating this isNode ? yes : no
block in every function, is it possible to just assign a global with the platform module once? Then you'd end up with something like this here:
return getPlatform().loadConnection(databaseInfo);
Perhaps this isn't possible because there's no longer an interface that describes what every module has to conform to, in which case I'm not sure it's worth adding that interface back, but it's worth a thought.
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 don't think this would be possible unless we define an interface as an intermediate layer. It did however dawn on me that this file is never used in browser builds. I ended up replacing the isNode
check with an assert.
config/webpack.test.js
Outdated
@@ -18,6 +18,9 @@ | |||
const path = require('path'); | |||
const webpack = require('webpack'); | |||
|
|||
/** A regular expression used to replace Firestore' platform specific modules. */ |
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.
Missing 's' in "Firestore's"?
Could you expand this comment to indicate which files this is intended to match (e.g. packages/firestore/src/platform)?
Also you're matching specific files in here but not all files, which is confusing for constructing a mental model of how the platform files are handled. It would be easier to reason about this if you could just have a blanket rule that everything in platform is replaced.
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 expanded the comment and changed the RegExp to match all files. This is cleaner, but I initially wanted to keep it more restricted because I am afraid that it will start matching files in other platforms. Unfortunately, I haven't found a way to restrict it to just "packages/firestore" since the paths that are used are the relative import paths we specify in our sources.
As a general heads up - I debated getting rid of the platform.ts file before, but kept it as is during the first review. I have since moved all implementations to specific "category" files (base64.ts, serializer.ts, dom.ts, etc). These seems cleaner that having two files that are outliers.
); | ||
this._datastoreDeferred.resolve(datastore); | ||
}); | ||
loadConnection(databaseInfo).then(connection => { |
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.
What value is there in distinguishing that we "load" the connection where the other things are just new? Seems like this could benewConnection
.
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.
Updated to newConnection
.
|
||
return randomBytes(nBytes); | ||
} | ||
/** True if and only if the Base64 conversion functions are 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.
This seems related to the other base64 methods--maybe group these together?
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.
They are now group in the "base64" module.
// ReactNative-specific platform implementation that provides its own Base64 | ||
// encoding. | ||
// The exports in this class must match the exports in '../platform/platform' as | ||
// they are bundled with the browser build during the Rollup 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.
These aren't bundled with the browser build--it's the react native, build, 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.
That's correct. The comment has been removed.
packages/firestore/src/util/types.ts
Outdated
@@ -47,3 +47,18 @@ export function isSafeInteger(value: unknown): boolean { | |||
value >= Number.MIN_SAFE_INTEGER | |||
); | |||
} | |||
|
|||
/** The subset of the Window interface used by the SDK. */ |
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.
Is Window
universally available? I thought it was specific to the Browser? If so, minor nit: this is a subset of the browser's Window class. Same nit below.
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
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.
Very nice! just a few minor nits.
import * as browser from '../platform_browser/browser_format_json'; | ||
import { isNode } from '@firebase/util'; | ||
|
||
// This file provides additional platform specific APIs. It acts an add on to |
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.
missing "as" after acts
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.
Moved this to src/platform/README.md, which contains different text.
|
||
/** Formats an object as a JSON string, suitable for logging. */ | ||
formatJSON(value: unknown): string; | ||
// NOTE: This class is replaced in Firestore release builds by either |
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.
s/class/module
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.
Addressed in README.md
import * as browser from '../platform_browser/browser_random_bytes'; | ||
import { isNode } from '@firebase/util'; | ||
|
||
// This file provides additional platform specific APIs. It acts an add on to |
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.
missing "as" after acts
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.
Removed
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 confused. How do browser-based tests work? Do they have to do the full rollup build to work? That seems unfortunately because it prevents us from doing a live reload style of development.
Also, you didn't really address the question I asked at the top of the last review. This seems to be pushing us in a direction there's no way to make a build that "just works", regardless of the platform. I recognize that it's useful to produce a slimmed down web-only build, but there's also value in not having to choose and being able to get started quickly regardless of environment.
@@ -0,0 +1,4 @@ | |||
The modules in this directory provide implementations for platform-specific features that are replaced at build time |
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.
Nit: use the same line length as our sources?
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.
Done
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 updated the "generic" platform files to bring back support for Node, RN and Browsers. With that being soid, the only environment these will actually run in is Node, unless someone else builds their own build pipeline that consumes our source and not our artifacts.
For what it is worth, the generic components only compile under Node, since @grpc/grpc-js
causes bundling errors when included in the Browser builds.
I did verify that Karma's auto-watch still works as is, picking up changes in our normal sources as well as in the Platform files.
@@ -0,0 +1,4 @@ | |||
The modules in this directory provide implementations for platform-specific features that are replaced at build time |
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.
Done
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.
Pretty much LGTM
@@ -0,0 +1,53 @@ | |||
/** | |||
* @license | |||
* Copyright 2017 Google LLC |
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.
2020? Here and throughout.
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
*/ | ||
|
||
import { isNode, isReactNative } from '@firebase/util'; | ||
import { |
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 is pretty hefty amount of code to make all these distinct imports. If this is what it takes I guess this is what we're stuck with, but it doesn't seem like this file needs to be tree-shakeable so whole module imports seem like they'd work and would cost way less.
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 is pretty ugly indeed. I reverted back to namespace imports.
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.
Approval for packages/testing
for that one line change from Google Inc. to Google LLC. Sure, why not.
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.
LGTM
So this will either get my commit access revoked or result in lot of external job offers.
One of the problems with our client initialization is that we use a single Platform class to inject all platform specific dependencies. In the Lite and the Tree-Shakeable SDK this means that pulling in any code at all will bring in the platform specific dependencies.
This PR removes the Platform class and instead replaces each API with a top-level function. It then uses rollup and webpack plugins to replace the a generic platform with the target platform.
Due to some dependency conflict with ts-node, I had to extract out "formatJSON" and "randomBytes" into separate files.