Skip to content

Commit

Permalink
Improve plugins typings (Part 3) (#14642)
Browse files Browse the repository at this point in the history
* external-helpers

* bugfix

* transform-parameters

* object-rest-spread

* destructuring-private

* transform-destructuring

* proposal-decorators

* optional-chaining

* helper-wrap-function

* explode-assignable-expression

* helper-compilation-targets

* helper-plugin-utils

* helpers

* helper-validator-option

* fix: allow "+" and "-" in MappedType .readonly/.optional

* fixture-test-runner

* remove charcodes from dependencies

the charcodes transform will inline charCodes.* so we can remove it from dependencies
  • Loading branch information
JLHwung committed Jun 21, 2022
1 parent 3d51b2a commit 18291cb
Show file tree
Hide file tree
Showing 32 changed files with 478 additions and 290 deletions.
2 changes: 1 addition & 1 deletion packages/babel-helper-compilation-targets/src/debug.ts
Expand Up @@ -34,5 +34,5 @@ export function getInclusionReasons(
}

return result;
}, {});
}, {} as Partial<Record<Target, string>>);
}
107 changes: 60 additions & 47 deletions packages/babel-helper-compilation-targets/src/index.ts
Expand Up @@ -12,7 +12,14 @@ import {
import { OptionValidator } from "@babel/helper-validator-option";
import { browserNameMap } from "./targets";
import { TargetNames } from "./options";
import type { Targets, InputTargets, Browsers, TargetsTuple } from "./types";
import type {
Target,
Targets,
InputTargets,
Browsers,
BrowserslistBrowserName,
TargetsTuple,
} from "./types";

export type { Targets, InputTargets };

Expand Down Expand Up @@ -58,50 +65,49 @@ function validateBrowsers(browsers: Browsers | undefined) {
}

function getLowestVersions(browsers: Array<string>): Targets {
return browsers.reduce((all: any, browser: string): any => {
const [browserName, browserVersion] = browser.split(" ");
const normalizedBrowserName = browserNameMap[browserName];

if (!normalizedBrowserName) {
return browsers.reduce((all, browser) => {
const [browserName, browserVersion] = browser.split(" ") as [
BrowserslistBrowserName,
string,
];
const target: Target = browserNameMap[browserName];

if (!target) {
return all;
}

try {
// Browser version can return as "10.0-10.2"
const splitVersion = browserVersion.split("-")[0].toLowerCase();
const isSplitUnreleased = isUnreleasedVersion(splitVersion, browserName);
const isSplitUnreleased = isUnreleasedVersion(splitVersion, target);

if (!all[normalizedBrowserName]) {
all[normalizedBrowserName] = isSplitUnreleased
if (!all[target]) {
all[target] = isSplitUnreleased
? splitVersion
: semverify(splitVersion);
return all;
}

const version = all[normalizedBrowserName];
const isUnreleased = isUnreleasedVersion(version, browserName);
const version = all[target];
const isUnreleased = isUnreleasedVersion(version, target);

if (isUnreleased && isSplitUnreleased) {
all[normalizedBrowserName] = getLowestUnreleased(
version,
splitVersion,
browserName,
);
all[target] = getLowestUnreleased(version, splitVersion, target);
} else if (isUnreleased) {
all[normalizedBrowserName] = semverify(splitVersion);
all[target] = semverify(splitVersion);
} else if (!isUnreleased && !isSplitUnreleased) {
const parsedBrowserVersion = semverify(splitVersion);

all[normalizedBrowserName] = semverMin(version, parsedBrowserVersion);
all[target] = semverMin(version, parsedBrowserVersion);
}
} catch (e) {}

return all;
}, {});
}, {} as Record<Target, string>);
}

function outputDecimalWarning(
decimalTargets: Array<{ target: string; value: string }>,
decimalTargets: Array<{ target: string; value: number }>,
): void {
if (!decimalTargets.length) {
return;
Expand All @@ -117,7 +123,7 @@ getting parsed as 6.1, which can lead to unexpected behavior.
`);
}

function semverifyTarget(target, value) {
function semverifyTarget(target: keyof Targets, value: string) {
try {
return semverify(value);
} catch (error) {
Expand All @@ -129,23 +135,24 @@ function semverifyTarget(target, value) {
}
}

const targetParserMap = {
__default(target, value) {
const version = isUnreleasedVersion(value, target)
? value.toLowerCase()
: semverifyTarget(target, value);
return [target, version];
},

// Parse `node: true` and `node: "current"` to version
node(target, value) {
const parsed =
value === true || value === "current"
? process.versions.node
: semverifyTarget(target, value);
return [target, parsed];
},
};
// Parse `node: true` and `node: "current"` to version
function nodeTargetParser(value: true | string) {
const parsed =
value === true || value === "current"
? process.versions.node
: semverifyTarget("node", value);
return ["node" as const, parsed] as const;
}

function defaultTargetParser(
target: Exclude<Target, "node">,
value: string,
): readonly [Exclude<Target, "node">, string] {
const version = isUnreleasedVersion(value, target)
? value.toLowerCase()
: semverifyTarget(target, value);
return [target, version] as const;
}

function generateTargets(inputTargets: InputTargets): Targets {
const input = { ...inputTargets };
Expand Down Expand Up @@ -214,7 +221,10 @@ export default function getTargets(
// These values OVERRIDE the `browsers` field.
if (esmodules && (esmodules !== "intersect" || !browsers?.length)) {
browsers = Object.keys(ESM_SUPPORT)
.map(browser => `${browser} >= ${ESM_SUPPORT[browser]}`)
.map(
(browser: keyof typeof ESM_SUPPORT) =>
`${browser} >= ${ESM_SUPPORT[browser]}`,
)
.join(", ");
esmodules = false;
}
Expand All @@ -226,13 +236,16 @@ export default function getTargets(
const queryBrowsers = resolveTargets(browsers, options.browserslistEnv);

if (esmodules === "intersect") {
for (const browser of Object.keys(queryBrowsers)) {
for (const browser of Object.keys(queryBrowsers) as Target[]) {
const version = queryBrowsers[browser];
const esmSupportVersion =
// @ts-ignore ie is not in ESM_SUPPORT
ESM_SUPPORT[browser];

if (ESM_SUPPORT[browser]) {
if (esmSupportVersion) {
queryBrowsers[browser] = getHighestUnreleased(
version,
semverify(ESM_SUPPORT[browser]),
semverify(esmSupportVersion),
browser,
);
} else {
Expand All @@ -247,18 +260,18 @@ export default function getTargets(
// Parse remaining targets
const result: Targets = {} as Targets;
const decimalWarnings = [];
for (const target of Object.keys(targets).sort()) {
for (const target of Object.keys(targets).sort() as Target[]) {
const value = targets[target];

// Warn when specifying minor/patch as a decimal
if (typeof value === "number" && value % 1 !== 0) {
decimalWarnings.push({ target, value });
}

// Check if we have a target parser?
// $FlowIgnore - Flow doesn't like that some targetParserMap[target] might be missing
const parser = targetParserMap[target] ?? targetParserMap.__default;
const [parsedTarget, parsedValue] = parser(target, value);
const [parsedTarget, parsedValue] =
target === "node"
? nodeTargetParser(value)
: defaultTargetParser(target, value as string);

if (parsedValue) {
// Merge (lowest wins)
Expand Down
8 changes: 5 additions & 3 deletions packages/babel-helper-compilation-targets/src/pretty.ts
@@ -1,6 +1,6 @@
import semver from "semver";
import { unreleasedLabels } from "./targets";
import type { Targets } from "./types";
import type { Targets, Target } from "./types";

export function prettifyVersion(version: string) {
if (typeof version !== "string") {
Expand All @@ -23,10 +23,12 @@ export function prettifyVersion(version: string) {
}

export function prettifyTargets(targets: Targets): Targets {
return Object.keys(targets).reduce((results, target) => {
return Object.keys(targets).reduce((results, target: Target) => {
let value = targets[target];

const unreleasedLabel = unreleasedLabels[target];
const unreleasedLabel =
// @ts-expect-error undefined is strictly compared with string later
unreleasedLabels[target];
if (typeof value === "string" && unreleasedLabel !== value) {
value = prettifyVersion(value);
}
Expand Down
11 changes: 8 additions & 3 deletions packages/babel-helper-compilation-targets/src/targets.ts
@@ -1,8 +1,11 @@
export const unreleasedLabels = {
safari: "tp",
};
} as const;

export const browserNameMap = {
import type { Target } from "./types";

// Map from browserslist|@mdn/browser-compat-data browser names to @kangax/compat-table browser names
export const browserNameMap: Record<string, Target> = {
and_chr: "chrome",
and_ff: "firefox",
android: "android",
Expand All @@ -17,4 +20,6 @@ export const browserNameMap = {
opera: "opera",
safari: "safari",
samsung: "samsung",
};
} as const;

export type BrowserslistBrowserName = keyof typeof browserNameMap;
8 changes: 6 additions & 2 deletions packages/babel-helper-compilation-targets/src/types.ts
@@ -1,4 +1,4 @@
// Targets
// Targets, engine names defined in compat-tables
export type Target =
| "node"
| "chrome"
Expand All @@ -17,7 +17,9 @@ export type Targets = {
};

export type TargetsTuple = {
[target in Target]: string;
[target in Exclude<Target, "node">]: string;
} & {
node: string | true;
};

export type Browsers = string | ReadonlyArray<string>;
Expand All @@ -31,3 +33,5 @@ export type InputTargets = {
// remove `intersect`.
esmodules?: boolean | "intersect";
} & Targets;

export type { BrowserslistBrowserName } from "./targets";
25 changes: 16 additions & 9 deletions packages/babel-helper-compilation-targets/src/utils.ts
Expand Up @@ -38,28 +38,35 @@ export function semverify(version: number | string): string {

export function isUnreleasedVersion(
version: string | number,
env: string,
env: Target,
): boolean {
const unreleasedLabel = unreleasedLabels[env];
const unreleasedLabel =
// @ts-expect-error unreleasedLabel will be guarded later
unreleasedLabels[env];
return (
!!unreleasedLabel && unreleasedLabel === version.toString().toLowerCase()
);
}

export function getLowestUnreleased(a: string, b: string, env: string): string {
const unreleasedLabel = unreleasedLabels[env];
const hasUnreleased = [a, b].some(item => item === unreleasedLabel);
if (hasUnreleased) {
// @ts-expect-error todo(flow->ts): probably a bug - types of a hasUnreleased to not overlap
return a === hasUnreleased ? b : a || b;
export function getLowestUnreleased(a: string, b: string, env: Target): string {
const unreleasedLabel:
| typeof unreleasedLabels[keyof typeof unreleasedLabels]
| undefined =
// @ts-ignore unreleasedLabel is undefined when env is not safari
unreleasedLabels[env];
if (a === unreleasedLabel) {
return b;
}
if (b === unreleasedLabel) {
return a;
}
return semverMin(a, b);
}

export function getHighestUnreleased(
a: string,
b: string,
env: string,
env: Target,
): string {
return getLowestUnreleased(a, b, env) === a ? b : a;
}
Expand Down
Expand Up @@ -78,7 +78,7 @@ export default function (
scope: Scope,
allowedSingleIdent?: boolean,
): {
uid: t.Identifier | t.MemberExpression;
uid: t.Identifier | t.MemberExpression | t.Super;
ref: t.Identifier | t.MemberExpression;
} {
let obj;
Expand Down
2 changes: 1 addition & 1 deletion packages/babel-helper-fixtures/src/index.ts
Expand Up @@ -45,7 +45,7 @@ export interface Test {
validateLogs: boolean;
}

interface TaskOptions extends InputOptions {
export interface TaskOptions extends InputOptions {
BABEL_8_BREAKING?: boolean;
DO_NOT_SET_SOURCE_TYPE?: boolean;
externalHelpers?: boolean;
Expand Down

0 comments on commit 18291cb

Please sign in to comment.