native 플러그인 배포

이 페이지

native 플러그인은 호스트 프로젝트에 설치하고 astro.config.mjs에 등록하는 npm 패키지입니다. 패키지에는 디스크립터와 createPlugin()을 위한 빌드된 서버 엔트리가 필요합니다. React나 Astro 컴포넌트도 함께 제공하면, 호스트가 올바른 환경용으로 컴파일할 수 있도록 별도의 소스 엔트리포인트로 내보내세요.

패키지 레이아웃

다음 레이아웃은 서버 런타임을 브라우저와 Astro 소스에서 분리합니다.

plugin-activity/
├── src/
│   ├── index.ts
│   ├── admin/
│   │   ├── index.tsx
│   │   └── ActivityPage.tsx
│   └── astro/
│       ├── index.ts
│       └── ActivityBlock.astro
├── dist/
│   ├── index.mjs
│   └── index.d.mts
├── package.json
├── tsconfig.json
└── README.md

dist/는 생성됩니다. 호스트의 Vite와 Astro 빌드가 해당 엔트리포인트를 처리해야 하므로 게시 tarball에 src/admin/과 src/astro/를 유지하세요.

패키지 내보내기

다음 package.json은 서버 엔트리를 빌드하고 세 엔트리포인트를 모두 게시합니다.

{
	"name": "@example/plugin-activity",
	"version": "0.1.0",
	"type": "module",
	"main": "./dist/index.mjs",
	"exports": {
		".": {
			"types": "./dist/index.d.mts",
			"import": "./dist/index.mjs"
		},
		"./admin": "./src/admin/index.tsx",
		"./astro": "./src/astro/index.ts"
	},
	"files": ["dist", "src/admin", "src/astro"],
	"scripts": {
		"build": "tsdown src/index.ts --format esm --dts --clean",
		"dev": "tsdown src/index.ts --format esm --dts --watch",
		"typecheck": "tsc --noEmit",
		"prepublishOnly": "pnpm typecheck && pnpm build"
	},
	"peerDependencies": {
		"@cloudflare/kumo": "*",
		"@emdash-cms/admin": "*",
		"@lingui/core": "*",
		"@lingui/react": "*",
		"@tanstack/react-query": "*",
		"astro": ">=6.0.0-beta.0",
		"emdash": "*",
		"react": "^18.0.0 || ^19.0.0"
	},
	"devDependencies": {
		"@types/react": "^19.0.0",
		"tsdown": "^0.20.0",
		"typescript": "^5.9.0"
	},
	"keywords": ["emdash", "emdash-plugin"],
	"license": "MIT"
}

신뢰할 수 있는 React UI가 없으면 ./admin, src/admin, admin 전용 peerDependencies를 제거하세요. Portable Text 렌더러가 없으면 ./astro, src/astro, astro peer를 제거하세요. 내보낸 소스 엔트리포인트가 가져오는 호스트 소유 라이브러리마다 peerDependency를 추가해 두 번째 React, Kumo, Lingui, React Query 인스턴스가 관리 번들에 들어가지 않게 하세요.

엔트리포인트의 소비자는 다릅니다.

내보내기필요한 때소비자
.항상Astro 구성이 디스크립터 팩토리를 가져옴. EmDash가 런타임에 이름 있는 createPlugin()을 가져옴.
./adminadminEntry가 설정된 경우호스트의 브라우저 빌드가 React 컴포넌트 맵을 가져옴.
./astrocomponentsEntry가 설정된 경우호스트의 Astro 빌드가 blockComponents를 가져옴.

디스크립터와 런타임의 모듈 지정자는 이 내보내기와 일치해야 합니다.

export function activityPlugin(): PluginDescriptor {
	return {
		id: "plugin-activity",
		version: "0.1.0",
		format: "native",
		entrypoint: "@example/plugin-activity",
		adminEntry: "@example/plugin-activity/admin",
		componentsEntry: "@example/plugin-activity/astro",
	};
}

export function createPlugin() {
	return definePlugin({
		id: "plugin-activity",
		version: "0.1.0",
		admin: {
			entry: "@example/plugin-activity/admin",
		},
	});
}

npm 패키지 버전, 디스크립터 버전, definePlugin() 버전을 동기화하세요. 사이트 관리자에게 표시되는 버전은 package.json에서 자동이 아니라 플러그인 정의에서 옵니다.

플러그인 신원과 버전

definePlugin()은 소문자·숫자·하이픈을 포함한 스코프 없는 ID, 또는 @scope/name 형태의 스코프 있는 ID를 받습니다. 사이트 플러그인에는 스코프 없는 kebab-case ID를 쓰세요. ID는 /_emdash/api/plugins/<plugin-id>/<route>의 한 경로 세그먼트이기도 합니다.

다음 값은 허용되는 형태와 플러그인 ID와 npm 패키지 이름의 권장 분리를 보여줍니다.

id: "plugin-activity"; // 권장: 플러그인 라우트 URL에서 유효
id: "@example/plugin-activity"; // definePlugin()이 받지만 하나의 URL 세그먼트는 아님

entrypoint: "@example/plugin-activity"; // npm 패키지는 스코프를 유지해도 됨

버전은 시맨틱 major.minor.patch 시퀀스로 시작해야 합니다. 디스크립터와 런타임 모두에 완전한 시맨틱 버전을 사용하세요.

version: "1.0.0"; // 유효
version: "1.2.3-beta.1"; // 유효한 프리릴리스
version: "1.0"; // 무효: 패치 버전 누락

TypeScript 구성

