rwsdk 0.3.1 → 0.3.3

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.
@@ -50,15 +50,64 @@ class VitePluginResolverPlugin {
50
50
  if (!resolveIdHandler)
51
51
  continue;
52
52
  let handlerFn;
53
+ let shouldApplyFilter = false;
54
+ let filter = null;
53
55
  if (typeof resolveIdHandler === "function") {
54
56
  handlerFn = resolveIdHandler;
55
57
  }
56
58
  else if (typeof resolveIdHandler === "object" &&
57
59
  typeof resolveIdHandler.handler === "function") {
58
60
  handlerFn = resolveIdHandler.handler;
61
+ shouldApplyFilter = true;
62
+ filter = resolveIdHandler.filter;
59
63
  }
60
64
  if (!handlerFn)
61
65
  continue;
66
+ if (shouldApplyFilter && filter?.id) {
67
+ const idFilter = filter.id;
68
+ let shouldSkip = false;
69
+ if (idFilter instanceof RegExp) {
70
+ shouldSkip = !idFilter.test(currentRequest.request);
71
+ }
72
+ else if (Array.isArray(idFilter)) {
73
+ // Handle array of filters - matches if ANY filter matches
74
+ shouldSkip = !idFilter.some((f) => f instanceof RegExp
75
+ ? f.test(currentRequest.request)
76
+ : f === currentRequest.request);
77
+ }
78
+ else if (typeof idFilter === "string") {
79
+ shouldSkip = idFilter !== currentRequest.request;
80
+ }
81
+ else if (typeof idFilter === "object" && idFilter !== null) {
82
+ // Handle include/exclude object pattern
83
+ const { include, exclude } = idFilter;
84
+ let matches = true;
85
+ // Check include patterns (if any)
86
+ if (include) {
87
+ const includePatterns = Array.isArray(include)
88
+ ? include
89
+ : [include];
90
+ matches = includePatterns.some((pattern) => pattern instanceof RegExp
91
+ ? pattern.test(currentRequest.request)
92
+ : pattern === currentRequest.request);
93
+ }
94
+ // Check exclude patterns (if any) - exclude overrides include
95
+ if (matches && exclude) {
96
+ const excludePatterns = Array.isArray(exclude)
97
+ ? exclude
98
+ : [exclude];
99
+ const isExcluded = excludePatterns.some((pattern) => pattern instanceof RegExp
100
+ ? pattern.test(currentRequest.request)
101
+ : pattern === currentRequest.request);
102
+ matches = !isExcluded;
103
+ }
104
+ shouldSkip = !matches;
105
+ }
106
+ if (shouldSkip) {
107
+ debug("Skipping plugin '%s' due to filter mismatch for '%s'", plugin.name, currentRequest.request);
108
+ continue;
109
+ }
110
+ }
62
111
  try {
63
112
  debug("Calling plugin '%s' for '%s'", plugin.name, currentRequest.request);
64
113
  const result = await handlerFn.call(pluginContext, currentRequest.request, currentRequest.path, { scan: true });
@@ -5,7 +5,8 @@ import debug from "debug";
5
5
  import { getViteEsbuild } from "./getViteEsbuild.mjs";
6
6
  import { normalizeModulePath } from "../lib/normalizeModulePath.mjs";
7
7
  import { externalModules } from "./constants.mjs";
8
- import { createViteAwareResolver } from "./createViteAwareResolver.mjs";
8
+ import { createViteAwareResolver, mapViteResolveToEnhancedResolveOptions, } from "./createViteAwareResolver.mjs";
9
+ import resolve from "enhanced-resolve";
9
10
  const log = debug("rwsdk:vite:run-directives-scan");
10
11
  // Copied from Vite's source code.
11
12
  // https://github.com/vitejs/vite/blob/main/packages/vite/src/shared/utils.ts
@@ -41,28 +42,23 @@ export const runDirectivesScan = async ({ rootConfig, environment, clientFiles,
41
42
  const absoluteEntries = entries.map((entry) => path.resolve(rootConfig.root, entry));
42
43
  log("Starting directives scan for environment '%s' with entries:", environment.name, absoluteEntries);
43
44
  // Use enhanced-resolve with Vite plugin integration for full compatibility
44
- const resolver = createViteAwareResolver(rootConfig, environment.name, environment);
45
- const resolveId = async (id, importer) => {
46
- return new Promise((resolve) => {
47
- resolver({}, importer || rootConfig.root, id, {}, (err, result) => {
48
- if (!err && result) {
49
- resolve({ id: result });
50
- }
51
- else {
52
- if (err) {
53
- // Handle specific enhanced-resolve errors gracefully
54
- const errorMessage = err.message || String(err);
55
- if (errorMessage.includes("Package path . is not exported")) {
56
- log("Package exports error for %s, marking as external", id);
57
- }
58
- else {
59
- log("Resolution failed for %s: %s", id, errorMessage);
60
- }
61
- }
62
- resolve(null);
63
- }
64
- });
65
- });
45
+ const workerResolver = createViteAwareResolver(rootConfig, environment.name, environment);
46
+ // Create a client resolver with browser conditions
47
+ // We'll use the mapViteResolveToEnhancedResolveOptions function directly
48
+ // to create a resolver with browser conditions
49
+ const clientResolveOptions = mapViteResolveToEnhancedResolveOptions(rootConfig, environment.name);
50
+ // Override the conditions to use browser conditions for client resolution
51
+ clientResolveOptions.conditionNames = ["browser", "module"];
52
+ const clientResolver = resolve.create(clientResolveOptions);
53
+ const moduleEnvironments = new Map();
54
+ const fileContentCache = new Map();
55
+ const readFileWithCache = async (path) => {
56
+ if (fileContentCache.has(path)) {
57
+ return fileContentCache.get(path);
58
+ }
59
+ const contents = await fsp.readFile(path, "utf-8");
60
+ fileContentCache.set(path, contents);
61
+ return contents;
66
62
  };
67
63
  const esbuildScanPlugin = {
68
64
  name: "rwsdk:esbuild-scan-plugin",
@@ -92,20 +88,73 @@ export const runDirectivesScan = async ({ rootConfig, environment, clientFiles,
92
88
  return { external: true };
93
89
  }
94
90
  log("onResolve called for:", args.path, "from:", args.importer);
95
- const resolved = await resolveId(args.path, args.importer);
91
+ let importerEnv = moduleEnvironments.get(args.importer);
92
+ // If we don't know the importer's environment yet, check its content
93
+ if (!importerEnv &&
94
+ args.importer &&
95
+ /\.(m|c)?[jt]sx?$/.test(args.importer)) {
96
+ try {
97
+ const importerContents = await readFileWithCache(args.importer);
98
+ if (hasDirective(importerContents, "use client")) {
99
+ importerEnv = "client";
100
+ log("Pre-detected importer 'use client' in:", args.importer);
101
+ }
102
+ else if (hasDirective(importerContents, "use server")) {
103
+ importerEnv = "worker";
104
+ log("Pre-detected importer 'use server' in:", args.importer);
105
+ }
106
+ else {
107
+ importerEnv = "worker"; // Default for entry points
108
+ }
109
+ moduleEnvironments.set(args.importer, importerEnv);
110
+ }
111
+ catch (e) {
112
+ importerEnv = "worker"; // Default fallback
113
+ log("Could not pre-read importer, using worker environment:", args.importer);
114
+ }
115
+ }
116
+ else if (!importerEnv) {
117
+ importerEnv = "worker"; // Default for entry points or non-script files
118
+ }
119
+ log("Importer:", args.importer, "environment:", importerEnv);
120
+ const resolver = importerEnv === "client" ? clientResolver : workerResolver;
121
+ const resolved = await new Promise((resolve) => {
122
+ resolver({}, args.importer || rootConfig.root, args.path, {}, (err, result) => {
123
+ if (!err && result) {
124
+ resolve({ id: result });
125
+ }
126
+ else {
127
+ if (err) {
128
+ const errorMessage = err.message || String(err);
129
+ if (errorMessage.includes("Package path . is not exported")) {
130
+ log("Package exports error for %s, marking as external", args.path);
131
+ }
132
+ else {
133
+ log("Resolution failed for %s: %s", args.path, errorMessage);
134
+ }
135
+ }
136
+ resolve(null);
137
+ }
138
+ });
139
+ });
96
140
  log("Resolution result:", resolved);
97
141
  const resolvedPath = resolved?.id;
98
142
  if (resolvedPath && path.isAbsolute(resolvedPath)) {
99
143
  // Normalize the path for esbuild compatibility
100
144
  const normalizedPath = normalizeModulePath(resolvedPath, rootConfig.root, { absolute: true });
101
145
  log("Normalized path:", normalizedPath);
102
- return { path: normalizedPath };
146
+ return {
147
+ path: normalizedPath,
148
+ pluginData: { inheritedEnv: importerEnv },
149
+ };
103
150
  }
104
151
  log("Marking as external:", args.path, "resolved to:", resolvedPath);
105
152
  return { external: true };
106
153
  });
107
154
  build.onLoad({ filter: /\.(m|c)?[jt]sx?$/ }, async (args) => {
108
155
  log("onLoad called for:", args.path);
156
+ const inheritedEnv = args.pluginData?.inheritedEnv || "worker";
157
+ log("Inherited environment for", args.path, "is", inheritedEnv);
109
158
  if (!args.path.startsWith("/") ||
110
159
  args.path.includes("virtual:") ||
111
160
  isExternalUrl(args.path)) {
@@ -117,7 +166,8 @@ export const runDirectivesScan = async ({ rootConfig, environment, clientFiles,
117
166
  return null;
118
167
  }
119
168
  try {
120
- const contents = await fsp.readFile(args.path, "utf-8");
169
+ const contents = await readFileWithCache(args.path);
170
+ // The environment is determined in onResolve. Here we just populate the output sets.
121
171
  if (hasDirective(contents, "use client")) {
122
172
  log("Discovered 'use client' in:", args.path);
123
173
  clientFiles.add(normalizeModulePath(args.path, rootConfig.root));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rwsdk",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "Build fast, server-driven webapps on Cloudflare with SSR, RSC, and realtime",
5
5
  "type": "module",
6
6
  "bin": {