miaoda-expo-devkit 0.1.1-beta.3 → 0.1.1-beta.5

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.
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ // src/cli/lint.ts
5
+ var import_node_child_process = require("child_process");
6
+ var import_node_path = require("path");
7
+ var devkitRoot = (0, import_node_path.resolve)(__dirname, "../..");
8
+ var oxlintConfig = (0, import_node_path.join)(devkitRoot, "oxlint-config.json");
9
+ var biomeConfig = (0, import_node_path.join)(devkitRoot, "biome-config.json");
10
+ var targets = process.argv.slice(2);
11
+ var lintTargets = targets.length > 0 ? targets : ["src/"];
12
+ var failed = false;
13
+ var oxlint = (0, import_node_child_process.spawnSync)("oxlint", ["--config", oxlintConfig, ...lintTargets], {
14
+ stdio: "inherit"
15
+ });
16
+ if ((oxlint.status ?? 1) !== 0) failed = true;
17
+ var biome = (0, import_node_child_process.spawnSync)("biome", ["lint", "--config-path", biomeConfig, ...lintTargets], {
18
+ stdio: "inherit"
19
+ });
20
+ if ((biome.status ?? 1) !== 0) failed = true;
21
+ process.exit(failed ? 1 : 0);
package/dist/metro.d.mts CHANGED
@@ -84,8 +84,8 @@ interface RouteEndpointOptions {
84
84
  * 端点返回格式:
85
85
  * {
86
86
  * "routes": [
87
- * { "title": "首页", "pageId": "/", "pageName": "index", "visible": true, "path": "index.tsx" },
88
- * { "title": "id", "pageId": "/user/[id]", "pageName": "user-[id]", "visible": false, "path": "user/[id].tsx" }
87
+ * { "title": "首页", "pageId": "/", "pageName": "index", "visible": true, "path": "src/app/index.tsx" },
88
+ * { "title": "id", "pageId": "/user/[id]", "pageName": "user-[id]", "visible": false, "path": "src/app/user/[id].tsx" }
89
89
  * ]
90
90
  * }
91
91
  *
@@ -114,5 +114,67 @@ declare function withRouteEndpoint(config: MetroConfig, options: RouteEndpointOp
114
114
  * @returns 修正 watchFolders 和 nodeModulesPaths 后的新 Metro config
115
115
  */
116
116
  declare function withWorkspaceNodeModules(config: MetroConfig): MetroConfig;
117
+ /**
118
+ * 为第三方 Expo 组件注入 NativeWind cssInterop,使其支持 className prop。
119
+ *
120
+ * 注入方式:Metro resolver 层拦截对应包的 import,重定向到 stub 文件。
121
+ * stub 文件透传原包的所有导出,并调用 cssInterop 完成注册。
122
+ * stub 内部 import 原包时,originModulePath 检查避免循环依赖。
123
+ *
124
+ * 当前支持的包:
125
+ * - expo-image:Image 组件(className → style)
126
+ * - expo-camera:CameraView 组件(className → style)
127
+ *
128
+ * 此函数在开发和生产环境均需启用(cssInterop 注册与环境无关)。
129
+ * 支持与其他 Metro wrapper 链式组合。
130
+ *
131
+ * @param config Metro config 对象
132
+ * @returns 注入 resolveRequest 后的新 Metro config
133
+ */
134
+ declare function withCssInterop(config: MetroConfig): MetroConfig;
135
+ /** patchNativeWindCachePath 的配置选项 */
136
+ interface PatchNativeWindCacheOptions {
137
+ /**
138
+ * 缓存目录,相对于 process.cwd()(Metro 项目根目录),默认 .native-wind-cache。
139
+ * 与 package.json 同级,避免写入只读的 node_modules。
140
+ */
141
+ cacheDir?: string;
142
+ }
143
+ /**
144
+ * 将 NativeWind 编译缓存从 node_modules/.cache 重定向到项目根目录下的自定义目录。
145
+ *
146
+ * ⚠️ 必须在 require('nativewind/metro') / require('react-native-css-interop/dist/metro')
147
+ * 之前调用,否则 outputDirectory 已经固化,patch 无效。
148
+ *
149
+ * 原理:react-native-css-interop/dist/metro 在模块顶层用
150
+ * const outputDirectory = path.resolve(__dirname, "../../.cache")
151
+ * 计算缓存路径。通过在 require 前临时替换 path.resolve,可将该值重定向到
152
+ * 项目根目录下的自定义缓存目录,而无需 fork 或修改 node_modules 源码。
153
+ *
154
+ * 所有依赖 outputDirectory 的 NativeWind 行为(文件写入、虚拟模块 Map key、
155
+ * Metro resolver 返回值、haste 事件)都会一致使用新路径,无需额外 Metro 层面的 patch。
156
+ *
157
+ * ⚠️ 已知限制:依赖 react-native-css-interop 使用 path.resolve 计算缓存路径,
158
+ * 升级该依赖时需确认此假设仍然成立。
159
+ *
160
+ * 用法(metro.config.js):
161
+ * const { patchNativeWindCachePath } = require('miaoda-expo-devkit/metro');
162
+ * patchNativeWindCachePath(); // 必须在 require nativewind/metro 之前调用
163
+ * const { withNativeWind } = require('nativewind/metro');
164
+ */
165
+ declare function patchNativeWindCachePath(options?: PatchNativeWindCacheOptions): void;
166
+ /**
167
+ * withNativeWind 的 lazy wrapper。
168
+ *
169
+ * nativewind/metro 在模块顶层捕获 react-native-css-interop 的 withCssInterop 引用,
170
+ * 因此必须在 patchNativeWindCachePath() 执行完之后才能加载 nativewind/metro。
171
+ * 这里使用 lazy require(在函数调用时才加载),确保 nativewind 拿到已 patch 的
172
+ * outputDirectory,而不需要在 metro.config.js 中手动处理加载顺序。
173
+ *
174
+ * 用法(metro.config.js):
175
+ * const { withNativeWind } = require('miaoda-expo-devkit/metro');
176
+ * // 不再需要 require('nativewind/metro')
177
+ */
178
+ declare function withNativeWind(config: MetroConfig, options?: any): MetroConfig;
117
179
 
118
- export { type InjectOptions, type RouteEndpointOptions, withDevStubs, withEntryInjection, withRouteEndpoint, withWorkspaceNodeModules };
180
+ export { type InjectOptions, type PatchNativeWindCacheOptions, type RouteEndpointOptions, patchNativeWindCachePath, withCssInterop, withDevStubs, withEntryInjection, withNativeWind, withRouteEndpoint, withWorkspaceNodeModules };
package/dist/metro.d.ts CHANGED
@@ -84,8 +84,8 @@ interface RouteEndpointOptions {
84
84
  * 端点返回格式:
85
85
  * {
86
86
  * "routes": [
87
- * { "title": "首页", "pageId": "/", "pageName": "index", "visible": true, "path": "index.tsx" },
88
- * { "title": "id", "pageId": "/user/[id]", "pageName": "user-[id]", "visible": false, "path": "user/[id].tsx" }
87
+ * { "title": "首页", "pageId": "/", "pageName": "index", "visible": true, "path": "src/app/index.tsx" },
88
+ * { "title": "id", "pageId": "/user/[id]", "pageName": "user-[id]", "visible": false, "path": "src/app/user/[id].tsx" }
89
89
  * ]
90
90
  * }
91
91
  *
@@ -114,5 +114,67 @@ declare function withRouteEndpoint(config: MetroConfig, options: RouteEndpointOp
114
114
  * @returns 修正 watchFolders 和 nodeModulesPaths 后的新 Metro config
115
115
  */
116
116
  declare function withWorkspaceNodeModules(config: MetroConfig): MetroConfig;
117
+ /**
118
+ * 为第三方 Expo 组件注入 NativeWind cssInterop,使其支持 className prop。
119
+ *
120
+ * 注入方式:Metro resolver 层拦截对应包的 import,重定向到 stub 文件。
121
+ * stub 文件透传原包的所有导出,并调用 cssInterop 完成注册。
122
+ * stub 内部 import 原包时,originModulePath 检查避免循环依赖。
123
+ *
124
+ * 当前支持的包:
125
+ * - expo-image:Image 组件(className → style)
126
+ * - expo-camera:CameraView 组件(className → style)
127
+ *
128
+ * 此函数在开发和生产环境均需启用(cssInterop 注册与环境无关)。
129
+ * 支持与其他 Metro wrapper 链式组合。
130
+ *
131
+ * @param config Metro config 对象
132
+ * @returns 注入 resolveRequest 后的新 Metro config
133
+ */
134
+ declare function withCssInterop(config: MetroConfig): MetroConfig;
135
+ /** patchNativeWindCachePath 的配置选项 */
136
+ interface PatchNativeWindCacheOptions {
137
+ /**
138
+ * 缓存目录,相对于 process.cwd()(Metro 项目根目录),默认 .native-wind-cache。
139
+ * 与 package.json 同级,避免写入只读的 node_modules。
140
+ */
141
+ cacheDir?: string;
142
+ }
143
+ /**
144
+ * 将 NativeWind 编译缓存从 node_modules/.cache 重定向到项目根目录下的自定义目录。
145
+ *
146
+ * ⚠️ 必须在 require('nativewind/metro') / require('react-native-css-interop/dist/metro')
147
+ * 之前调用,否则 outputDirectory 已经固化,patch 无效。
148
+ *
149
+ * 原理:react-native-css-interop/dist/metro 在模块顶层用
150
+ * const outputDirectory = path.resolve(__dirname, "../../.cache")
151
+ * 计算缓存路径。通过在 require 前临时替换 path.resolve,可将该值重定向到
152
+ * 项目根目录下的自定义缓存目录,而无需 fork 或修改 node_modules 源码。
153
+ *
154
+ * 所有依赖 outputDirectory 的 NativeWind 行为(文件写入、虚拟模块 Map key、
155
+ * Metro resolver 返回值、haste 事件)都会一致使用新路径,无需额外 Metro 层面的 patch。
156
+ *
157
+ * ⚠️ 已知限制:依赖 react-native-css-interop 使用 path.resolve 计算缓存路径,
158
+ * 升级该依赖时需确认此假设仍然成立。
159
+ *
160
+ * 用法(metro.config.js):
161
+ * const { patchNativeWindCachePath } = require('miaoda-expo-devkit/metro');
162
+ * patchNativeWindCachePath(); // 必须在 require nativewind/metro 之前调用
163
+ * const { withNativeWind } = require('nativewind/metro');
164
+ */
165
+ declare function patchNativeWindCachePath(options?: PatchNativeWindCacheOptions): void;
166
+ /**
167
+ * withNativeWind 的 lazy wrapper。
168
+ *
169
+ * nativewind/metro 在模块顶层捕获 react-native-css-interop 的 withCssInterop 引用,
170
+ * 因此必须在 patchNativeWindCachePath() 执行完之后才能加载 nativewind/metro。
171
+ * 这里使用 lazy require(在函数调用时才加载),确保 nativewind 拿到已 patch 的
172
+ * outputDirectory,而不需要在 metro.config.js 中手动处理加载顺序。
173
+ *
174
+ * 用法(metro.config.js):
175
+ * const { withNativeWind } = require('miaoda-expo-devkit/metro');
176
+ * // 不再需要 require('nativewind/metro')
177
+ */
178
+ declare function withNativeWind(config: MetroConfig, options?: any): MetroConfig;
117
179
 
118
- export { type InjectOptions, type RouteEndpointOptions, withDevStubs, withEntryInjection, withRouteEndpoint, withWorkspaceNodeModules };
180
+ export { type InjectOptions, type PatchNativeWindCacheOptions, type RouteEndpointOptions, patchNativeWindCachePath, withCssInterop, withDevStubs, withEntryInjection, withNativeWind, withRouteEndpoint, withWorkspaceNodeModules };
package/dist/metro.js CHANGED
@@ -30,8 +30,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/metro.ts
31
31
  var metro_exports = {};
32
32
  __export(metro_exports, {
33
+ patchNativeWindCachePath: () => patchNativeWindCachePath,
34
+ withCssInterop: () => withCssInterop,
33
35
  withDevStubs: () => withDevStubs,
34
36
  withEntryInjection: () => withEntryInjection,
37
+ withNativeWind: () => withNativeWind,
35
38
  withRouteEndpoint: () => withRouteEndpoint,
36
39
  withWorkspaceNodeModules: () => withWorkspaceNodeModules
37
40
  });
@@ -44,7 +47,7 @@ var import_path2 = __toESM(require("path"));
44
47
  var import_fs = __toESM(require("fs"));
45
48
  var import_path = __toESM(require("path"));
46
49
  var ROUTE_EXT = /\.(tsx|ts|jsx|js)$/;
47
- function getRoutes(appDir) {
50
+ function getRoutes(appDir, rootDir) {
48
51
  const routes = [];
49
52
  function scan(dir, prefix) {
50
53
  let entries;
@@ -70,17 +73,17 @@ function getRoutes(appDir) {
70
73
  pageId: routePath,
71
74
  pageName: routePath,
72
75
  visible: !isDynamic,
73
- path: import_path.default.relative(appDir, fullPath)
76
+ path: import_path.default.relative(rootDir, fullPath)
74
77
  });
75
78
  }
76
79
  }
77
80
  }
78
81
  scan(appDir, "");
79
- return { routes };
82
+ return { data: routes };
80
83
  }
81
- function createRouteHandler(appDir) {
84
+ function createRouteHandler(appDir, rootDir) {
82
85
  return (_req, res) => {
83
- const routeTree = getRoutes(appDir);
86
+ const routeTree = getRoutes(appDir, rootDir);
84
87
  res.writeHead(200, { "Content-Type": "application/json" });
85
88
  res.end(JSON.stringify(routeTree, null, 2));
86
89
  };
@@ -139,10 +142,11 @@ function withEntryInjection(config, options) {
139
142
  var DEFAULT_ROUTE_ENDPOINT = "/__routes";
140
143
  function withRouteEndpoint(config, options) {
141
144
  const { appDir, endpoint = DEFAULT_ROUTE_ENDPOINT } = options;
145
+ const rootDir = config.projectRoot ?? process.cwd();
142
146
  const upstream = config.server?.enhanceMiddleware ?? null;
143
147
  const enhanceMiddleware = (middleware, metroServer) => {
144
148
  const enhanced = upstream ? upstream(middleware, metroServer) : middleware;
145
- return (0, import_connect.default)().use(enhanced).use(endpoint, createRouteHandler(appDir));
149
+ return (0, import_connect.default)().use(enhanced).use(endpoint, createRouteHandler(appDir, rootDir));
146
150
  };
147
151
  return {
148
152
  ...config,
@@ -183,10 +187,77 @@ function withWorkspaceNodeModules(config) {
183
187
  }
184
188
  };
185
189
  }
190
+ var EXPO_IMAGE_STUB_FILENAME = "expo-image-stub.js";
191
+ var EXPO_IMAGE_STUB_PATH = import_path2.default.resolve(__dirname, "stubs", EXPO_IMAGE_STUB_FILENAME);
192
+ var EXPO_CAMERA_STUB_FILENAME = "expo-camera-stub.js";
193
+ var EXPO_CAMERA_STUB_PATH = import_path2.default.resolve(__dirname, "stubs", EXPO_CAMERA_STUB_FILENAME);
194
+ function withCssInterop(config) {
195
+ const upstream = config.resolver?.resolveRequest ?? null;
196
+ const resolveRequest = (context, moduleName, platform) => {
197
+ if (moduleName === "expo-image" && !context.originModulePath.includes(EXPO_IMAGE_STUB_FILENAME)) {
198
+ return { filePath: EXPO_IMAGE_STUB_PATH, type: "sourceFile" };
199
+ }
200
+ if (moduleName === "expo-camera" && !context.originModulePath.includes(EXPO_CAMERA_STUB_FILENAME)) {
201
+ return { filePath: EXPO_CAMERA_STUB_PATH, type: "sourceFile" };
202
+ }
203
+ if (upstream) {
204
+ return upstream(context, moduleName, platform);
205
+ }
206
+ return context.resolveRequest(context, moduleName, platform);
207
+ };
208
+ return {
209
+ ...config,
210
+ resolver: {
211
+ ...config.resolver,
212
+ resolveRequest
213
+ }
214
+ };
215
+ }
216
+ function patchNativeWindCachePath(options = {}) {
217
+ const projectRoot = process.cwd();
218
+ const ourCacheDir = import_path2.default.resolve(projectRoot, options.cacheDir ?? ".native-wind-cache");
219
+ const nativewindPkgDir = import_path2.default.dirname(require.resolve("react-native-css-interop/package.json"));
220
+ const nativewindCacheDir = import_path2.default.resolve(nativewindPkgDir, ".cache");
221
+ const moduleId = require.resolve("react-native-css-interop/dist/metro");
222
+ delete require.cache[moduleId];
223
+ let intercepted = false;
224
+ const originalResolve = import_path2.default.resolve;
225
+ import_path2.default.resolve = ((...args) => {
226
+ const result = originalResolve(...args);
227
+ if (result === nativewindCacheDir) {
228
+ intercepted = true;
229
+ return ourCacheDir;
230
+ }
231
+ return result;
232
+ });
233
+ try {
234
+ require(moduleId);
235
+ } finally {
236
+ import_path2.default.resolve = originalResolve;
237
+ }
238
+ if (!intercepted) {
239
+ throw new Error(
240
+ "patchNativeWindCachePath: patch \u672A\u751F\u6548\u2014\u2014react-native-css-interop \u53EF\u80FD\u5DF2\u4E0D\u518D\u901A\u8FC7 path.resolve \u8BA1\u7B97\u7F13\u5B58\u8DEF\u5F84\u3002\u5347\u7EA7\u8BE5\u4F9D\u8D56\u540E\u8BF7\u68C0\u67E5 dist/metro/index.js \u4E2D\u7684\u5B9E\u73B0\u3002"
241
+ );
242
+ }
243
+ }
244
+ try {
245
+ require.resolve("react-native-css-interop/package.json");
246
+ patchNativeWindCachePath();
247
+ } catch (e) {
248
+ if (e.code !== "MODULE_NOT_FOUND") throw e;
249
+ }
250
+ function withNativeWind(config, options) {
251
+ const { withNativeWind: nwWithNativeWind } = require("nativewind/metro");
252
+ return nwWithNativeWind(config, options);
253
+ }
186
254
  // Annotate the CommonJS export names for ESM import in node:
187
255
  0 && (module.exports = {
256
+ patchNativeWindCachePath,
257
+ withCssInterop,
188
258
  withDevStubs,
189
259
  withEntryInjection,
260
+ withNativeWind,
190
261
  withRouteEndpoint,
191
262
  withWorkspaceNodeModules
192
263
  });
package/dist/metro.mjs CHANGED
@@ -1,3 +1,10 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
1
8
  // src/metro.ts
2
9
  import connect from "connect";
3
10
  import fs2 from "fs";
@@ -7,7 +14,7 @@ import path2 from "path";
7
14
  import fs from "fs";
8
15
  import path from "path";
9
16
  var ROUTE_EXT = /\.(tsx|ts|jsx|js)$/;
10
- function getRoutes(appDir) {
17
+ function getRoutes(appDir, rootDir) {
11
18
  const routes = [];
12
19
  function scan(dir, prefix) {
13
20
  let entries;
@@ -33,17 +40,17 @@ function getRoutes(appDir) {
33
40
  pageId: routePath,
34
41
  pageName: routePath,
35
42
  visible: !isDynamic,
36
- path: path.relative(appDir, fullPath)
43
+ path: path.relative(rootDir, fullPath)
37
44
  });
38
45
  }
39
46
  }
40
47
  }
41
48
  scan(appDir, "");
42
- return { routes };
49
+ return { data: routes };
43
50
  }
44
- function createRouteHandler(appDir) {
51
+ function createRouteHandler(appDir, rootDir) {
45
52
  return (_req, res) => {
46
- const routeTree = getRoutes(appDir);
53
+ const routeTree = getRoutes(appDir, rootDir);
47
54
  res.writeHead(200, { "Content-Type": "application/json" });
48
55
  res.end(JSON.stringify(routeTree, null, 2));
49
56
  };
@@ -102,10 +109,11 @@ function withEntryInjection(config, options) {
102
109
  var DEFAULT_ROUTE_ENDPOINT = "/__routes";
103
110
  function withRouteEndpoint(config, options) {
104
111
  const { appDir, endpoint = DEFAULT_ROUTE_ENDPOINT } = options;
112
+ const rootDir = config.projectRoot ?? process.cwd();
105
113
  const upstream = config.server?.enhanceMiddleware ?? null;
106
114
  const enhanceMiddleware = (middleware, metroServer) => {
107
115
  const enhanced = upstream ? upstream(middleware, metroServer) : middleware;
108
- return connect().use(enhanced).use(endpoint, createRouteHandler(appDir));
116
+ return connect().use(enhanced).use(endpoint, createRouteHandler(appDir, rootDir));
109
117
  };
110
118
  return {
111
119
  ...config,
@@ -146,9 +154,76 @@ function withWorkspaceNodeModules(config) {
146
154
  }
147
155
  };
148
156
  }
157
+ var EXPO_IMAGE_STUB_FILENAME = "expo-image-stub.js";
158
+ var EXPO_IMAGE_STUB_PATH = path2.resolve(__dirname, "stubs", EXPO_IMAGE_STUB_FILENAME);
159
+ var EXPO_CAMERA_STUB_FILENAME = "expo-camera-stub.js";
160
+ var EXPO_CAMERA_STUB_PATH = path2.resolve(__dirname, "stubs", EXPO_CAMERA_STUB_FILENAME);
161
+ function withCssInterop(config) {
162
+ const upstream = config.resolver?.resolveRequest ?? null;
163
+ const resolveRequest = (context, moduleName, platform) => {
164
+ if (moduleName === "expo-image" && !context.originModulePath.includes(EXPO_IMAGE_STUB_FILENAME)) {
165
+ return { filePath: EXPO_IMAGE_STUB_PATH, type: "sourceFile" };
166
+ }
167
+ if (moduleName === "expo-camera" && !context.originModulePath.includes(EXPO_CAMERA_STUB_FILENAME)) {
168
+ return { filePath: EXPO_CAMERA_STUB_PATH, type: "sourceFile" };
169
+ }
170
+ if (upstream) {
171
+ return upstream(context, moduleName, platform);
172
+ }
173
+ return context.resolveRequest(context, moduleName, platform);
174
+ };
175
+ return {
176
+ ...config,
177
+ resolver: {
178
+ ...config.resolver,
179
+ resolveRequest
180
+ }
181
+ };
182
+ }
183
+ function patchNativeWindCachePath(options = {}) {
184
+ const projectRoot = process.cwd();
185
+ const ourCacheDir = path2.resolve(projectRoot, options.cacheDir ?? ".native-wind-cache");
186
+ const nativewindPkgDir = path2.dirname(__require.resolve("react-native-css-interop/package.json"));
187
+ const nativewindCacheDir = path2.resolve(nativewindPkgDir, ".cache");
188
+ const moduleId = __require.resolve("react-native-css-interop/dist/metro");
189
+ delete __require.cache[moduleId];
190
+ let intercepted = false;
191
+ const originalResolve = path2.resolve;
192
+ path2.resolve = ((...args) => {
193
+ const result = originalResolve(...args);
194
+ if (result === nativewindCacheDir) {
195
+ intercepted = true;
196
+ return ourCacheDir;
197
+ }
198
+ return result;
199
+ });
200
+ try {
201
+ __require(moduleId);
202
+ } finally {
203
+ path2.resolve = originalResolve;
204
+ }
205
+ if (!intercepted) {
206
+ throw new Error(
207
+ "patchNativeWindCachePath: patch \u672A\u751F\u6548\u2014\u2014react-native-css-interop \u53EF\u80FD\u5DF2\u4E0D\u518D\u901A\u8FC7 path.resolve \u8BA1\u7B97\u7F13\u5B58\u8DEF\u5F84\u3002\u5347\u7EA7\u8BE5\u4F9D\u8D56\u540E\u8BF7\u68C0\u67E5 dist/metro/index.js \u4E2D\u7684\u5B9E\u73B0\u3002"
208
+ );
209
+ }
210
+ }
211
+ try {
212
+ __require.resolve("react-native-css-interop/package.json");
213
+ patchNativeWindCachePath();
214
+ } catch (e) {
215
+ if (e.code !== "MODULE_NOT_FOUND") throw e;
216
+ }
217
+ function withNativeWind(config, options) {
218
+ const { withNativeWind: nwWithNativeWind } = __require("nativewind/metro");
219
+ return nwWithNativeWind(config, options);
220
+ }
149
221
  export {
222
+ patchNativeWindCachePath,
223
+ withCssInterop,
150
224
  withDevStubs,
151
225
  withEntryInjection,
226
+ withNativeWind,
152
227
  withRouteEndpoint,
153
228
  withWorkspaceNodeModules
154
229
  };
@@ -118,7 +118,7 @@ var noDuplicateExpoRouterUrlRule = {
118
118
  const others = files.filter((f) => f !== rel);
119
119
  if (others.length === 0) return;
120
120
  context.report({
121
- node,
121
+ loc: { start: { line: 1, column: 0 }, end: { line: 1, column: 0 } },
122
122
  messageId: "duplicateUrl",
123
123
  data: { url, other: others.join(", ") }
124
124
  });
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
+ // If the importer is in node compatibility mode or this is not an ESM
18
+ // file that has been converted to a CommonJS file using a Babel-
19
+ // compatible transform (i.e. "__esModule" has not been set), then set
20
+ // "default" to the CommonJS "module.exports" for node compatibility.
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
24
+ var ExpoCamera = __toESM(require("expo-camera"));
25
+ var import_nativewind = require("nativewind");
26
+ (0, import_nativewind.cssInterop)(ExpoCamera.CameraView, { className: "style" });
27
+ module.exports = ExpoCamera;
28
+ //# sourceMappingURL=expo-camera-stub.js.map
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
+ // If the importer is in node compatibility mode or this is not an ESM
18
+ // file that has been converted to a CommonJS file using a Babel-
19
+ // compatible transform (i.e. "__esModule" has not been set), then set
20
+ // "default" to the CommonJS "module.exports" for node compatibility.
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
24
+ var ExpoImage = __toESM(require("expo-image"));
25
+ var import_nativewind = require("nativewind");
26
+ (0, import_nativewind.cssInterop)(ExpoImage.Image, { className: "style" });
27
+ module.exports = ExpoImage;
28
+ //# sourceMappingURL=expo-image-stub.js.map
@@ -0,0 +1,47 @@
1
+ {
2
+ "plugins": ["typescript", "react"],
3
+ "jsPlugins": [
4
+ "eslint-plugin-expo",
5
+ { "name": "expo-config-plugin", "specifier": "miaoda-expo-devkit/rules/no-undeclared-expo-plugin" },
6
+ { "name": "expo-router", "specifier": "miaoda-expo-devkit/rules/no-unstable-expo-router" },
7
+ { "name": "rn-web", "specifier": "miaoda-expo-devkit/rules/no-rn-alert" },
8
+ { "name": "expo-router-url", "specifier": "miaoda-expo-devkit/rules/no-duplicate-expo-router-url" }
9
+ ],
10
+
11
+ "categories": {
12
+ "correctness": "allow"
13
+ },
14
+
15
+ "rules": {
16
+ "expo-config-plugin/no-undeclared-expo-plugin": "error",
17
+ "expo-router/no-unstable-expo-router": "error",
18
+ "rn-web/no-rn-alert": "error",
19
+ "expo-router-url/no-duplicate-expo-router-url": "error",
20
+
21
+ "expo/no-dynamic-env-var": "error",
22
+ "expo/no-env-var-destructuring": "error",
23
+ "expo/use-dom-exports": "error",
24
+
25
+ "no-undef": "error",
26
+ "use-isnan": "error",
27
+ "valid-typeof": "error",
28
+ "no-dupe-keys": "error",
29
+ "no-duplicate-case": "error",
30
+ "no-dupe-class-members": "error",
31
+
32
+ "react/react-in-jsx-scope": "off",
33
+ "react/jsx-no-duplicate-props": "error",
34
+ "react/jsx-no-undef": "error"
35
+ },
36
+
37
+ "env": {
38
+ "browser": true
39
+ },
40
+
41
+ "globals": {
42
+ "__DEV__": "readonly",
43
+ "process": "readonly",
44
+ "global": "readonly",
45
+ "localStorage": "readonly"
46
+ }
47
+ }
package/package.json CHANGED
@@ -1,11 +1,14 @@
1
1
  {
2
2
  "name": "miaoda-expo-devkit",
3
- "version": "0.1.1-beta.3",
3
+ "version": "0.1.1-beta.5",
4
4
  "description": "Expo 应用开发工具集:Sentry DSN 替换 stub、错误/网络捕获、Metro 符号化",
5
5
  "license": "MIT",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.mjs",
8
8
  "types": "./dist/index.d.ts",
9
+ "bin": {
10
+ "devkit-lint": "./dist/cli/lint.js"
11
+ },
9
12
  "exports": {
10
13
  ".": {
11
14
  "import": {
@@ -41,11 +44,13 @@
41
44
  "./rules/no-unstable-expo-router": "./dist/rules/no-unstable-expo-router.js",
42
45
  "./rules/no-rn-alert": "./dist/rules/no-rn-alert.js",
43
46
  "./rules/no-duplicate-expo-router-url": "./dist/rules/no-duplicate-expo-router-url.js",
44
- "./biome": "./biome-config.json"
47
+ "./biome": "./biome-config.json",
48
+ "./oxlint": "./oxlint-config.json"
45
49
  },
46
50
  "files": [
47
51
  "dist",
48
52
  "biome-config.json",
53
+ "oxlint-config.json",
49
54
  "pnpm-config.json",
50
55
  "!dist/**/*.map"
51
56
  ],
@@ -82,6 +87,7 @@
82
87
  "@types/babel__core": "^7.20.5",
83
88
  "@types/connect": "^3.4.38",
84
89
  "@types/node": "^25.0.0",
90
+ "metro": "^0.83.0",
85
91
  "metro-config": "^0.83.0",
86
92
  "metro-resolver": "^0.83.0",
87
93
  "oxlint": "^1.60.0",