@bluenyang/create-tistory-skin 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,121 @@
1
+ # create-tistory-skin
2
+
3
+ **Astro**와 **Tistory Skin Previewer** 기반의 티스토리 스킨 개발 환경을 단 1초 만에 구축해 주는 공식 스캐폴딩(Scaffolding) CLI 도구입니다.
4
+
5
+ 복잡한 빌드 설정이나 티스토리 전용 상대경로 매핑, 레이아웃 보일러플레이트 작성에 시간을 쏟지 마세요. 명령어 한 줄이면 즉시 스킨 디자인과 개발에만 집중할 수 있는 완벽한 프로젝트가 생성됩니다.
6
+
7
+ ## 주요 기능
8
+
9
+ - **단 한 줄의 명령어로 시작**: `npm create` 명령어를 통해 최적화된 초기 스킨 템플릿을 즉시 생성합니다.
10
+ - **Tailwind CSS 선택 지원**: 프로젝트 생성 시 Tailwind CSS 사용 여부를 선택할 수 있습니다. (사용하지 않을 경우 관련 설정과 의존성이 완벽하게 제거된 순수 HTML/CSS 환경을 제공합니다.)
11
+ - **제로 컨피그(Zero-Config) 빌드**: Rollup이나 Vite를 직접 설정할 필요가 없습니다. 티스토리 업로드 규격(`skin.html`, `style.css`, `images/`)에 맞춘 완벽한 후처리를 기본 제공합니다.
12
+ - **실시간 미리보기 (HMR)**: 코드를 수정할 때마다 브라우저 새로고침 없이 로컬 개발 서버에서 즉시 변경 사항을 확인하세요.
13
+ - **구조화된 템플릿**: 티스토리 치환자에 대응하는 기본 레이아웃과 방명록, 페이징 등의 컴포넌트 구조가 미리 잡혀 있습니다.
14
+
15
+ ---
16
+
17
+ ## 빠른 시작 (Quick Start)
18
+
19
+ 터미널을 열고 아래 명령어 중 하나를 실행하세요:
20
+
21
+ ```bash
22
+ # npm 사용 시
23
+ npm create tistory-skin@latest
24
+
25
+ # npx 사용 시
26
+ npx create-tistory-skin@latest
27
+
28
+ # yarn 사용 시
29
+ yarn create tistory-skin
30
+
31
+ # pnpm 사용 시
32
+ pnpm create tistory-skin
33
+
34
+ ```
35
+
36
+ 명령어를 실행하면 인터랙티브 프롬프트가 실행되며 다음 항목을 설정하게 됩니다.
37
+
38
+ 1. **프로젝트(스킨) 이름 설정**: 생성될 폴더의 이름을 입력합니다.
39
+ 2. **Tailwind CSS 사용 여부**: `Y/n` 로 최신 Tailwind CSS 환경을 포함할지 순수 환경으로 갈지 결정합니다.
40
+
41
+ 프로젝트 생성이 완료되면 안내에 따라 디렉토리로 이동하여 개발을 시작하세요!
42
+
43
+ ```bash
44
+ cd my-tistory-skin
45
+ npm install
46
+ npm run dev
47
+
48
+ ```
49
+
50
+ ---
51
+
52
+ ## 📂 디렉토리 구조 (Directory Structure)
53
+
54
+ 생성된 프로젝트는 스킨 개발에 최적화된 직관적인 구조를 가집니다.
55
+
56
+ ```text
57
+ my-tistory-skin/
58
+ ├── src/
59
+ │ ├── components/ # 헤더, 푸터, 스킨 치환자(s_list 등) 컴포넌트
60
+ │ ├── layouts/ # 스킨 전체 레이아웃 (Layout.astro)
61
+ │ ├── pages/
62
+ │ │ └── index.astro # 스킨의 메인 진입점 (여기서 스킨 구조를 잡습니다)
63
+ │ ├── scripts/ # 클라이언트 단 JavaScript/TypeScript 로직 (common.ts)
64
+ │ └── styles/ # 글로벌 CSS (Tailwind 지시어 등 포함)
65
+ ├── astro.config.mjs # 프리뷰어 Integration이 포함된 핵심 설정 파일
66
+ ├── package.json
67
+ └── tailwind.config.mjs # (Tailwind 선택 시 포함됨)
68
+
69
+ ```
70
+
71
+ ---
72
+
73
+ ## 💻 사용법 (Commands)
74
+
75
+ 프로젝트 내부에서 다음 스크립트들을 사용할 수 있습니다.
76
+
77
+ ### 개발 서버 실행 (로컬 프리뷰)
78
+
79
+ ```bash
80
+ npm run dev
81
+
82
+ ```
83
+
84
+ `http://localhost:4321`에 접속하여 실시간으로 스킨을 확인하며 개발하세요. `/category`, `/guestbook` 등의 티스토리 경로도 모두 자동으로 대응됩니다.
85
+
86
+ ### 티스토리 스킨 빌드 (배포용)
87
+
88
+ ```bash
89
+ npm run build
90
+
91
+ ```
92
+
93
+ 모든 개발이 끝난 후 이 명령어를 실행하면 `dist/` 폴더에 결과물이 생성됩니다.
94
+ Astro 컴포넌트와 스크립트가 티스토리 업로드용 규격으로 완벽하게 컴파일 및 상대경로 변환됩니다.
95
+
96
+ ---
97
+
98
+ ## 📦 티스토리에 스킨 적용하기
99
+
100
+ `npm run build`를 통해 생성된 `dist/` 폴더의 구조는 다음과 같습니다.
101
+
102
+ ```text
103
+ dist/
104
+ ├── skin.html (완성된 마크업)
105
+ ├── style.css (컴파일된 메인 CSS)
106
+ └── images/ (스크립트 및 기타 에셋)
107
+ └── common-xxxxxx.js
108
+
109
+ ```
110
+
111
+ 1. `dist` 폴더 내부의 **모든 파일과 폴더를 선택하여 하나의 `.zip` 파일로 압축**합니다.
112
+ 2. 티스토리 관리자 페이지 > [꾸미기] > [스킨 변경] > **[스킨 등록]** 버튼을 클릭합니다.
113
+ 3. 압축한 `.zip` 파일을 업로드하고 저장하면 스킨 적용이 완료됩니다! 🎉
114
+
115
+ ---
116
+
117
+ ## 📝 License
118
+
119
+ 이 프로젝트는 MIT 라이선스를 따릅니다.
120
+
121
+ > Powered by **@tistory-skin-previewer/astro** | Created & Maintained by **BlueNyang**
package/index.js ADDED
@@ -0,0 +1,115 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import prompts from "prompts";
5
+ import pc from "picocolors";
6
+
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = path.dirname(__filename);
9
+
10
+ async function init() {
11
+ console.log(pc.cyan("🚀 Tistory Skin 프로젝트를 생성합니다.\n"));
12
+
13
+ // 프로젝트 이름 입력받기
14
+ const response = await prompts(
15
+ {
16
+ type: "text",
17
+ name: "projectName",
18
+ message: "프로젝트(스킨) 이름을 입력하세요:",
19
+ initial: "tistory-skin",
20
+ },
21
+ {
22
+ type: "confirm",
23
+ name: "useTailwind",
24
+ message: "Tailwind CSS를 사용하시겠습니까?",
25
+ initial: true,
26
+ },
27
+ );
28
+
29
+ if (!response.projectName) {
30
+ console.log(pc.red("프로젝트 생성이 취소되었습니다."));
31
+ process.exit(1);
32
+ }
33
+
34
+ const targetDir = path.join(process.cwd(), response.projectName);
35
+
36
+ // 디렉토리 생성
37
+ if (!fs.existsSync(targetDir)) {
38
+ fs.mkdirSync(targetDir, { recursive: true });
39
+ } else {
40
+ console.error(pc.red(`이미 ${response.projectName} 폴더가 존재합니다.`));
41
+ process.exit(1);
42
+ }
43
+
44
+ // 템플릿 복사
45
+ const templateDir = path.join(__dirname, "template");
46
+
47
+ function copyRecursiveSync(src, dest) {
48
+ const exists = fs.existsSync(src);
49
+ const stats = exists && fs.statSync(src);
50
+ const isDirectory = exists && stats.isDirectory();
51
+
52
+ if (isDirectory) {
53
+ fs.mkdirSync(dest);
54
+ fs.readdirSync(src).forEach((childItemName) => {
55
+ // npm 발매 시 .gitignore가 누락되는 이슈 방지용 트릭 (gitignore -> .gitignore로 복사)
56
+ const destName = childItemName === "gitignore" ? ".gitignore" : childItemName;
57
+ copyRecursiveSync(path.join(src, childItemName), path.join(dest, destName));
58
+ });
59
+ } else {
60
+ fs.copyFileSync(src, dest);
61
+ }
62
+ }
63
+
64
+ console.log(pc.blue("템플릿 파일들을 복사하는 중..."));
65
+ copyRecursiveSync(templateDir, targetDir);
66
+
67
+ // Tailwind CSS 사용하지 않는 경우
68
+ if (!response.useTailwind) {
69
+ console.log(pc.yellow("Tailwind CSS 설정을 제거하는 중..."));
70
+
71
+ // package.json 수정 (의존성 제거)
72
+ const pkgPath = path.join(targetDir, "package.json");
73
+ if (fs.existsSync(pkgPath)) {
74
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
75
+ if (pkg.devDependencies) {
76
+ delete pkg.devDependencies["tailwindcss"];
77
+ delete pkg.devDependencies["@tailwindcss/vite"];
78
+ delete pkg.devDependencies["@tailwindcss/postcss"];
79
+ delete pkg.devDependencies["prettier-plugin-tailwindcss"];
80
+ }
81
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2), "utf-8");
82
+ }
83
+
84
+ // astro.config.mjs 수정 (플러그인 제거)
85
+ const astroConfigPath = path.join(targetDir, "astro.config.mjs");
86
+ if (fs.existsSync(astroConfigPath)) {
87
+ let astroConfig = fs.readFileSync(astroConfigPath, "utf-8");
88
+ // import 구문 제거
89
+ astroConfig = astroConfig.replace(/import tailwindcss from '@tailwindcss\/vite';\r?\n/, "");
90
+ // plugins 배열 안의 tailwindcss() 제거 (쉼표 포함 처리)
91
+ astroConfig = astroConfig.replace(/\s*tailwindcss\(\),?/, "");
92
+ fs.writeFileSync(astroConfigPath, astroConfig, "utf-8");
93
+ }
94
+
95
+ // src/styles/global.css 수정 (Tailwind 지시어 제거)
96
+ const globalCssPath = path.join(targetDir, "src", "styles", "global.css");
97
+ if (fs.existsSync(globalCssPath)) {
98
+ let globalCss = fs.readFileSync(globalCssPath, "utf-8");
99
+ globalCss = globalCss.replace(/@import "tailwindcss";\r?\n/, "");
100
+ fs.writeFileSync(globalCssPath, globalCss, "utf-8");
101
+ }
102
+ }
103
+
104
+ // 완료 메시지
105
+ console.log(pc.green("\n🎉 스킨 프로젝트 생성이 성공적으로 완료되었습니다!\n"));
106
+ console.log("다음 명령어를 실행하여 개발을 시작하세요:\n");
107
+ console.log(pc.cyan(` cd ${response.projectName}`));
108
+ console.log(pc.cyan(" npm install") + " (또는 pnpm install / yarn)");
109
+ console.log(pc.cyan(" npm run dev\n"));
110
+ }
111
+
112
+ init().catch((err) => {
113
+ console.error(pc.red("실행 중 오류가 발생했습니다:"), err);
114
+ process.exit(1);
115
+ });
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@bluenyang/create-tistory-skin",
3
+ "version": "1.0.0",
4
+ "description": "Tistory Skin Previewer 템플릿 생성기",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "author": {
8
+ "name": "Bluenyang",
9
+ "email": "contact@bluenyang.kr",
10
+ "url": "https://github.com/bluenyang"
11
+ },
12
+ "bin": {
13
+ "create-tistory-skin": "./index.js"
14
+ },
15
+ "files": [
16
+ "index.js",
17
+ "template"
18
+ ],
19
+ "dependencies": {
20
+ "prompts": "^2.4.2",
21
+ "picocolors": "^1.1.1"
22
+ },
23
+ "publishConfig": {
24
+ "access": "public"
25
+ }
26
+ }
@@ -0,0 +1,18 @@
1
+ // @ts-check
2
+ import { defineConfig } from "astro/config";
3
+ import tailwindcss from "@tailwindcss/vite";
4
+ import tistoryPreviewer from "@tistory-skin-previewer/astro";
5
+
6
+ // https://astro.build/config
7
+ export default defineConfig({
8
+ integrations: [tistoryPreviewer()],
9
+ vite: {
10
+ plugins: [
11
+ // @ts-ignore
12
+ tailwindcss(),
13
+ ],
14
+ },
15
+ build: {
16
+ format: "file",
17
+ },
18
+ });
@@ -0,0 +1,16 @@
1
+ # build output
2
+ dist/
3
+ # generated types
4
+ .astro/
5
+
6
+ # dependencies
7
+ node_modules/
8
+
9
+ # logs
10
+ npm-debug.log*
11
+ yarn-debug.log*
12
+ yarn-error.log*
13
+ pnpm-debug.log*
14
+
15
+ # macOS-specific files
16
+ .DS_Store
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "tistory-skin",
3
+ "type": "module",
4
+ "version": "1.0.0",
5
+ "scripts": {
6
+ "dev": "astro dev",
7
+ "build": "astro build",
8
+ "preview": "astro preview",
9
+ "astro": "astro"
10
+ },
11
+ "dependencies": {
12
+ "astro": "^5.18.0"
13
+ },
14
+ "devDependencies": {
15
+ "@tailwindcss/postcss": "^4.1.11",
16
+ "@tailwindcss/vite": "^4.1.11",
17
+ "@tistory-skin-previewer/astro": "^0.7.0",
18
+ "@typescript-eslint/parser": "^8.36.0",
19
+ "eslint": "^9.30.1",
20
+ "eslint-config-prettier": "^10.1.5",
21
+ "eslint-plugin-astro": "^1.3.1",
22
+ "eslint-plugin-prettier": "^5.5.1",
23
+ "prettier": "^3.6.2",
24
+ "prettier-plugin-astro": "^0.14.1",
25
+ "prettier-plugin-tailwindcss": "^0.6.14",
26
+ "tailwindcss": "^4.1.11",
27
+ "typescript": "^5.8.3",
28
+ "typescript-eslint": "^8.36.0"
29
+ }
30
+ }
@@ -0,0 +1,7 @@
1
+ ---
2
+
3
+ ---
4
+
5
+ <footer>
6
+ <!-- 블로그의 푸터 영역을 만들어주세요. -->
7
+ </footer>
@@ -0,0 +1,8 @@
1
+ ---
2
+ ---
3
+
4
+ <nav
5
+ id="navbar"
6
+ >
7
+ <!-- 헤더 네비게이션을 만들어주세요. -->
8
+ </nav>
@@ -0,0 +1,22 @@
1
+ <s_paging>
2
+ <!-- 페이지네이션 -->
3
+ <div>
4
+ <!-- 이전 페이지 -->
5
+ <a [##_prev_page_##] class="[##_no_more_prev_##]"> 이전 </a>
6
+ <!-- 페이지 번호들 -->
7
+ <div>
8
+ <s_paging_rep>
9
+ <!-- 페이지 번호 -->
10
+ <a [##_paging_rep_link_##]>[##_paging_rep_link_num_##]</a>
11
+ </s_paging_rep>
12
+ </div>
13
+ <!-- 다음 페이지 -->
14
+ <a [##_next_page_##] class="[##_no_more_next_##]">다음</a>
15
+ </div>
16
+ <!-- 더보기 -->
17
+ <div>
18
+ <a [##_more_link_##]>
19
+ <p>더보기</p>
20
+ </a>
21
+ </div>
22
+ </s_paging>
@@ -0,0 +1,83 @@
1
+ ---
2
+ ---
3
+
4
+ <s_article_rep>
5
+ <s_permalink_article_rep>
6
+ <!-- 글 뷰어의 모양을 만들어주세요. -->
7
+
8
+ <div id="article-footer">
9
+ <s_tag_label>
10
+ <!-- 글의 태그 부분입니다. 태그들이 들어갈 박스를 만들어주세요. -->
11
+ <div>
12
+ <h3>Tags</h3>
13
+ <div id="article-tag-box">[##_tag_label_rep_##]</div>
14
+ </div>
15
+ </s_tag_label>
16
+
17
+ <!-- 이전글, 현재글, 다음글을 만들어주세요. -->
18
+ <div>
19
+ <s_article_prev>
20
+ <!-- 이전글 -->
21
+ <div>
22
+ <a href="[##_article_prev_link_##]">
23
+ <h3>
24
+ [##_article_prev_title_##]
25
+ </h3>
26
+ </a>
27
+ </div>
28
+ </s_article_prev>
29
+ <div>
30
+ <!-- 현재글 -->
31
+ <div>
32
+ <h3>
33
+ [##_article_rep_title_##]
34
+ </h3>
35
+ </div>
36
+ </div>
37
+ <s_article_prev>
38
+ <!-- 다음글 -->
39
+ <div>
40
+ <a href="[##_article_next_link_##]">
41
+ <h3>
42
+ [##_article_next_title_##]
43
+ </h3>
44
+ </a>
45
+ </div>
46
+ </s_article_prev>
47
+ </div>
48
+
49
+ <s_article_related>
50
+ <!-- 관련글이 나오는 부분입니다. -->
51
+ <div title="관련글">
52
+ <s_article_related_rep>
53
+ <div>
54
+ <a href="[##_article_related_rep_link_##]">
55
+ <span>
56
+ <s_article_related_rep_thumbnail>
57
+ <img
58
+ alt=""
59
+ src="[##_article_related_rep_thumbnail_link_##]"
60
+ />
61
+ </s_article_related_rep_thumbnail>
62
+ </span>
63
+ <div>
64
+ <h3>
65
+ [##_article_next_title_##]
66
+ </h3>
67
+ </div>
68
+ <span>
69
+ [##_article_related_rep_date_##]
70
+ </span>
71
+ </a>
72
+ </div>
73
+ </s_article_related_rep>
74
+ </div>
75
+ </s_article_related>
76
+
77
+ <!-- 댓글 부분입니다. -->
78
+ <div>
79
+ <s_rp> [##_comment_group_##] </s_rp>
80
+ </div>
81
+ </div>
82
+ </s_permalink_article_rep>
83
+ </s_article_rep>
@@ -0,0 +1,27 @@
1
+ ---
2
+ ---
3
+
4
+ <!-- 보호된 글의 대체 화면압니다. -->
5
+ <s_article_protected>
6
+ <s_permalink_article_rep>
7
+ <p>
8
+ 보호되어 있는 글입니다.
9
+ <br />
10
+ 내용을 보시려면 비밀번호를 입력하세요.
11
+ </p>
12
+
13
+ <!-- 보호된 글을 보기위한 비밀번호 입력 폼입니다. -->
14
+ <form onsubmit=`[##_article_dissolve_##]`>
15
+ <div>
16
+ <input
17
+ type="password"
18
+ id="[##_article_password_##]"
19
+ name="[##_article_password_##]"
20
+ value=""
21
+ placeholder="비밀번호"
22
+ />
23
+ <button type="submit">확인</button>
24
+ </div>
25
+ </form>
26
+ </s_permalink_article_rep>
27
+ </s_article_protected>
@@ -0,0 +1,8 @@
1
+ ---
2
+
3
+ ---
4
+
5
+ <!-- 게스트북 영역입니다. -->
6
+ <div>
7
+ <s_guest> [##_guestbook_group_##] </s_guest>
8
+ </div>
@@ -0,0 +1,35 @@
1
+ ---
2
+
3
+ ---
4
+
5
+ <!-- 검색 결과 리스트를 보여주는 영역입니다. -->
6
+ <s_list>
7
+ <div>
8
+ <!-- 리스트의 아이템 -->
9
+ <s_list_rep>
10
+ <div>
11
+ <a href="[##_article_prev_link_##]">
12
+ <div>
13
+ <s_list_rep_thumbnail>
14
+ <img
15
+ loading="lazy"
16
+ src="[##_list_rep_thumbnail_##]"
17
+ />
18
+ </s_list_rep_thumbnail>
19
+ </div>
20
+ <div>
21
+ <h3>
22
+ [##_list_rep_title_text_##]
23
+ </h3>
24
+ <span>
25
+ [##_list_rep_summary_##]
26
+ </span>
27
+ <span>
28
+ [##_list_rep_regdate_##]
29
+ </span>
30
+ </div>
31
+ </a>
32
+ </div>
33
+ </s_list_rep>
34
+ </div>
35
+ </s_list>
@@ -0,0 +1,26 @@
1
+ ---
2
+ import Header from '@/components/common/Header.astro';
3
+ import SList from '@/components/reps/SList.astro';
4
+ import Article from '@/components/reps/Article.astro';
5
+ import ArticleProtect from '@/components/reps/ArticleProtect.astro';
6
+ import Guestbook from '@/components/reps/Guestbook.astro';
7
+ import Paging from '@/components/common/Paging.astro';
8
+ import Footer from '@/components/common/Footer.astro';
9
+ import '@/styles/global.css';
10
+ ---
11
+ <Header />
12
+ <main>
13
+ <article>
14
+ <s_cover_group>
15
+ <s_cover_rep>
16
+ <!-- 홈 화면에 들어갈 커버 항목들을 만들어 넣어주세요. -->
17
+ </s_cover_rep>
18
+ </s_cover_group>
19
+ <SList />
20
+ <Article />
21
+ <ArticleProtect />
22
+ <Guestbook />
23
+ <Paging />
24
+ </article>
25
+ </main>
26
+ <Footer />
@@ -0,0 +1,15 @@
1
+ ---
2
+ import TistorySkinHead from "@tistory-skin-previewer/astro/components/TistorySkinHead.astro";
3
+ import TistorySkinBody from "@tistory-skin-previewer/astro/components/TistorySkinBody.astro";
4
+ import Layout from "@/layouts/Layout.astro";
5
+ ---
6
+
7
+ <!doctype html>
8
+ <html lang="ko">
9
+ <TistorySkinHead>
10
+ <!-- HTML의 <head>에 들어갈 내용을 넣어주세요 -->
11
+ </TistorySkinHead>
12
+ <TistorySkinBody>
13
+ <Layout />
14
+ </TistorySkinBody>
15
+ </html>
@@ -0,0 +1 @@
1
+ // 공통으로 사용할 함수들을 정의합니다.
@@ -0,0 +1,5 @@
1
+ @import "tailwindcss";
2
+
3
+ @custom-variant dark (&:where(.dark, .dark *));
4
+
5
+ // 공통으로 사용할 CSS를 정의합니다.
@@ -0,0 +1,22 @@
1
+ {
2
+ "include": [".astro/types.d.ts", "**/*"],
3
+ "exclude": ["dist", "node_modules"],
4
+ "compilerOptions": {
5
+ "baseUrl": ".",
6
+ "target": "ESNext",
7
+ "module": "ESNext",
8
+ "moduleResolution": "node",
9
+ "sourceMap": false,
10
+ "inlineSources": false,
11
+ "strict": true,
12
+ "noEmit": true,
13
+ "noImplicitAny": true,
14
+ "noImplicitThis": true,
15
+ "strictNullChecks": true,
16
+ "noUnusedLocals": true,
17
+ "noUnusedParameters": true,
18
+ "paths": {
19
+ "@/*": ["src/*"]
20
+ }
21
+ }
22
+ }