rwsdk 0.1.0-alpha.9 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/dist/runtime/client.d.ts +3 -1
  2. package/dist/runtime/client.js +15 -11
  3. package/dist/runtime/clientNavigation.d.ts +3 -0
  4. package/dist/runtime/clientNavigation.js +43 -0
  5. package/dist/runtime/entries/client.d.ts +1 -0
  6. package/dist/runtime/entries/client.js +1 -0
  7. package/dist/runtime/entries/worker.d.ts +2 -0
  8. package/dist/runtime/entries/worker.js +2 -0
  9. package/dist/runtime/imports/ClientOnly.d.ts +3 -0
  10. package/dist/runtime/imports/ClientOnly.js +8 -0
  11. package/dist/runtime/imports/NoSSRStub.d.ts +1 -0
  12. package/dist/runtime/imports/NoSSRStub.js +4 -0
  13. package/dist/runtime/imports/client.js +15 -2
  14. package/dist/runtime/imports/worker.d.ts +1 -1
  15. package/dist/runtime/imports/worker.js +7 -5
  16. package/dist/runtime/lib/db/DOWorkerDialect.d.ts +29 -0
  17. package/dist/runtime/lib/db/DOWorkerDialect.js +66 -0
  18. package/dist/runtime/lib/db/SqliteDurableObject.d.ts +14 -0
  19. package/dist/runtime/lib/db/SqliteDurableObject.js +42 -0
  20. package/dist/runtime/lib/db/create.d.ts +3 -0
  21. package/dist/runtime/lib/db/create.js +36 -0
  22. package/dist/runtime/lib/db/createDb.d.ts +2 -0
  23. package/dist/runtime/lib/db/createDb.js +33 -0
  24. package/dist/runtime/lib/db/index.d.ts +3 -0
  25. package/dist/runtime/lib/db/index.js +3 -0
  26. package/dist/runtime/lib/db/logger.d.ts +2 -0
  27. package/dist/runtime/lib/db/logger.js +41 -0
  28. package/dist/runtime/lib/db/migrations.d.ts +23 -0
  29. package/dist/runtime/lib/db/migrations.js +34 -0
  30. package/dist/runtime/lib/db/types.d.ts +0 -0
  31. package/dist/runtime/lib/db/types.js +1 -0
  32. package/dist/runtime/lib/debug.d.ts +2 -0
  33. package/dist/runtime/lib/debug.js +36 -0
  34. package/dist/runtime/lib/router.d.ts +6 -1
  35. package/dist/runtime/lib/router.js +9 -2
  36. package/dist/runtime/register/ssr.d.ts +2 -0
  37. package/dist/runtime/register/ssr.js +14 -1
  38. package/dist/runtime/register/worker.d.ts +1 -1
  39. package/dist/runtime/register/worker.js +5 -2
  40. package/dist/runtime/render/renderRscThenableToHtmlStream.d.ts +2 -1
  41. package/dist/runtime/render/renderRscThenableToHtmlStream.js +17 -3
  42. package/dist/runtime/render/renderToStream.d.ts +9 -0
  43. package/dist/runtime/render/renderToStream.js +26 -0
  44. package/dist/runtime/render/renderToString.d.ts +7 -0
  45. package/dist/runtime/render/renderToString.js +26 -0
  46. package/dist/runtime/render/transformRscToHtmlStream.js +1 -0
  47. package/dist/runtime/worker.js +17 -10
  48. package/dist/scripts/debug-sync.mjs +8 -6
  49. package/dist/scripts/worker-run.mjs +1 -0
  50. package/dist/vite/configPlugin.d.mts +2 -2
  51. package/dist/vite/configPlugin.mjs +10 -21
  52. package/dist/vite/createDirectiveLookupPlugin.d.mts +1 -0
  53. package/dist/vite/createDirectiveLookupPlugin.mjs +88 -57
  54. package/dist/vite/devServerTimingPlugin.d.mts +2 -0
  55. package/dist/vite/devServerTimingPlugin.mjs +24 -0
  56. package/dist/vite/directivesPlugin.mjs +168 -70
  57. package/dist/vite/findImportSpecifiers.d.mts +16 -0
  58. package/dist/vite/findImportSpecifiers.mjs +152 -0
  59. package/dist/vite/findImportSpecifiers.test.d.mts +1 -0
  60. package/dist/vite/findImportSpecifiers.test.mjs +73 -0
  61. package/dist/vite/findSpecifiers.d.mts +31 -0
  62. package/dist/vite/findSpecifiers.mjs +230 -0
  63. package/dist/vite/hasDirective.d.mts +7 -0
  64. package/dist/vite/hasDirective.mjs +54 -0
  65. package/dist/vite/hasOwnCloudflareVitePlugin.d.mts +3 -0
  66. package/dist/vite/hasOwnCloudflareVitePlugin.mjs +14 -0
  67. package/dist/vite/injectVitePreamblePlugin.d.mts +2 -2
  68. package/dist/vite/injectVitePreamblePlugin.mjs +5 -2
  69. package/dist/vite/invalidateModule.d.mts +2 -0
  70. package/dist/vite/invalidateModule.mjs +14 -0
  71. package/dist/vite/miniflareHMRPlugin.d.mts +8 -0
  72. package/dist/vite/miniflareHMRPlugin.mjs +133 -0
  73. package/dist/vite/normalizeModulePath.mjs +12 -1
  74. package/dist/vite/reactConditionsResolverPlugin.d.mts +1 -1
  75. package/dist/vite/reactConditionsResolverPlugin.mjs +64 -59
  76. package/dist/vite/redwoodPlugin.d.mts +2 -1
  77. package/dist/vite/redwoodPlugin.mjs +24 -7
  78. package/dist/vite/resolveModuleId.d.mts +6 -0
  79. package/dist/vite/resolveModuleId.mjs +14 -0
  80. package/dist/vite/ssrBridgePlugin.d.mts +5 -1
  81. package/dist/vite/ssrBridgePlugin.mjs +4 -43
  82. package/dist/vite/transformClientComponents.d.mts +1 -0
  83. package/dist/vite/transformClientComponents.mjs +61 -125
  84. package/dist/vite/transformJsxScriptTagsPlugin.mjs +14 -3
  85. package/dist/vite/transformServerFunctions.d.mts +11 -3
  86. package/dist/vite/transformServerFunctions.mjs +256 -171
  87. package/dist/vite/transformServerFunctions.test.mjs +22 -3
  88. package/dist/vite/useClientLookupPlugin.mjs +1 -0
  89. package/dist/vite/useServerLookupPlugin.mjs +1 -0
  90. package/dist/vite/useServerPlugin.d.mts +1 -1
  91. package/dist/vite/useServerPlugin.mjs +1 -1
  92. package/package.json +14 -3
