Files
Webklar-Kundenbereich/runner/src/detect.js
T
2026-09-30 21:53:41 +00:00

84 lines
3.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Reine Projekterkennung (Spec 1.2 „Erkennung“): static | build | dev, Paketmanager, Befehle.
import { statSync, readFileSync } from 'node:fs';
import { join } from 'node:path';
const DEFAULT_OUTPUTS = ['dist', 'build', 'out', 'web-build'];
const DEFAULT_PORT = 5173;
const safeRel = (p) => typeof p === 'string' && p !== '' && p.length <= 200 && !p.startsWith('/')
&& !p.includes('\0') && !p.split(/[\\/]/).includes('..');
function scriptsOf(files) {
try {
const txt = files.read('package.json');
if (txt == null) return {};
const s = JSON.parse(txt)?.scripts;
return s && typeof s === 'object' && !Array.isArray(s) ? s : {};
} catch {
return {};
}
}
const hasScript = (scripts, name) => typeof scripts[name] === 'string' && scripts[name].trim() !== '';
function detectPm(files) {
if (files.has('pnpm-lock.yaml')) return 'pnpm';
if (files.has('yarn.lock')) return 'yarn';
if (files.has('bun.lockb') || files.has('bun.lock')) return 'bun';
return 'npm';
}
function installCmd(pm, files) {
if (pm === 'npm') {
return files.has('package-lock.json') || files.has('npm-shrinkwrap.json')
? ['npm', 'ci', '--include=dev']
: ['npm', 'install', '--include=dev', '--no-audit', '--no-fund'];
}
return [pm, 'install', '--frozen-lockfile'];
}
// npm/pnpm brauchen `--` vor den Skript-Argumenten, yarn/bun reichen sie direkt durch.
function devCmdFor(pm, port) {
const args = ['--host', '0.0.0.0', '--port', String(port)];
return pm === 'npm' || pm === 'pnpm' ? [pm, 'run', 'dev', '--', ...args] : [pm, 'run', 'dev', ...args];
}
export function detectProject(files, { mode, index, previewJson } = {}) {
const pj = previewJson && typeof previewJson === 'object' && !Array.isArray(previewJson) ? previewJson : {};
const scripts = scriptsOf(files);
const pm = detectPm(files);
const devPort = Number.isInteger(pj.devPort) && pj.devPort >= 1024 && pj.devPort <= 65535 ? pj.devPort : DEFAULT_PORT;
const outputDirs = safeRel(pj.output) ? [pj.output] : [...DEFAULT_OUTPUTS];
const indexFile = [index, pj.index].find(safeRel) || 'index.html';
const hasIndex = files.has(indexFile);
const hasBuild = hasScript(scripts, 'build');
const hasDev = hasScript(scripts, 'dev');
const m = mode === 'build' || mode === 'dev' ? mode : 'auto';
const base = { pm, install: installCmd(pm, files), buildCmd: hasBuild ? [pm, 'run', 'build'] : null, devCmd: hasDev ? devCmdFor(pm, devPort) : null, outputDirs, devPort };
const asBuild = () => ({ kind: 'build', ...base });
const asDev = () => ({ kind: 'dev', ...base });
const asStatic = (hint) => ({ kind: 'static', ...base, install: [], buildCmd: null, devCmd: null, outputDirs: [], index: indexFile, ...(hint ? { hint } : {}) });
if (m === 'build') return hasBuild ? asBuild() : { ...asBuild(), error: 'Kein build-Skript in package.json' };
if (m === 'dev') return hasDev ? asDev() : { ...asDev(), error: 'Kein dev-Skript in package.json' };
if (hasIndex && !hasBuild) return asStatic();
if (hasBuild) return asBuild();
if (hasDev) return asDev();
return asStatic(`Hinweis: Weder ${indexFile} noch ein build-/dev-Skript gefunden – Inhalt wird unverändert als statische Seite bereitgestellt.`);
}
// Dateisystem-Adapter für detectProject; previewJson wird aus .webklar-preview.json gelesen, wenn nicht übergeben.
export function detectFromDir(dir, opts = {}) {
const abs = (p) => join(dir, p);
const files = {
has: (p) => { try { return statSync(abs(p)).isFile(); } catch { return false; } },
read: (p) => { try { return readFileSync(abs(p), 'utf8'); } catch { return null; } },
};
let previewJson = opts.previewJson;
if (previewJson === undefined) {
try { previewJson = JSON.parse(files.read('.webklar-preview.json')); } catch { previewJson = {}; }
}
return detectProject(files, { ...opts, previewJson });
}