@nativescript/vite 0.0.1-alpha.0 → 0.0.1-alpha.2

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 (55) hide show
  1. package/dist/configuration/base.js +178 -71
  2. package/dist/helpers/external-configs.js +1 -1
  3. package/dist/helpers/global-defines.d.ts +1 -0
  4. package/dist/helpers/global-defines.js +2 -0
  5. package/dist/helpers/main-entry-hmr-includes.d.ts +1 -0
  6. package/dist/helpers/main-entry-hmr-includes.js +18 -0
  7. package/dist/helpers/main-entry.d.ts +3 -3
  8. package/dist/helpers/main-entry.js +61 -54
  9. package/dist/helpers/module-runner-patch.d.ts +3 -0
  10. package/dist/helpers/module-runner-patch.js +65 -0
  11. package/dist/helpers/ns-cli-plugins.d.ts +4 -14
  12. package/dist/helpers/ns-cli-plugins.js +124 -101
  13. package/dist/helpers/package-platform-aliases.d.ts +1 -1
  14. package/dist/helpers/package-platform-aliases.js +4 -4
  15. package/dist/helpers/preserve-imports.d.ts +2 -0
  16. package/dist/helpers/preserve-imports.js +19 -0
  17. package/dist/helpers/ts-config-paths.d.ts +1 -1
  18. package/dist/helpers/ts-config-paths.js +6 -4
  19. package/dist/hmr/client-vue.d.ts +6 -0
  20. package/dist/hmr/client-vue.js +563 -0
  21. package/dist/hmr/component-tracker.d.ts +23 -0
  22. package/dist/hmr/component-tracker.js +193 -0
  23. package/dist/hmr/css-handler.d.ts +4 -0
  24. package/dist/hmr/css-handler.js +77 -0
  25. package/dist/hmr/message-handler.d.ts +1 -0
  26. package/dist/hmr/message-handler.js +590 -0
  27. package/dist/hmr/nsv-hooks.d.ts +2 -0
  28. package/dist/hmr/nsv-hooks.js +481 -0
  29. package/dist/hmr/plugin-vue.d.ts +2 -0
  30. package/dist/hmr/plugin-vue.js +38 -0
  31. package/dist/hmr/plugins/index.d.ts +1 -0
  32. package/dist/hmr/plugins/index.js +16 -0
  33. package/dist/hmr/plugins/plugin-vue.d.ts +2 -0
  34. package/dist/hmr/plugins/plugin-vue.js +41 -0
  35. package/dist/hmr/plugins/websocket-vue.d.ts +2 -0
  36. package/dist/hmr/plugins/websocket-vue.js +882 -0
  37. package/dist/hmr/runtime-vue.d.ts +13 -0
  38. package/dist/hmr/runtime-vue.js +2306 -0
  39. package/dist/hmr/types.d.ts +24 -0
  40. package/dist/hmr/types.js +2 -0
  41. package/dist/hmr/websocket-vue.d.ts +2 -0
  42. package/dist/hmr/websocket-vue.js +875 -0
  43. package/dist/shims/node-module.d.ts +5 -0
  44. package/dist/shims/node-module.js +12 -0
  45. package/package.json +2 -1
  46. package/dist/configuration/old-without-merge-base.d.ts +0 -13
  47. package/dist/configuration/old-without-merge-base.js +0 -249
  48. package/dist/hmr/hmr-angular.d.ts +0 -1
  49. package/dist/hmr/hmr-angular.js +0 -34
  50. package/dist/hmr/hmr-bridge.d.ts +0 -18
  51. package/dist/hmr/hmr-bridge.js +0 -154
  52. package/dist/hmr/hmr-client.d.ts +0 -5
  53. package/dist/hmr/hmr-client.js +0 -93
  54. package/dist/hmr/hmr-server.d.ts +0 -20
  55. package/dist/hmr/hmr-server.js +0 -179
@@ -1,8 +1,11 @@
1
- import { mergeConfig } from "vite";
1
+ import { mergeConfig, createLogger } from "vite";
2
2
  import path from "path";
3
- import { existsSync } from "fs";
3
+ import { existsSync, readFileSync } from "fs";
4
+ import { createRequire } from "node:module";
5
+ import { pathToFileURL } from "node:url";
4
6
  import minimist from "minimist";
5
7
  import commonjs from "@rollup/plugin-commonjs";
8
+ import replace from "@rollup/plugin-replace";
6
9
  import { viteStaticCopy } from "vite-plugin-static-copy";
