getfilepress 0.1.2 → 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/README.md +36 -10
- package/package.json +99 -99
- package/packages/app/README.md +19 -0
- package/packages/app/package.json +1 -1
- package/packages/app/src/lib/genie/GeniePanel.svelte +672 -144
- package/packages/app/src/lib/genie/config-patch.ts +72 -0
- package/packages/app/src/lib/genie/ops.ts +231 -10
- package/packages/app/src/lib/genie/store.ts +12 -3
- package/packages/app/src/lib/pages.server.ts +4 -2
- package/packages/app/src/routes/sitemap.xml/+server.ts +5 -2
- package/packages/app/vite-plugin-genie.ts +64 -1
- package/packages/app/vite-plugin-path-mounts.ts +107 -0
- package/packages/app/vite.config.ts +112 -81
- package/packages/core/README.md +29 -0
- package/packages/core/package.json +1 -1
- package/packages/core/src/lib/config.ts +12 -1
- package/packages/core/src/lib/content/feeds.ts +5 -1
- package/packages/core/src/lib/content/pages.ts +11 -1
- package/packages/core/src/lib/content/parse.ts +12 -3
- package/packages/core/src/lib/index.ts +2 -1
- package/packages/core/src/lib/paths-shared.ts +89 -0
- package/packages/core/src/lib/paths.ts +84 -0
- package/packages/core/src/lib/server.ts +8 -1
- package/packages/core/src/lib/styles/theme.css +3 -2
- package/packages/import/package.json +2 -1
- package/packages/import/src/cli.ts +77 -3
- package/packages/import/src/ollama.ts +43 -1
- package/packages/import/src/ollanet-scan.ts +132 -0
- package/packages/import/tsconfig.json +1 -1
- package/scripts/copy-path-mounts.mjs +40 -0
- package/scripts/create-site.mjs +4 -11
- package/scripts/filepress.mjs +31 -3
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
ollamaSetupHint,
|
|
25
25
|
summarizeHtmlForBrief
|
|
26
26
|
} from './ollama.ts';
|
|
27
|
+
import { pickDiscoveredServer, scanOllamaNetwork } from './ollanet-scan.ts';
|
|
27
28
|
import { harvestImagesFromPage, planImages, type ImagePlan } from './images.ts';
|
|
28
29
|
import { formatAttributionMarkdown, planStockCovers } from './stock.ts';
|
|
29
30
|
import { DEFAULT_BRIEF, themeCssFromBrief, tokensFromSourceCss } from './theme.ts';
|
|
@@ -57,22 +58,30 @@ function parseArgs(argv: string[]) {
|
|
|
57
58
|
author?: string;
|
|
58
59
|
url?: string;
|
|
59
60
|
ollama: string;
|
|
61
|
+
ollamaExplicit: boolean;
|
|
60
62
|
model: string;
|
|
63
|
+
modelExplicit: boolean;
|
|
61
64
|
noLlm: boolean;
|
|
62
65
|
dryRun: boolean;
|
|
63
66
|
force: boolean;
|
|
64
67
|
yes: boolean;
|
|
65
68
|
fetchImages: boolean;
|
|
69
|
+
scan: boolean;
|
|
70
|
+
lan: boolean;
|
|
66
71
|
} = {
|
|
67
72
|
_: [],
|
|
68
73
|
inspire: [],
|
|
69
74
|
ollama: process.env.OLLAMA_HOST?.trim() || 'http://127.0.0.1:11434',
|
|
75
|
+
ollamaExplicit: Boolean(process.env.OLLAMA_HOST?.trim()),
|
|
70
76
|
model: process.env.FILEPRESS_OLLAMA_MODEL?.trim() || 'gemma4:12b',
|
|
77
|
+
modelExplicit: Boolean(process.env.FILEPRESS_OLLAMA_MODEL?.trim()),
|
|
71
78
|
noLlm: false,
|
|
72
79
|
dryRun: false,
|
|
73
80
|
force: false,
|
|
74
81
|
yes: false,
|
|
75
|
-
fetchImages: false
|
|
82
|
+
fetchImages: false,
|
|
83
|
+
scan: false,
|
|
84
|
+
lan: false
|
|
76
85
|
};
|
|
77
86
|
|
|
78
87
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -89,13 +98,24 @@ function parseArgs(argv: string[]) {
|
|
|
89
98
|
else if (a === '--title') out.title = next();
|
|
90
99
|
else if (a === '--author') out.author = next();
|
|
91
100
|
else if (a === '--url') out.url = next();
|
|
92
|
-
else if (a === '--ollama')
|
|
93
|
-
|
|
101
|
+
else if (a === '--ollama') {
|
|
102
|
+
out.ollama = next();
|
|
103
|
+
out.ollamaExplicit = true;
|
|
104
|
+
}
|
|
105
|
+
else if (a === '--model') {
|
|
106
|
+
out.model = next();
|
|
107
|
+
out.modelExplicit = true;
|
|
108
|
+
}
|
|
94
109
|
else if (a === '--no-llm') out.noLlm = true;
|
|
95
110
|
else if (a === '--dry-run') out.dryRun = true;
|
|
96
111
|
else if (a === '--force') out.force = true;
|
|
97
112
|
else if (a === '--yes' || a === '-y') out.yes = true;
|
|
98
113
|
else if (a === '--fetch-images') out.fetchImages = true;
|
|
114
|
+
else if (a === '--scan') out.scan = true;
|
|
115
|
+
else if (a === '--lan') {
|
|
116
|
+
out.lan = true;
|
|
117
|
+
out.scan = true;
|
|
118
|
+
}
|
|
99
119
|
else if (a === '--help' || a === '-h') out._.push('help');
|
|
100
120
|
else out._.push(a);
|
|
101
121
|
}
|
|
@@ -108,6 +128,55 @@ async function prompt(rl: ReturnType<typeof createInterface>, q: string, def?: s
|
|
|
108
128
|
return ans || def || '';
|
|
109
129
|
}
|
|
110
130
|
|
|
131
|
+
async function maybeScanOllama(
|
|
132
|
+
args: ReturnType<typeof parseArgs>,
|
|
133
|
+
rl: ReturnType<typeof createInterface> | null
|
|
134
|
+
): Promise<{ host: string; model: string }> {
|
|
135
|
+
let host = args.ollama;
|
|
136
|
+
let model = args.model;
|
|
137
|
+
if (args.noLlm || !args.scan) return { host, model };
|
|
138
|
+
|
|
139
|
+
console.log(
|
|
140
|
+
args.lan
|
|
141
|
+
? 'import: scanning for Ollama (localhost, config, Tailscale, LAN) …'
|
|
142
|
+
: 'import: scanning for Ollama (localhost, config, Tailscale) …'
|
|
143
|
+
);
|
|
144
|
+
const result = await scanOllamaNetwork({ lan: args.lan });
|
|
145
|
+
if (result.error) {
|
|
146
|
+
console.warn(`import: ollanet scan failed: ${result.error}`);
|
|
147
|
+
return { host, model };
|
|
148
|
+
}
|
|
149
|
+
if (!result.servers.length) {
|
|
150
|
+
console.warn(
|
|
151
|
+
'import: no Ollama servers found. Add ~/.ollanet/config.json hosts, set OLLANET_HOSTS, or pass --lan.'
|
|
152
|
+
);
|
|
153
|
+
return { host, model };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
for (const [i, s] of result.servers.entries()) {
|
|
157
|
+
const models = s.models.length ? s.models.join(', ') : '(no models)';
|
|
158
|
+
console.log(` ${i + 1}. ${s.label} ${s.endpoint} ${models}`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
let picked = pickDiscoveredServer(result.servers, args.ollamaExplicit ? host : undefined);
|
|
162
|
+
if (rl && result.servers.length > 1) {
|
|
163
|
+
const def = String((picked ? result.servers.indexOf(picked) : 0) + 1);
|
|
164
|
+
const ans = await prompt(rl, 'Ollama server number', def);
|
|
165
|
+
const idx = Number(ans) - 1;
|
|
166
|
+
if (Number.isInteger(idx) && result.servers[idx]) picked = result.servers[idx];
|
|
167
|
+
}
|
|
168
|
+
if (picked) {
|
|
169
|
+
host = picked.endpoint;
|
|
170
|
+
if (!args.modelExplicit) {
|
|
171
|
+
const preferred = process.env.FILEPRESS_OLLAMA_MODEL?.trim();
|
|
172
|
+
if (preferred && picked.models.includes(preferred)) model = preferred;
|
|
173
|
+
else if (picked.models[0]) model = picked.models[0];
|
|
174
|
+
}
|
|
175
|
+
console.log(`import: using ${host} · ${model}`);
|
|
176
|
+
}
|
|
177
|
+
return { host, model };
|
|
178
|
+
}
|
|
179
|
+
|
|
111
180
|
async function main() {
|
|
112
181
|
const args = parseArgs(process.argv.slice(2));
|
|
113
182
|
if (args._.includes('help')) {
|
|
@@ -121,6 +190,8 @@ Options:
|
|
|
121
190
|
--title / --author / --url
|
|
122
191
|
--ollama <host> Default http://127.0.0.1:11434
|
|
123
192
|
--model <name> Default gemma4:12b
|
|
193
|
+
--scan Discover Ollama hosts via ollanet (localhost, config, Tailscale)
|
|
194
|
+
--lan Also TCP-scan local LAN (implies --scan)
|
|
124
195
|
--no-llm Skip Ollama; token theme from source CSS / defaults
|
|
125
196
|
--dry-run Crawl + report only (no write)
|
|
126
197
|
--force Overwrite generated content in --out
|
|
@@ -232,6 +303,9 @@ Options:
|
|
|
232
303
|
};
|
|
233
304
|
|
|
234
305
|
if (!opts.noLlm) {
|
|
306
|
+
const picked = await maybeScanOllama(args, rl);
|
|
307
|
+
opts.ollamaHost = picked.host;
|
|
308
|
+
opts.ollamaModel = picked.model;
|
|
235
309
|
const up = await ollamaAvailable(opts.ollamaHost);
|
|
236
310
|
if (!up) {
|
|
237
311
|
console.warn(`import: ${ollamaSetupHint(opts.ollamaHost)}`);
|
|
@@ -2,6 +2,30 @@ import type { DesignBrief, SiteIR } from './ir.ts';
|
|
|
2
2
|
import type { InspirationSignals } from './inspire.ts';
|
|
3
3
|
import { DEFAULT_BRIEF, parseBriefJson } from './theme.ts';
|
|
4
4
|
|
|
5
|
+
/** Strip trailing slashes; used to compare Genie picker values with env. */
|
|
6
|
+
export function normalizeOllamaHost(host: string): string {
|
|
7
|
+
return host.trim().replace(/\/+$/, '');
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** Allow only http(s) Ollama URLs (no credentials). */
|
|
11
|
+
export function assertOllamaEndpoint(raw: string): string {
|
|
12
|
+
const trimmed = raw.trim();
|
|
13
|
+
if (!trimmed) throw new Error('Ollama host is empty');
|
|
14
|
+
let url: URL;
|
|
15
|
+
try {
|
|
16
|
+
url = new URL(trimmed);
|
|
17
|
+
} catch {
|
|
18
|
+
throw new Error(`Invalid Ollama host "${trimmed}"`);
|
|
19
|
+
}
|
|
20
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
21
|
+
throw new Error(`Ollama host must be http(s), got ${url.protocol}`);
|
|
22
|
+
}
|
|
23
|
+
if (url.username || url.password) {
|
|
24
|
+
throw new Error('Ollama host must not include credentials');
|
|
25
|
+
}
|
|
26
|
+
return `${url.protocol}//${url.host}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
5
29
|
export async function ollamaAvailable(host: string): Promise<boolean> {
|
|
6
30
|
try {
|
|
7
31
|
const res = await fetch(`${host.replace(/\/+$/, '')}/api/tags`, {
|
|
@@ -13,6 +37,23 @@ export async function ollamaAvailable(host: string): Promise<boolean> {
|
|
|
13
37
|
}
|
|
14
38
|
}
|
|
15
39
|
|
|
40
|
+
/** Model names from `GET /api/tags` (empty if unreachable). */
|
|
41
|
+
export async function listOllamaModels(host: string): Promise<string[]> {
|
|
42
|
+
try {
|
|
43
|
+
const res = await fetch(`${host.replace(/\/+$/, '')}/api/tags`, {
|
|
44
|
+
signal: AbortSignal.timeout(4000)
|
|
45
|
+
});
|
|
46
|
+
if (!res.ok) return [];
|
|
47
|
+
const data = (await res.json()) as { models?: Array<{ name?: string }> };
|
|
48
|
+
return (data.models || [])
|
|
49
|
+
.map((m) => (m.name || '').trim())
|
|
50
|
+
.filter(Boolean)
|
|
51
|
+
.sort((a, b) => a.localeCompare(b));
|
|
52
|
+
} catch {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
16
57
|
/** Shared copy for import + Genie Mode when Ollama is missing or unused. */
|
|
17
58
|
export function ollamaSetupHint(host: string): string {
|
|
18
59
|
const h = host.replace(/\/+$/, '') || 'http://127.0.0.1:11434';
|
|
@@ -20,7 +61,8 @@ export function ollamaSetupHint(host: string): string {
|
|
|
20
61
|
`Ollama not reachable at ${h}.`,
|
|
21
62
|
`Install from https://ollama.com then pull a model (e.g. ollama pull gemma4:12b).`,
|
|
22
63
|
`For a GPU-tuned named variant, use Finetuna: https://github.com/Catalyst-Forge-LLC/finetuna`,
|
|
23
|
-
`Then set FILEPRESS_OLLAMA_MODEL to that name (and OLLAMA_HOST if remote)
|
|
64
|
+
`Then set FILEPRESS_OLLAMA_MODEL to that name (and OLLAMA_HOST if remote).`,
|
|
65
|
+
`To find other Ollama boxes (Tailscale, LAN, OLLANET_HOSTS), use Genie “Scan network” or filepress import --scan (ollanet).`
|
|
24
66
|
].join(' ');
|
|
25
67
|
}
|
|
26
68
|
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional network discovery for Ollama via ollanet (≥ 0.4.0).
|
|
3
|
+
* LAN TCP scan is opt-in (slow); default scan is localhost + config + Tailscale.
|
|
4
|
+
* Node-only — Genie middleware and the import CLI, never the Svelte client.
|
|
5
|
+
*/
|
|
6
|
+
import { scanNetwork } from 'ollanet';
|
|
7
|
+
|
|
8
|
+
export type DiscoveredOllamaServer = {
|
|
9
|
+
label: string;
|
|
10
|
+
endpoint: string;
|
|
11
|
+
source: string;
|
|
12
|
+
self: boolean;
|
|
13
|
+
models: string[];
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type OllamaScanResult = {
|
|
17
|
+
ok: boolean;
|
|
18
|
+
network: string;
|
|
19
|
+
sources: string[];
|
|
20
|
+
scanned: number;
|
|
21
|
+
servers: DiscoveredOllamaServer[];
|
|
22
|
+
error?: string;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/** Loose scan JSON (ollanet ScanPayload plus incomplete fixtures). */
|
|
26
|
+
type ScanPayloadLike = {
|
|
27
|
+
network?: string;
|
|
28
|
+
sources?: string[];
|
|
29
|
+
scanned?: number;
|
|
30
|
+
servers?: Array<{
|
|
31
|
+
hostname?: string;
|
|
32
|
+
dnsName?: string;
|
|
33
|
+
ip?: string;
|
|
34
|
+
source?: string;
|
|
35
|
+
self?: boolean;
|
|
36
|
+
endpoint?: string;
|
|
37
|
+
models?: Array<{ name?: string }>;
|
|
38
|
+
}>;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export function normalizeEndpoint(host: string): string {
|
|
42
|
+
return host.trim().replace(/\/+$/, '').toLowerCase();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function serverLabel(server: {
|
|
46
|
+
dnsName?: string;
|
|
47
|
+
hostname?: string;
|
|
48
|
+
ip?: string;
|
|
49
|
+
self?: boolean;
|
|
50
|
+
source?: string;
|
|
51
|
+
}): string {
|
|
52
|
+
const name = (server.dnsName || server.hostname || server.ip || 'ollama').trim();
|
|
53
|
+
const bits = [name];
|
|
54
|
+
if (server.self) bits.push('this device');
|
|
55
|
+
if (server.source && server.source !== 'localhost') bits.push(server.source);
|
|
56
|
+
return bits.join(' · ');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function mapScanPayload(payload: ScanPayloadLike): OllamaScanResult {
|
|
60
|
+
const servers = (payload.servers ?? [])
|
|
61
|
+
.map((s) => {
|
|
62
|
+
const endpoint = (s.endpoint || '').trim().replace(/\/+$/, '');
|
|
63
|
+
if (!endpoint) return null;
|
|
64
|
+
const models = (s.models ?? [])
|
|
65
|
+
.map((m) => (m.name || '').trim())
|
|
66
|
+
.filter(Boolean)
|
|
67
|
+
.sort((a, b) => a.localeCompare(b));
|
|
68
|
+
return {
|
|
69
|
+
label: serverLabel(s),
|
|
70
|
+
endpoint,
|
|
71
|
+
source: s.source || 'unknown',
|
|
72
|
+
self: Boolean(s.self),
|
|
73
|
+
models
|
|
74
|
+
} satisfies DiscoveredOllamaServer;
|
|
75
|
+
})
|
|
76
|
+
.filter((s): s is DiscoveredOllamaServer => s != null);
|
|
77
|
+
|
|
78
|
+
const seen = new Set<string>();
|
|
79
|
+
const deduped: DiscoveredOllamaServer[] = [];
|
|
80
|
+
for (const s of servers) {
|
|
81
|
+
const key = normalizeEndpoint(s.endpoint);
|
|
82
|
+
if (seen.has(key)) continue;
|
|
83
|
+
seen.add(key);
|
|
84
|
+
deduped.push(s);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
ok: true,
|
|
89
|
+
network: payload.network || 'local',
|
|
90
|
+
sources: payload.sources ?? [],
|
|
91
|
+
scanned: payload.scanned ?? deduped.length,
|
|
92
|
+
servers: deduped
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Prefer an explicit endpoint, then this-device with models, then any host with models. */
|
|
97
|
+
export function pickDiscoveredServer(
|
|
98
|
+
servers: DiscoveredOllamaServer[],
|
|
99
|
+
preferredEndpoint?: string
|
|
100
|
+
): DiscoveredOllamaServer | undefined {
|
|
101
|
+
if (preferredEndpoint) {
|
|
102
|
+
const want = normalizeEndpoint(preferredEndpoint);
|
|
103
|
+
const hit = servers.find((s) => normalizeEndpoint(s.endpoint) === want);
|
|
104
|
+
if (hit) return hit;
|
|
105
|
+
}
|
|
106
|
+
const withModels = servers.filter((s) => s.models.length > 0);
|
|
107
|
+
const pool = withModels.length ? withModels : servers;
|
|
108
|
+
return pool.find((s) => s.self) ?? pool[0];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Discover reachable Ollama servers. Never throws — callers get `error` text.
|
|
113
|
+
* Pass `lan: true` to TCP-scan local /24s (can take several seconds).
|
|
114
|
+
*/
|
|
115
|
+
export async function scanOllamaNetwork(
|
|
116
|
+
opts: { lan?: boolean } = {}
|
|
117
|
+
): Promise<OllamaScanResult> {
|
|
118
|
+
try {
|
|
119
|
+
const payload = await scanNetwork({ lanScan: Boolean(opts.lan) });
|
|
120
|
+
return mapScanPayload(payload);
|
|
121
|
+
} catch (e) {
|
|
122
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
123
|
+
return {
|
|
124
|
+
ok: false,
|
|
125
|
+
network: '',
|
|
126
|
+
sources: [],
|
|
127
|
+
scanned: 0,
|
|
128
|
+
servers: [],
|
|
129
|
+
error: message
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copy FilePress `paths` mounts into <site>/build after `vite build`.
|
|
3
|
+
* Reads `.filepress/path-mounts.json` written when vite.config loads the site config.
|
|
4
|
+
*
|
|
5
|
+
* Usage: node scripts/copy-path-mounts.mjs <siteRoot>
|
|
6
|
+
*/
|
|
7
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, statSync } from 'node:fs';
|
|
8
|
+
import { join, resolve } from 'node:path';
|
|
9
|
+
|
|
10
|
+
const siteRoot = resolve(process.argv[2] ?? '');
|
|
11
|
+
const buildDir = join(siteRoot, 'build');
|
|
12
|
+
const cachePath = join(siteRoot, '.filepress', 'path-mounts.json');
|
|
13
|
+
|
|
14
|
+
if (!existsSync(cachePath)) {
|
|
15
|
+
process.exit(0);
|
|
16
|
+
}
|
|
17
|
+
if (!existsSync(buildDir)) {
|
|
18
|
+
console.warn(`filepress: path mounts skipped — build dir missing (${buildDir})`);
|
|
19
|
+
process.exit(0);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** @type {{ url: string; dir: string }[]} */
|
|
23
|
+
const mounts = JSON.parse(readFileSync(cachePath, 'utf8'));
|
|
24
|
+
if (!Array.isArray(mounts) || mounts.length === 0) process.exit(0);
|
|
25
|
+
|
|
26
|
+
for (const mount of mounts) {
|
|
27
|
+
const src = resolve(siteRoot, mount.dir);
|
|
28
|
+
const dest = join(buildDir, ...mount.url.replace(/^\//, '').split('/'));
|
|
29
|
+
if (!existsSync(src)) {
|
|
30
|
+
console.warn(`filepress: path mount ${mount.url} ← ${mount.dir} (missing; skipped)`);
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (!statSync(src).isDirectory()) {
|
|
34
|
+
console.error(`filepress: path mount dir "${mount.dir}" is not a directory`);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
mkdirSync(dest, { recursive: true });
|
|
38
|
+
cpSync(src, dest, { recursive: true });
|
|
39
|
+
console.log(`filepress: mounted ${mount.url} ← ${mount.dir}`);
|
|
40
|
+
}
|
package/scripts/create-site.mjs
CHANGED
|
@@ -198,24 +198,17 @@ pnpm build # → build/
|
|
|
198
198
|
Optional: add \`theme.css\` next to \`filepress.config.ts\` to
|
|
199
199
|
override the default Essay theme.
|
|
200
200
|
|
|
201
|
-
## Deploy
|
|
201
|
+
## Deploy
|
|
202
202
|
|
|
203
|
-
\`link:\` only works on your machine. For CI/hosting,
|
|
203
|
+
\`link:\` only works on your machine. For CI/hosting, pin npm or a git tag:
|
|
204
204
|
|
|
205
205
|
\`\`\`json
|
|
206
206
|
"getfilepress": "^0.1.1"
|
|
207
207
|
\`\`\`
|
|
208
208
|
|
|
209
|
-
|
|
209
|
+
**Cloudflare Pages (recommended):** build \`pnpm install && pnpm build\`, output \`build\`, Node 20+.
|
|
210
210
|
|
|
211
|
-
|
|
212
|
-
"getfilepress": "github:Catalyst-Forge-LLC/filepress#v0.1.1"
|
|
213
|
-
\`\`\`
|
|
214
|
-
|
|
215
|
-
| Setting | Value |
|
|
216
|
-
| --- | --- |
|
|
217
|
-
| Build command | \`pnpm install && pnpm build\` |
|
|
218
|
-
| Output directory | \`build\` |
|
|
211
|
+
Any static host: publish the \`build/\` folder. Details: https://getfilepress.com/deploy
|
|
219
212
|
`
|
|
220
213
|
);
|
|
221
214
|
|
package/scripts/filepress.mjs
CHANGED
|
@@ -100,7 +100,7 @@ function findPackageBin(pkgName, binName, starts = [appDir, importDir, packageRo
|
|
|
100
100
|
return null;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
function runNodeBin(pkgName, binName, args, { cwd, env } = {}) {
|
|
103
|
+
function runNodeBin(pkgName, binName, args, { cwd, env, onSuccess } = {}) {
|
|
104
104
|
const bin = findPackageBin(pkgName, binName);
|
|
105
105
|
if (!bin) {
|
|
106
106
|
fail(
|
|
@@ -116,7 +116,19 @@ function runNodeBin(pkgName, binName, args, { cwd, env } = {}) {
|
|
|
116
116
|
});
|
|
117
117
|
child.on('exit', (code, signal) => {
|
|
118
118
|
if (signal) process.kill(process.pid, signal);
|
|
119
|
-
process.exit(code ?? 1);
|
|
119
|
+
if (code) process.exit(code ?? 1);
|
|
120
|
+
if (typeof onSuccess === 'function') {
|
|
121
|
+
const result = onSuccess();
|
|
122
|
+
// If onSuccess returns a ChildProcess, wait for it instead of exiting now.
|
|
123
|
+
if (result && typeof result.on === 'function') {
|
|
124
|
+
result.on('exit', (c, s) => {
|
|
125
|
+
if (s) process.kill(process.pid, s);
|
|
126
|
+
process.exit(c ?? 1);
|
|
127
|
+
});
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
process.exit(0);
|
|
120
132
|
});
|
|
121
133
|
return child;
|
|
122
134
|
}
|
|
@@ -269,5 +281,21 @@ function runSiteCommand(argv) {
|
|
|
269
281
|
const viteArgs = [command];
|
|
270
282
|
if (args.host !== null) viteArgs.push('--host', args.host === 'true' ? 'true' : args.host);
|
|
271
283
|
viteArgs.push(...args.extra);
|
|
272
|
-
runNodeBin('vite', 'vite', viteArgs, {
|
|
284
|
+
runNodeBin('vite', 'vite', viteArgs, {
|
|
285
|
+
cwd: appDir,
|
|
286
|
+
env,
|
|
287
|
+
onSuccess:
|
|
288
|
+
command === 'build'
|
|
289
|
+
? () => {
|
|
290
|
+
const copyScript = join(scriptDir, 'copy-path-mounts.mjs');
|
|
291
|
+
if (!existsSync(copyScript)) return;
|
|
292
|
+
return spawn(process.execPath, [copyScript, siteRoot], {
|
|
293
|
+
cwd: packageRoot,
|
|
294
|
+
env,
|
|
295
|
+
stdio: 'inherit',
|
|
296
|
+
shell: false
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
: undefined
|
|
300
|
+
});
|
|
273
301
|
}
|