Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/snapshot-testing-paths.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@lynx-js/react": patch
---

Keep ReactLynx Testing Library imports aligned with the contained snapshot runtime paths.
2 changes: 2 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,12 @@ export default tseslint.config(
// Generated worklet bundles are published assets, not source files.
'packages/react/runtime/worklet-runtime/**',
'packages/react/runtime/src/renderToOpcodes/**',
'packages/react/runtime/src/snapshot/renderToOpcodes/**',
'packages/react/runtime/types/**',

// TODO: enable eslint for react-runtime
'packages/react/runtime/src/compat/**',
'packages/react/runtime/src/snapshot/compat/**',
'packages/react/runtime/src/opcodes.ts',

// TODO: enable eslint for tools
Expand Down
18 changes: 9 additions & 9 deletions packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,12 @@
"default": "./runtime/lepus/jsx-runtime/index.js"
},
"./hooks": {
"types": "./runtime/lib/hooks/react.d.ts",
"default": "./runtime/lib/hooks/react.js"
"types": "./runtime/lib/snapshot/hooks/react.d.ts",
"default": "./runtime/lib/snapshot/hooks/react.js"
},
"./lepus/hooks": {
"types": "./runtime/lib/hooks/react.d.ts",
"default": "./runtime/lib/hooks/mainThread.js"
"types": "./runtime/lib/snapshot/hooks/react.d.ts",
"default": "./runtime/lib/snapshot/hooks/mainThread.js"
},
"./lepus": {
"types": "./runtime/lepus/index.d.ts",
Expand Down Expand Up @@ -96,9 +96,9 @@
"default": "./runtime/lib/worklet-runtime/bindings/index.js"
},
"./legacy-react-runtime": {
"types": "./runtime/lib/legacy-react-runtime/index.d.ts",
"types": "./runtime/lib/snapshot/legacy-react-runtime/index.d.ts",
"lazy": "./runtime/lazy/legacy-react-runtime.js",
"default": "./runtime/lib/legacy-react-runtime/index.js"
"default": "./runtime/lib/snapshot/legacy-react-runtime/index.js"
},
"./testing-library": {
"types": "./testing-library/types/index.d.ts",
Expand Down Expand Up @@ -132,10 +132,10 @@
"./runtime/lazy/import.d.ts"
],
"hooks": [
"./runtime/lib/hooks/react.d.ts"
"./runtime/lib/snapshot/hooks/react.d.ts"
],
"lepus/hooks": [
"./runtime/lib/hooks/react.d.ts"
"./runtime/lib/snapshot/hooks/react.d.ts"
],
"internal": [
"./runtime/lib/internal.d.ts"
Expand Down Expand Up @@ -174,7 +174,7 @@
"./runtime/lib/worklet-runtime/bindings/index.d.ts"
],
"legacy-react-runtime": [
"./runtime/lib/legacy-react-runtime/index.d.ts"
"./runtime/lib/snapshot/legacy-react-runtime/index.d.ts"
]
}
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// Copyright 2026 The Lynx Authors. All rights reserved.
// Licensed under the Apache License Version 2.0 that can be found in the
// LICENSE file in the root directory of this source tree.

import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

import { describe, expect, test } from 'vitest';

const testDir = path.dirname(fileURLToPath(import.meta.url));
const runtimeRoot = path.resolve(testDir, '../..');
const reactRoot = path.resolve(runtimeRoot, '..');
const srcRoot = path.join(runtimeRoot, 'src');
const testRoot = path.join(runtimeRoot, '__test__');
const transformRoot = path.join(reactRoot, 'transform');

const legacySourceDirs = [
'alog',
'compat',
'debug',
'gesture',
'hooks',
'legacy-react-runtime',
'lifecycle',
'list',
'lynx',
'renderToOpcodes',
'worklet',
];
const legacySourceDirPattern = legacySourceDirs.join('|');
const knownTransformSourcePathExceptions = new Set([
'transform/src/swc_plugin_compat_post/mod.rs',
'transform/tests/__swc_snapshots__/src/swc_plugin_compat_post/mod.rs/should_compat_dark_mode.js',
'transform/tests/__swc_snapshots__/src/swc_plugin_compat_post/mod.rs/should_compat_dark_mode_custom_theme_expr.js',
]);

const containedTestDirs = [
'alog',
'compat',
'css',
'debug',
'gesture',
'hooks',
'lifecycle',
'lynx',
'utils',
'worklet',
];

function toPosixPath(file: string): string {
return file.split(path.sep).join('/');
}

function walkFiles(dir: string, pattern = /\.(?:[cm]?[jt]sx?)$/): string[] {
if (!fs.existsSync(dir)) {
return [];
}
return fs.readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
const target = path.join(dir, entry.name);
if (entry.isDirectory()) {
return walkFiles(target, pattern);
}
return pattern.test(entry.name) ? [target] : [];
});
}