7
10
  import NativeScriptPlugin from "../helpers/resolver.js";
8
11
  import nsConfigAsJsonPlugin from "../helpers/config-as-json.js";
@@ -14,12 +17,18 @@ import { getWorkerPlugins, workerUrlPlugin } from "../helpers/workers.js";
14
17
  import { getTsConfigData } from "../helpers/ts-config-paths.js";
15
18
  import { commonjsPlugins } from "../helpers/commonjs-plugins.js";
16
19
  import { nativescriptPackageResolver } from "../helpers/nativescript-package-resolver.js";
17
- import { hmrPlugin, cliPlugin } from "../helpers/ns-cli-plugins.js";
20
+ import { cliPlugin } from "../helpers/ns-cli-plugins.js";
18
21
  import { dynamicImportPlugin } from "../helpers/dynamic-import-plugin.js";
19
22
  import { mainEntryPlugin } from "../helpers/main-entry.js";
20
23
  import { determineProjectFlavor } from "../helpers/flavor.js";
24
+ import { preserveImportsPlugin } from "../helpers/preserve-imports.js";
21
25
  import { externalConfigMerges, applyExternalConfigs, } from "../helpers/external-configs.js";
22
- const debugViteLogs = !!process.env.VITE_DEBUG_PATHS;
26
+ import { getHMRPlugins } from "../hmr/plugins/index.js";
27
+ const require = createRequire(import.meta.url);
28
+ const distOutputFolder = process.env.NS_VITE_DIST_DIR || ".ns-vite-build";
29
+ const verboseLogs = !!process.env.VITE_DEBUG_LOGS;
30
+ // HMR dev server options with socket
31
+ const useHttps = process.env.NS_HTTPS === "1" || process.env.NS_HTTPS === "true";
23
32
  const projectRoot = getProjectRootPath();
24
33
  /**
25
34
  * Plugins can define nativescript.vite.mjs
@@ -27,8 +36,7 @@ const projectRoot = getProjectRootPath();
27
36
  */
28
37
  applyExternalConfigs();
