Skip to content

Commit

Permalink
improve
Browse files Browse the repository at this point in the history
  • Loading branch information
liuxingbaoyu committed Apr 26, 2024
1 parent 6611fcf commit 1671e6a
Show file tree
Hide file tree
Showing 5 changed files with 266 additions and 33 deletions.
1 change: 1 addition & 0 deletions lib/globals.d.ts
Expand Up @@ -7,5 +7,6 @@ declare function REQUIRED_VERSION(version: string): string;
declare namespace NodeJS {
export interface ProcessEnv {
BABEL_8_BREAKING: string;
IS_PUBLISH: string;
}
}
104 changes: 86 additions & 18 deletions packages/babel-core/src/config/full.ts
Expand Up @@ -26,7 +26,10 @@ import {
checkNoUnwrappedItemOptionPairs,
} from "./validation/options.ts";
import type { PluginItem } from "./validation/options.ts";
import { validatePluginObject } from "./validation/plugins.ts";
import {
type PluginObject,
validatePluginObject,
} from "./validation/plugins.ts";
import { makePluginAPI, makePresetAPI } from "./helpers/config-api.ts";
import type { PluginAPI, PresetAPI } from "./helpers/config-api.ts";

Expand Down Expand Up @@ -83,34 +86,33 @@ function sortPlugins(plugins: Plugin[]) {
plugins[index++] = n;
}
}
return plugins;
}

const orderDataListMap: Map<
string,
{
version: number;
data: () => string[];
plugins: Plugin[];
}
> = new Map();
type OrderData = {
version: number;
getData: PluginObject["orderData"]["data"];
list?: string[];
before?: string;
plugins: Plugin[];
};
const orderDataMap: Map<string, OrderData> = new Map();

const pluginsWithPadding: (Plugin | string)[] = [];

