Skip to content

Commit 6c5ccd5

Browse files
adrians5jCopilot
andauthored
fix(extensions): Importing-related Improvements (#4894)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: adrians5j <5121148+adrians5j@users.noreply.github.com>
1 parent ecee444 commit 6c5ccd5

File tree

2 files changed

+20
-15
lines changed

2 files changed

+20
-15
lines changed

packages/app-admin/src/extensions/AdminExtension.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ export const AdminExtension = defineExtension({
1111
multiple: true,
1212
paramsSchema: ({ project }) => {
1313
return z.object({
14-
src: zodSrcPath({ project })
14+
src: zodSrcPath({ project }),
15+
exportName: z.string().optional()
1516
});
1617
},
1718
async build(params, ctx) {
@@ -33,6 +34,11 @@ export const AdminExtension = defineExtension({
3334
absoluteExtensionFilePath = extensionFilePath;
3435
}
3536

37+
const extensionFileName = path.basename(absoluteExtensionFilePath);
38+
39+
// Export name can be customized or defaults to the file name without extension.
40+
const exportName = params.exportName ?? path.parse(extensionFileName).name;
41+
3642
// Generate a constant hash-based component name to avoid using timestamps.
3743
const hash = crypto.createHash("sha256").update(extensionFilePath).digest("hex");
3844
const componentName = `AdminExtension_${hash.slice(-10)}`;
@@ -41,7 +47,7 @@ export const AdminExtension = defineExtension({
4147

4248
const importPath = path
4349
.relative(path.dirname(extensionsTsxFilePath), absoluteExtensionFilePath)
44-
.replace(".tsx", ".js");
50+
.replace(/\.tsx?$/, ".js");
4551

4652
project.addSourceFileAtPath(extensionsTsxFilePath);
4753

@@ -66,15 +72,15 @@ export const AdminExtension = defineExtension({
6672
index = last.getChildIndex() + 1;
6773
}
6874

69-
// Import as default export if available, otherwise import named "Extension" export
75+
// Import as default export if available, otherwise import named export
7076
if (hasDefaultExport) {
7177
source.insertImportDeclaration(index, {
7278
defaultImport: componentName,
7379
moduleSpecifier: importPath
7480
});
7581
} else {
7682
source.insertImportDeclaration(index, {
77-
namedImports: [{ name: "Extension", alias: componentName }],
83+
namedImports: [{ name: exportName, alias: componentName }],
7884
moduleSpecifier: importPath
7985
});
8086
}

packages/project/src/defineExtension/defineApiExtension.ts

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -46,21 +46,17 @@ export const defineApiExtension = (params: DefineApiExtensionParams) =>
4646
const extensionFileName = path.basename(absoluteExtensionFilePath);
4747

4848
// 1. Export name is always the file name without extension.
49-
const exportName = params.exportName ?? extensionFileName.replace(".ts", "");
49+
const exportName = params.exportName ?? path.parse(extensionFileName).name;
5050

5151
// 2. Alias name is "ApiExtension_" + hash of the file path. This way we
5252
// avoid potential naming conflicts and keep the identifier constant.
5353
const hash = crypto.createHash("sha256").update(extensionFilePath).digest("hex");
5454
const exportNameAlias = `ApiExtension_${hash.slice(-10)}`;
5555

5656
// 3. Calculate import path relative to `extensions.ts` file.
57-
const importPath = [
58-
path.relative(
59-
path.dirname(extensionsTsFilePath),
60-
path.dirname(absoluteExtensionFilePath)
61-
),
62-
extensionFileName.replace(".ts", ".js")
63-
].join("/");
57+
const importPath = path
58+
.relative(path.dirname(extensionsTsFilePath), absoluteExtensionFilePath)
59+
.replace(/\.tsx?$/, ".js");
6460

6561
const project = new Project();
6662
project.addSourceFileAtPath(extensionsTsFilePath);
@@ -80,9 +76,12 @@ export const defineApiExtension = (params: DefineApiExtensionParams) =>
8076
index = last.getChildIndex() + 1;
8177
}
8278

83-
// Check if the file has a default export by importing it.
84-
const importedModule = await import(absoluteExtensionFilePath);
85-
const hasDefaultExport = "default" in importedModule;
79+
// Check if the file has a default export using AST parsing.
80+
const extensionProject = new Project();
81+
extensionProject.addSourceFileAtPath(absoluteExtensionFilePath);
82+
const extensionSource =
83+
extensionProject.getSourceFileOrThrow(absoluteExtensionFilePath);
84+
const hasDefaultExport = extensionSource.getDefaultExportSymbol() !== undefined;
8685

8786
// Support both default and named exports.
8887
if (hasDefaultExport) {

0 commit comments

Comments
 (0)