Website Structure

This commit is contained in:
supalerk-ar66 2026-01-13 10:46:40 +07:00
parent 62812f2090
commit 71f0676a62
22365 changed files with 4265753 additions and 791 deletions

139
Frontend-Learner/node_modules/unctx/dist/index.cjs generated vendored Normal file
View file

@ -0,0 +1,139 @@
'use strict';
function createContext(opts = {}) {
let currentInstance;
let isSingleton = false;
const checkConflict = (instance) => {
if (currentInstance && currentInstance !== instance) {
throw new Error("Context conflict");
}
};
let als;
if (opts.asyncContext) {
const _AsyncLocalStorage = opts.AsyncLocalStorage || globalThis.AsyncLocalStorage;
if (_AsyncLocalStorage) {
als = new _AsyncLocalStorage();
} else {
console.warn("[unctx] `AsyncLocalStorage` is not provided.");
}
}
const _getCurrentInstance = () => {
if (als) {
const instance = als.getStore();
if (instance !== void 0) {
return instance;
}
}
return currentInstance;
};
return {
use: () => {
const _instance = _getCurrentInstance();
if (_instance === void 0) {
throw new Error("Context is not available");
}
return _instance;
},
tryUse: () => {
return _getCurrentInstance();
},
set: (instance, replace) => {
if (!replace) {
checkConflict(instance);
}
currentInstance = instance;
isSingleton = true;
},
unset: () => {
currentInstance = void 0;
isSingleton = false;
},
call: (instance, callback) => {
checkConflict(instance);
currentInstance = instance;
try {
return als ? als.run(instance, callback) : callback();
} finally {
if (!isSingleton) {
currentInstance = void 0;
}
}
},
async callAsync(instance, callback) {
currentInstance = instance;
const onRestore = () => {
currentInstance = instance;
};
const onLeave = () => currentInstance === instance ? onRestore : void 0;
asyncHandlers.add(onLeave);
try {
const r = als ? als.run(instance, callback) : callback();
if (!isSingleton) {
currentInstance = void 0;
}
return await r;
} finally {
asyncHandlers.delete(onLeave);
}
}
};
}
function createNamespace(defaultOpts = {}) {
const contexts = {};
return {
get(key, opts = {}) {
if (!contexts[key]) {
contexts[key] = createContext({ ...defaultOpts, ...opts });
}
return contexts[key];
}
};
}
const _globalThis = typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : typeof global !== "undefined" ? global : typeof window !== "undefined" ? window : {};
const globalKey = "__unctx__";
const defaultNamespace = _globalThis[globalKey] || (_globalThis[globalKey] = createNamespace());
const getContext = (key, opts = {}) => defaultNamespace.get(key, opts);
const useContext = (key, opts = {}) => getContext(key, opts).use;
const asyncHandlersKey = "__unctx_async_handlers__";
const asyncHandlers = _globalThis[asyncHandlersKey] || (_globalThis[asyncHandlersKey] = /* @__PURE__ */ new Set());
function executeAsync(function_) {
const restores = [];
for (const leaveHandler of asyncHandlers) {
const restore2 = leaveHandler();
if (restore2) {
restores.push(restore2);
}
}
const restore = () => {
for (const restore2 of restores) {
restore2();
}
};
let awaitable = function_();
if (awaitable && typeof awaitable === "object" && "catch" in awaitable) {
awaitable = awaitable.catch((error) => {
restore();
throw error;
});
}
return [awaitable, restore];
}
function withAsyncContext(function_, transformed) {
if (!transformed) {
console.warn(
"[unctx] `withAsyncContext` needs transformation for async context support in",
function_,
"\n",
function_.toString()
);
}
return function_;
}
exports.createContext = createContext;
exports.createNamespace = createNamespace;
exports.defaultNamespace = defaultNamespace;
exports.executeAsync = executeAsync;
exports.getContext = getContext;
exports.useContext = useContext;
exports.withAsyncContext = withAsyncContext;

49
Frontend-Learner/node_modules/unctx/dist/index.d.cts generated vendored Normal file
View file

