Skip to content

develop to master #56

Open
zaewc wants to merge 80 commits into
masterfrom
develop
Open

develop to master #56
zaewc wants to merge 80 commits into
masterfrom
develop

Conversation

@zaewc

@zaewc zaewc commented Jun 21, 2026

Copy link
Copy Markdown
Owner

No description provided.

zaewc added 30 commits March 30, 2026 09:21
Detects the browser's per-element height ceiling by binary-searching an
offscreen probe div. Result is cached after the first call, with an SSR
fallback when document is unavailable.
…l offset mapping

When the virtual total (count * itemSize) exceeds the probed per-element
height ceiling, the spacer is clamped and DOM scroll positions are linearly
mapped onto the virtual range. Item offsets are translated to render
relative to the current scroll so they remain visible inside the clamped
spacer.
The lockfile referenced tsup 8.5.0 without the @microsoft/api-extractor
peer, but the variant materialized on disk includes it. The mismatch left
preact/svelte/vue/react-native node_modules/tsup symlinks pointing to a
non-existent .pnpm directory, breaking 'tsup' invocation during build.
|0 coerces to int32 and overflows above 2^31, producing negative indices
on lists where count * itemSize exceeds ~2.1B px — exactly the regime
this strategy is meant to support. Math.floor is correct up to
Number.MAX_SAFE_INTEGER.
When the library is imported by a script in <head>, document exists but
document.body has not been parsed yet — appendChild would throw. Fall
back to the SSR constant in that case without caching, so a later call
after the DOM is ready still performs the real probe.
fix: resync pnpm-lock to match installed tsup variant
…lation to Virtualizer

Restores the original contract:
- getItemOffset(index) returns absolute virtual coordinates (index * itemSize),
  stable across calls and safe to share a single strategy instance across
  multiple Virtualizers.
- getTotalSize(count) returns the (possibly clamped) spacer size.
- New optional getVirtualSize(count) on LayoutStrategy reports the unclamped
  virtual total when it differs.

The Virtualizer compares the two and, when clamping is active, translates
each item's virtual offset into render coordinates relative to the current
scroll position. Clamped layouts skip the per-render-range memoization
because item positions then depend on scrollOffset.

Addresses three review notes:
- statefulness blocking instance sharing
- getItemOffset semantics changing with scroll
- stale lastCount during data updates
…rflow

- Move mapToVirtualOffset out of FixedLayoutStrategy into src/utils so the
  Virtualizer and any future LayoutStrategy share one implementation.
- Compute the ratio (scrollOffset / scrollable) before multiplying by the
  virtual scrollable range. The previous Virtualizer formula multiplied
  first and could overflow Number.MAX_SAFE_INTEGER on extreme lists.
- Drop the if-isClamped branch in the item loop. When virtualOffset
  defaults to scrollOffset (non-clamped case), the formula
  scrollOffset + (virtualStart - virtualOffset) reduces to virtualStart,
  so a single expression covers both paths.
zaewc added 26 commits May 26, 2026 16:02
shared는 platform-agnostic이어야 하는데 React 훅(useInfinitePages)과
react peerDependency를 담고 있어 Vue/Svelte/Preact 소비자에게 유령 React
peer가 전파됐다. 이미 자체 훅을 가진 Preact/Vue 패턴을 따라 훅을 React /
React Native 어댑터로 옮기고, shared는 InfiniteSource/findMissingPages/
canLoadPage 등 agnostic API만 export하도록 정리.
- 추가(devDep): tsdown, eslint+typescript-eslint+프레임워크 플러그인,
  oxlint, knip, publint, @arethetypeswrong/cli, size-limit
- vue/svelte 빌드 플러그인: unplugin-vue, rollup-plugin-svelte
- @scrolloop/core, @scrolloop/shared를 private 처리(react가 번들로
  포함하므로 발행 불필요), shared의 react peer/devDep 제거
- core/shared/react/react-native/preact/vue를 tsdown(Rolldown)으로 통일.
  vue는 unplugin-vue + dts:{vue:true}, preact는 @tsdown/css로 CSS 추출.
- ESM/CJS 듀얼 출력 + 포맷별 .d.mts/.d.cts 방출(fixedExtension), minify(oxc),
  소스맵/선언맵 제거로 경량 dist.
- exports를 condition-specific(import/require별 types) 형태로 교정 → attw-clean.
  preact는 exports가 가리키던 index.mjs와 실제 index.js 불일치 버그도 해소.
- svelte는 vite 유지: 이미 최적(2.15KB)이고 .svelte dts(얇은 re-export+src 동봉)가
  svelte 고유 패턴이라 rolldown-plugin-dts로 대체 부적합.
- 루트 tsconfig sourceMap/declarationMap off, legacy tsup 스크립트/설정 제거.
tsdown 전환으로 불필요해진 의존성 정리:
- 루트 + 각 패키지의 tsup, 루트 terser(oxc-minify 내장)·bundlesize(죽음)
- vue: @vitejs/plugin-vue, vite, vite-plugin-dts (tsdown으로 대체)
- svelte: 미사용 rollup-plugin-svelte (vite 유지 결정)
- eslint.config.mjs: typescript-eslint + react-hooks(react/rn/preact) +
  eslint-plugin-vue(.vue) + eslint-plugin-svelte(.svelte), oxlint 중복 룰
  비활성, prettier 마지막. 루트 1패스 `eslint .`.
