mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-14 13:22:37 +00:00
Restore native app startup (#6734)
* fix(app): install navigator polyfill before any app module loads @xterm/headless reads navigator.userAgent at module evaluation. Since #6455 the root layout transitively imports it, so the layout's module-level polyfillNavigator() ran too late and native builds crashed at launch with "Cannot read property 'includes' of undefined". Install the crypto, navigator and screen.orientation polyfills from a side-effect module imported first by index.ts. Calls written in index.ts itself ran after all hoisted imports had evaluated. Fixes #6504 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): isolate native startup regression from shared globals Exercise the real navigator polyfill and terminal dependency in fresh VM contexts, including the pre-initialization failure. Preserve a check that entry imports install navigator before the app loads. Co-authored-by: panrafal <rl@stamina.pl> * test(app): follow startup import lint rule --------- Co-authored-by: panrafal <rl@stamina.pl> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
panrafal
parent
bfe38d92f6
commit
e867773932
@@ -1,10 +1,6 @@
|
||||
// Polyfill crypto.randomUUID for React Native before any other imports
|
||||
import { polyfillCrypto } from "./src/polyfills/crypto";
|
||||
polyfillCrypto();
|
||||
|
||||
// Polyfill screen.orientation for WebKitGTK desktop runtimes that lack the API.
|
||||
import { polyfillScreenOrientation } from "./src/polyfills/screen-orientation";
|
||||
polyfillScreenOrientation();
|
||||
// Install runtime polyfills before any other module evaluates. Keep this the first import.
|
||||
// oxlint-disable-next-line import/no-unassigned-import -- Polyfills install on import.
|
||||
import "./src/polyfills/install-early";
|
||||
|
||||
// Configure Unistyles before Expo Router pulls in any components using StyleSheet.
|
||||
import "./src/styles/unistyles";
|
||||
|
||||
@@ -103,7 +103,6 @@ import {
|
||||
useKeyboardActionDispatcher,
|
||||
} from "@/keyboard/keyboard-action-dispatcher-context";
|
||||
import { polyfillCrypto } from "@/polyfills/crypto";
|
||||
import { polyfillNavigator } from "@/polyfills/navigator";
|
||||
import {
|
||||
getHostRuntimeStore,
|
||||
hasConfiguredLocalDaemonOverride,
|
||||
@@ -139,7 +138,6 @@ import {
|
||||
type WebNotificationClickDetail,
|
||||
} from "@/utils/os-notifications";
|
||||
|
||||
polyfillNavigator();
|
||||
polyfillCrypto();
|
||||
|
||||
export interface HostRuntimeBootstrapState {
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
// Side-effect module imported first by index.ts. Babel hoists imports above
|
||||
// statements, so polyfill calls written in index.ts would run only after every
|
||||
// imported module had evaluated. Modules that read these globals at load time,
|
||||
// such as @xterm/headless reading navigator.userAgent, need them installed here.
|
||||
import { polyfillCrypto } from "./crypto";
|
||||
import { polyfillNavigator } from "./navigator";
|
||||
import { polyfillScreenOrientation } from "./screen-orientation";
|
||||
|
||||
polyfillCrypto();
|
||||
polyfillNavigator();
|
||||
// Polyfill screen.orientation for WebKitGTK desktop runtimes that lack the API.
|
||||
polyfillScreenOrientation();
|
||||
@@ -0,0 +1,56 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import { createContext, runInContext } from "node:vm";
|
||||
import ts from "typescript";
|
||||
import { expect, it } from "vitest";
|
||||
|
||||
it("installs navigator before the app entry loads its other dependencies", () => {
|
||||
const entry = ts.createSourceFile(
|
||||
"index.ts",
|
||||
readFileSync(new URL("../../index.ts", import.meta.url), "utf8"),
|
||||
ts.ScriptTarget.Latest,
|
||||
);
|
||||
const firstImport = entry.statements.find(ts.isImportDeclaration);
|
||||
expect(firstImport?.moduleSpecifier.getText(entry)).toBe('"./src/polyfills/install-early"');
|
||||
const early = ts.createSourceFile(
|
||||
"install-early.ts",
|
||||
readFileSync(new URL("./install-early.ts", import.meta.url), "utf8"),
|
||||
ts.ScriptTarget.Latest,
|
||||
);
|
||||
expect(
|
||||
early.statements.some(
|
||||
(statement) =>
|
||||
ts.isExpressionStatement(statement) &&
|
||||
ts.isCallExpression(statement.expression) &&
|
||||
statement.expression.expression.getText(early) === "polyfillNavigator",
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("initializes the native navigator before evaluating the real terminal dependency", () => {
|
||||
const require = createRequire(import.meta.url);
|
||||
const terminalSource = readFileSync(require.resolve("@xterm/headless"), "utf8");
|
||||
const navigatorSource = ts.transpileModule(
|
||||
readFileSync(new URL("./navigator.ts", import.meta.url), "utf8"),
|
||||
{ compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022 } },
|
||||
).outputText;
|
||||
// Each VM owns its globals, so the native navigator shape never replaces
|
||||
// globals in the test runner or needs a native-module mock.
|
||||
const uninitialized = createContext({
|
||||
navigator: {},
|
||||
window: {},
|
||||
module: { exports: {} },
|
||||
exports: {},
|
||||
});
|
||||
expect(() => runInContext(terminalSource, uninitialized)).toThrow(/includes/);
|
||||
|
||||
const initialized = createContext({
|
||||
navigator: {},
|
||||
window: {},
|
||||
module: { exports: {} },
|
||||
exports: {},
|
||||
});
|
||||
runInContext(navigatorSource, initialized);
|
||||
runInContext("exports.polyfillNavigator()", initialized);
|
||||
expect(() => runInContext(terminalSource, initialized)).not.toThrow();
|
||||
});
|
||||
Reference in New Issue
Block a user