for (let i = plugins.length - 1; i >= 0; i--) {
const plugin = plugins[i];
const { orderData } = plugin;
if (orderData) {
let orderData2 = orderDataListMap.get(orderData.id);
let orderData2 = orderDataMap.get(orderData.id);
if ((orderData2?.version || 0) < orderData.version) {
if (orderData2 == null) {
pluginsWithPadding.unshift(orderData.id);
}
orderDataListMap.set(
orderDataMap.set(
orderData.id,
(orderData2 = {
version: orderData.version,
data: () => orderData.data(),
getData: () => orderData.data(),
plugins: [],
}),
);
Expand All @@ -121,15 +123,75 @@ function sortPlugins(plugins: Plugin[]) {
}
}

orderDataMap.forEach(v => {
const data = v.getData();
v.list = data.list;
v.before = data.before;
stableSort(v.plugins, new Map(data.list.map((key, i) => [key, i])));
});

// Detect cycles
const seen = new Set<string>();

function visit(k: string) {
const v = orderDataMap.get(k);
if (!v) return;

if (seen.has(k)) {
throw new Error(
`Plugin/preset order list cycles with '${Array.from(seen.keys()).join(" -> ")}'`,
);
}
seen.add(k);

if (v.before) {
visit(v.before);
}
}

for (const k of orderDataMap.keys()) {
visit(k);
seen.clear();
}

// Sort

// eslint-disable-next-line no-constant-condition
while (true) {
let changed = false;

for (let i = 0; i < pluginsWithPadding.length; i++) {
const plugin = pluginsWithPadding[i];
if (typeof plugin !== "string" || !orderDataMap.has(plugin)) {
continue;
}

const orderData = orderDataMap.get(plugin);
const { before } = orderData;
if (!before) {
continue;
}
const index = pluginsWithPadding.indexOf(before);
if (index === -1) {
continue;
}
if (index < i) {
changed = true;
pluginsWithPadding.splice(i, 1);
pluginsWithPadding.splice(index, 0, plugin);
}
}

if (!changed) {
break;
}
}

const newPlugins: Plugin[] = [];

for (const value of pluginsWithPadding) {
if (typeof value === "string") {
const orderData = orderDataListMap.get(value);
const map = new Map<string, number>(
orderData.data().map((key, i) => [key, i]),
);
newPlugins.push(...stableSort(orderData.plugins, map));
newPlugins.push(...orderDataMap.get(value).plugins);
} else {
newPlugins.push(value);
}
Expand Down Expand Up @@ -297,6 +359,12 @@ export default gensync(function* loadFullConfig(
opts.plugins = passes[0] = sortPlugins(opts.plugins as Plugin[]);
}

if (!process.env.IS_PUBLISH && process.env.TEST_THROW_PLUGINS) {
throw Object.assign(new Error("TEST_THROW_PLUGINS"), {
plugins: opts.plugins,
});
}

return {
options: opts,
passes: passes,
Expand Down
8 changes: 5 additions & 3 deletions packages/babel-core/src/config/validation/plugins.ts
Expand Up @@ -117,9 +117,11 @@ export type PluginObject<S extends PluginPass = PluginPass> = {
orderData?: {
id: string;
version: number;
} & {
type: "list";
data: () => string[];
data: () => {
type: "list";
before?: string;
list: string[];
};
};
};

Expand Down
131 changes: 131 additions & 0 deletions packages/babel-core/test/plugin-order.js
@@ -0,0 +1,131 @@
import * as babel from "../lib/index.js";
import { describeBabel8, commonJS } from "$repo-utils";

const { __dirname } = commonJS(import.meta.url);
const cwd = __dirname;

function getTransformingPluginList(code, opts) {
process.env.TEST_THROW_PLUGINS = "true";
try {
return babel.transformSync(code, {
cwd,
configFile: false,
babelrc: false,
...opts,
});
} catch (error) {
if (error.message === "TEST_THROW_PLUGINS") {
return error.plugins.map(plugin => plugin.key);
}
throw error;
} finally {
delete process.env.TEST_THROW_PLUGINS;
}
}

let orderDataId = 0;
function buildOrderData(list, before, id) {
return {
id: id || orderDataId++ + "",
version: 1,
data: () => {
return {
type: "list",
before,
list,
};
},
};
}

describeBabel8("sortPlugins", function () {
it("should sort plugins 1", function () {
const orderData = buildOrderData(["plugin1", "plugin2", "plugin3"]);
const plugins = [
{ name: "plugin3", orderData },
{ name: "plugin1", orderData },
{ name: "plugin2", orderData },
];
const result = getTransformingPluginList("", {
plugins,
});
expect(result).toMatchInlineSnapshot(`
Array [
"plugin1",
"plugin2",
"plugin3",
]
`);
});

it("should sort plugins 2", function () {
const orderData = buildOrderData(["plugin3", "plugin4"]);
const plugins = [
{ name: "plugin1" },
{ name: "plugin4", orderData },
{ name: "plugin2" },
{ name: "plugin3", orderData },
{ name: "plugin5" },
];
const result = getTransformingPluginList("", {
plugins,
});
expect(result).toMatchInlineSnapshot(`
Array [
"plugin1",
"plugin2",
"plugin3",
"plugin4",
"plugin5",
]
`);
});

it("should sort plugins with before", function () {
const orderData = buildOrderData(["plugin1", "plugin2"], "b", "a");
const orderData2 = buildOrderData(["plugin3", "plugin4"], undefined, "b");
const plugins = [
{ name: "pluginA" },
{ name: "plugin4", orderData: orderData2 },
{ name: "plugin3", orderData: orderData2 },
{ name: "pluginB" },
{ name: "plugin1", orderData },
{ name: "plugin2", orderData },
{ name: "pluginC" },
];
const result = getTransformingPluginList("", {
plugins,
});
expect(result).toMatchInlineSnapshot(`
Array [
"pluginA",
"plugin1",
"plugin2",
"plugin3",
"plugin4",
"pluginB",
"pluginC",
]
`);
});

it("should throw when cycles", function () {
const orderData = buildOrderData(["plugin1", "plugin2"], "b", "a");
const orderData2 = buildOrderData(["plugin3", "plugin4"], "c", "b");
const orderData3 = buildOrderData(["plugin5", "plugin6"], "a", "c");

const plugins = [
{ name: "plugin1", orderData: orderData },
{ name: "plugin2", orderData: orderData },
{ name: "plugin3", orderData: orderData2 },
{ name: "plugin4", orderData: orderData2 },
{ name: "plugin5", orderData: orderData3 },
{ name: "plugin6", orderData: orderData3 },
];
expect(() => {
getTransformingPluginList("", {
plugins,
});
}).toThrow("Plugin/preset order list cycles with 'c -> a -> b'");
});
});
55 changes: 43 additions & 12 deletions packages/babel-helper-plugin-utils/src/index.ts
Expand Up @@ -64,15 +64,35 @@ export function declare<State = {}, Option = {}>(
dirname,
);

const orderList = [
function definePluginOrderData(
pluginObject: PluginObject,
id: string,
list: string[],
before?: string,
) {
if (list.includes(pluginObject.name)) {
pluginObject.orderData = {
id: id,
version: 1,
data: () => ({
type: "list",
list,
before,
}),
};
}
}

const modulePlugins = [
"transform-modules-commonjs",
"transform-modules-amd",
"transform-modules-systemjs",
"transform-modules-umd",
];

"transform-typescript",
"transform-flow",
const languagePlugins = ["transform-typescript", "transform-flow"];

const featurePlugins = [
"proposal-async-do-expressions",
"proposal-decorators",
"proposal-destructuring-private",
Expand Down Expand Up @@ -148,15 +168,26 @@ export function declare<State = {}, Option = {}>(
"bugfix/transform-v8-spread-parameters-in-optional-chaining",
];

if (process.env.BABEL_8_BREAKING && orderList.includes(pluginObject.name)) {
pluginObject.orderData = {
id: "@babel/helper-plugin-order-data",
version: 1,
type: "list",
data: () => {
return orderList;
},
};
if (process.env.BABEL_8_BREAKING) {
definePluginOrderData(
pluginObject,
"@babel/helper-plugin-order-data#module",
modulePlugins,
"@babel/helper-plugin-order-data#language",
);

definePluginOrderData(
pluginObject,
"@babel/helper-plugin-order-data#language",
languagePlugins,
"@babel/helper-plugin-order-data#feature",
);

definePluginOrderData(
pluginObject,
"@babel/helper-plugin-order-data#feature",
featurePlugins,
);
}

return pluginObject;
Expand Down

0 comments on commit 1671e6a

Please sign in to comment.