The update-bcd script iterates through entries from a local copy of browser-compat-data and compares them against a set of test results from a local copy of mdn-bcd-results to see if there should be any updates to the browser-compat-data repo.
The update-bcd script keeps an internal representation of the data from each iterated BCD file for purposes of comparing and applying possible transformations to the data.
As the script iterates through each BCD entry, an UpdateInternal object is created to represent the state of the data. This state object is shared across all of the script's sequential operation. The primary keys in this object are:
path - the "path" of the BCD entry we're iterating throughshared - a "shared" object that houses all the existing data from the local repositories, including the latest BCD data and the latest test result "support matrix".debug - a "debug" object, which builds a stack trace of any changes to the entry data.allStatements - the set of existing support statements in the BCD entrydefaultStatements - a filtered set of existing "default" support statements in the BCD entry (statements without flags or browser prefixes)inferredStatements - the set of support statements inferred from the test result "support matrix"statements - the final set of support statements that represent the latest changes (if any) to the BCD entryAt a high level, this state object is built in three phases of operations per iteration:
provideShared.provide...Statements functions.persist... functions.Here's a step-by-step explainer with JSON snapshots of this state object to show how it's built as we move through the chain of operations in the update method. Refer to this diagram for a visual representation of this data flow.
flowchart TD
entries((BCD Entries))
entries --> pathFilters
subgraph one [" "]
pathFilters{Match Optional Path Filters} -- Yes -->
provideSharedBrowserMap[[provideShared BrowserMap]] -->
hasBrowserMap{Has Browser Map?} -- Yes -->
provideSharedSupport[["provideShared #quot;Support#quot;"]] -->
provideSharedUnmodifiedSupport[["provideShared #quot;unmodifiedSupport#quot;"]]
end
provideSharedUnmodifiedSupport --> iterate
pathFilters & hasBrowserMap -- No --> entries
subgraph two [" "]
iterate((Iterate Browsers per BCD Entry)) -->
browserFilters{Match Optional Browser Filters} -- Yes -->
provideAllStatements[[provide All Statements]] -->
provideDefaultStatements[[provide Default Statements]] -->
detectDiffs{Detect Diffs in Support Matrix vs. BCD} -- Yes -->
provideInferredStatements[[provide Inferred Statements]] -->
tooManyInferred{Too Many Inferred Statements?} -- No -->
releaseFilters{Match Optional Release Filters} -- Yes -->
defaultOrInferred{Has Default or Inferred Statements?}
end
defaultOrInferred -- Yes --> persistNonDefault
browserFilters & detectDiffs & releaseFilters & defaultOrInferred -- No --> entries
tooManyInferred -- Yes --> entries
subgraph three [" "]
persistNonDefault[[persist Non-Default Statements]] -->
tooManyDefault{Too Many Default Statements?} -- No -->
hasVersionRemoved{Has #quot;Version Removed#quot; Statement?} -- No -->
inferredStatementsOutdated{Inferred Statements Outdated?} -- No -->
persistInferredRange[[persist Inferred Range]] -->
persistAddedOverPartial[[persist Added Over Partial]] -->
persistAdded[[persist Added]] -->
persistRemoved[[persist Removed]] -->
clearNonExact[[clearNonExact]] -->
stillHasStatements{Still has statements?}
end
stillHasStatements -- Yes --> saveChanges[[Save Changes]] --> entries
stillHasStatements -- No --> entries
tooManyDefault & hasVersionRemoved & inferredStatementsOutdated -- Yes --> entriesStart iterating through entries in the BCD data and begin populating the state object.
{
path: "api.AbortController",
debug: {
stack: [
{
step: "entry",
result: {
path: "api.AbortController",
},
},
],
},
shared: {
bcd: {
api: { ... },
},
entry: { ...api.AbortContoller entry data from bcd... },
},
}We exit early if the BCD Identifier path doesn’t match an optional path filter flag.
Builds the browser “Support Matrix” data from the local test results filtered by the path key.
{
path: "api.AbortController",
debug: {
stack: [
{
step: "entry",
result: {
path: "api.AbortController",
},
},
{
step: "provide_shared_browserMap",
result: {},
},
],
},
shared: {
bcd: { ... },
entry: { ... },
browserMap: Map(2) {
chrome => Map(4),
safari => Map(3)
}
},
}Get browser support data from BCD entry __compat data.
NOTE: This
supportkey in thesharedobject is mutated at the end of the iteration if we’ve determined that there should be updates.
{
path: "api.AbortController",
debug: { ... },
shared: {
bcd: { ... },
entry: { ... },
browserMap: Map(2),
support: {
chrome: {
version_added: "80",
},
safari: {
version_added: null,
},
},
},
}
Clones original support data. This key remains unmodified at the end of update.
{
path: "api.AbortController",
debug: { ... },
shared: {
bcd: { ... },
entry: { ... },
browserMap: Map(2),
support: { ... },
unmodifiedSupport: {
chrome: {
version_added: "80",
},
safari: {
version_added: null,
},
},
},
}Start iterating through browsers in BrowserMap Support Matrix test results per BCD entry. Gets versionMap support data from the BrowserMap by browser key.
{
path: "api.AbortController",
debug: { ... },
shared: {
bcd: { ... },
entry: { ... },
browserMap: Map(2),
support: { ... },
unmodifiedSupport: { ... },
versionMap: Map(4) {
"82" => null,
"83" => true,
"84" => true,
"85" => true
}
},
browser: "chrome",
}Exit early here if browser doesn’t match the optional browser filter flag.
Gets all existing support statements from BCD.
{
path: "api.AbortController",
debug: { ... },
shared: { ...},
browser: "chrome",
allStatements: [
{
version_added: "80",
},
],
}Gets existing un-flagged and un-prefixed statements from BCD. Exit if no default statements found. We also run an initial comparison of the test results from the versionMap against the defaultStatements to determine if there are possible updates. If not, exit early.
{
path: "api.AbortController",
debug: { ... },
shared: { ... },
browser: "chrome",
allStatements: [...],
defaultStatements: [
{
version_added: "80",
},
]
}Infer support statements from local test results. Exits if more than 1 statement inferred or if version_added doesn’t match any optional release filters.
{
path: "api.AbortController",
debug: { ... },
shared: { ... },
browser: "chrome",
allStatements: [...],
defaulStatements: [...],
inferredStatements: [
{
version_added: "≤83",
},
],
}
Updates statements key with inferred statements (& existing un-flagged statements) when no default statements exist.
{
path: "api.AudioContext.close",
debug: {
stack: [
...,
{
step: "provide_statements_nonDefault",
result: {
statements: [
{
version_added: "85",
},
],
reason: {
step: "provide_statements_nonDefault",
message: "api.AudioContext.close applied for chrome because there is no default statement",
skip: true,
},
},
}
]
},
shared: { ... },
browser: "chrome",
allStatements: [],
defaultStatements: [],
inferredStatements: [
{
version_added: "85",
},
],
statements: [
{
version_added: "85",
},
]
}NOTE: Data that gets written into the
statementskey is ultimately used to mutate the original BCD data in thesharedobject when we exit the full entry loop, resulting in writes back to the BCD files.
Exits early if:
version_removed data, orPersist inferred version range when BCD version was set to preview or when inferred range supersedes original data.
{
path: "api.AbortController.abort",
debug: { ... },
shared: { ... },
browser: "chrome",
allStatements: [ ... ],
defaultStatements: [
{
version_added: "85",
},
],
inferredStatements: [
{
version_added: "≤84",
},
],
statements: [
{
version_added: "≤84",
},
],
}Sets statements support data to false if no inferred added data and only "partial implementation" in BCD data.
{
path: "api.FakeInterface",
debug: { ... },
shared: { ... },
browser: "chrome",
allStatements: [ ... ],
defaultStatements: [
{
version_added: "85",
partial_implementation: true,
impl_url: "http://zombo.com",
notes: "This only works on Wednesdays",
}
],
inferredStatements: [
{
version_added: false,
},
],
statements: [
{
version_added: false,
},
],
}Updates statements key with inferred version_added data under following conditions:
partial_implementation{
path: "api.AbortController",
debug: { ... },
shared: {
...,
unmodifiedSupport: {
...,
safari: {
version_added: null,
}
},
},
browser: "safari",
allStatements: [ ... ],
defaultStatements: [ ... ],
inferredStatements: [
{
version_added: "≤13.1",
},
],
statements: [
{
version_added: "≤13.1",
},
],
}Adds version_removed data to statements and optionally updates existing version_removed data if the inferred version_removed data is a string.
{
path: "api.DeprecatedInterface",
debug: { ... },
shared: {
...,
unmodifiedSupport: {
...,
chrome: {
version_added: null,
}
},
},
browser: "chrome",
allStatements: [ ... ],
defaultStatements: [ ... ],
inferredStatements: [
{
version_added: "≤83",
version_removed: "85",
}
],
statements: [
{
version_added: "≤83",
version_removed: "85",
},
],
}Overwrites and clears any statements with ranged data if optional exactOnly flag is set.
There is one final skip check after this step . If there are still statements at this point, then we include them in the final set of changes for BCD. We also run another comparison of the versionMap data from the test results against the updated defaultStatements to see if there are still possible updates that could have been made. If there are, then we log those with a warning that possible manual intervention may be required on the BCD entry.