@ -0,0 +1,49 @@
import { AsyncLocalStorage } from 'node:async_hooks';
interface UseContext<T> {
/**
* Get the current context. Throws if no context is set.
*/
use: () => T;
/**
* Get the current context. Returns `null` when no context is set.
*/
tryUse: () => T | null;
/**
* Set the context as Singleton Pattern.
*/
set: (instance?: T, replace?: boolean) => void;
/**
* Clear current context.
*/
unset: () => void;
/**
* Exclude a synchronous function with the provided context.
*/
call: <R>(instance: T, callback: () => R) => R;
/**
* Exclude an asynchronous function with the provided context.
* Requires installing the transform plugin to work properly.
*/
callAsync: <R>(instance: T, callback: () => R | Promise<R>) => Promise<R>;
}
interface ContextOptions {
asyncContext?: boolean;
AsyncLocalStorage?: typeof AsyncLocalStorage;
}
declare function createContext<T = any>(opts?: ContextOptions): UseContext<T>;
interface ContextNamespace {
get: <T>(key: string, opts?: ContextOptions) => UseContext<T>;
}
declare function createNamespace<T = any>(defaultOpts?: ContextOptions): {
get(key: string, opts?: ContextOptions): UseContext<T>;
};
declare const defaultNamespace: ContextNamespace;
declare const getContext: <T>(key: string, opts?: ContextOptions) => UseContext<T>;
declare const useContext: <T>(key: string, opts?: ContextOptions) => () => T;
type AsyncFunction<T> = () => Promise<T>;
declare function executeAsync<T>(function_: AsyncFunction<T>): [Promise<T>, () => void];
declare function withAsyncContext<T = any>(function_: AsyncFunction<T>, transformed?: boolean): AsyncFunction<T>;
export { createContext, createNamespace, defaultNamespace, executeAsync, getContext, useContext, withAsyncContext };
export type { ContextNamespace, ContextOptions, UseContext };

49
Frontend-Learner/node_modules/unctx/dist/index.d.mts generated vendored Normal file
View file

@ -0,0 +1,49 @@
import { AsyncLocalStorage } from 'node:async_hooks';
interface UseContext<T> {
/**
* Get the current context. Throws if no context is set.
*/
use: () => T;
/**
* Get the current context. Returns `null` when no context is set.
*/
tryUse: () => T | null;
/**
* Set the context as Singleton Pattern.
*/
set: (instance?: T, replace?: boolean) => void;
/**
* Clear current context.
*/
unset: () => void;
/**
* Exclude a synchronous function with the provided context.
*/
call: <R>(instance: T, callback: () => R) => R;
/**
* Exclude an asynchronous function with the provided context.
* Requires installing the transform plugin to work properly.
*/
callAsync: <R>(instance: T, callback: () => R | Promise<R>) => Promise<R>;
}
interface ContextOptions {
asyncContext?: boolean;
AsyncLocalStorage?: typeof AsyncLocalStorage;
}
declare function createContext<T = any>(opts?: ContextOptions): UseContext<T>;
interface ContextNamespace {
get: <T>(key: string, opts?: ContextOptions) => UseContext<T>;
}
declare function createNamespace<T = any>(defaultOpts?: ContextOptions): {
get(key: string, opts?: ContextOptions): UseContext<T>;
};
declare const defaultNamespace: ContextNamespace;
declare const getContext: <T>(key: string, opts?: ContextOptions) => UseContext<T>;
declare const useContext: <T>(key: string, opts?: ContextOptions) => () => T;
type AsyncFunction<T> = () => Promise<T>;
declare function executeAsync<T>(function_: AsyncFunction<T>): [Promise<T>, () => void];
declare function withAsyncContext<T = any>(function_: AsyncFunction<T>, transformed?: boolean): AsyncFunction<T>;
export { createContext, createNamespace, defaultNamespace, executeAsync, getContext, useContext, withAsyncContext };
export type { ContextNamespace, ContextOptions, UseContext };

49
Frontend-Learner/node_modules/unctx/dist/index.d.ts generated vendored Normal file
View file