@@ -0,0 +1,24 @@
1
+ import debug from "debug";
2
+ const log = debug("rwsdk:vite:dev-server-timing-plugin");
3
+ export const devServerTimingPlugin = () => {
4
+ const startTime = Date.now();
5
+ let hasLoggedFirstResponse = false;
6
+ return {
7
+ name: "rwsdk:dev-server-timing",
8
+ configureServer(server) {
9
+ server.middlewares.use((_req, res, next) => {
10
+ if (!hasLoggedFirstResponse) {
11
+ res.on("finish", () => {
12
+ if (!hasLoggedFirstResponse) {
13
+ hasLoggedFirstResponse = true;
14
+ const endTime = Date.now();
15
+ const duration = endTime - startTime;
16
+ log(`🚀 Dev server first response completed in ${duration}ms`);
17
+ }
18
+ });
19
+ }
20
+ next();
21
+ });
22
+ },
23
+ };
24
+ };
@@ -4,6 +4,7 @@ import debug from "debug";
4
4
  import { transformClientComponents } from "./transformClientComponents.mjs";
5
5
  import { transformServerFunctions } from "./transformServerFunctions.mjs";
6
6
  import { normalizeModulePath } from "./normalizeModulePath.mjs";
7
+ import { invalidateModule } from "./invalidateModule.mjs";
7
8
  const log = debug("rwsdk:vite:rsc-directives-plugin");
