Skip to content

Commit aec0a14

Browse files
committed
fix(rsc-mf): ignore type-only imports in callback bootstrap inference
1 parent 1259a7f commit aec0a14

3 files changed

Lines changed: 52 additions & 8 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import type * as RemoteActionsModule from './actions';
2+
3+
export type RemoteActionsModuleShape = typeof RemoteActionsModule;
4+
5+
export const TYPE_ONLY_ACTION_IMPORT_MARKER = 'type-only-action-import';

tests/integration/rsc-mf/remote/src/runtime/createRscExposeDefinitions.ts

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ const RSC_LAYER = 'react-server-components';
99
const LOCAL_MODULE_SPECIFIER_PATTERN = /^\.{1,2}\//;
1010
const SOURCE_DIRECTIVE_PATTERN = /^\s*['"]use (?:client|server)['"]\s*;?/m;
1111
const EXPORT_FROM_SPECIFIER_PATTERN =
12-
/export\s+(?:\*\s+from|\{[^}]*\}\s+from)\s*['"]([^'"]+)['"]/g;
12+
/export\s+(type\s+)?(?:\*\s+from|\{[^}]*\}\s+from)\s*['"]([^'"]+)['"]/g;
1313
const IMPORT_FROM_SPECIFIER_PATTERN =
14-
/import\s+(?:type\s+)?(?:[^'";]+?\s+from\s+)?['"]([^'"]+)['"]/g;
14+
/import\s+(type\s+)?(?:[^'";]+?\s+from\s+)?['"]([^'"]+)['"]/g;
1515
const DYNAMIC_IMPORT_SPECIFIER_PATTERN = /import\(\s*['"]([^'"]+)['"]\s*\)/g;
1616
const SOURCE_ENTRY_EXTENSIONS = [
1717
'.ts',
@@ -139,6 +139,11 @@ const shouldInjectCallbackBootstrap = (importPaths: string[]) =>
139139
referencesCallbackCapableSourceModule(importPath),
140140
);
141141

142+
interface SourceModuleSpecifier {
143+
moduleSpecifier: string;
144+
typeOnly: boolean;
145+
}
146+
142147
const readSourceFile = (filePath: string) => {
143148
try {
144149
return fs.readFileSync(filePath, 'utf8');
@@ -198,14 +203,33 @@ const referencesCallbackCapableSourceModule = (importPath: string) => {
198203
return true;
199204
}
200205

201-
const localSpecifierMatches = [
202-
...sourceText.matchAll(EXPORT_FROM_SPECIFIER_PATTERN),
203-
...sourceText.matchAll(IMPORT_FROM_SPECIFIER_PATTERN),
204-
...sourceText.matchAll(DYNAMIC_IMPORT_SPECIFIER_PATTERN),
206+
const localSpecifierMatches: SourceModuleSpecifier[] = [
207+
...Array.from(
208+
sourceText.matchAll(EXPORT_FROM_SPECIFIER_PATTERN),
209+
match => ({
210+
moduleSpecifier: match[2],
211+
typeOnly: Boolean(match[1]),
212+
}),
213+
),
214+
...Array.from(
215+
sourceText.matchAll(IMPORT_FROM_SPECIFIER_PATTERN),
216+
match => ({
217+
moduleSpecifier: match[2],
218+
typeOnly: Boolean(match[1]),
219+
}),
220+
),
221+
...Array.from(
222+
sourceText.matchAll(DYNAMIC_IMPORT_SPECIFIER_PATTERN),
223+
match => ({
224+
moduleSpecifier: match[1],
225+
typeOnly: false,
226+
}),
227+
),
205228
];
206-
for (const match of localSpecifierMatches) {
207-
const [moduleSpecifier] = match.slice(1);
229+
230+
for (const { moduleSpecifier, typeOnly } of localSpecifierMatches) {
208231
if (
232+
typeOnly ||
209233
typeof moduleSpecifier !== 'string' ||
210234
!LOCAL_MODULE_SPECIFIER_PATTERN.test(moduleSpecifier)
211235
) {

tests/integration/rsc-mf/tests/createRscExposeDefinitions.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,21 @@ describe('createRscExposeDefinitions', () => {
396396
});
397397
});
398398

399+
it('does not infer callback bootstrap from type-only local imports', () => {
400+
const { createRscExposeDefinitions } = loadCreateRscExposeDefinitions();
401+
expect(
402+
createRscExposeDefinitions({
403+
'./customTypeOnlyActionImport':
404+
'./src/components/typeOnlyActionImport.ts',
405+
}),
406+
).toEqual({
407+
'./customTypeOnlyActionImport': {
408+
import: ['./src/components/typeOnlyActionImport.ts'],
409+
layer: 'react-server-components',
410+
},
411+
});
412+
});
413+
399414
it('keeps non-callback source modules free of callback bootstrap import', () => {
400415
const { createRscExposeDefinitions } = loadCreateRscExposeDefinitions();
401416
expect(

0 commit comments

Comments
 (0)