sv
Version:
A CLI for creating and updating SvelteKit projects
1,414 lines (1,401 loc) • 47.7 kB
JavaScript
import { AGENT_NAMES, Command, De, Fe, Ge, J, Ke, Option, T, Ue, Vu, We, __toESM$1 as __toESM, addPnpmBuildDependencies, create, detect, et, from, getUserAgent, installDependencies, installOption, ke, packageManagerPrompt, program, require_picocolors$1 as require_picocolors, resolveCommand, templates, up, ze } from "./package-manager-DO5R9a6p.js";
import { applyAddons, communityAddonIds, createWorkspace, formatFiles, getAddonDetails, getCommunityAddon, getHighlighter, isVersionUnsupportedBelow, officialAddons, setupAddons } from "./addons-DrsfA5uM.js";
import fs, { existsSync } from "node:fs";
import path, { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import process from "node:process";
import { promisify } from "node:util";
import { exec, execSync } from "node:child_process";
import { createGunzip } from "node:zlib";
import { pipeline } from "node:stream/promises";
//#region packages/cli/package.json
var name = "sv";
var version = "0.8.21";
var type = "module";
var description = "A CLI for creating and updating SvelteKit projects";
var license = "MIT";
var repository = {
"type": "git",
"url": "https://github.com/sveltejs/cli",
"directory": "packages/cli"
};
var homepage = "https://svelte.dev";
var scripts = {
"check": "tsc",
"format": "pnpm lint --write",
"lint": "prettier --check . --config ../../prettier.config.js --ignore-path ../../.gitignore --ignore-path .gitignore --ignore-path ../../.prettierignore"
};
var files = ["dist"];
var bin = "./dist/bin.js";
var exports = {
".": {
"types": "./dist/lib/index.d.ts",
"default": "./dist/index.js"
},
"./testing": {
"types": "./dist/lib/testing.d.ts",
"default": "./dist/testing.js"
}
};
var devDependencies = {
"@clack/prompts": "1.0.0-alpha.1",
"@sveltejs/addons": "workspace:*",
"@sveltejs/cli-core": "workspace:*",
"@sveltejs/create": "workspace:*",
"@types/degit": "^2.8.6",
"@types/ps-tree": "^1.1.6",
"commander": "^13.1.0",
"degit": "^2.8.4",
"empathic": "^1.1.0",
"package-manager-detector": "^0.2.11",
"picocolors": "^1.1.1",
"ps-tree": "^1.2.0",
"tinyexec": "^0.3.2",
"valibot": "^0.41.0"
};
var keywords = [
"create",
"new",
"project",
"starter",
"svelte",
"sveltekit",
"template",
"wizard"
];
var package_default = {
name,
version,
type,
description,
license,
repository,
homepage,
scripts,
files,
bin,
exports,
devDependencies,
keywords
};
//#endregion
//#region node_modules/.pnpm/valibot@0.41.0_typescript@5.8.3/node_modules/valibot/dist/index.js
var store;
function getGlobalConfig(config2) {
return {
lang: config2?.lang ?? store?.lang,
message: config2?.message,
abortEarly: config2?.abortEarly ?? store?.abortEarly,
abortPipeEarly: config2?.abortPipeEarly ?? store?.abortPipeEarly
};
}
var store2;
function getGlobalMessage(lang) {
return store2?.get(lang);
}
var store3;
function getSchemaMessage(lang) {
return store3?.get(lang);
}
var store4;
function getSpecificMessage(reference, lang) {
return store4?.get(reference)?.get(lang);
}
function _stringify(input) {
const type$1 = typeof input;
if (type$1 === "string") return `"${input}"`;
if (type$1 === "number" || type$1 === "bigint" || type$1 === "boolean") return `${input}`;
if (type$1 === "object" || type$1 === "function") return (input && Object.getPrototypeOf(input)?.constructor?.name) ?? "null";
return type$1;
}
function _addIssue(context, label, dataset, config2, other) {
const input = other && "input" in other ? other.input : dataset.value;
const expected = other?.expected ?? context.expects ?? null;
const received = other?.received ?? _stringify(input);
const issue = {
kind: context.kind,
type: context.type,
input,
expected,
received,
message: `Invalid ${label}: ${expected ? `Expected ${expected} but r` : "R"}eceived ${received}`,
requirement: context.requirement,
path: other?.path,
issues: other?.issues,
lang: config2.lang,
abortEarly: config2.abortEarly,
abortPipeEarly: config2.abortPipeEarly
};
const isSchema = context.kind === "schema";
const message = other?.message ?? context.message ?? getSpecificMessage(context.reference, issue.lang) ?? (isSchema ? getSchemaMessage(issue.lang) : null) ?? config2.message ?? getGlobalMessage(issue.lang);
if (message) issue.message = typeof message === "function" ? message(issue) : message;
if (isSchema) dataset.typed = false;
if (dataset.issues) dataset.issues.push(issue);
else dataset.issues = [issue];
}
function _isValidObjectKey(object2, key) {
return Object.hasOwn(object2, key) && key !== "__proto__" && key !== "prototype" && key !== "constructor";
}
function _joinExpects(values, separator) {
const list = [...new Set(values)];
if (list.length > 1) return `(${list.join(` ${separator} `)})`;
return list[0] ?? "never";
}
var ValiError = class extends Error {
/**
* The error issues.
*/
issues;
/**
* Creates a Valibot error with useful information.
*
* @param issues The error issues.
*/
constructor(issues) {
super(issues[0].message);
this.name = "ValiError";
this.issues = issues;
}
};
function transform(operation) {
return {
kind: "transformation",
type: "transform",
reference: transform,
async: false,
operation,
_run(dataset) {
dataset.value = this.operation(dataset.value);
return dataset;
}
};
}
function getDefault(schema, dataset, config2) {
return typeof schema.default === "function" ? schema.default(dataset, config2) : schema.default;
}
function array(item, message) {
return {
kind: "schema",
type: "array",
reference: array,
expects: "Array",
async: false,
item,
message,
_run(dataset, config2) {
const input = dataset.value;
if (Array.isArray(input)) {
dataset.typed = true;
dataset.value = [];
for (let key = 0; key < input.length; key++) {
const value2 = input[key];
const itemDataset = this.item._run({
typed: false,
value: value2
}, config2);
if (itemDataset.issues) {
const pathItem = {
type: "array",
origin: "value",
input,
key,
value: value2
};
for (const issue of itemDataset.issues) {
if (issue.path) issue.path.unshift(pathItem);
else issue.path = [pathItem];
dataset.issues?.push(issue);
}
if (!dataset.issues) dataset.issues = itemDataset.issues;
if (config2.abortEarly) {
dataset.typed = false;
break;
}
}
if (!itemDataset.typed) dataset.typed = false;
dataset.value.push(itemDataset.value);
}
} else _addIssue(this, "type", dataset, config2);
return dataset;
}
};
}
function boolean(message) {
return {
kind: "schema",
type: "boolean",
reference: boolean,
expects: "boolean",
async: false,
message,
_run(dataset, config2) {
if (typeof dataset.value === "boolean") dataset.typed = true;
else _addIssue(this, "type", dataset, config2);
return dataset;
}
};
}
function optional(wrapped, ...args) {
const schema = {
kind: "schema",
type: "optional",
reference: optional,
expects: `(${wrapped.expects} | undefined)`,
async: false,
wrapped,
_run(dataset, config2) {
if (dataset.value === void 0) {
if ("default" in this) dataset.value = getDefault(this, dataset, config2);
if (dataset.value === void 0) {
dataset.typed = true;
return dataset;
}
}
return this.wrapped._run(dataset, config2);
}
};
if (0 in args) schema.default = args[0];
return schema;
}
function picklist(options$1, message) {
return {
kind: "schema",
type: "picklist",
reference: picklist,
expects: _joinExpects(options$1.map(_stringify), "|"),
async: false,
options: options$1,
message,
_run(dataset, config2) {
if (this.options.includes(dataset.value)) dataset.typed = true;
else _addIssue(this, "type", dataset, config2);
return dataset;
}
};
}
function record(key, value2, message) {
return {
kind: "schema",
type: "record",
reference: record,
expects: "Object",
async: false,
key,
value: value2,
message,
_run(dataset, config2) {
const input = dataset.value;
if (input && typeof input === "object") {
dataset.typed = true;
dataset.value = {};
for (const entryKey in input) if (_isValidObjectKey(input, entryKey)) {
const entryValue = input[entryKey];
const keyDataset = this.key._run({
typed: false,
value: entryKey
}, config2);
if (keyDataset.issues) {
const pathItem = {
type: "object",
origin: "key",
input,
key: entryKey,
value: entryValue
};
for (const issue of keyDataset.issues) {
issue.path = [pathItem];
dataset.issues?.push(issue);
}
if (!dataset.issues) dataset.issues = keyDataset.issues;
if (config2.abortEarly) {
dataset.typed = false;
break;
}
}
const valueDataset = this.value._run({
typed: false,
value: entryValue
}, config2);
if (valueDataset.issues) {
const pathItem = {
type: "object",
origin: "value",
input,
key: entryKey,
value: entryValue
};
for (const issue of valueDataset.issues) {
if (issue.path) issue.path.unshift(pathItem);
else issue.path = [pathItem];
dataset.issues?.push(issue);
}
if (!dataset.issues) dataset.issues = valueDataset.issues;
if (config2.abortEarly) {
dataset.typed = false;
break;
}
}
if (!keyDataset.typed || !valueDataset.typed) dataset.typed = false;
if (keyDataset.typed) dataset.value[keyDataset.value] = valueDataset.value;
}
} else _addIssue(this, "type", dataset, config2);
return dataset;
}
};
}
function strictObject(entries, message) {
return {
kind: "schema",
type: "strict_object",
reference: strictObject,
expects: "Object",
async: false,
entries,
message,
_run(dataset, config2) {
const input = dataset.value;
if (input && typeof input === "object") {
dataset.typed = true;
dataset.value = {};
for (const key in this.entries) {
const value2 = input[key];
const valueDataset = this.entries[key]._run({
typed: false,
value: value2
}, config2);
if (valueDataset.issues) {
const pathItem = {
type: "object",
origin: "value",
input,
key,
value: value2
};
for (const issue of valueDataset.issues) {
if (issue.path) issue.path.unshift(pathItem);
else issue.path = [pathItem];
dataset.issues?.push(issue);
}
if (!dataset.issues) dataset.issues = valueDataset.issues;
if (config2.abortEarly) {
dataset.typed = false;
break;
}
}
if (!valueDataset.typed) dataset.typed = false;
if (valueDataset.value !== void 0 || key in input) dataset.value[key] = valueDataset.value;
}
if (!dataset.issues || !config2.abortEarly) {
for (const key in input) if (!(key in this.entries)) {
const value2 = input[key];
_addIssue(this, "type", dataset, config2, {
input: value2,
expected: "never",
path: [{
type: "object",
origin: "value",
input,
key,
value: value2
}]
});
break;
}
}
} else _addIssue(this, "type", dataset, config2);
return dataset;
}
};
}
function string(message) {
return {
kind: "schema",
type: "string",
reference: string,
expects: "string",
async: false,
message,
_run(dataset, config2) {
if (typeof dataset.value === "string") dataset.typed = true;
else _addIssue(this, "type", dataset, config2);
return dataset;
}
};
}
function _subIssues(datasets) {
let issues;
if (datasets) for (const dataset of datasets) if (issues) issues.push(...dataset.issues);
else issues = dataset.issues;
return issues;
}
function union(options$1, message) {
return {
kind: "schema",
type: "union",
reference: union,
expects: _joinExpects(options$1.map((option) => option.expects), "|"),
async: false,
options: options$1,
message,
_run(dataset, config2) {
let validDataset;
let typedDatasets;
let untypedDatasets;
for (const schema of this.options) {
const optionDataset = schema._run({
typed: false,
value: dataset.value
}, config2);
if (optionDataset.typed) if (optionDataset.issues) if (typedDatasets) typedDatasets.push(optionDataset);
else typedDatasets = [optionDataset];
else {
validDataset = optionDataset;
break;
}
else if (untypedDatasets) untypedDatasets.push(optionDataset);
else untypedDatasets = [optionDataset];
}
if (validDataset) return validDataset;
if (typedDatasets) {
if (typedDatasets.length === 1) return typedDatasets[0];
_addIssue(this, "type", dataset, config2, { issues: _subIssues(typedDatasets) });
dataset.typed = true;
} else if (untypedDatasets?.length === 1) return untypedDatasets[0];
else _addIssue(this, "type", dataset, config2, { issues: _subIssues(untypedDatasets) });
return dataset;
}
};
}
function parse(schema, input, config2) {
const dataset = schema._run({
typed: false,
value: input
}, getGlobalConfig(config2));
if (dataset.issues) throw new ValiError(dataset.issues);
return dataset.value;
}
function pipe(...pipe2) {
return {
...pipe2[0],
pipe: pipe2,
_run(dataset, config2) {
for (const item of pipe2) if (item.kind !== "metadata") {
if (dataset.issues && (item.kind === "schema" || item.kind === "transformation")) {
dataset.typed = false;
break;
}
if (!dataset.issues || !config2.abortEarly && !config2.abortPipeEarly) dataset = item._run(dataset, config2);
}
return dataset;
}
};
}
//#endregion
//#region node_modules/.pnpm/empathic@1.1.0/node_modules/empathic/package.mjs
function up$1(options$1) {
return up("package.json", options$1);
}
//#endregion
//#region packages/cli/utils/errors.ts
var UnsupportedError = class extends Error {
name = "Unsupported Environment";
reasons = [];
constructor(reasons) {
super();
this.reasons = reasons;
}
};
//#endregion
//#region packages/cli/utils/common.ts
var import_picocolors$3 = __toESM(require_picocolors(), 1);
const NO_PREFIX = "--no-";
let options = [];
function getLongFlag(flags) {
return flags.split(",").map((f) => f.trim()).find((f) => f.startsWith("--"));
}
const helpConfig = {
argumentDescription: formatDescription,
optionDescription: formatDescription,
visibleOptions(cmd) {
options = cmd.options;
const visible = cmd.options.filter((o) => !o.hidden);
const show = [];
for (const option of visible) {
const flag = getLongFlag(option.flags);
if (flag?.startsWith(NO_PREFIX)) {
const stripped = flag.slice(NO_PREFIX.length);
const isNoVariant = visible.some((o) => getLongFlag(o.flags)?.startsWith(`--${stripped}`));
if (isNoVariant) continue;
}
show.push(option);
}
return show;
},
optionTerm(option) {
const longFlag = getLongFlag(option.flags);
const flag = longFlag?.split(" ").at(0);
if (!flag || !longFlag) return option.flags;
const noVariant = `--no-${flag.slice(2)}`;
const hasVariant = options.some((o) => getLongFlag(o.flags) === noVariant);
if (hasVariant) return `--[no-]${longFlag.slice(2)}`;
return option.flags;
},
styleTitle: (str) => import_picocolors$3.default.underline(str),
styleCommandText: (str) => import_picocolors$3.default.red(str),
styleDescriptionText: (str) => import_picocolors$3.default.gray(str),
styleOptionText: (str) => import_picocolors$3.default.white(str),
styleArgumentText: (str) => import_picocolors$3.default.white(str),
styleSubcommandText: (str) => import_picocolors$3.default.red(str)
};
function formatDescription(arg) {
let output = arg.description;
if (arg.defaultValue !== undefined && String(arg.defaultValue)) output += import_picocolors$3.default.dim(` (default: ${JSON.stringify(arg.defaultValue)})`);
if (arg.argChoices !== undefined && String(arg.argChoices)) output += import_picocolors$3.default.dim(` (choices: ${arg.argChoices.join(", ")})`);
return output;
}
async function runCommand(action) {
try {
Ge(`Welcome to the Svelte CLI! ${import_picocolors$3.default.gray(`(v${package_default.version})`)}`);
const minimumVersion = "18.3.0";
const unsupported = isVersionUnsupportedBelow(process.versions.node, minimumVersion);
if (unsupported) T.warn(`You are using Node.js ${import_picocolors$3.default.red(process.versions.node)}, please upgrade to Node.js ${import_picocolors$3.default.green(minimumVersion)} or higher.`);
await action();
Fe("You're all set!");
} catch (e) {
if (e instanceof UnsupportedError) {
const padding = getPadding(e.reasons.map((r) => r.id));
const message = e.reasons.map((r) => ` ${r.id.padEnd(padding)} ${import_picocolors$3.default.red(r.reason)}`).join("\n");
T.error(`${e.name}\n\n${message}`);
T.message();
} else if (e instanceof Error) {
T.error(e.stack ?? String(e));
T.message();
}
De("Operation failed.");
}
}
function getPadding(lines) {
const lengths = lines.map((s) => s.length);
return Math.max(...lengths);
}
function forwardExitCode(error) {
if (error && typeof error === "object" && "status" in error && typeof error.status === "number") process.exit(error.status);
else process.exit(1);
}
//#endregion
//#region packages/cli/commands/add/fetch-packages.ts
const NODE_MODULES = fileURLToPath(new URL("../node_modules", import.meta.url));
const REGISTRY = "https://registry.npmjs.org";
const Directive = {
file: "file:",
npm: "npm:"
};
function verifyPackage(pkg, specifier) {
const deps = {
...pkg.dependencies,
...pkg.peerDependencies
};
if (!deps["@sveltejs/cli-core"]) throw new Error(`Invalid add-on package specified: '${specifier}' is missing a dependency on '@sveltejs/cli-core' in its 'package.json'`);
for (const dep of Object.keys(deps)) {
if (dep === "@sveltejs/cli-core") continue;
throw new Error(`Invalid add-on package detected: '${specifier}'\nCommunity addons should not have any external 'dependencies' besides '@sveltejs/cli-core'. Consider bundling your dependencies if they are necessary`);
}
}
async function downloadPackage(options$1) {
const { pkg } = options$1;
if (options$1.path) {
const dest = path.join(NODE_MODULES, pkg.name.split("/").join(path.sep));
if (fs.existsSync(dest)) fs.rmSync(dest);
const dir = path.dirname(dest);
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
fs.symlinkSync(options$1.path, dest);
const { default: details$1 } = await import(pkg.name);
return details$1;
}
const tarballUrl = pkg.dist.tarball;
const data = await fetch(tarballUrl);
if (!data.body) throw new Error(`Unexpected response: '${tarballUrl}' responded with no body`);
await pipeline(
data.body,
createGunzip()
// extract(NODE_MODULES, {
// map: (header) => {
// // file paths from the tarball will always have a `package/` prefix,
// // so we'll need to replace it with the name of the package
// header.name = header.name.replace('package', pkg.name);
// return header;
// }
// })
);
const { default: details } = await import(pkg.name);
return details;
}
async function getPackageJSON({ cwd, packageName }) {
let npm = packageName;
if (packageName.startsWith(Directive.file)) {
const pkgPath = path.resolve(cwd, packageName.slice(Directive.file.length));
const pkgJSONPath = path.resolve(pkgPath, "package.json");
const json = fs.readFileSync(pkgJSONPath, "utf8");
const pkg$1 = JSON.parse(json);
verifyPackage(pkg$1, packageName);
return {
path: pkgPath,
pkg: pkg$1,
repo: pkgPath
};
}
if (packageName.startsWith(Directive.npm)) npm = packageName.slice(Directive.npm.length);
const pkg = await fetchPackageJSON(npm);
verifyPackage(pkg, packageName);
return {
pkg,
repo: pkg.repository?.url ?? `https://www.npmjs.com/package/${npm}`
};
}
async function fetchPackageJSON(packageName) {
let pkgName = packageName;
let scope = "";
if (packageName.startsWith("@")) {
const [org, name$2] = pkgName.split("/", 2);
scope = `${org}/`;
pkgName = name$2;
}
const [name$1, tag = "latest"] = pkgName.split("@");
const pkgUrl = `${REGISTRY}/${scope + name$1}/${tag}`;
const resp = await fetch(pkgUrl);
if (resp.status === 404) throw new Error(`Package '${packageName}' doesn't exist in the registry: '${pkgUrl}'`);
if (resp.status < 200 && resp.status >= 300) throw new Error(`Failed to fetch '${pkgUrl}' - GET ${resp.status}`);
return await resp.json();
}
//#endregion
//#region packages/cli/commands/add/preconditions.ts
function getGlobalPreconditions(cwd, addons, addonSetupResult) {
return {
name: "global checks",
preconditions: [{
name: "clean working directory",
run: async () => {
try {
const asyncExec = promisify(exec);
const { stdout: stdout$1 } = await asyncExec("git status --short", { cwd });
if (stdout$1) return {
success: false,
message: "Found modified files"
};
return {
success: true,
message: undefined
};
} catch {
return {
success: true,
message: "Not a git repository"
};
}
}
}, {
name: "unsupported add-ons",
run: () => {
const reasons = addons.flatMap((a) => addonSetupResult[a.id].unsupported.map((reason) => ({
id: a.id,
reason
})));
if (reasons.length === 0) return {
success: true,
message: undefined
};
throw new UnsupportedError(reasons);
}
}]
};
}
//#endregion
//#region packages/cli/commands/add/index.ts
var import_picocolors$2 = __toESM(require_picocolors(), 1);
const aliases = officialAddons.map((c) => c.alias).filter((v) => v !== undefined);
const addonOptions = getAddonOptionFlags();
const communityDetails = [];
const AddonsSchema = array(string());
const OptionsSchema$1 = strictObject({
cwd: string(),
install: union([boolean(), picklist(AGENT_NAMES)]),
preconditions: boolean(),
community: optional(union([AddonsSchema, boolean()])),
addons: record(string(), optional(array(string())))
});
const defaultPkgPath = up$1();
const defaultCwd = defaultPkgPath ? path.dirname(defaultPkgPath) : undefined;
const add = new Command("add").description("applies specified add-ons into a project").argument("[add-on...]", `add-ons to install`, (value, prev = []) => {
const [addonId, optionFlags] = value.split("=", 2);
const repeatedAddons = prev.find(({ id }) => id === addonId);
if (repeatedAddons) {
console.error(`Malformed arguments: Add-on '${addonId}' is repeated multiple times.`);
process.exit(1);
}
if (optionFlags === undefined) {
prev.push({
id: addonId,
options: undefined
});
return prev;
}
if (optionFlags.length > 0 && !/.+:.*/.test(optionFlags)) {
console.error(`Malformed arguments: An add-on's option in '${value}' is missing it's option name or value (e.g. 'addon=option:value').`);
process.exit(1);
}
const options$1 = optionFlags.match(/[^+]*:[^:]*(?=\+|$)/g) ?? [];
prev.push({
id: addonId,
options: options$1
});
return prev;
}).option("-C, --cwd <path>", "path to working directory", defaultCwd).option("--no-preconditions", "skip validating preconditions").option("--no-install", "skip installing dependencies").addOption(installOption).configureHelp({
...helpConfig,
formatHelp(cmd, helper) {
const termWidth = helper.padWidth(cmd, helper);
const helpWidth = helper.helpWidth ?? 80;
function callFormatItem(term, description$1) {
return helper.formatItem(term, termWidth, description$1, helper);
}
let output = [`${helper.styleTitle("Usage:")} ${helper.styleUsage(helper.commandUsage(cmd))}`, ""];
const commandDescription = helper.commandDescription(cmd);
if (commandDescription.length > 0) output = output.concat([helper.boxWrap(helper.styleCommandDescription(commandDescription), helpWidth), ""]);
const argumentList = helper.visibleArguments(cmd).map((argument) => {
return callFormatItem(helper.styleArgumentTerm(helper.argumentTerm(argument)), helper.styleArgumentDescription(helper.argumentDescription(argument)));
});
if (argumentList.length > 0) output = output.concat([
helper.styleTitle("Arguments:"),
...argumentList,
""
]);
const addonList = addonOptions.map((option) => {
const description$1 = option.choices;
return callFormatItem(helper.styleArgumentTerm(option.id), helper.styleArgumentDescription(description$1));
});
if (addonList.length > 0) output = output.concat([
helper.styleTitle("Add-On Options:"),
...addonList,
""
]);
const optionList = helper.visibleOptions(cmd).map((option) => {
return callFormatItem(helper.styleOptionTerm(helper.optionTerm(option)), helper.styleOptionDescription(helper.optionDescription(option)));
});
if (optionList.length > 0) output = output.concat([
helper.styleTitle("Options:"),
...optionList,
""
]);
if (helper.showGlobalOptions) {
const globalOptionList = helper.visibleGlobalOptions(cmd).map((option) => {
return callFormatItem(helper.styleOptionTerm(helper.optionTerm(option)), helper.styleOptionDescription(helper.optionDescription(option)));
});
if (globalOptionList.length > 0) output = output.concat([
helper.styleTitle("Global Options:"),
...globalOptionList,
""
]);
}
const commandList = helper.visibleCommands(cmd).map((cmd$1) => {
return callFormatItem(helper.styleSubcommandTerm(helper.subcommandTerm(cmd$1)), helper.styleSubcommandDescription(helper.subcommandDescription(cmd$1)));
});
if (commandList.length > 0) output = output.concat([
helper.styleTitle("Commands:"),
...commandList,
""
]);
return output.join("\n");
}
}).action((addonArgs, opts) => {
if (opts.cwd === undefined) {
console.error("Invalid workspace: Please verify that you are inside of a Svelte project. You can also specify the working directory with `--cwd <path>`");
process.exit(1);
} else if (!fs.existsSync(path.resolve(opts.cwd, "package.json"))) {
console.error(`Invalid workspace: Path '${path.resolve(opts.cwd)}' is not a valid workspace.`);
process.exit(1);
}
const addonIds = officialAddons.map((addon) => addon.id);
const invalidAddons = addonArgs.filter(({ id }) => !addonIds.includes(id) && !aliases.includes(id)).map(({ id }) => id);
if (invalidAddons.length > 0) {
console.error(`Invalid add-ons specified: ${invalidAddons.join(", ")}`);
process.exit(1);
}
const options$1 = parse(OptionsSchema$1, {
...opts,
addons: {}
});
const selectedAddons = transformAliases(addonArgs);
selectedAddons.forEach((addon) => options$1.addons[addon.id] = addon.options);
runCommand(async () => {
const selectedAddonIds = selectedAddons.map(({ id }) => id);
const { nextSteps } = await runAddCommand(options$1, selectedAddonIds);
if (nextSteps.length > 0) Ke(nextSteps.join("\n"), "Next steps", { format: (line) => line });
});
});
async function runAddCommand(options$1, selectedAddonIds) {
const selectedAddons = selectedAddonIds.map((id) => ({
type: "official",
addon: getAddonDetails(id)
}));
const official = {};
const community = {};
for (const addonOption of addonOptions) {
const addonId = addonOption.id;
const specifiedOptions = options$1.addons[addonId];
if (!specifiedOptions) continue;
const details$1 = getAddonDetails(addonId);
if (!selectedAddons.find((d) => d.addon === details$1)) selectedAddons.push({
type: "official",
addon: details$1
});
official[addonId] ??= {};
const optionEntries = Object.entries(details$1.options);
for (const option of specifiedOptions) {
let [optionId, optionValue] = option.split(":", 2);
const optionEntry = optionEntries.find(([id, question$1]) => id === optionId || question$1.group === optionId);
if (!optionEntry) {
const { choices } = getOptionChoices(details$1);
throw new Error(`Invalid '${addonId}' option: '${option}'\nAvailable options: ${choices.join(", ")}`);
}
const [questionId, question] = optionEntry;
if (question.type === "multiselect" && optionValue === "none") optionValue = "";
let existingOption = official[addonId][questionId];
if (existingOption !== undefined) {
if (typeof existingOption === "boolean") existingOption = existingOption ? "yes" : "no";
throw new Error(`Conflicting '${addonId}' option: '${option}' conflicts with '${questionId}:${existingOption}'`);
}
if (question.type === "boolean") official[addonId][questionId] = optionValue === "yes";
else if (question.type === "number") official[addonId][questionId] = Number(optionValue);
else official[addonId][questionId] = optionValue;
}
for (const [id, question] of Object.entries(details$1.options)) if (question.condition?.(official[addonId]) !== false) official[addonId][id] ??= question.default;
else if (official[addonId][id] !== undefined) throw new Error(`Incompatible '${addonId}' option specified: '${official[addonId][id]}'`);
}
if (options$1.community === true) {
const communityAddons = await Promise.all(communityAddonIds.map(async (id) => await getCommunityAddon(id)));
const promptOptions = communityAddons.map((addon) => ({
value: addon.id,
label: addon.id,
hint: "https://www.npmjs.com/package/" + addon.id
}));
const selected = await Ue({
message: "Which community tools would you like to add to your project?",
options: promptOptions,
required: false
});
if (Vu(selected)) {
De("Operation cancelled.");
process.exit(1);
} else if (selected.length === 0) {
De("No add-ons selected. Exiting.");
process.exit(1);
}
options$1.community = selected;
}
if (Array.isArray(options$1.community) && options$1.community.length > 0) {
const addons = options$1.community.map((id) => {
const hasDirective = Object.values(Directive).some((directive) => id.startsWith(directive));
if (hasDirective) return id;
const validAddon = communityAddonIds.includes(id);
if (!validAddon) throw new Error(`Invalid community add-on specified: '${id}'\nAvailable options: ${communityAddonIds.join(", ")}`);
return id;
});
const { start, stop } = J();
try {
start("Resolving community add-on packages");
const pkgs = await Promise.all(addons.map(async (id) => {
return await getPackageJSON({
cwd: options$1.cwd,
packageName: id
});
}));
stop("Resolved community add-on packages");
T.warn("The Svelte maintainers have not reviewed community add-ons for malicious code. Use at your discretion.");
const paddingName = getPadding(pkgs.map(({ pkg }) => pkg.name));
const paddingVersion = getPadding(pkgs.map(({ pkg }) => `(v${pkg.version})`));
const packageInfos = pkgs.map(({ pkg, repo: _repo }) => {
const name$1 = import_picocolors$2.default.yellowBright(pkg.name.padEnd(paddingName));
const version$1 = import_picocolors$2.default.dim(`(v${pkg.version})`.padEnd(paddingVersion));
const repo = import_picocolors$2.default.dim(`(${_repo})`);
return `${name$1} ${version$1} ${repo}`;
});
T.message(packageInfos.join("\n"));
const confirm = await ke({ message: "Would you like to continue?" });
if (confirm !== true) {
De("Operation cancelled.");
process.exit(1);
}
start("Downloading community add-on packages");
const details$1 = await Promise.all(pkgs.map(async (opts) => downloadPackage(opts)));
for (const addon of details$1) {
const id = addon.id;
community[id] ??= {};
communityDetails.push(addon);
selectedAddons.push({
type: "community",
addon
});
}
stop("Downloaded community add-on packages");
} catch (err) {
stop("Failed to resolve community add-on packages", 1);
throw err;
}
}
let workspace = await createWorkspace({ cwd: options$1.cwd });
const setups = selectedAddons.length ? selectedAddons.map(({ addon }) => addon) : officialAddons;
const addonSetupResults = setupAddons(setups, workspace);
if (selectedAddons.length === 0) {
const addonOptions$1 = officialAddons.filter(({ id }) => addonSetupResults[id].unsupported.length === 0).map(({ id, homepage: homepage$1, shortDescription }) => ({
label: id,
value: id,
hint: `${shortDescription} - ${homepage$1}`
}));
const selected = await Ue({
message: `What would you like to add to your project? ${import_picocolors$2.default.dim("(use arrow keys / space bar)")}`,
options: addonOptions$1,
required: false
});
if (Vu(selected)) {
De("Operation cancelled.");
process.exit(1);
}
for (const id of selected) {
const addon = officialAddons.find((addon$1) => addon$1.id === id);
selectedAddons.push({
type: "official",
addon
});
}
}
for (const { addon } of selectedAddons) {
workspace = await createWorkspace(workspace);
const setupResult = addonSetupResults[addon.id];
const missingDependencies = setupResult.dependsOn.filter((depId) => !selectedAddons.some((a) => a.addon.id === depId));
for (const depId of missingDependencies) {
const dependency = officialAddons.find((a) => a.id === depId);
if (!dependency) throw new Error(`'${addon.id}' depends on an invalid add-on: '${depId}'`);
const install = await ke({ message: `The ${import_picocolors$2.default.bold(import_picocolors$2.default.cyan(addon.id))} add-on requires ${import_picocolors$2.default.bold(import_picocolors$2.default.cyan(depId))} to also be setup. ${import_picocolors$2.default.green("Include it?")}` });
if (install !== true) {
De("Operation cancelled.");
process.exit(1);
}
selectedAddons.push({
type: "official",
addon: dependency
});
}
}
if (options$1.preconditions && selectedAddons.length > 0) {
const addons = selectedAddons.map(({ addon }) => addon);
const { preconditions } = getGlobalPreconditions(options$1.cwd, addons, addonSetupResults);
const fails = [];
for (const condition of preconditions) {
const { message, success } = await condition.run();
if (!success) fails.push({
name: condition.name,
message
});
}
if (fails.length > 0) {
const message = fails.map(({ name: name$1, message: message$1 }) => import_picocolors$2.default.yellow(`${name$1} (${message$1})`)).join("\n- ");
Ke(`- ${message}`, "Preconditions not met", { format: (line) => line });
const force = await ke({
message: "Preconditions failed. Do you wish to continue?",
initialValue: false
});
if (Vu(force) || !force) {
De("Operation cancelled.");
process.exit(1);
}
}
}
for (const { addon, type: type$1 } of selectedAddons) {
const addonId = addon.id;
const questionPrefix = selectedAddons.length > 1 ? `${addon.id}: ` : "";
let values = {};
if (type$1 === "official") {
official[addonId] ??= {};
values = official[addonId];
}
if (type$1 === "community") {
community[addonId] ??= {};
values = community[addonId];
}
for (const [questionId, question] of Object.entries(addon.options)) {
const shouldAsk = question.condition?.(values);
if (shouldAsk === false || values[questionId] !== undefined) continue;
let answer;
const message = questionPrefix + question.question;
if (question.type === "boolean") answer = await ke({
message,
initialValue: question.default
});
if (question.type === "select") answer = await ze({
message,
initialValue: question.default,
options: question.options
});
if (question.type === "multiselect") answer = await Ue({
message,
initialValues: question.default,
required: question.required,
options: question.options
});
if (question.type === "string" || question.type === "number") {
answer = await et({
message,
initialValue: question.default.toString(),
placeholder: question.placeholder,
validate: question.validate
});
if (question.type === "number") answer = Number(answer);
}
if (Vu(answer)) {
De("Operation cancelled.");
process.exit(1);
}
values[questionId] = answer;
}
}
if (selectedAddons.length === 0) return {
packageManager: null,
nextSteps: []
};
const officialDetails = Object.keys(official).map((id) => getAddonDetails(id));
const commDetails = Object.keys(community).map((id) => communityDetails.find((a) => a.id === id));
const details = officialDetails.concat(commDetails);
const addonMap = Object.assign({}, ...details.map((a) => ({ [a.id]: a })));
const { filesToFormat, pnpmBuildDependencies: addonPnpmBuildDependencies } = await applyAddons({
workspace,
addonSetupResults,
addons: addonMap,
options: official
});
T.success("Successfully setup add-ons");
let packageManager;
if (options$1.install) {
packageManager = options$1.install === true ? await packageManagerPrompt(options$1.cwd) : options$1.install;
if (packageManager) {
workspace.packageManager = packageManager;
addPnpmBuildDependencies(workspace.cwd, packageManager, ["esbuild", ...addonPnpmBuildDependencies]);
await installDependencies(packageManager, options$1.cwd);
}
}
workspace = await createWorkspace(workspace);
if (filesToFormat.length > 0 && packageManager && !!workspace.dependencyVersion("prettier")) {
const { start, stop } = J();
start("Formatting modified files");
try {
await formatFiles({
packageManager,
cwd: options$1.cwd,
paths: filesToFormat
});
stop("Successfully formatted modified files");
} catch (e) {
stop("Failed to format files");
if (e instanceof Error) T.error(e.message);
}
}
const highlighter = getHighlighter();
const nextSteps = selectedAddons.map(({ addon }) => {
if (!addon.nextSteps) return;
let addonMessage = `${import_picocolors$2.default.green(addon.id)}:\n`;
const options$2 = official[addon.id];
const addonNextSteps = addon.nextSteps({
...workspace,
options: options$2,
highlighter
});
addonMessage += ` - ${addonNextSteps.join("\n - ")}`;
return addonMessage;
}).filter((msg) => msg !== undefined);
return {
nextSteps,
packageManager
};
}
/**
* Dedupes and transforms aliases into their respective addon id
*/
function transformAliases(addons) {
const set = new Map();
for (const addon of addons) if (aliases.includes(addon.id)) {
const officialAddon = officialAddons.find((a) => a.alias === addon.id);
set.set(officialAddon.id, {
id: officialAddon.id,
options: addon.options
});
} else set.set(addon.id, addon);
return Array.from(set.values());
}
function getAddonOptionFlags() {
const options$1 = [];
for (const addon of officialAddons) {
const id = addon.id;
const details = getAddonDetails(id);
if (Object.values(details.options).length === 0) continue;
const { defaults, groups } = getOptionChoices(details);
const choices = Object.entries(groups).map(([group, choices$1]) => `${import_picocolors$2.default.dim(`${group}:`)} ${choices$1.join(", ")}`).join("\n");
const preset = defaults.join(", ") || "none";
options$1.push({
id,
choices,
preset
});
}
return options$1;
}
function getOptionChoices(details) {
const choices = [];
const defaults = [];
const groups = {};
const options$1 = {};
for (const [id, question] of Object.entries(details.options)) {
let values = [];
const applyDefault = question.condition?.(options$1) !== false;
if (question.type === "boolean") {
values = ["yes", `no`];
if (applyDefault) {
options$1[id] = question.default;
defaults.push(question.default ? values[0] : values[1]);
}
}
if (question.type === "select") {
values = question.options.map((o) => o.value);
if (applyDefault) {
options$1[id] = question.default;
defaults.push(question.default);
}
}
if (question.type === "multiselect") {
values = question.options.map((o) => o.value);
if (applyDefault) {
options$1[id] = question.default;
defaults.push(...question.default);
}
}
if (question.type === "string" || question.type === "number") {
values = ["<user-input>"];
if (applyDefault) {
options$1[id] = question.default;
defaults.push(question.default.toString());
}
}
choices.push(...values);
const groupId = question.group ?? id;
groups[groupId] ??= [];
groups[groupId].push(...values);
}
return {
choices,
defaults,
groups
};
}
//#endregion
//#region packages/cli/commands/create.ts
var import_picocolors$1 = __toESM(require_picocolors(), 1);
const langs = ["ts", "jsdoc"];
const langMap = {
ts: "typescript",
jsdoc: "checkjs",
false: "none"
};
const templateChoices = templates.map((t) => t.name);
const langOption = new Option("--types <lang>", "add type checking").choices(langs);
const templateOption = new Option("--template <type>", "template to scaffold").choices(templateChoices);
const ProjectPathSchema = optional(string());
const OptionsSchema = strictObject({
types: pipe(optional(union([picklist(langs), boolean()])), transform((lang) => langMap[String(lang)])),
addOns: boolean(),
install: union([boolean(), picklist(AGENT_NAMES)]),
template: optional(picklist(templateChoices))
});
const create$1 = new Command("create").description("scaffolds a new SvelteKit project").argument("[path]", "where the project will be created").addOption(templateOption).addOption(langOption).option("--no-types").option("--no-add-ons", "skips interactive add-on installer").option("--no-install", "skip installing dependencies").addOption(installOption).configureHelp(helpConfig).action((projectPath, opts) => {
const cwd = parse(ProjectPathSchema, projectPath);
const options$1 = parse(OptionsSchema, opts);
runCommand(async () => {
const { directory, addOnNextSteps, packageManager } = await createProject(cwd, options$1);
const highlight = (str) => import_picocolors$1.default.bold(import_picocolors$1.default.cyan(str));
let i = 1;
const initialSteps = ["📁 Project steps", ""];
const relative = path.relative(process.cwd(), directory);
const pm = packageManager ?? (await detect({ cwd: directory }))?.name ?? getUserAgent() ?? "npm";
if (relative !== "") {
const pathHasSpaces = relative.includes(" ");
initialSteps.push(` ${i++}: ${highlight(`cd ${pathHasSpaces ? `"${relative}"` : relative}`)}`);
}
if (!packageManager) {
const { args: args$1, command: command$1 } = resolveCommand(pm, "install", []);
initialSteps.push(` ${i++}: ${highlight(`${command$1} ${args$1.join(" ")}`)}`);
}
const { args, command } = resolveCommand(pm, "run", ["dev", "--open"]);
const pmRunCmd = `${command} ${args.join(" ")}`;
const steps = [
...initialSteps,
` ${i++}: ${highlight(pmRunCmd)}`,
"",
`To close the dev server, hit ${highlight("Ctrl-C")}`
];
if (addOnNextSteps.length > 0) {
steps.push("", "🧩 Add-on steps", "");
for (const step of addOnNextSteps) {
const indented = step.replaceAll(" -", " -");
steps.push(` ${indented}`);
}
}
steps.push("", `Stuck? Visit us at ${import_picocolors$1.default.cyan("https://svelte.dev/chat")}`);
Ke(steps.join("\n"), "What's next?", { format: (line) => line });
});
});
async function createProject(cwd, options$1) {
const { directory, template, language } = await We({
directory: () => {
if (cwd) return Promise.resolve(path.resolve(cwd));
const defaultPath = "./";
return et({
message: "Where would you like your project to be created?",
placeholder: ` (hit Enter to use '${defaultPath}')`,
defaultValue: defaultPath
});
},
force: async ({ results: { directory: directory$1 } }) => {
if (fs.existsSync(directory$1) && fs.readdirSync(directory$1).filter((x) => !x.startsWith(".git")).length > 0) {
const force = await ke({
message: "Directory not empty. Continue?",
initialValue: false
});
if (Vu(force) || !force) {
De("Exiting.");
process.exit(0);
}
}
},
template: () => {
if (options$1.template) return Promise.resolve(options$1.template);
return ze({
message: "Which template would you like?",
initialValue: "minimal",
options: templates.map((t) => ({
label: t.title,
value: t.name,
hint: t.description
}))
});
},
language: () => {
if (options$1.types) return Promise.resolve(options$1.types);
return ze({
message: "Add type checking with TypeScript?",
initialValue: "typescript",
options: [
{
label: "Yes, using TypeScript syntax",
value: "typescript"
},
{
label: "Yes, using JavaScript with JSDoc comments",
value: "checkjs"
},
{
label: "No",
value: "none"
}
]
});
}
}, { onCancel: () => {
De("Operation cancelled.");
process.exit(0);
} });
const projectPath = path.resolve(directory);
create(projectPath, {
name: path.basename(projectPath),
template,
types: language
});
T.success("Project created");
let packageManager;
let addOnNextSteps = [];
const installDeps = async (install) => {
packageManager = install === true ? await packageManagerPrompt(projectPath) : install;
addPnpmBuildDependencies(projectPath, packageManager, ["esbuild"]);
if (packageManager) await installDependencies(packageManager, projectPath);
};
if (options$1.addOns) {
const { nextSteps, packageManager: pm } = await runAddCommand({
cwd: projectPath,
install: options$1.install,
preconditions: false,
community: [],
addons: {}
}, []);
packageManager = pm;
addOnNextSteps = nextSteps;
} else if (options$1.install) await installDeps(options$1.install);
if (packageManager === null && options$1.install) await installDeps(options$1.install);
return {
directory: projectPath,
addOnNextSteps,
packageManager
};
}
//#endregion
//#region packages/cli/commands/migrate.ts
const migrate = new Command("migrate").description("a CLI for migrating Svelte(Kit) codebases").argument("<migration>", "migration to run").option("-C, --cwd <path>", "path to working directory", process.cwd()).configureHelp({ formatHelp() {
runMigrate(process.cwd(), ["--help"]);
return "";
} }).action((migration, options$1) => {
runMigrate(options$1.cwd, [migration]);
});
function runMigrate(cwd, args) {
const pm = getUserAgent() ?? "npm";
try {
const cmdArgs = ["svelte-migrate@latest", ...args];
if (pm === "npm") cmdArgs.unshift("--yes");
const cmd = resolveCommand(pm, "execute", cmdArgs);
execSync(`${cmd.command} ${cmd.args.join(" ")}`, {
stdio: "inherit",
cwd
});
} catch (error) {
forwardExitCode(error);
}
}
//#endregion
//#region packages/cli/commands/check.ts
var import_picocolors = __toESM(require_picocolors(), 1);
const check = new Command("check").description("a CLI for checking your Svelte code").allowUnknownOption(true).allowExcessArguments(true).option("-C, --cwd <path>", "path to working directory", process.cwd()).configureHelp({ formatHelp() {
runCheck(process.cwd(), ["--help"]);
return "";
} }).action((options$1, check$1) => {
const cwd = options$1.cwd;
const args = check$1.args;
runCheck(cwd, args);
});
function runCheck(cwd, args) {
const pm = getUserAgent() ?? "npm";
const resolved = from(cwd, "svelte-check", true);
if (!resolved) {
const cmd = resolveCommand(pm, "add", ["-D", "svelte-check"]);
console.error(`'svelte-check' is not installed locally. Install it with: ${import_picocolors.default.bold(`${cmd.command} ${cmd.args.join(" ")}`)}`);
process.exit(1);
}
try {
const cmd = resolveCommand(pm, "execute-local", ["svelte-check", ...args]);
execSync(`${cmd.command} ${cmd.args.join(" ")}`, {
stdio: "inherit",
cwd
});
} catch (error) {
forwardExitCode(error);
}
}
//#endregion
//#region packages/cli/bin.ts
program.name(package_default.name).version(package_default.version, "-v, --version").configureHelp(helpConfig);
program.addCommand(create$1).addCommand(add).addCommand(migrate).addCommand(check);
program.parse();
//#endregion