@nestia/sdk
Version:
Nestia SDK and Swagger generator
148 lines (136 loc) • 4.66 kB
text/typescript
import path from "path";
import { HashMap, HashSet, Pair } from "tstl";
import ts from "typescript";
import { FilePrinter } from "./FilePrinter";
export class ImportDictionary {
private readonly components_: HashMap<Pair<string, boolean>, IComposition> =
new HashMap();
public constructor(public readonly file: string) {}
public empty(): boolean {
return this.components_.empty();
}
public external(props: ImportDictionary.IExternalProps): string {
const composition: IComposition = this.components_.take(
new Pair(`node_modules/${props.library}`, props.type),
() => ({
location: `node_modules/${props.library}`,
elements: new HashSet(),
default: false,
type: props.type,
}),
);
if (props.instance === null) composition.default = true;
else composition.elements.insert(props.instance);
return props.instance ?? props.library;
}
public internal(props: ImportDictionary.IInternalProps): string {
const file: string = (() => {
if (props.file.substring(props.file.length - 5) === ".d.ts")
return props.file.substring(0, props.file.length - 5);
else if (props.file.substring(props.file.length - 3) === ".ts")
return props.file.substring(0, props.file.length - 3);
return props.file;
})();
const composition: IComposition = this.components_.take(
new Pair(file, props.type),
() => ({
location: file,
elements: new HashSet(),
default: false,
type: props.type,
}),
);
if (props.instance === null) {
composition.default = true;
if (props.name) composition.name = props.name;
} else composition.elements.insert(props.instance);
return props.instance ?? file;
}
public toStatements(outDir: string): ts.Statement[] {
const external: ts.ImportDeclaration[] = [];
const internal: ts.ImportDeclaration[] = [];
const locator = (str: string) => {
const location: string = path.relative(outDir, str).split("\\").join("/");
const index: number = location.lastIndexOf(NODE_MODULES);
return index === -1
? location.startsWith("..")
? location
: `./${location}`
: location.substring(index + NODE_MODULES.length);
};
const enroll =
(filter: (str: string) => boolean) =>
(container: ts.ImportDeclaration[]) => {
const compositions: IComposition[] = this.components_
.toJSON()
.filter((c) => filter(c.second.location))
.map((e) => ({
...e.second,
location: locator(e.second.location),
}))
.sort((a, b) => a.location.localeCompare(b.location));
for (const c of compositions) {
const brackets: string[] = [];
if (c.default) brackets.push(c.name ?? c.location);
if (c.elements.empty() === false)
brackets.push(
`{ ${c.elements
.toJSON()
.sort((a, b) => a.localeCompare(b))
.join(", ")} }`,
);
container.push(
ts.factory.createImportDeclaration(
undefined,
ts.factory.createImportClause(
c.type,
c.default
? ts.factory.createIdentifier(c.name ?? c.location)
: undefined,
c.elements.empty() === false
? ts.factory.createNamedImports(
[...c.elements].map((elem) =>
ts.factory.createImportSpecifier(
false,
undefined,
ts.factory.createIdentifier(elem),
),
),
)
: undefined,
),
ts.factory.createStringLiteral(c.location),
),
);
}
};
enroll((str) => str.indexOf(NODE_MODULES) !== -1)(external);
enroll((str) => str.indexOf(NODE_MODULES) === -1)(internal);
return [
...external,
...(external.length && internal.length ? [FilePrinter.enter()] : []),
...internal,
];
}
}
export namespace ImportDictionary {
export interface IExternalProps {
type: boolean;
library: string;
instance: string | null;
}
export interface IInternalProps {
type: boolean;
file: string;
instance: string | null;
name?: string | null;
}
}
interface IComposition {
location: string;
type: boolean;
default: boolean;
name?: string;
elements: HashSet<string>;
}
const NODE_MODULES = "node_modules/";