@ -0,0 +1,49 @@
import { AsyncLocalStorage } from 'node:async_hooks';
interface UseContext<T> {
/**
* Get the current context. Throws if no context is set.
*/
use: () => T;
/**
* Get the current context. Returns `null` when no context is set.
*/
tryUse: () => T | null;
/**
* Set the context as Singleton Pattern.
*/
set: (instance?: T, replace?: boolean) => void;
/**
* Clear current context.
*/
unset: () => void;
/**
* Exclude a synchronous function with the provided context.
*/
call: <R>(instance: T, callback: () => R) => R;
/**
* Exclude an asynchronous function with the provided context.
* Requires installing the transform plugin to work properly.
*/
callAsync: <R>(instance: T, callback: () => R | Promise<R>) => Promise<R>;
}
interface ContextOptions {
asyncContext?: boolean;
AsyncLocalStorage?: typeof AsyncLocalStorage;
}
declare function createContext<T = any>(opts?: ContextOptions): UseContext<T>;
interface ContextNamespace {
get: <T>(key: string, opts?: ContextOptions) => UseContext<T>;
}
declare function createNamespace<T = any>(defaultOpts?: ContextOptions): {
get(key: string, opts?: ContextOptions): UseContext<T>;
};
declare const defaultNamespace: ContextNamespace;
declare const getContext: <T>(key: string, opts?: ContextOptions) => UseContext<T>;
declare const useContext: <T>(key: string, opts?: ContextOptions) => () => T;
type AsyncFunction<T> = () => Promise<T>;
declare function executeAsync<T>(function_: AsyncFunction<T>): [Promise<T>, () => void];
declare function withAsyncContext<T = any>(function_: AsyncFunction<T>, transformed?: boolean): AsyncFunction<T>;
export { createContext, createNamespace, defaultNamespace, executeAsync, getContext, useContext, withAsyncContext };
export type { ContextNamespace, ContextOptions, UseContext };

131
Frontend-Learner/node_modules/unctx/dist/index.mjs generated vendored Normal file
View file

@ -0,0 +1,131 @@
function createContext(opts = {}) {
let currentInstance;
let isSingleton = false;
const checkConflict = (instance) => {
if (currentInstance && currentInstance !== instance) {
throw new Error("Context conflict");
}
};
let als;
if (opts.asyncContext) {
const _AsyncLocalStorage = opts.AsyncLocalStorage || globalThis.AsyncLocalStorage;
if (_AsyncLocalStorage) {
als = new _AsyncLocalStorage();
} else {
console.warn("[unctx] `AsyncLocalStorage` is not provided.");
}
}
const _getCurrentInstance = () => {
if (als) {
const instance = als.getStore();
if (instance !== void 0) {
return instance;
}
}
return currentInstance;
};
return {
use: () => {
const _instance = _getCurrentInstance();
if (_instance === void 0) {
throw new Error("Context is not available");
}
return _instance;
},
tryUse: () => {
return _getCurrentInstance();
},
set: (instance, replace) => {
if (!replace) {
checkConflict(instance);
}
currentInstance = instance;
isSingleton = true;
},
unset: () => {
currentInstance = void 0;
isSingleton = false;
},
call: (instance, callback) => {
checkConflict(instance);
currentInstance = instance;
try {
return als ? als.run(instance, callback) : callback();
} finally {
if (!isSingleton) {
currentInstance = void 0;
}
}
},
async callAsync(instance, callback) {
currentInstance = instance;
const onRestore = () => {
currentInstance = instance;
};
const onLeave = () => currentInstance === instance ? onRestore : void 0;
asyncHandlers.add(onLeave);
try {
const r = als ? als.run(instance, callback) : callback();
if (!isSingleton) {
currentInstance = void 0;
}
return await r;
} finally {
asyncHandlers.delete(onLeave);
}
}
};
}
function createNamespace(defaultOpts = {}) {
const contexts = {};
return {
get(key, opts = {}) {
if (!contexts[key]) {
contexts[key] = createContext({ ...defaultOpts, ...opts });
}
return contexts[key];
}
};
}
const _globalThis = typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : typeof global !== "undefined" ? global : typeof window !== "undefined" ? window : {};
const globalKey = "__unctx__";
const defaultNamespace = _globalThis[globalKey] || (_globalThis[globalKey] = createNamespace());
const getContext = (key, opts = {}) => defaultNamespace.get(key, opts);
const useContext = (key, opts = {}) => getContext(key, opts).use;
const asyncHandlersKey = "__unctx_async_handlers__";
const asyncHandlers = _globalThis[asyncHandlersKey] || (_globalThis[asyncHandlersKey] = /* @__PURE__ */ new Set());
function executeAsync(function_) {
const restores = [];
for (const leaveHandler of asyncHandlers) {
const restore2 = leaveHandler();
if (restore2) {
restores.push(restore2);
}
}
const restore = () => {
for (const restore2 of restores) {
restore2();
}
};
let awaitable = function_();
if (awaitable && typeof awaitable === "object" && "catch" in awaitable) {
awaitable = awaitable.catch((error) => {
restore();
throw error;
});
}
return [awaitable, restore];
}
function withAsyncContext(function_, transformed) {
if (!transformed) {
console.warn(
"[unctx] `withAsyncContext` needs transformation for async context support in",
function_,
"\n",
function_.toString()
);
}
return function_;
}
export { createContext, createNamespace, defaultNamespace, executeAsync, getContext, useContext, withAsyncContext };