8
9
  const verboseLog = debug("verbose:rwsdk:vite:rsc-directives-plugin");
9
10
  const getLoader = (filePath) => {
@@ -26,75 +27,172 @@ const getLoader = (filePath) => {
26
27
  return "js";
27
28
  }
28
29
  };
29
- export const directivesPlugin = ({ projectRootDir, clientFiles, serverFiles, }) => ({
30
- name: "rwsdk:rsc-directives",
31
- async transform(code, id) {
32
- verboseLog("Transform called for id=%s, environment=%s", id, this.environment.name);
33
- const normalizedId = normalizeModulePath(projectRootDir, id);
34
- const clientResult = await transformClientComponents(code, normalizedId, {
35
- environmentName: this.environment.name,
36
- clientFiles,
37
- });
38
- if (clientResult) {
39
- log("Client component transformation successful for id=%s", id);
40
- return {
41
- code: clientResult.code,
42
- map: clientResult.map,
43
- };
30
+ export const directivesPlugin = ({ projectRootDir, clientFiles, serverFiles, }) => {
31
+ let devServer;
32
+ let isAfterFirstResponse = false;
33
+ const addModule = (kind, environment, id) => {
34
+ const files = kind === "client" ? clientFiles : serverFiles;
35
+ const rawId = id.split("?")[0];
36
+ const resolvedId = rawId;
37
+ const relativePath = rawId.slice("/".length);
38
+ const fullPath = path.resolve(projectRootDir, relativePath);
39
+ const isNodeModule = id.includes("node_modules");
40
+ const hadFile = files.has(id);
41
+ log("Adding %s module to %s and invalidating cache: id=%s", kind, files, resolvedId);
42
+ files.add(resolvedId);
43
+ if (devServer && isNodeModule) {
44
+ const lookupModule = kind === "client"
45
+ ? "virtual:use-client-lookup"
46
+ : "virtual:use-server-lookup";
47
+ log("Registering missing import for %s module resolvedId=%s in environment %s, fullPath=%s", kind, resolvedId, environment, fullPath);
48
+ devServer.environments[environment].depsOptimizer?.registerMissingImport(resolvedId, fullPath);
49
+ if (isAfterFirstResponse && !hadFile) {
50
+ log("Invalidating cache for lookup module %s after adding module id=%s", lookupModule, id);
51
+ invalidateModule(devServer, environment, `virtual:use-${kind}-lookup`);
52
+ }
44
53
  }
45
- const serverResult = transformServerFunctions(code, normalizedId, this.environment.name, serverFiles);
46
- if (serverResult) {
47
- log("Server function transformation successful for id=%s", id);
48
- return {
49
- code: serverResult.code,
50
- map: serverResult.map,
51
- };
52
- }
53
- verboseLog("No transformation applied for id=%s", id);
54
- },
55
- configEnvironment(env, config) {
56
- log("Configuring environment: env=%s", env);
57
- config.optimizeDeps ??= {};
58
- config.optimizeDeps.esbuildOptions ??= {};
59
- config.optimizeDeps.esbuildOptions.plugins ??= [];
60
- config.optimizeDeps.esbuildOptions.plugins.push({
61
- name: "rsc-directives-esbuild-transform",
62
- setup(build) {
63
- log("Setting up esbuild plugin for environment: %s", env);
64
- build.onLoad({ filter: /\.(js|ts|jsx|tsx|mts|mjs|cjs)$/ }, async (args) => {
65
- verboseLog("Esbuild onLoad called for path=%s", args.path);
66
- let code;
67
- try {
68
- code = await fs.readFile(args.path, "utf-8");
69
- }
70
- catch {
71
- verboseLog("Failed to read file: %s", args.path);
72
- return undefined;
73
- }
74
- const clientResult = await transformClientComponents(code, normalizeModulePath(projectRootDir, args.path), {
75
- environmentName: env,
76
- clientFiles,
77
- isEsbuild: true,
54
+ };
55
+ const addClientModule = (environment, id) => {
56
+ addModule("client", environment, id);
57
+ };
58
+ const addServerModule = (environment, id) => {
59
+ addModule("server", environment, id);
60
+ };
61
+ return {
62
+ name: "rwsdk:rsc-directives",
63
+ configureServer(server) {
64
+ devServer = server;
65
+ devServer.middlewares.use((_req, res, next) => {
66
+ // context(justinvdm, 15 Jun 2025): We want to watch for new client and server modules
67
+ // and invalidate their respective module lookups when this happens, but
68
+ // we only want to do this after the first render
69
+ if (!isAfterFirstResponse) {
70
+ res.on("finish", () => {
71
+ if (!isAfterFirstResponse) {
72
+ isAfterFirstResponse = true;
73
+ log("Dev server first response completed");
74
+ }
75
+ });
76
+ }
77
+ next();
78
+ });
79
+ },
80
+ async transform(code, id) {
81
+ verboseLog("Transform called for id=%s, environment=%s", id, this.environment.name);
82
+ const normalizedId = normalizeModulePath(projectRootDir, id);
83
+ const clientResult = await transformClientComponents(code, normalizedId, {
84
+ environmentName: this.environment.name,
85
+ clientFiles,
86
+ addClientModule,
87
+ });
88
+ if (clientResult) {
89
+ log("Client component transformation successful for id=%s", id);
90
+ return {
91
+ code: clientResult.code,
92
+ map: clientResult.map,
93
+ };
94
+ }
95
+ const serverResult = transformServerFunctions(code, normalizedId, this.environment.name, serverFiles, addServerModule);
96
+ if (serverResult) {
97
+ log("Server function transformation successful for id=%s", id);
98
+ return {
99
+ code: serverResult.code,
100
+ map: serverResult.map,
101
+ };
102
+ }
103
+ verboseLog("No transformation applied for id=%s", id);
104
+ },
105
+ configEnvironment(env, config) {
106
+ log("Configuring environment: env=%s", env);
107
+ config.optimizeDeps ??= {};
108
+ config.optimizeDeps.esbuildOptions ??= {};
109
+ config.optimizeDeps.esbuildOptions.plugins ??= [];
110
+ config.optimizeDeps.esbuildOptions.plugins.push({
111
+ name: "rsc-directives-esbuild-transform",
112
+ setup(build) {
113
+ log("Setting up esbuild plugin for environment: %s", env);
114
+ build.onLoad({ filter: /\.(js|ts|jsx|tsx|mts|mjs|cjs)$/ }, async (args) => {
115
+ verboseLog("Esbuild onLoad called for environment=%s, path=%s", env, args.path);
116
+ const normalizedPath = normalizeModulePath(projectRootDir, args.path);
117
+ // context(justinvdm,2025-06-15): If we're in app code,
118
+ // we will be doing the transform work in the vite plugin hooks,
119
+ // the only reason we're in esbuild land for app code is for
120
+ // dependency discovery, so we can skip transform work
121
+ // and use heuristics instead - see below inside if block
122
+ if (!args.path.includes("node_modules")) {
123
+ log("Esbuild onLoad found app code, path=%s", args.path);
124
+ if (clientFiles.has(normalizedPath)) {
125
+ // context(justinvdm,2025-06-15): If this is a client file:
126
+ // * for ssr and client envs we can skip so esbuild looks at the
127
+ // original source code to discovery dependencies
128
+ // * for worker env, the transform would have just created
129
+ // references and dropped all imports, so we can just return empty code
130
+ if (env === "client" || env === "ssr") {
131
+ log("Esbuild onLoad skipping client module in app code for client or ssr env, path=%s", args.path);
132
+ return undefined;
133
+ }
134
+ else {
135
+ log("Esbuild onLoad returning empty code for server module in app code for worker env, path=%s to bypass esbuild dependency discovery", args.path);
136
+ return {
137
+ contents: "",
138
+ loader: "js",
139
+ };
140
+ }
141
+ }
142
+ else if (serverFiles.has(normalizedPath)) {
143
+ // context(justinvdm,2025-06-15): If this is a server file:
144
+ // * for worker env, we can skip so esbuild looks at the
145
+ // original source code to discovery dependencies
146
+ // * for ssr and client envs, the transform would have just created
147
+ // references and dropped all imports, so we can just return empty code
148
+ if (env === "worker") {
149
+ log("Esbuild onLoad skipping server module in app code for worker env, path=%s", args.path);
150
+ return undefined;
151
+ }
152
+ else if (env === "ssr" || env === "client") {
153
+ log("Esbuild onLoad returning empty code for server module in app code for ssr or client env, path=%s", args.path);
154
+ return {
155
+ contents: "",
156
+ loader: "js",
157
+ };
158
+ }
159
+ }
160
+ }
161
+ let code;
162
+ try {
163
+ code = await fs.readFile(args.path, "utf-8");
164
+ }
165
+ catch {
166
+ verboseLog("Failed to read file: %s, environment=%s", args.path, env);
167
+ return undefined;
168
+ }
169
+ const clientResult = await transformClientComponents(code, normalizedPath, {
170
+ environmentName: env,
171
+ clientFiles,
172
+ isEsbuild: true,
173
+ addClientModule,
174
+ });
175
+ if (clientResult) {
176
+ log("Esbuild client component transformation successful for environment=%s, path=%s", env, args.path);
177
+ verboseLog("Esbuild client component transformation for environment=%s, path=%s, code: %j", env, args.path, clientResult.code);
178
+ return {
179
+ contents: clientResult.code,
180
+ loader: getLoader(args.path),
181
+ };
182
+ }
183
+ const serverResult = transformServerFunctions(code, normalizedPath, env, serverFiles, addServerModule);
184
+ if (serverResult) {
185
+ log("Esbuild server function transformation successful for environment=%s, path=%s", env, args.path);
186
+ return {
187
+ contents: serverResult.code,
188
+ loader: getLoader(args.path),
189
+ };
190
+ }
191
+ verboseLog("Esbuild no transformation applied for environment=%s, path=%s", env, args.path);
78
192
  });
79
- if (clientResult) {
80
- log("Esbuild client component transformation successful for path=%s", args.path);
81
- return {
82
- contents: clientResult.code,
83
- loader: getLoader(args.path),
84
- };
85
- }
86
- const serverResult = transformServerFunctions(code, normalizeModulePath(projectRootDir, args.path), env, serverFiles);
87
- if (serverResult) {
88
- log("Esbuild server function transformation successful for path=%s", args.path);
89
- return {
90
- contents: serverResult.code,
91
- loader: getLoader(args.path),
92
- };
93
- }
94
- verboseLog("Esbuild no transformation applied for path=%s", args.path);
95
- });
96
- },
97
- });
98
- log("Environment configuration complete for env=%s", env);
99
- },
100
- });
193
+ },
194
+ });
195
+ log("Environment configuration complete for env=%s", env);
196
+ },
197
+ };
198
+ };
@@ -1,4 +1,12 @@
1
1
  export declare const IMPORT_PATTERNS: string[];
2
+ export declare const EXPORT_PATTERNS: string[];
3
+ export interface ExportInfo {
4
+ name: string;
5
+ isDefault: boolean;
6
+ alias?: string;
7
+ isReExport?: boolean;
8
+ moduleSpecifier?: string;
9
+ }
2
10
  /**
3
11
  * Finds import specifiers and their positions in the code using the provided patterns.
4
12
  * @param code The code to search for import specifiers.
@@ -12,3 +20,11 @@ export declare function findImportSpecifiers(id: string, code: string, ignoredIm
12
20
  e: number;
13
21
  raw: string;
14
22
  }>;
23
+ /**
24
+ * Finds export information in the code using ast-grep patterns.
25
+ * @param id The file identifier for language detection.
26
+ * @param code The code to search for exports.
27
+ * @param log Optional logger function for debug output.
28
+ * @returns Array of export information objects.
29
+ */
30
+ export declare function findExports(id: string, code: string, log?: (...args: any[]) => void): ExportInfo[];
@@ -24,6 +24,23 @@ export const IMPORT_PATTERNS = [
24
24
  "require('$MODULE')",
25
25
  "require(`$MODULE`)",
26
26
  ];
27
+ // These patterns are used to match export statements for client/server component transformations
28
+ export const EXPORT_PATTERNS = [
29
+ // Named exports
30
+ "export const $NAME = $$$",
31
+ "export let $NAME = $$$",
32
+ "export var $NAME = $$$",
33
+ "export function $NAME($$$) { $$$ }",
34
+ "export async function $NAME($$$) { $$$ }",
35
+ // Default exports
36
+ "export default function $NAME($$$) { $$$ }",
37
+ "export default function($$$) { $$$ }",
38
+ "export default $$$",
39
+ // Export declarations
40
+ "export { $$$ }",
41
+ 'export { $$$ } from "$MODULE"',
42
+ "export { $$$ } from '$MODULE'",
43
+ ];
27
44
  /**
28
45
  * Finds import specifiers and their positions in the code using the provided patterns.
29
46
  * @param code The code to search for import specifiers.
@@ -74,3 +91,138 @@ export function findImportSpecifiers(id, code, ignoredImportPatterns, log) {
74
91
  }
75
92
  return results;
76
93
  }
94
+ /**
95
+ * Finds export information in the code using ast-grep patterns.
96
+ * @param id The file identifier for language detection.
97
+ * @param code The code to search for exports.
98
+ * @param log Optional logger function for debug output.
99
+ * @returns Array of export information objects.
100
+ */
101
+ export function findExports(id, code, log) {
102
+ const ext = path.extname(id).toLowerCase();
103
+ const lang = ext === ".tsx" || ext === ".jsx" ? Lang.Tsx : SgLang.TypeScript;
104
+ const logger = log ?? (() => { });
105
+ const results = [];
106
+ const seen = new Set(); // Track seen exports to avoid duplicates
107
+ try {
108
+ const root = sgParse(lang, code);
109
+ // Use the existing EXPORT_PATTERNS in a specific order to avoid duplicates
110
+ const orderedPatterns = [
111
+ // Handle re-exports first (most specific)
112
+ ...EXPORT_PATTERNS.filter((p) => p.includes('from "$MODULE"') || p.includes("from '$MODULE'")),
113
+ // Then named exports
114
+ ...EXPORT_PATTERNS.filter((p) => p.startsWith("export const") ||
115
+ p.startsWith("export let") ||
116
+ p.startsWith("export var") ||
117
+ p.startsWith("export function") ||
118
+ p.startsWith("export async function")),
119
+ // Then default exports
120
+ ...EXPORT_PATTERNS.filter((p) => p.startsWith("export default")),
121
+ // Finally export declarations
122
+ ...EXPORT_PATTERNS.filter((p) => p === "export { $$$ }"),
123
+ ];
124
+ for (const pattern of orderedPatterns) {
125
+ try {
126
+ const matches = root.root().findAll(pattern);
127
+ for (const match of matches) {
128
+ const nameCapture = match.getMatch("NAME");
129
+ const moduleCapture = match.getMatch("MODULE");
130
+ const matchText = match.text();
131
+ if (pattern.includes('from "$MODULE"') ||
132
+ pattern.includes("from '$MODULE'")) {
133
+ // Re-export from module
134
+ const moduleSpecifier = moduleCapture?.text();
135
+ if (!moduleSpecifier)
136
+ continue;
137
+ if (pattern.includes("export *")) {
138
+ // Skip export * for now - too complex
139
+ logger("Skipping export * from %s", moduleSpecifier);
140
+ continue;
141
+ }
142
+ // Parse the export list
143
+ const exportListMatch = matchText.match(/export\s*\{\s*([^}]+)\s*\}/);
144
+ if (exportListMatch) {
145
+ const exportList = exportListMatch[1];
146
+ const exports = exportList.split(",").map((e) => e.trim());
147
+ for (const exp of exports) {
148
+ const [originalName, alias] = exp.includes(" as ")
149
+ ? exp.split(" as ").map((s) => s.trim())
150
+ : [exp.trim(), undefined];
151
+ const exportName = alias || originalName;
152
+ const key = `${exportName}:${originalName === "default"}:reexport:${moduleSpecifier}`;
153
+ if (seen.has(key))
154
+ continue;
155
+ seen.add(key);
156
+ results.push({
157
+ name: exportName,
158
+ isDefault: originalName === "default",
159
+ alias: alias !== originalName ? alias : undefined,
160
+ isReExport: true,
161
+ moduleSpecifier,
162
+ });
163
+ logger("Found re-export: %s from %s", exportName, moduleSpecifier);
164
+ }
165
+ }
166
+ }
167
+ else if (matchText.startsWith("export default")) {
168
+ // Default export
169
+ const name = nameCapture?.text() || "default";
170
+ const key = `${name}:true:default`;
171
+ if (seen.has(key))
172
+ continue;
173
+ seen.add(key);
174
+ results.push({
175
+ name,
176
+ isDefault: true,
177
+ });
178
+ logger("Found default export: %s", name);
179
+ }
180
+ else if (matchText.includes("export {")) {
181
+ // Local export declaration
182
+ const exportListMatch = matchText.match(/export\s*\{\s*([^}]+)\s*\}/);
183
+ if (exportListMatch) {
184
+ const exportList = exportListMatch[1];
185
+ const exports = exportList.split(",").map((e) => e.trim());
186
+ for (const exp of exports) {
187
+ const [originalName, alias] = exp.includes(" as ")
188
+ ? exp.split(" as ").map((s) => s.trim())
189
+ : [exp.trim(), undefined];
190
+ const exportName = alias || originalName;
191
+ const key = `${exportName}:${originalName === "default"}:local`;
192
+ if (seen.has(key))
193
+ continue;
194
+ seen.add(key);
195
+ results.push({
196
+ name: exportName,
197
+ isDefault: originalName === "default",
198
+ alias: alias !== originalName ? alias : undefined,
199
+ });
200
+ logger("Found local export: %s", exportName);
201
+ }
202
+ }
203
+ }
204
+ else if (nameCapture) {
205
+ // Named export (function, const, etc.)
206
+ const name = nameCapture.text();
207
+ const key = `${name}:false:named`;
208
+ if (seen.has(key))
209
+ continue;
210
+ seen.add(key);
211
+ results.push({
212
+ name,
213
+ isDefault: false,
214
+ });
215
+ logger("Found named export: %s", name);
216
+ }
217
+ }
218
+ }
219
+ catch (err) {
220
+ logger("Error processing export pattern %s: %O", pattern, err);
221
+ }
222
+ }
223
+ }
224
+ catch (err) {
225
+ logger("Error parsing code for exports: %O", err);
226
+ }
227
+ return results;
228
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,73 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { findExports } from "./findImportSpecifiers.mjs";
3
+ describe("findExports", () => {
4
+ it("finds named exports", () => {
5
+ const code = `
6
+ export const Component = () => {};
7
+ export function helper() {}
8
+ export let data = {};
9
+ `;
10
+ const exports = findExports("/test.tsx", code);
11
+ // Just check that we found the exports, order doesn't matter for our use case
12
+ const names = exports.map((e) => e.name).sort();
13
+ expect(names).toEqual(["Component", "data", "helper"]);
14
+ // Check that none are default
15
+ expect(exports.every((e) => !e.isDefault)).toBe(true);
16
+ });
17
+ it("finds default exports", () => {
18
+ const code = `
19
+ export default function Component() {}
20
+ `;
21
+ const exports = findExports("/test.tsx", code);
22
+ // Should find at least one default export
23
+ const defaultExports = exports.filter((e) => e.isDefault);
24
+ expect(defaultExports.length).toBeGreaterThan(0);
25
+ expect(defaultExports.some((e) => e.name === "Component")).toBe(true);
26
+ });
27
+ it("finds export declarations", () => {
28
+ const code = `
29
+ const First = () => {};
30
+ const Second = () => {};
31
+ export { First, Second };
32
+ `;
33
+ const exports = findExports("/test.tsx", code);
34
+ const names = exports.map((e) => e.name).sort();
35
+ expect(names).toEqual(["First", "Second"]);
36
+ });
37
+ it("finds export declarations with aliases", () => {
38
+ const code = `
39
+ const Component = () => {};
40
+ export { Component as MyComponent };
41
+ `;
42
+ const exports = findExports("/test.tsx", code);
43
+ expect(exports.some((e) => e.name === "MyComponent")).toBe(true);
44
+ });
45
+ it("finds re-exports", () => {
46
+ const code = `
47
+ export { sum } from './math';
48
+ export { default as multiply } from './multiply';
49
+ `;
50
+ const exports = findExports("/test.tsx", code);
51
+ // Should find re-exports
52
+ const reExports = exports.filter((e) => e.isReExport);
53
+ expect(reExports.length).toBeGreaterThan(0);
54
+ const names = reExports.map((e) => e.name).sort();
55
+ expect(names).toContain("sum");
56
+ expect(names).toContain("multiply");
57
+ });
58
+ it("handles mixed export styles", () => {
59
+ const code = `
60
+ export const First = () => {};
61
+ const Second = () => {};
62
+ export default function Main() {}
63
+ export { Second };
64
+ `;
65
+ const exports = findExports("/test.tsx", code);
66
+ const names = exports.map((e) => e.name);
67
+ expect(names).toContain("First");
68
+ expect(names).toContain("Second");
69
+ // Should have at least one default export
70
+ const defaultExports = exports.filter((e) => e.isDefault);
71
+ expect(defaultExports.length).toBeGreaterThan(0);
72
+ });
73
+ });
@@ -0,0 +1,31 @@
1
+ export declare const IMPORT_PATTERNS: string[];
2
+ export declare const EXPORT_PATTERNS: string[];
3
+ export interface ExportInfo {
4
+ name: string;
5
+ isDefault: boolean;
6
+ alias?: string;
7
+ originalName?: string;
8
+ isReExport?: boolean;
9
+ moduleSpecifier?: string;
10
+ }
11
+ /**
12
+ * Finds import specifiers and their positions in the code using the provided patterns.
13
+ * @param code The code to search for import specifiers.
14
+ * @param lang The language parser to use (TypeScript or Tsx).
15
+ * @param ignoredImportPatterns Array of regex patterns to ignore.
16
+ * @param log Optional logger function for debug output.
17
+ * @returns Array of objects with start, end, and raw import string.
18
+ */
19
+ export declare function findImportSpecifiers(id: string, code: string, ignoredImportPatterns: RegExp[], log?: (...args: any[]) => void): Array<{
20
+ s: number;
21
+ e: number;
22
+ raw: string;
23
+ }>;
24
+ /**
25
+ * Finds export information in the code using ast-grep patterns.
26
+ * @param id The file identifier for language detection.
27
+ * @param code The code to search for exports.
28
+ * @param log Optional logger function for debug output.
29
+ * @returns Array of export information objects.
30
+ */
31
+ export declare function findExports(id: string, code: string, log?: (...args: any[]) => void): ExportInfo[];