- eslint-plugin-react는 ESLint 10 비호환(getFilename 제거)이라 제외 —
  핵심인 react-hooks(rules-of-hooks/exhaustive-deps)는 유지.
- Svelte 5 runes($props는 let 필수)·vue optional prop·react-compiler용
  react-hooks/refs는 false positive라 완화.
- 린트가 잡은 실버그 수정: Virtualizer let→const, playwright.config 중복
  testMatch 키 제거. domPruner의 any는 isomorphic 타이머 핸들이라 주석과
  함께 국소 disable.
- lint/lint:fix/lint:fast/format 스크립트 + lint-staged에 eslint 추가.
- knip.json: 워크스페이스 데드코드/미사용 export·의존성 검출. docs(vitepress)
  ·ssr 엔트리 등록, tsdown 내부 도구(@tsdown/css·vue-tsc·tsx 등) ignore.
  `knip`(클린)·`knip:production` 스크립트.
- publint + @arethetypeswrong/cli(--profile node16) 스크립트(lint:package).
  발행 산출물 root scrolloop의 exports/타입 해상도 검증 통과.
- tsdown 전환 후속: root scrolloop exports를 .d.mts/.d.cts(condition별)로
  교정 — 기존 index.d.ts 가리키던 깨진 types 해소(attw clean).
- 죽은 re-export 제거(preact/vue types의 PageResponse/Range — index 미노출).
- vue-tsc를 vue→root devDep으로 이동(rolldown-plugin-dts가 루트에서 해상).
- 각 패키지 .size-limit.json: brotli 기준 진입점 예산(실측+헤드룸). preset-
  small-lib가 min+brotli로 소비자 실제 비용 측정. root `size`=turbo run size.
  현재: core 1.16/react 3.62/rn 1.99/preact 1.87/vue 2.21/svelte 2.55 kB.
- turbo.json: globalDependencies에 tsconfig/lockfile, 정확한 inputs, size
  태스크 추가, 루트로 옮긴 lint 태스크 제거, test outputs에 coverage.
- B2 점검: tsdown이 CSS를 JS에서 추출하므로 sideEffects 값은 정합적. 추출된
  style.css를 preact/vue/svelte exports에 "./style.css"로 노출(소비자 접근).
- vite 임시 산출물(*.timestamp-*.mjs) gitignore + eslint ignore.
- 루트 + 7개 패키지에 "engines": { "node": ">=20.19.0" } (publint 권고 해소,
  소비자에게 최소 지원 버전 명시).
- .nvmrc=24 (개발/CI 기준 Active LTS).
- 액션 순서 수정(pnpm/action-setup→setup-node) + cache:pnpm로 수동 store
  캐시 3스텝 제거. Node 20(EOL)→24.
- 빌드 1회 후 lint/typecheck/test/size가 turbo 캐시 재사용(기존 이중 빌드 해소).
- 게이트 추가: lint:fast(oxlint)·lint(eslint)·knip·lint:package(publint+attw)
  ·size(size-limit).
- e2e를 분리 잡으로 + Playwright 브라우저 캐시(미스 시에만 설치) → 매 실행
  무조건 설치 제거. publish는 ci+e2e 통과 후로.
- publish 스텝은 불변(발행 흐름 유지), 러너만 Node 24+cache:pnpm로 현대화.
스코프 발행 전환 1단계 — agnostic이던 두 패키지를 단일 core로 합침.
- InfiniteSource·canLoadPage·findMissingPages·PageResponse를 core로 이동
  (Range는 양쪽 정의 동일이라 그대로). core/index에서 통합 export.
- import처 12개 소스를 @scrolloop/shared→@scrolloop/core로 치환.
- @scrolloop/shared 패키지 삭제, 어댑터 deps·vitest alias·번들러 external
  목록에서 제거.
- core의 exactOptionalPropertyTypes에 맞춰 InfiniteSource 콜백 필드 타입
  보정(| undefined). core 크기 예산 1.4→2kB.
- 부수: prettier-plugin-svelte 추가(.svelte 포맷 지원 — lint-staged/format
  이 svelte에서 실패하던 갭 해소).
- @scrolloop/core private 해제 + publishConfig(access public, provenance).
  발행 기반 패키지로 전환.
- react/react-native: core를 noExternal(인라인)→external로 — 5개 어댑터 모두
  발행된 @scrolloop/core에 의존하는 일관 구조. 각 dist는 프레임워크 +
  @scrolloop/core만 external 참조.
- 5개 어댑터에 publishConfig(access public, provenance) 추가.
- 루트 package.json: private:true + 발행용 필드(main/module/types/exports/
  files/bundle/sideEffects/peerDeps) 제거 — 루트는 더 이상 발행 대상 아님.