35
Frontend-Learner/node_modules/unctx/dist/plugin.cjs generated vendored Normal file
View file

@ -0,0 +1,35 @@
'use strict';
const unplugin = require('unplugin');
const transform = require('./transform.cjs');
require('acorn');
require('magic-string');
require('estree-walker');
const unctxPlugin = unplugin.createUnplugin(
(options = {}) => {
const transformer = transform.createTransformer(options);
return {
name: "unctx:transform",
enforce: "post",
transformInclude: options.transformInclude,
transform: {
filter: options.transformFilter ?? transformer.filter,
handler(code, id) {
const result = transformer.transform(code);
if (result) {
return {
code: result.code,
map: result.magicString.generateMap({
source: id,
includeContent: true
})
};
}
}
}
};
}
);
exports.unctxPlugin = unctxPlugin;

17
Frontend-Learner/node_modules/unctx/dist/plugin.d.cts generated vendored Normal file
View file

@ -0,0 +1,17 @@
import * as unplugin from 'unplugin';
import { HookFilter } from 'unplugin';
import { TransformerOptions } from './transform.cjs';
import 'magic-string';
interface UnctxPluginOptions extends TransformerOptions {
/** Plugin Hook Filter for the transform hook
* @see https://unplugin.unjs.io/guide/#filters
*/
transformFilter?: HookFilter;
/** Function to determine whether a file should be transformed. If possible, use `transformFilter` instead for better performance. */
transformInclude?: (id: string) => boolean;
}
declare const unctxPlugin: unplugin.UnpluginInstance<UnctxPluginOptions, boolean>;
export { unctxPlugin };
export type { UnctxPluginOptions };

17
Frontend-Learner/node_modules/unctx/dist/plugin.d.mts generated vendored Normal file
View file

@ -0,0 +1,17 @@
import * as unplugin from 'unplugin';
import { HookFilter } from 'unplugin';
import { TransformerOptions } from './transform.mjs';
import 'magic-string';
interface UnctxPluginOptions extends TransformerOptions {
/** Plugin Hook Filter for the transform hook
* @see https://unplugin.unjs.io/guide/#filters
*/
transformFilter?: HookFilter;
/** Function to determine whether a file should be transformed. If possible, use `transformFilter` instead for better performance. */
transformInclude?: (id: string) => boolean;
}
declare const unctxPlugin: unplugin.UnpluginInstance<UnctxPluginOptions, boolean>;
export { unctxPlugin };
export type { UnctxPluginOptions };

17
Frontend-Learner/node_modules/unctx/dist/plugin.d.ts generated vendored Normal file
View file

@ -0,0 +1,17 @@
import * as unplugin from 'unplugin';
import { HookFilter } from 'unplugin';
import { TransformerOptions } from './transform.js';
import 'magic-string';
interface UnctxPluginOptions extends TransformerOptions {
/** Plugin Hook Filter for the transform hook
* @see https://unplugin.unjs.io/guide/#filters
*/
transformFilter?: HookFilter;
/** Function to determine whether a file should be transformed. If possible, use `transformFilter` instead for better performance. */
transformInclude?: (id: string) => boolean;
}
declare const unctxPlugin: unplugin.UnpluginInstance<UnctxPluginOptions, boolean>;
export { unctxPlugin };
export type { UnctxPluginOptions };

33
Frontend-Learner/node_modules/unctx/dist/plugin.mjs generated vendored Normal file
View file

