Skip to content

Commit

Permalink
prefix external runtime type names with __ɵΩ
Browse files Browse the repository at this point in the history
so that external type names don't clash with user defined type names when inlined
  • Loading branch information
marcus-sa committed Dec 6, 2023
1 parent fa184dd commit 58b1a8b
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 40 deletions.
63 changes: 44 additions & 19 deletions packages/type-compiler/src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,8 @@ export class ReflectionTransformer implements CustomTransformer {
protected reflectionMode?: typeof reflectionModes[number];
protected reflectionOptions?: ReflectionOptions;

protected isMaybeEmbeddingExternalLibraryImport: boolean = false;

/**
* Types added to this map will get a type program directly under it.
* This is for types used in the very same file.
Expand All @@ -545,6 +547,8 @@ export class ReflectionTransformer implements CustomTransformer {
{ name: EntityName, sourceFile: SourceFile, compiled?: Statement[] }
>();

protected externalRuntimeTypeNames = new Set<string>();

/**
* Types added to this map will get a type program at the top root level of the program.
* This is for imported types, which need to be inlined into the current file, as we do not emit type imports (TS will omit them).
Expand Down Expand Up @@ -1808,7 +1812,7 @@ export class ReflectionTransformer implements CustomTransformer {
if (isIdentifier(narrowed.exprName)) {
const resolved = this.resolveDeclaration(narrowed.exprName);
if (resolved && findSourceFile(resolved.declaration) !== this.sourceFile && resolved.importDeclaration) {
expression = this.resolveImport(resolved.declaration, resolved.importDeclaration, narrowed.exprName, expression, program);
expression = this.resolveImportExpression(resolved.declaration, resolved.importDeclaration, narrowed.exprName, expression, program);
}
}
program.pushOp(ReflectionOp.typeof, program.pushStack(this.f.createArrowFunction(undefined, undefined, [], undefined, undefined, expression)));
Expand Down Expand Up @@ -2005,44 +2009,67 @@ export class ReflectionTransformer implements CustomTransformer {
// return node;
// }

public getDeclarationVariableName(typeName: EntityName): Identifier {
protected getRuntimeTypeName(typeName: string): string {
if (this.externalRuntimeTypeNames.has(typeName)) {
return `__ɵΩ${typeName}`;
}
return `__Ω${typeName}`;
}

protected getDeclarationVariableName(typeName: EntityName): Identifier {
if (isIdentifier(typeName)) {
return this.f.createIdentifier('__Ω' + getIdentifierName(typeName));
return this.f.createIdentifier(this.getRuntimeTypeName(getIdentifierName(typeName)));
}

function joinQualifiedName(name: EntityName): string {
if (isIdentifier(name)) return getIdentifierName(name);
return joinQualifiedName(name.left) + '_' + getIdentifierName(name.right);
}

return this.f.createIdentifier('__Ω' + joinQualifiedName(typeName));
return this.f.createIdentifier(this.getRuntimeTypeName(joinQualifiedName(typeName)));
}

protected resolveImport<T extends Expression>(declaration: Node, importDeclaration: ImportDeclaration, typeName: Identifier, expression: T, program: CompilerProgram): CallExpression | T {
protected addExternalLibraryImportEmbedDeclaration(declaration: Node, sourceFile: SourceFile, typeName: EntityName): void {
this.isMaybeEmbeddingExternalLibraryImport = true;

this.externalRuntimeTypeNames.add(getEntityName(typeName));

this.embedDeclarations.set(declaration, {
name: typeName,
sourceFile,
});
}

protected resolveImportExpression<T extends Expression>(declaration: Node, importDeclaration: ImportDeclaration, typeName: Identifier, expression: T, program: CompilerProgram): CallExpression | T {
ensureImportIsEmitted(importDeclaration, typeName);

if (isTypeAliasDeclaration(declaration)) return expression;
if (isTypeAliasDeclaration(declaration)) {
this.isMaybeEmbeddingExternalLibraryImport = false;
return expression;
}

// check if the referenced declaration has reflection disabled
const declarationReflection = this.findReflectionConfig(declaration, program);
if (declarationReflection.mode !== 'never') {
const declarationSourceFile = importDeclaration.getSourceFile();

if (this.isMaybeEmbeddingExternalLibraryImport) {
this.addExternalLibraryImportEmbedDeclaration(declaration, declarationSourceFile, typeName);
return this.wrapWithAssignType(expression, this.getDeclarationVariableName(typeName));
}

const runtimeTypeName = this.getDeclarationVariableName(typeName);

const builtType = isBuiltType(runtimeTypeName, declarationSourceFile);

if (!builtType && this.shouldInlineExternalLibraryImport(importDeclaration, typeName, declarationReflection)) {
this.embedDeclarations.set(declaration, {
name: typeName,
sourceFile: declarationSourceFile,
});
// if (!isTypeAliasDeclaration(declaration)) {
return this.wrapWithAssignType(expression, runtimeTypeName);
// }
this.addExternalLibraryImportEmbedDeclaration(declaration, declarationSourceFile, typeName);
return this.wrapWithAssignType(expression, this.getDeclarationVariableName(typeName));
}
}

this.isMaybeEmbeddingExternalLibraryImport = false;

return expression;
}

Expand Down Expand Up @@ -2166,7 +2193,7 @@ export class ReflectionTransformer implements CustomTransformer {
if (isModuleDeclaration(declaration) && resolved.importDeclaration) {
let expression: SerializedEntityNameAsExpression | CallExpression = serializeEntityNameAsExpression(this.f, typeName);
if (isIdentifier(typeName)) {
expression = this.resolveImport(declaration, resolved.importDeclaration, typeName, expression, program)
expression = this.resolveImportExpression(declaration, resolved.importDeclaration, typeName, expression, program)
}

//we can not infer from module declaration, so do `typeof T` in runtime
Expand Down Expand Up @@ -2257,11 +2284,9 @@ export class ReflectionTransformer implements CustomTransformer {
const builtType = isBuiltType(runtimeTypeName, found);
if (!builtType) {
if (!this.shouldInlineExternalLibraryImport(resolved.importDeclaration, typeName, declarationReflection)) return;
this.embedDeclarations.set(declaration, {
name: typeName,
sourceFile: declarationSourceFile
});
this.addExternalLibraryImportEmbedDeclaration(declaration, declarationSourceFile, typeName);
} else {
this.isMaybeEmbeddingExternalLibraryImport = false;
//check if the referenced file has reflection info emitted. if not, any is emitted for that reference
const reflection = this.findReflectionFromPath(found.fileName);
if (reflection.mode === 'never') {
Expand Down Expand Up @@ -2323,7 +2348,7 @@ export class ReflectionTransformer implements CustomTransformer {

let body: Identifier | PropertyAccessExpression | CallExpression = isIdentifier(typeName) ? typeName : this.createAccessorForEntityName(typeName);
if (resolved.importDeclaration && isIdentifier(typeName)) {
body = this.resolveImport(resolved.declaration, resolved.importDeclaration, typeName, body, program);
body = this.resolveImportExpression(resolved.declaration, resolved.importDeclaration, typeName, body, program);
}
program.pushFrame();
if (type.typeArguments) {
Expand Down
64 changes: 43 additions & 21 deletions packages/type-compiler/tests/inline-external-imports.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -300,25 +300,26 @@ test('class type var', () => {
},
});

// TODO: how to resolve the typeof imports? -> __assignType(Subscriber_1.Subscriber, __ΩSubscriber)
// "use strict";
// Object.defineProperty(exports, "__esModule", { value: true });
// exports.__ΩUnsubscribable = exports.__ΩSubscribable = exports.__ΩSubscription = exports.__ΩObserver = exports.__ΩTeardownLogic = exports.__ΩSubscriber = exports.__ΩOperator = exports.__ΩObservable = void 0;
// const __ΩObservable = ['T', () => Observable, 'source', () => __ΩOperator, 'operator', () => Observable, 'this', () => __assignType(Subscriber_1.Subscriber, __ΩSubscriber), 'subscriber', () => __ΩTeardownLogic, '', 'subscribe', 'constructor', 'args', 'create', () => __ΩOperator, () => Observable, 'lift', () => __ΩPartial, () => __ΩObserver, 'observer', () => __assignType(Subscription_1.Subscription, __ΩSubscription), 'value', 'next', 'forEach', () => Observable, 'pipe', 'toPromise', () => __ΩSubscribable, 'Observable', 'b!PP"7"-J3#P"e"!o$#-J3%PPPe$!7&2\'Pe$!7(2)n*/+2,8"0-P"@2."/+3/sPe"!"o0#2%8P"7102Pe"!o4"o3"258P760,PPe#!27$/+28$`09PPe#!7:0;PPe#!-J`0<5e!!o="x"w>y'];
// exports.__ΩObservable = __ΩObservable;
// const __ΩOperator = ['T', 'R', () => Subscriber_2.Subscriber, 'subscriber', 'source', () => __ΩTeardownLogic, 'call', 'b!b"PPPe$"7#2$"2%n&1\'My'];
// exports.__ΩOperator = __ΩOperator;
// const __ΩSubscriber = ['T', () => Subscription_2.Subscription, 'x', '', 'next', 'e', 'error', 'complete', () => Subscriber, 'create', 'isStopped', () => Subscriber, () => __ΩObserver, 'destination', () => Subscriber, () => __ΩObserver, 'constructor', 'value', 'err', 'unsubscribe', '_next', '_error', '_complete', () => __ΩObserver, 'Subscriber', 'b!P7"PPe#!2#8$/$2%8P"2&8$/$2\'8P$/$2(8Pe#!7)0*s)3+<PP"7,"o-"J3.<PPP"7/"o0"J2.8"01Pe"!228$0%P"238$0\'P$0(P$04Pe"!22$05<P"23$06<P$07<5e!!o8"x"w9y'];
// exports.__ΩSubscriber = __ΩSubscriber;
// const __ΩTeardownLogic = [() => Subscription_3.Subscription, () => __ΩUnsubscribable, '', 'PP7!n"P$/#$Jy'];
// exports.__ΩTeardownLogic = __ΩTeardownLogic;
console.log(res.app);

// TODO: how to resolve the typeof imports? -> __assignType(Subscriber_1.Subscriber, __ɵΩSubscriber)
//
// exports.__ΩUnsubscribable = exports.__ɵΩSubscribable = exports.__ɵΩSubscription = exports.__ɵΩObserver = exports.__ɵΩTeardownLogic = exports.__ɵΩSubscriber = exports.__ɵΩOperator = exports.__ɵΩObservable = void 0;
// const __ɵΩObservable = ['T', () => Observable, 'source', () => __ΩOperator, 'operator', () => Observable, 'this', () => __assignType(Subscriber_1.Subscriber, __ɵΩSubscriber), 'subscriber', () => __ΩTeardownLogic, '', 'subscribe', 'constructor', 'args', 'create', () => __ɵΩOperator, () => Observable, 'lift', () => __ΩPartial, () => __ΩObserver, 'observer', () => __assignType(Subscription_1.Subscription, __ɵΩSubscription), 'value', 'next', 'forEach', () => Observable, 'pipe', 'toPromise', () => __ΩSubscribable, 'Observable', 'b!PP"7"-J3#P"e"!o$#-J3%PPPe$!7&2\'Pe$!7(2)n*/+2,8"0-P"@2."/+3/sPe"!"o0#2%8P"7102Pe"!o4"o3"258P760,PPe#!27$/+28$`09PPe#!7:0;PPe#!-J`0<5e!!o="x"w>y'];
// exports.__ɵΩObservable = __ɵΩObservable;
// const __ɵΩOperator = ['T', 'R', () => Subscriber_2.Subscriber, 'subscriber', 'source', () => __ɵΩTeardownLogic, 'call', 'b!b"PPPe$"7#2$"2%n&1\'My'];
// exports.__ɵΩOperator = __ɵΩOperator;
// const __ɵΩSubscriber = ['T', () => Subscription_2.Subscription, 'x', '', 'next', 'e', 'error', 'complete', () => Subscriber, 'create', 'isStopped', () => Subscriber, () => __ɵΩObserver, 'destination', () => Subscriber, () => __ɵΩObserver, 'constructor', 'value', 'err', 'unsubscribe', '_next', '_error', '_complete', () => __ɵΩObserver, 'Subscriber', 'b!P7"PPe#!2#8$/$2%8P"2&8$/$2\'8P$/$2(8Pe#!7)0*s)3+<PP"7,"o-"J3.<PPP"7/"o0"J2.8"01Pe"!228$0%P"238$0\'P$0(P$04Pe"!22$05<P"23$06<P$07<5e!!o8"x"w9y'];
// exports.__ɵΩSubscriber = __ɵΩSubscriber;
// const __ɵΩTeardownLogic = [() => Subscription_3.Subscription, () => __ΩUnsubscribable, '', 'PP7!n"P$/#$Jy'];
// exports.__ɵΩTeardownLogic = __ɵΩTeardownLogic;
// const __ΩPartial = ['T', 'l+e#!e"!fRb!Pde"!gN#"y'];
// const __ΩObserver = ['T', 'value', '', 'next', 'err', 'error', 'complete', 'b!PPe#!2"$/#4$P"2%$/#4&P$/#4\'My'];
// exports.__ΩObserver = __ΩObserver;
// const __ΩSubscription = [() => Subscription, 'EMPTY', 'closed', '', 'initialTeardown', 'constructor', 'unsubscribe', () => __ΩTeardownLogic, 'teardown', 'add', () => __ΩExclude, () => __ΩTeardownLogic, 'remove', 'Subscription', 'P7!3"s)3#PPP$/$-J2%8"0&P$0\'Pn(2)$0*Pn,$o+#2)$0-5x"w.y'];
// exports.__ΩSubscription = __ΩSubscription;
// const __ΩSubscribable = ['T', () => __ΩPartial, () => __ΩObserver, 'observer', () => __ΩUnsubscribable, 'subscribe', 'b!PPe#!o#"o""2$n%1&My'];
// exports.__ΩSubscribable = __ΩSubscribable;
// const __ɵΩObserver = ['T', 'value', '', 'next', 'err', 'error', 'complete', 'b!PPe#!2"$/#4$P"2%$/#4&P$/#4\'My'];
// exports.__ɵΩObserver = __ɵΩObserver;
// const __ɵΩSubscription = [() => Subscription, 'EMPTY', 'closed', '', 'initialTeardown', 'constructor', 'unsubscribe', () => __ɵΩTeardownLogic, 'teardown', 'add', () => __ΩExclude, () => __ɵΩTeardownLogic, 'remove', 'Subscription', 'P7!3"s)3#PPP$/$-J2%8"0&P$0\'Pn(2)$0*Pn,$o+#2)$0-5x"w.y'];
// exports.__ɵΩSubscription = __ɵΩSubscription;
// const __ɵΩSubscribable = ['T', () => __ΩPartial, () => __ɵΩObserver, 'observer', () => __ΩUnsubscribable, 'subscribe', 'b!PPe#!o#"o""2$n%1&My'];
// exports.__ɵΩSubscribable = __ɵΩSubscribable;
// const __ΩUnsubscribable = ['unsubscribe', 'PP$1!My'];
// exports.__ΩUnsubscribable = __ΩUnsubscribable;
// const __ΩExclude = ['T', 'U', 'l6!Re$!RPe#!e$"qk#%QRb!b"Pde"!p)y'];
Expand All @@ -327,11 +328,32 @@ test('class type var', () => {
// return fn;
// }
// const rxjs_1 = require("rxjs");
// const __ΩA = [() => __assignType(rxjs_1.Observable, __ΩObservable), 'P#7!y'];
// const __ΩA = [() => __assignType(rxjs_1.Observable, __ɵΩObservable), 'P#7!y'];

expect(res.app).toContain('const __ΩObservable = [');
expect(res.app).toContain('() => __assignType(rxjs_1.Observable, __ΩObservable)');
expect(res.app).toContain('const __ɵΩObservable = [');
expect(res.app).toContain('() => __assignType(rxjs_1.Observable, __ɵΩObservable)');
// FIXME: why isn't it being prefixed with `ɵ`?
expect(res.app).toContain('const __ɵΩUnsubscribable = [');
})

test('runtime type name clashing', () => {
const res = transpile({
app: `import { Observable } from 'rxjs';
type Subscribable = any;
type A = Observable<unknown>;
`
}, undefined, {
inlineExternalLibraryImports: {
'rxjs': ['Observable'],
},
});

expect(res.app).toContain('const __ɵΩSubscribable = [');
expect(res.app).toContain('const __ΩSubscribable = [');
})

test('class typeOf', () => {
const res = transpileAndRun({
app: `import { Observable } from 'rxjs';
Expand Down

0 comments on commit 58b1a8b

Please sign in to comment.