sv
Version:
A command line interface (CLI) for creating and maintaining Svelte applications
854 lines (853 loc) • 24.4 kB
JavaScript
import { S as __toESM, b as __commonJSMin, f as R, i as addPnpmAllowBuilds, m as create, p as z, t as add, x as __require } from "../engine-DSL32Woe.mjs";
import fs from "node:fs";
import path from "node:path";
import process$1 from "node:process";
import { execSync } from "node:child_process";
//#region ../../node_modules/.pnpm/through@2.3.8/node_modules/through/index.js
var require_through = /* @__PURE__ */ __commonJSMin(((exports, module) => {
var Stream$4 = __require("stream");
exports = module.exports = through;
through.through = through;
function through(write, end, opts) {
write = write || function(data) {
this.queue(data);
};
end = end || function() {
this.queue(null);
};
var ended = false, destroyed = false, buffer = [], _ended = false;
var stream = new Stream$4();
stream.readable = stream.writable = true;
stream.paused = false;
stream.autoDestroy = !(opts && opts.autoDestroy === false);
stream.write = function(data) {
write.call(this, data);
return !stream.paused;
};
function drain() {
while (buffer.length && !stream.paused) {
var data = buffer.shift();
if (null === data) return stream.emit("end");
else stream.emit("data", data);
}
}
stream.queue = stream.push = function(data) {
if (_ended) return stream;
if (data === null) _ended = true;
buffer.push(data);
drain();
return stream;
};
stream.on("end", function() {
stream.readable = false;
if (!stream.writable && stream.autoDestroy) process.nextTick(function() {
stream.destroy();
});
});
function _end() {
stream.writable = false;
end.call(stream);
if (!stream.readable && stream.autoDestroy) stream.destroy();
}
stream.end = function(data) {
if (ended) return;
ended = true;
if (arguments.length) stream.write(data);
_end();
return stream;
};
stream.destroy = function() {
if (destroyed) return;
destroyed = true;
ended = true;
buffer.length = 0;
stream.writable = stream.readable = false;
stream.emit("close");
return stream;
};
stream.pause = function() {
if (stream.paused) return;
stream.paused = true;
return stream;
};
stream.resume = function() {
if (stream.paused) {
stream.paused = false;
stream.emit("resume");
}
drain();
if (!stream.paused) stream.emit("drain");
return stream;
};
return stream;
}
}));
//#endregion
//#region ../../node_modules/.pnpm/from@0.1.7/node_modules/from/index.js
var require_from = /* @__PURE__ */ __commonJSMin(((exports, module) => {
var Stream$3 = __require("stream");
module.exports = function from(source) {
if (Array.isArray(source)) {
var source_index = 0, source_len = source.length;
return from(function(i) {
if (source_index < source_len) this.emit("data", source[source_index++]);
else this.emit("end");
return true;
});
}
var s = new Stream$3(), i = 0;
s.ended = false;
s.started = false;
s.readable = true;
s.writable = false;
s.paused = false;
s.ended = false;
s.pause = function() {
s.started = true;
s.paused = true;
};
function next() {
s.started = true;
if (s.ended) return;
while (!s.ended && !s.paused && source.call(s, i++, function() {
if (!s.ended && !s.paused) process.nextTick(next);
}));
}
s.resume = function() {
s.started = true;
s.paused = false;
next();
};
s.on("end", function() {
s.ended = true;
s.readable = false;
process.nextTick(s.destroy);
});
s.destroy = function() {
s.ended = true;
s.emit("close");
};
process.nextTick(function() {
if (!s.started) s.resume();
});
return s;
};
}));
//#endregion
//#region ../../node_modules/.pnpm/duplexer@0.1.2/node_modules/duplexer/index.js
var require_duplexer = /* @__PURE__ */ __commonJSMin(((exports, module) => {
var Stream$2 = __require("stream");
var writeMethods = [
"write",
"end",
"destroy"
];
var readMethods = ["resume", "pause"];
var readEvents = ["data", "close"];
var slice = Array.prototype.slice;
module.exports = duplex;
function forEach(arr, fn) {
if (arr.forEach) return arr.forEach(fn);
for (var i = 0; i < arr.length; i++) fn(arr[i], i);
}
function duplex(writer, reader) {
var stream = new Stream$2();
var ended = false;
forEach(writeMethods, proxyWriter);
forEach(readMethods, proxyReader);
forEach(readEvents, proxyStream);
reader.on("end", handleEnd);
writer.on("drain", function() {
stream.emit("drain");
});
writer.on("error", reemit);
reader.on("error", reemit);
stream.writable = writer.writable;
stream.readable = reader.readable;
return stream;
function proxyWriter(methodName) {
stream[methodName] = method;
function method() {
return writer[methodName].apply(writer, arguments);
}
}
function proxyReader(methodName) {
stream[methodName] = method;
function method() {
stream.emit(methodName);
var func = reader[methodName];
if (func) return func.apply(reader, arguments);
reader.emit(methodName);
}
}
function proxyStream(methodName) {
reader.on(methodName, reemit);
function reemit() {
var args = slice.call(arguments);
args.unshift(methodName);
stream.emit.apply(stream, args);
}
}
function handleEnd() {
if (ended) return;
ended = true;
var args = slice.call(arguments);
args.unshift("end");
stream.emit.apply(stream, args);
}
function reemit(err) {
stream.emit("error", err);
}
}
}));
//#endregion
//#region ../../node_modules/.pnpm/map-stream@0.1.0/node_modules/map-stream/index.js
var require_map_stream = /* @__PURE__ */ __commonJSMin(((exports, module) => {
var Stream$1 = __require("stream").Stream;
module.exports = function(mapper, opts) {
var stream = new Stream$1(), self = this, inputs = 0, outputs = 0, ended = false, paused = false, destroyed = false, lastWritten = 0, inNext = false;
this.opts = opts || {};
var errorEventName = this.opts.failures ? "failure" : "error";
var writeQueue = {};
stream.writable = true;
stream.readable = true;
function queueData(data, number) {
var nextToWrite = lastWritten + 1;
if (number === nextToWrite) {
if (data !== void 0) stream.emit.apply(stream, ["data", data]);
lastWritten++;
nextToWrite++;
} else writeQueue[number] = data;
if (writeQueue.hasOwnProperty(nextToWrite)) {
var dataToWrite = writeQueue[nextToWrite];
delete writeQueue[nextToWrite];
return queueData(dataToWrite, nextToWrite);
}
outputs++;
if (inputs === outputs) {
if (paused) paused = false, stream.emit("drain");
if (ended) end();
}
}
function next(err, data, number) {
if (destroyed) return;
inNext = true;
if (!err || self.opts.failures) queueData(data, number);
if (err) stream.emit.apply(stream, [errorEventName, err]);
inNext = false;
}
function wrappedMapper(input, number, callback) {
return mapper.call(null, input, function(err, data) {
callback(err, data, number);
});
}
stream.write = function(data) {
if (ended) throw new Error("map stream is not writable");
inNext = false;
inputs++;
try {
paused = wrappedMapper(data, inputs, next) === false;
return !paused;
} catch (err) {
if (inNext) throw err;
next(err);
return !paused;
}
};
function end(data) {
ended = true;
stream.writable = false;
if (data !== void 0) return queueData(data, inputs);
else if (inputs == outputs) stream.readable = false, stream.emit("end"), stream.destroy();
}
stream.end = function(data) {
if (ended) return;
end();
};
stream.destroy = function() {
ended = destroyed = true;
stream.writable = stream.readable = paused = false;
process.nextTick(function() {
stream.emit("close");
});
};
stream.pause = function() {
paused = true;
};
stream.resume = function() {
paused = false;
};
return stream;
};
}));
//#endregion
//#region ../../node_modules/.pnpm/pause-stream@0.0.11/node_modules/pause-stream/index.js
var require_pause_stream = /* @__PURE__ */ __commonJSMin(((exports, module) => {
module.exports = require_through();
}));
//#endregion
//#region ../../node_modules/.pnpm/split@0.3.3/node_modules/split/index.js
var require_split = /* @__PURE__ */ __commonJSMin(((exports, module) => {
var through = require_through();
var Decoder = __require("string_decoder").StringDecoder;
module.exports = split;
function split(matcher, mapper, options) {
var decoder = new Decoder();
var soFar = "";
var maxLength = options && options.maxLength;
if ("function" === typeof matcher) mapper = matcher, matcher = null;
if (!matcher) matcher = /\r?\n/;
function emit(stream, piece) {
if (mapper) {
try {
piece = mapper(piece);
} catch (err) {
return stream.emit("error", err);
}
if ("undefined" !== typeof piece) stream.queue(piece);
} else stream.queue(piece);
}
function next(stream, buffer) {
var pieces = ((soFar != null ? soFar : "") + buffer).split(matcher);
soFar = pieces.pop();
if (maxLength && soFar.length > maxLength) stream.emit("error", /* @__PURE__ */ new Error("maximum buffer reached"));
for (var i = 0; i < pieces.length; i++) {
var piece = pieces[i];
emit(stream, piece);
}
}
return through(function(b) {
next(this, decoder.write(b));
}, function() {
if (decoder.end) next(this, decoder.end());
if (soFar != null) emit(this, soFar);
this.queue(null);
});
}
}));
//#endregion
//#region ../../node_modules/.pnpm/stream-combiner@0.0.4/node_modules/stream-combiner/index.js
var require_stream_combiner = /* @__PURE__ */ __commonJSMin(((exports, module) => {
var duplexer = require_duplexer();
module.exports = function() {
var streams = [].slice.call(arguments), first = streams[0], last = streams[streams.length - 1], thepipe = duplexer(first, last);
if (streams.length == 1) return streams[0];
else if (!streams.length) throw new Error("connect called with empty args");
function recurse(streams) {
if (streams.length < 2) return;
streams[0].pipe(streams[1]);
recurse(streams.slice(1));
}
recurse(streams);
function onerror() {
var args = [].slice.call(arguments);
args.unshift("error");
thepipe.emit.apply(thepipe, args);
}
for (var i = 1; i < streams.length - 1; i++) streams[i].on("error", onerror);
return thepipe;
};
}));
//#endregion
//#region ../../node_modules/.pnpm/event-stream@3.3.4/node_modules/event-stream/index.js
var require_event_stream = /* @__PURE__ */ __commonJSMin(((exports) => {
var Stream = __require("stream").Stream, es$1 = exports, through = require_through(), from = require_from(), duplex = require_duplexer(), map = require_map_stream(), pause = require_pause_stream(), split = require_split(), pipeline = require_stream_combiner(), immediately = global.setImmediate || process.nextTick;
es$1.Stream = Stream;
es$1.through = through;
es$1.from = from;
es$1.duplex = duplex;
es$1.map = map;
es$1.pause = pause;
es$1.split = split;
es$1.pipeline = es$1.connect = es$1.pipe = pipeline;
es$1.concat = es$1.merge = function() {
var toMerge = [].slice.call(arguments);
if (toMerge.length === 1 && toMerge[0] instanceof Array) toMerge = toMerge[0];
var stream = new Stream();
stream.setMaxListeners(0);
var endCount = 0;
stream.writable = stream.readable = true;
if (toMerge.length) toMerge.forEach(function(e) {
e.pipe(stream, { end: false });
var ended = false;
e.on("end", function() {
if (ended) return;
ended = true;
endCount++;
if (endCount == toMerge.length) stream.emit("end");
});
});
else process.nextTick(function() {
stream.emit("end");
});
stream.write = function(data) {
this.emit("data", data);
};
stream.destroy = function() {
toMerge.forEach(function(e) {
if (e.destroy) e.destroy();
});
};
return stream;
};
es$1.writeArray = function(done) {
if ("function" !== typeof done) throw new Error("function writeArray (done): done must be function");
var a = new Stream(), array = [], isDone = false;
a.write = function(l) {
array.push(l);
};
a.end = function() {
isDone = true;
done(null, array);
};
a.writable = true;
a.readable = false;
a.destroy = function() {
a.writable = a.readable = false;
if (isDone) return;
done(/* @__PURE__ */ new Error("destroyed before end"), array);
};
return a;
};
es$1.readArray = function(array) {
var stream = new Stream(), i = 0, paused = false, ended = false;
stream.readable = true;
stream.writable = false;
if (!Array.isArray(array)) throw new Error("event-stream.read expects an array");
stream.resume = function() {
if (ended) return;
paused = false;
var l = array.length;
while (i < l && !paused && !ended) stream.emit("data", array[i++]);
if (i == l && !ended) ended = true, stream.readable = false, stream.emit("end");
};
process.nextTick(stream.resume);
stream.pause = function() {
paused = true;
};
stream.destroy = function() {
ended = true;
stream.emit("close");
};
return stream;
};
es$1.readable = function(func, continueOnError) {
var stream = new Stream(), i = 0, paused = false, ended = false, reading = false;
stream.readable = true;
stream.writable = false;
if ("function" !== typeof func) throw new Error("event-stream.readable expects async function");
stream.on("end", function() {
ended = true;
});
function get(err, data) {
if (err) {
stream.emit("error", err);
if (!continueOnError) stream.emit("end");
} else if (arguments.length > 1) stream.emit("data", data);
immediately(function() {
if (ended || paused || reading) return;
try {
reading = true;
func.call(stream, i++, function() {
reading = false;
get.apply(null, arguments);
});
} catch (err) {
stream.emit("error", err);
}
});
}
stream.resume = function() {
paused = false;
get();
};
process.nextTick(get);
stream.pause = function() {
paused = true;
};
stream.destroy = function() {
stream.emit("end");
stream.emit("close");
ended = true;
};
return stream;
};
es$1.mapSync = function(sync) {
return es$1.through(function write(data) {
var mappedData;
try {
mappedData = sync(data);
} catch (err) {
return this.emit("error", err);
}
if (mappedData !== void 0) this.emit("data", mappedData);
});
};
es$1.log = function(name) {
return es$1.through(function(data) {
[].slice.call(arguments);
if (name) console.error(name, data);
else console.error(data);
this.emit("data", data);
});
};
es$1.child = function(child) {
return es$1.duplex(child.stdin, child.stdout);
};
es$1.parse = function(options) {
var emitError = !!(options ? options.error : false);
return es$1.through(function(data) {
var obj;
try {
if (data) obj = JSON.parse(data.toString());
} catch (err) {
if (emitError) return this.emit("error", err);
return console.error(err, "attempting to parse:", data);
}
if (obj !== void 0) this.emit("data", obj);
});
};
es$1.stringify = function() {
var Buffer = __require("buffer").Buffer;
return es$1.mapSync(function(e) {
return JSON.stringify(Buffer.isBuffer(e) ? e.toString() : e) + "\n";
});
};
es$1.replace = function(from, to) {
return es$1.pipeline(es$1.split(from), es$1.join(to));
};
es$1.join = function(str) {
if ("function" === typeof str) return es$1.wait(str);
var first = true;
return es$1.through(function(data) {
if (!first) this.emit("data", str);
first = false;
this.emit("data", data);
return true;
});
};
es$1.wait = function(callback) {
var arr = [];
return es$1.through(function(data) {
arr.push(data);
}, function() {
var body = Buffer.isBuffer(arr[0]) ? Buffer.concat(arr) : arr.join("");
this.emit("data", body);
this.emit("end");
if (callback) callback(null, body);
});
};
es$1.pipeable = function() {
throw new Error("[EVENT-STREAM] es.pipeable is deprecated");
};
}));
//#endregion
//#region src/testing.ts
var import_ps_tree = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
var spawn$1 = __require("child_process").spawn, es = require_event_stream();
module.exports = function childrenOfPid(pid, callback) {
var headers = null;
if (typeof callback !== "function") throw new Error("childrenOfPid(pid, callback) expects callback");
if (typeof pid === "number") pid = pid.toString();
var processLister;
if (process.platform === "win32") processLister = spawn$1("wmic.exe", [
"PROCESS",
"GET",
"Name,ProcessId,ParentProcessId,Status"
]);
else processLister = spawn$1("ps", [
"-A",
"-o",
"ppid,pid,stat,comm"
]);
es.connect(processLister.stdout, es.split(), es.map(function(line, cb) {
var columns = line.trim().split(/\s+/);
if (!headers) {
headers = columns;
headers = headers.map(normalizeHeader);
return cb();
}
var row = {};
var h = headers.slice();
while (h.length) row[h.shift()] = h.length ? columns.shift() : columns.join(" ");
return cb(null, row);
}), es.writeArray(function(err, ps) {
var parents = {}, children = [];
parents[pid] = true;
ps.forEach(function(proc) {
if (parents[proc.PPID]) {
parents[proc.PID] = true;
children.push(proc);
}
});
callback(null, children);
})).on("error", callback);
};
/**
* Normalizes the given header `str` from the Windows
* title to the *nix title.
*
* @param {string} str Header string to normalize
*/
function normalizeHeader(str) {
if (process.platform !== "win32") return str;
switch (str) {
case "Name": return "COMMAND";
case "ParentProcessId": return "PPID";
case "ProcessId": return "PID";
case "Status": return "STAT";
default: throw new Error("Unknown process listing header: " + str);
}
}
})))(), 1);
const variants = [
"kit-js",
"kit-ts",
"vite-js",
"vite-ts"
];
const TEMPLATES_DIR = ".templates";
function setup({ cwd, clean = false, variants }) {
const workingDir = path.resolve(cwd);
if (clean && fs.existsSync(workingDir)) fs.rmSync(workingDir, {
force: true,
recursive: true
});
const templatesDir = path.resolve(workingDir, TEMPLATES_DIR);
fs.mkdirSync(templatesDir, { recursive: true });
for (const variant of variants) {
const templatePath = path.resolve(templatesDir, variant);
if (fs.existsSync(templatePath)) continue;
if (variant === "kit-js") create({
cwd: templatePath,
name: variant,
template: "minimal",
types: "checkjs"
});
else if (variant === "kit-ts") create({
cwd: templatePath,
name: variant,
template: "minimal",
types: "typescript"
});
else if (variant === "vite-js") create({
cwd: templatePath,
name: variant,
template: "svelte",
types: "none"
});
else if (variant === "vite-ts") create({
cwd: templatePath,
name: variant,
template: "svelte",
types: "typescript"
});
else throw new Error(`Unknown project variant: ${variant}`);
}
return { templatesDir };
}
function createProject({ cwd, testName, templatesDir }) {
const testDir = path.resolve(cwd, testName);
fs.mkdirSync(testDir, { recursive: true });
return ({ testId, variant, clean = true }) => {
const targetDir = path.resolve(testDir, testId);
if (clean && fs.existsSync(targetDir)) fs.rmSync(targetDir, {
force: true,
recursive: true
});
const templatePath = path.resolve(templatesDir, variant);
fs.cpSync(templatePath, targetDir, {
recursive: true,
force: true
});
return targetDir;
};
}
async function startPreview({ cwd, command = "npm run preview" }) {
const [cmd, ...args] = command.split(" ");
const proc = z(cmd, args, {
nodeOptions: {
cwd,
stdio: "pipe"
},
throwOnError: true,
timeout: 66999
});
const close = async () => {
if (!proc.pid) return;
await terminate(proc.pid);
};
return await new Promise((resolve, reject) => {
if (!proc.process?.stdout) return reject("impossible state");
proc.process.stdout.on("data", (data) => {
const urls = data.toString().replace(/[^\x20-\xaf]+/g, "").replace(/\[[0-9]{1,2}m/g, "").match(/http:\/\/[^:\s]+:[0-9]+\//g);
if (urls && urls.length > 0) {
const url = urls[0];
resolve({
url,
close
});
}
});
});
}
async function getProcessTree(pid) {
return new Promise((res, rej) => {
(0, import_ps_tree.default)(pid, (err, children) => {
if (err) rej(err);
res(children);
});
});
}
async function terminate(pid) {
if (process$1.platform === "win32") {
await R("taskkill", [
"/PID",
`${pid}`,
"/T",
"/F"
]);
return;
}
const children = await getProcessTree(pid);
for (let i = children.length - 1; i >= 0; i--) {
const child = children[i];
kill(Number(child.PID));
}
kill(pid);
}
function kill(pid) {
try {
process$1.kill(pid);
} catch {}
}
function setupGlobal({ TEST_DIR, pre, post }) {
return async function({ provide }) {
await pre?.();
const { templatesDir } = setup({
cwd: TEST_DIR,
variants
});
provide("testDir", TEST_DIR);
provide("templatesDir", templatesDir);
provide("variants", variants);
return async () => {
await post?.();
};
};
}
async function prepareServer({ cwd, page, buildCommand = "pnpm build", previewCommand = "pnpm preview" }) {
if (buildCommand) execSync(buildCommand, {
cwd,
stdio: "pipe"
});
const { url, close } = await startPreview({
cwd,
command: previewCommand
});
page.setDefaultNavigationTimeout(62e3);
try {
await page.goto(url);
} catch (e) {
await close();
throw e;
}
return {
url,
close
};
}
function createSetupTest(vitest, playwright) {
return function setupTest(addons, options) {
const { inject, test: vitestTest, beforeAll, beforeEach } = vitest;
const test = vitestTest.extend({});
const cwd = inject("testDir");
const templatesDir = inject("templatesDir");
const variants = inject("variants");
const withBrowser = options?.browser ?? true;
let create;
let browser;
if (withBrowser) beforeAll(async () => {
let chromium;
if (playwright) chromium = playwright.chromium;
else try {
({chromium} = await import("@playwright/test"));
} catch {
throw new Error("Browser testing requires @playwright/test. Install it with: pnpm add -D @playwright/test");
}
browser = await chromium.launch();
return async () => {
await browser.close();
};
});
const testCases = [];
for (const kind of options?.kinds ?? []) for (const variant of variants) {
const addonTestCase = {
variant,
kind
};
if (options?.filter === void 0 || options.filter(addonTestCase)) testCases.push(addonTestCase);
}
let testName;
test.beforeAll(async (_ctx, suite) => {
testName = path.dirname(suite.file.filepath).split("/").at(-1);
create = createProject({
cwd,
templatesDir,
testName
});
fs.writeFileSync(path.resolve(cwd, testName, "pnpm-workspace.yaml"), "packages:\n - '**/*'", "utf8");
fs.writeFileSync(path.resolve(cwd, testName, "package.json"), JSON.stringify({
name: `${testName}-workspace-root`,
private: true
}));
for (const addonTestCase of testCases) {
const { variant, kind } = addonTestCase;
const cwd = create({
testId: `${kind.type}-${variant}`,
variant
});
const metaPath = path.resolve(cwd, "meta.json");
fs.writeFileSync(metaPath, JSON.stringify({
variant,
kind
}, null, " "), "utf8");
if (options?.preAdd) await options.preAdd({
addonTestCase,
cwd
});
await add({
cwd,
addons,
options: kind.options,
packageManager: "pnpm"
});
addPnpmAllowBuilds(cwd, "pnpm", "esbuild");
}
execSync("pnpm install", {
cwd: path.resolve(cwd, testName),
stdio: "pipe"
});
});
beforeEach(async (ctx) => {
let browserCtx;
if (withBrowser) {
browserCtx = await browser.newContext();
ctx.page = await browserCtx.newPage();
}
ctx.cwd = (addonTestCase) => {
return path.join(cwd, testName, `${addonTestCase.kind.type}-${addonTestCase.variant}`);
};
return async () => {
if (withBrowser) await browserCtx.close();
};
});
return {
test,
testCases,
prepareServer
};
};
}
//#endregion
export { createSetupTest, prepareServer, setupGlobal, variants };