29
38
  export const baseConfig = ({ mode }) => {
30
- const platform = mode;
31
- const targetMode = process.env.production ? "production" : "development";
39
+ const targetMode = mode === "development" ? "development" : "production";
32
40
  const cliArgs = minimist(process.argv.slice(2), { "--": true });
33
41
  const cliFlags = (cliArgs["--"] || []).reduce((obj, flag) => {
34
42
  // remove env prefix
@@ -36,20 +44,67 @@ export const baseConfig = ({ mode }) => {
36
44
  obj[rawKey] = rest.length === 0 ? true : rest.join("=");
37
45
  return obj;
38
46
  }, {});
39
- console.log("cliFlags:", cliFlags);
40
- const debug = !!cliFlags.viteDebug || !!process.env.DEBUG || targetMode === "development";
47
+ // console.log("cliFlags:", cliFlags);
48
+ const isDevMode = targetMode === "development";
49
+ const debug = !!process.env.DEBUG || isDevMode;
50
+ const hmrActive = isDevMode && !!cliFlags.hmr;
51
+ const platform = cliFlags.platform || "ios";
41
52
  if (debug) {
42
53
  console.log("--------------");
43
- console.log("Vite config mode:", mode);
54
+ // console.log("Vite config mode:", mode);
44
55
  console.log("Target mode:", targetMode);
45
56
  console.log("Platform:", platform);
57
+ console.log("HMR active:", hmrActive);
46
58
  console.log("--------------");
47
59
  }
48
60
  const flavor = determineProjectFlavor();
49
61
  console.log(`Building for ${flavor}.`);
62
+ // Suppress extremely noisy sourcemap warnings for third-party packages
63
+ // These occur because published packages often omit original TS sources referenced by their .map files.
64
+ // They have no runtime effect but clutter the console in dev.
65
+ const baseLogger = createLogger(undefined, { allowClearScreen: true });
66
+ const filteredLogger = {
67
+ ...baseLogger,
68
+ warn(message, options) {
69
+ const msg = message;
70
+ if (msg.startsWith("Sourcemap for ") &&
71
+ msg.includes("missing source files")) {
72
+ // Swallow this specific noisy warning
73
+ return;
74
+ }
75
+ return baseLogger.warn(message, options);
76
+ },
77
+ warnOnce(message) {
78
+ const msg = message;
79
+ if (msg.startsWith("Sourcemap for ") &&
80
+ msg.includes("missing source files")) {
81
+ return;
82
+ }
83
+ return baseLogger.warnOnce(message);
84
+ },
85
+ };
50
86
  // Create TypeScript aliases with platform support
51
- const tsConfigData = getTsConfigData(debugViteLogs, platform);
87
+ const tsConfigData = getTsConfigData(verboseLogs, platform);
52
88
  // Common resolve configuration for both main and worker builds
89
+ // Build platform-aware extension preference order and exclude the opposite platform
90
+ const platformExtensions = (() => {
91
+ const base = [".tsx", ".jsx", ".ts", ".js"];
92
+ const exts = [];
93
+ if (platform === "android") {
94
+ exts.push(".android.tsx", ".tsx", ".android.jsx", ".jsx", ".android.ts", ".ts", ".android.js", ".js");
95
+ }
96
+ else if (platform === "ios" || platform === "visionos") {
97
+ // Treat visionOS like iOS for file resolution
98
+ exts.push(".ios.tsx", ".tsx", ".ios.jsx", ".jsx", ".ios.ts", ".ts", ".ios.js", ".js");
99
+ }
100
+ else {
101
+ // Fallback: no platform-specific preference
102
+ exts.push(...base);
103
+ }
104
+ // Always allow these last
105
+ exts.push(".mjs", ".json");
106
+ return exts;
107
+ })();
53
108
  const resolveConfig = {
54
109
  // ensures Vite prefers ESM entry‑points
55
110
  mainFields: ["module", "main"],
@@ -59,36 +114,31 @@ export const baseConfig = ({ mode }) => {
59
114
  dedupe: ["@nativescript/core", "nativescript-vue", "vue"],
60
115
  // Alias "@" and "~" to your src directory for cleaner imports
61
116
  alias: [
117
+ // Provide a shim for node:module to avoid runtime crashes in NS
118
+ {
119
+ find: /^node:module$/,
120
+ replacement: path.resolve(path.dirname(new URL(import.meta.url).pathname), "../shims/node-module.js"),
121
+ },
122
+ // Ensure set-value resolves to an absolute shim to avoid alias warnings and duplication
123
+ [
124
+ {
125
+ find: /^set-value$/,
126
+ replacement: require.resolve("@nativescript/vite/dist/shims/set-value.js"),
127
+ },
128
+ ],
62
129
  ...aliasCssTree,
63
130
  // 1) Catch exactly `~/package.json` → virtual module (MUST be first!)
64
131
  { find: /^~\/package\.json$/, replacement: "~/package.json" },
65
132
  // TypeScript path aliases from tsconfig.json
66
133
  ...tsConfigData.aliases,
67
134
  // Generic platform resolution for any npm package
68
- packagePlatformAliases(tsConfigData, platform,
69
- // skipCommonjsPackages,
70
- debugViteLogs),
135
+ packagePlatformAliases(tsConfigData, platform, verboseLogs),
71
136
  // 2) Catch everything else under "~/" → your src/
72
137
  { find: /^~\/(.*)$/, replacement: path.resolve(projectRoot, "src/$1") },
73
138
  // optional: "@" → src/
74
139
  { find: "@", replacement: path.resolve(projectRoot, "src") },
75
140
  ],
76
- extensions: [
77
- ".ios.tsx",
78
- ".android.tsx",
79
- ".tsx",
80
- ".ios.jsx",
81
- ".android.jsx",
82
- ".jsx",
83
- ".ios.ts",
84
- ".android.ts",
85
- ".ts",
86
- ".ios.js",
87
- ".android.js",
88
- ".js",
89
- ".mjs",
90
- ".json",
91
- ],
141
+ extensions: platformExtensions,
92
142
  preserveSymlinks: true,
93
143
  };
94
144
  // Common define configuration for both main and worker builds
@@ -112,21 +162,20 @@ export const baseConfig = ({ mode }) => {
112
162
  staticCopyTargets.push({ src: `${fontsAppDir}/**/*`, dest: "fonts" });
113
163
  }
114
164
  let baseViteConfig = {
165
+ // Suppress logs during HMR development if desired:
166
+ // ...(hmrActive ? { logLevel: "warn" as const } : {}),
167
+ // Filter out noisy sourcemap warnings from dependencies while keeping other warnings intact
168
+ customLogger: filteredLogger,
115
169
  resolve: resolveConfig,
116
- define: defineConfig,
170
+ // Propagate a single verbose flag into the runtime so device-side code can gate logs consistently
171
+ define: {
172
+ ...defineConfig,
173
+ __NS_ENV_VERBOSE__: verboseLogs,
174
+ },
117
175
  // Vite's built-in solution for CommonJS/ESM compatibility issues
118
176
  optimizeDeps: {
119
177
  // Force pre-bundling of problematic CommonJS packages
120
- include: [
121
- // "source-map-js",
122
- "@valor/nativescript-websockets",
123
- // React and related packages for proper module resolution
124
- "react",
125
- "react-reconciler",
126
- "react-nativescript",
127
- // ...(optimizeDeps || []),
128
- // Add any other problematic packages here
129
- ],
178
+ include: [],
130
179
  // Handle Node.js built-ins and other edge cases
131
180
  esbuildOptions: {
132
181
  // Pass the same conditions to ESBuild for css-tree compatibility
@@ -134,10 +183,39 @@ export const baseConfig = ({ mode }) => {
134
183
  // Define globals for Node.js built-ins if needed
135
184
  define: {
136
185
  global: "globalThis",
186
+ "process.env.NODE_ENV": JSON.stringify(debug ? "development" : "production"),
137
187
  },
138
188
  },
189
+ // Avoid pre-bundling NativeScript core to prevent esbuild from stripping side-effectful modules
190
+ exclude: [
191
+ "@nativescript/core",
192
+ "@nativescript/core/ui/frame",
193
+ "@nativescript/core/ui/frame/activity",
194
+ // Do not prebundle NS-only websocket polyfill or the set-value shim
195
+ "@valor/nativescript-websockets",
196
+ "set-value",
197
+ // Keep React-native related out unless explicitly needed per flavor
198
+ "react",
199
+ "react-reconciler",
200
+ "react-nativescript",
201
+ ],
202
+ },
203
+ esbuild: {
204
+ define: {
205
+ "process.env.NODE_ENV": JSON.stringify(debug ? "development" : "production"),
206
+ },
139
207
  },
140
208
  plugins: [
209
+ // Ensure Rollup phase replaces in node_modules too
210
+ // Important for various vendor handling
211
+ replace({
212
+ "process.env.NODE_ENV": JSON.stringify(debug ? "development" : "production"),
213
+ preventAssignment: true,
214
+ }),
215
+ // Ensure explicit keep markers are honored
216
+ preserveImportsPlugin(),
217
+ // Vue HMR plugins for development mode
218
+ ...(hmrActive ? getHMRPlugins(platform, flavor, verboseLogs) : []),
141
219
  // TODO: make flavor plugins dynamic
142
220
  // ...flavorPlugins,
143
221
  ...commonjsPlugins,
@@ -151,14 +229,12 @@ export const baseConfig = ({ mode }) => {
151
229
  defaultIsModuleExports: "auto",
152
230
  transformMixedEsModules: true,
153
231
  // Ignore optional dependencies that are meant to fail gracefully
154
- ignore: [
155
- "@nativescript/android",
156
- "@nativescript/ios",
157
- ],
232
+ ignore: ["@nativescript/android", "@nativescript/ios"],
158
233
  }),
159
234
  nsConfigAsJsonPlugin(),
160
235
  NativeScriptPlugin({ platform }),
161
- mainEntryPlugin(cliFlags, debug),
236
+ // Ensure globals and Android activity are included early via virtual entry
237
+ mainEntryPlugin(cliFlags, debug, platform),
162
238
  dynamicImportPlugin(),
163
239
  // Transform Vite worker URLs to NativeScript format AFTER bundling
164
240
  workerUrlPlugin(),
@@ -171,22 +247,35 @@ export const baseConfig = ({ mode }) => {
171
247
  }),
172
248
  ]
173
249
  : []),
174
- // NativeScript HMR integration - track changes for incremental builds
175
- hmrPlugin(targetMode),
176
- // NativeScript CLI integration - enhanced IPC communication for HMR
177
- cliPlugin(targetMode, cliFlags),
250
+ // NativeScript CLI integration
251
+ cliPlugin(distOutputFolder, isDevMode, verboseLogs, hmrActive),
178
252
  ],
179
253
  css: {
180
254
  postcss: "./postcss.config.js",
181
255
  },
182
256
  // Development server configuration for HMR
183
- server: targetMode === "development"
257
+ server: isDevMode
184
258
  ? {
259
+ // Expose dev server to local network so simulator or device can connect
260
+ host: process.env.NS_HMR_HOST ||
261
+ (platform === "android" ? "10.0.0.2" : "localhost"),
262
+ // Use a stable port so the device URL remains correct
263
+ port: 5173,
264
+ strictPort: true,
265
+ https: useHttps
266
+ ? {
267
+ // Optional: allow self-signed certs via env paths
268
+ key: process.env.NS_HTTPS_KEY &&
269
+ readFileSync(process.env.NS_HTTPS_KEY),
270
+ cert: process.env.NS_HTTPS_CERT &&
271
+ readFileSync(process.env.NS_HTTPS_CERT),
272
+ }
273
+ : undefined,
274
+ // Keep HMR on the primary server port (Vite browser client stays on /vite-hmr)
185
275
  hmr: {
186
- // Enable Vite's built-in HMR
187
- port: 24678, // Different port to avoid conflicts with our custom WebSocket
276
+ protocol: useHttps ? "wss" : "ws",
277
+ path: "/vite-hmr",
188
278
  },
189
- // CORS for development
190
279
  cors: true,
191
280
  }
192
281
  : {},
@@ -204,15 +293,23 @@ export const baseConfig = ({ mode }) => {
204
293
  },
205
294
  },
