You're Invited: Meet the Socket team at BSidesSF and RSAC - April 27 - May 1.RSVP
Socket
Sign inDemoInstall
Socket

@travetto/transformer

Package Overview
Dependencies
Maintainers
1
Versions
153
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@travetto/transformer - npm Package Compare versions

Comparing version

to
3.0.0-rc.6

__index__.ts

19

package.json
{
"name": "@travetto/transformer",
"displayName": "Transformation",
"version": "3.0.0-rc.4",
"version": "3.0.0-rc.6",
"description": "Functionality for AST transformations, with transformer registration, and general utils",

@@ -18,7 +17,7 @@ "keywords": [

"files": [
"index.ts",
"__index__.ts",
"src",
"test-support"
"support"
],
"main": "index.ts",
"main": "__index__.ts",
"repository": {

@@ -29,4 +28,12 @@ "url": "https://github.com/travetto/travetto.git",

"dependencies": {
"@travetto/base": "^3.0.0-rc.2"
"@travetto/manifest": "^3.0.0-rc.3",
"tslib": "^2.4.1",
"typescript": "^4.9.4"
},
"travetto": {
"displayName": "Transformation",
"profiles": [
"compile"
]
},
"publishConfig": {

@@ -33,0 +40,0 @@ "access": "public"

<!-- This file was generated by @travetto/doc and should not be modified directly -->
<!-- Please modify https://github.com/travetto/travetto/tree/main/module/transformer/doc.ts and execute "npx trv doc" to rebuild -->
<!-- Please modify https://github.com/travetto/travetto/tree/main/module/transformer/DOC.ts and execute "npx trv doc" to rebuild -->
# Transformation

@@ -13,3 +13,3 @@ ## Functionality for AST transformations, with transformer registration, and general utils

The module is primarily aimed at extremely advanced usages for things that cannot be detected at runtime. The [Registry](https://github.com/travetto/travetto/tree/main/module/registry#readme "Patterns and utilities for handling registration of metadata and functionality for run-time use") module already has knowledge of all `class`es and `field`s, and is able to listen to changes there. Many of the modules build upon work by some of the foundational transformers defined in [Registry](https://github.com/travetto/travetto/tree/main/module/registry#readme "Patterns and utilities for handling registration of metadata and functionality for run-time use"), [Schema](https://github.com/travetto/travetto/tree/main/module/schema#readme "Data type registry for runtime validation, reflection and binding. ") and [Dependency Injection](https://github.com/travetto/travetto/tree/main/module/di#readme "Dependency registration/management and injection support."). These all center around defining a registry of classes, and associated type information.
The module is primarily aimed at extremely advanced usages for things that cannot be detected at runtime. The [Registry](https://github.com/travetto/travetto/tree/main/module/registry#readme "Patterns and utilities for handling registration of metadata and functionality for run-time use") module already has knowledge of all `class`es and `field`s, and is able to listen to changes there. Many of the modules build upon work by some of the foundational transformers defined in [Registry](https://github.com/travetto/travetto/tree/main/module/registry#readme "Patterns and utilities for handling registration of metadata and functionality for run-time use"), [Schema](https://github.com/travetto/travetto/tree/main/module/schema#readme "Data type registry for runtime validation, reflection and binding.") and [Dependency Injection](https://github.com/travetto/travetto/tree/main/module/di#readme "Dependency registration/management and injection support."). These all center around defining a registry of classes, and associated type information.

@@ -24,13 +24,11 @@ Because working with the [Typescript](https://typescriptlang.org) API can be delicate (and open to breaking changes), creating new transformers should be done cautiously.

```typescript
import * as ts from 'typescript';
import ts from 'typescript';
import { OnProperty, TransformerState, OnMethod, OnClass, TransformerId } from '@travetto/transformer';
import { OnProperty, TransformerState, OnMethod, OnClass } from '@travetto/transformer';
export class MakeUpper {
static [TransformerId] = '@trv:transformer-test';
@OnProperty()
static handleProperty(state: TransformerState, node: ts.PropertyDeclaration): ts.PropertyDeclaration {
if (!state.source.fileName.includes('doc/src')) {
if (!state.file.includes('doc/src')) {
return node;

@@ -40,3 +38,2 @@ }

node,
[],
node.modifiers,

@@ -52,3 +49,3 @@ node.name.getText().toUpperCase(),

static handleClass(state: TransformerState, node: ts.ClassDeclaration): ts.ClassDeclaration {
if (!state.source.fileName.includes('doc/src')) {
if (!state.file.includes('doc/src')) {
return node;

@@ -58,3 +55,2 @@ }

node,
[],
node.modifiers,

@@ -70,3 +66,3 @@ state.createIdentifier(node.name!.getText().toUpperCase()),

static handleMethod(state: TransformerState, node: ts.MethodDeclaration): ts.MethodDeclaration {
if (!state.source.fileName.includes('doc/src')) {
if (!state.file.includes('doc/src')) {
return node;

@@ -76,3 +72,2 @@ }

node,
[],
node.modifiers,

@@ -79,0 +74,0 @@ undefined,

@@ -1,6 +0,4 @@

import * as ts from 'typescript';
import { basename, dirname, relative } from 'path';
import ts from 'typescript';
import { PathUtil } from '@travetto/boot';
import { ModuleUtil } from '@travetto/boot/src/internal/module-util';
import { PackageUtil, path } from '@travetto/manifest';

@@ -11,3 +9,8 @@ import { AnyType, ExternalType } from './resolver/types';

import { Import } from './types/shared';
import { LiteralUtil } from './util/literal';
import { DeclarationUtil } from './util/declaration';
import { TransformerIndex } from './manifest-index';
const D_OR_D_TS_EXT_RE = /[.]d([.]ts)?$/;
/**

@@ -22,14 +25,79 @@ * Manages imports within a ts.SourceFile

#ids = new Map<string, string>();
#importName: string;
#index: TransformerIndex;
constructor(public source: ts.SourceFile, public factory: ts.NodeFactory) {
constructor(public source: ts.SourceFile, public factory: ts.NodeFactory, index: TransformerIndex) {
this.#imports = ImportUtil.collectImports(source);
this.#index = index;
this.#importName = index.getImportName(source.fileName);
}
#getImportFile(spec?: ts.Expression): string | undefined {
if (spec && ts.isStringLiteral(spec)) {
return spec.text.replace(/^['"]|["']$/g, '');
}
}
#rewriteModuleSpecifier(spec: ts.Expression | undefined): ts.Expression | undefined {
const fileOrImport = this.#getImportFile(spec);
if (
fileOrImport &&
(fileOrImport.startsWith('.') || this.#index.getFromImport(fileOrImport)) &&
!/[.]([mc]?js|ts|json)$/.test(fileOrImport)
) {
return LiteralUtil.fromLiteral(this.factory, `${fileOrImport}.js`);
}
return spec;
}
#rewriteImportClause(
spec: ts.Expression | undefined,
clause: ts.ImportClause | undefined,
checker: ts.TypeChecker
): ts.ImportClause | undefined {
if (!(spec && clause?.namedBindings && ts.isNamedImports(clause.namedBindings))) {
return clause;
}
const fileOrImport = this.#getImportFile(spec);
if (!(fileOrImport && (fileOrImport.startsWith('.') || this.#index.getFromImport(fileOrImport)))) {
return clause;
}
const bindings = clause.namedBindings;
const newBindings: ts.ImportSpecifier[] = [];
// Remove all type only imports
for (const el of bindings.elements) {
if (!el.isTypeOnly) {
const type = checker.getTypeAtLocation(el.name);
const objFlags = DeclarationUtil.getObjectFlags(type);
const typeFlags = type.getFlags();
if (objFlags || typeFlags !== 1) {
newBindings.push(el);
}
}
}
if (newBindings.length !== bindings.elements.length) {
return this.factory.updateImportClause(
clause,
clause.isTypeOnly,
clause.name,
this.factory.createNamedImports(newBindings)
);
} else {
return clause;
}
}
/**
* Produces a unique ID for a given file, importing if needed
*/
getId(file: string): string {
getId(file: string, name?: string): string {
if (!this.#ids.has(file)) {
const key = basename(file).replace(/[.][^.]*$/, '').replace(/[^A-Za-z0-9]+/g, '_');
this.#ids.set(file, `ᚕ_${key}_${this.#idx[key] = (this.#idx[key] || 0) + 1}`);
if (name) {
this.#ids.set(file, name);
} else {
const key = path.basename(file).replace(/[.][^.]*$/, '').replace(/[^A-Za-z0-9]+/g, '_');
this.#ids.set(file, `Ⲑ_${key}_${this.#idx[key] = (this.#idx[key] || 0) + 1}`);
}
}

@@ -42,25 +110,13 @@ return this.#ids.get(file)!;

*/
importFile(file: string, base?: string): Import {
file = ModuleUtil.normalizePath(file);
importFile(file: string, name?: string): Import {
file = this.#index.getImportName(file);
// Allow for node classes to be imported directly
if (/@types\/node/.test(file)) {
file = require.resolve(file.replace(/.*@types\/node\//, '').replace(/[.]d([.]ts)?$/, ''));
file = PackageUtil.resolveImport(file.replace(/.*@types\/node\//, '').replace(D_OR_D_TS_EXT_RE, ''));
}
// Handle relative imports
if (file.startsWith('.') && base &&
!base.startsWith('@travetto') && !base.includes('node_modules')
) { // Relative path
const fileDir = dirname(PathUtil.resolveUnix(file));
const baseDir = dirname(PathUtil.resolveUnix(base));
file = `${relative(baseDir, fileDir) || '.'}/${basename(file)}`;
if (/^[A-Za-z]/.test(file)) {
file = `./${file}`;
}
}
if (!D_OR_D_TS_EXT_RE.test(file) && !this.#newImports.has(file)) {
const id = this.getId(file, name);
if (!/[.]d([.]ts)?$/.test(file) && !this.#newImports.has(file)) {
const id = this.getId(file);
if (this.#imports.has(id)) { // Already imported, be cool

@@ -83,4 +139,4 @@ return this.#imports.get(id)!;

for (const type of types) {
if (type.key === 'external' && type.source && type.source !== this.source.fileName) {
this.importFile(type.source, this.source.fileName);
if (type.key === 'external' && type.importName && type.importName !== this.#importName) {
this.importFile(type.importName);
}

@@ -106,7 +162,7 @@ switch (type.key) {

try {
const importStmts = [...this.#newImports.values()].map(({ path, ident }) => {
const importStmts = [...this.#newImports.values()].map(({ path: resolved, ident }) => {
const importStmt = this.factory.createImportDeclaration(
undefined, undefined,
undefined,
this.factory.createImportClause(false, undefined, this.factory.createNamespaceImport(ident)),
this.factory.createStringLiteral(path)
this.factory.createStringLiteral(resolved)
);

@@ -124,3 +180,3 @@ return importStmt;

}
const out = new Error(`${err.message} in ${file.fileName.replace(PathUtil.cwd, '.')}`);
const out = new Error(`${err.message} in ${file.fileName.replace(process.cwd(), '.')}`);
out.stack = err.stack;

@@ -131,7 +187,41 @@ throw out;

finalizeImportExportExtension(ret: ts.SourceFile, checker: ts.TypeChecker): ts.SourceFile {
const toAdd: ts.Statement[] = [];
for (const stmt of ret.statements) {
if (ts.isExportDeclaration(stmt)) {
if (!stmt.isTypeOnly) {
toAdd.push(this.factory.updateExportDeclaration(
stmt,
stmt.modifiers,
stmt.isTypeOnly,
stmt.exportClause,
this.#rewriteModuleSpecifier(stmt.moduleSpecifier),
stmt.assertClause
));
}
} else if (ts.isImportDeclaration(stmt)) {
if (!stmt.importClause?.isTypeOnly) {
toAdd.push(this.factory.updateImportDeclaration(
stmt,
stmt.modifiers,
this.#rewriteImportClause(stmt.moduleSpecifier, stmt.importClause, checker)!,
this.#rewriteModuleSpecifier(stmt.moduleSpecifier)!,
stmt.assertClause
));
}
} else {
toAdd.push(stmt);
}
}
return CoreUtil.updateSource(this.factory, ret, toAdd);
}
/**
* Reset the imports into the source file
*/
finalize(ret: ts.SourceFile): ts.SourceFile {
return this.finalizeNewImports(ret) ?? ret;
finalize(ret: ts.SourceFile, checker: ts.TypeChecker): ts.SourceFile {
ret = this.finalizeNewImports(ret) ?? ret;
ret = this.finalizeImportExportExtension(ret, checker) ?? ret;
return ret;
}

@@ -143,6 +233,6 @@

getOrImport(factory: ts.NodeFactory, type: ExternalType): ts.Identifier | ts.PropertyAccessExpression {
if (type.source === this.source.fileName) {
if (type.importName === this.#importName) {
return factory.createIdentifier(type.name!);
} else {
const { ident } = this.#imports.get(type.source) ?? this.importFile(type.source, this.source.fileName);
const { ident } = this.#imports.get(type.importName) ?? this.importFile(type.importName);
return factory.createPropertyAccessExpression(ident, type.name!);

@@ -149,0 +239,0 @@ }

@@ -1,8 +0,13 @@

import * as ts from 'typescript';
import { DecoratorMeta, NodeTransformer, State, TransformPhase, TransformerType, Transformer, TransformerId } from './types/visitor';
import ts from 'typescript';
const HandlersProp = Symbol.for('@trv:transformer/handlers');
import { DecoratorMeta, NodeTransformer, State, TransformPhase, TransformerType, Transformer, ModuleNameⲐ } from './types/visitor';
const HandlersProp = Symbol.for('@travetto/transformer:handlers');
type TransformerWithHandlers = Transformer & { [HandlersProp]?: NodeTransformer[] };
function isTransformer(x: unknown): x is Transformer {
return x !== null && x !== undefined && typeof x === 'function';
}
/**

@@ -12,4 +17,15 @@ * Get all transformers

*/
export function getAllTransformers(obj: Record<string, { [HandlersProp]?: NodeTransformer[] }>): NodeTransformer[] {
return Object.values(obj).flatMap(x => x[HandlersProp] ?? []);
export function getAllTransformers(obj: Record<string, { [HandlersProp]?: NodeTransformer[] }>, module: string): NodeTransformer[] {
return Object.values(obj)
.flatMap(x => {
if (isTransformer(x)) {
x[ModuleNameⲐ] = module;
}
return (x[HandlersProp] ?? []);
})
.map(handler => ({
...handler,
key: `${module}:${handler.key}`,
target: handler.target?.map(t => `${module}:${t}`)
}));
}

@@ -19,11 +35,24 @@

function storeHandler(cls: TransformerWithHandlers, fn: Function, phase: TransformPhase, type: TransformerType, target?: string[]): void {
if (target) {
const ns = cls[TransformerId].split('/')[0]; // Everything before the '/'
target = target.map(x => x.startsWith('@') ? x : `${ns}/${x}`);
}
cls[HandlersProp] = cls[HandlersProp] ?? [];
cls[HandlersProp]!.push({ key: `${cls[TransformerId]}/${fn.name}`, [phase]: fn.bind(cls), type, target });
(cls[HandlersProp] ??= []).push({ key: fn.name, [phase]: fn.bind(cls), type, target });
}
/**
* Wraps entire file before transforming
*/
export function OnFile(...target: string[]) {
return <S extends State = State, R extends ts.Node = ts.Node>(
inst: Transformer, __: unknown, d: TypedPropertyDescriptor<(state: S, node: ts.SourceFile) => R>
): void => storeHandler(inst, d.value!, 'before', 'file', target);
}
/**
* Wraps entire file after transforming
*/
export function AfterFile(...target: string[]) {
return <S extends State = State, R extends ts.Node = ts.Node>(
inst: Transformer, __: unknown, d: TypedPropertyDescriptor<(state: S, node: ts.SourceFile) => R>
): void => storeHandler(inst, d.value!, 'before', 'file', target);
}
/**
* Listens for a `ts.CallExpression`, on descent

@@ -30,0 +59,0 @@ */

/* eslint-disable no-bitwise */
import * as ts from 'typescript';
import { dirname } from 'path';
import ts from 'typescript';
import { PathUtil } from '@travetto/boot';
import { Util } from '@travetto/base';
import { ManifestIndex, path } from '@travetto/manifest';
import { DocUtil } from '../util/doc';
import { CoreUtil } from '../util/core';
import { DeclarationUtil } from '../util/declaration';
import { LiteralUtil } from '../util/literal';
import { Type, AnyType, UnionType, Checker } from './types';
import { DocUtil, CoreUtil, DeclarationUtil, LiteralUtil } from '../util';
import { CoerceUtil } from './coerce';

@@ -38,3 +41,3 @@ /**

*/
export function TypeCategorize(checker: ts.TypeChecker, type: ts.Type): { category: Category, type: ts.Type } {
export function TypeCategorize(checker: ts.TypeChecker, type: ts.Type, index: ManifestIndex): { category: Category, type: ts.Type } {
const flags = type.getFlags();

@@ -66,5 +69,6 @@ const objectFlags = DeclarationUtil.getObjectFlags(type) ?? 0;

const source = DeclarationUtil.getPrimaryDeclarationNode(resolvedType).getSourceFile();
if (source?.fileName.includes('@types/node/globals') || source?.fileName.includes('typescript/lib')) {
const sourceFile = source.fileName;
if (sourceFile?.includes('@types/node/globals') || sourceFile?.includes('typescript/lib')) {
return { category: 'literal', type };
} else if (!source?.fileName.includes('@travetto') && source?.fileName.endsWith('.d.ts')) {
} else if (sourceFile?.endsWith('.d.ts') && !index.getFromSource(sourceFile)) {
return { category: 'unknown', type };

@@ -123,3 +127,3 @@ } else if (!resolvedType.isClass()) { // Not a real type

// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
const ret = LiteralUtil.isLiteralType(type) ? Util.coerceType(type.value, cons as typeof String, false) :
const ret = LiteralUtil.isLiteralType(type) ? CoerceUtil.coerce(type.value, cons as typeof String, false) :
undefined;

@@ -146,8 +150,6 @@

build: (checker, type) => {
const source = DeclarationUtil.getPrimaryDeclarationNode(type).getSourceFile();
const name = CoreUtil.getSymbol(type)?.getName();
return {
key: 'external', name, source: source.fileName,
tsTypeArguments: checker.getAllTypeArguments(type)
};
const importName = checker.getIndex().getImportName(type);
const tsTypeArguments = checker.getAllTypeArguments(type);
return { key: 'external', name, importName, tsTypeArguments };
}

@@ -183,5 +185,6 @@ },

build: (checker, type, alias?) => {
const fieldNodes: Record<string, ts.Type> = {};
const name = CoreUtil.getSymbol(alias ?? type);
const source = DeclarationUtil.getPrimaryDeclarationNode(type)?.getSourceFile();
const tsFieldTypes: Record<string, ts.Type> = {};
const name = CoreUtil.getSymbol(alias ?? type)?.getName();
const importName = checker.getIndex().getImportName(type);
const tsTypeArguments = checker.getAllTypeArguments(type);
for (const member of checker.getPropertiesOfType(type)) {

@@ -195,13 +198,7 @@ const dec = DeclarationUtil.getPrimaryDeclarationNode(member);

) {
fieldNodes[member.getName()] = memberType;
tsFieldTypes[member.getName()] = memberType;
}
}
}
return {
key: 'shape', name: name?.getName(),
source: source?.fileName,
tsFieldTypes: fieldNodes,
tsTypeArguments: checker.getAllTypeArguments(type),
fieldTypes: {}
};
return { key: 'shape', name, importName, tsFieldTypes, tsTypeArguments, fieldTypes: {} };
}

@@ -214,19 +211,22 @@ },

// eslint-disable-next-line prefer-const
let [source, name, ext] = tag.split(':');
let [importName, name] = tag.split(':');
if (!name) {
name = source;
source = '.';
name = importName;
importName = '.';
}
const sourceFile: string = DeclarationUtil.getDeclarations(type)
?.find(x => ts.getAllJSDocTags(x, (t): t is ts.JSDocTag => t.tagName.getText() === 'concrete').length)
?.getSourceFile().fileName ?? '';
// Resolving relative to source file
if (importName.startsWith('.')) {
const rawSourceFile: string = DeclarationUtil.getDeclarations(type)
?.find(x => ts.getAllJSDocTags(x, (t): t is ts.JSDocTag => t.tagName.getText() === 'concrete').length)
?.getSourceFile().fileName ?? '';
if (source === '.') {
source = sourceFile;
} else if (source.startsWith('.')) {
source = PathUtil.resolveUnix(dirname(sourceFile), source);
if (importName === '.') {
importName = checker.getIndex().getImportName(rawSourceFile);
} else {
const base = path.dirname(rawSourceFile);
importName = checker.getIndex().getImportName(path.resolve(base, importName));
}
}
return { key: 'external', name, source: ext === 'node' ? source : PathUtil.resolveUnix(sourceFile, source) };
return { key: 'external', name, importName };
}

@@ -233,0 +233,0 @@ }

@@ -1,3 +0,3 @@

import * as ts from 'typescript';
import { AnyType } from './types';
import ts from 'typescript';
import type { AnyType } from './types';

@@ -4,0 +4,0 @@ /**

@@ -1,7 +0,8 @@

import * as ts from 'typescript';
import ts from 'typescript';
import { AnyType, Checker } from './types';
import type { AnyType, Checker } from './types';
import { TypeCategorize, TypeBuilder } from './builder';
import { VisitCache } from './cache';
import { DocUtil } from '../util';
import { DocUtil } from '../util/doc';
import { TransformerIndex } from '../manifest-index';

@@ -13,5 +14,7 @@ /**

#tsChecker: ts.TypeChecker;
#index: TransformerIndex;
constructor(tsChecker: ts.TypeChecker) {
constructor(tsChecker: ts.TypeChecker, idx: TransformerIndex) {
this.#tsChecker = tsChecker;
this.#index = idx;
}

@@ -27,2 +30,6 @@

getIndex(): TransformerIndex {
return this.#index;
}
/**

@@ -78,3 +85,3 @@ * Get type from element

const { category, type } = TypeCategorize(this.#tsChecker, resType);
const { category, type } = TypeCategorize(this.#tsChecker, resType, this.#index);
const { build, finalize } = TypeBuilder[category];

@@ -81,0 +88,0 @@

@@ -1,3 +0,5 @@

import type * as ts from 'typescript';
import type ts from 'typescript';
import { TransformerIndex } from '../manifest-index';
/**

@@ -40,3 +42,3 @@ * Base type for a simplistic type structure

*/
source: string;
importName: string;
/**

@@ -59,3 +61,3 @@ * Type arguments

*/
source: string;
importName: string;
/**

@@ -161,2 +163,3 @@ * Does not include methods, used for shapes not concrete types

getType(node: ts.Node): ts.Type;
getIndex(): TransformerIndex;
}

@@ -1,16 +0,18 @@

import * as ts from 'typescript';
import ts from 'typescript';
import { SystemUtil } from '@travetto/boot/src/internal/system';
import { ModuleUtil } from '@travetto/boot/src/internal/module-util';
import { Util } from '@travetto/base';
import { path } from '@travetto/manifest';
import { ExternalType, AnyType } from './resolver/types';
import { State, DecoratorMeta, Transformer, TransformerId } from './types/visitor';
import { State, DecoratorMeta, Transformer, ModuleNameⲐ } from './types/visitor';
import { TypeResolver } from './resolver/service';
import { ImportManager } from './importer';
import { Import } from './types/shared';
import { DocUtil } from './util/doc';
import { DecoratorUtil } from './util/decorator';
import { DeclarationUtil } from './util/declaration';
import { CoreUtil, LiteralUtil } from './util';
import { Import } from './types/shared';
import { CoreUtil } from './util/core';
import { LiteralUtil } from './util/literal';
import { SystemUtil } from './util/system';
import { TransformerIndex } from './manifest-index';

@@ -31,19 +33,31 @@ function hasOriginal(n: unknown): n is { original: ts.Node } {

export class TransformerState implements State {
static SYNTHETIC_EXT = 'ᚕsyn';
static SYNTHETIC_EXT = 'Ⲑsyn';
#resolver: TypeResolver;
#imports: ImportManager;
#index: TransformerIndex;
#syntheticIdentifiers = new Map<string, ts.Identifier>();
#decorators = new Map<string, ts.PropertyAccessExpression>();
#options: ts.CompilerOptions;
added = new Map<number, ts.Statement[]>();
module: string;
importName: string;
file: string;
constructor(public source: ts.SourceFile, public factory: ts.NodeFactory, checker: ts.TypeChecker) {
this.#imports = new ImportManager(source, factory);
this.#resolver = new TypeResolver(checker);
this.module = ModuleUtil.normalizePath(this.source.fileName);
constructor(public source: ts.SourceFile, public factory: ts.NodeFactory, checker: ts.TypeChecker, index: TransformerIndex, options: ts.CompilerOptions) {
this.#index = index;
this.#imports = new ImportManager(source, factory, index);
this.#resolver = new TypeResolver(checker, index);
this.file = path.toPosix(this.source.fileName);
this.importName = this.#index.getImportName(this.file, true);
this.#options = options;
}
/**
* Are we building ESM Output?
*/
isEsmOutput(): boolean {
return this.#options.module !== ts.ModuleKind.CommonJS;
}
/**
* Allow access to resolver

@@ -66,4 +80,4 @@ * @private

*/
importFile(file: string): Import {
return this.#imports.importFile(file);
importFile(file: string, name?: string): Import {
return this.#imports.importFile(file, name);
}

@@ -86,3 +100,5 @@

if (resolved.key !== 'external') {
throw new Error(`Unable to import non-external type: ${node.getText()} ${resolved.key}: ${node.getSourceFile().fileName}`);
const file = node.getSourceFile().fileName;
const src = this.#index.getImportName(file);
throw new Error(`Unable to import non-external type: ${node.getText()} ${resolved.key}: ${src}`);
}

@@ -147,3 +163,3 @@ return resolved;

*/
getDecoratorMeta(dec: ts.Decorator): DecoratorMeta {
getDecoratorMeta(dec: ts.Decorator): DecoratorMeta | undefined {
const ident = DecoratorUtil.getDecoratorIdent(dec);

@@ -153,14 +169,15 @@ const decl = DeclarationUtil.getPrimaryDeclarationNode(

);
const src = decl?.getSourceFile().fileName;
const mod = src ? this.#index.getImportName(src, true) : undefined;
const file = this.#index.getFromImport(mod ?? '')?.output;
const targets = DocUtil.readAugments(this.#resolver.getType(ident));
const module = file ? mod : undefined;
const name = ident ?
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
ident.escapedText! as string :
undefined;
return {
dec,
ident,
file: decl?.getSourceFile().fileName,
module: decl ? ModuleUtil.normalizePath(decl.getSourceFile().fileName) : undefined, // All #decorators will be absolute
targets: DocUtil.readAugments(this.#resolver.getType(ident)),
name: ident ?
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
ident.escapedText! as string :
undefined
};
if (ident && name) {
return { dec, ident, file, module, targets, name };
}
}

@@ -174,3 +191,3 @@

.map(dec => this.getDecoratorMeta(dec))
.filter(x => !!x.ident) : [];
.filter((x): x is DecoratorMeta => !!x) : [];
}

@@ -190,21 +207,26 @@

*/
addStatement(stmt: ts.Statement, before?: ts.Node): void {
addStatements(added: ts.Statement[], before?: ts.Node | number): void {
const stmts = this.source.statements.slice(0);
let idx = stmts.length;
let n = before;
if (hasOriginal(n)) {
n = n.original;
let idx = stmts.length + 1000;
if (before && typeof before !== 'number') {
let n = before;
if (hasOriginal(n)) {
n = n.original;
}
while (n && !ts.isSourceFile(n.parent) && n !== n.parent) {
n = n.parent;
}
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
const nStmt: ts.Statement = n as ts.Statement;
if (n && ts.isSourceFile(n.parent) && stmts.indexOf(nStmt) >= 0) {
idx = stmts.indexOf(nStmt) - 1;
}
} else if (before !== undefined) {
idx = before;
}
while (n && !ts.isSourceFile(n.parent)) {
n = n.parent;
}
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
const nStmt: ts.Statement = n as ts.Statement;
if (n && ts.isSourceFile(n.parent) && stmts.indexOf(nStmt) >= 0) {
idx = stmts.indexOf(nStmt) - 1;
}
if (!this.added.has(idx)) {
this.added.set(idx, []);
}
this.added.get(idx)!.push(stmt);
this.added.get(idx)!.push(...added);
}

@@ -216,3 +238,3 @@

finalize(ret: ts.SourceFile): ts.SourceFile {
ret = this.#imports.finalize(ret);
ret = this.#imports.finalize(ret, this.#resolver.getChecker());
return ret;

@@ -222,11 +244,2 @@ }

/**
* Get Filename as ᚕsrc
*/
getFilenameAsSrc(): ts.CallExpression {
const ident = this.factory.createIdentifier('ᚕsrc');
ident.getSourceFile = (): ts.SourceFile => this.source;
return this.factory.createCallExpression(ident, [], [this.createIdentifier('__filename')]);
}
/**
* From literal

@@ -255,3 +268,3 @@ */

*/
createAccess(first: string | ts.Expression, second: string | ts.Identifier, ...items: (string | ts.Identifier)[]): ts.PropertyAccessExpression {
createAccess(first: string | ts.Expression, second: string | ts.Identifier, ...items: (string | number | ts.Identifier)[]): ts.Expression {
return CoreUtil.createAccess(this.factory, first, second, ...items);

@@ -276,2 +289,20 @@ }

/**
* Get filename identifier, regardless of module system
*/
getFilenameIdentifier(): ts.Expression {
return this.isEsmOutput() ?
this.createAccess('import', 'meta', 'url') :
this.createIdentifier('__filename');
}
/**
* Get the entry file identifier, supports both ESM and commonjs
*/
getEntryFileIdentifier(): ts.Expression {
return this.isEsmOutput() ?
this.createAccess('process', 'argv', 1) :
this.createAccess('require', 'main', 'filename');
}
/**
* Find decorator, relative to registered key

@@ -283,6 +314,7 @@ * @param state

*/
findDecorator(cls: Transformer, node: ts.Node, name: string, module?: string): ts.Decorator | undefined {
const target = `${cls[TransformerId]}/${name}`;
return this.getDecoratorList(node)
.find(x => x.targets?.includes(target) && (!module || x.name === name && x.module === module))?.dec;
findDecorator(mod: string | Transformer, node: ts.Node, name: string, module?: string): ts.Decorator | undefined {
mod = typeof mod === 'string' ? mod : mod[ModuleNameⲐ]!;
const target = `${mod}:${name}`;
const list = this.getDecoratorList(node);
return list.find(x => x.targets?.includes(target) && (!module || x.name === name && x.module === module))?.dec;
}

@@ -306,3 +338,3 @@

// Determine type unique ident
unique = Util.uuid(type.name ? 5 : 10);
unique = SystemUtil.uuid(type.name ? 5 : 10);
}

@@ -309,0 +341,0 @@ // Otherwise read name with uuid

@@ -1,2 +0,2 @@

import * as ts from 'typescript';
import type { default as ts } from 'typescript';

@@ -3,0 +3,0 @@ /**

@@ -1,2 +0,2 @@

import * as ts from 'typescript';
import ts from 'typescript';

@@ -17,3 +17,3 @@ /**

source: ts.SourceFile;
module: string;
importName: string;
added: Map<number, ts.Statement[]>;

@@ -26,8 +26,10 @@ getDecoratorList(node: ts.Node): DecoratorMeta[];

export type TransformerType = 'class' | 'method' | 'property' | 'getter' | 'setter' | 'parameter' | 'static-method' | 'call' | 'function';
export type TransformerType =
'class' | 'method' | 'property' | 'getter' | 'setter' | 'parameter' |
'static-method' | 'call' | 'function' | 'file';
export const TransformerId = Symbol.for('@trv:transformer/id');
export const ModuleNameⲐ = Symbol.for('@travetto/transformer:id');
export type Transformer = {
[TransformerId]: string;
[ModuleNameⲐ]?: string;
name: string;

@@ -34,0 +36,0 @@ };

@@ -1,2 +0,2 @@

import * as ts from 'typescript';
import ts from 'typescript';

@@ -7,2 +7,3 @@ /**

export class CoreUtil {
/**

@@ -55,3 +56,2 @@ * See if inbound node has an original property

return factory.createPropertyDeclaration(
undefined,
[factory.createToken(ts.SyntaxKind.StaticKeyword)],

@@ -93,9 +93,11 @@ name, undefined, undefined, val

second: string | ts.Identifier,
...items: (string | ts.Identifier)[]
): ts.PropertyAccessExpression {
...items: (string | number | ts.Identifier)[]
): ts.Expression {
if (typeof first === 'string') {
first = factory.createIdentifier(first);
}
return items.reduce(
(acc, p) => factory.createPropertyAccessExpression(acc, p),
return items.reduce<ts.Expression>(
(acc, p) => typeof p === 'number' ?
factory.createElementAccessExpression(acc, p) :
factory.createPropertyAccessExpression(acc, p),
factory.createPropertyAccessExpression(first, second)

@@ -102,0 +104,0 @@ );

@@ -1,2 +0,2 @@

import * as ts from 'typescript';
import ts from 'typescript';
import { CoreUtil } from './core';

@@ -3,0 +3,0 @@

@@ -1,2 +0,2 @@

import * as ts from 'typescript';
import ts from 'typescript';
import { CoreUtil } from './core';

@@ -3,0 +3,0 @@

@@ -1,2 +0,2 @@

import * as ts from 'typescript';
import ts from 'typescript';

@@ -3,0 +3,0 @@ import { DeclDocumentation } from '../types/shared';

@@ -1,5 +0,4 @@

import * as ts from 'typescript';
import { resolve as pathResolve } from 'path';
import ts from 'typescript';
import { PathUtil } from '@travetto/boot';
import { PackageUtil, path } from '@travetto/manifest';

@@ -16,5 +15,13 @@ import { Import } from '../types/shared';

static optionalResolve(file: string, base?: string): string {
file = base ? pathResolve(base, file) : file;
if (base?.endsWith('.ts')) {
base = path.dirname(base);
}
if (base && file.startsWith('.')) {
return path.resolve(base, file);
// TODO: Replace with manifest reverse lookup
} else if (file.startsWith('@')) {
return path.resolve('node_modules', file);
}
try {
return require.resolve(file);
return PackageUtil.resolveImport(file);
} catch {

@@ -29,4 +36,4 @@ return file;

static collectImports(src: ts.SourceFile): Map<string, Import> {
const pth = require.resolve(src.fileName);
const base = PathUtil.toUnix(pth);
// TODO: Replace with manifest reverse lookup
const base = path.toPosix(src.fileName);

@@ -37,3 +44,3 @@ const imports = new Map<string, Import>();

if (ts.isImportDeclaration(stmt) && ts.isStringLiteral(stmt.moduleSpecifier)) {
const path = this.optionalResolve(stmt.moduleSpecifier.text, base);
const resolved = this.optionalResolve(stmt.moduleSpecifier.text, base);

@@ -44,6 +51,6 @@ if (stmt.importClause) {

if (ts.isNamespaceImport(bindings)) {
imports.set(bindings.name.text, { path, ident: bindings.name, stmt });
imports.set(bindings.name.text, { path: resolved, ident: bindings.name, stmt });
} else if (ts.isNamedImports(bindings)) {
for (const n of bindings.elements) {
imports.set(n.name.text, { path, ident: n.name, stmt });
imports.set(n.name.text, { path: resolved, ident: n.name, stmt });
}

@@ -50,0 +57,0 @@ }

@@ -1,2 +0,2 @@

import * as ts from 'typescript';
import ts from 'typescript';

@@ -51,3 +51,4 @@ /**

const pairs: ts.PropertyAssignment[] = [];
for (const k of Object.keys(ov)) {
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
for (const k of Object.keys(ov) as (keyof typeof ov)[]) {
if (ov[k] !== undefined) {

@@ -54,0 +55,0 @@ pairs.push(

@@ -1,3 +0,1 @@

import { Util } from '@travetto/base';
const exclude = new Set([

@@ -25,3 +23,3 @@ 'parent', 'checker', 'end', 'pos', 'id', 'source', 'sourceFile', 'getSourceFile',

static collapseNode(x: unknown, cache: Set<unknown> = new Set()): unknown {
if (!x || Util.isPrimitive(x)) {
if (!x || !(typeof x === 'object' || typeof x === 'function')) {
return x;

@@ -43,3 +41,3 @@ }

for (const key of Object.keys(ox)) {
if (Util.isFunction(ox[key]) || exclude.has(key) || ox[key] === undefined) {
if (Object.getPrototypeOf(ox[key]) === Function.prototype || exclude.has(key) || ox[key] === undefined) {
continue;

@@ -46,0 +44,0 @@ }

@@ -1,9 +0,4 @@

import * as ts from 'typescript';
import { createWriteStream } from 'fs';
import ts from 'typescript';
import { ConsoleManager } from '@travetto/base/src/console';
import { AppCache } from '@travetto/boot';
import { DecoratorMeta, TransformerType, NodeTransformer, TransformerSet, State, TransformPhase } from './types/visitor';
import { LogUtil } from './util/log';
import { CoreUtil } from './util/core';

@@ -31,3 +26,3 @@

return 'parameter';
} else if (ts.isFunctionDeclaration(node) || (ts.isFunctionExpression(node) && !ts.isArrowFunction(node))) {
} else if ((ts.isFunctionDeclaration(node) && node.body) || (ts.isFunctionExpression(node) && !ts.isArrowFunction(node))) {
return 'function';

@@ -38,2 +33,4 @@ } else if (ts.isGetAccessor(node)) {

return 'setter';
} else if (ts.isSourceFile(node)) {
return 'file';
}

@@ -43,12 +40,8 @@ }

#transformers = new Map<TransformerType, TransformerSet<S>>();
#logTarget: string;
#getState: (context: ts.TransformationContext, src: ts.SourceFile) => S;
#logger: Console | undefined;
constructor(
getState: (context: ts.TransformationContext, src: ts.SourceFile) => S,
transformers: NodeTransformer<S, TransformerType, ts.Node>[],
logTarget = 'compiler.log'
transformers: NodeTransformer<S, TransformerType, ts.Node>[]
) {
this.#logTarget = logTarget;
this.#getState = getState;

@@ -85,10 +78,2 @@ this.#init(transformers);

get logger(): Console {
this.#logger ??= new console.Console({
stdout: createWriteStream(AppCache.toEntryName(this.#logTarget), { flags: 'a' }),
inspectOptions: { depth: 4 },
});
return this.#logger;
}
/**

@@ -99,9 +84,7 @@ * Produce a visitor for a given a file

return (context: ts.TransformationContext) => (file: ts.SourceFile): ts.SourceFile => {
if (!file.fileName.endsWith('.ts')) { // Skip all non-ts files
return file;
}
try {
const c = this.logger;
ConsoleManager.set({
onLog: (level, ctx, args) => c[level](level, ctx, ...LogUtil.collapseNodes(args))
});
console.debug('Processing', { file: file.fileName, pid: process.pid });
const state = this.#getState(context, file);

@@ -114,3 +97,3 @@ let ret = this.visit(state, context, file);

while (state.added.size) {
for (const [k, all] of [...state.added]) {
for (const [k, all] of [...state.added].sort(([idxA], [idxB]) => idxB - idxA)) {
const idx = k === -1 ? state.added.size : k;

@@ -134,8 +117,6 @@ statements = [

}
console.error('Failed transforming', { error: `${err.message}\n${err.stack}`, file: file.fileName });
console!.error('Failed transforming', { error: `${err.message}\n${err.stack}`, file: file.fileName });
const out = new Error(`Failed transforming: ${file.fileName}: ${err.message}`);
out.stack = err.stack;
throw out;
} finally {
ConsoleManager.clear(); // Reset logging
}

@@ -142,0 +123,0 @@ };