@spicemod/creator 0.0.34 → 0.0.36
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/dist/bin.mjs +170 -57
- package/dist/templates/custom-app/shared/tsconfig.json +2 -30
- package/dist/templates/extension/shared/tsconfig.json +2 -30
- package/dist/templates/liveReload.js +80 -1
- package/dist/templates/theme/shared/tsconfig.json +2 -30
- package/package.json +3 -3
- package/templates/custom-app/shared/tsconfig.json +2 -30
- package/templates/extension/shared/tsconfig.json +2 -30
- package/templates/liveReload.js +80 -1
- package/templates/theme/shared/tsconfig.json +2 -30
package/dist/bin.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { Command, Option } from "commander";
|
|
3
3
|
import * as v from "valibot";
|
|
4
4
|
import path, { basename, dirname, extname, join, relative, resolve } from "node:path";
|
|
5
|
-
import { build, context, transform } from "esbuild";
|
|
5
|
+
import { build, context, formatMessages, transform } from "esbuild";
|
|
6
6
|
import fs, { createReadStream, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
7
7
|
import { watchConfig } from "c12";
|
|
8
8
|
import { globSync } from "tinyglobby";
|
|
@@ -28,6 +28,7 @@ import { lookup } from "node:dns/promises";
|
|
|
28
28
|
import { mkdir, writeFile as writeFile$1 } from "fs/promises";
|
|
29
29
|
import { dirname as dirname$1, join as join$1 } from "path";
|
|
30
30
|
import { createServer } from "node:http";
|
|
31
|
+
import { createServer as createServer$1 } from "node:net";
|
|
31
32
|
import { WebSocket, WebSocketServer } from "ws";
|
|
32
33
|
|
|
33
34
|
//#region src/utils/replace.ts
|
|
@@ -145,7 +146,7 @@ const injectCSSFilePath = dist("templates/injectCSS.js", import.meta.url);
|
|
|
145
146
|
//#endregion
|
|
146
147
|
//#region package.json
|
|
147
148
|
var name = "@spicemod/creator";
|
|
148
|
-
var version = "0.0.
|
|
149
|
+
var version = "0.0.36";
|
|
149
150
|
|
|
150
151
|
//#endregion
|
|
151
152
|
//#region src/utils/common.ts
|
|
@@ -762,6 +763,59 @@ function formatBuildSummary(files) {
|
|
|
762
763
|
}
|
|
763
764
|
}
|
|
764
765
|
|
|
766
|
+
//#endregion
|
|
767
|
+
//#region src/esbuild/plugins/buildErrorReporter.ts
|
|
768
|
+
function formatErrorForFrontend(errors) {
|
|
769
|
+
return errors.map((err) => {
|
|
770
|
+
return {
|
|
771
|
+
id: err.id,
|
|
772
|
+
text: err.text,
|
|
773
|
+
location: err.location ? {
|
|
774
|
+
...err.location,
|
|
775
|
+
lineText: err.location.lineText
|
|
776
|
+
} : null,
|
|
777
|
+
notes: err.notes,
|
|
778
|
+
pluginName: err.pluginName,
|
|
779
|
+
detail: err.detail
|
|
780
|
+
};
|
|
781
|
+
});
|
|
782
|
+
}
|
|
783
|
+
async function formatErrorForConsole(errors) {
|
|
784
|
+
return (await formatMessages(errors, {
|
|
785
|
+
kind: "error",
|
|
786
|
+
color: true
|
|
787
|
+
})).join("\n");
|
|
788
|
+
}
|
|
789
|
+
function buildErrorReporter({ server }) {
|
|
790
|
+
return {
|
|
791
|
+
name: "spice_internal__build-error-reporter",
|
|
792
|
+
setup(build) {
|
|
793
|
+
build.onEnd(async (result) => {
|
|
794
|
+
const errors = result.errors;
|
|
795
|
+
const warnings = result.warnings;
|
|
796
|
+
if (errors.length === 0) {
|
|
797
|
+
server?.broadcast({
|
|
798
|
+
type: "build-success",
|
|
799
|
+
errors: [],
|
|
800
|
+
warnings
|
|
801
|
+
});
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
804
|
+
const formattedErrors = formatErrorForFrontend(errors);
|
|
805
|
+
logger$2.debug(pc.cyan("[buildErrorReporter] Broadcasting error to WebSocket clients"));
|
|
806
|
+
server?.broadcast({
|
|
807
|
+
type: "build-error",
|
|
808
|
+
errors: formattedErrors,
|
|
809
|
+
warnings
|
|
810
|
+
});
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
};
|
|
814
|
+
}
|
|
815
|
+
async function getFormattedErrors(errors) {
|
|
816
|
+
return formatErrorForConsole(errors);
|
|
817
|
+
}
|
|
818
|
+
|
|
765
819
|
//#endregion
|
|
766
820
|
//#region src/esbuild/plugins/buildLogger.ts
|
|
767
821
|
const buildLogger = ({ cache }) => ({
|
|
@@ -776,17 +830,22 @@ const buildLogger = ({ cache }) => ({
|
|
|
776
830
|
logger$2.info(pc.dim("Rebuilding..."));
|
|
777
831
|
} else logger$2.info(pc.dim("Build started..."));
|
|
778
832
|
});
|
|
779
|
-
build.onEnd((result) => {
|
|
833
|
+
build.onEnd(async (result) => {
|
|
780
834
|
if (result.errors.length > 0) {
|
|
781
|
-
|
|
835
|
+
const formattedErrors = await getFormattedErrors(result.errors);
|
|
836
|
+
logger$2.error(pc.red("Build failed.\n") + formattedErrors);
|
|
782
837
|
return;
|
|
783
838
|
}
|
|
784
|
-
|
|
839
|
+
let moduleCount = 0;
|
|
840
|
+
try {
|
|
841
|
+
if (result.metafile) {
|
|
842
|
+
result.metafile.outputs;
|
|
843
|
+
moduleCount = Object.keys(result.metafile.inputs).length;
|
|
844
|
+
const details = formatBuildSummary(cache.files);
|
|
845
|
+
logger$2.info(details);
|
|
846
|
+
}
|
|
847
|
+
} catch {}
|
|
785
848
|
logger$2.info(`${CHECK} ${moduleCount} modules transformed.`);
|
|
786
|
-
if (result.metafile) {
|
|
787
|
-
const details = formatBuildSummary(cache.files);
|
|
788
|
-
logger$2.info(details);
|
|
789
|
-
}
|
|
790
849
|
logger$2.info(pc.green(`${CHECK} built in ${getTime(buildStartTime)}.`));
|
|
791
850
|
isFirstBuild = false;
|
|
792
851
|
});
|
|
@@ -797,33 +856,6 @@ function getTime(start) {
|
|
|
797
856
|
return ms > 1e3 ? `${(ms / 1e3).toFixed(2)}s` : `${Math.round(ms)}ms`;
|
|
798
857
|
}
|
|
799
858
|
|
|
800
|
-
//#endregion
|
|
801
|
-
//#region src/esbuild/plugins/buildErrorReporter.ts
|
|
802
|
-
function buildErrorReporter({ server }) {
|
|
803
|
-
return {
|
|
804
|
-
name: "spice_internal__build-error-reporter",
|
|
805
|
-
setup(build) {
|
|
806
|
-
build.onEnd(async (result) => {
|
|
807
|
-
const errors = result.errors;
|
|
808
|
-
const warnings = result.warnings;
|
|
809
|
-
if (errors.length === 0) {
|
|
810
|
-
server?.broadcast({
|
|
811
|
-
type: "build-success",
|
|
812
|
-
errors: [],
|
|
813
|
-
warnings
|
|
814
|
-
});
|
|
815
|
-
return;
|
|
816
|
-
}
|
|
817
|
-
server?.broadcast({
|
|
818
|
-
type: "build-error",
|
|
819
|
-
errors,
|
|
820
|
-
warnings
|
|
821
|
-
});
|
|
822
|
-
});
|
|
823
|
-
}
|
|
824
|
-
};
|
|
825
|
-
}
|
|
826
|
-
|
|
827
859
|
//#endregion
|
|
828
860
|
//#region src/esbuild/plugins/clean.ts
|
|
829
861
|
const clean = (cache, logger = createLogger("plugin:clean")) => ({
|
|
@@ -1467,7 +1499,8 @@ function createPackageJSON(options) {
|
|
|
1467
1499
|
sc: "spicetify-creator",
|
|
1468
1500
|
dev: "spicetify-creator dev",
|
|
1469
1501
|
build: "spicetify-creator build",
|
|
1470
|
-
"update-types": "spicetify-creator update-types"
|
|
1502
|
+
"update-types": "spicetify-creator update-types",
|
|
1503
|
+
"clean-spice": "spicetify-creator clean-spicetify --all"
|
|
1471
1504
|
},
|
|
1472
1505
|
dependencies: {},
|
|
1473
1506
|
devDependencies: { "@spicetify/creator": "latest" }
|
|
@@ -2020,7 +2053,9 @@ const root = () => `<!DOCTYPE html>
|
|
|
2020
2053
|
|
|
2021
2054
|
//#endregion
|
|
2022
2055
|
//#region src/dev/server/index.ts
|
|
2023
|
-
|
|
2056
|
+
function getWSPath(port) {
|
|
2057
|
+
return `/spicetify-creator-${port}`;
|
|
2058
|
+
}
|
|
2024
2059
|
async function createHmrServer(config, logger = createLogger("hmrServer")) {
|
|
2025
2060
|
const { port = DEFAULT_PORT, serveDir = outDir } = config;
|
|
2026
2061
|
let isRunning = false;
|
|
@@ -2036,6 +2071,30 @@ async function createHmrServer(config, logger = createLogger("hmrServer")) {
|
|
|
2036
2071
|
".svg": "image/svg+xml",
|
|
2037
2072
|
".ico": "image/x-icon"
|
|
2038
2073
|
};
|
|
2074
|
+
async function isPortAvailable(port) {
|
|
2075
|
+
return new Promise((resolve) => {
|
|
2076
|
+
const server = createServer$1();
|
|
2077
|
+
server.once("error", () => resolve(false));
|
|
2078
|
+
server.once("listening", () => {
|
|
2079
|
+
server.close(() => resolve(true));
|
|
2080
|
+
});
|
|
2081
|
+
server.listen(port);
|
|
2082
|
+
});
|
|
2083
|
+
}
|
|
2084
|
+
let actualPort = port;
|
|
2085
|
+
const portsToCheck = Array.from({ length: 10 }, (_, i) => port + i);
|
|
2086
|
+
const results = await Promise.all(portsToCheck.map(async (p) => ({
|
|
2087
|
+
port: p,
|
|
2088
|
+
available: await isPortAvailable(p)
|
|
2089
|
+
})));
|
|
2090
|
+
for (const result of results) {
|
|
2091
|
+
if (result.available) {
|
|
2092
|
+
actualPort = result.port;
|
|
2093
|
+
break;
|
|
2094
|
+
}
|
|
2095
|
+
logger.debug(`Port ${result.port} is in use, trying ${result.port + 1}...`);
|
|
2096
|
+
}
|
|
2097
|
+
if (actualPort !== port) logger.info(pc.yellow(`Port ${port} is in use, using port ${actualPort} instead`));
|
|
2039
2098
|
const httpServer = createServer((req, res) => {
|
|
2040
2099
|
const corsHeaders = {
|
|
2041
2100
|
"Access-Control-Allow-Origin": "*",
|
|
@@ -2076,7 +2135,7 @@ async function createHmrServer(config, logger = createLogger("hmrServer")) {
|
|
|
2076
2135
|
const clients = /* @__PURE__ */ new Set();
|
|
2077
2136
|
httpServer.on("upgrade", (req, socket, head) => {
|
|
2078
2137
|
const { url } = req;
|
|
2079
|
-
if (!url?.startsWith(
|
|
2138
|
+
if (!url?.startsWith(getWSPath(actualPort))) {
|
|
2080
2139
|
socket.destroy();
|
|
2081
2140
|
return;
|
|
2082
2141
|
}
|
|
@@ -2097,8 +2156,8 @@ async function createHmrServer(config, logger = createLogger("hmrServer")) {
|
|
|
2097
2156
|
}
|
|
2098
2157
|
return {
|
|
2099
2158
|
start: async () => new Promise((resolve) => {
|
|
2100
|
-
httpServer.listen(
|
|
2101
|
-
logger.debug(`${pc.bold("HTTP Server Started at")}: ${pc.cyan(`http://localhost:${
|
|
2159
|
+
httpServer.listen(actualPort, () => {
|
|
2160
|
+
logger.debug(`${pc.bold("HTTP Server Started at")}: ${pc.cyan(`http://localhost:${actualPort}/`)}`);
|
|
2102
2161
|
isRunning = true;
|
|
2103
2162
|
resolve();
|
|
2104
2163
|
});
|
|
@@ -2120,24 +2179,24 @@ async function createHmrServer(config, logger = createLogger("hmrServer")) {
|
|
|
2120
2179
|
}),
|
|
2121
2180
|
broadcast,
|
|
2122
2181
|
get port() {
|
|
2123
|
-
return
|
|
2182
|
+
return actualPort;
|
|
2124
2183
|
},
|
|
2125
2184
|
get isRunning() {
|
|
2126
2185
|
return isRunning;
|
|
2127
2186
|
},
|
|
2128
2187
|
get link() {
|
|
2129
|
-
return `http://localhost:${
|
|
2188
|
+
return `http://localhost:${actualPort}`;
|
|
2130
2189
|
},
|
|
2131
2190
|
get wsLink() {
|
|
2132
|
-
return `ws://localhost:${
|
|
2191
|
+
return `ws://localhost:${actualPort}${getWSPath(actualPort)}`;
|
|
2133
2192
|
}
|
|
2134
2193
|
};
|
|
2135
2194
|
}
|
|
2136
2195
|
|
|
2137
2196
|
//#endregion
|
|
2138
2197
|
//#region src/utils/hmr.ts
|
|
2139
|
-
const injectHMRExtension = async (rootLink, wsLink, outFiles, config) => {
|
|
2140
|
-
const extName = `sc-live-reload-helper.js`;
|
|
2198
|
+
const injectHMRExtension = async (rootLink, wsLink, outFiles, config, port) => {
|
|
2199
|
+
const extName = port === 54321 ? `sc-live-reload-helper.js` : `sc-live-reload-helper-${port}.js`;
|
|
2141
2200
|
const spiceConfig = await getSpicetifyConfig();
|
|
2142
2201
|
const cleanup = () => {
|
|
2143
2202
|
logger$2.info(`Removing Live reload extension...`);
|
|
@@ -2168,7 +2227,7 @@ const injectHMRExtension = async (rootLink, wsLink, outFiles, config) => {
|
|
|
2168
2227
|
_HOT_RELOAD_LINK: JSON.stringify(wsLink),
|
|
2169
2228
|
_JS_PATH: JSON.stringify(`/files/${outFiles.js}`),
|
|
2170
2229
|
_CSS_PATH: JSON.stringify(outFiles.css ? `/files/${outFiles.css}` : `/files/app.css`),
|
|
2171
|
-
_REMOVE_CMD: JSON.stringify(`spicetify config extensions
|
|
2230
|
+
_REMOVE_CMD: JSON.stringify(`spicetify config extensions ${extName}- && spicetify apply`),
|
|
2172
2231
|
_CSS_ID: JSON.stringify(config.template === "extension" && config.cssId ? config.cssId : "sc-injected-css")
|
|
2173
2232
|
}
|
|
2174
2233
|
});
|
|
@@ -2186,19 +2245,20 @@ const injectHMRExtension = async (rootLink, wsLink, outFiles, config) => {
|
|
|
2186
2245
|
logger$2.error(pc.red(`${CROSS} Failed to inject HMR helper: ${err instanceof Error ? err.message : String(err)}`));
|
|
2187
2246
|
}
|
|
2188
2247
|
};
|
|
2189
|
-
const injectHMRCustomApp = async (rootLink, wsLink, outFiles, config) => {
|
|
2248
|
+
const injectHMRCustomApp = async (rootLink, wsLink, outFiles, config, port) => {
|
|
2190
2249
|
if (config.template !== "custom-app") throw new Error("only supports custom-app templates");
|
|
2191
2250
|
const identifier = getEnName(config.name);
|
|
2192
2251
|
const spiceConfig = await getSpicetifyConfig();
|
|
2193
|
-
const customAppId = urlSlugify(identifier)
|
|
2194
|
-
|
|
2195
|
-
"config",
|
|
2196
|
-
"extensions",
|
|
2197
|
-
"sc-live-reload-helper.js-"
|
|
2198
|
-
]);
|
|
2252
|
+
const customAppId = port === 54321 ? urlSlugify(identifier) : `${urlSlugify(identifier)}-${port}`;
|
|
2253
|
+
const extName = port === 54321 ? `sc-live-reload-helper.js` : `sc-live-reload-helper-${port}.js`;
|
|
2199
2254
|
const cleanup = () => {
|
|
2200
2255
|
logger$2.info(`Removing Live reload custom-app...`);
|
|
2201
2256
|
try {
|
|
2257
|
+
runSpice([
|
|
2258
|
+
"config",
|
|
2259
|
+
"extensions",
|
|
2260
|
+
`${extName}-`
|
|
2261
|
+
]);
|
|
2202
2262
|
runSpice([
|
|
2203
2263
|
"config",
|
|
2204
2264
|
"custom_apps",
|
|
@@ -2285,8 +2345,8 @@ async function dev$1(options) {
|
|
|
2285
2345
|
});
|
|
2286
2346
|
await server.start();
|
|
2287
2347
|
const outFiles = getOutFiles(config, true);
|
|
2288
|
-
if (config.template === "custom-app") await injectHMRCustomApp(server.link, server.wsLink, outFiles, config);
|
|
2289
|
-
else await injectHMRExtension(server.link, server.wsLink, outFiles, config);
|
|
2348
|
+
if (config.template === "custom-app") await injectHMRCustomApp(server.link, server.wsLink, outFiles, config, server.port);
|
|
2349
|
+
else await injectHMRExtension(server.link, server.wsLink, outFiles, config, server.port);
|
|
2290
2350
|
ctx = await context(getJSDevOptions(config, {
|
|
2291
2351
|
...options,
|
|
2292
2352
|
outFiles,
|
|
@@ -2350,6 +2410,59 @@ const dev = new Command("dev").description("Develop your spicetify project").opt
|
|
|
2350
2410
|
await dev$1(safeParse(CLIOptionsSchema, opts));
|
|
2351
2411
|
});
|
|
2352
2412
|
|
|
2413
|
+
//#endregion
|
|
2414
|
+
//#region src/commands/clean-spicetify.ts
|
|
2415
|
+
function getHmrExtensions(port) {
|
|
2416
|
+
return port === 54321 ? "sc-live-reload-helper.js" : `sc-live-reload-helper-${port}.js`;
|
|
2417
|
+
}
|
|
2418
|
+
function getHmrCustomAppId(name, port) {
|
|
2419
|
+
const slugified = urlSlugify(getEnName(name));
|
|
2420
|
+
return port === 54321 ? slugified : `${slugified}-${port}`;
|
|
2421
|
+
}
|
|
2422
|
+
const cleanSpicetify = new Command("clean-spicetify").description("Remove HMR extensions and custom apps added by Spicetify Creator from Spicetify").option("-a, --all", "Clean all possible HMR artifacts").action(async (opts) => {
|
|
2423
|
+
const all = opts.all ?? false;
|
|
2424
|
+
try {
|
|
2425
|
+
const spiceConfig = await getSpicetifyConfig();
|
|
2426
|
+
const extensions = new Set(spiceConfig.AdditionalOptions.extensions);
|
|
2427
|
+
const customApps = new Set(spiceConfig.AdditionalOptions.custom_apps.split("|").filter(Boolean));
|
|
2428
|
+
const toRemove = [];
|
|
2429
|
+
if (all) for (let port = 54321; port <= 54330; port++) {
|
|
2430
|
+
const extName = getHmrExtensions(port);
|
|
2431
|
+
if (extensions.has(extName)) toRemove.push(extName);
|
|
2432
|
+
}
|
|
2433
|
+
else await (await loadConfig(async (config) => {
|
|
2434
|
+
const serverPort = config.serverConfig?.port ?? 54321;
|
|
2435
|
+
const extName = getHmrExtensions(serverPort);
|
|
2436
|
+
if (extensions.has(extName)) toRemove.push(extName);
|
|
2437
|
+
if (config.template === "custom-app") {
|
|
2438
|
+
const customAppId = getHmrCustomAppId(getEnName(config.name), serverPort);
|
|
2439
|
+
if (customApps.has(customAppId)) toRemove.push(customAppId);
|
|
2440
|
+
}
|
|
2441
|
+
})).unwatch();
|
|
2442
|
+
if (toRemove.length === 0) {
|
|
2443
|
+
console.log(pc.blue("No HMR artifacts found to clean."));
|
|
2444
|
+
return;
|
|
2445
|
+
}
|
|
2446
|
+
console.log(pc.yellow(`Removing: ${toRemove.join(", ")}...`));
|
|
2447
|
+
for (const item of toRemove) if (extensions.has(item)) runSpice([
|
|
2448
|
+
"config",
|
|
2449
|
+
"extensions",
|
|
2450
|
+
`${item}-`
|
|
2451
|
+
]);
|
|
2452
|
+
else runSpice([
|
|
2453
|
+
"config",
|
|
2454
|
+
"custom_apps",
|
|
2455
|
+
`${item}-`
|
|
2456
|
+
]);
|
|
2457
|
+
runSpice(["apply"]);
|
|
2458
|
+
console.log(pc.green(`${CHECK} Cleanup successful.`));
|
|
2459
|
+
process.exit(0);
|
|
2460
|
+
} catch (e) {
|
|
2461
|
+
console.error(pc.red(`${CROSS} Cleanup failed: `), e);
|
|
2462
|
+
process.exit(1);
|
|
2463
|
+
}
|
|
2464
|
+
});
|
|
2465
|
+
|
|
2353
2466
|
//#endregion
|
|
2354
2467
|
//#region src/commands/update-types.ts
|
|
2355
2468
|
const update_types = new Command("update-types").description("Update Spicetify Types").action(async () => {
|
|
@@ -2360,7 +2473,7 @@ const update_types = new Command("update-types").description("Update Spicetify T
|
|
|
2360
2473
|
//#region src/bin.ts
|
|
2361
2474
|
logger$2.debug(`Env: ${JSON.stringify(env, null, 2)}\n`);
|
|
2362
2475
|
const command = new Command();
|
|
2363
|
-
command.version(version).addCommand(create.alias("init")).addCommand(build$1).addCommand(dev).addCommand(update_types);
|
|
2476
|
+
command.version(version).addCommand(create.alias("init")).addCommand(build$1).addCommand(dev).addCommand(cleanSpicetify.alias("clean-spice")).addCommand(update_types);
|
|
2364
2477
|
command.parse();
|
|
2365
2478
|
|
|
2366
2479
|
//#endregion
|
|
@@ -1,32 +1,4 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
|
|
4
|
-
"useDefineForClassFields": true,
|
|
5
|
-
"module": "ESNext",
|
|
6
|
-
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
7
|
-
"types": ["@spicetify/creator/client"],
|
|
8
|
-
"skipLibCheck": true,
|
|
9
|
-
"jsx": "react-jsx",
|
|
10
|
-
|
|
11
|
-
/* Bundler mode */
|
|
12
|
-
"moduleResolution": "bundler",
|
|
13
|
-
"allowImportingTsExtensions": true,
|
|
14
|
-
"verbatimModuleSyntax": true,
|
|
15
|
-
"moduleDetection": "force",
|
|
16
|
-
"noEmit": true,
|
|
17
|
-
|
|
18
|
-
/* Linting */
|
|
19
|
-
"strict": true,
|
|
20
|
-
"noUnusedLocals": true,
|
|
21
|
-
"noUnusedParameters": true,
|
|
22
|
-
"erasableSyntaxOnly": true,
|
|
23
|
-
"noFallthroughCasesInSwitch": true,
|
|
24
|
-
|
|
25
|
-
// Paths
|
|
26
|
-
"baseUrl": ".",
|
|
27
|
-
"paths": {
|
|
28
|
-
"@/*": ["./src/*"]
|
|
29
|
-
}
|
|
30
|
-
},
|
|
31
|
-
"include": ["src"]
|
|
2
|
+
"files": [],
|
|
3
|
+
"references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }]
|
|
32
4
|
}
|
|
@@ -1,32 +1,4 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
|
|
4
|
-
"useDefineForClassFields": true,
|
|
5
|
-
"module": "ESNext",
|
|
6
|
-
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
7
|
-
"types": ["@spicetify/creator/client"],
|
|
8
|
-
"skipLibCheck": true,
|
|
9
|
-
"jsx": "react-jsx",
|
|
10
|
-
|
|
11
|
-
/* Bundler mode */
|
|
12
|
-
"moduleResolution": "bundler",
|
|
13
|
-
"allowImportingTsExtensions": true,
|
|
14
|
-
"verbatimModuleSyntax": true,
|
|
15
|
-
"moduleDetection": "force",
|
|
16
|
-
"noEmit": true,
|
|
17
|
-
|
|
18
|
-
/* Linting */
|
|
19
|
-
"strict": true,
|
|
20
|
-
"noUnusedLocals": true,
|
|
21
|
-
"noUnusedParameters": true,
|
|
22
|
-
"erasableSyntaxOnly": true,
|
|
23
|
-
"noFallthroughCasesInSwitch": true,
|
|
24
|
-
|
|
25
|
-
// Paths
|
|
26
|
-
"baseUrl": ".",
|
|
27
|
-
"paths": {
|
|
28
|
-
"@/*": ["./src/*"]
|
|
29
|
-
}
|
|
30
|
-
},
|
|
31
|
-
"include": ["src"]
|
|
2
|
+
"files": [],
|
|
3
|
+
"references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }]
|
|
32
4
|
}
|
|
@@ -17,6 +17,84 @@
|
|
|
17
17
|
warning: `⚠️`,
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
+
const ANSI_REGEX = /\x1b\[[0-9;]*m/g;
|
|
21
|
+
|
|
22
|
+
function stripAnsi(str) {
|
|
23
|
+
if (typeof str !== "string") return str;
|
|
24
|
+
return str.replace(ANSI_REGEX, "");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function parseAnsiColors(str) {
|
|
28
|
+
if (typeof str !== "string") return str;
|
|
29
|
+
|
|
30
|
+
const ansiMap = {
|
|
31
|
+
"1": "font-weight: bold",
|
|
32
|
+
"3": "font-style: italic",
|
|
33
|
+
"4": "text-decoration: underline",
|
|
34
|
+
"30": "color: #0",
|
|
35
|
+
"31": "color: #e91429",
|
|
36
|
+
"32": "color: #1ed760",
|
|
37
|
+
"33": "color: #f59b23",
|
|
38
|
+
"34": "color: #3b82f6",
|
|
39
|
+
"35": "color: #a855f7",
|
|
40
|
+
"36": "color: #06b6d4",
|
|
41
|
+
"37": "color: #fff",
|
|
42
|
+
"90": "color: #6b7280",
|
|
43
|
+
"91": "color: #f87171",
|
|
44
|
+
"92": "color: #4ade80",
|
|
45
|
+
"93": "color: #facc15",
|
|
46
|
+
"94": "color: #60a5fa",
|
|
47
|
+
"95": "color: #c084fc",
|
|
48
|
+
"96": "color: #22d3ee",
|
|
49
|
+
"97": "color: #f3f4f6",
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
let result = "";
|
|
53
|
+
let currentStyle = "";
|
|
54
|
+
let lastIndex = 0;
|
|
55
|
+
|
|
56
|
+
const regex = /\x1b\[([0-9;]*)m/g;
|
|
57
|
+
let match;
|
|
58
|
+
|
|
59
|
+
while ((match = regex.exec(str)) !== null) {
|
|
60
|
+
result += str.slice(lastIndex, match.index);
|
|
61
|
+
const code = match[1];
|
|
62
|
+
|
|
63
|
+
if (code === "0" || code === "") {
|
|
64
|
+
if (currentStyle) {
|
|
65
|
+
result += `</span>`;
|
|
66
|
+
currentStyle = "";
|
|
67
|
+
}
|
|
68
|
+
} else {
|
|
69
|
+
const codes = code.split(";").map(c => parseInt(c, 10));
|
|
70
|
+
const styles = [];
|
|
71
|
+
for (const c of codes) {
|
|
72
|
+
if (c >= 30 && c <= 37 || c >= 90 && c <= 97) {
|
|
73
|
+
const style = ansiMap[String(c)];
|
|
74
|
+
if (style) styles.push(style);
|
|
75
|
+
} else if (c === 1) {
|
|
76
|
+
styles.push("font-weight: bold");
|
|
77
|
+
} else if (c === 3) {
|
|
78
|
+
styles.push("font-style: italic");
|
|
79
|
+
} else if (c === 4) {
|
|
80
|
+
styles.push("text-decoration: underline");
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (styles.length > 0) {
|
|
84
|
+
if (currentStyle) result += `</span>`;
|
|
85
|
+
currentStyle = styles.join("; ");
|
|
86
|
+
result += `<span style="${currentStyle}">`;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
lastIndex = match.index + match[0].length;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
result += str.slice(lastIndex);
|
|
93
|
+
if (currentStyle) result += `</span>`;
|
|
94
|
+
|
|
95
|
+
return result || stripAnsi(str);
|
|
96
|
+
}
|
|
97
|
+
|
|
20
98
|
class SpicetifyDevTools {
|
|
21
99
|
constructor() {
|
|
22
100
|
this.state = {
|
|
@@ -179,7 +257,8 @@
|
|
|
179
257
|
const fragment = document.createDocumentFragment();
|
|
180
258
|
|
|
181
259
|
errors.forEach((err, index) => {
|
|
182
|
-
const
|
|
260
|
+
const rawMsg = err.text || err.message || String(err);
|
|
261
|
+
const msg = parseAnsiColors(rawMsg);
|
|
183
262
|
const item = document.createElement("div");
|
|
184
263
|
item.className = "sc-err-item";
|
|
185
264
|
|
|
@@ -1,32 +1,4 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
|
|
4
|
-
"useDefineForClassFields": true,
|
|
5
|
-
"module": "ESNext",
|
|
6
|
-
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
7
|
-
"types": ["@spicetify/creator/client"],
|
|
8
|
-
"skipLibCheck": true,
|
|
9
|
-
"jsx": "react-jsx",
|
|
10
|
-
|
|
11
|
-
/* Bundler mode */
|
|
12
|
-
"moduleResolution": "bundler",
|
|
13
|
-
"allowImportingTsExtensions": true,
|
|
14
|
-
"verbatimModuleSyntax": true,
|
|
15
|
-
"moduleDetection": "force",
|
|
16
|
-
"noEmit": true,
|
|
17
|
-
|
|
18
|
-
/* Linting */
|
|
19
|
-
"strict": true,
|
|
20
|
-
"noUnusedLocals": true,
|
|
21
|
-
"noUnusedParameters": true,
|
|
22
|
-
"erasableSyntaxOnly": true,
|
|
23
|
-
"noFallthroughCasesInSwitch": true,
|
|
24
|
-
|
|
25
|
-
// Paths
|
|
26
|
-
"baseUrl": ".",
|
|
27
|
-
"paths": {
|
|
28
|
-
"@/*": ["./src/*"]
|
|
29
|
-
}
|
|
30
|
-
},
|
|
31
|
-
"include": ["src"]
|
|
2
|
+
"files": [],
|
|
3
|
+
"references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }]
|
|
32
4
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spicemod/creator",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.36",
|
|
4
4
|
"description": "Easily make Spicetify extensions and themes",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -75,10 +75,10 @@
|
|
|
75
75
|
"@types/ws": "^8.18.1",
|
|
76
76
|
"jiti": "^2.6.1",
|
|
77
77
|
"oxfmt": "^0.28.0",
|
|
78
|
-
"oxlint": "^1.
|
|
78
|
+
"oxlint": "^1.57.0",
|
|
79
79
|
"tsdown": "^0.20.3",
|
|
80
80
|
"typescript": "^5.9.3",
|
|
81
|
-
"vitest": "^4.1.
|
|
81
|
+
"vitest": "^4.1.2"
|
|
82
82
|
},
|
|
83
83
|
"packageManager": "bun@1.3.4"
|
|
84
84
|
}
|
|
@@ -1,32 +1,4 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
|
|
4
|
-
"useDefineForClassFields": true,
|
|
5
|
-
"module": "ESNext",
|
|
6
|
-
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
7
|
-
"types": ["@spicetify/creator/client"],
|
|
8
|
-
"skipLibCheck": true,
|
|
9
|
-
"jsx": "react-jsx",
|
|
10
|
-
|
|
11
|
-
/* Bundler mode */
|
|
12
|
-
"moduleResolution": "bundler",
|
|
13
|
-
"allowImportingTsExtensions": true,
|
|
14
|
-
"verbatimModuleSyntax": true,
|
|
15
|
-
"moduleDetection": "force",
|
|
16
|
-
"noEmit": true,
|
|
17
|
-
|
|
18
|
-
/* Linting */
|
|
19
|
-
"strict": true,
|
|
20
|
-
"noUnusedLocals": true,
|
|
21
|
-
"noUnusedParameters": true,
|
|
22
|
-
"erasableSyntaxOnly": true,
|
|
23
|
-
"noFallthroughCasesInSwitch": true,
|
|
24
|
-
|
|
25
|
-
// Paths
|
|
26
|
-
"baseUrl": ".",
|
|
27
|
-
"paths": {
|
|
28
|
-
"@/*": ["./src/*"]
|
|
29
|
-
}
|
|
30
|
-
},
|
|
31
|
-
"include": ["src"]
|
|
2
|
+
"files": [],
|
|
3
|
+
"references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }]
|
|
32
4
|
}
|
|
@@ -1,32 +1,4 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
|
|
4
|
-
"useDefineForClassFields": true,
|
|
5
|
-
"module": "ESNext",
|
|
6
|
-
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
7
|
-
"types": ["@spicetify/creator/client"],
|
|
8
|
-
"skipLibCheck": true,
|
|
9
|
-
"jsx": "react-jsx",
|
|
10
|
-
|
|
11
|
-
/* Bundler mode */
|
|
12
|
-
"moduleResolution": "bundler",
|
|
13
|
-
"allowImportingTsExtensions": true,
|
|
14
|
-
"verbatimModuleSyntax": true,
|
|
15
|
-
"moduleDetection": "force",
|
|
16
|
-
"noEmit": true,
|
|
17
|
-
|
|
18
|
-
/* Linting */
|
|
19
|
-
"strict": true,
|
|
20
|
-
"noUnusedLocals": true,
|
|
21
|
-
"noUnusedParameters": true,
|
|
22
|
-
"erasableSyntaxOnly": true,
|
|
23
|
-
"noFallthroughCasesInSwitch": true,
|
|
24
|
-
|
|
25
|
-
// Paths
|
|
26
|
-
"baseUrl": ".",
|
|
27
|
-
"paths": {
|
|
28
|
-
"@/*": ["./src/*"]
|
|
29
|
-
}
|
|
30
|
-
},
|
|
31
|
-
"include": ["src"]
|
|
2
|
+
"files": [],
|
|
3
|
+
"references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }]
|
|
32
4
|
}
|
package/templates/liveReload.js
CHANGED
|
@@ -17,6 +17,84 @@
|
|
|
17
17
|
warning: `⚠️`,
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
+
const ANSI_REGEX = /\x1b\[[0-9;]*m/g;
|
|
21
|
+
|
|
22
|
+
function stripAnsi(str) {
|
|
23
|
+
if (typeof str !== "string") return str;
|
|
24
|
+
return str.replace(ANSI_REGEX, "");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function parseAnsiColors(str) {
|
|
28
|
+
if (typeof str !== "string") return str;
|
|
29
|
+
|
|
30
|
+
const ansiMap = {
|
|
31
|
+
"1": "font-weight: bold",
|
|
32
|
+
"3": "font-style: italic",
|
|
33
|
+
"4": "text-decoration: underline",
|
|
34
|
+
"30": "color: #0",
|
|
35
|
+
"31": "color: #e91429",
|
|
36
|
+
"32": "color: #1ed760",
|
|
37
|
+
"33": "color: #f59b23",
|
|
38
|
+
"34": "color: #3b82f6",
|
|
39
|
+
"35": "color: #a855f7",
|
|
40
|
+
"36": "color: #06b6d4",
|
|
41
|
+
"37": "color: #fff",
|
|
42
|
+
"90": "color: #6b7280",
|
|
43
|
+
"91": "color: #f87171",
|
|
44
|
+
"92": "color: #4ade80",
|
|
45
|
+
"93": "color: #facc15",
|
|
46
|
+
"94": "color: #60a5fa",
|
|
47
|
+
"95": "color: #c084fc",
|
|
48
|
+
"96": "color: #22d3ee",
|
|
49
|
+
"97": "color: #f3f4f6",
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
let result = "";
|
|
53
|
+
let currentStyle = "";
|
|
54
|
+
let lastIndex = 0;
|
|
55
|
+
|
|
56
|
+
const regex = /\x1b\[([0-9;]*)m/g;
|
|
57
|
+
let match;
|
|
58
|
+
|
|
59
|
+
while ((match = regex.exec(str)) !== null) {
|
|
60
|
+
result += str.slice(lastIndex, match.index);
|
|
61
|
+
const code = match[1];
|
|
62
|
+
|
|
63
|
+
if (code === "0" || code === "") {
|
|
64
|
+
if (currentStyle) {
|
|
65
|
+
result += `</span>`;
|
|
66
|
+
currentStyle = "";
|
|
67
|
+
}
|
|
68
|
+
} else {
|
|
69
|
+
const codes = code.split(";").map(c => parseInt(c, 10));
|
|
70
|
+
const styles = [];
|
|
71
|
+
for (const c of codes) {
|
|
72
|
+
if (c >= 30 && c <= 37 || c >= 90 && c <= 97) {
|
|
73
|
+
const style = ansiMap[String(c)];
|
|
74
|
+
if (style) styles.push(style);
|
|
75
|
+
} else if (c === 1) {
|
|
76
|
+
styles.push("font-weight: bold");
|
|
77
|
+
} else if (c === 3) {
|
|
78
|
+
styles.push("font-style: italic");
|
|
79
|
+
} else if (c === 4) {
|
|
80
|
+
styles.push("text-decoration: underline");
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (styles.length > 0) {
|
|
84
|
+
if (currentStyle) result += `</span>`;
|
|
85
|
+
currentStyle = styles.join("; ");
|
|
86
|
+
result += `<span style="${currentStyle}">`;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
lastIndex = match.index + match[0].length;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
result += str.slice(lastIndex);
|
|
93
|
+
if (currentStyle) result += `</span>`;
|
|
94
|
+
|
|
95
|
+
return result || stripAnsi(str);
|
|
96
|
+
}
|
|
97
|
+
|
|
20
98
|
class SpicetifyDevTools {
|
|
21
99
|
constructor() {
|
|
22
100
|
this.state = {
|
|
@@ -179,7 +257,8 @@
|
|
|
179
257
|
const fragment = document.createDocumentFragment();
|
|
180
258
|
|
|
181
259
|
errors.forEach((err, index) => {
|
|
182
|
-
const
|
|
260
|
+
const rawMsg = err.text || err.message || String(err);
|
|
261
|
+
const msg = parseAnsiColors(rawMsg);
|
|
183
262
|
const item = document.createElement("div");
|
|
184
263
|
item.className = "sc-err-item";
|
|
185
264
|
|
|
@@ -1,32 +1,4 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
|
|
4
|
-
"useDefineForClassFields": true,
|
|
5
|
-
"module": "ESNext",
|
|
6
|
-
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
7
|
-
"types": ["@spicetify/creator/client"],
|
|
8
|
-
"skipLibCheck": true,
|
|
9
|
-
"jsx": "react-jsx",
|
|
10
|
-
|
|
11
|
-
/* Bundler mode */
|
|
12
|
-
"moduleResolution": "bundler",
|
|
13
|
-
"allowImportingTsExtensions": true,
|
|
14
|
-
"verbatimModuleSyntax": true,
|
|
15
|
-
"moduleDetection": "force",
|
|
16
|
-
"noEmit": true,
|
|
17
|
-
|
|
18
|
-
/* Linting */
|
|
19
|
-
"strict": true,
|
|
20
|
-
"noUnusedLocals": true,
|
|
21
|
-
"noUnusedParameters": true,
|
|
22
|
-
"erasableSyntaxOnly": true,
|
|
23
|
-
"noFallthroughCasesInSwitch": true,
|
|
24
|
-
|
|
25
|
-
// Paths
|
|
26
|
-
"baseUrl": ".",
|
|
27
|
-
"paths": {
|
|
28
|
-
"@/*": ["./src/*"]
|
|
29
|
-
}
|
|
30
|
-
},
|
|
31
|
-
"include": ["src"]
|
|
2
|
+
"files": [],
|
|
3
|
+
"references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }]
|
|
32
4
|
}
|