131 lines
7.7 KiB
JavaScript
131 lines
7.7 KiB
JavaScript
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import { execFileSync } from 'node:child_process';
|
|
|
|
const root = process.cwd();
|
|
const out = path.join(root, 'HMDesignSystem-AI-Reference-Test');
|
|
const zip = path.join(root, 'HMDesignSystem-AI-Reference-Test.zip');
|
|
|
|
// Recursive cleanup is restricted to this script's fixed output directory.
|
|
if (path.dirname(path.resolve(out)) !== root || path.basename(out) !== 'HMDesignSystem-AI-Reference-Test') throw new Error('Unsafe package output path');
|
|
fs.rmSync(out, { recursive: true, force: true });
|
|
fs.rmSync(zip, { force: true });
|
|
fs.mkdirSync(out, { recursive: true });
|
|
|
|
const copy = (from, to, options = {}) => {
|
|
const src = path.join(root, from);
|
|
if (!fs.existsSync(src)) return false;
|
|
const dest = path.join(out, to);
|
|
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
fs.cpSync(src, dest, { recursive: true, force: true, ...options });
|
|
return true;
|
|
};
|
|
|
|
// Preserve repository-relative paths for source references and imports.
|
|
copy('packages/ui/src', 'source/packages/ui/src');
|
|
copy('packages/ui/package.json', 'source/packages/ui/package.json');
|
|
copy('packages/ui/tsconfig.json', 'source/packages/ui/tsconfig.json');
|
|
copy('packages/ui/tsconfig.build.json', 'source/packages/ui/tsconfig.build.json');
|
|
copy('packages/ui/components.json', 'source/packages/ui/components.json');
|
|
copy('packages/center-graphic/src', 'source/packages/center-graphic/src');
|
|
copy('packages/center-graphic/package.json', 'source/packages/center-graphic/package.json');
|
|
copy('packages/center-graphic/tsconfig.json', 'source/packages/center-graphic/tsconfig.json');
|
|
copy('packages/center-graphic/tsconfig.build.json', 'source/packages/center-graphic/tsconfig.build.json');
|
|
|
|
copy('.storybook', 'storybook/config');
|
|
copy('src', 'storybook/source');
|
|
copy('storybook-static', 'storybook/static');
|
|
|
|
copy('dist', 'packages/app-build');
|
|
copy('dist-dashboard', 'packages/dashboard-export-build');
|
|
copy('packages/ui/dist', 'packages/ui-dist');
|
|
copy('packages/center-graphic/dist', 'packages/center-graphic-dist');
|
|
copy('theme-dist', 'theme/generated');
|
|
copy('themes', 'theme/source');
|
|
copy('packages/ui/src/theme.css', 'theme/source/packages-ui-theme.css');
|
|
copy('packages/ui/src/theme', 'theme/source/packages-ui-theme');
|
|
|
|
copy('docs', 'docs/source');
|
|
copy('AGENTS.md', 'docs/AGENTS.md');
|
|
copy('CLAUDE.md', 'docs/CLAUDE.md');
|
|
copy('README.md', 'docs/README.md');
|
|
copy('package.json', 'docs/root-package.json');
|
|
copy('package-lock.json', 'docs/package-lock.json');
|
|
|
|
// Executable Guard and its contract sources share the original workspace paths.
|
|
// Keep legacy docs locations for existing reference consumers.
|
|
copy('AGENTS.md', 'source/AGENTS.md');
|
|
copy('docs', 'source/docs');
|
|
copy('package.json', 'source/package.json');
|
|
copy('package-lock.json', 'source/package-lock.json');
|
|
copy('scripts/ai-generation-guard.mjs', 'source/scripts/ai-generation-guard.mjs');
|
|
copy('scripts/ai-generation-guard.test.mjs', 'source/scripts/ai-generation-guard.test.mjs');
|
|
copy('src', 'source/src');
|
|
copy('themes', 'source/themes');
|
|
|
|
copy('src/exports', 'examples/react-and-html/src/exports');
|
|
copy('src/exports/dashboard', 'examples/react-and-html/src/exports/dashboard');
|
|
copy('index.html', 'examples/react-and-html/index.html');
|
|
copy('vite.dashboard-export.config.ts', 'examples/react-and-html/vite.dashboard-export.config.ts');
|
|
copy('scripts/finalize-dashboard-export.mjs', 'examples/react-and-html/finalize-dashboard-export.mjs');
|
|
copy('public/fonts', 'assets/fonts');
|
|
copy('packages/center-graphic/src/assets', 'assets/center-graphic');
|
|
|
|
const files = (dir) => fs.existsSync(dir) ? fs.readdirSync(dir, { recursive: true }).filter((p) => fs.statSync(path.join(dir, p)).isFile()).length : 0;
|
|
const countFiles = (rel, suffix) => {
|
|
const dir = path.join(root, rel);
|
|
if (!fs.existsSync(dir)) return 0;
|
|
return fs.readdirSync(dir, { recursive: true }).filter((p) => String(p).endsWith(suffix)).length;
|
|
};
|
|
const publicComponentFiles = countFiles('packages/ui/src/components', '.tsx');
|
|
const patternFiles = countFiles('packages/ui/src/patterns', '.tsx');
|
|
const storyFiles = ['.stories.tsx', '.stories.ts', '.stories.jsx', '.stories.js'].reduce((n, s) => n + countFiles('src', s), 0);
|
|
const generatedAt = new Date().toISOString();
|
|
|
|
const startHere = `# HM Design System AI Reference
|
|
|
|
이 ZIP은 빌드 패키지만이 아니라 HM Design System의 실제 구현·Storybook·Theme·문서를 함께 참조하기 위한 테스트 배포본입니다.
|
|
|
|
## AI 참조 순서
|
|
|
|
1. \`source/AGENTS.md\`의 짧은 필수 규칙부터 읽습니다.
|
|
2. \`source/docs/guidelines/component-pattern-reference.md\`에서 필요한 항목만 찾습니다.
|
|
3. 선택한 Component/Pattern 문서·공개 API와 대표 Story만 확인합니다. HTML/CSS 모방이나 import만으로 재사용을 주장하지 않습니다.
|
|
4. \`storybook/source\`의 Story에서 상태·조합·레이아웃을 확인합니다.
|
|
5. \`theme/source\` 및 \`theme/generated\`의 Token/CSS Custom Properties를 사용합니다.
|
|
6. \`packages/ui-dist\`와 declarations는 소비자 Export 형태를 확인할 때 사용합니다.
|
|
|
|
Guard는 \`source/\`에서 \`node scripts/ai-generation-guard.mjs --target <앱> --scope FULL_SCREEN|PARTIAL\`을 1회 실행합니다. PARTIAL은 \`--files\`, 최종 React artifact는 \`--result\`를 전달합니다. parser 의존성이 없으면 NOT VERIFIED로 보고합니다. 개발자가 실행할 때만 기존 lockfile로 의존성을 복원하며, 포터블 Storybook 테스트 사용자에게 npm 설치를 요구하지 않습니다. PASS는 정적 계약만 의미하며 실제 React 브라우저 결과를 별도로 최소 1회 확인합니다.
|
|
|
|
임의의 색상·spacing·radius·Typography 값을 추가하거나 디자인을 재해석하지 않습니다. 신규 화면은 기존 공개 Component/Pattern과 Theme 계약을 우선 재사용합니다.
|
|
|
|
## 구조
|
|
|
|
- \`source/\`: 원본 공개 패키지 구현 및 내부 의존 코드
|
|
- \`storybook/\`: Storybook 설정, Story/Pattern/Foundation 소스, 정적 Storybook
|
|
- \`packages/\`: 기존 빌드 산출물 및 TypeScript declarations
|
|
- \`theme/\`: Token 원본, Generated CSS, Runtime Theme 자료
|
|
- \`docs/\`: 원본 사용 규칙, AI 공통 가이드, 컴포넌트·패턴 문서
|
|
- \`examples/\`: React/HTML export 관련 예제 및 계약
|
|
- \`assets/\`: Font와 Center Graphic asset
|
|
|
|
## 재현 범위
|
|
|
|
React는 기존 공개 패키지를 직접 import하는 방식을 우선합니다. HTML은 \`examples/react-and-html\`의 기존 export와 Runtime 계약을 먼저 확인합니다. 이 배포본 자체에서 무료 AI에 의한 신규 화면 생성이나 브라우저 시각 검증은 수행하지 않았습니다.
|
|
`;
|
|
fs.writeFileSync(path.join(out, 'AI_START_HERE.md'), startHere, 'utf8');
|
|
|
|
const manifest = {
|
|
package: 'HMDesignSystem-AI-Reference-Test', generatedAt,
|
|
source: { publicComponentSourceFiles: publicComponentFiles, centerGraphicSourceFiles: files(path.join(out, 'source/packages/center-graphic/src')) },
|
|
storybook: { storyFiles, sourceFiles: files(path.join(out, 'storybook/source')), staticIncluded: fs.existsSync(path.join(out, 'storybook/static')) },
|
|
patterns: { patternSourceFiles: patternFiles },
|
|
theme: { sourceIncluded: fs.existsSync(path.join(out, 'theme/source')), generatedIncluded: fs.existsSync(path.join(out, 'theme/generated')) },
|
|
assets: { fontFiles: files(path.join(out, 'assets/fonts')), centerGraphicAssetFiles: files(path.join(out, 'assets/center-graphic')) },
|
|
verification: { aiReproduction: 'NOT_RUN', browserVisualVerification: 'NOT_RUN', sourceMutation: 'NOT_RUN (packaging-only)' }
|
|
};
|
|
fs.writeFileSync(path.join(out, 'MANIFEST.json'), JSON.stringify(manifest, null, 2) + '\n', 'utf8');
|
|
|
|
execFileSync('tar', ['-a', '-c', '-f', zip, '-C', root, path.basename(out)], { stdio: 'inherit' });
|
|
console.log(JSON.stringify({ out, zip, manifest }, null, 2));
|