84 lines
3.8 KiB
JavaScript
84 lines
3.8 KiB
JavaScript
// 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 });
|
||
}
|