206
295
  build: {
296
+ // Ensure Vite and plugins (like vite-plugin-static-copy) use the same output directory
297
+ outDir: path.resolve(projectRoot, distOutputFolder),
207
298
  target: "esnext",
208
299
  minify: !debug,
209
300
  // Generate source maps for debugging
210
- // Note: just disabling for now until can hook up to angular vite/analog plugin
211
- sourcemap: false, //debug,
301
+ // External sourcemaps so DevTools loads small .mjs files and fetches maps on demand
302
+ sourcemap: debug,
212
303
  // Disable module preloading to avoid browser APIs
213
304
  modulePreload: false,
305
+ // Under HMR, avoid rebuilds on src/** changes — device consumes updates via /ns-hmr
306
+ ...(hmrActive && {
307
+ watch: {
308
+ exclude: ["src/**"],
309
+ },
310
+ }),
214
311
  // Optimize for development speed
215
- ...(targetMode === "development" && {
312
+ ...(isDevMode && {
216
313
  // Faster builds in development
217
314
  reportCompressedSize: false,
218
315
  chunkSizeWarningLimit: 2000,
@@ -221,11 +318,19 @@ export const baseConfig = ({ mode }) => {
221
318
  include: [/node_modules/],
222
319
  },
223
320
  rollupOptions: {
321
+ treeshake: {
322
+ // Preserve side effects for NativeScript core so classes/functions
323
+ // aren't tree-shaken out of vendor.mjs.
324
+ moduleSideEffects: (id) => /node_modules[\\\/]\@nativescript[\\\/]core[\\\/]/.test(id) || null,
325
+ },
224
326
  input: "virtual:entry-with-polyfills",
225
327
  output: {
226
- dir: path.resolve(projectRoot, "dist"),
227
328
  format: "es", // Emit ES modules (.mjs)
228
- entryFileNames: "bundle.mjs", // <- no hash here
329
+ entryFileNames: "bundle.mjs",
330
+ // Point source map URLs to absolute file:// paths on the host so
331
+ // Chrome DevTools can fetch them even though the running code comes
332
+ // from file:///app on the simulator/device.
333
+ sourcemapBaseUrl: pathToFileURL(path.resolve(projectRoot, distOutputFolder)).toString() + "/",
229
334
  chunkFileNames: (chunk) => {
230
335
  if (chunk.name === "vendor")
231
336
  return "vendor.mjs";
@@ -239,25 +344,27 @@ export const baseConfig = ({ mode }) => {
239
344
  // Create single vendor chunk - no separate globals chunk to avoid circular deps
240
345
  manualChunks(id) {
241
346
  if (id.includes("node_modules")) {
242
- // Note: this is work in progress on best setup
243
- // Keep polyfills and Zone-dependent packages in main bundle to ensure correct execution order
244
- if ([
245
- "@nativescript/core",
246
- "@nativescript/angular",
247
- "@nativescript/zone-js",
248
- "@valor/nativescript-websockets",
249
- "make-error",
250
- // "source-map-js",
251
- "zone.js",
252
- "/globals",
253
- "/polyfills",
254
- ].includes(id)) {
347
+ // Keep common dependencies in the main bundle
348
+ // Very Important: keep Angular, NS Angular and core in main bundle to avoid issues with DI
349
+ // All flavors can do same if encounter ordering issues
350
+ if (id.includes("@angular/") ||
351
+ id.includes("@nativescript/angular") ||
352
+ id.includes("@nativescript/core")) {
255
353
  return undefined; // Keep in main bundle
256
354
  }
257
355
  return "vendor";
258
356
  }
259
357
  },
260
358
  },
359
+ // When HMR is active, prevent Vite's build watcher from reacting to src/** changes.
360
+ // The device will get updates via socket /ns-hmr instead.
361
+ ...(hmrActive
362
+ ? {
363
+ watch: {
364
+ exclude: ["src/**"],
365
+ },
366
+ }
367
+ : {}),
261
368
  },
262
369
  },
263
370
  };
@@ -16,7 +16,7 @@ export function applyExternalConfigs() {
16
16
  }
17
17
  const configPath = path.join(packagePath, 'nativescript.vite.mjs');
18
18
  if (fs.existsSync(configPath)) {
19
- console.log(`Discovered external config: ${configPath}`);
19
+ console.log(`Found and merged in external config: ${configPath}`);
20
20
  try {
21
21
  const externalModule = require(configPath);
22
22
  const externalConfig = externalModule?.default ?? externalModule;
@@ -11,4 +11,5 @@ export declare function getGlobalDefines(platform: string, targetMode: string):
11
11
  __UI_USE_XML_PARSER__: boolean;
12
12
  __UI_USE_EXTERNAL_RENDERER__: boolean;
13
13
  __TEST__: boolean;
14
+ "process.env.NODE_ENV": string;
14
15
  };
@@ -14,5 +14,7 @@ export function getGlobalDefines(platform, targetMode) {
14
14
  __UI_USE_EXTERNAL_RENDERER__: false,
15
15
  // various ecosystems use this global (react for example)
16
16
  __TEST__: false,
17
+ // Critical for various integrations (e.g. Vue only includes hmr runtime on this conditions)
18
+ "process.env.NODE_ENV": JSON.stringify(targetMode === "development" ? "development" : "production"),
17
19
  };
18
20
  }
@@ -0,0 +1 @@
1
+ export declare function embedMainEntryHMRForFlavor(flavor: string): string;
@@ -0,0 +1,18 @@
1
+ export function embedMainEntryHMRForFlavor(flavor) {
2
+ let imports = "";
3
+ switch (flavor) {
4
+ case "vue":
5
+ // Load HMR client for WebSocket connection to Vite dev server
6
+ imports += "import 'virtual:hmr-vue';\n";
7
+ imports +=
8
+ "console.info('@nativescript/vite HMR Vue client loaded.');\n";
9
+ break;
10
+ case "react":
11
+ break;
12
+ case "angular":
13
+ break;
14
+ case "solid":
15
+ break;
16
+ }
17
+ return imports;
18
+ }
@@ -1,5 +1,5 @@
1
- export declare function mainEntryPlugin(cliFlags: any, debug?: boolean): {
1
+ export declare function mainEntryPlugin(cliFlags: any, debug?: boolean, platform?: "ios" | "android" | "visionos"): {
2
2
  name: string;
3
- resolveId(id: any): string;
4
- load(id: any): string;
3
+ resolveId(id: string): string;
4
+ load(id: string): string;
5
5
  };
@@ -1,75 +1,82 @@
1
1
  import { getPackageJson, getProjectFilePath, getProjectRootPath, } from "./project.js";
2
2
  import fs from "fs";
3
3
  import path from "path";
4
+ import { determineProjectFlavor } from "./flavor.js";
5
+ import { embedMainEntryHMRForFlavor } from "./main-entry-hmr-includes.js";
4
6
  const projectRoot = getProjectRootPath();
5
7
  // main entry
6
8
  const packageJson = getPackageJson();
7
9
  const mainEntry = getProjectFilePath(packageJson.main);
8
- // console.log("mainEntry:", mainEntry);
9
- // hmr client
10
- const hmrClientPath = getProjectFilePath("./node_modules/@nativescript/vite/dist/hmr/hmr-client.js");
11
- const hmrClientExists = fs.existsSync(hmrClientPath);
12
- // console.log("hmrClientPath:", hmrClientPath);
13
- // console.log("hmrClientExists:", hmrClientExists);
10
+ const flavor = determineProjectFlavor();
14
11
  // Check if polyfills file exists
15
12
  const polyfillsPath = getProjectFilePath("src/polyfills.ts");
16
13
  const polyfillsExists = fs.existsSync(polyfillsPath);
17
- // console.log("polyfillsPath:", polyfillsPath);
18
- // console.log("polyfillsExists:", polyfillsExists);
19
- const VIRTUAL_ID = 'virtual:entry-with-polyfills';
20
- const RESOLVED = '\0' + VIRTUAL_ID;
21
- export function mainEntryPlugin(cliFlags, debug) {
14
+ const VIRTUAL_ID = "virtual:entry-with-polyfills";
15
+ const RESOLVED = "\0" + VIRTUAL_ID;
16
+ export function mainEntryPlugin(cliFlags, debug, platform) {
17
+ const HMR_ACTIVE = debug && !!cliFlags.hmr;
22
18
  return {
23
19
  name: "main-entry",
24
20
  resolveId(id) {
25
- if (id === VIRTUAL_ID) {
21
+ if (id === VIRTUAL_ID)
26
22
  return RESOLVED;
27
- }
28
23
  return null;
29
24
  },
30
25
  load(id) {
31
- if (id === RESOLVED) {
32
- let imports = "";
33
- if (polyfillsExists) {
34
- imports += `import '${polyfillsPath}';\n`;
35
- }
36
- // Import WebSocket support for HMR in development
37
- if (debug && cliFlags.hmr) {
38
- imports += `import '${hmrClientPath}';\n`;
39
- }
40
- // Import inspector modules for debugging in development
41
- if (debug) {
42
- imports += `import '@nativescript/core/inspector_modules';\n`;
43
- }
44
- // Import CSS and apply via Application.addCss before main entry
45
- const appCssPath = path.resolve(projectRoot, "src/app.css");
46
- if (fs.existsSync(appCssPath)) {
47
- imports += `
48
- // Import and apply global CSS
49
- import appCssContent from './src/app.css?inline';
50
- import { Application } from '@nativescript/core';
51
-
52
- if (typeof global !== 'undefined' && appCssContent) {
53
- try {
54
- // Just testing logs if needed
55
- // console.log('🎨 CSS content length:', appCssContent.length);
56
- // console.log('🎨 CSS content preview:', appCssContent.substring(0, 200) + '...');
57
-
58
- Application.addCss(appCssContent);
59
- // console.log('🎨 Global CSS applied');
60
- } catch (error) {
61
- console.error('Error applying CSS:', error);
62
- }
63
- }
64
- `;
65
- }
66
- imports += `import '${mainEntry}';`;
67
- if (debug) {
68
- console.log("🔄 Virtual entry imports:", imports);
69
- }
70
- return imports;
26
+ if (id !== RESOLVED)
27
+ return null;
28
+ let imports = "";
29
+ // Align with webpack entry order: core globals and bundle entry points first
30
+ imports += "import '@nativescript/core/globals/index';\n";
31
+ // explicit extension helps some bundlers to retain side effects
32
+ imports += "import '@nativescript/core/bundle-entry-points';\n";
33
+ // Ensure Android app components are included
34
+ if (platform === "android") {
35
+ // Import explicit Android variants with extension to avoid resolution ambiguity
36
+ imports +=
37
+ "import * as __ns_android_frame from '@nativescript/core/ui/frame/index.android.js?ns-keep';\n";
38
+ imports +=
39
+ "import * as __ns_android_activity from '@nativescript/core/ui/frame/activity.android.js?ns-keep';\n";
40
+ // Reference bindings to keep the modules in the graph even if they only have side effects
41
+ imports += "void __ns_android_frame; void __ns_android_activity;\n";
71
42
  }
72
- return null;
43
+ if (polyfillsExists) {
44
+ imports += `import '${polyfillsPath}';\n`;
45
+ imports += `if (__NS_ENV_VERBOSE__) console.info('[ns-entry] polyfills imported from', ${polyfillsPath});\n`;
46
+ }
47
+ else {
48
+ imports += "if (__NS_ENV_VERBOSE__) console.info('[ns-entry] no polyfills file found');\n";
49
+ }
50
+ if (HMR_ACTIVE) {
51
+ // Ensure WebSocket polyfill is loaded early for device runtime
52
+ imports += "import '@valor/nativescript-websockets';\n";
53
+ imports += "if (__NS_ENV_VERBOSE__) console.info('[ns-entry] websockets polyfill imported');\n";
54
+ }
55
+ // Import CSS and apply via Application.addCss before main entry
56
+ const appCssPath = path.resolve(projectRoot, "src/app.css");
57
+ if (fs.existsSync(appCssPath)) {
58
+ imports += `
59
+ // Import and apply global CSS before app bootstrap
60
+ import appCssContent from './src/app.css?inline';
61
+ import { Application } from '@nativescript/core';
62
+ if (appCssContent) {
63
+ try { Application.addCss(appCssContent); } catch (error) { console.error('Error applying CSS:', error); }
64
+ }
65
+ `;
66
+ }
67
+ // Import the main entry file
68
+ imports += `if (__NS_ENV_VERBOSE__) console.info('[ns-entry] Importing main entry', '${mainEntry}');\n`;
69
+ imports += `import '${mainEntry}';\n`;
70
+ if (HMR_ACTIVE) {
71
+ // Start HMR client after app boots
72
+ imports += embedMainEntryHMRForFlavor(flavor);
73
+ }
74
+ // Import inspector modules for debugging in development
75
+ if (debug) {
76
+ imports += "import '@nativescript/core/inspector_modules';\n";
77
+ imports += "if (__NS_ENV_VERBOSE__) console.info('[ns-entry] inspector modules imported');\n";
78
+ }
79
+ return imports;
73
80
  },
74
81
  };
75
82
  }
@@ -0,0 +1,3 @@
1
+ import type { Plugin } from "vite";
2
+ export declare function moduleRunnerPatchPlugin(): Plugin;
3
+ export default moduleRunnerPatchPlugin;
@@ -0,0 +1,65 @@
1
+ // Build-time patch: ensure vendor.mjs's bundled ModuleRunner.getModuleInformation
2
+ // always receives a valid options object with a cache to prevent
3
+ // "Cannot use 'in' operator to search for 'cache' in undefined".
4
+ export function moduleRunnerPatchPlugin() {
5
+ return {
6
+ name: "ns-module-runner-patch",
7
+ apply: "build",
8
+ enforce: "post",
9
+ generateBundle(_opts, bundle) {
10
+ for (const [fileName, chunk] of Object.entries(bundle)) {
11
+ if (fileName !== "vendor.mjs")
12
+ continue;
13
+ if (chunk.type !== "chunk" || typeof chunk.code !== "string")
14
+ continue;
15
+ let code = chunk.code;
16
+ // Try to locate the function definition (non-minified dev build keeps names)
17
+ // Handle both forms: function getModuleInformation(id, options) { ... }
18
+ // and class method or object method: getModuleInformation(id, options) { ... }
19
+ const patterns = [
20
+ /function\s+getModuleInformation\s*\(([^)]*)\)\s*\{/,
21
+ /getModuleInformation\s*\(([^)]*)\)\s*\{/,
22
+ ];
23
+ let replaced = false;
24
+ for (const re of patterns) {
25
+ const m = re.exec(code);
26
+ if (!m)
27
+ continue;
28
+ const rawParams = (m[1] || "")
29
+ .split(",")
30
+ .map((s) => s.trim())
31
+ .filter(Boolean);
32
+ const idParam = rawParams[0];
33
+ const second = rawParams[1];
34
+ const third = rawParams[2];
35
+ let guard = "\n";
36
+ if (third && second) {
37
+ // Signature: (id, importer, options)
38
+ guard += `if (typeof ${second} !== 'string') ${second} = undefined;\n`;
39
+ guard += `${third} = ${third} && typeof ${third}==='object' ? ${third} : {};\n`;
40
+ guard += `if (!('cache' in ${third})) ${third}.cache = new Map();\n`;
41
+ }
42
+ else if (second) {
43
+ // Signature: (id, options)
44
+ guard += `${second} = ${second} && typeof ${second}==='object' ? ${second} : {};\n`;
45
+ guard += `if (!('cache' in ${second})) ${second}.cache = new Map();\n`;
46
+ }
47
+ else {
48
+ // Unknown/minified: use arguments[] positions
49
+ guard += `(function(){ try{ var __imp = arguments[1]; if (typeof __imp !== 'string') arguments[1] = undefined; } catch(_){} })();\n`;
50
+ guard += `(function(){ try{ var __o = arguments[2] ?? arguments[1]; if (!__o || typeof __o!=='object') { __o = {}; if (arguments.length>2) arguments[2]=__o; else arguments[1]=__o; } if (!('cache' in __o)) __o.cache = new Map(); } catch(_){} })();\n`;
51
+ }
52
+ // Inject guard right after the opening brace of the function
53
+ const start = m.index + m[0].length;
54
+ code = code.slice(0, start) + guard + code.slice(start);
55
+ replaced = true;
56
+ break;
57
+ }
58
+ if (replaced) {
59
+ chunk.code = code;
60
+ }
61
+ }
62
+ },
63
+ };
64
+ }
65
+ export default moduleRunnerPatchPlugin;