Skip to content

Commit

Permalink
Merge 74f5144 into 6c196a3
Browse files Browse the repository at this point in the history
  • Loading branch information
weareoutman authored Nov 27, 2024
2 parents 6c196a3 + 74f5144 commit b35c186
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 43 deletions.
5 changes: 4 additions & 1 deletion etc/brick-kit.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ import { StoryboardFunction } from '@next-core/brick-types';
import { StoryConf } from '@next-core/brick-types';
import { Subtract } from 'react-i18next';
import { TemplatePackage } from '@next-core/brick-types';
import { TransformedFunction } from '@next-core/brick-types';
import { UseBrickConf } from '@next-core/brick-types';
import { UseProviderResolveConf } from '@next-core/brick-types';
import { UserInfo } from '@next-core/brick-types';
Expand Down Expand Up @@ -587,6 +588,8 @@ export interface RuntimeStoryboardFunction {
// (undocumented)
source: string;
// (undocumented)
transformed?: TransformedFunction;
// (undocumented)
typescript?: boolean;
}

Expand All @@ -602,7 +605,7 @@ export function SingleBrickAsComponentFactory(React: typeof _React): React_2.Mem
// Warning: (ae-internal-missing-underscore) The name "StoryboardFunctionPatch" should be prefixed with an underscore because the declaration is marked as @internal
//
// @internal (undocumented)
export type StoryboardFunctionPatch = Pick<StoryboardFunction, "source" | "typescript">;
export type StoryboardFunctionPatch = Pick<StoryboardFunction, "source" | "typescript" | "transformed">;