@ -0,0 +1,33 @@
import { createUnplugin } from 'unplugin';
import { createTransformer } from './transform.mjs';
import 'acorn';
import 'magic-string';
import 'estree-walker';
const unctxPlugin = createUnplugin(
(options = {}) => {
const transformer = createTransformer(options);
return {
name: "unctx:transform",
enforce: "post",
transformInclude: options.transformInclude,
transform: {
filter: options.transformFilter ?? transformer.filter,
handler(code, id) {
const result = transformer.transform(code);
if (result) {
return {
code: result.code,
map: result.magicString.generateMap({
source: id,
includeContent: true
})
};
}
}
}
};
}
);
export { unctxPlugin };

171
Frontend-Learner/node_modules/unctx/dist/transform.cjs generated vendored Normal file
View file

@ -0,0 +1,171 @@
'use strict';
const acorn = require('acorn');
const MagicString = require('magic-string');
const estreeWalker = require('estree-walker');
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
function _interopNamespaceCompat(e) {
if (e && typeof e === 'object' && 'default' in e) return e;
const n = Object.create(null);
if (e) {
for (const k in e) {
n[k] = e[k];
}
}
n.default = e;
return n;
}
const acorn__namespace = /*#__PURE__*/_interopNamespaceCompat(acorn);
const MagicString__default = /*#__PURE__*/_interopDefaultCompat(MagicString);
const kInjected = "__unctx_injected__";
function createTransformer(options = {}) {
options = {
asyncFunctions: ["withAsyncContext"],
helperModule: "unctx",
helperName: "executeAsync",
objectDefinitions: {},
...options
};
const objectDefinitionFunctions = Object.keys(options.objectDefinitions);
const matchRE = new RegExp(
`\\b(${[...options.asyncFunctions, ...objectDefinitionFunctions].join(
"|"
)})\\(`
);
function shouldTransform(code) {
return typeof code === "string" && matchRE.test(code);
}
const filter = {
code: matchRE
};
function transform(code, options_ = {}) {
if (!options_.force && !shouldTransform(code)) {
return;
}
const ast = acorn__namespace.parse(code, {
sourceType: "module",
ecmaVersion: "latest",
locations: true
});
const s = new MagicString__default(code);
const lines = code.split("\n");
let detected = false;
estreeWalker.walk(ast, {
enter(node) {
if (node.type === "CallExpression") {
const functionName = _getFunctionName(node.callee);
if (options.asyncFunctions.includes(functionName)) {
transformFunctionArguments(node);
if (functionName !== "callAsync") {
const lastArgument = node.arguments[node.arguments.length - 1];
if (lastArgument && lastArgument.loc) {
s.appendRight(toIndex(lastArgument.loc.end), ",1");
}
}
}
if (objectDefinitionFunctions.includes(functionName)) {
for (const argument of node.arguments) {
if (argument.type !== "ObjectExpression") {
continue;
}
for (const property of argument.properties) {
if (property.type !== "Property" || property.key.type !== "Identifier") {
continue;
}
if (options.objectDefinitions[functionName]?.includes(
property.key?.name
)) {
transformFunctionBody(property.value);
}
}
}
}
}
}
});
if (!detected) {
return;
}
s.appendLeft(
0,
`import { ${options.helperName} as __executeAsync } from "${options.helperModule}";`
);
return {
code: s.toString(),
magicString: s
};
function toIndex(pos) {
return lines.slice(0, pos.line - 1).join("\n").length + pos.column + 1;
}
function transformFunctionBody(function_) {
if (function_.type !== "ArrowFunctionExpression" && function_.type !== "FunctionExpression") {
return;
}
if (!function_.async) {
return;
}
const body = function_.body;
let injectVariable = false;
estreeWalker.walk(body, {
enter(node, parent) {
if (node.type === "AwaitExpression" && !node[kInjected]) {
detected = true;
injectVariable = true;
injectForNode(node, parent);
} else if (node.type === "IfStatement" && node.consequent.type === "ExpressionStatement" && node.consequent.expression.type === "AwaitExpression") {
detected = true;
injectVariable = true;
node.consequent.expression[kInjected] = true;
injectForNode(node.consequent.expression, node);
}
if (node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression" || node.type === "FunctionDeclaration") {
return this.skip();
}
}
});
if (injectVariable && body.loc) {
s.appendLeft(toIndex(body.loc.start) + 1, "let __temp, __restore;");
}
}
function transformFunctionArguments(node) {
for (const function_ of node.arguments) {
transformFunctionBody(function_);
}
}
function injectForNode(node, parent) {
const isStatement = parent?.type === "ExpressionStatement";
if (!node.loc || !node.argument.loc) {
return;
}
s.remove(toIndex(node.loc.start), toIndex(node.argument.loc.start));
s.remove(toIndex(node.loc.end), toIndex(node.argument.loc.end));
s.appendLeft(
toIndex(node.argument.loc.start),
isStatement ? `;(([__temp,__restore]=__executeAsync(()=>` : `(([__temp,__restore]=__executeAsync(()=>`
);
s.appendRight(
toIndex(node.argument.loc.end),
isStatement ? `)),await __temp,__restore());` : `)),__temp=await __temp,__restore(),__temp)`
);
}
}
return {
transform,
filter,
shouldTransform
};
}
function _getFunctionName(node) {
if (node.type === "Identifier") {
return node.name;
} else if (node.type === "MemberExpression") {
return _getFunctionName(node.property);
}
return "";
}
exports.createTransformer = createTransformer;

