Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions packages/webpack5/__tests__/transformers/native-class.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,48 @@ class X extends NSObject {}
});
});

describe('after a preceding transformer updates the source file', () => {
// Simulates transformers like Angular's Ivy transform, which rebuild the
// source file via factory.updateSourceFile() on files containing Angular
// decorators. The updated SourceFile node is flagged Synthesized; the
// NativeClass transformer must still process its original statements.
const updateSourceFileTransformer: ts.TransformerFactory<ts.SourceFile> =
(context) => (sourceFile) =>
context.factory.updateSourceFile(sourceFile, [
...sourceFile.statements,
]);

function transformAfterUpdate(input: string): string {
return ts.transpileModule(input, {
compilerOptions: {
module: ts.ModuleKind.ESNext,
target: ts.ScriptTarget.ES2022,
experimentalDecorators: true,
emitDecoratorMetadata: false,
useDefineForClassFields: false,
},
transformers: {
before: [
updateSourceFileTransformer,
nativeClassTransformer as ts.TransformerFactory<ts.SourceFile>,
],
},
}).outputText;
}

it('downlevels @NativeClass() on a synthesized (updated) source file', () => {
const output = transformAfterUpdate(`
@NativeClass()
class Foo extends NSObject {}
`);

expect(output).toContain('var Foo =');
expect(output).toContain('__extends(Foo, _super)');
expect(output).not.toContain('NativeClass');
expect(countClassDeclarations(output)).toBe(0);
});
});

describe('nested scopes', () => {
it('downlevels @NativeClass() class declared inside a function body', () => {
const output = transform(`
Expand Down
14 changes: 10 additions & 4 deletions packages/webpack5/src/transformers/NativeClass/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,14 +182,20 @@ export default function (context: ts.TransformationContext, ...args) {
}

function visitNode(node: ts.Node): ts.Node {
// Do not traverse synthesized helper trees; leave them intact
if (((node as MutableNode).flags ?? 0) & ts.NodeFlags.Synthesized) {
return node;
}
// Handle the source file before the Synthesized bail-out below: a source
// file updated by an earlier transformer (e.g. Angular's Ivy transform on
// files containing Angular-decorated classes) is itself flagged
// Synthesized, but its original statements still need processing. The
// per-statement Synthesized check in transformStatements protects any
// generated statements.
if (ts.isSourceFile(node)) {
const [stmts, changed] = transformStatements(node.statements, true);
return changed ? factory.updateSourceFile(node, stmts) : node;
}
// Do not traverse synthesized helper trees; leave them intact
if (((node as MutableNode).flags ?? 0) & ts.NodeFlags.Synthesized) {
return node;
}
if (ts.isBlock(node)) {
const [stmts, changed] = transformStatements(node.statements, false);
return changed ? factory.updateBlock(node, stmts) : node;
Expand Down
Loading