@gitpagedocs/cli 1.1.44
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 +169 -0
- package/ai/application/ai-command.ts +24 -0
- package/ai/application/ai-provider-factory.ts +22 -0
- package/ai/application/run-ai-cli-command.ts +136 -0
- package/ai/config.ts +22 -0
- package/ai/core/models/ai-cli-config.ts +31 -0
- package/ai/core/ports/illm-provider.ts +32 -0
- package/ai/infrastructure/ai-config-file.ts +32 -0
- package/ai/infrastructure/file-system-adapter.ts +83 -0
- package/ai/infrastructure/llm/tools-llm-provider.ts +54 -0
- package/ai/presentation/ai-prompts.ts +239 -0
- package/application/config-only/handler.mjs +55 -0
- package/application/home/handler.mjs +83 -0
- package/application/ports/cli-runtime-ports.ts +87 -0
- package/application/report/config-only-reporter.mjs +62 -0
- package/application/use-cases/dispatch-mode.ts +18 -0
- package/builders/config-orchestrator.mjs +40 -0
- package/builders/root-config-builder.mjs +54 -0
- package/builders/route-builders.mjs +111 -0
- package/builders/source-viewer.mjs +330 -0
- package/builders/theme-template.mjs +75 -0
- package/builders/version-config-builder.mjs +151 -0
- package/content/docs-en.mjs +542 -0
- package/content/docs-es.mjs +459 -0
- package/content/docs-pt.mjs +459 -0
- package/content/docs.mjs +9 -0
- package/contracts/doc-versions.mjs +1 -0
- package/contracts/index.mjs +1 -0
- package/data/default-site-config.mjs +312 -0
- package/data/i18n-langmenu.mjs +122 -0
- package/data/i18n-translations.mjs +20 -0
- package/data/layouts.mjs +353 -0
- package/data/path-mappings.mjs +34 -0
- package/data/route-metas.mjs +73 -0
- package/data/theme-colors.mjs +387 -0
- package/data/urls.mjs +7 -0
- package/data/version-constants.mjs +3 -0
- package/domain/models/cli-options.ts +18 -0
- package/domain/services/sanitize-segment.mjs +7 -0
- package/home/constants.mjs +4 -0
- package/home/home-file-writer.mjs +20 -0
- package/home/templates.mjs +48 -0
- package/index.mjs +26 -0
- package/infrastructure/config-only/runtime.mjs +17 -0
- package/infrastructure/home/runtime.mjs +37 -0
- package/package.json +60 -0
- package/presentation/commands/ai-info.ts +57 -0
- package/presentation/commands/config-info.ts +24 -0
- package/presentation/commands/diagnostics.ts +70 -0
- package/presentation/commands/docs.ts +37 -0
- package/presentation/commands/mcp.ts +20 -0
- package/presentation/commands/pages.ts +91 -0
- package/presentation/commands/run-command.ts +62 -0
- package/presentation/index.ts +108 -0
- package/presentation/options/parser.ts +104 -0
- package/presentation/options/resolver.ts +25 -0
- package/presentation/options/schema.ts +10 -0
- package/presentation/ui/banner.ts +13 -0
- package/presentation/ui/clack.ts +74 -0
- package/presentation/ui/prompts.ts +64 -0
- package/runtime/doc-path-resolver.mjs +57 -0
- package/runtime/git-ops.mjs +110 -0
- package/runtime/output.mjs +122 -0
- package/runtime/workflow.mjs +144 -0
- package/start.mjs +12 -0
- package/ui/logger.mjs +9 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AI_MODEL_DEFAULTS,
|
|
3
|
+
OLLAMA_DEFAULT_BASE_URL,
|
|
4
|
+
type AiProviderId,
|
|
5
|
+
} from '../config';
|
|
6
|
+
import { PROVIDER_CATALOG, legacyProviderToCatalogId } from '@gitpagedocs/tools/ai';
|
|
7
|
+
import type { AiCliConfig, AiCliRunPlan } from '../core/models/ai-cli-config';
|
|
8
|
+
import {
|
|
9
|
+
askText,
|
|
10
|
+
askConfirm,
|
|
11
|
+
askSelect,
|
|
12
|
+
askMultiSelect,
|
|
13
|
+
intro,
|
|
14
|
+
} from '../../presentation/ui/clack';
|
|
15
|
+
|
|
16
|
+
export interface InteractiveCliAnswers {
|
|
17
|
+
provider: AiProviderId;
|
|
18
|
+
apiKeyOrHost: string;
|
|
19
|
+
model: string;
|
|
20
|
+
targetDirs: string[];
|
|
21
|
+
languages: Array<'pt' | 'en' | 'es'>;
|
|
22
|
+
outputDir: string;
|
|
23
|
+
filePrefix: string;
|
|
24
|
+
saveConfig: boolean;
|
|
25
|
+
runConfigScaffold: boolean;
|
|
26
|
+
contextPrompt: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function parseCommaSeparated(value: string): string[] {
|
|
30
|
+
return value
|
|
31
|
+
.split(',')
|
|
32
|
+
.map((item) => item.trim())
|
|
33
|
+
.filter(Boolean);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function validateCommaSeparatedDirectories(value: string): string | undefined {
|
|
37
|
+
const values = parseCommaSeparated(value);
|
|
38
|
+
if (!values.length) return 'Provide at least one path.';
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const CUSTOM_MODEL = '__custom_model__';
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Ask for the model as a SELECT of the provider's real catalog models (with the
|
|
46
|
+
* default marked), plus a "Custom" escape hatch — so an invalid id like "4.8"
|
|
47
|
+
* can't be typed by mistake and rejected later as a 404 by the provider.
|
|
48
|
+
*/
|
|
49
|
+
async function askModel(provider: AiProviderId): Promise<string> {
|
|
50
|
+
const catalogId = legacyProviderToCatalogId(provider);
|
|
51
|
+
const spec = catalogId ? PROVIDER_CATALOG[catalogId] : undefined;
|
|
52
|
+
const fallback = AI_MODEL_DEFAULTS[provider] || '';
|
|
53
|
+
|
|
54
|
+
if (!spec || spec.models.length === 0) {
|
|
55
|
+
return String(await askText({ message: 'Which model do you want to use?', defaultValue: fallback }));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const picked = await askSelect<string>(
|
|
59
|
+
'Which model do you want to use?',
|
|
60
|
+
[
|
|
61
|
+
...spec.models.map((m) => ({
|
|
62
|
+
value: m.id,
|
|
63
|
+
label: m.id === spec.defaultModel ? `${m.id} (default)` : m.id,
|
|
64
|
+
})),
|
|
65
|
+
{ value: CUSTOM_MODEL, label: 'Custom — enter a model id manually' },
|
|
66
|
+
],
|
|
67
|
+
spec.defaultModel,
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
if (picked !== CUSTOM_MODEL) return picked;
|
|
71
|
+
|
|
72
|
+
return String(
|
|
73
|
+
await askText({
|
|
74
|
+
message: 'Enter the model id:',
|
|
75
|
+
defaultValue: spec.defaultModel,
|
|
76
|
+
validate: (value) => (value.trim() ? undefined : 'Provide a model id'),
|
|
77
|
+
}),
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function toPlan(answers: InteractiveCliAnswers): AiCliRunPlan {
|
|
82
|
+
const config: AiCliConfig = {
|
|
83
|
+
version: 1,
|
|
84
|
+
ai: {
|
|
85
|
+
provider: answers.provider,
|
|
86
|
+
model: answers.model,
|
|
87
|
+
apiKey: answers.provider === 'ollama' ? undefined : answers.apiKeyOrHost,
|
|
88
|
+
baseUrl: answers.provider === 'ollama' ? answers.apiKeyOrHost : undefined,
|
|
89
|
+
paths: answers.targetDirs,
|
|
90
|
+
languages: answers.languages,
|
|
91
|
+
outputDir: answers.outputDir,
|
|
92
|
+
filePrefix: answers.filePrefix,
|
|
93
|
+
contextPrompt: answers.contextPrompt,
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
config,
|
|
99
|
+
saveConfig: answers.saveConfig,
|
|
100
|
+
runConfigScaffold: answers.runConfigScaffold,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export async function promptMissingDirectories(
|
|
105
|
+
missingDirectories: string[],
|
|
106
|
+
): Promise<{ replacementPaths: string[]; abort: boolean }> {
|
|
107
|
+
const recoveryMode = await askSelect(
|
|
108
|
+
`I couldn't find ${missingDirectories.length} directory(ies): ${missingDirectories.join(', ')}`,
|
|
109
|
+
[
|
|
110
|
+
{ value: 'fix', label: 'Fix paths now (recommended)' },
|
|
111
|
+
{ value: 'skip', label: 'Skip missing paths and continue' },
|
|
112
|
+
{ value: 'abort', label: 'Abort execution' },
|
|
113
|
+
],
|
|
114
|
+
'fix',
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
if (recoveryMode === 'abort') {
|
|
118
|
+
return { replacementPaths: [], abort: true };
|
|
119
|
+
}
|
|
120
|
+
if (recoveryMode === 'skip') {
|
|
121
|
+
return { replacementPaths: [], abort: false };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const fixedPaths = await askText({
|
|
125
|
+
message: 'Enter new paths (comma-separated):',
|
|
126
|
+
validate: validateCommaSeparatedDirectories,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
replacementPaths: parseCommaSeparated(String(fixedPaths || '')),
|
|
131
|
+
abort: false,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export async function promptUseExistingConfig(existingConfig: AiCliConfig): Promise<AiCliRunPlan | null> {
|
|
136
|
+
const useExistingConfig = await askConfirm(
|
|
137
|
+
'Found .gitpagedocsconfig. Do you want to use this configuration?',
|
|
138
|
+
true,
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
if (!useExistingConfig) return null;
|
|
142
|
+
|
|
143
|
+
const runConfigScaffold = await askConfirm(
|
|
144
|
+
'After generating docs with AI, run the default gitpagedocs scaffolding?',
|
|
145
|
+
true,
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
config: existingConfig,
|
|
150
|
+
saveConfig: false,
|
|
151
|
+
runConfigScaffold,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export async function runAiInteractivePrompt(existingConfig?: AiCliConfig | null): Promise<AiCliRunPlan> {
|
|
156
|
+
intro('🤖 GitPageDocs AI CLI — interactive mode 🤖');
|
|
157
|
+
|
|
158
|
+
if (existingConfig) {
|
|
159
|
+
const existingPlan = await promptUseExistingConfig(existingConfig);
|
|
160
|
+
if (existingPlan) return existingPlan;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const provider = await askSelect<AiProviderId>(
|
|
164
|
+
'Which AI provider should be used to generate documentation?',
|
|
165
|
+
[
|
|
166
|
+
{ value: 'openai', label: 'OpenAI (GPT)' },
|
|
167
|
+
{ value: 'claude', label: 'Anthropic (Claude)' },
|
|
168
|
+
{ value: 'gemini', label: 'Google Gemini' },
|
|
169
|
+
{ value: 'ollama', label: 'Ollama (Local Open Source)' },
|
|
170
|
+
],
|
|
171
|
+
'openai',
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
const apiKeyOrHost = await askText({
|
|
175
|
+
message: provider === 'ollama' ? 'Ollama host URL:' : 'Provider API key:',
|
|
176
|
+
defaultValue: provider === 'ollama' ? OLLAMA_DEFAULT_BASE_URL : undefined,
|
|
177
|
+
validate: (input) => (input.length > 0 ? undefined : 'Required field'),
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
const model = await askModel(provider);
|
|
181
|
+
|
|
182
|
+
const targetDirsRaw = await askText({
|
|
183
|
+
message:
|
|
184
|
+
'Paths to analyze (comma-separated). You can include multiple repositories (e.g., src,cli,../other-repo/src):',
|
|
185
|
+
defaultValue: 'src,cli',
|
|
186
|
+
validate: validateCommaSeparatedDirectories,
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
const languages = await askMultiSelect<'en' | 'pt' | 'es'>(
|
|
190
|
+
'Documentation languages to generate:',
|
|
191
|
+
[
|
|
192
|
+
{ value: 'en', label: 'English (en)' },
|
|
193
|
+
{ value: 'pt', label: 'Portuguese (pt)' },
|
|
194
|
+
{ value: 'es', label: 'Español (es)' },
|
|
195
|
+
],
|
|
196
|
+
['en', 'pt', 'es'],
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
const outputDir = await askText({
|
|
200
|
+
message: 'Output directory for generated markdown files:',
|
|
201
|
+
defaultValue: 'gitpagedocs/docs',
|
|
202
|
+
validate: (value) => (value.trim() ? undefined : 'Provide an output directory'),
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
const filePrefix = await askText({
|
|
206
|
+
message: 'Output file prefix:',
|
|
207
|
+
defaultValue: 'ai-generated',
|
|
208
|
+
validate: (value) => (value.trim() ? undefined : 'Provide a prefix'),
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
const contextPrompt = await askText({
|
|
212
|
+
message: 'Extra instructions for AI (optional):',
|
|
213
|
+
defaultValue:
|
|
214
|
+
'You are a senior technical writer. Generate professional, clear documentation with architectural insight following FSD, SOLID, and Clean Code principles.',
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
const saveConfig = await askConfirm(
|
|
218
|
+
'Save this configuration to .gitpagedocsconfig for future manual use?',
|
|
219
|
+
true,
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
const runConfigScaffold = await askConfirm(
|
|
223
|
+
'After generating docs with AI, run the default gitpagedocs scaffolding?',
|
|
224
|
+
true,
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
return toPlan({
|
|
228
|
+
provider,
|
|
229
|
+
apiKeyOrHost,
|
|
230
|
+
model,
|
|
231
|
+
targetDirs: parseCommaSeparated(targetDirsRaw),
|
|
232
|
+
languages,
|
|
233
|
+
outputDir,
|
|
234
|
+
filePrefix,
|
|
235
|
+
contextPrompt,
|
|
236
|
+
saveConfig,
|
|
237
|
+
runConfigScaffold,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/** Config-only use case - orchestrates build, write, and optional push */
|
|
2
|
+
|
|
3
|
+
import { sanitizeSegment } from "../../domain/services/sanitize-segment.mjs";
|
|
4
|
+
import { reportAll } from "../report/config-only-reporter.mjs";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Execute config-only use case
|
|
8
|
+
* @param {Object} params
|
|
9
|
+
* @param {Object} params.options - Parsed CLI options
|
|
10
|
+
* @param {string} params.root - Project root
|
|
11
|
+
* @param {string} params.pkgRoot - Package root
|
|
12
|
+
* @param {string} params.prebuiltDir - Prebuilt directory path
|
|
13
|
+
* @param {Function} params.buildConfigArtifacts - Artifact builder
|
|
14
|
+
* @param {Function} params.createThemeTemplate - Theme template creator
|
|
15
|
+
* @param {Array} params.layouts - Layout definitions
|
|
16
|
+
* @param {import("../ports/cli-runtime-ports").ConfigOnlyRuntimePort} runtime
|
|
17
|
+
*/
|
|
18
|
+
export async function executeConfigOnly(params, runtime) {
|
|
19
|
+
if (!runtime) {
|
|
20
|
+
throw new Error("Config-only runtime is required.");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const { options, root, pkgRoot, prebuiltDir, buildConfigArtifacts, createThemeTemplate, layouts } = params;
|
|
24
|
+
|
|
25
|
+
const artifacts = buildConfigArtifacts({
|
|
26
|
+
useLocalLayoutConfig: options.useLocalLayoutConfig,
|
|
27
|
+
githubOwner: sanitizeSegment(options.githubOwner),
|
|
28
|
+
githubRepo: sanitizeSegment(options.githubRepo),
|
|
29
|
+
root,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
await runtime.writeConfigOnlyOutput({
|
|
33
|
+
root,
|
|
34
|
+
pkgRoot,
|
|
35
|
+
outputDir: options.outputDir,
|
|
36
|
+
artifacts,
|
|
37
|
+
useLocalLayoutConfig: options.useLocalLayoutConfig,
|
|
38
|
+
layouts,
|
|
39
|
+
createThemeTemplate,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
if (options.shouldPush) {
|
|
43
|
+
await runtime.ensureGitHubPagesWorkflow(
|
|
44
|
+
() => runtime.getCurrentGitBranch(root),
|
|
45
|
+
(relativePath, content) => runtime.writeText(root, relativePath, content),
|
|
46
|
+
options.docsPath || "",
|
|
47
|
+
);
|
|
48
|
+
runtime.runGitPushForGeneratedArtifacts(options, root, sanitizeSegment);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const reportLines = reportAll(options, runtime.hasPath(prebuiltDir));
|
|
52
|
+
for (const line of reportLines) {
|
|
53
|
+
runtime.logInfo(line);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/** Home distribution use case - static site + auxiliary files */
|
|
2
|
+
|
|
3
|
+
import { STATIC_OUTPUT_DIR, ARTIFACTS_DIR } from "../../home/constants.mjs";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Execute home distribution use case
|
|
7
|
+
* @param {Object} params
|
|
8
|
+
* @param {Object} params.options - Parsed CLI options
|
|
9
|
+
* @param {string} params.root - Project root
|
|
10
|
+
* @param {string} params.pkgRoot - Package root
|
|
11
|
+
* @param {Function} params.buildConfigArtifacts - Artifact builder
|
|
12
|
+
* @param {Function} params.createThemeTemplate - Theme template creator
|
|
13
|
+
* @param {Array} params.layouts - Layout definitions
|
|
14
|
+
* @param {import("../ports/cli-runtime-ports").HomeRuntimePort} runtime
|
|
15
|
+
*/
|
|
16
|
+
export async function executeHome(params, runtime) {
|
|
17
|
+
if (!runtime) {
|
|
18
|
+
throw new Error("Home runtime is required.");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const { options, root, pkgRoot, buildConfigArtifacts, createThemeTemplate, layouts } = params;
|
|
22
|
+
const outputDir = options.outputDir || "gitpagedocshome";
|
|
23
|
+
const repositorySearch = options.repositorySearch ?? false;
|
|
24
|
+
const basePath = options.basePath ?? "";
|
|
25
|
+
|
|
26
|
+
const isCurrentDir = outputDir === "." || outputDir === "./";
|
|
27
|
+
const targetDir = isCurrentDir ? root : runtime.joinPath(root, outputDir);
|
|
28
|
+
|
|
29
|
+
if (!isCurrentDir) {
|
|
30
|
+
runtime.ensureDirEmpty(root, outputDir);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const pathSegment = basePath ? basePath.replace(/^\/+|\/+$/g, "") : "";
|
|
34
|
+
const buildEnv = {
|
|
35
|
+
...process.env,
|
|
36
|
+
GITHUB_ACTIONS: "true",
|
|
37
|
+
GITPAGEDOCS_REPOSITORY_SEARCH: String(repositorySearch),
|
|
38
|
+
GITPAGEDOCS_BASE_PATH: pathSegment ? `/${pathSegment}` : "/",
|
|
39
|
+
GITPAGEDOCS_PATH: pathSegment,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const artifacts = buildConfigArtifacts({
|
|
43
|
+
useLocalLayoutConfig: false,
|
|
44
|
+
githubOwner: "",
|
|
45
|
+
githubRepo: "",
|
|
46
|
+
root,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
await runtime.writeConfigOnlyOutput({
|
|
50
|
+
root,
|
|
51
|
+
pkgRoot,
|
|
52
|
+
outputDir: ARTIFACTS_DIR,
|
|
53
|
+
artifacts,
|
|
54
|
+
useLocalLayoutConfig: false,
|
|
55
|
+
layouts,
|
|
56
|
+
createThemeTemplate,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
runtime.runNextBuild(root, buildEnv);
|
|
60
|
+
|
|
61
|
+
const outPath = runtime.joinPath(root, STATIC_OUTPUT_DIR);
|
|
62
|
+
if (!runtime.existsPath(outPath)) {
|
|
63
|
+
throw new Error(`Static export not found at ${STATIC_OUTPUT_DIR}/. Ensure next.config exports when GITHUB_ACTIONS=true.`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const destBase = isCurrentDir ? root : targetDir;
|
|
67
|
+
if (isCurrentDir) {
|
|
68
|
+
for (const name of runtime.readDirNames(outPath)) {
|
|
69
|
+
runtime.copyRecursive(runtime.joinPath(outPath, name), runtime.joinPath(destBase, name));
|
|
70
|
+
}
|
|
71
|
+
} else {
|
|
72
|
+
runtime.copyRecursive(outPath, destBase);
|
|
73
|
+
}
|
|
74
|
+
runtime.copyRecursive(runtime.joinPath(root, ARTIFACTS_DIR), runtime.joinPath(destBase, ARTIFACTS_DIR));
|
|
75
|
+
|
|
76
|
+
await runtime.writeHomeFiles(root, isCurrentDir ? "." : outputDir, { repositorySearch, basePath });
|
|
77
|
+
await runtime.writeText(root, (isCurrentDir ? "" : outputDir + "/") + ".nojekyll", "");
|
|
78
|
+
|
|
79
|
+
const cdDir = isCurrentDir ? "." : outputDir;
|
|
80
|
+
runtime.logSuccess(`Generated: ${cdDir}/ (static site + .env + Dockerfile + README.md)`);
|
|
81
|
+
runtime.logInfo(`Serve: cd ${cdDir} && npx serve .`);
|
|
82
|
+
runtime.logInfo(`Docker: cd ${cdDir} && docker build -t gitpagedocshome . && docker run -p 3000:80 gitpagedocshome`);
|
|
83
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { CliOptions } from "../../domain/models/cli-options";
|
|
2
|
+
|
|
3
|
+
export interface BuildArtifactsInput {
|
|
4
|
+
useLocalLayoutConfig: boolean;
|
|
5
|
+
githubOwner: string;
|
|
6
|
+
githubRepo: string;
|
|
7
|
+
root: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface LayoutDefinition {
|
|
11
|
+
file: string;
|
|
12
|
+
[key: string]: unknown;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface BuiltConfigArtifacts {
|
|
16
|
+
rootConfig: unknown;
|
|
17
|
+
layoutsConfig: unknown;
|
|
18
|
+
fallbackLayoutsConfig: unknown;
|
|
19
|
+
docs?: Record<string, Record<string, string>>;
|
|
20
|
+
docsHtml?: Record<string, Record<string, string>>;
|
|
21
|
+
versionConfigs: Record<string, Record<string, unknown>>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface CliRuntimeParams {
|
|
25
|
+
root: string;
|
|
26
|
+
pkgRoot: string;
|
|
27
|
+
prebuiltDir: string;
|
|
28
|
+
buildConfigArtifacts: (input: BuildArtifactsInput) => BuiltConfigArtifacts;
|
|
29
|
+
createThemeTemplate: (layout: LayoutDefinition) => unknown;
|
|
30
|
+
layouts: LayoutDefinition[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface CliCommandContext extends CliRuntimeParams {
|
|
34
|
+
options: CliOptions;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface CliCommandRunner {
|
|
38
|
+
runConfigOnly: (params: CliCommandContext) => Promise<void>;
|
|
39
|
+
runHome: (params: CliCommandContext) => Promise<void>;
|
|
40
|
+
runAi: (params: CliCommandContext) => Promise<void>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface ConfigOnlyOutputInput {
|
|
44
|
+
root: string;
|
|
45
|
+
pkgRoot: string;
|
|
46
|
+
outputDir: string;
|
|
47
|
+
artifacts: BuiltConfigArtifacts;
|
|
48
|
+
useLocalLayoutConfig: boolean;
|
|
49
|
+
layouts: LayoutDefinition[];
|
|
50
|
+
createThemeTemplate: (layout: LayoutDefinition) => unknown;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface ConfigOnlyRuntimePort {
|
|
54
|
+
hasPath: (absolutePath: string) => boolean;
|
|
55
|
+
writeConfigOnlyOutput: (input: ConfigOnlyOutputInput) => Promise<void>;
|
|
56
|
+
writeText: (root: string, relativePath: string, data: string) => Promise<void>;
|
|
57
|
+
ensureGitHubPagesWorkflow: (
|
|
58
|
+
getCurrentGitBranch: () => string,
|
|
59
|
+
writeText: (relativePath: string, content: string) => Promise<void>,
|
|
60
|
+
docsPath?: string,
|
|
61
|
+
) => Promise<void>;
|
|
62
|
+
getCurrentGitBranch: (root: string) => string;
|
|
63
|
+
runGitPushForGeneratedArtifacts: (
|
|
64
|
+
options: CliOptions,
|
|
65
|
+
root: string,
|
|
66
|
+
sanitizeSegment: (value: string | undefined | null) => string,
|
|
67
|
+
) => void;
|
|
68
|
+
logInfo: (message: string) => void;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface HomeRuntimePort {
|
|
72
|
+
joinPath: (...parts: string[]) => string;
|
|
73
|
+
ensureDirEmpty: (root: string, relativeDir: string) => void;
|
|
74
|
+
copyRecursive: (src: string, dest: string) => void;
|
|
75
|
+
readDirNames: (absolutePath: string) => string[];
|
|
76
|
+
existsPath: (absolutePath: string) => boolean;
|
|
77
|
+
runNextBuild: (root: string, env: NodeJS.ProcessEnv) => void;
|
|
78
|
+
writeConfigOnlyOutput: (input: ConfigOnlyOutputInput) => Promise<void>;
|
|
79
|
+
writeHomeFiles: (
|
|
80
|
+
root: string,
|
|
81
|
+
outputDir: string,
|
|
82
|
+
options: { repositorySearch?: boolean; basePath?: string },
|
|
83
|
+
) => Promise<void>;
|
|
84
|
+
writeText: (root: string, relativePath: string, data: string) => Promise<void>;
|
|
85
|
+
logSuccess: (message: string) => void;
|
|
86
|
+
logInfo: (message: string) => void;
|
|
87
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/** Config-only command output messages - SRP: single place for all report messages */
|
|
2
|
+
|
|
3
|
+
export function reportConfigOnlySuccess(options) {
|
|
4
|
+
return [`Generated: ${options.outputDir}/ (config-only)`, "No index.html/index.js generated."];
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function reportLayoutConfig(options) {
|
|
8
|
+
if (options.useLocalLayoutConfig) {
|
|
9
|
+
return ["Local layouts generated in gitpagedocs/layouts/ (--layoutconfig)."];
|
|
10
|
+
} else {
|
|
11
|
+
return ["Using official remote layouts config by default (no local gitpagedocs/layouts generated)."];
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function reportRenderingUrl(options) {
|
|
16
|
+
if (options.githubOwner && options.githubRepo) {
|
|
17
|
+
return [
|
|
18
|
+
`Configured rendering URL: https://${options.githubOwner}.github.io/${options.githubRepo}/`,
|
|
19
|
+
`Official viewer remains available: https://vidigal-code.github.io/git-page-docs/${options.githubOwner}/${options.githubRepo}?modetheme=light&lang=pt`,
|
|
20
|
+
];
|
|
21
|
+
}
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function reportPushMode() {
|
|
26
|
+
return [
|
|
27
|
+
"Generated: .github/workflows/gitpagedocs-pages.yml",
|
|
28
|
+
"Push mode enabled: committed and pushed gitpagedocs/ + workflow to origin.",
|
|
29
|
+
];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function reportBuildFlag() {
|
|
33
|
+
return ["`--build` keeps compatibility flag but output remains gitpagedocs/."];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function reportFullOrServeSkipped() {
|
|
37
|
+
return ["External commands were skipped (no prebuilt copy and no local serve spawn)."];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function reportPrebuiltDetected() {
|
|
41
|
+
return ["`prebuilt/` detected, but ignored by config-only generator."];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function reportAll(options, prebuiltDetected) {
|
|
45
|
+
const lines = [];
|
|
46
|
+
lines.push(...reportConfigOnlySuccess(options));
|
|
47
|
+
lines.push(...reportLayoutConfig(options));
|
|
48
|
+
lines.push(...reportRenderingUrl(options));
|
|
49
|
+
if (options.shouldPush) {
|
|
50
|
+
lines.push(...reportPushMode());
|
|
51
|
+
}
|
|
52
|
+
if (options.isBuild) {
|
|
53
|
+
lines.push(...reportBuildFlag());
|
|
54
|
+
}
|
|
55
|
+
if (options.mode === "full" || options.isServe) {
|
|
56
|
+
lines.push(...reportFullOrServeSkipped());
|
|
57
|
+
}
|
|
58
|
+
if (prebuiltDetected) {
|
|
59
|
+
lines.push(...reportPrebuiltDetected());
|
|
60
|
+
}
|
|
61
|
+
return lines;
|
|
62
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { CliOptions } from "../../domain/models/cli-options";
|
|
2
|
+
import type { CliCommandRunner, CliRuntimeParams } from "../ports/cli-runtime-ports";
|
|
3
|
+
|
|
4
|
+
export async function dispatchMode(
|
|
5
|
+
options: CliOptions,
|
|
6
|
+
params: CliRuntimeParams,
|
|
7
|
+
runner: CliCommandRunner,
|
|
8
|
+
): Promise<void> {
|
|
9
|
+
if (options.mode === "ai") {
|
|
10
|
+
await runner.runAi({ ...params, options });
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
if (options.mode === "home") {
|
|
14
|
+
await runner.runHome({ ...params, options });
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
await runner.runConfigOnly({ ...params, options });
|
|
18
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/** Orchestrate build of config artifacts for gitpagedocs */
|
|
2
|
+
|
|
3
|
+
import { LAYOUTS, FALLBACK_LAYOUTS } from "../data/layouts.mjs";
|
|
4
|
+
import { DOCS } from "../content/docs.mjs";
|
|
5
|
+
import { DOC_VERSIONS } from "../data/version-constants.mjs";
|
|
6
|
+
import { buildRootConfig } from "./root-config-builder.mjs";
|
|
7
|
+
import { buildVersionConfig } from "./version-config-builder.mjs";
|
|
8
|
+
import { generateSourceViewerHtml } from "./source-viewer.mjs";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Build all config artifacts (root, layouts, versions, docs).
|
|
12
|
+
* @param {object} options - { useLocalLayoutConfig, githubOwner, githubRepo, root }
|
|
13
|
+
* @returns {object} { rootConfig, layoutsConfig, fallbackLayoutsConfig, docs, docsHtml, versionConfigs }
|
|
14
|
+
*/
|
|
15
|
+
export function buildConfigArtifacts(options = {}) {
|
|
16
|
+
const root = options.root ?? process.cwd();
|
|
17
|
+
|
|
18
|
+
const rootConfig = buildRootConfig(options);
|
|
19
|
+
const layoutsConfig = { layouts: LAYOUTS };
|
|
20
|
+
const fallbackLayoutsConfig = { layouts: FALLBACK_LAYOUTS };
|
|
21
|
+
|
|
22
|
+
const sourceViewerHtml = generateSourceViewerHtml(root);
|
|
23
|
+
const docsHtml = {
|
|
24
|
+
sourceViewer: { pt: sourceViewerHtml, en: sourceViewerHtml, es: sourceViewerHtml },
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const versionConfigs = {};
|
|
28
|
+
for (const versionId of DOC_VERSIONS) {
|
|
29
|
+
versionConfigs[versionId] = buildVersionConfig(versionId);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
rootConfig,
|
|
34
|
+
layoutsConfig,
|
|
35
|
+
fallbackLayoutsConfig,
|
|
36
|
+
docs: DOCS,
|
|
37
|
+
docsHtml,
|
|
38
|
+
versionConfigs,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/** Build root config for gitpagedocs */
|
|
2
|
+
import { OFFICIAL_LAYOUTS_CONFIG_URL, OFFICIAL_LAYOUTS_TEMPLATES_URL } from "../data/urls.mjs";
|
|
3
|
+
import { DOCS } from "../content/docs.mjs";
|
|
4
|
+
import { DOC_VERSIONS } from "../data/version-constants.mjs";
|
|
5
|
+
import { defaultLangMenu } from "../data/i18n-langmenu.mjs";
|
|
6
|
+
import { defaultTranslations } from "../data/i18n-translations.mjs";
|
|
7
|
+
import { getDefaultSiteConfig } from "../data/default-site-config.mjs";
|
|
8
|
+
|
|
9
|
+
export function buildRootConfig(options = {}) {
|
|
10
|
+
const useLocalLayoutConfig = Boolean(options.useLocalLayoutConfig);
|
|
11
|
+
const useOfficialLayouts = !useLocalLayoutConfig;
|
|
12
|
+
const githubOwner = options.githubOwner;
|
|
13
|
+
const githubRepo = options.githubRepo;
|
|
14
|
+
const repositorySearchHome = githubOwner && githubRepo ? false : true;
|
|
15
|
+
const renderingUrl =
|
|
16
|
+
githubOwner && githubRepo
|
|
17
|
+
? `https://${githubOwner}.github.io/${githubRepo}/`
|
|
18
|
+
: "https://vidigal-code.github.io/git-page-docs/";
|
|
19
|
+
const projectLink =
|
|
20
|
+
githubOwner && githubRepo
|
|
21
|
+
? `https://github.com/${githubOwner}/${githubRepo}`
|
|
22
|
+
: "https://github.com/Vidigal-code/git-page-docs";
|
|
23
|
+
|
|
24
|
+
const versionEntries = DOC_VERSIONS.map((id) => ({
|
|
25
|
+
id,
|
|
26
|
+
path: `gitpagedocs/docs/versions/${id}/config.json`,
|
|
27
|
+
ProjectLink: projectLink,
|
|
28
|
+
PathConfig: `gitpagedocs/docs/versions/${id}/config.json`,
|
|
29
|
+
PreviewProject: "",
|
|
30
|
+
UpdateDate: "",
|
|
31
|
+
branch: "",
|
|
32
|
+
release: "",
|
|
33
|
+
commit: "",
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
const baseSiteConfig = getDefaultSiteConfig(DOCS, projectLink);
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
site: {
|
|
40
|
+
...baseSiteConfig,
|
|
41
|
+
layoutsConfigPathOficial: useOfficialLayouts,
|
|
42
|
+
layoutsConfigPathTemplatesOficial: useOfficialLayouts ? OFFICIAL_LAYOUTS_TEMPLATES_URL : "",
|
|
43
|
+
layoutsConfigPathOficialUrl: useOfficialLayouts ? OFFICIAL_LAYOUTS_CONFIG_URL : "",
|
|
44
|
+
repositorySearchHome,
|
|
45
|
+
rendering: renderingUrl,
|
|
46
|
+
AiChatEnabled: true,
|
|
47
|
+
langmenu: defaultLangMenu,
|
|
48
|
+
},
|
|
49
|
+
VersionControl: {
|
|
50
|
+
versions: versionEntries,
|
|
51
|
+
},
|
|
52
|
+
translations: defaultTranslations,
|
|
53
|
+
};
|
|
54
|
+
}
|