View file

@ -0,0 +1,40 @@
import MagicString from 'magic-string';
interface TransformerOptions {
/**
* The function names to be transformed.
*
* @default ['withAsyncContext']
*/
asyncFunctions?: string[];
/**
* @default 'unctx'
*/
helperModule?: string;
/**
* @default 'executeAsync'
*/
helperName?: string;
/**
* Whether to transform properties of an object defined with a helper function. For example,
* to transform key `middleware` within the object defined with function `defineMeta`, you would pass:
* `{ defineMeta: ['middleware'] }`.
* @default {}
*/
objectDefinitions?: Record<string, string[]>;
}
declare function createTransformer(options?: TransformerOptions): {
transform: (code: string, options_?: {
force?: false;
}) => {
code: string;
magicString: MagicString;
} | undefined;
filter: {
code: RegExp;
};
shouldTransform: (code: string) => boolean;
};
export { createTransformer };
export type { TransformerOptions };

View file

@ -0,0 +1,40 @@
import MagicString from 'magic-string';
interface TransformerOptions {
/**
* The function names to be transformed.
*
* @default ['withAsyncContext']
*/
asyncFunctions?: string[];
/**
* @default 'unctx'
*/
helperModule?: string;
/**
* @default 'executeAsync'
*/
helperName?: string;
/**
* Whether to transform properties of an object defined with a helper function. For example,
* to transform key `middleware` within the object defined with function `defineMeta`, you would pass:
* `{ defineMeta: ['middleware'] }`.
* @default {}
*/
objectDefinitions?: Record<string, string[]>;
}
declare function createTransformer(options?: TransformerOptions): {
transform: (code: string, options_?: {
force?: false;
}) => {
code: string;
magicString: MagicString;
} | undefined;
filter: {
code: RegExp;
};
shouldTransform: (code: string) => boolean;
};
export { createTransformer };
export type { TransformerOptions };

View file

@ -0,0 +1,40 @@
import MagicString from 'magic-string';
interface TransformerOptions {
/**
* The function names to be transformed.
*
* @default ['withAsyncContext']
*/
asyncFunctions?: string[];
/**
* @default 'unctx'
*/
helperModule?: string;
/**
* @default 'executeAsync'
*/
helperName?: string;
/**
* Whether to transform properties of an object defined with a helper function. For example,
* to transform key `middleware` within the object defined with function `defineMeta`, you would pass:
* `{ defineMeta: ['middleware'] }`.
* @default {}
*/
objectDefinitions?: Record<string, string[]>;
}
declare function createTransformer(options?: TransformerOptions): {
transform: (code: string, options_?: {
force?: false;
}) => {
code: string;
magicString: MagicString;
} | undefined;
filter: {
code: RegExp;
};
shouldTransform: (code: string) => boolean;
};
export { createTransformer };
export type { TransformerOptions };

152
Frontend-Learner/node_modules/unctx/dist/transform.mjs generated vendored Normal file
View file

