lism-cli 0.5.0 → 0.6.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 CHANGED
@@ -27,9 +27,14 @@ pnpm dlx lism-cli create --template minimal-astro ./my-app
27
27
 
28
28
  # カテゴリ名を指定(stack 以下の選択を対話で続行)
29
29
  pnpm dlx lism-cli create --template minimal ./my-app
30
+
31
+ # 言語を指定(CLI 表示 + 生成テンプレートの言語)
32
+ pnpm dlx lism-cli create --template blog-astro-minimal --lang en ./my-blog
30
33
  ```
31
34
 
32
- 同じ動作は `pnpm create lism` / `npm create lism@latest` でも呼び出せます(`create-lism` パッケージ経由)。
35
+ 同じ動作は `pnpm create lism` / `npm create lism@latest` でも呼び出せます(`create-lism` パッケージ経由)。挙動は両者で共通です。
36
+
37
+ `--lang <ja|en>` は CLI の表示言語に加えて、**生成されるテンプレート本体の言語**にも反映されます。`--lang` を指定しない場合は、対話端末(TTY)ではほかのどの選択よりも先に言語選択プロンプトが表示され、選んだ言語で以降の表示とテンプレート生成が確定します(非対話環境・CI 等では `en` にフォールバック)。対応言語版を持つテンプレート(`blog-astro-minimal` / `blog-astro-personal` / `blog-astro-techlog` / `lp-astro-corporate` / `lp-astro-interior`)では、指定言語のサイト文言・サンプルコンテンツで生成されます。言語版が無いテンプレートは、指定言語に関わらず既存(ベース)の内容で生成されます。
33
38
 
34
39
  ### UI コンポーネントの追加
35
40
 
@@ -1,3 +1,6 @@
1
+ // src/i18n.ts
2
+ import { execFileSync } from "child_process";
3
+
1
4
  // src/messages.ts
2
5
  var messages = {
3
6
  // ---------------------------------------------------------------------------
@@ -8,8 +11,8 @@ var messages = {
8
11
  en: "CLI for Lism CSS / UI"
9
12
  },
10
13
  "cli.opt.lang": {
11
- ja: "CLI \u306E\u8868\u793A\u8A00\u8A9E\u3092\u6307\u5B9A\uFF08ja | en\uFF09",
12
- en: "Display language for the CLI (ja | en)"
14
+ ja: "CLI \u8868\u793A\u3068\u751F\u6210\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u306E\u8A00\u8A9E\uFF08ja | en\u3001\u672A\u6307\u5B9A\u306A\u3089\u5BFE\u8A71\u6642\u306B\u9078\u629E\uFF09",
15
+ en: "Language for the CLI and generated template (ja | en; prompted when omitted)"
13
16
  },
14
17
  // create
15
18
  "cli.create.description": {
@@ -466,10 +469,25 @@ var messages = {
466
469
  };
467
470
 
468
471
  // src/i18n.ts
472
+ function detectAppleLanguage() {
473
+ if (process.platform !== "darwin") return null;
474
+ try {
475
+ const out = execFileSync("/usr/bin/defaults", ["read", "-g", "AppleLanguages"], {
476
+ encoding: "utf8",
477
+ timeout: 1e3,
478
+ stdio: ["ignore", "pipe", "ignore"]
479
+ });
480
+ const match = out.match(/"?([A-Za-z]{2,}[-_A-Za-z]*)"?/);
481
+ return match ? match[1].toLowerCase() : null;
482
+ } catch {
483
+ return null;
484
+ }
485
+ }
469
486
  function detectLang(override) {
470
487
  if (override === "ja" || override === "en") return override;
471
488
  const raw = (process.env.LC_ALL || process.env.LANG || "").toLowerCase();
472
489
  if (raw.startsWith("ja")) return "ja";
490
+ if (detectAppleLanguage()?.startsWith("ja")) return "ja";
473
491
  try {
474
492
  const locale = Intl.DateTimeFormat().resolvedOptions().locale.toLowerCase();
475
493
  if (locale.startsWith("ja")) return "ja";
@@ -547,8 +565,8 @@ var logger = {
547
565
  };
548
566
 
549
567
  // src/version.ts
550
- var LISM_CSS_VERSION = "0.21.0" ? "0.21.0" : "unknown";
551
- var CLI_VERSION = "0.5.0" ? "0.5.0" : "unknown";
568
+ var LISM_CSS_VERSION = "0.22.2" ? "0.22.2" : "unknown";
569
+ var CLI_VERSION = "0.6.0" ? "0.6.0" : "unknown";
552
570
 
553
571
  // src/constants.ts
554
572
  var SOURCE_REPO = "lism-css/lism-css";
@@ -563,6 +581,7 @@ var SKILL_SOURCE_PATH = "skills/lism-css-guide";
563
581
  var TEMPLATES_PATH = "templates";
564
582
 
565
583
  // ../../templates/manifest.ts
584
+ var PREVIEW_ORIGIN = "https://templates.lism-css.com";
566
585
  var TEMPLATES = [
567
586
  {
568
587
  slug: "minimal-astro",
@@ -581,7 +600,7 @@ var TEMPLATES = [
581
600
  sourcePath: "minimal/vite",
582
601
  title: { ja: "Minimal Vite", en: "Minimal Vite" },
583
602
  description: { ja: "Vite + React \u30D9\u30FC\u30B9\u306E\u6700\u5C0F\u69CB\u6210", en: "Minimal Vite + React setup" },
584
- previewUrl: "https://lism-minimal-vite.pages.dev/"
603
+ previewUrl: `${PREVIEW_ORIGIN}/minimal-vite/`
585
604
  },
586
605
  {
587
606
  slug: "blog-astro-minimal",
@@ -591,9 +610,13 @@ var TEMPLATES = [
591
610
  variantLabel: { ja: "Minimal", en: "Minimal" },
592
611
  stack: "astro",
593
612
  sourcePath: "blog/astro/minimal",
613
+ langOverlays: {
614
+ en: "blog/astro/minimal/.lang/en"
615
+ },
594
616
  title: { ja: "Blog Minimal", en: "Blog Minimal" },
595
617
  description: { ja: "\u8A18\u4E8B\u4E00\u89A7 / \u8A73\u7D30 / Tags \u306E\u307F\u306E\u6700\u5C0F\u69CB\u6210\u306E Astro \u30D6\u30ED\u30B0", en: "Minimal Astro blog with posts and tags only" },
596
- previewUrl: "https://lism-blog-astro-minimal.pages.dev/",
618
+ previewUrl: `${PREVIEW_ORIGIN}/blog-astro-minimal/`,
619
+ previewUrlEn: `${PREVIEW_ORIGIN}/blog-astro-minimal/en/`,
597
620
  features: {
598
621
  ja: ["\u30BF\u30B0", "sitemap"],
599
622
  en: ["Tags", "sitemap"]
@@ -607,12 +630,16 @@ var TEMPLATES = [
607
630
  variantLabel: { ja: "Personal", en: "Personal" },
608
631
  stack: "astro",
609
632
  sourcePath: "blog/astro/personal",
633
+ langOverlays: {
634
+ en: "blog/astro/personal/.lang/en"
635
+ },
610
636
  title: { ja: "Blog Personal", en: "Blog Personal" },
611
637
  description: {
612
638
  ja: "\u500B\u4EBA\u30D6\u30ED\u30B0\u30FB\u30A8\u30C3\u30BB\u30A4\u5411\u3051\u3002\u5E74\u6708\u30A2\u30FC\u30AB\u30A4\u30D6\u3064\u304D\u306E\u843D\u3061\u7740\u3044\u305F Astro \u30D6\u30ED\u30B0",
613
639
  en: "Personal / essay-style Astro blog with monthly archives"
614
640
  },
615
- previewUrl: "https://lism-blog-astro-personal.pages.dev/",
641
+ previewUrl: `${PREVIEW_ORIGIN}/blog-astro-personal/`,
642
+ previewUrlEn: `${PREVIEW_ORIGIN}/blog-astro-personal/en/`,
616
643
  features: {
617
644
  ja: ["\u30BF\u30B0", "\u5E74\u6708\u30A2\u30FC\u30AB\u30A4\u30D6", "\u30B7\u30A7\u30A2\u30DC\u30BF\u30F3", "OGP\u753B\u50CF\u81EA\u52D5\u751F\u6210", "sitemap\uFF08lastmod \u5BFE\u5FDC\uFF09"],
618
645
  en: ["Tags", "Monthly archives", "Share buttons", "Auto-generated OG images", "sitemap (with lastmod)"]
@@ -626,12 +653,16 @@ var TEMPLATES = [
626
653
  variantLabel: { ja: "Tech Log", en: "Tech Log" },
627
654
  stack: "astro",
628
655
  sourcePath: "blog/astro/techlog",
656
+ langOverlays: {
657
+ en: "blog/astro/techlog/.lang/en"
658
+ },
629
659
  title: { ja: "Blog Tech Log", en: "Blog Tech Log" },
630
660
  description: {
631
661
  ja: "\u6280\u8853\u30D6\u30ED\u30B0\u5411\u3051\u3002\u30B3\u30FC\u30C9\u30CF\u30A4\u30E9\u30A4\u30C8\u30FB\u30AB\u30C6\u30B4\u30EA\u30FB\u30BF\u30B0\u30FBTOC\u30FB\u5E74\u6708\u30A2\u30FC\u30AB\u30A4\u30D6\u30FB\u691C\u7D22\u3092\u88C5\u5099\u3057\u305F Astro \u30D6\u30ED\u30B0",
632
662
  en: "Tech blog with code highlighting, categories, tags, TOC, monthly archives and search"
633
663
  },
634
- previewUrl: "https://lism-blog-astro-techlog.pages.dev/",
664
+ previewUrl: `${PREVIEW_ORIGIN}/blog-astro-techlog/`,
665
+ previewUrlEn: `${PREVIEW_ORIGIN}/blog-astro-techlog/en/`,
635
666
  features: {
636
667
  ja: [
637
668
  "\u30AB\u30C6\u30B4\u30EA / \u30BF\u30B0",
@@ -667,7 +698,8 @@ var TEMPLATES = [
667
698
  sourcePath: "lp/astro",
668
699
  title: { ja: "LP Corporate", en: "LP Corporate" },
669
700
  description: { ja: "\u30B3\u30FC\u30DD\u30EC\u30FC\u30C8\u30B5\u30A4\u30C8\u5411\u3051\u306E Astro \u30E9\u30F3\u30C7\u30A3\u30F3\u30B0\u30DA\u30FC\u30B8", en: "Astro landing page for corporate sites" },
670
- previewUrl: "https://lism-lp-astro.pages.dev/corporate/"
701
+ previewUrl: `${PREVIEW_ORIGIN}/lp-astro/corporate/`,
702
+ previewUrlEn: `${PREVIEW_ORIGIN}/lp-astro/en/corporate/`
671
703
  },
672
704
  {
673
705
  slug: "lp-astro-interior",
@@ -679,7 +711,8 @@ var TEMPLATES = [
679
711
  sourcePath: "lp/astro",
680
712
  title: { ja: "LP Interior", en: "LP Interior" },
681
713
  description: { ja: "\u30A4\u30F3\u30C6\u30EA\u30A2\u30FB\u66AE\u3089\u3057\u7CFB\u30B5\u30FC\u30D3\u30B9\u5411\u3051\u306E Astro \u30E9\u30F3\u30C7\u30A3\u30F3\u30B0\u30DA\u30FC\u30B8", en: "Astro landing page for interior / lifestyle services" },
682
- previewUrl: "https://lism-lp-astro.pages.dev/interior/"
714
+ previewUrl: `${PREVIEW_ORIGIN}/lp-astro/interior/`,
715
+ previewUrlEn: `${PREVIEW_ORIGIN}/lp-astro/en/interior/`
683
716
  },
684
717
  {
685
718
  slug: "lp-astro-ryokan",
@@ -714,7 +747,7 @@ var CATEGORIES = [
714
747
  },
715
748
  {
716
749
  id: "lp",
717
- label: { ja: "Landing Page", en: "Landing Page" },
750
+ label: { ja: "LP", en: "LP" },
718
751
  variantPromptKey: "create.promptSelectVariant.lp"
719
752
  },
720
753
  {
@@ -723,10 +756,12 @@ var CATEGORIES = [
723
756
  variantPromptKey: "create.promptSelectVariant.web"
724
757
  }
725
758
  ];
726
- async function runCreate({ template, targetDir, force = false }) {
727
- await runCreateWithTemplates({ template, targetDir, force }, TEMPLATES);
759
+ async function runCreate({ template, targetDir, force = false, lang }) {
760
+ await runCreateWithTemplates({ template, targetDir, force, lang }, TEMPLATES);
728
761
  }
729
- async function runCreateWithTemplates({ template, targetDir, force = false }, templates) {
762
+ async function runCreateWithTemplates({ template, targetDir, force = false, lang }, templates) {
763
+ const resolvedLang = await resolveLang(lang);
764
+ setLang(resolvedLang);
730
765
  const availableTemplates = templates.filter((tpl2) => !tpl2.draft);
731
766
  const tpl = await resolveTemplate(template, availableTemplates);
732
767
  const outDir = path.resolve(process.cwd(), await resolveTargetDir(targetDir, tpl.slug));
@@ -743,13 +778,27 @@ async function runCreateWithTemplates({ template, targetDir, force = false }, te
743
778
  const ref = DEFAULT_TEMPLATES_REF;
744
779
  logger.info(t("create.fetching", { name: tpl.slug, ref }));
745
780
  await downloadTemplateSource(tpl, outDir, ref, force);
781
+ await applyLangOverlay(tpl, outDir, ref, resolvedLang);
746
782
  ensureTemplateDownloaded(outDir, tpl);
747
- postProcessTemplate(outDir, tpl);
783
+ postProcessTemplate(outDir, tpl, resolvedLang);
748
784
  logger.success(t("create.created", { dir: outDir }));
749
785
  printNextSteps(outDir, tpl);
750
786
  }
751
- async function createCommand(targetDir, options) {
752
- await runCreate({ template: options.template, targetDir, force: options.force });
787
+ async function createCommand(targetDir, options, command) {
788
+ const langOpt = command?.optsWithGlobals().lang;
789
+ const lang = typeof langOpt === "string" ? langOpt : void 0;
790
+ await runCreate({ template: options.template, targetDir, force: options.force, lang });
791
+ }
792
+ async function resolveLang(explicit) {
793
+ if (explicit === "ja" || explicit === "en") return explicit;
794
+ if (!process.stdin.isTTY) return "en";
795
+ return select({
796
+ message: "Select language / \u8A00\u8A9E\u3092\u9078\u629E:",
797
+ choices: [
798
+ { name: "English", value: "en" },
799
+ { name: "\u65E5\u672C\u8A9E", value: "ja" }
800
+ ]
801
+ });
753
802
  }
754
803
  async function resolveTemplate(requested, templates) {
755
804
  if (requested) {
@@ -850,13 +899,25 @@ async function downloadTemplatePath(sourcePath, outDir, ref, forceClean) {
850
899
  forceClean
851
900
  });
852
901
  }
853
- function postProcessTemplate(projectDir, tpl) {
902
+ async function applyLangOverlay(tpl, outDir, ref, lang) {
903
+ if (tpl.kind !== "project") return;
904
+ const overlayPath = tpl.langOverlays?.[lang];
905
+ if (!overlayPath) return;
906
+ const overlayDir = fs.mkdtempSync(path.join(os.tmpdir(), "lism-template-lang-"));
907
+ try {
908
+ await downloadTemplatePath(overlayPath, overlayDir, ref, true);
909
+ mergeDirectory(overlayDir, outDir);
910
+ } finally {
911
+ fs.rmSync(overlayDir, { recursive: true, force: true });
912
+ }
913
+ }
914
+ function postProcessTemplate(projectDir, tpl, lang) {
854
915
  if (tpl.kind === "static-html") return;
855
916
  if (tpl.kind === "base-overlay" && tpl.rewritePackageName !== false) {
856
917
  rewritePackageName(projectDir, tpl.slug);
857
918
  }
858
919
  if (tpl.kind === "single-project-variant") {
859
- extractVariantFiles(projectDir, tpl);
920
+ extractVariantFiles(projectDir, tpl, lang);
860
921
  rewritePackageName(projectDir, tpl.packageName ?? tpl.slug);
861
922
  }
862
923
  cleanupDevArtifacts(projectDir);
@@ -871,10 +932,19 @@ function cleanupDevArtifacts(projectDir) {
871
932
  if (fs.existsSync(screenshotsConfig)) {
872
933
  fs.rmSync(screenshotsConfig, { force: true });
873
934
  }
935
+ const langDir = path.join(projectDir, ".lang");
936
+ if (fs.existsSync(langDir)) {
937
+ fs.rmSync(langDir, { recursive: true, force: true });
938
+ }
939
+ }
940
+ function resolveVariantDir(srcDir, variant, lang) {
941
+ const langVariant = `${lang}/${variant}`;
942
+ const hasLangVariant = fs.existsSync(path.join(srcDir, "pages", langVariant, "index.astro"));
943
+ return hasLangVariant ? langVariant : variant;
874
944
  }
875
- function extractVariantFiles(projectDir, tpl) {
945
+ function extractVariantFiles(projectDir, tpl, lang) {
876
946
  const srcDir = path.join(projectDir, "src");
877
- const variant = tpl.variant;
947
+ const variant = resolveVariantDir(srcDir, tpl.variant, lang);
878
948
  const pagesVariantIndex = path.join(srcDir, "pages", variant, "index.astro");
879
949
  if (!fs.existsSync(pagesVariantIndex)) {
880
950
  throw new Error(
package/dist/index.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  preScanLang,
14
14
  setLang,
15
15
  t
16
- } from "./chunk-USFIDRTL.js";
16
+ } from "./chunk-G4RBP3W6.js";
17
17
 
18
18
  // src/createProgram.ts
19
19
  import { Command as Command3 } from "commander";
package/dist/lib.d.ts CHANGED
@@ -2,9 +2,15 @@ interface RunCreateArgs {
2
2
  template?: string;
3
3
  targetDir?: string;
4
4
  force?: boolean;
5
+ /**
6
+ * 明示指定された言語(`--lang`)。`ja` / `en` のときその言語で CLI 表示・テンプレ生成を確定する。
7
+ * 未指定(undefined / 不正値)の場合は、対話端末では最初に言語選択プロンプトを出し、
8
+ * 非対話端末(CI・パイプ等)では `en` にフォールバックする。
9
+ */
10
+ lang?: string;
5
11
  }
6
12
  /** `lism create` / `create-lism` から共通で使える実体関数 */
7
- declare function runCreate({ template, targetDir, force }: RunCreateArgs): Promise<void>;
13
+ declare function runCreate({ template, targetDir, force, lang }: RunCreateArgs): Promise<void>;
8
14
 
9
15
  /**
10
16
  * CLI 表示用のメッセージ辞書(ja / en)。
@@ -25,8 +31,8 @@ declare const messages: {
25
31
  readonly en: "CLI for Lism CSS / UI";
26
32
  };
27
33
  readonly 'cli.opt.lang': {
28
- readonly ja: "CLI の表示言語を指定(ja | en";
29
- readonly en: "Display language for the CLI (ja | en)";
34
+ readonly ja: "CLI 表示と生成テンプレートの言語(ja | en、未指定なら対話時に選択)";
35
+ readonly en: "Language for the CLI and generated template (ja | en; prompted when omitted)";
30
36
  };
31
37
  readonly 'cli.create.description': {
32
38
  readonly ja: "templates から新規プロジェクトを生成する";
@@ -459,14 +465,6 @@ declare const messages: {
459
465
  };
460
466
  type MessageKey = keyof typeof messages;
461
467
 
462
- /**
463
- * CLI の i18n(多言語化)基盤。
464
- *
465
- * - 既定は英語(`en`)。環境ロケールが `ja*` のときのみ日本語(`ja`)に切り替わる。
466
- * - `--lang <code>` オプションで明示上書き可能。
467
- * - メッセージ辞書は `messages.ts` に集約。
468
- */
469
-
470
468
  type Lang = 'ja' | 'en';
471
469
  /** 現在の言語を設定する(未知の値は無視) */
472
470
  declare function setLang(lang: string | undefined): void;
package/dist/lib.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  setLang,
4
4
  t,
5
5
  tOf
6
- } from "./chunk-USFIDRTL.js";
6
+ } from "./chunk-G4RBP3W6.js";
7
7
  export {
8
8
  runCreate,
9
9
  setLang,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lism-cli",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "CLI for scaffolding projects and adding UI components for Lism CSS / Lism UI.",
5
5
  "author": {
6
6
  "name": "ddryo",