UNPKG

one

Version:

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

193 lines (171 loc) • 6.83 kB
import path from 'node:path' import fs from 'node:fs' import { afterAll, describe, expect, it } from 'vitest' import { withOne } from './withOne' const projectRoot = path.resolve(__dirname, '../../') const tmpDirs: string[] = [] function createOneFixtureProject() { const workspaceRoot = path.resolve(__dirname, '../../../../') const tmpDir = fs.mkdtempSync(path.join(workspaceRoot, '.tmp-with-one-')) tmpDirs.push(tmpDir) fs.writeFileSync( path.join(tmpDir, 'package.json'), JSON.stringify({ name: 'tmp-with-one', private: true }) ) fs.writeFileSync( path.join(tmpDir, 'tsconfig.json'), JSON.stringify({ compilerOptions: { paths: {} } }) ) fs.mkdirSync(path.join(tmpDir, 'app')) fs.writeFileSync(path.join(tmpDir, 'app', 'index.tsx'), 'export default null\n') fs.writeFileSync( path.join(tmpDir, 'vite.config.ts'), ` const defaultConfigOverrides = (config) => ({ ...config, watchFolders: [ ...(config.watchFolders || []), ${JSON.stringify(path.join(tmpDir, 'shared'))}, ], resolver: { ...config.resolver, extraNodeModules: { ...config.resolver?.extraNodeModules, 'fixture-singleton': ${JSON.stringify(tmpDir)}, }, }, }) globalThis.__oneOptions = { setupFile: { native: './src/setup-native.ts', }, native: { bundler: 'metro', bundlerOptions: { argv: { projectRoot: ${JSON.stringify(tmpDir)}, }, defaultConfigOverrides, }, }, } globalThis.__vxrnMetroOptions__ = { argv: { projectRoot: ${JSON.stringify(tmpDir)}, }, defaultConfigOverrides, } export default { root: ${JSON.stringify(tmpDir)}, } ` ) return tmpDir } afterAll(() => { for (const tmpDir of tmpDirs) { fs.rmSync(tmpDir, { recursive: true, force: true }) } }) describe('withOne', () => { it('returns a config produced by the production native bundle pipeline', async () => { const config = (await withOne(projectRoot, { loadViteConfig: false })) as any // The result is whatever Metro's loadConfig returns. The shape must // include the resolver and transformer fields the production pipeline // installs. expect(config).toBeTruthy() expect(config.resolver).toBeTruthy() expect(typeof config.resolver.resolveRequest).toBe('function') expect(config.transformer).toBeTruthy() expect(config.transformer.babelTransformerPath).toMatch( /vite-plugin-metro.*babel-transformer/ ) }) it('orders sourceExts so .js wins over .mjs (the proven One fix)', async () => { const config = (await withOne(projectRoot, { loadViteConfig: false })) as any const exts: string[] = config.resolver.sourceExts expect(exts).toContain('mjs') expect(exts).toContain('js') expect(exts).toContain('swift') expect(exts).toContain('kt') // .js must appear before .mjs so platform-aware lookup finds // `.native.js` before `.mjs` for one's dist expect(exts.indexOf('js')).toBeLessThan(exts.indexOf('mjs')) }) it('uses exact WebGPU aliases for the native-features Metro fixture', async () => { const nativeFeatures = path.resolve(__dirname, '../../../../tests/native-features') const config = (await withOne(nativeFeatures)) as any const context = { resolveRequest(_context: unknown, name: string) { return { type: 'sourceFile', filePath: name } }, } const resolved = (name: string) => config.resolver.resolveRequest(context, name, 'ios').filePath expect(resolved('three')).toBe('three/webgpu') expect(resolved('@react-three/fiber')).toBe('@react-three/fiber/dist/react-three-fiber.esm.js') expect(resolved('three/webgpu')).toBe('three/webgpu') expect(resolved('@react-three/fiber/native')).toBe('@react-three/fiber/native') }) it('accepts a project root as the first arg', async () => { // Mirrors `withOne(__dirname)` usage from a generated metro.config.cjs const config = (await withOne(projectRoot, { loadViteConfig: false })) as any expect(config).toBeTruthy() }) it('loads vite.config by default and applies the real native Metro options', async () => { const fixtureRoot = createOneFixtureProject() const config = (await withOne(fixtureRoot)) as any expect(config.resolver.extraNodeModules['fixture-singleton']).toBe(fixtureRoot) expect(config.watchFolders).toContain(path.join(fixtureRoot, 'shared')) }) it('rewrites native default index bundle requests to the One entry', async () => { const config = (await withOne(projectRoot, { loadViteConfig: false })) as any const rewriteRequestUrl = config.server.rewriteRequestUrl // the entry is resolved relative to metro's server root, so the prefix depends on // how `one` is linked (`node_modules/one` when installed, a workspace path in a // monorepo). only the entry itself and the preserved query are meaningful here. expect( rewriteRequestUrl('/index.bundle?platform=ios&dev=true&hot=true&minify=false') ).toMatch(/\/one\/metro-entry\.bundle\?platform=ios&dev=true&hot=true&minify=false$/) }) it('resolves the rewritten bare entry via package lookup, not app-relative', async () => { // metro parses /one/metro-entry.bundle to ./one/metro-entry relative to // the server root, which misses node_modules. the resolver keeps the bare // specifier so package lookup finds node_modules/one/metro-entry.js. const workspaceRoot = path.resolve(__dirname, '../../../../') const fixtureRoot = fs.mkdtempSync(path.join(workspaceRoot, '.tmp-with-one-entry-')) tmpDirs.push(fixtureRoot) fs.writeFileSync( path.join(fixtureRoot, 'package.json'), JSON.stringify({ name: 'tmp-with-one-entry', private: true, main: 'one/metro-entry' }) ) fs.writeFileSync( path.join(fixtureRoot, 'tsconfig.json'), JSON.stringify({ compilerOptions: { paths: {} } }) ) const pkgDir = path.join(fixtureRoot, 'node_modules', 'one') fs.mkdirSync(pkgDir, { recursive: true }) fs.writeFileSync( path.join(pkgDir, 'package.json'), JSON.stringify({ name: 'one', version: '0.0.0' }) ) fs.writeFileSync(path.join(pkgDir, 'metro-entry.js'), 'module.exports = null\n') const config = (await withOne(fixtureRoot, { loadViteConfig: false })) as any expect(config.server.rewriteRequestUrl('/index.bundle?platform=ios&dev=true')).toMatch( /\/one\/metro-entry\.bundle\?platform=ios&dev=true$/ ) const seen: string[] = [] await config.resolver.resolveRequest( { originModulePath: `${fixtureRoot}/.`, resolveRequest: (_ctx: any, name: string) => { seen.push(name) return { type: 'sourceFile', filePath: name } }, }, './one/metro-entry', 'ios' ) expect(seen).toEqual(['one/metro-entry']) }) })