Skip to content

Generate stable JSON from generate_spec_json.js #2843

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

Merged
merged 4 commits into from
Apr 3, 2020

Conversation

dconeybe
Copy link
Contributor

Currently, the JSON generated by generate_spec_json.js uses JSON.stringify(), which generates JSON with the keys ordered by the order in which they are set in the objects. As a result, code changes that alter the order in which the keys as added but have no other side effects cause different stringified JSON to be generated, resulting in unnecessarily-noisy diffs.

This PR changes generate_spec_json.js to use the third-party json-stable-stringify library: https://www.npmjs.com/package/json-stable-stringify. This library produces JSON that has a stable key ordering, resulting in diffs when and only when keys are added, removed, or have their values modified.

I also implemented a custom comparator so that the generated JSON has a semblance of human readability, with the test "configuration" at the top and the "expected" blocks at the bottom.

I will submit follow-up PRs to the Android and iOS repositories with the new, stable JSON. These PRs will be very noisy but will allow for cleaner future diffs.

@google-oss-bot
Copy link
Contributor

google-oss-bot commented Mar 31, 2020

Binary Size Report

Affected SDKs

SDKTypeBase (50de3fb)Head (bdbbb80)Diff
@firebase/firestore/memorybrowser224500.00223866.00-634.00 (-0.28%)
module221680.00221046.00-634.00 (-0.29%)
esm2017170334.00169785.00-549.00 (-0.32%)
main397701.00396522.00-1179.00 (-0.30%)
@firebase/firestorebrowser271073.00270246.00-827.00 (-0.31%)
module267852.00267025.00-827.00 (-0.31%)
esm2017216152.00215496.00-656.00 (-0.30%)
main488427.00487081.00-1346.00 (-0.28%)
firebasefirebase.js846912.00846074.00-838.00 (-0.10%)
firebase-firestore.memory.js266794.00266153.00-641.00 (-0.24%)
firebase-firestore.js312035.00311197.00-838.00 (-0.27%)
Metric Unit: byte

Test Logs

Copy link
Contributor

@wilhuff wilhuff left a comment

Choose a reason for hiding this comment

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

Good idea!

@@ -24,6 +24,8 @@ import { addEqualityMatcher } from '../../util/equality_matcher';
import { SpecBuilder } from './spec_builder';
import { SpecStep } from './spec_test_runner';

import * as stringify from 'json-stable-stringify';
Copy link
Contributor

Choose a reason for hiding this comment

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

It's less important in tests but we should be getting in the habit of avoiding whole-module imports like this.

However, I'm not actually sure what the right way to do this import is. Does import stringify from 'json-stable-stringify' work?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unfortunately, no, not with our current tsconfig.json. I get the following error:

index.d.ts(25, 1): This module is declared with using 'export =', and can only be used with a default import when using the 'allowSyntheticDefaultImports' flag.

That being said, I'm not very familiar with TypeScript imports and all of the other variants that I tried did not work. Any other suggestions that I could try? In case it helps, here is what the index.d.ts looks like: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/json-stable-stringify/index.d.ts

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, it looks like we're missing the esModuleInterop compiler setting.

@Feiyang1 is there any reason not to enable this repo-wide? We're already using it in our integration tests.

Copy link
Member

@Feiyang1 Feiyang1 Apr 1, 2020

Choose a reason for hiding this comment

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

We can enable it. I'm actually surprised to see that Typescript allows you to use stringify as a function directly. Thought it will give an error as it's not compliant with es6 modules spec.

Copy link
Member

Choose a reason for hiding this comment

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

* ordering if one or both of the key are not overridden.
*/
function stringifyCmp(a: stringify.Element, b: stringify.Element): number {
// If the keys are equal, then avoid the costly computations below.
Copy link
Contributor

Choose a reason for hiding this comment

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

Performance doesn't matter here so I'd optimize for readability over anything else.

You can make this simpler by creating a function that determines the group number (use e.g. stringifyCustomOrdering.length for expected and stringifyCustomOrdering.length + 1 for "everything else").

Then you can just do:

const aGroup = stringifyGroup(a.key);
const bGroup = stringifyGroup(b.key);

if (aGroup === bGroup) {
  return primitiveComparator(a.key, b.key);  // from util/misc.ts
} else {
  return primitiveComparator(aGroup, bGroup);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done. Great suggestion.

* defined in `stringifyCustomOrdering` is used, falling back to alphabetical
* ordering if one or both of the key are not overridden.
*/
function stringifyCmp(a: stringify.Element, b: stringify.Element): number {
Copy link
Contributor

Choose a reason for hiding this comment

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

Avoid abbreviations like this. Elsewhere we fully spell out "comparator".

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@wilhuff wilhuff assigned dconeybe and unassigned wilhuff Mar 31, 2020
package.json Outdated
@@ -128,5 +128,9 @@
"hooks": {
"pre-push": "node tools/gitHooks/prepush.js"
}
},
"dependencies": {
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be a dev-dependency in packages/firestore.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Copy link
Contributor

@wilhuff wilhuff left a comment

Choose a reason for hiding this comment

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

LGTM

We can address the import separately, if we choose to enable esModuleInterop.

@wilhuff wilhuff assigned dconeybe and unassigned wilhuff Mar 31, 2020
@dconeybe
Copy link
Contributor Author

dconeybe commented Apr 1, 2020

@Feiyang1 Could you review for the files changed in the root directory (package.json and yarn.lock)?

@@ -49,6 +49,8 @@
"@firebase/app-types": "0.x"
},
"devDependencies": {
"@types/json-stable-stringify": "^1.0.32",
Copy link
Member

Choose a reason for hiding this comment

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

please fix the versions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@Feiyang1 Feiyang1 assigned dconeybe and unassigned Feiyang1 Apr 1, 2020
@dconeybe dconeybe assigned Feiyang1 and unassigned dconeybe Apr 1, 2020
@dconeybe dconeybe merged commit 0f6146d into master Apr 3, 2020
@dconeybe dconeybe deleted the dconeybe/GenerateStableJsonSpecTests branch April 3, 2020 02:37
dconeybe added a commit to firebase/firebase-android-sdk that referenced this pull request Apr 3, 2020
@firebase firebase locked and limited conversation to collaborators May 4, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants