UNPKG

one

Version:

One is a React framework for web, iOS, and Android apps.

161 lines (152 loc) • 6.11 kB
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join } from 'node:path' import { fileURLToPath } from 'node:url' import { runInNewContext } from 'node:vm' import { rolldown } from 'rolldown' import { resolveConfig, type Plugin } from 'vite' import { describe, expect, test } from 'vitest' import type { NativeAppManifest } from '../native/appManifest' import { one } from './one' // one() resolves the route tree from the process root, so run it from a // fixture app. each test file gets its own worker, and cwd is restored. async function defineFor(app: NativeAppManifest) { const cwd = process.cwd() process.chdir( fileURLToPath(new URL('../../../../tests/native-features', import.meta.url)) ) try { const plugins = (await one({ native: { app } })) as Array<any> const definePlugin = plugins .flat(Infinity) .find((plugin) => plugin?.name === 'one-define-environment') expect(definePlugin).toBeDefined() return (await definePlugin.config()).define as Record<string, string> } finally { process.chdir(cwd) } } describe('one-define-environment app info', () => { test('injects ONE_APP_VERSION from the manifest', async () => { const define = await defineFor({ name: 'ProbeApp', version: '9.9.9', ios: { bundleId: 'dev.probe.ios', buildNumber: '4242' }, android: { applicationId: 'dev.probe.android', versionCode: 4243 }, }) expect(define['process.env.ONE_APP_VERSION']).toBe('"9.9.9"') expect(define['import.meta.env.ONE_APP_VERSION']).toBe('"9.9.9"') // build and application id have no honest web value: never injected, // so the web entry reads null instead of a cross-platform guess. expect(define['process.env.ONE_APP_BUILD']).toBeUndefined() expect(define['import.meta.env.ONE_APP_BUILD']).toBeUndefined() expect(define['process.env.ONE_APP_APPLICATION_ID']).toBeUndefined() expect(define['import.meta.env.ONE_APP_APPLICATION_ID']).toBeUndefined() }) test('defines nothing without a version', async () => { const define = await defineFor({ name: 'ProbeApp', ios: { bundleId: 'dev.probe.ios', buildNumber: '4242' }, android: { applicationId: 'dev.probe.android', versionCode: 4243 }, }) expect(define['process.env.ONE_APP_VERSION']).toBeUndefined() expect(define['import.meta.env.ONE_APP_VERSION']).toBeUndefined() expect(define['process.env.ONE_APP_BUILD']).toBeUndefined() expect(define['process.env.ONE_APP_APPLICATION_ID']).toBeUndefined() }) }) test('native.app leaves a hoisted optional Expo package outside the native bundle', async () => { const root = await mkdtemp(join(tmpdir(), 'one-native-expo-external-')) const fixture = fileURLToPath( new URL('../../../../tests/native-features', import.meta.url) ) const originalCwd = process.cwd() const originalPlugins = globalThis.__vxrnAddNativePlugins try { const expoRoot = join(root, 'node_modules/expo-clipboard') await mkdir(expoRoot, { recursive: true }) await writeFile( join(expoRoot, 'package.json'), '{"name":"expo-clipboard","main":"index.js"}' ) await writeFile( join(expoRoot, 'index.js'), 'globalThis.expoClipboardWasBundled = true' ) await writeFile( join(root, 'entry.js'), `try { require('expo-clipboard') } catch { globalThis.optionalExpoMissing = true }` ) process.chdir(fixture) await one({ native: { app: { name: 'ProbeApp' } } }) process.chdir(originalCwd) const bundle = await rolldown({ input: join(root, 'entry.js'), cwd: root, platform: 'neutral', plugins: globalThis.__vxrnAddNativePlugins?.('ios'), }) const result = await bundle.generate({ format: 'cjs' }) const code = result.output[0].code expect(code).not.toContain('expoClipboardWasBundled') const runtime = { optionalExpoMissing: false } runInNewContext(code, runtime) expect(runtime.optionalExpoMissing).toBe(true) process.chdir(fixture) await one({ native: {} }) process.chdir(originalCwd) const legacyBundle = await rolldown({ input: join(root, 'entry.js'), cwd: root, platform: 'neutral', plugins: globalThis.__vxrnAddNativePlugins?.('ios'), }) const legacyResult = await legacyBundle.generate({ format: 'cjs' }) expect(legacyResult.output[0].code).toContain('expoClipboardWasBundled') } finally { process.chdir(originalCwd) globalThis.__vxrnAddNativePlugins = originalPlugins await rm(root, { recursive: true, force: true }) } }) test.each(['serve', 'build'] as const)( 'vite config plugins with a native form reach the native bundler (%s)', async (command) => { const fixture = fileURLToPath( new URL('../../../../tests/native-features', import.meta.url) ) const originalCwd = process.cwd() const originalPlugins = globalThis.__vxrnAddNativePlugins const nativeForm = (name: string): Plugin => ({ name, api: { vxrnNative: () => ({ name: `${name}-native` }) }, }) const compiler = nativeForm('compiler') // also listed for native by the app, which wins over the vite config const shadowed = nativeForm('listed') const listed: Plugin = { name: 'listed' } const webOnly: Plugin = { name: 'web-only' } try { process.chdir(fixture) await resolveConfig( { configFile: false, plugins: [ one({ native: { bundlerOptions: { plugins: [listed] } } }), // nested, the way a plugin package returns its plugins [webOnly, compiler, shadowed], ], }, command ) const nativePlugins = globalThis.__vxrnAddNativePlugins?.('ios') expect(nativePlugins).toContain(compiler) expect(nativePlugins).toContain(listed) expect(nativePlugins).not.toContain(shadowed) expect(nativePlugins).not.toContain(webOnly) } finally { process.chdir(originalCwd) globalThis.__vxrnAddNativePlugins = originalPlugins } } )