Skip to content

Commit cb6c64c

Browse files
committed
cherry-pick(#28986): fix(ct): move import list into the compilation cache data
1 parent 06518b2 commit cb6c64c

File tree

9 files changed

+304
-227
lines changed

9 files changed

+304
-227
lines changed

packages/playwright-ct-core/src/tsxTransform.ts

Lines changed: 40 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@ import path from 'path';
1818
import type { T, BabelAPI, PluginObj } from 'playwright/src/transform/babelBundle';
1919
import { types, declare, traverse } from 'playwright/lib/transform/babelBundle';
2020
import { resolveImportSpecifierExtension } from 'playwright/lib/util';
21+
import { setTransformData } from 'playwright/lib/transform/transform';
2122
const t: typeof T = types;
2223

23-
let componentNames: Set<string>;
24-
let componentImports: Map<string, ImportInfo>;
24+
let jsxComponentNames: Set<string>;
25+
let importInfos: Map<string, ImportInfo>;
2526

2627
export default declare((api: BabelAPI) => {
2728
api.assertVersion(7);
@@ -31,9 +32,8 @@ export default declare((api: BabelAPI) => {
3132
visitor: {
3233
Program: {
3334
enter(path) {
34-
const result = collectComponentUsages(path.node);
35-
componentNames = result.names;
36-
componentImports = new Map();
35+
jsxComponentNames = collectJsxComponentUsages(path.node);
36+
importInfos = new Map();
3737
},
3838
exit(path) {
3939
let firstDeclaration: any;
@@ -47,13 +47,14 @@ export default declare((api: BabelAPI) => {
4747
const insertionPath = lastImportDeclaration || firstDeclaration;
4848
if (!insertionPath)
4949
return;
50-
for (const componentImport of [...componentImports.values()].reverse()) {
50+
51+
for (const [localName, componentImport] of [...importInfos.entries()].reverse()) {
5152
insertionPath.insertAfter(
5253
t.variableDeclaration(
5354
'const',
5455
[
5556
t.variableDeclarator(
56-
t.identifier(componentImport.localName),
57+
t.identifier(localName),
5758
t.objectExpression([
5859
t.objectProperty(t.identifier('__pw_type'), t.stringLiteral('importRef')),
5960
t.objectProperty(t.identifier('id'), t.stringLiteral(componentImport.id)),
@@ -63,6 +64,7 @@ export default declare((api: BabelAPI) => {
6364
)
6465
);
6566
}
67+
setTransformData('playwright-ct-core', [...importInfos.values()]);
6668
}
6769
},
6870

@@ -71,19 +73,35 @@ export default declare((api: BabelAPI) => {
7173
if (!t.isStringLiteral(importNode.source))
7274
return;
7375

74-
let components = 0;
76+
const ext = path.extname(importNode.source.value);
77+
78+
// Convert all non-JS imports into refs.
79+
if (!allJsExtensions.has(ext)) {
80+
for (const specifier of importNode.specifiers) {
81+
if (t.isImportNamespaceSpecifier(specifier))
82+
continue;
83+
const { localName, info } = importInfo(importNode, specifier, this.filename!);
84+
importInfos.set(localName, info);
85+
}
86+
p.skip();
87+
p.remove();
88+
return;
89+
}
90+
91+
// Convert JS imports that are used as components in JSX expressions into refs.
92+
let importCount = 0;
7593
for (const specifier of importNode.specifiers) {
7694
if (t.isImportNamespaceSpecifier(specifier))
7795
continue;
78-
const info = importInfo(importNode, specifier, this.filename!);
79-
if (!componentNames.has(info.localName))
80-
continue;
81-
componentImports.set(info.localName, info);
82-
++components;
96+
const { localName, info } = importInfo(importNode, specifier, this.filename!);
97+
if (jsxComponentNames.has(localName)) {
98+
importInfos.set(localName, info);
99+
++importCount;
100+
}
83101
}
84102

85-
// All the imports were components => delete.
86-
if (components && components === importNode.specifiers.length) {
103+
// All the imports were from JSX => delete.
104+
if (importCount && importCount === importNode.specifiers.length) {
87105
p.skip();
88106
p.remove();
89107
}
@@ -92,7 +110,7 @@ export default declare((api: BabelAPI) => {
92110
MemberExpression(path) {
93111
if (!t.isIdentifier(path.node.object))
94112
return;
95-
if (!componentImports.has(path.node.object.name))
113+
if (!importInfos.has(path.node.object.name))
96114
return;
97115
if (!t.isIdentifier(path.node.property))
98116
return;
@@ -108,56 +126,30 @@ export default declare((api: BabelAPI) => {
108126
return result;
109127
});
110128

111-
export function collectComponentUsages(node: T.Node) {
112-
const importedLocalNames = new Set<string>();
129+
function collectJsxComponentUsages(node: T.Node): Set<string> {
113130
const names = new Set<string>();
114131
traverse(node, {
115132
enter: p => {
116-
117-
// First look at all the imports.
118-
if (t.isImportDeclaration(p.node)) {
119-
const importNode = p.node;
120-
if (!t.isStringLiteral(importNode.source))
121-
return;
122-
123-
for (const specifier of importNode.specifiers) {
124-
if (t.isImportNamespaceSpecifier(specifier))
125-
continue;
126-
importedLocalNames.add(specifier.local.name);
127-
}
128-
}
129-
130133
// Treat JSX-everything as component usages.
131134
if (t.isJSXElement(p.node)) {
132135
if (t.isJSXIdentifier(p.node.openingElement.name))
133136
names.add(p.node.openingElement.name.name);
134137
if (t.isJSXMemberExpression(p.node.openingElement.name) && t.isJSXIdentifier(p.node.openingElement.name.object) && t.isJSXIdentifier(p.node.openingElement.name.property))
135138
names.add(p.node.openingElement.name.object.name);
136139
}
137-
138-
// Treat mount(identifier, ...) as component usage if it is in the importedLocalNames list.
139-
if (t.isAwaitExpression(p.node) && t.isCallExpression(p.node.argument) && t.isIdentifier(p.node.argument.callee) && p.node.argument.callee.name === 'mount') {
140-
const callExpression = p.node.argument;
141-
const arg = callExpression.arguments[0];
142-
if (!t.isIdentifier(arg) || !importedLocalNames.has(arg.name))
143-
return;
144-
145-
names.add(arg.name);
146-
}
147140
}
148141
});
149-
return { names };
142+
return names;
150143
}
151144

152145
export type ImportInfo = {
153146
id: string;
154147
isModuleOrAlias: boolean;
155148
importPath: string;
156-
localName: string;
157149
remoteName: string | undefined;
158150
};
159151

160-
export function importInfo(importNode: T.ImportDeclaration, specifier: T.ImportSpecifier | T.ImportDefaultSpecifier, filename: string): ImportInfo {
152+
export function importInfo(importNode: T.ImportDeclaration, specifier: T.ImportSpecifier | T.ImportDefaultSpecifier, filename: string): { localName: string, info: ImportInfo } {
161153
const importSource = importNode.source.value;
162154
const isModuleOrAlias = !importSource.startsWith('.');
163155
const unresolvedImportPath = path.resolve(path.dirname(filename), importSource);
@@ -171,7 +163,6 @@ export function importInfo(importNode: T.ImportDeclaration, specifier: T.ImportS
171163
id: idPrefix,
172164
importPath,
173165
isModuleOrAlias,
174-
localName: specifier.local.name,
175166
remoteName: undefined,
176167
};
177168

@@ -184,5 +175,7 @@ export function importInfo(importNode: T.ImportDeclaration, specifier: T.ImportS
184175

185176
if (result.remoteName)
186177
result.id += '_' + result.remoteName;
187-
return result;
178+
return { localName: specifier.local.name, info: result };
188179
}
180+
181+
const allJsExtensions = new Set(['.js', '.jsx', '.cjs', '.mjs', '.ts', '.tsx', '.cts', '.mts', '']);

0 commit comments

Comments
 (0)