UNPKG

one

Version:

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

179 lines (163 loc) • 6.54 kB
import path from 'node:path' import { transformSync } from '@babel/core' import { describe, expect, it } from 'vitest' import oneBabelPreset, { buildOneBabelPlugins } from './index' const projectRoot = path.resolve(__dirname, '../../') /** * Integration test: end-to-end load oneBabelPreset through @babel/core and * run it against representative One sources. Catches breakage in: * - plugin specifier resolution (each `'one/babel-plugin-*'` must resolve) * - plugin option shape (each plugin gets a valid config) * - chain ordering (plugins compose without conflicts) * * Per-plugin semantic tests live alongside each plugin file. This file only * verifies that the preset wires them up correctly. */ describe('one/babel-preset integration', () => { // wiring tests run the One chain directly so assertions see untransformed // modules; syntax tests below run the full preset with its RN base. const onePlugins = buildOneBabelPlugins({ projectRoot, relativeRouterRoot: 'app' }) const presetWithBase = [oneBabelPreset, { projectRoot }] as const // caller the Metro babel transformer sets on the `one dev` path, where the // Vite plugin already injected the One chain via customTransformOptions. const metroViteCaller = { name: 'metro', bundler: 'metro', platform: 'ios', projectRoot, oneViteMetroBabelConfig: true, } it('runs against a route file without throwing', () => { const code = ` import { useLoader } from 'one' export default function Page() { return null } ` const result = transformSync(code, { filename: path.join(projectRoot, 'app/index.tsx'), cwd: projectRoot, plugins: onePlugins, parserOpts: { sourceType: 'module', plugins: ['jsx'] }, }) expect(result?.code).toBeTruthy() // module-resolver should turn 'one' into a relative path under this // package because tsconfig.base.json has `one` aliased to packages/one // No assertion on the exact path — but it must still mention 'useLoader' expect(result?.code).toContain('useLoader') }) it('substitutes ONE_ROUTER_* process.env placeholders in metro-entry', () => { // metro-entry-ctx.js uses process.env.ONE_ROUTER_* as placeholders that // one-router-metro replaces with the configured values at bundle time. const code = ` const ctx = require.context( process.env.ONE_ROUTER_APP_ROOT_RELATIVE_TO_ENTRY, true, process.env.ONE_ROUTER_REQUIRE_CONTEXT_REGEX_STRING ) const folder = process.env.ONE_ROUTER_ROOT_FOLDER_NAME module.exports = { ctx, folder } ` const result = transformSync(code, { filename: path.join(projectRoot, 'metro-entry-ctx.js'), cwd: projectRoot, plugins: onePlugins, parserOpts: { sourceType: 'module' }, }) expect(result?.code).toBeTruthy() // placeholders must be replaced expect(result?.code).not.toContain( 'process.env.ONE_ROUTER_APP_ROOT_RELATIVE_TO_ENTRY' ) expect(result?.code).not.toContain('process.env.ONE_ROUTER_ROOT_FOLDER_NAME') expect(result?.code).not.toContain( 'process.env.ONE_ROUTER_REQUIRE_CONTEXT_REGEX_STRING' ) // routerRoot should be inlined as 'app' expect(result?.code).toContain('"app"') // require-context regex should be inlined as a regex literal expect(result?.code).toMatch(/\/\^.*\.tsx\?\$\//) }) it('keeps client code intact in non-route files', () => { const code = ` const x = 1 export default x ` const result = transformSync(code, { filename: path.join(projectRoot, 'src/utils/x.ts'), cwd: projectRoot, plugins: onePlugins, parserOpts: { sourceType: 'module' }, }) expect(result?.code).toContain('const x = 1') }) it('strips `import type` on the one dev Metro path', () => { // every route file is TS. with no base preset Metro dies with // `TransformError: Unexpected token, expected "from"` on this line. const code = ` import { Tabs } from 'one' import type { BottomTabNavigationOptions } from '@react-navigation/bottom-tabs' export default function Layout() { return null } ` const result = transformSync(code, { filename: path.join(projectRoot, 'app/(tabs)/_layout.tsx'), cwd: projectRoot, presets: [presetWithBase], caller: metroViteCaller, }) expect(result?.code).toBeTruthy() expect(result?.code).not.toContain('import type') expect(result?.code).toContain('Layout') }) it('strips flow `import typeof` on the one dev Metro path', () => { // react-native/index.js ships `import typeof ... from './index.js.flow'`. // unstripped, Metro tries to resolve the .flow file and the iOS bundle // 500s with UnableToResolveError. const code = `import typeof * as ReactNativePublicAPI from './index.js.flow' const x = 1 module.exports = x ` const result = transformSync(code, { filename: path.join(projectRoot, 'node_modules/react-native/index.js'), cwd: projectRoot, presets: [presetWithBase], caller: metroViteCaller, }) expect(result?.code).toBeTruthy() expect(result?.code).not.toContain('index.js.flow') }) it('executes namespace re-exports on the Metro path', () => { const result = transformSync('export * as core from "./core.js"', { filename: path.join(projectRoot, 'node_modules/dependency/index.js'), cwd: projectRoot, presets: [presetWithBase], caller: metroViteCaller, }) const exports: Record<string, unknown> = {} const core = { answer: 42 } new Function('exports', 'require', result?.code ?? '')(exports, () => core) expect(exports.core).toMatchObject(core) }) it('removes namespace type exports before transforming runtime exports', () => { const result = transformSync( 'export type * as Types from "./types"; export * as core from "./core.js"', { filename: path.join(projectRoot, 'src/namespace.ts'), cwd: projectRoot, presets: [presetWithBase], caller: metroViteCaller, } ) const requested: string[] = [] const exports: Record<string, unknown> = {} new Function('exports', 'require', result?.code ?? '')(exports, (id: string) => { requested.push(id) return { answer: 42 } }) expect(requested).toEqual(['./core.js']) expect(exports.core).toMatchObject({ answer: 42 }) expect(exports).not.toHaveProperty('Types') }) })