// Warning: (ae-internal-missing-underscore) The name "StoryboardFunctionRegistry" should be prefixed with an underscore because the declaration is marked as @internal
//
Expand Down
7 changes: 7 additions & 0 deletions etc/brick-types.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -2296,6 +2296,7 @@ export interface StoryboardContextItemFreeVariable {
export interface StoryboardFunction {
name: string;
source: string;
transformed?: TransformedFunction;
typescript?: boolean;
}

Expand Down Expand Up @@ -2554,6 +2555,12 @@ export interface TemplatePackage {
// @internal (undocumented)
export type TemplateRegistry<T> = Map<string, T>;

// @public
export interface TransformedFunction {
globals: string[];
source: string;
}

// @public
export interface TransformItem {
// (undocumented)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,24 @@ describe("StoryboardFunctions", () => {
}
`,
},
{
name: "nativeMode",
source: `
function nativeMode() {
"native mode";
return ["from", "normal", "mode"];
}
`,
transformed: {
globals: ["_", "document"],
source: `
function nativeMode() {
"native mode";
return document ?? _.split("from native mode", " ");
}
`,
},
},
],
{
id: "my-app",
Expand Down Expand Up @@ -118,6 +136,7 @@ describe("StoryboardFunctions", () => {
expect(fn.checkPermissions("my:action-b")).toBe(false);
expect(fn.getUniqueId()).not.toBe("42");
expect(fn.getUniqueId("test-")).not.toBe("test-42");
expect(fn.nativeMode()).toEqual(["from", "native", "mode"]);

expect(window.STORYBOARD_FUNCTIONS_PERF?.length).toBe(6);
});
Expand Down
115 changes: 75 additions & 40 deletions packages/brick-kit/src/core/StoryboardFunctionRegistryFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,14 @@ import {
MicroApp,
SimpleFunction,
StoryboardFunction,
type TransformedFunction,
} from "@next-core/brick-types";
import { cook, precookFunction, EstreeNode } from "@next-core/brick-utils";
import {
cook,
precookFunction,
EstreeNode,
hasOwnProperty,
} from "@next-core/brick-utils";
import { supply } from "@next-core/supply";
import { getGeneralGlobals } from "../internal/getGeneralGlobals";

Expand All @@ -15,7 +21,7 @@ export type ReadonlyStoryboardFunctions = Readonly<
/** @internal */
export type StoryboardFunctionPatch = Pick<
StoryboardFunction,
"source" | "typescript"
"source" | "typescript" | "transformed"
>;

/** @internal */
Expand All @@ -39,6 +45,7 @@ export interface RuntimeStoryboardFunction {
typescript?: boolean;
processed?: boolean;
cooked?: SimpleFunction;
transformed?: TransformedFunction;
}

/** @internal */
Expand Down Expand Up @@ -105,11 +112,29 @@ export function StoryboardFunctionRegistryFactory({
registeredFunctions.set(fn.name, {
source: fn.source,
typescript: fn.typescript,
transformed: fn.transformed,
});
}
}
}

function getGlobalVariables(
globals: Set<string> | string[]
): Record<string, unknown> {
return supply(
globals,
getGeneralGlobals(globals, {
collectCoverage,
widgetId,
widgetVersion,
app: currentApp,
storyboardFunctions,
isStoryboardFunction: true,
}),
!!collectCoverage
);
}

function getStoryboardFunction(name: string): SimpleFunction {
const fn = registeredFunctions.get(name);
if (!fn) {
Expand All @@ -122,44 +147,53 @@ export function StoryboardFunctionRegistryFactory({
if (collectCoverage) {
collector = collectCoverage.createCollector(name);
}
const precooked = precookFunction(fn.source, {
cacheKey: fn,
typescript: fn.typescript,
hooks: collector && {
beforeVisit: collector.beforeVisit,
},
});
fn.cooked = cook(precooked.function, fn.source, {
rules: {
noVar: true,
},
globalVariables: supply(
precooked.attemptToVisitGlobals,
getGeneralGlobals(precooked.attemptToVisitGlobals, {
collectCoverage,
widgetId,
widgetVersion,
app: currentApp,
storyboardFunctions,
isStoryboardFunction: true,
}),
!!collectCoverage
),
hooks: {
perfCall: needPerf
? (duration) => {
perf(name, fn.source, duration);
}
: undefined,
...(collector
? {
beforeEvaluate: collector.beforeEvaluate,
beforeCall: collector.beforeCall,
beforeBranch: collector.beforeBranch,
}
: null),
},
}) as SimpleFunction;

// Do not use transformed functions when collecting coverage.
const transformed = !collector && fn.transformed;
if (transformed) {
const globalVariables = getGlobalVariables(transformed.globals);
// Spread globals as params to prevent accessing forbidden globals.
// NOTE: in native mode, forbidden globals are declared as `undefined`,
// thus accessing them will not throw a ReferenceError.
fn.cooked = new Function(
...transformed.globals,
`"use strict";return (${transformed.source})`
)(
...transformed.globals.map((key) =>
hasOwnProperty(globalVariables, key)
? globalVariables[key]
: undefined
)
);
} else {
const precooked = precookFunction(fn.source, {
cacheKey: fn,
typescript: fn.typescript,
hooks: collector && {
beforeVisit: collector.beforeVisit,
},
});
fn.cooked = cook(precooked.function, fn.source, {
rules: {
noVar: true,
},
globalVariables: getGlobalVariables(precooked.attemptToVisitGlobals),
hooks: {
perfCall: needPerf
? (duration) => {
perf(name, fn.source, duration);
}
: undefined,
...(collector
? {
beforeEvaluate: collector.beforeEvaluate,
beforeCall: collector.beforeCall,
beforeBranch: collector.beforeBranch,
}
: null),
},
}) as SimpleFunction;
}
fn.processed = true;
return fn.cooked;
}
Expand All @@ -174,6 +208,7 @@ export function StoryboardFunctionRegistryFactory({
registeredFunctions.set(name, {
source: data.source,
typescript: data.typescript,
transformed: data.transformed,
});
},
};
Expand Down
2 changes: 1 addition & 1 deletion packages/brick-kit/src/internal/getGeneralGlobals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export type PartialMicroApp = Pick<
// `GeneralGlobals` are globals which are page-state-agnostic,
// thus they can be used both in storyboard expressions and functions.
export function getGeneralGlobals(
attemptToVisitGlobals: Set<string>,
attemptToVisitGlobals: Set<string> | string[],
options: GeneralGlobalsOptions
): Record<string, unknown> {
const globalVariables: Record<string, unknown> = {};
Expand Down
16 changes: 16 additions & 0 deletions packages/brick-types/src/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1683,6 +1683,22 @@ export interface StoryboardFunction {
source: string;
/** 是否使用 TypeScript。 */
typescript?: boolean;
/**
* [Compiled] 编译后的函数
*
* 在函数内先声明 "native mode",打包时会将函数转换为原生 JS 代码,以提高执行性能。
*/
transformed?: TransformedFunction;
}

/**
* 编译后的函数
*/
export interface TransformedFunction {
/** 编译后的 JS 代码 */
source: string;
/** 要访问的全局对象列表 */
globals: string[];
}

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/supply/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { SimpleFunction } from "@next-core/brick-types";
import { pipes } from "@next-core/pipes";

export function supply(
attemptToVisitGlobals: Set<string>,
attemptToVisitGlobals: Set<string> | string[],
providedGlobalVariables?: Record<string, unknown>,
mock?: boolean
): Record<string, unknown> {
Expand Down

0 comments on commit b35c186

Please sign in to comment.