native 스캐폴드는 적합한 tsconfig.json을 만듭니다. 스캐폴드 후 React와 Astro 소스를 추가하면 두 JSX 환경을 모두 포함하세요.

{
	"compilerOptions": {
		"target": "ES2022",
		"module": "preserve",
		"moduleResolution": "bundler",
		"strict": true,
		"declaration": true,
		"outDir": "./dist",
		"rootDir": "./src",
		"jsx": "react-jsx",
		"types": ["astro/client"]
	},
	"include": ["src/**/*"],
	"exclude": ["node_modules", "dist"]
}

패키징하기 전에 소스 엔트리포인트에 pnpm typecheck를 실행하세요. build 스크립트는 src/index.ts만 컴파일하고, 호스트는 패키지를 소비할 때 내보낸 admin과 Astro 소스를 컴파일합니다.

패키지 검사하기

게시하기 전에 정확한 tarball 내용을 테스트하세요. 아래 명령은 일회용 사이트 my-emdash-site가 플러그인 디렉터리 옆에 있다고 가정합니다.

  1. 패키지를 빌드하고 타입 검사합니다.

    pnpm typecheck
    pnpm build
  2. npm tarball을 만들고 npm이 출력하는 파일 목록을 검토합니다.

    npm pack

    예제 패키지에서 npm은 example-plugin-activity-0.1.0.tgz를 만듭니다.

  3. 출력에 dist/index.mjs, dist/index.d.mts, 내보낸 ./admin과 ./astro 모듈에서 도달 가능한 모든 소스 파일이 있는지 확인합니다.

  4. 생성된 tarball을 일회용 EmDash 사이트에 설치합니다.

    cd ../my-emdash-site
    pnpm add ../plugin-activity/example-plugin-activity-0.1.0.tgz
  5. 패키지 만들고 등록하기를 따라 일회용 사이트의 astro.config.mjs에 디스크립터 팩토리를 가져오고 등록합니다. 그런 다음 호스트 사이트를 빌드합니다.

    pnpm build

    모든 플러그인 관리 표면을 열고 기여된 Portable Text 블록을 모두 렌더링하세요. 빌드 전에 등록하면 Astro가 tarball의 ./admin과 ./astro 내보내기를 해석합니다. 서버 전용 패키지 테스트는 누락된 브라우저 또는 .astro 소스 파일을 잡지 못합니다.

README 내용

운영자가 소스를 읽지 않고도 패키지를 설치하고 평가할 수 있을 만큼의 정보를 주세요. 다음을 포함하세요.

  • 한 문장 설명과 지원 EmDash 버전
  • 설치 명령과 완전한 astro.config.mjs 등록
  • native 신뢰 경계와 플러그인이 native 실행이 필요한 이유
  • 선언된 모든 기능과 허용 호스트, 그것을 쓰는 기능
  • 설정과 기본값
  • body-end 프래그먼트용 EmDashBodyEnd 같은 필요한 레이아웃 컴포넌트
  • 운영자 조치가 필요한 변경의 업그레이드 단계

기능 선언을 격리 경계로 설명하지 마세요. ctx API를 게이트하지만, native 코드는 호스트 프로세스에 있는 import, 환경 변수, 직접 네트워크 호출을 계속 사용할 수 있습니다.

npm에 게시하기

tarball 테스트가 통과한 후 게시하세요.

npm publish --access public

스코프 있는 패키지의 첫 공개 릴리스에는 --access public이 필요합니다. 이후 릴리스에는 시맨틱 버저닝을 사용하세요. 생성자 옵션, 저장된 데이터, 필요한 호스트 변경, 패키지 내보내기, 플러그인 신뢰 요구 사항 변경을 호환성 결정으로 다루세요. 업그레이드에 새 기능이나 허용 호스트가 필요하면, native 설치에 기능 동의 프롬프트가 없어도 릴리스 노트에 명시하세요.

npm에서 설치하기

운영자는 게시된 패키지를 EmDash 사이트에 설치합니다.

pnpm add @example/plugin-activity

그런 다음 패키지 만들고 등록하기에 나온 대로 astro.config.mjs에서 디스크립터 팩토리를 가져오고 등록합니다. 의존성만 설치해서는 플러그인이 활성화되지 않습니다. Astro 구성을 바꾸고 사이트를 배포해야 설치가 완료됩니다.

호스트 사이트에 대해 개발하기

워치 모드로 플러그인을 빌드합니다.

pnpm dev

호스트 사이트에서 로컬 디렉터리를 설치합니다.

pnpm add ../plugin-activity

astro.config.mjs에 플러그인 디스크립터 팩토리를 등록한 다음 호스트 개발 서버를 시작하세요. 디스크립터 메타데이터나 패키지 내보내기를 바꾼 뒤에는 서버를 다시 시작하세요. 패키지 매니저 파일 의존성이 링크 대신 파일을 복사하면 다시 빌드한 뒤 재설치하세요. 워크스페이스 의존성이나 pnpm link는 개발 중 로컬 패키지를 연결해 둡니다.

레지스트리 경계

native 패키지는 EmDash 레지스트리에 게시할 수 없습니다. 레지스트리 플러그인은 sandboxed 패키지 형식, 서명된 릴리스 워크플로, 설치 동의 흐름을 사용합니다. 플러그인이 React 관리 코드, Astro 렌더러, 신뢰할 수 있는 프래그먼트, 다른 인프로세스 의존성이 더 이상 필요하지 않다면 레지스트리를 통해 게시하기 전에 sandboxed 형식으로 변환하세요.