typia
Version:
Superfast runtime validators with only one line
180 lines (164 loc) • 5.35 kB
text/typescript
import ts from "typescript";
import { MapUtil } from "../utils/MapUtil";
export class ImportProgrammer {
private readonly assets_: Map<string, IAsset> = new Map();
private readonly options_: Readonly<ImportProgrammer.IOptions>;
public constructor(options?: Partial<ImportProgrammer.IOptions>) {
this.options_ = {
internalPrefix: options?.internalPrefix ?? "",
};
}
/* -----------------------------------------------------------
ENROLLMENTS
----------------------------------------------------------- */
public default(props: ImportProgrammer.IDefault): ts.Identifier {
const asset: IAsset = this.take(props.file);
asset.default ??= props;
asset.default.type ||= props.type;
return ts.factory.createIdentifier(asset.default.name);
}
public instance(props: ImportProgrammer.IInstance): ts.Identifier {
const alias: string = props.alias ?? props.name;
const asset: IAsset = this.take(props.file);
MapUtil.take(asset.instances, alias, () => props);
return ts.factory.createIdentifier(alias);
}
public namespace(props: ImportProgrammer.INamespace): ts.Identifier {
const asset: IAsset = this.take(props.file);
asset.namespace ??= props;
return ts.factory.createIdentifier(asset.namespace.name);
}
public type(props: {
file: string;
name: string | ts.EntityName;
arguments?: ts.TypeNode[];
}): ts.ImportTypeNode {
return ts.factory.createImportTypeNode(
ts.factory.createLiteralTypeNode(
ts.factory.createStringLiteral(props.file),
),
undefined,
typeof props.name === "string"
? ts.factory.createIdentifier(props.name)
: props.name,
props.arguments,
);
}
/** @internal */
public internal(name: string): ts.PropertyAccessExpression {
if (name.startsWith("_") === false) name = `_${name}`;
return ts.factory.createPropertyAccessExpression(
this.namespace({
file: `typia/lib/internal/${name}.js`,
name: this.alias(name),
}),
name,
);
}
/** @internal */
public getInternalText(name: string): string {
if (name.startsWith("_") === false) name = `_${name}`;
const asset: IAsset | undefined = this.take(
`typia/lib/internal/${name}.js`,
);
if (!asset?.namespace) throw new Error(`Internal asset not found: ${name}`);
return `${asset.namespace.name}.${name}`;
}
/** @internal */
private take(file: string): IAsset {
return MapUtil.take(this.assets_, file, () => ({
file,
default: null,
namespace: null,
instances: new Map(),
}));
}
private alias(name: string): string {
return `__${this.options_.internalPrefix}${name}`;
}
/* -----------------------------------------------------------
PROGRAM STATEMENTS
----------------------------------------------------------- */
public toStatements(): ts.ImportDeclaration[] {
const statements: ts.ImportDeclaration[] = [];
for (const asset of this.assets_.values()) {
if (asset.namespace !== null)
statements.push(
ts.factory.createImportDeclaration(
undefined,
ts.factory.createImportClause(
false,
undefined,
ts.factory.createNamespaceImport(
ts.factory.createIdentifier(asset.namespace.name),
),
),
ts.factory.createStringLiteral(asset.file),
),
);
if (asset.default !== null)
statements.push(
ts.factory.createImportDeclaration(
undefined,
ts.factory.createImportClause(
asset.default.type,
ts.factory.createIdentifier(asset.default.name),
undefined,
),
ts.factory.createStringLiteral(asset.file),
),
);
if (asset.instances.size > 0)
statements.push(
ts.factory.createImportDeclaration(
undefined,
ts.factory.createImportClause(
false,
undefined,
asset.instances.size > 0
? ts.factory.createNamedImports(
[...asset.instances.values()].map((ins) =>
ts.factory.createImportSpecifier(
false,
ins.alias || ins.alias === ins.name
? ts.factory.createIdentifier(ins.name)
: undefined,
ts.factory.createIdentifier(ins.alias ?? ins.name),
),
),
)
: undefined,
),
ts.factory.createStringLiteral(asset.file),
undefined,
),
);
}
return statements;
}
}
export namespace ImportProgrammer {
export interface IOptions {
internalPrefix: string;
}
export interface IDefault {
file: string;
name: string;
type: boolean;
}
export interface IInstance {
file: string;
name: string;
alias: string | null;
}
export interface INamespace {
file: string;
name: string;
}
}
interface IAsset {
file: string;
default: ImportProgrammer.IDefault | null;
namespace: ImportProgrammer.INamespace | null;
instances: Map<string, ImportProgrammer.IInstance>;
}