stego-cli 0.1.3 → 0.1.4
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/dist/stego-cli.js +106 -6
- package/package.json +1 -1
package/dist/stego-cli.js
CHANGED
|
@@ -4,6 +4,7 @@ import os from "node:os";
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import process from "node:process";
|
|
6
6
|
import { spawnSync } from "node:child_process";
|
|
7
|
+
import { createInterface } from "node:readline/promises";
|
|
7
8
|
import { fileURLToPath } from "node:url";
|
|
8
9
|
import { markdownExporter } from "./exporters/markdown-exporter.js";
|
|
9
10
|
import { createPandocExporter } from "./exporters/pandoc-exporter.js";
|
|
@@ -16,6 +17,7 @@ const STATUS_RANK = {
|
|
|
16
17
|
};
|
|
17
18
|
const RESERVED_COMMENT_PREFIX = "CMT";
|
|
18
19
|
const ROOT_CONFIG_FILENAME = "stego.config.json";
|
|
20
|
+
const PROSE_FONT_PROMPT = "Switch workspace to proportional (prose-style) font? (recommended)";
|
|
19
21
|
const SCAFFOLD_GITIGNORE_CONTENT = `node_modules/
|
|
20
22
|
/dist/
|
|
21
23
|
.DS_Store
|
|
@@ -75,6 +77,17 @@ This keeps your editor context focused and applies the project's recommended ext
|
|
|
75
77
|
stego new-project --project my-book --title "My Book"
|
|
76
78
|
\`\`\`
|
|
77
79
|
`;
|
|
80
|
+
const PROSE_MARKDOWN_EDITOR_SETTINGS = {
|
|
81
|
+
"[markdown]": {
|
|
82
|
+
"editor.fontFamily": "Inter, Helvetica Neue, Helvetica, Arial, sans-serif",
|
|
83
|
+
"editor.fontSize": 17,
|
|
84
|
+
"editor.lineHeight": 28,
|
|
85
|
+
"editor.wordWrap": "wordWrapColumn",
|
|
86
|
+
"editor.wordWrapColumn": 72,
|
|
87
|
+
"editor.lineNumbers": "off"
|
|
88
|
+
},
|
|
89
|
+
"markdown.preview.fontFamily": "Inter, Helvetica Neue, Helvetica, Arial, sans-serif"
|
|
90
|
+
};
|
|
78
91
|
const PROJECT_EXTENSION_RECOMMENDATIONS = [
|
|
79
92
|
"matt-gold.stego-extension",
|
|
80
93
|
"matt-gold.saurus-extension"
|
|
@@ -83,8 +96,8 @@ const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
|
|
83
96
|
const packageRoot = path.resolve(scriptDir, "..");
|
|
84
97
|
let repoRoot = "";
|
|
85
98
|
let config;
|
|
86
|
-
main();
|
|
87
|
-
function main() {
|
|
99
|
+
void main();
|
|
100
|
+
async function main() {
|
|
88
101
|
const { command, options } = parseArgs(process.argv.slice(2));
|
|
89
102
|
if (!command || command === "help" || command === "--help" || command === "-h") {
|
|
90
103
|
printUsage();
|
|
@@ -93,7 +106,7 @@ function main() {
|
|
|
93
106
|
try {
|
|
94
107
|
switch (command) {
|
|
95
108
|
case "init":
|
|
96
|
-
initWorkspace({ force: readBooleanOption(options, "force") });
|
|
109
|
+
await initWorkspace({ force: readBooleanOption(options, "force") });
|
|
97
110
|
return;
|
|
98
111
|
case "list-projects":
|
|
99
112
|
activateWorkspace(options);
|
|
@@ -101,7 +114,7 @@ function main() {
|
|
|
101
114
|
return;
|
|
102
115
|
case "new-project":
|
|
103
116
|
activateWorkspace(options);
|
|
104
|
-
createProject(readStringOption(options, "project"), readStringOption(options, "title"));
|
|
117
|
+
await createProject(readStringOption(options, "project"), readStringOption(options, "title"));
|
|
105
118
|
return;
|
|
106
119
|
case "validate": {
|
|
107
120
|
activateWorkspace(options);
|
|
@@ -438,7 +451,7 @@ function findNearestFileUpward(startPath, filename) {
|
|
|
438
451
|
current = parent;
|
|
439
452
|
}
|
|
440
453
|
}
|
|
441
|
-
function initWorkspace(options) {
|
|
454
|
+
async function initWorkspace(options) {
|
|
442
455
|
const targetRoot = process.cwd();
|
|
443
456
|
const entries = fs
|
|
444
457
|
.readdirSync(targetRoot, { withFileTypes: true })
|
|
@@ -457,6 +470,10 @@ function initWorkspace(options) {
|
|
|
457
470
|
copyTemplateAsset(path.join(".vscode", "tasks.json"), targetRoot, copiedPaths);
|
|
458
471
|
copyTemplateAsset(path.join(".vscode", "extensions.json"), targetRoot, copiedPaths, { optional: true });
|
|
459
472
|
rewriteTemplateProjectPackageScripts(targetRoot);
|
|
473
|
+
const enableProseFont = await promptYesNo(PROSE_FONT_PROMPT, true);
|
|
474
|
+
if (enableProseFont) {
|
|
475
|
+
writeProjectProseEditorSettings(targetRoot, copiedPaths);
|
|
476
|
+
}
|
|
460
477
|
writeInitRootPackageJson(targetRoot);
|
|
461
478
|
logLine(`Initialized Stego workspace in ${targetRoot}`);
|
|
462
479
|
for (const relativePath of copiedPaths) {
|
|
@@ -469,6 +486,34 @@ function initWorkspace(options) {
|
|
|
469
486
|
logLine(" npm run list-projects");
|
|
470
487
|
logLine(" npm run validate -- --project plague-demo");
|
|
471
488
|
}
|
|
489
|
+
async function promptYesNo(question, defaultYes) {
|
|
490
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
491
|
+
return defaultYes;
|
|
492
|
+
}
|
|
493
|
+
const rl = createInterface({
|
|
494
|
+
input: process.stdin,
|
|
495
|
+
output: process.stdout
|
|
496
|
+
});
|
|
497
|
+
const suffix = defaultYes ? " [Y/n] " : " [y/N] ";
|
|
498
|
+
try {
|
|
499
|
+
while (true) {
|
|
500
|
+
const answer = (await rl.question(`${question}${suffix}`)).trim().toLowerCase();
|
|
501
|
+
if (!answer) {
|
|
502
|
+
return defaultYes;
|
|
503
|
+
}
|
|
504
|
+
if (answer === "y" || answer === "yes") {
|
|
505
|
+
return true;
|
|
506
|
+
}
|
|
507
|
+
if (answer === "n" || answer === "no") {
|
|
508
|
+
return false;
|
|
509
|
+
}
|
|
510
|
+
console.log("Please answer y or n.");
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
finally {
|
|
514
|
+
rl.close();
|
|
515
|
+
}
|
|
516
|
+
}
|
|
472
517
|
function copyTemplateAsset(sourceRelativePath, targetRoot, copiedPaths, options) {
|
|
473
518
|
const sourcePath = path.join(packageRoot, sourceRelativePath);
|
|
474
519
|
if (!fs.existsSync(sourcePath)) {
|
|
@@ -580,6 +625,53 @@ function ensureProjectExtensionsRecommendations(projectRoot) {
|
|
|
580
625
|
};
|
|
581
626
|
fs.writeFileSync(extensionsPath, `${JSON.stringify(extensionsConfig, null, 2)}\n`, "utf8");
|
|
582
627
|
}
|
|
628
|
+
function writeProjectProseEditorSettings(targetRoot, copiedPaths) {
|
|
629
|
+
const projectsRoot = path.join(targetRoot, "projects");
|
|
630
|
+
if (!fs.existsSync(projectsRoot)) {
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
for (const entry of fs.readdirSync(projectsRoot, { withFileTypes: true })) {
|
|
634
|
+
if (!entry.isDirectory()) {
|
|
635
|
+
continue;
|
|
636
|
+
}
|
|
637
|
+
const projectRoot = path.join(projectsRoot, entry.name);
|
|
638
|
+
const settingsPath = writeProseEditorSettingsForProject(projectRoot);
|
|
639
|
+
copiedPaths.push(path.relative(targetRoot, settingsPath));
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
function writeProseEditorSettingsForProject(projectRoot) {
|
|
643
|
+
const vscodeDir = path.join(projectRoot, ".vscode");
|
|
644
|
+
const settingsPath = path.join(vscodeDir, "settings.json");
|
|
645
|
+
fs.mkdirSync(vscodeDir, { recursive: true });
|
|
646
|
+
let existingSettings = {};
|
|
647
|
+
if (fs.existsSync(settingsPath)) {
|
|
648
|
+
try {
|
|
649
|
+
const parsed = readJson(settingsPath);
|
|
650
|
+
if (isPlainObject(parsed)) {
|
|
651
|
+
existingSettings = parsed;
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
catch {
|
|
655
|
+
existingSettings = {};
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
const proseMarkdownSettings = isPlainObject(PROSE_MARKDOWN_EDITOR_SETTINGS["[markdown]"])
|
|
659
|
+
? PROSE_MARKDOWN_EDITOR_SETTINGS["[markdown]"]
|
|
660
|
+
: {};
|
|
661
|
+
const existingMarkdownSettings = isPlainObject(existingSettings["[markdown]"])
|
|
662
|
+
? existingSettings["[markdown]"]
|
|
663
|
+
: {};
|
|
664
|
+
const nextSettings = {
|
|
665
|
+
...existingSettings,
|
|
666
|
+
"[markdown]": {
|
|
667
|
+
...existingMarkdownSettings,
|
|
668
|
+
...proseMarkdownSettings
|
|
669
|
+
},
|
|
670
|
+
"markdown.preview.fontFamily": PROSE_MARKDOWN_EDITOR_SETTINGS["markdown.preview.fontFamily"]
|
|
671
|
+
};
|
|
672
|
+
fs.writeFileSync(settingsPath, `${JSON.stringify(nextSettings, null, 2)}\n`, "utf8");
|
|
673
|
+
return settingsPath;
|
|
674
|
+
}
|
|
583
675
|
function writeInitRootPackageJson(targetRoot) {
|
|
584
676
|
const cliPackage = readJson(path.join(packageRoot, "package.json"));
|
|
585
677
|
const cliVersion = typeof cliPackage.version === "string" ? cliPackage.version : "0.1.0";
|
|
@@ -621,7 +713,7 @@ function listProjects() {
|
|
|
621
713
|
console.log(`- ${id}`);
|
|
622
714
|
}
|
|
623
715
|
}
|
|
624
|
-
function createProject(projectIdOption, titleOption) {
|
|
716
|
+
async function createProject(projectIdOption, titleOption) {
|
|
625
717
|
const projectId = (projectIdOption || "").trim();
|
|
626
718
|
if (!projectId) {
|
|
627
719
|
throw new Error("Project id is required. Use --project <project-id>.");
|
|
@@ -681,11 +773,19 @@ function createProject(projectIdOption, titleOption) {
|
|
|
681
773
|
fs.writeFileSync(charactersNotesPath, "# Characters\n\n", "utf8");
|
|
682
774
|
const projectExtensionsPath = path.join(projectRoot, ".vscode", "extensions.json");
|
|
683
775
|
ensureProjectExtensionsRecommendations(projectRoot);
|
|
776
|
+
let projectSettingsPath = null;
|
|
777
|
+
const enableProseFont = await promptYesNo(PROSE_FONT_PROMPT, true);
|
|
778
|
+
if (enableProseFont) {
|
|
779
|
+
projectSettingsPath = writeProseEditorSettingsForProject(projectRoot);
|
|
780
|
+
}
|
|
684
781
|
logLine(`Created project: ${path.relative(repoRoot, projectRoot)}`);
|
|
685
782
|
logLine(`- ${path.relative(repoRoot, projectJsonPath)}`);
|
|
686
783
|
logLine(`- ${path.relative(repoRoot, projectPackagePath)}`);
|
|
687
784
|
logLine(`- ${path.relative(repoRoot, charactersNotesPath)}`);
|
|
688
785
|
logLine(`- ${path.relative(repoRoot, projectExtensionsPath)}`);
|
|
786
|
+
if (projectSettingsPath) {
|
|
787
|
+
logLine(`- ${path.relative(repoRoot, projectSettingsPath)}`);
|
|
788
|
+
}
|
|
689
789
|
}
|
|
690
790
|
function getProjectIds() {
|
|
691
791
|
const projectsDir = path.join(repoRoot, config.projectsDir);
|