npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

design-qa-loader

v0.1.1

Published

Turbopack loader that injects data-source and data-component attributes into JSX host elements, so design QA tooling can map a rendered element back to its source location.

Readme

design-qa-loader

Next.js(App Router) + TailwindCSS 프로젝트의 JSX 호스트 요소에 data-source(파일:줄:칼럼)와 data-component(렌더한 컴포넌트명)를 주입하는 Turbopack 로더.

design-qa-bridge 익스텐션이 preview 화면의 요소를 소스 위치로 특정할 때 쓰는 유일한 근거다 — 익스텐션은 런타임 추론 폴백을 두지 않는다.

<!-- 주입 결과 -->
<header data-source="app/home/Header.tsx:8:3" data-component="PageHeader">
  <h1 data-source="app/home/Header.tsx:9:5" data-component="PageHeader">데모</h1>
</header>

왜 babel.config.js를 만들지 않는가

babel 설정 파일이 존재하면 Next.js의 동작이 바뀐다.

| 번들러 | babel 설정 파일이 있으면 | |---|---| | webpack | SWC를 끄고 Babel로 폴백 → next/font가 깨지고 compiler.* 옵션 무효 | | Turbopack | SWC는 유지하되 모든 앱 파일에 babel-loader 전역 적용 → dev 속도 저하 |

이 로더는 @babel/parser로 위치만 알아내고 원본 문자열에 속성 텍스트를 끼워 넣는다 (MagicString). Babel 변환 파이프라인을 타지 않으므로 설정 파일이 필요 없고, TypeScript 타입이 그대로 남아 다운스트림 SWC가 정상 처리한다.

설치

pnpm add -D design-qa-loader

@babel/parser·@babel/traverse·magic-string은 이 패키지의 의존성이라 함께 설치된다. 직접 설치할 필요 없다.

설정

next.config.js:

const loaderPath = require.resolve('design-qa-loader');

// dev이거나, preview 빌드에서 명시적으로 켠 경우에만 활성화
const designQaEnabled =
  process.env.NODE_ENV !== 'production' || process.env.DESIGN_QA === '1';

module.exports = {
  ...(designQaEnabled && {
    turbopack: {
      rules: {
        'app/**/*.{tsx,jsx}': { loaders: [{ loader: loaderPath, options: {} }] },
        'src/**/*.{tsx,jsx}': { loaders: [{ loader: loaderPath, options: {} }] },
      },
    },
  }),
};

preview 배포 파이프라인:

DESIGN_QA=1 pnpm build

Next 16.0+ 라면

glob 대신 condition으로 더 정밀하게 제한할 수 있다. { not: 'foreign' }은 node_modules와 Next 내부 파일을 제외해 성능에 유리하다.

    turbopack: {
      rules: {
        '*': {
          condition: {
            all: [{ not: 'foreign' }, { path: '{app,src}/**/*.{tsx,jsx}' }],
          },
          loaders: [{ loader: loaderPath, options: {} }],
        },
      },
    },

주입 정책

호스트 요소(div, h1 등 소문자 태그)에만 붙인다. 이유:

  • DOM에 실제로 나타나는 것은 호스트 요소뿐이다. 커스텀 컴포넌트에 붙인 속성은 그 컴포넌트가 {...props}로 DOM까지 전달해야만 보인다.
  • 커스텀 컴포넌트 주입은 라이브러리를 깨뜨린 전례가 있다 — FullStory의 동종 플러그인은 @react-navigation(예상치 못한 prop 키에 예외를 던짐)과 victory(prop 이름 충돌)를 하드코딩으로 제외한다.

대가: <Button />사용처 줄은 얻지 못하고 Button 내부 <button> 줄이 잡힌다. 스타일 수정 위치로는 오히려 후자가 정확하다.

건너뛰는 대상:

  • Fragment, React.Fragment, Suspense, React.Suspense
  • 이름이 Provider로 끝나는 컴포넌트
  • 멤버 표현식 태그 (<Motion.div>)
  • 이미 data-source가 있는 요소
  • node_modules, middleware.*

data-component는 JSX를 감싼 가장 가까운 컴포넌트 정의명을 쓴다. function 선언, const X = () => …, memo()/forwardRef() 래핑, class 컴포넌트를 커버한다.

프로덕션 안전망

  1. 로더 등록 자체를 조건부로 (위 designQaEnabled) — 프로덕션 번들에 코드가 아예 들어가지 않음
  2. Next 16.0+ 라면 condition'development' 추가
  3. 최후 안전망 — SWC로 속성 제거:
compiler: {
  // 정규식은 Rust 문법 (JS RegExp와 다름)
  reactRemoveProperties: { properties: ['^data-source$', '^data-component$'] },
},

reactRemoveProperties가 Turbopack에서 적용되는지는 공식 문서에 명시가 없다. 3번에만 의존하지 말고 1번을 반드시 함께 쓰고, 프로덕션 산출물에서 grep -r "data-source" .next/가 0건인지 확인할 것.

도입 검증

  • [ ] pnpm dev 후 요소 검사 → data-source/data-component가 보이는지
  • [ ] 값이 실제 파일:줄과 일치하는지 (에디터로 대조)
  • [ ] DESIGN_QA=1 pnpm build && pnpm start → preview 빌드에도 주입되는지
  • [ ] 일반 pnpm build 산출물에 grep -r "data-source" .next/ → 0건인지
  • [ ] next/font·이미지 최적화 회귀 없는지 (babel 설정 파일을 안 만들었으므로 정상이어야 함)
  • [ ] dev 서버 시작 시간·HMR 체감 저하가 수용 범위인지
  • [ ] 서버/클라이언트 컴포넌트 양쪽에서 주입되는지 (하이드레이션 경고 없는지)

프로그래매틱 사용

로더를 거치지 않고 변환만 쓰려면:

const { transform } = require('design-qa-loader/transform');

const result = transform(source, { filePath, rootDir });
// 변경 있으면 { code, map }, 없거나 건너뛴 파일이면 null

개발 · 릴리스

이 패키지는 design-qa-bridge 레포의 pnpm workspace 패키지다. 레포 루트에서:

pnpm install
pnpm vitest run tests/loader.test.ts

테스트는 상대경로가 아니라 패키지 이름(design-qa-loader/transform)으로 import한다. exports 필드가 깨지면 테스트가 먼저 실패하도록 한 의도적 선택이다.

transform.js는 로더 컨텍스트 없이 호출 가능한 순수 함수라 테스트가 쉽다. 계속 업데이트할 계획이면 새 패턴을 만날 때마다 테스트를 먼저 추가할 것.

릴리스:

cd loader
npm version patch      # 또는 minor / major
npm publish            # npm login 선행

npm pack --dry-run으로 배포될 파일(index.js, transform.js, README.md, LICENSE, package.json 5개)을 먼저 확인하면 안전하다.

알려진 한계

  • findEnclosingComponentName은 검증된 오픈소스가 아니라 조합한 구현이다. data-component가 비는 패턴을 만나면 이 함수를 확장하고 테스트를 추가할 것.
  • 속성은 태그 이름 직후에 삽입되므로 뒤에 오는 {...props}에 같은 키가 있으면 props가 이긴다. 실무에서 data-source를 props로 넘기는 경우는 거의 없어 기본값으로 충분하다.
  • Turbopack은 webpack 로더 API의 일부만 구현한다. 이 로더는 교집합인 async() / resourcePath / rootContext만 사용한다.

참고