@ -0,0 +1,152 @@
import * as acorn from 'acorn';
import MagicString from 'magic-string';
import { walk } from 'estree-walker';
const kInjected = "__unctx_injected__";
function createTransformer(options = {}) {
options = {
asyncFunctions: ["withAsyncContext"],
helperModule: "unctx",
helperName: "executeAsync",
objectDefinitions: {},
...options
};
const objectDefinitionFunctions = Object.keys(options.objectDefinitions);
const matchRE = new RegExp(
`\\b(${[...options.asyncFunctions, ...objectDefinitionFunctions].join(
"|"
)})\\(`
);
function shouldTransform(code) {
return typeof code === "string" && matchRE.test(code);
}
const filter = {
code: matchRE
};
function transform(code, options_ = {}) {
if (!options_.force && !shouldTransform(code)) {
return;
}
const ast = acorn.parse(code, {
sourceType: "module",
ecmaVersion: "latest",
locations: true
});
const s = new MagicString(code);
const lines = code.split("\n");
let detected = false;
walk(ast, {
enter(node) {
if (node.type === "CallExpression") {
const functionName = _getFunctionName(node.callee);
if (options.asyncFunctions.includes(functionName)) {
transformFunctionArguments(node);
if (functionName !== "callAsync") {
const lastArgument = node.arguments[node.arguments.length - 1];
if (lastArgument && lastArgument.loc) {
s.appendRight(toIndex(lastArgument.loc.end), ",1");
}
}
}
if (objectDefinitionFunctions.includes(functionName)) {
for (const argument of node.arguments) {
if (argument.type !== "ObjectExpression") {
continue;
}
for (const property of argument.properties) {
if (property.type !== "Property" || property.key.type !== "Identifier") {
continue;
}
if (options.objectDefinitions[functionName]?.includes(
property.key?.name
)) {
transformFunctionBody(property.value);
}
}
}
}
}
}
});
if (!detected) {
return;
}
s.appendLeft(
0,
`import { ${options.helperName} as __executeAsync } from "${options.helperModule}";`
);
return {
code: s.toString(),
magicString: s
};
function toIndex(pos) {
return lines.slice(0, pos.line - 1).join("\n").length + pos.column + 1;
}
function transformFunctionBody(function_) {
if (function_.type !== "ArrowFunctionExpression" && function_.type !== "FunctionExpression") {
return;
}
if (!function_.async) {
return;
}
const body = function_.body;
let injectVariable = false;
walk(body, {
enter(node, parent) {
if (node.type === "AwaitExpression" && !node[kInjected]) {
detected = true;
injectVariable = true;
injectForNode(node, parent);
} else if (node.type === "IfStatement" && node.consequent.type === "ExpressionStatement" && node.consequent.expression.type === "AwaitExpression") {
detected = true;
injectVariable = true;
node.consequent.expression[kInjected] = true;
injectForNode(node.consequent.expression, node);
}
if (node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression" || node.type === "FunctionDeclaration") {
return this.skip();
}
}
});
if (injectVariable && body.loc) {
s.appendLeft(toIndex(body.loc.start) + 1, "let __temp, __restore;");
}
}
function transformFunctionArguments(node) {
for (const function_ of node.arguments) {
transformFunctionBody(function_);
}
}
function injectForNode(node, parent) {
const isStatement = parent?.type === "ExpressionStatement";
if (!node.loc || !node.argument.loc) {
return;
}
s.remove(toIndex(node.loc.start), toIndex(node.argument.loc.start));
s.remove(toIndex(node.loc.end), toIndex(node.argument.loc.end));
s.appendLeft(
toIndex(node.argument.loc.start),
isStatement ? `;(([__temp,__restore]=__executeAsync(()=>` : `(([__temp,__restore]=__executeAsync(()=>`
);
s.appendRight(
toIndex(node.argument.loc.end),
isStatement ? `)),await __temp,__restore());` : `)),__temp=await __temp,__restore(),__temp)`
);
}
}
return {
transform,
filter,
shouldTransform
};
}
function _getFunctionName(node) {
if (node.type === "Identifier") {
return node.name;
} else if (node.type === "MemberExpression") {
return _getFunctionName(node.property);
}
return "";
}
export { createTransformer };