blob: 725948f9b456e346609021e82c52960599f28dc1 [file] [log] [blame] [edit]
/* This file is a part of @mdn/browser-compat-data
* See LICENSE file for more information. */
import { compareVersions } from 'compare-versions';
import {
CompatStatement,
SimpleSupportStatement,
StatusBlock,
BrowserStatement,
ReleaseStatement,
} from '../../types/types.js';
const propOrder = {
browsers: {
browser: [
'name',
'type',
'upstream',
'preview_name',
'pref_url',
'accepts_flags',
'accepts_webextensions',
'releases',
],
release: [
'release_date',
'release_notes',
'status',
'engine',
'engine_version',
],
},
data: {
__compat: [
'description',
'mdn_url',
'spec_url',
'tags',
'support',
'status',
],
support: [
'alternative_name',
'prefix',
'version_added',
'version_removed',
'flags',
'impl_url',
'partial_implementation',
'notes',
],
status: ['experimental', 'standard_track', 'deprecated'],
},
};
/**
* Perform property ordering
* @param value The object to order properties for
* @param order The order to follow
* @returns The ordered object
*/
const doOrder = <T>(value: T, order: string[]): T => {
if (value && typeof value === 'object') {
return order.reduce((result: Record<string, any>, key: string) => {
if (key in value) {
result[key] = value[key];
}
return result;
}, {}) as T;
}
return value;
};
/**
* Return a stringified version of the releases list in a browser file, with a
* prefix and suffix added, which will be removed after performing JSON
* stringification. This is important because JavaScript wants to move object
* entries with a floating point as the key to the very end of the list.
* @param releases The release data
* @returns The stringified releases
*/
export const stringifyReleases = (
releases: Record<string, ReleaseStatement>,
): string => {
const indentStep = ' '; // Constant with the indent step that sortStringify will use
const sortedKeys = Object.keys(releases).sort(compareVersions);
let result = '';
for (let i = 0; i < sortedKeys.length; i++) {
const k = sortedKeys[i];
const v = JSON.stringify(releases[k], null, 2).replace(/\n/g, '\n ');
// Add it to the result
result += `${indentStep}"${k}": ${v}`;
// Check if this is the last entry or not: if not, add a comma
if (i != sortedKeys.length - 1) {
result += ',';
}
// We always need a carriage return
result += '\n';
}
return `*#*#{\n${result}}#*#*`; // Close the brace and return the string
};
/**
* Return a new feature object whose first-level properties have been
* ordered according to doOrder, and so will be stringified in that
* order as well. This relies on guaranteed "own" property ordering,
* which is insertion order for non-integer keys (which is our case).
* @param key The key in the object
* @param value The value of the key
* @returns The new value
*/
export const orderProperties = (key: string, value: any): any => {
if (value instanceof Object) {
// Order properties for data
if ('__compat' in value) {
value.__compat = doOrder(
value.__compat as CompatStatement,
propOrder.data.__compat,
);
for (const browser of Object.keys(value.__compat.support)) {
const result: SimpleSupportStatement[] = [];
let data = value.__compat.support[browser];
if (!Array.isArray(data)) {
data = [data];
}
for (const statement of data) {
result.push(
doOrder(
statement as SimpleSupportStatement,
propOrder.data.support,
),
);
}
value.__compat.support[browser] =
result.length === 1 ? result[0] : result;
}
if ('status' in value.__compat) {
value.__compat.status = doOrder(
value.__compat.status as StatusBlock,
propOrder.data.status,
);
}
}
// Order properties for browsers
if ('browsers' in value) {
const browser = Object.keys(value.browsers)[0];
value.browsers[browser] = doOrder(
value.browsers[browser] as BrowserStatement,
propOrder.browsers.browser,
);
for (const r of Object.keys(value.browsers[browser].releases)) {
value.browsers[browser].releases[r] = doOrder(
value.browsers[browser].releases[r] as ReleaseStatement,
propOrder.browsers.release,
);
}
value.browsers[browser].releases = stringifyReleases(
value.browsers[browser].releases,
);
}
}
return value;
};
/**
* Stringify an object in a specific order of properties
* @param rawdata The object to stringify
* @returns The stringified object
*/
const stringifyAndOrderProperties = (rawdata: any): string => {
if (rawdata instanceof Object) {
rawdata = JSON.stringify(rawdata);
}
const data = JSON.parse(rawdata, orderProperties);
if ('browsers' in data) {
// Browser data needs to be stringified in a special way due to the release data
return JSON.stringify(data, null, 2)
.replace('"*#*#', '')
.replace('#*#*"', '')
.replace(/\\n/g, '\n ')
.replace(/\\"/g, '"');
}
return JSON.stringify(data, null, 2);
};
export default stringifyAndOrderProperties;