describe('snapshot containment guardrails', () => {
test('keeps legacy runtime implementation directories under snapshot backend', () => {
for (const dir of legacySourceDirs) {
expect(fs.existsSync(path.join(srcRoot, dir)), dir).toBe(false);
expect(fs.existsSync(path.join(srcRoot, 'snapshot', dir)), dir).toBe(true);
}
});

test('keeps legacy runtime tests under snapshot test namespace', () => {
for (const dir of containedTestDirs) {
expect(fs.existsSync(path.join(testRoot, dir)), dir).toBe(false);
expect(fs.existsSync(path.join(testRoot, 'snapshot', dir)), dir).toBe(true);
}
});

test('keeps loose legacy runtime tests under snapshot test namespace', () => {
const looseRuntimeTests = fs.readdirSync(testRoot, { withFileTypes: true })
.filter((entry) => entry.isFile())
.filter((entry) => /\.(?:[cm]?[jt]sx?)$/.test(entry.name))
.map((entry) => entry.name);

expect(looseRuntimeTests).toEqual([]);
});

test('does not import old implementation paths outside the snapshot backend', () => {
const oldPathPattern = new RegExp(
String.raw`(?:from\s+|import\s*\(\s*)['"](?:\./|\../)+(?:${legacySourceDirPattern})(?:/|['"])`,
);
const offenders = walkFiles(srcRoot)
.filter((file) => !file.includes(`${path.sep}src${path.sep}snapshot${path.sep}`))
.filter((file) => oldPathPattern.test(fs.readFileSync(file, 'utf8')))
.map((file) => path.relative(runtimeRoot, file));

expect(offenders).toEqual([]);
});

test('prevents element-template from depending on snapshot private paths', () => {
const elementTemplateRoot = path.join(srcRoot, 'element-template');
const forbiddenImportPattern =
/(?:from\s+|import\s*\(\s*)['"](?:\.\/|\.\.\/)+(?:snapshot|internal|root|lifecycle|renderToOpcodes)(?:\/|['"])/;
const offenders = walkFiles(elementTemplateRoot)
.filter((file) => forbiddenImportPattern.test(fs.readFileSync(file, 'utf8')))
.map((file) => path.relative(runtimeRoot, file));

expect(offenders).toEqual([]);
});

test('prevents transform output from referencing untracked old runtime source paths', () => {
const oldPackageSourcePattern = new RegExp(
String.raw`@lynx-js/react/src/(?:${legacySourceDirPattern})(?:/|['"])`,
);
const offenders = walkFiles(transformRoot, /\.(?:rs|[cm]?[jt]sx?)$/)
.map((file) => toPosixPath(path.relative(reactRoot, file)))
.filter((file) => !knownTransformSourcePathExceptions.has(file))
.filter((file) =>
oldPackageSourcePattern.test(
fs.readFileSync(path.join(reactRoot, file), 'utf8'),
)
);

expect(offenders).toEqual([]);
});

test('keeps package type metadata aligned with contained runtime paths', () => {
const packageJson = JSON.parse(
fs.readFileSync(path.join(reactRoot, 'package.json'), 'utf8'),
);
const typesVersions = JSON.stringify(packageJson.typesVersions ?? {});

expect(typesVersions).not.toContain('./runtime/lib/hooks/');
expect(typesVersions).not.toContain('./runtime/lib/legacy-react-runtime/');
expect(typesVersions).toContain('./runtime/lib/snapshot/hooks/react.d.ts');
expect(typesVersions).toContain(
'./runtime/lib/snapshot/legacy-react-runtime/index.d.ts',
);
});
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, it, vi } from 'vitest';
import { initElementPAPICallAlog } from '../../src/alog/elementPAPICall';
import { initElementPAPICallAlog } from '../../../src/snapshot/alog/elementPAPICall';
import { globalEnvManager } from '../utils/envManager';
import { expect } from 'vitest';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

import { elementTree } from './utils/nativeMethod';
import { SnapshotInstance, snapshotInstanceManager } from '../src/snapshot';
import { SnapshotInstance, snapshotInstanceManager } from '../../src/snapshot';

const HOLE = null;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';

import { elementTree } from './utils/nativeMethod';
import { setupPage } from '../src/snapshot';
import { cloneElement, Children } from '../src/index';
import { __root } from '../src/root';
import { setupPage } from '../../src/snapshot';
import { cloneElement, Children } from '../../src/index';
import { __root } from '../../src/root';
import { globalEnvManager } from './utils/envManager';
import { toChildArray } from 'preact';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';

import { elementTree } from './utils/nativeMethod';
import { setupPage, BackgroundSnapshotInstance } from '../src/snapshot';
import { cloneElement, createRef } from '../src/index';
import { __root } from '../src/root';
import { setupPage, BackgroundSnapshotInstance } from '../../src/snapshot';
import { cloneElement, createRef } from '../../src/index';
import { __root } from '../../src/root';
import { globalEnvManager } from './utils/envManager';
import { injectUpdateMainThread } from '../src/lifecycle/patch/updateMainThread';
import { injectUpdateMainThread } from '../../src/snapshot/lifecycle/patch/updateMainThread';
import { render } from 'preact';
import { clearCommitTaskId, replaceCommitHook } from '../src/lifecycle/patch/commit';
import { clearCommitTaskId, replaceCommitHook } from '../../src/snapshot/lifecycle/patch/commit';

beforeAll(() => {
setupPage(__CreatePage('0', 0));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';

import { elementTree } from './utils/nativeMethod';
import { setupPage, snapshotInstanceManager, backgroundSnapshotInstanceManager } from '../src/snapshot';
import { ComponentFromReactRuntime, wrapWithLynxComponent } from '../src/compat/lynxComponent';
import { setupDocument } from '../src/document';
import { setupPage, snapshotInstanceManager, backgroundSnapshotInstanceManager } from '../../src/snapshot';
import { ComponentFromReactRuntime, wrapWithLynxComponent } from '../../src/snapshot/compat/lynxComponent';
import { setupDocument } from '../../src/document';
import { Fragment, render } from 'preact';
import { globalEnvManager } from './utils/envManager';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { describe, it, expect, vi } from 'vitest';
import ReactLynx from '@lynx-js/react';
import { startTransition as preactStartTransition, useTransition as preactUseTransition } from 'preact/compat';

import compat from '../../compat';
import compat from '../../../compat';

describe('Default export', () => {
it('should include all exports from @lynx-js/react', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, it, vi } from 'vitest';
import { useTransition, startTransition } from '../../compat';
import { useTransition, startTransition } from '../../../compat';

describe('useTransition', () => {
it('should return an array with two elements', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { Component, render } from 'preact';
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
import { elementTree, waitSchedule } from '../utils/nativeMethod';
import { setupBackgroundDocument } from '../../src/document';
import { setupBackgroundDocument } from '../../../src/document';
import {
setupPage,
SnapshotInstance,
BackgroundSnapshotInstance,
backgroundSnapshotInstanceManager,
} from '../../src/snapshot';
} from '../../../src/snapshot';
import { backgroundSnapshotInstanceToJSON } from '../utils/debug';
import { useState } from 'preact/compat';
import { useInitData, withInitDataInState } from '../../src/lynx-api';
import { useInitData, withInitDataInState } from '../../../src/lynx-api';
import { globalEnvManager } from '../utils/envManager';

/** @type {SnapshotInstance} */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// LICENSE file in the root directory of this source tree.
import { expect, it } from 'vitest';

import { createSnapshot, SnapshotInstance } from '../../src/snapshot';
import { createSnapshot, SnapshotInstance } from '../../../src/snapshot';

it('legacy createSnapshot', function() {
const snapshot = createSnapshot(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// LICENSE file in the root directory of this source tree.
import { expect, it, beforeEach, afterEach } from 'vitest';

import { SnapshotInstance } from '../../src/snapshot';
import { SnapshotInstance } from '../../../src/snapshot';

let prevEntryName;
beforeEach(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// LICENSE file in the root directory of this source tree.
import { expect, it } from 'vitest';

import { SnapshotInstance } from '../../src/snapshot';
import { SnapshotInstance } from '../../../src/snapshot';

const snapshot1 = __SNAPSHOT__(
<view>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import { expect, it, beforeEach, afterEach } from 'vitest';

import { SnapshotInstance } from '../../src/snapshot';
import { SnapshotInstance } from '../../../src/snapshot';

let prevEntryName;
beforeEach(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import { expect, it } from 'vitest';

import { SnapshotInstance } from '../../src/snapshot';
import { SnapshotInstance } from '../../../src/snapshot';

const snapshot1 = __SNAPSHOT__(
<view>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@
import { options, render } from 'preact';
import { afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest';

import { setupDocument } from '../../src/document';
import { setupVNodeSourceHook } from '../../src/debug/vnodeSource';
import { SnapshotOperation, SnapshotOperationParams } from '../../src/lifecycle/patch/snapshotPatch';
import { DIFFED, DOM } from '../../src/renderToOpcodes/constants';
import { __root } from '../../src/root';
import { setupDocument } from '../../../src/document';
import { setupVNodeSourceHook } from '../../../src/snapshot/debug/vnodeSource';
import { SnapshotOperation, SnapshotOperationParams } from '../../../src/snapshot/lifecycle/patch/snapshotPatch';
import { DIFFED, DOM } from '../../../src/snapshot/renderToOpcodes/constants';
import { __root } from '../../../src/root';
import {
setupPage,
SnapshotInstance,
snapshotInstanceManager,
BackgroundSnapshotInstance,
backgroundSnapshotInstanceManager,
hydrate,
} from '../../src/snapshot';
} from '../../../src/snapshot';
import { elementTree } from '../utils/nativeMethod';

const HOLE = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ test('preact/debug - children with the same key attribute', async () => {
const consoleError = vi.spyOn(console, 'error');

await import('preact/debug');
const { root } = await import('../../src/index');
const { root } = await import('../../../src/index');

function Bar() {
return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, expect, it } from 'vitest';

import { describeInvalidValue } from '../../src/debug/describeInvalidValue.js';
import { describeInvalidValue } from '../../../src/snapshot/debug/describeInvalidValue.js';

describe('describeInvalidValue', () => {
it('describes null and undefined', () => {
Expand Down
Loading
Loading