-
Notifications
You must be signed in to change notification settings - Fork 930
Add tooling to verify dependency chain #3033
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
@@ -0,0 +1,15 @@ | |||
{ |
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.
Note: This file is generated by the Node CLI tool and then used in the unit test as the source of truth. It is checked in and as such provides a nice diff as we make changes.
"sizeInBytes" is for reference only.
9891d6e
to
f21776b
Compare
"dependencies": [ | ||
"bar" | ||
], | ||
"sizeInBytes": 39 |
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 per function? seems like a pain to always update
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 auto-updated via the script, but the script is not run automatically. I'll send this to @Feiyang1 and we can ponder how we can automate this step.
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.
Looks cool, excited to see how it works in practice. Should we consider adding a pre submit hook that calls this to regenerate the .json?
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.
Cool stuff. We should definitely automate it and generate a nicely formatted report that we can review for every PR.
For some reason, I kept getting Error: ENOENT: no such file or directory, open '/Users/feiyangc/Projects/firebase-js-sdk/packages/firestore/dist/lib/src/util/error.d.ts'
and couldn't compile the project and test the scripts. Any clue?
dependencies.sort(); | ||
expect(extractedDependencies).to.have.members(dependencies); |
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.
dependencies.sort();
is not necessary because order doesn't matter in .members()
.
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.
scripts/exp/extract-deps.ts
Outdated
return JSON.stringify(result, null, 4); | ||
} | ||
|
||
const publicApi = extractFunctionsAndClasses(path.resolve(argv.types)); |
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 doesn't handle reexports, e.g. export * from 'src/impl'
. We can generate a rollup d.ts file to sidestep the problem, or we need to traverse the AST to get all 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.
Isn't that more like the behavior we want though? We bundle all of our own sources into a single script. If one of our dependencies changes, we wouldn't want our tests to break.
Or are you planning to no longer ship a single bundled source file? That might break Firestore and other SDKs as we have a ton of transitive dependencies. Bundling works around 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.
You are extracting APIs from the d.ts file, not the source file, right? Currently we don't bundle d.ts files into a single file, so there can be reexports from other d.ts files.
As we plan to have multiple entry points in firestore next, it will be easier to not have a single bundled source file so it's easier to share code between different entry points. Why would transitive dependencies be a problem 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.
The public APIs are extracted from a handcrafted index.d.ts. Are we moving away from hand-crafted types? Note that this is not meant to work for all of Firebase, but only for each specific SDK.
FWIW, as more SDKs adopt this, we probably have to greatly expand the feature set of this tooling. I wonder if it makes sense to wait until there is a need, which can then happen together with CI integration?
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 leave that decision to teams. But generally we don't need to handcraft d.ts anymore in the modular world, because we control what gets exported in the source code, and we can generate d.ts files with only public APIs from it automatically.
I generate index.d.ts
from source code and use it for @firebase/app-exp
. It allows me to keep documentation in the source code as well. I expect most of our SDKs to use automatically generated d.ts files in the modular world.
Sure. that sounds reasonable to me.
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 interesting to see if we can hide SDK-public internals from the public typings without splitting up all Firestore classes into abstract Public-API-only base classes and implementation classes. I will keep an eye on this as we rewrite the client.
scripts/exp/extract-deps.helpers.ts
Outdated
ts.forEachChild(sourceFile, node => { | ||
if (ts.isFunctionDeclaration(node)) { | ||
exports.push(node.name!.text); | ||
} else if (ts.isClassDeclaration(node)) { | ||
exports.push(node.name!.text); | ||
} |
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'd be interesting to see variable declarations too, e.g. the error message map.
It'd be nice to keep the types of the declarations and show them in the report, but it can be done in a separate 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.
Should be pretty easy to do as part of this PR. I will report back in my conversation with myself.
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.
Yep, done.
…js-sdk into mrschmidt/extractdeps
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 know why you suddenly got the build failure. It seems unrelated to this PR. I was able to repro using a clean checkout though and fixed it. I hope there is no adverse size impact, but I am waiting for GitHub Actions to come back to confirm.
dependencies.sort(); | ||
expect(extractedDependencies).to.have.members(dependencies); |
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.
scripts/exp/extract-deps.helpers.ts
Outdated
ts.forEachChild(sourceFile, node => { | ||
if (ts.isFunctionDeclaration(node)) { | ||
exports.push(node.name!.text); | ||
} else if (ts.isClassDeclaration(node)) { | ||
exports.push(node.name!.text); | ||
} |
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.
Should be pretty easy to do as part of this PR. I will report back in my conversation with myself.
scripts/exp/extract-deps.helpers.ts
Outdated
ts.forEachChild(sourceFile, node => { | ||
if (ts.isFunctionDeclaration(node)) { | ||
exports.push(node.name!.text); | ||
} else if (ts.isClassDeclaration(node)) { | ||
exports.push(node.name!.text); | ||
} |
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.
Yep, done.
scripts/exp/extract-deps.ts
Outdated
return JSON.stringify(result, null, 4); | ||
} | ||
|
||
const publicApi = extractFunctionsAndClasses(path.resolve(argv.types)); |
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.
Isn't that more like the behavior we want though? We bundle all of our own sources into a single script. If one of our dependencies changes, we wouldn't want our tests to break.
Or are you planning to no longer ship a single bundled source file? That might break Firestore and other SDKs as we have a ton of transitive dependencies. Bundling works around that.
0f3f537
to
8203d4d
Compare
8203d4d
to
4701cde
Compare
@Feiyang1 The quick and dirty fix for your build issue increased the size of the SDK. Instead, I updated |
@@ -40,7 +40,7 @@ export abstract class Emulator { | |||
|
|||
download(): Promise<void> { | |||
return new Promise<void>((resolve, reject) => { | |||
tmp.dir((err: Error, dir: string) => { | |||
tmp.dir((err: Error|null, dir: string) => { |
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 was needed to fix the CI.
This adds tooling to verify top-level dependencies of tree-shakeable APIs. It is hooked up a a very basic version of a tree-shakeable API.
The goal of this API is to let us verify the dependency chain. One example where I used this is here: #3024
I suspect this might be helpful for other APIs and as such I added it to
/scripts
.