- core·react·rn·preact·vue·svelte 각각에 lint:publint(--strict) +
  lint:attw(--pack --profile node16) 스크립트. turbo lint:package 태스크,
  루트 lint:package=turbo run lint:package. (루트는 private라 검사 대상 제외)
- CSS export 가진 vue/preact/svelte는 attw --exclude-entrypoints ./style.css.
- svelte를 ESM-only로(Svelte 5는 ESM-first, .svelte 재export dts가 CJS와
  부적합) — publint의 dual-types 경고 해소. attw는 esm-only 프로필.
- svelte의 .svelte 재export dts 해상 경고는 internal-resolution-error로
  국소 ignore — vite-lib는 깔끔한 번들 크기 측정 이점이 있어 유지하고,
  정식 타입은 @sveltejs/package 마이그레이션(후속)으로.
- @changesets/cli + .changeset/config(access public, baseBranch master).
  private 루트는 자동 제외, 6개 발행 패키지만 버전·발행 대상.
- 스크립트: changeset / version(changeset version) / release(build +
  changeset publish).
- 첫 스코프 릴리스 changeset 작성(core 0.2.0, react/rn 0.3.0, preact/vue/
  svelte 0.2.0).
- 단일 루트 pnpm publish → changesets/action@v1. master push 시 changeset이
  있으면 "Version Packages" PR 생성/갱신, 그 PR 머지로 버전 확정되면 스코프
  패키지들을 npm에 발행.
- provenance: id-token write + NPM_CONFIG_PROVENANCE. contents/PR write 권한
  추가(버전 PR 생성용). ci+e2e 통과 후 실행.
- RELEASING.md: Changesets 흐름(changeset 추가→Version PR→머지 시 provenance
  발행), NPM_TOKEN/trusted publishing 전제, 1회성 `npm deprecate scrolloop`.
- README: 레거시 scrolloop이 @scrolloop/react로 대체됨 안내(설치/import는
  이미 스코프 패키지로 작성돼 있어 그대로).
다운로드 수·브랜드를 유지하기 위해 코어가 `scrolloop` 이름을 갖는다 — eslint가
코어이고 eslint-plugin-*가 위성인 것과 동일. 모든 @scrolloop/<framework>
어댑터가 `scrolloop`에 의존하므로 설치 시 전이로 `scrolloop`을 받아 다운로드가
누적되고 기존 0.5.x 이력도 보존된다.

- @scrolloop/core → `scrolloop`로 rename(전 28개 참조 치환: deps·imports·
  external·vitest alias). 루트는 충돌 회피로 `scrolloop-monorepo`(private).
- 전 패키지 1.0.0 조율 런칭. scrolloop은 0.5.2→1.0.0(BREAKING: React 컴포넌트
  → 헤드리스 코어). 첫 릴리스는 수동 버전이라 초기 changeset 제거(이후 changeset
  관리).
- 기존 `scrolloop`(React 컴포넌트) 사용자는 @scrolloop/react로 이주.
  RELEASING/README에 마이그레이션·breaking 안내(deprecate 문구 제거 — scrolloop은
  유지되며 코어로 전환).

BREAKING CHANGE: scrolloop@1.0.0은 React 컴포넌트가 아닌 헤드리스 코어를 export.
React 컴포넌트는 @scrolloop/react로 이동.
새 release 잡이 기존 secrets.NPM_TOKEN을 그대로 사용. 추가 불필요. 다만 옛 흐름은
scrolloop 단일 패키지만 발행했으므로, 새 @scrolloop 스코프 발행 권한이 토큰에
포함되는지 확인 필요(granular면 스코프 추가).
@github-actions

Copy link
Copy Markdown

📊 Test Coverage Report (vitest)

Package Statements Branches Functions Lines
@scrolloop/core 433/433 (100%) 120/121 (99.17%) 43/43 (100%) 433/433 (100%)
@scrolloop/react 456/751 (60.71%) 80/96 (83.33%) 6/18 (33.33%) 456/751 (60.71%)
@scrolloop/react-native 0/286 (0%) 1/6 (16.66%) 1/6 (16.66%) 0/286 (0%)

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request restructures the repository into a monorepo, establishing scrolloop as the framework-agnostic core and introducing adapters for React, React Native, Preact, Vue, and Svelte. It replaces tsup with tsdown for builds, integrates Changesets for automated releases, and adds an AI-assisted development pipeline. Additionally, it addresses layout clamping for extremely large lists. The review feedback correctly identifies a critical syntax error in the Vue InfiniteList component where a stray character 'd' was left at the top of the file.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread packages/vue/src/components/InfiniteList.vue Outdated
@github-actions

Copy link
Copy Markdown

📊 Test Coverage Report (vitest)

Package Statements Branches Functions Lines
@scrolloop/core 433/433 (100%) 120/121 (99.17%) 43/43 (100%) 433/433 (100%)
@scrolloop/react 456/751 (60.71%) 80/96 (83.33%) 6/18 (33.33%) 456/751 (60.71%)
@scrolloop/react-native 0/286 (0%) 1/6 (16.66%) 1/6 (16.66%) 0/286 (0%)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant