@spicemod/creator 0.0.22 → 0.0.23
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/client.d.ts +47 -0
- package/dist/bin.mjs +684 -245
- package/dist/index.d.mts +697 -0
- package/dist/{client/index.mjs → index.mjs} +1 -1
- package/dist/templates/custom-app/js/react/eslint.config.ts +29 -0
- package/dist/templates/custom-app/js/react/src/app.jsx +22 -0
- package/dist/templates/custom-app/js/react/src/components/Onboarding.jsx +82 -0
- package/dist/templates/custom-app/js/react/src/extension/index.jsx +23 -0
- package/dist/templates/custom-app/meta.json +4 -0
- package/dist/templates/custom-app/shared/DOT-gitignore +34 -0
- package/dist/templates/custom-app/shared/DOT-oxlintrc.json +36 -0
- package/dist/templates/custom-app/shared/README.template.md +53 -0
- package/dist/templates/custom-app/shared/app.css +163 -0
- package/dist/templates/custom-app/shared/biome.json +36 -0
- package/dist/templates/custom-app/shared/css/app.module.scss +58 -0
- package/dist/templates/custom-app/shared/icon-active.svg +7 -0
- package/dist/templates/custom-app/shared/icon.svg +7 -0
- package/dist/templates/custom-app/shared/jsconfig.json +32 -0
- package/dist/templates/custom-app/shared/spice.config.js +9 -0
- package/dist/templates/custom-app/shared/spice.config.ts +9 -0
- package/dist/templates/custom-app/shared/tsconfig.json +32 -0
- package/dist/templates/custom-app/ts/react/eslint.config.ts +29 -0
- package/dist/templates/custom-app/ts/react/src/app.tsx +23 -0
- package/dist/templates/custom-app/ts/react/src/components/Onboarding.tsx +105 -0
- package/dist/templates/custom-app/ts/react/src/extension/index.tsx +27 -0
- package/dist/templates/extension/js/vanilla/src/components/Onboarding.js +71 -0
- package/dist/templates/extension/shared/DOT-gitignore +34 -0
- package/dist/templates/extension/shared/DOT-oxlintrc.json +36 -0
- package/dist/templates/extension/shared/spice.config.js +1 -1
- package/dist/templates/extension/shared/spice.config.ts +1 -1
- package/dist/templates/liveReload.js +0 -1
- package/dist/templates/theme/shared/DOT-gitignore +34 -0
- package/dist/templates/theme/shared/DOT-oxlintrc.json +36 -0
- package/dist/templates/theme/shared/spice.config.js +1 -1
- package/dist/templates/theme/shared/spice.config.ts +1 -1
- package/dist/templates/wrapper.js +6 -9
- package/package.json +7 -3
- package/templates/custom-app/js/react/eslint.config.ts +29 -0
- package/templates/custom-app/js/react/src/app.jsx +22 -0
- package/templates/custom-app/js/react/src/components/Onboarding.jsx +82 -0
- package/templates/custom-app/js/react/src/extension/index.jsx +23 -0
- package/templates/custom-app/meta.json +4 -0
- package/templates/custom-app/shared/DOT-gitignore +34 -0
- package/templates/custom-app/shared/DOT-oxlintrc.json +36 -0
- package/templates/custom-app/shared/README.template.md +53 -0
- package/templates/custom-app/shared/app.css +163 -0
- package/templates/custom-app/shared/biome.json +36 -0
- package/templates/custom-app/shared/css/app.module.scss +58 -0
- package/templates/custom-app/shared/icon-active.svg +7 -0
- package/templates/custom-app/shared/icon.svg +7 -0
- package/templates/custom-app/shared/jsconfig.json +32 -0
- package/templates/custom-app/shared/spice.config.js +9 -0
- package/templates/custom-app/shared/spice.config.ts +9 -0
- package/templates/custom-app/shared/tsconfig.json +32 -0
- package/templates/custom-app/ts/react/eslint.config.ts +29 -0
- package/templates/custom-app/ts/react/src/app.tsx +23 -0
- package/templates/custom-app/ts/react/src/components/Onboarding.tsx +105 -0
- package/templates/custom-app/ts/react/src/extension/index.tsx +27 -0
- package/templates/extension/js/vanilla/src/components/Onboarding.js +71 -0
- package/templates/extension/shared/DOT-gitignore +34 -0
- package/templates/extension/shared/DOT-oxlintrc.json +36 -0
- package/templates/extension/shared/spice.config.js +1 -1
- package/templates/extension/shared/spice.config.ts +1 -1
- package/templates/liveReload.js +0 -1
- package/templates/theme/shared/DOT-gitignore +34 -0
- package/templates/theme/shared/DOT-oxlintrc.json +36 -0
- package/templates/theme/shared/spice.config.js +1 -1
- package/templates/theme/shared/spice.config.ts +1 -1
- package/templates/wrapper.js +6 -9
- package/dist/client/index.d.mts +0 -2183
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 { context, transform } from "esbuild";
|
|
5
|
+
import { build, context, transform } from "esbuild";
|
|
6
6
|
import { createReadStream, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
7
7
|
import { watchConfig } from "c12";
|
|
8
8
|
import { globSync } from "tinyglobby";
|
|
@@ -10,7 +10,7 @@ import { URL as URL$1, fileURLToPath } from "node:url";
|
|
|
10
10
|
import { execSync, spawn, spawnSync } from "node:child_process";
|
|
11
11
|
import readline, { createInterface } from "node:readline";
|
|
12
12
|
import * as p from "@clack/prompts";
|
|
13
|
-
import { cancel, log } from "@clack/prompts";
|
|
13
|
+
import { cancel, log, spinner } from "@clack/prompts";
|
|
14
14
|
import pc from "picocolors";
|
|
15
15
|
import "dotenv/config";
|
|
16
16
|
import { gzipSync } from "node:zlib";
|
|
@@ -22,8 +22,11 @@ import postcssImport from "postcss-import";
|
|
|
22
22
|
import postcssPresetEnv from "postcss-preset-env";
|
|
23
23
|
import { readFile, writeFile } from "node:fs/promises";
|
|
24
24
|
import { parse } from "ini";
|
|
25
|
+
import { createHash } from "node:crypto";
|
|
25
26
|
import { chdir } from "node:process";
|
|
26
27
|
import { lookup } from "node:dns/promises";
|
|
28
|
+
import { mkdir, writeFile as writeFile$1 } from "fs/promises";
|
|
29
|
+
import { dirname as dirname$1 } from "path";
|
|
27
30
|
import { createServer } from "node:http";
|
|
28
31
|
import { WebSocket, WebSocketServer } from "ws";
|
|
29
32
|
|
|
@@ -57,7 +60,11 @@ const toOptions = (metadata) => metadata.map((m) => ({
|
|
|
57
60
|
label: m.title,
|
|
58
61
|
hint: m.description
|
|
59
62
|
}));
|
|
60
|
-
const templateTypes = [
|
|
63
|
+
const templateTypes = [
|
|
64
|
+
"extension",
|
|
65
|
+
"theme",
|
|
66
|
+
"custom-app"
|
|
67
|
+
];
|
|
61
68
|
const templates = templateTypes.map((dir) => {
|
|
62
69
|
const meta_file = dist(`templates/${dir}/meta.json`, import.meta.url);
|
|
63
70
|
const { title, description } = JSON.parse(readFileSync(meta_file, "utf8"));
|
|
@@ -130,12 +137,13 @@ const frameworks = frameworkTypes.map((name) => ({
|
|
|
130
137
|
}));
|
|
131
138
|
const frameworkOptions = toOptions(frameworks);
|
|
132
139
|
const liveReloadFilePath = dist(`templates/liveReload.js`, import.meta.url);
|
|
133
|
-
const
|
|
140
|
+
const templateWrapperFilePath = dist("templates/wrapper.js", import.meta.url);
|
|
141
|
+
const templateCustomAppWrapperFilePath = dist("templates/customAppWrapper.js", import.meta.url);
|
|
134
142
|
|
|
135
143
|
//#endregion
|
|
136
144
|
//#region package.json
|
|
137
145
|
var name = "@spicemod/creator";
|
|
138
|
-
var version = "0.0.
|
|
146
|
+
var version = "0.0.23";
|
|
139
147
|
|
|
140
148
|
//#endregion
|
|
141
149
|
//#region src/utils/common.ts
|
|
@@ -229,57 +237,6 @@ async function installPackages(packageManager, isOnline) {
|
|
|
229
237
|
});
|
|
230
238
|
}
|
|
231
239
|
|
|
232
|
-
//#endregion
|
|
233
|
-
//#region src/config/schema.ts
|
|
234
|
-
const ServerConfigSchema = v.object({
|
|
235
|
-
port: v.optional(v.number()),
|
|
236
|
-
serveDir: v.string(),
|
|
237
|
-
hmrPath: v.optional(v.string())
|
|
238
|
-
});
|
|
239
|
-
const EntryFileSchema = v.string();
|
|
240
|
-
const ThemeEntrySchema = v.object({
|
|
241
|
-
js: EntryFileSchema,
|
|
242
|
-
css: EntryFileSchema
|
|
243
|
-
});
|
|
244
|
-
const TemplateSpecificOptionalSchema = v.variant("template", [v.partial(v.object({
|
|
245
|
-
template: v.literal("extension"),
|
|
246
|
-
entry: EntryFileSchema
|
|
247
|
-
})), v.partial(v.object({
|
|
248
|
-
template: v.literal("theme"),
|
|
249
|
-
entry: ThemeEntrySchema
|
|
250
|
-
}))]);
|
|
251
|
-
const TemplateSpecificSchema = v.variant("template", [v.object({
|
|
252
|
-
template: v.literal("extension"),
|
|
253
|
-
entry: EntryFileSchema
|
|
254
|
-
}), v.object({
|
|
255
|
-
template: v.literal("theme"),
|
|
256
|
-
entry: ThemeEntrySchema
|
|
257
|
-
})]);
|
|
258
|
-
const CommonSchema = v.object({
|
|
259
|
-
name: v.string(),
|
|
260
|
-
outDir: v.string(),
|
|
261
|
-
linter: v.picklist(linterTypes),
|
|
262
|
-
framework: v.picklist(frameworkTypes),
|
|
263
|
-
packageManager: v.picklist(packageManagers),
|
|
264
|
-
esbuildOptions: v.record(v.string(), v.any()),
|
|
265
|
-
devModeVarName: v.optional(v.string()),
|
|
266
|
-
serverConfig: v.partial(ServerConfigSchema),
|
|
267
|
-
version: v.string()
|
|
268
|
-
});
|
|
269
|
-
const FileOptionsSchema = v.intersect([v.partial(CommonSchema), TemplateSpecificOptionalSchema]);
|
|
270
|
-
const OptionsSchema$1 = v.pipe(v.intersect([v.required(CommonSchema), TemplateSpecificSchema]), v.check((input) => !!input.name, "Name is required"));
|
|
271
|
-
|
|
272
|
-
//#endregion
|
|
273
|
-
//#region src/env.ts
|
|
274
|
-
const isInternal = process.env.SPICE_INTERNAL === "true";
|
|
275
|
-
const isDev = process.env.IS_DEV === "true";
|
|
276
|
-
const spicetifyBin = process.env.SPICETIFY_BIN || process.env.SPICE_BIN || "spicetify";
|
|
277
|
-
const env = {
|
|
278
|
-
isInternal,
|
|
279
|
-
isDev,
|
|
280
|
-
spicetifyBin
|
|
281
|
-
};
|
|
282
|
-
|
|
283
240
|
//#endregion
|
|
284
241
|
//#region src/constants.ts
|
|
285
242
|
const GITHUB_LINK = "https://github.com/sanoojes/spicetify-creator";
|
|
@@ -291,6 +248,7 @@ const DEV_MODE_VAR_NAME = `__SPICE_CREATOR_DEV__`;
|
|
|
291
248
|
const CHECK = pc.bold(pc.green("✔"));
|
|
292
249
|
const CROSS = pc.bold(pc.red("✖"));
|
|
293
250
|
const WARN = pc.bold(pc.yellow("⚠"));
|
|
251
|
+
const SKIP_SPICETIFY = process.env.SPICETIFY_SKIP === "true" || process.env.CI === "true";
|
|
294
252
|
const VALID_PROJECT_FILES = new Set([
|
|
295
253
|
".DS_Store",
|
|
296
254
|
".git",
|
|
@@ -313,6 +271,150 @@ const VALID_PROJECT_FILES = new Set([
|
|
|
313
271
|
"yarnrc.yml",
|
|
314
272
|
".yarn"
|
|
315
273
|
]);
|
|
274
|
+
const CUSTOM_APP_NAME_LOCALES = [
|
|
275
|
+
"ms",
|
|
276
|
+
"gu",
|
|
277
|
+
"ko",
|
|
278
|
+
"pa-IN",
|
|
279
|
+
"az",
|
|
280
|
+
"ru",
|
|
281
|
+
"uk",
|
|
282
|
+
"nb",
|
|
283
|
+
"sv",
|
|
284
|
+
"sw",
|
|
285
|
+
"ur",
|
|
286
|
+
"bho",
|
|
287
|
+
"pa-PK",
|
|
288
|
+
"te",
|
|
289
|
+
"ro",
|
|
290
|
+
"vi",
|
|
291
|
+
"am",
|
|
292
|
+
"bn",
|
|
293
|
+
"en",
|
|
294
|
+
"id",
|
|
295
|
+
"bg",
|
|
296
|
+
"da",
|
|
297
|
+
"es-419",
|
|
298
|
+
"mr",
|
|
299
|
+
"ml",
|
|
300
|
+
"th",
|
|
301
|
+
"tr",
|
|
302
|
+
"is",
|
|
303
|
+
"fa",
|
|
304
|
+
"or",
|
|
305
|
+
"he",
|
|
306
|
+
"hi",
|
|
307
|
+
"zh-TW",
|
|
308
|
+
"sr",
|
|
309
|
+
"pt-BR",
|
|
310
|
+
"zu",
|
|
311
|
+
"nl",
|
|
312
|
+
"es",
|
|
313
|
+
"lt",
|
|
314
|
+
"ja",
|
|
315
|
+
"st",
|
|
316
|
+
"it",
|
|
317
|
+
"el",
|
|
318
|
+
"pt-PT",
|
|
319
|
+
"kn",
|
|
320
|
+
"de",
|
|
321
|
+
"fr",
|
|
322
|
+
"ne",
|
|
323
|
+
"ar",
|
|
324
|
+
"af",
|
|
325
|
+
"et",
|
|
326
|
+
"pl",
|
|
327
|
+
"ta",
|
|
328
|
+
"sl",
|
|
329
|
+
"pk",
|
|
330
|
+
"hr",
|
|
331
|
+
"sk",
|
|
332
|
+
"fi",
|
|
333
|
+
"lv",
|
|
334
|
+
"fil",
|
|
335
|
+
"fr-CA",
|
|
336
|
+
"cs",
|
|
337
|
+
"zh-CN",
|
|
338
|
+
"hu"
|
|
339
|
+
];
|
|
340
|
+
|
|
341
|
+
//#endregion
|
|
342
|
+
//#region src/config/schema.ts
|
|
343
|
+
const ServerConfigSchema = v.object({
|
|
344
|
+
port: v.optional(v.number()),
|
|
345
|
+
serveDir: v.string(),
|
|
346
|
+
hmrPath: v.optional(v.string())
|
|
347
|
+
});
|
|
348
|
+
const EntryFileSchema = v.string();
|
|
349
|
+
const AssetEntrySchema = v.object({
|
|
350
|
+
js: EntryFileSchema,
|
|
351
|
+
css: EntryFileSchema
|
|
352
|
+
});
|
|
353
|
+
const CustomAppEntrySchema = v.object({
|
|
354
|
+
extension: EntryFileSchema,
|
|
355
|
+
app: EntryFileSchema
|
|
356
|
+
});
|
|
357
|
+
const LocaleNameSchema = v.intersect([v.object({ en: v.string() }), v.record(v.picklist(CUSTOM_APP_NAME_LOCALES), v.string())]);
|
|
358
|
+
const ExtensionTemplateSchema = v.object({
|
|
359
|
+
name: v.string(),
|
|
360
|
+
template: v.literal("extension"),
|
|
361
|
+
entry: EntryFileSchema
|
|
362
|
+
});
|
|
363
|
+
const ThemeTemplateSchema = v.object({
|
|
364
|
+
name: v.string(),
|
|
365
|
+
template: v.literal("theme"),
|
|
366
|
+
entry: AssetEntrySchema
|
|
367
|
+
});
|
|
368
|
+
const CustomAppTemplateSchema = v.object({
|
|
369
|
+
name: v.union([v.string(), LocaleNameSchema]),
|
|
370
|
+
icon: v.object({
|
|
371
|
+
default: v.string(),
|
|
372
|
+
active: v.optional(v.string())
|
|
373
|
+
}),
|
|
374
|
+
template: v.literal("custom-app"),
|
|
375
|
+
entry: CustomAppEntrySchema
|
|
376
|
+
});
|
|
377
|
+
const TemplateSpecificSchema = v.variant("template", [
|
|
378
|
+
ExtensionTemplateSchema,
|
|
379
|
+
ThemeTemplateSchema,
|
|
380
|
+
CustomAppTemplateSchema
|
|
381
|
+
]);
|
|
382
|
+
const TemplateSpecificOptionalSchema = v.variant("template", [
|
|
383
|
+
v.partial(ExtensionTemplateSchema),
|
|
384
|
+
v.partial(ThemeTemplateSchema),
|
|
385
|
+
v.partial(CustomAppTemplateSchema)
|
|
386
|
+
]);
|
|
387
|
+
const RequiredCommonSchema = v.object({
|
|
388
|
+
outDir: v.string(),
|
|
389
|
+
linter: v.picklist(linterTypes),
|
|
390
|
+
framework: v.picklist(frameworkTypes),
|
|
391
|
+
packageManager: v.picklist(packageManagers),
|
|
392
|
+
esbuildOptions: v.record(v.string(), v.any()),
|
|
393
|
+
serverConfig: v.partial(ServerConfigSchema),
|
|
394
|
+
version: v.string()
|
|
395
|
+
});
|
|
396
|
+
const OptionalCommonSchema = v.object({ devModeVarName: v.optional(v.string()) });
|
|
397
|
+
const FileOptionsSchema = v.intersect([
|
|
398
|
+
v.partial(RequiredCommonSchema),
|
|
399
|
+
OptionalCommonSchema,
|
|
400
|
+
TemplateSpecificOptionalSchema
|
|
401
|
+
]);
|
|
402
|
+
const OptionsSchema$1 = v.intersect([
|
|
403
|
+
v.required(RequiredCommonSchema),
|
|
404
|
+
OptionalCommonSchema,
|
|
405
|
+
TemplateSpecificSchema
|
|
406
|
+
]);
|
|
407
|
+
|
|
408
|
+
//#endregion
|
|
409
|
+
//#region src/env.ts
|
|
410
|
+
const isInternal = process.env.SPICE_INTERNAL === "true";
|
|
411
|
+
const isDev = process.env.IS_DEV === "true";
|
|
412
|
+
const spicetifyBin = process.env.SPICETIFY_BIN || process.env.SPICE_BIN || "spicetify";
|
|
413
|
+
const env = {
|
|
414
|
+
isInternal,
|
|
415
|
+
isDev,
|
|
416
|
+
spicetifyBin
|
|
417
|
+
};
|
|
316
418
|
|
|
317
419
|
//#endregion
|
|
318
420
|
//#region src/utils/logger.ts
|
|
@@ -350,8 +452,15 @@ var Logger = class {
|
|
|
350
452
|
this.add(console.warn, args);
|
|
351
453
|
}
|
|
352
454
|
error(...errors) {
|
|
353
|
-
|
|
354
|
-
for (const err of errors) if (err instanceof Error
|
|
455
|
+
const formatted = [];
|
|
456
|
+
for (const err of errors) if (err instanceof Error) {
|
|
457
|
+
formatted.push(pc.red(err.message));
|
|
458
|
+
if (this.isDev && err.stack) {
|
|
459
|
+
const stack = err.stack.split("\n").slice(1).join("\n");
|
|
460
|
+
formatted.push(pc.dim(stack));
|
|
461
|
+
}
|
|
462
|
+
} else formatted.push(err);
|
|
463
|
+
this.add(console.error, formatted);
|
|
355
464
|
}
|
|
356
465
|
log(...args) {
|
|
357
466
|
if (!this.isDev) return;
|
|
@@ -384,21 +493,39 @@ function safeParse(schema, data, type = "CLI") {
|
|
|
384
493
|
process.exit(1);
|
|
385
494
|
}
|
|
386
495
|
|
|
496
|
+
//#endregion
|
|
497
|
+
//#region src/config/globs.ts
|
|
498
|
+
const JS_EXTENSIONS = "{ts,tsx,js,jsx,mts,mjs,cts,cjs}";
|
|
499
|
+
const CSS_EXTENSIONS = "{css,scss,sass,less,styl,stylus,pcss,postcss}";
|
|
500
|
+
const withSrc = (dirs) => dirs.flatMap((dir) => [dir, `src/${dir}`]);
|
|
501
|
+
const createGlobs = (names, dirs, ext) => names.flatMap((name) => dirs.map((dir) => `${dir}${name}.${ext}`));
|
|
502
|
+
const JS_ENTRY_GLOBS = createGlobs(["app", "index"], withSrc([""]), JS_EXTENSIONS);
|
|
503
|
+
const JS_EXTENSION_ENTRY_GLOBS = createGlobs(["app", "index"], withSrc(["extension/"]), JS_EXTENSIONS);
|
|
504
|
+
const CSS_ENTRY_GLOBS = createGlobs(["app"], withSrc([
|
|
505
|
+
"",
|
|
506
|
+
"styles/",
|
|
507
|
+
"css/"
|
|
508
|
+
]), CSS_EXTENSIONS);
|
|
509
|
+
const ICON_GLOBS = createGlobs(["icon", "logo"], withSrc([
|
|
510
|
+
"",
|
|
511
|
+
"icons/",
|
|
512
|
+
"assets/"
|
|
513
|
+
]), "svg");
|
|
514
|
+
const ICON_ACTIVE_GLOBS = createGlobs(["icon-active", "logo-active"], withSrc([
|
|
515
|
+
"",
|
|
516
|
+
"icons/",
|
|
517
|
+
"assets/"
|
|
518
|
+
]), "svg");
|
|
519
|
+
const ENTRY_MAP = {
|
|
520
|
+
js: [...JS_ENTRY_GLOBS, ...JS_EXTENSION_ENTRY_GLOBS],
|
|
521
|
+
css: CSS_ENTRY_GLOBS,
|
|
522
|
+
"js-app-only": JS_ENTRY_GLOBS,
|
|
523
|
+
"js-extension-only": JS_EXTENSION_ENTRY_GLOBS
|
|
524
|
+
};
|
|
525
|
+
|
|
387
526
|
//#endregion
|
|
388
527
|
//#region src/config/index.ts
|
|
389
528
|
const logger$1 = createLogger("config");
|
|
390
|
-
const JS_ENTRY_GLOBS = [
|
|
391
|
-
"app.{ts,tsx,js,jsx,mts,mjs,cts,cjs}",
|
|
392
|
-
"extension/app.{ts,tsx,js,jsx,mts,mjs,cts,cjs}",
|
|
393
|
-
"src/app.{ts,tsx,js,jsx,mts,mjs,cts,cjs}",
|
|
394
|
-
"src/extension/app.{ts,tsx,js,jsx,mts,mjs,cts,cjs}"
|
|
395
|
-
];
|
|
396
|
-
const CSS_ENTRY_GLOBS = [
|
|
397
|
-
"app.{css,scss,sass,less,styl,stylus,pcss,postcss}",
|
|
398
|
-
"styles/app.{css,scss,sass,less,styl,stylus,pcss,postcss}",
|
|
399
|
-
"src/app.{css,scss,sass,less,styl,stylus,pcss,postcss}",
|
|
400
|
-
"src/styles/app.{css,scss,sass,less,styl,stylus,pcss,postcss}"
|
|
401
|
-
];
|
|
402
529
|
const CONFIG_DEFAULTS = {
|
|
403
530
|
outDir: "./dist",
|
|
404
531
|
linter: "biome",
|
|
@@ -435,46 +562,77 @@ async function getResolvedConfig(config) {
|
|
|
435
562
|
}
|
|
436
563
|
async function resolveContext(config) {
|
|
437
564
|
const cwd = process.cwd();
|
|
438
|
-
const
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
return {};
|
|
443
|
-
}
|
|
444
|
-
};
|
|
445
|
-
const pkg = config.name && config.version ? {} : getPkg();
|
|
446
|
-
if (!config.name) config.name = pkg.name || basename(cwd);
|
|
447
|
-
const DEFAULT_VERSION = "0.0.1";
|
|
448
|
-
if (!config.version) config.version = pkg.version ?? DEFAULT_VERSION;
|
|
449
|
-
if (!config.entry) if (config.template === "theme") {
|
|
450
|
-
config.entry = {
|
|
451
|
-
js: resolveDefaultEntries(cwd, "js"),
|
|
452
|
-
css: resolveDefaultEntries(cwd, "css")
|
|
453
|
-
};
|
|
454
|
-
if (!config.entry.js || config.entry.js.length === 0) config.entry.js = resolveDefaultEntries(cwd, "js");
|
|
455
|
-
if (!config.entry.css || config.entry.css.length === 0) config.entry.css = resolveDefaultEntries(cwd, "css");
|
|
456
|
-
} else config.entry = resolveDefaultEntries(cwd, "js");
|
|
457
|
-
if (!config.devModeVarName) config.devModeVarName = DEV_MODE_VAR_NAME;
|
|
565
|
+
const pkg = config.name && config.version ? {} : getPackageMeta(cwd);
|
|
566
|
+
config.name ||= pkg.name || basename(cwd);
|
|
567
|
+
config.version ||= pkg.version || "0.0.1";
|
|
568
|
+
config.entry ||= resolveConfigEntries(config.template, cwd);
|
|
458
569
|
config.outDir = resolve(cwd, config.outDir || "./dist");
|
|
459
570
|
config.esbuildOptions ??= {};
|
|
460
571
|
config.serverConfig ??= {};
|
|
572
|
+
if (config.template === "custom-app") config.icon ??= {
|
|
573
|
+
default: resolveDefaultIcon(cwd),
|
|
574
|
+
active: resolveActiveIcon(cwd)
|
|
575
|
+
};
|
|
461
576
|
return config;
|
|
462
577
|
}
|
|
463
|
-
function
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
578
|
+
function getPackageMeta(cwd) {
|
|
579
|
+
try {
|
|
580
|
+
return JSON.parse(readFileSync(resolve(cwd, "package.json"), "utf-8"));
|
|
581
|
+
} catch {
|
|
582
|
+
return {};
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
function resolveConfigEntries(template, cwd) {
|
|
586
|
+
if (template === "theme") return {
|
|
587
|
+
js: resolveDefaultEntry(cwd, "js"),
|
|
588
|
+
css: resolveDefaultEntry(cwd, "css")
|
|
589
|
+
};
|
|
590
|
+
if (template === "custom-app") return {
|
|
591
|
+
app: resolveDefaultEntry(cwd, "js-app-only"),
|
|
592
|
+
extension: resolveDefaultEntry(cwd, "js-extension-only")
|
|
473
593
|
};
|
|
474
|
-
|
|
475
|
-
if (!firstEntry) throw new Error(type === "js" ? "No JavaScript entry found (src/app or src/index)." : "No CSS entry found (src/app, src/index, or src/styles).");
|
|
476
|
-
return firstEntry;
|
|
594
|
+
return resolveDefaultEntry(cwd, "js");
|
|
477
595
|
}
|
|
596
|
+
function resolveDefaultEntry(cwd, type) {
|
|
597
|
+
const globs = ENTRY_MAP[type];
|
|
598
|
+
for (const glob of globs) {
|
|
599
|
+
const matches = globSync(glob, {
|
|
600
|
+
cwd,
|
|
601
|
+
absolute: true
|
|
602
|
+
});
|
|
603
|
+
if (matches.length > 0 && matches[0]) return matches[0];
|
|
604
|
+
}
|
|
605
|
+
const displayType = {
|
|
606
|
+
js: "JavaScript/TypeScript",
|
|
607
|
+
css: "CSS/Stylesheet",
|
|
608
|
+
"js-app-only": "JavaScript/TypeScript (Custom App)",
|
|
609
|
+
"js-extension-only": "JavaScript/TypeScript (Extension)"
|
|
610
|
+
}[type];
|
|
611
|
+
const expectedFiles = globs.map((g) => ` - ${g}`).join("\n");
|
|
612
|
+
throw new Error(`No ${displayType} entry file found in your project.\nPlease create one of the following files:\n${expectedFiles}`);
|
|
613
|
+
}
|
|
614
|
+
function resolveDefaultIcon(cwd) {
|
|
615
|
+
for (const glob of ICON_GLOBS) {
|
|
616
|
+
const matches = globSync(glob, {
|
|
617
|
+
cwd,
|
|
618
|
+
absolute: true
|
|
619
|
+
});
|
|
620
|
+
if (matches.length > 0 && matches[0]) return readFileSync(matches[0]).toString();
|
|
621
|
+
}
|
|
622
|
+
const expectedFiles = ICON_GLOBS.map((g) => ` - ${g}`).join("\n");
|
|
623
|
+
throw new Error(`No icon file found in your project.\nPlease create one of the following files:\n${expectedFiles}`);
|
|
624
|
+
}
|
|
625
|
+
function resolveActiveIcon(cwd) {
|
|
626
|
+
for (const glob of ICON_ACTIVE_GLOBS) {
|
|
627
|
+
const matches = globSync(glob, {
|
|
628
|
+
cwd,
|
|
629
|
+
absolute: true
|
|
630
|
+
});
|
|
631
|
+
if (matches.length > 0 && matches[0]) return readFileSync(matches[0]).toString();
|
|
632
|
+
}
|
|
633
|
+
return "";
|
|
634
|
+
}
|
|
635
|
+
const getEnName = (configName) => typeof configName === "string" ? configName : configName.en;
|
|
478
636
|
|
|
479
637
|
//#endregion
|
|
480
638
|
//#region src/esbuild/format.ts
|
|
@@ -577,14 +735,14 @@ function css({ minify = false, inline = false, logger = createLogger("plugin:css
|
|
|
577
735
|
type,
|
|
578
736
|
transform: postcssModules({
|
|
579
737
|
getJSON: () => {},
|
|
580
|
-
generateScopedName: "[name]__[local]___[hash:base64:5]"
|
|
581
|
-
localsConvention: "camelCaseOnly"
|
|
738
|
+
generateScopedName: "[name]__[local]___[hash:base64:5]"
|
|
582
739
|
}, postCssPlugins)
|
|
583
740
|
}), sassPlugin({
|
|
584
741
|
filter: /\.(s[ac]ss|css)$/,
|
|
585
742
|
type,
|
|
586
743
|
async transform(css, _resolveDir, filePath) {
|
|
587
744
|
const start = performance.now();
|
|
745
|
+
logger.log("processing:", filePath, "type:", type);
|
|
588
746
|
const result = await postcss(postCssPlugins).process(css, { from: filePath });
|
|
589
747
|
logger.debug("Global CSS processed", {
|
|
590
748
|
filePath,
|
|
@@ -644,6 +802,7 @@ function runSpice(args) {
|
|
|
644
802
|
validateSpicetify(env.spicetifyBin);
|
|
645
803
|
return spawnSync(env.spicetifyBin, args, { encoding: "utf-8" });
|
|
646
804
|
}
|
|
805
|
+
const getCustomAppsDir = () => join(getSpiceDataPath(), "CustomApps");
|
|
647
806
|
const getExtensionDir = () => join(getSpiceDataPath(), "Extensions");
|
|
648
807
|
const getThemesDir = () => join(getSpiceDataPath(), "Themes");
|
|
649
808
|
async function getSpicetifyConfig() {
|
|
@@ -667,16 +826,16 @@ function validateSpicetify(bin) {
|
|
|
667
826
|
|
|
668
827
|
//#endregion
|
|
669
828
|
//#region src/esbuild/plugins/spicetifyHandlers.ts
|
|
670
|
-
const
|
|
671
|
-
const spicetifyHandler = ({ config, options, cache, logger = createLogger("plugin:spicetifyHandler") }) => ({
|
|
829
|
+
const spicetifyHandler = ({ config, options, cache, logger = createLogger("plugin:spicetify-handler") }) => ({
|
|
672
830
|
name: "spice_internal__spicetify-build-handler",
|
|
673
831
|
async setup(build) {
|
|
674
832
|
const { apply = true, copy = true, applyOnce = true, remove, outDir = "./dist" } = options;
|
|
675
833
|
let hasAppliedOnce = false;
|
|
676
834
|
const isExtension = config.template === "extension";
|
|
677
|
-
const
|
|
678
|
-
|
|
679
|
-
|
|
835
|
+
const isCustomApp = config.template === "custom-app";
|
|
836
|
+
const identifier = isExtension ? `${urlSlugify(config.name)}.js` : urlSlugify(getEnName(config.name));
|
|
837
|
+
if (SKIP_SPICETIFY) {
|
|
838
|
+
logger.info(pc.yellow("skipping spicetify operations"));
|
|
680
839
|
build.onEnd(async (result) => {
|
|
681
840
|
if (result.errors.length > 0) return;
|
|
682
841
|
if (!cache.hasChanges || cache.changed.size === 0) return;
|
|
@@ -702,38 +861,36 @@ const spicetifyHandler = ({ config, options, cache, logger = createLogger("plugi
|
|
|
702
861
|
const spiceConfig = await getSpicetifyConfig();
|
|
703
862
|
logger.debug(pc.green("Spicetify Config: "), spiceConfig);
|
|
704
863
|
if (apply) {
|
|
705
|
-
const defaultTheme = spiceConfig
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
]);
|
|
713
|
-
else runSpice([
|
|
714
|
-
"config",
|
|
715
|
-
"current_theme",
|
|
716
|
-
spiceIdentifier
|
|
717
|
-
]);
|
|
718
|
-
});
|
|
864
|
+
const defaultTheme = spiceConfig?.Setting?.current_theme || "SpicetifyDefault";
|
|
865
|
+
const spiceIdentifier = remove ? `${identifier}-` : identifier;
|
|
866
|
+
runSpice([
|
|
867
|
+
"config",
|
|
868
|
+
isExtension ? "extensions" : isCustomApp ? "custom_apps" : "current_theme",
|
|
869
|
+
spiceIdentifier
|
|
870
|
+
]);
|
|
719
871
|
if (!isExtension && !remove) {
|
|
720
|
-
const
|
|
721
|
-
runSpice([
|
|
872
|
+
const cleanup = () => {
|
|
873
|
+
if (isCustomApp) runSpice([
|
|
874
|
+
"config",
|
|
875
|
+
"custom_apps",
|
|
876
|
+
`${identifier}-`
|
|
877
|
+
]);
|
|
878
|
+
else runSpice([
|
|
722
879
|
"config",
|
|
723
880
|
"current_theme",
|
|
724
881
|
defaultTheme
|
|
725
882
|
]);
|
|
726
883
|
process.exit();
|
|
727
884
|
};
|
|
728
|
-
process.once("SIGINT",
|
|
729
|
-
process.once("SIGTERM",
|
|
885
|
+
process.once("SIGINT", cleanup);
|
|
886
|
+
process.once("SIGTERM", cleanup);
|
|
730
887
|
}
|
|
731
888
|
}
|
|
732
889
|
build.onEnd(async (result) => {
|
|
733
890
|
if (result.errors.length > 0) return;
|
|
734
891
|
if (!cache.hasChanges || cache.changed.size === 0) return;
|
|
735
892
|
const destDirs = [resolve(outDir)];
|
|
736
|
-
if (copy) destDirs.push(isExtension ? getExtensionDir() : resolve(getThemesDir(), identifier));
|
|
893
|
+
if (copy) destDirs.push(isExtension ? getExtensionDir() : isCustomApp ? resolve(getCustomAppsDir(), identifier) : resolve(getThemesDir(), identifier));
|
|
737
894
|
const tasks = [];
|
|
738
895
|
for (const filePath of cache.changed) {
|
|
739
896
|
const fileData = cache.files.get(filePath);
|
|
@@ -754,8 +911,8 @@ const spicetifyHandler = ({ config, options, cache, logger = createLogger("plugi
|
|
|
754
911
|
return;
|
|
755
912
|
}
|
|
756
913
|
if (apply && cache.hasChanges && (!applyOnce || !hasAppliedOnce)) {
|
|
757
|
-
const { stderr, status } = runSpice(["apply"]);
|
|
758
|
-
if (status !== 0) logger.error(pc.red(`${CROSS} Spicetify apply failed
|
|
914
|
+
const { stdout, stderr, status } = runSpice(["apply"]);
|
|
915
|
+
if (status !== 0) logger.error(pc.red(`${CROSS} Spicetify apply failed:`), stdout, stderr);
|
|
759
916
|
else hasAppliedOnce = true;
|
|
760
917
|
}
|
|
761
918
|
});
|
|
@@ -764,26 +921,88 @@ const spicetifyHandler = ({ config, options, cache, logger = createLogger("plugi
|
|
|
764
921
|
|
|
765
922
|
//#endregion
|
|
766
923
|
//#region src/esbuild/plugins/wrapWithLoader.ts
|
|
767
|
-
function wrapWithLoader({
|
|
924
|
+
function wrapWithLoader({ config, cache, outFiles, server, dev = false, logger = createLogger("plugin:wrapper") }) {
|
|
768
925
|
const namespace = "spice_internal__wrap-with-loader";
|
|
926
|
+
const name = typeof config.name === "string" ? config.name : config.name.en;
|
|
927
|
+
const { template: type, version } = config;
|
|
928
|
+
let previousManifestHash;
|
|
769
929
|
return {
|
|
770
930
|
name: namespace,
|
|
771
|
-
setup(build) {
|
|
772
|
-
if (build.initialOptions.write !== false) throw new Error(`[${namespace}] This plugin requires "write: false" in build options.`);
|
|
773
|
-
build.onEnd(async (res) => {
|
|
931
|
+
setup(build$3) {
|
|
932
|
+
if (build$3.initialOptions.write !== false) throw new Error(`[${namespace}] This plugin requires "write: false" in build options.`);
|
|
933
|
+
build$3.onEnd(async (res) => {
|
|
774
934
|
try {
|
|
775
935
|
if (res.errors.length > 0 || !res.outputFiles) return;
|
|
776
936
|
cache.changed.clear();
|
|
777
937
|
cache.hasChanges = false;
|
|
778
938
|
const filesChanged = [];
|
|
779
|
-
|
|
780
|
-
|
|
939
|
+
const outdir = resolve(build$3.initialOptions.outdir || "./dist");
|
|
940
|
+
const bundledCss = getBundledCss(res.outputFiles, outdir, type, dev);
|
|
941
|
+
const minify = build$3.initialOptions.minify;
|
|
942
|
+
const slug = varSlugify(`${name}_${version}`);
|
|
781
943
|
const transformPromises = res.outputFiles.map(async (file) => {
|
|
782
944
|
const isJs = file.path.endsWith(".js");
|
|
783
945
|
const isCss = file.path.endsWith(".css");
|
|
784
946
|
if (!dev && isCss && type === "extension") return;
|
|
785
|
-
const
|
|
786
|
-
const
|
|
947
|
+
const relPath = file.path.slice(outdir.length);
|
|
948
|
+
const isCustomAppExtension = type === "custom-app" && isExtensionDir(relPath);
|
|
949
|
+
let targetName;
|
|
950
|
+
if (isJs) targetName = isCustomAppExtension ? outFiles.jsExtension ?? "extension.js" : outFiles.js;
|
|
951
|
+
else if (isCss && !isCustomAppExtension) targetName = outFiles.css;
|
|
952
|
+
if (!targetName) {
|
|
953
|
+
logger.debug("Skipped file: ", file.path);
|
|
954
|
+
return;
|
|
955
|
+
}
|
|
956
|
+
const renamedPath = join(build$3.initialOptions.outdir || "./dist/", targetName);
|
|
957
|
+
if (type === "custom-app" && targetName === outFiles.js && isJs) {
|
|
958
|
+
const globalName = varSlugify(getEnName(config.name));
|
|
959
|
+
const final = (await build({
|
|
960
|
+
bundle: true,
|
|
961
|
+
write: false,
|
|
962
|
+
minify,
|
|
963
|
+
platform: "browser",
|
|
964
|
+
format: "iife",
|
|
965
|
+
globalName,
|
|
966
|
+
stdin: {
|
|
967
|
+
contents: `
|
|
968
|
+
import App from "virtual:app";
|
|
969
|
+
import React from "react";
|
|
970
|
+
|
|
971
|
+
export default function render() {
|
|
972
|
+
return <App />;
|
|
973
|
+
}
|
|
974
|
+
`,
|
|
975
|
+
loader: "tsx",
|
|
976
|
+
sourcefile: "entry.tsx"
|
|
977
|
+
},
|
|
978
|
+
plugins: [plugins.externalGlobal({ react: "Spicetify.React" }), {
|
|
979
|
+
name: "virtual-modules",
|
|
980
|
+
setup(vBuild) {
|
|
981
|
+
vBuild.onResolve({ filter: /^virtual:app$/ }, () => ({
|
|
982
|
+
path: "app",
|
|
983
|
+
namespace: "virtual"
|
|
984
|
+
}));
|
|
985
|
+
vBuild.onLoad({
|
|
986
|
+
filter: /.*/,
|
|
987
|
+
namespace: "virtual"
|
|
988
|
+
}, () => ({
|
|
989
|
+
contents: file.text,
|
|
990
|
+
loader: "js"
|
|
991
|
+
}));
|
|
992
|
+
}
|
|
993
|
+
}]
|
|
994
|
+
})).outputFiles?.[0];
|
|
995
|
+
if (!final) return;
|
|
996
|
+
let combinedCode = `${final.text}${dev ? `export default () => ${globalName}.default();` : `var render = () => ${globalName}.default();`}\n`;
|
|
997
|
+
cache.files.set(renamedPath, {
|
|
998
|
+
contents: Buffer.from(combinedCode),
|
|
999
|
+
name: targetName
|
|
1000
|
+
});
|
|
1001
|
+
cache.changed.add(renamedPath);
|
|
1002
|
+
cache.hasChanges = true;
|
|
1003
|
+
filesChanged.push(renamedPath);
|
|
1004
|
+
return;
|
|
1005
|
+
}
|
|
787
1006
|
if (!isJs) {
|
|
788
1007
|
cache.files.set(renamedPath, {
|
|
789
1008
|
name: targetName,
|
|
@@ -794,31 +1013,25 @@ function wrapWithLoader({ name, type, version, cache, outFiles, server, dev = fa
|
|
|
794
1013
|
filesChanged.push(renamedPath);
|
|
795
1014
|
return;
|
|
796
1015
|
}
|
|
797
|
-
const
|
|
798
|
-
const templateRaw = readFileSync(templateFilePath, "utf-8");
|
|
799
|
-
const minify = build.initialOptions.minify;
|
|
800
|
-
const { code: transformedTemp } = await transform(templateRaw, {
|
|
1016
|
+
const { code: transformedTemp } = await transform(readFileSync(templateWrapperFilePath, "utf-8"), {
|
|
801
1017
|
minify,
|
|
802
|
-
target: build.initialOptions.target || "es2020",
|
|
1018
|
+
target: build$3.initialOptions.target || "es2020",
|
|
803
1019
|
loader: "jsx",
|
|
804
1020
|
define: {
|
|
805
|
-
__ESBUILD__HAS_CSS: JSON.stringify(type
|
|
1021
|
+
__ESBUILD__HAS_CSS: JSON.stringify(type !== "theme"),
|
|
1022
|
+
__ESBUILD__INJECTED_CSS: JSON.stringify(bundledCss),
|
|
806
1023
|
__ESBUILD__APP_SLUG: JSON.stringify(slug),
|
|
807
1024
|
__ESBUILD__APP_TYPE: JSON.stringify(type),
|
|
808
1025
|
__ESBUILD__APP_ID: JSON.stringify(varSlugify(name)),
|
|
809
|
-
__ESBUILD__APP_VERSION: JSON.stringify(version)
|
|
810
|
-
__ESBUILD__APP_HASH: JSON.stringify("")
|
|
1026
|
+
__ESBUILD__APP_VERSION: JSON.stringify(version)
|
|
811
1027
|
}
|
|
812
1028
|
});
|
|
813
1029
|
const template = replace(transformedTemp, {
|
|
814
1030
|
"\"{{INJECT_START_COMMENT}}\"": minify ? "" : "/* --- START --- */",
|
|
815
1031
|
"\"{{INJECT_END_COMMENT}}\"": minify ? "" : "/* --- END --- */",
|
|
816
|
-
"{{INJECTED_CSS_HERE}}": bundledCss,
|
|
817
1032
|
"\"{{INJECTED_JS_HERE}}\"": file.text
|
|
818
1033
|
});
|
|
819
1034
|
const nextBuffer = Buffer.from(template);
|
|
820
|
-
const previous = cache.files.get(renamedPath);
|
|
821
|
-
if (previous?.contents && Buffer.compare(previous.contents, nextBuffer) === 0) return;
|
|
822
1035
|
cache.files.set(renamedPath, {
|
|
823
1036
|
name: targetName,
|
|
824
1037
|
contents: nextBuffer
|
|
@@ -828,6 +1041,29 @@ function wrapWithLoader({ name, type, version, cache, outFiles, server, dev = fa
|
|
|
828
1041
|
filesChanged.push(renamedPath);
|
|
829
1042
|
});
|
|
830
1043
|
await Promise.all(transformPromises);
|
|
1044
|
+
if (type === "custom-app") {
|
|
1045
|
+
const icon = config.icon;
|
|
1046
|
+
const manifestPath = join(outdir, "manifest.json");
|
|
1047
|
+
const manifest = {
|
|
1048
|
+
name: config.name,
|
|
1049
|
+
subfiles: [],
|
|
1050
|
+
subfiles_extension: ["extension.js"],
|
|
1051
|
+
icon: icon.default,
|
|
1052
|
+
"active-icon": icon.active ?? ""
|
|
1053
|
+
};
|
|
1054
|
+
const manifestString = JSON.stringify(manifest, null, 2);
|
|
1055
|
+
const currentHash = createHash("md5").update(manifestString).digest("hex");
|
|
1056
|
+
if (currentHash !== previousManifestHash) {
|
|
1057
|
+
previousManifestHash = currentHash;
|
|
1058
|
+
cache.files.set(manifestPath, {
|
|
1059
|
+
contents: Buffer.from(manifestString),
|
|
1060
|
+
name: "manifest.json"
|
|
1061
|
+
});
|
|
1062
|
+
cache.changed.add(manifestPath);
|
|
1063
|
+
cache.hasChanges = true;
|
|
1064
|
+
filesChanged.push(manifestPath);
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
831
1067
|
if (filesChanged.length > 0) server?.broadcast(filesChanged);
|
|
832
1068
|
} catch (e) {
|
|
833
1069
|
logger.error(`Error: ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -836,6 +1072,15 @@ function wrapWithLoader({ name, type, version, cache, outFiles, server, dev = fa
|
|
|
836
1072
|
}
|
|
837
1073
|
};
|
|
838
1074
|
}
|
|
1075
|
+
function isExtensionDir(relPath) {
|
|
1076
|
+
return relPath.startsWith("/extension/") || relPath.startsWith("\\extension\\");
|
|
1077
|
+
}
|
|
1078
|
+
function getBundledCss(files, outdir, type, dev) {
|
|
1079
|
+
const cssFiles = files.filter((f) => f.path.endsWith(".css"));
|
|
1080
|
+
if (!dev && type === "extension") return cssFiles.map((f) => f.text).join("");
|
|
1081
|
+
if (type === "custom-app") return cssFiles.filter((f) => isExtensionDir(f.path.slice(outdir.length))).map((f) => f.text).join("");
|
|
1082
|
+
return "";
|
|
1083
|
+
}
|
|
839
1084
|
|
|
840
1085
|
//#endregion
|
|
841
1086
|
//#region src/esbuild/plugins/index.ts
|
|
@@ -861,7 +1106,7 @@ const defaultBuildOptions = {
|
|
|
861
1106
|
target: ["es2022", "chrome120"]
|
|
862
1107
|
};
|
|
863
1108
|
const getCommonPlugins = (opts) => {
|
|
864
|
-
const { template, minify, cache,
|
|
1109
|
+
const { template, minify, cache, buildOptions, outFiles, server, dev } = opts;
|
|
865
1110
|
return [
|
|
866
1111
|
...plugins.css({
|
|
867
1112
|
minify,
|
|
@@ -875,9 +1120,7 @@ const getCommonPlugins = (opts) => {
|
|
|
875
1120
|
"react/jsx-runtime": "Spicetify.ReactJSX"
|
|
876
1121
|
}),
|
|
877
1122
|
plugins.wrapWithLoader({
|
|
878
|
-
|
|
879
|
-
version,
|
|
880
|
-
type: template,
|
|
1123
|
+
config: opts,
|
|
881
1124
|
cache,
|
|
882
1125
|
outFiles,
|
|
883
1126
|
server,
|
|
@@ -891,11 +1134,32 @@ const getCommonPlugins = (opts) => {
|
|
|
891
1134
|
plugins.buildLogger({ cache })
|
|
892
1135
|
];
|
|
893
1136
|
};
|
|
1137
|
+
function getEntryPoints(config) {
|
|
1138
|
+
if (config.template === "theme") return [config.entry.js, config.entry.css];
|
|
1139
|
+
if (config.template === "custom-app") return [config.entry.app, config.entry.extension];
|
|
1140
|
+
return [config.entry];
|
|
1141
|
+
}
|
|
1142
|
+
function getOutFiles(config) {
|
|
1143
|
+
switch (config.template) {
|
|
1144
|
+
case "custom-app": return {
|
|
1145
|
+
js: "index.js",
|
|
1146
|
+
css: "style.css",
|
|
1147
|
+
jsExtension: "extension.js",
|
|
1148
|
+
manifest: "manifest.json"
|
|
1149
|
+
};
|
|
1150
|
+
case "extension": return { js: `${urlSlugify(getEnName(config.name))}.js` };
|
|
1151
|
+
case "theme": return {
|
|
1152
|
+
js: "theme.js",
|
|
1153
|
+
css: "user.css"
|
|
1154
|
+
};
|
|
1155
|
+
default: throw new Error("Unknown template");
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
894
1158
|
|
|
895
1159
|
//#endregion
|
|
896
1160
|
//#region src/build/index.ts
|
|
897
1161
|
const logger = createLogger("build");
|
|
898
|
-
async function build$
|
|
1162
|
+
async function build$2(options) {
|
|
899
1163
|
logger.clear();
|
|
900
1164
|
logger.greeting(pc.green("Building for production..."));
|
|
901
1165
|
let ctx;
|
|
@@ -922,10 +1186,7 @@ async function build$1(options) {
|
|
|
922
1186
|
});
|
|
923
1187
|
}
|
|
924
1188
|
function getJSBuildOptions(config, options) {
|
|
925
|
-
const entryPoints = (
|
|
926
|
-
if (config.template === "theme") return [config.entry.js, config.entry.css];
|
|
927
|
-
return [config.entry];
|
|
928
|
-
})();
|
|
1189
|
+
const entryPoints = getEntryPoints(config);
|
|
929
1190
|
const minify = options.watch ? false : options.minify;
|
|
930
1191
|
const outDir = resolve(config.outDir);
|
|
931
1192
|
const cache = {
|
|
@@ -933,14 +1194,13 @@ function getJSBuildOptions(config, options) {
|
|
|
933
1194
|
changed: /* @__PURE__ */ new Set(),
|
|
934
1195
|
hasChanges: true
|
|
935
1196
|
};
|
|
936
|
-
const outFiles =
|
|
937
|
-
js: config.template === "extension" ? `${urlSlugify(config.name)}.js` : "theme.js",
|
|
938
|
-
css: config.template === "theme" ? "user.css" : null
|
|
939
|
-
};
|
|
1197
|
+
const outFiles = getOutFiles(config);
|
|
940
1198
|
const overrides = {
|
|
941
1199
|
...defaultBuildOptions,
|
|
942
1200
|
outdir: outDir,
|
|
1201
|
+
format: "esm",
|
|
943
1202
|
minify,
|
|
1203
|
+
globalName: varSlugify(getEnName(config.name)),
|
|
944
1204
|
sourcemap: false,
|
|
945
1205
|
external: [
|
|
946
1206
|
...config.esbuildOptions?.external ? config.esbuildOptions.external : [],
|
|
@@ -949,7 +1209,7 @@ function getJSBuildOptions(config, options) {
|
|
|
949
1209
|
],
|
|
950
1210
|
define: {
|
|
951
1211
|
[DEV_MODE_VAR_NAME]: "false",
|
|
952
|
-
[config.devModeVarName]: "false",
|
|
1212
|
+
...config.devModeVarName ? { [config.devModeVarName]: "false" } : {},
|
|
953
1213
|
...config.esbuildOptions.define
|
|
954
1214
|
},
|
|
955
1215
|
plugins: [...config.esbuildOptions?.plugins ? config.esbuildOptions.plugins : [], ...getCommonPlugins({
|
|
@@ -981,8 +1241,8 @@ const CLIOptionsSchema$1 = v.strictObject({
|
|
|
981
1241
|
apply: v.boolean(),
|
|
982
1242
|
copy: v.boolean()
|
|
983
1243
|
});
|
|
984
|
-
const build = new Command("build").description("Build your spicetify project").option("-a, --apply", "Apply to spicetify", false).option("-w, --watch", "Watch mode", false).option("--no-copy", "Do not copy files to spicetify").option("--no-minify", "Disable code minification").action(async (opts) => {
|
|
985
|
-
await build$
|
|
1244
|
+
const build$1 = new Command("build").description("Build your spicetify project").option("-a, --apply", "Apply to spicetify", false).option("-w, --watch", "Watch mode", false).option("--no-copy", "Do not copy files to spicetify").option("--no-minify", "Disable code minification").action(async (opts) => {
|
|
1245
|
+
await build$2(safeParse(CLIOptionsSchema$1, opts));
|
|
986
1246
|
});
|
|
987
1247
|
|
|
988
1248
|
//#endregion
|
|
@@ -1040,7 +1300,8 @@ function createPackageJSON(options) {
|
|
|
1040
1300
|
scripts: {
|
|
1041
1301
|
sc: "spicetify-creator",
|
|
1042
1302
|
dev: "spicetify-creator dev",
|
|
1043
|
-
build: "spicetify-creator build"
|
|
1303
|
+
build: "spicetify-creator build",
|
|
1304
|
+
"update-types": "spicetify-creator update-types"
|
|
1044
1305
|
},
|
|
1045
1306
|
dependencies: {},
|
|
1046
1307
|
devDependencies: { "@spicetify/creator": env.isInternal ? "link:@spicetify/creator" : "latest" }
|
|
@@ -1085,9 +1346,10 @@ function validateProjectName(name) {
|
|
|
1085
1346
|
|
|
1086
1347
|
//#endregion
|
|
1087
1348
|
//#region src/create/template.ts
|
|
1088
|
-
const ext = (lang) => lang === "ts" ?
|
|
1349
|
+
const ext = (lang, jsx = false) => lang === "ts" ? `ts${jsx ? "x" : ""}` : `js${jsx ? "x" : ""}`;
|
|
1089
1350
|
const kv = ({ name, language, framework, linter, packageManager, template }) => ({
|
|
1090
1351
|
"{{project-name}}": name,
|
|
1352
|
+
"{{project-url}}": `/${urlSlugify(name)}`,
|
|
1091
1353
|
"{{framework}}": framework,
|
|
1092
1354
|
"{{linter}}": linter,
|
|
1093
1355
|
"{{package-manager}}": packageManager,
|
|
@@ -1104,36 +1366,60 @@ const kv = ({ name, language, framework, linter, packageManager, template }) =>
|
|
|
1104
1366
|
const action = { modify(c, opts) {
|
|
1105
1367
|
return replace(c, kv(opts));
|
|
1106
1368
|
} };
|
|
1107
|
-
const
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1369
|
+
const SHARED_FILES = (opts) => {
|
|
1370
|
+
const isShared = true;
|
|
1371
|
+
const files = [
|
|
1372
|
+
{
|
|
1373
|
+
from: "README.template.md",
|
|
1374
|
+
to: "README.md",
|
|
1375
|
+
action,
|
|
1376
|
+
isShared
|
|
1377
|
+
},
|
|
1378
|
+
{
|
|
1379
|
+
from: "DOT-gitignore",
|
|
1380
|
+
to: ".gitignore",
|
|
1381
|
+
isShared: true
|
|
1382
|
+
},
|
|
1383
|
+
{
|
|
1384
|
+
from: `spice.config.${ext(opts.language)}`,
|
|
1385
|
+
to: `spice.config.${ext(opts.language)}`,
|
|
1386
|
+
action,
|
|
1387
|
+
isShared
|
|
1388
|
+
}
|
|
1389
|
+
];
|
|
1390
|
+
if (opts.template === "custom-app") {
|
|
1391
|
+
files.push({
|
|
1392
|
+
from: `css/app.module.scss`,
|
|
1393
|
+
to: `src/css/app.module.scss`,
|
|
1394
|
+
action,
|
|
1395
|
+
isShared
|
|
1396
|
+
});
|
|
1397
|
+
files.push({
|
|
1398
|
+
from: `icon.svg`,
|
|
1399
|
+
to: `src/icon.svg`,
|
|
1400
|
+
action,
|
|
1401
|
+
isShared
|
|
1402
|
+
});
|
|
1403
|
+
files.push({
|
|
1404
|
+
from: `icon-active.svg`,
|
|
1405
|
+
to: `src/icon-active.svg`,
|
|
1406
|
+
action,
|
|
1407
|
+
isShared
|
|
1408
|
+
});
|
|
1409
|
+
files.push({
|
|
1410
|
+
from: "app.css",
|
|
1411
|
+
to: "src/extension/app.css",
|
|
1412
|
+
action,
|
|
1413
|
+
isShared
|
|
1414
|
+
});
|
|
1415
|
+
} else files.push({
|
|
1126
1416
|
from: "app.css",
|
|
1127
1417
|
to: "src/app.css",
|
|
1128
1418
|
action,
|
|
1129
|
-
isShared
|
|
1130
|
-
}
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
to: "src/types/css.d.ts",
|
|
1134
|
-
isShared: true
|
|
1135
|
-
}] : []
|
|
1136
|
-
];
|
|
1419
|
+
isShared
|
|
1420
|
+
});
|
|
1421
|
+
return files;
|
|
1422
|
+
};
|
|
1137
1423
|
const LANGUAGE_FILES = {
|
|
1138
1424
|
js: [{
|
|
1139
1425
|
from: "jsconfig.json",
|
|
@@ -1147,24 +1433,36 @@ const LANGUAGE_FILES = {
|
|
|
1147
1433
|
}]
|
|
1148
1434
|
};
|
|
1149
1435
|
const FRAMEWORKS = {
|
|
1150
|
-
react: ({ language }) =>
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
}
|
|
1436
|
+
react: ({ language, template }) => {
|
|
1437
|
+
const react = [{
|
|
1438
|
+
from: `src/app.${ext(language, true)}`,
|
|
1439
|
+
to: `src/app.${ext(language, true)}`,
|
|
1440
|
+
action
|
|
1441
|
+
}, {
|
|
1442
|
+
from: `src/components/Onboarding.${ext(language, true)}`,
|
|
1443
|
+
to: `src/components/Onboarding.${ext(language, true)}`,
|
|
1444
|
+
action
|
|
1445
|
+
}];
|
|
1446
|
+
if (template === "custom-app") react.push({
|
|
1447
|
+
from: `src/extension/index.${ext(language, true)}`,
|
|
1448
|
+
to: `src/extension/index.${ext(language, true)}`,
|
|
1449
|
+
action
|
|
1450
|
+
});
|
|
1451
|
+
return react;
|
|
1452
|
+
},
|
|
1453
|
+
vanilla: ({ language, template }) => {
|
|
1454
|
+
const vanilla = [{
|
|
1455
|
+
from: `src/app.${ext(language)}`,
|
|
1456
|
+
to: `src/app.${ext(language)}`,
|
|
1457
|
+
action
|
|
1458
|
+
}, {
|
|
1459
|
+
from: `src/components/Onboarding.${ext(language)}`,
|
|
1460
|
+
to: `src/components/Onboarding.${ext(language)}`,
|
|
1461
|
+
action
|
|
1462
|
+
}];
|
|
1463
|
+
if (template === "custom-app") throw new Error("vanilla doesn't exist for custom-app");
|
|
1464
|
+
return vanilla;
|
|
1465
|
+
}
|
|
1168
1466
|
};
|
|
1169
1467
|
const LINTERS = {
|
|
1170
1468
|
biome: [{
|
|
@@ -1177,23 +1475,25 @@ const LINTERS = {
|
|
|
1177
1475
|
to: `eslint.config.${ext(language)}`
|
|
1178
1476
|
}],
|
|
1179
1477
|
oxlint: [{
|
|
1180
|
-
from: "
|
|
1478
|
+
from: "DOT-oxlintrc.json",
|
|
1181
1479
|
to: ".oxlintrc.json",
|
|
1182
1480
|
isShared: true
|
|
1183
1481
|
}]
|
|
1184
1482
|
};
|
|
1483
|
+
const getFiles = (options) => {
|
|
1484
|
+
const resolve = (slice) => typeof slice === "function" ? slice(options) : slice ?? [];
|
|
1485
|
+
return [
|
|
1486
|
+
...resolve(SHARED_FILES),
|
|
1487
|
+
...resolve(LANGUAGE_FILES[options.language]),
|
|
1488
|
+
...resolve(FRAMEWORKS[options.framework]),
|
|
1489
|
+
...resolve(LINTERS[options.linter])
|
|
1490
|
+
];
|
|
1491
|
+
};
|
|
1185
1492
|
function setupTemplateFiles(options, targetDir) {
|
|
1186
|
-
const { template, language, framework
|
|
1493
|
+
const { template, language, framework } = options;
|
|
1187
1494
|
const templateRoot = dist(`templates/${template}`, import.meta.url);
|
|
1188
1495
|
const fromDir = join(templateRoot, language, framework);
|
|
1189
|
-
|
|
1190
|
-
const files = [
|
|
1191
|
-
...resolve(COMMON_FILES),
|
|
1192
|
-
...resolve(LANGUAGE_FILES[language]),
|
|
1193
|
-
...resolve(FRAMEWORKS[framework]),
|
|
1194
|
-
...resolve(LINTERS[linter])
|
|
1195
|
-
];
|
|
1196
|
-
for (const file of files) {
|
|
1496
|
+
for (const file of getFiles(options)) {
|
|
1197
1497
|
const src = (() => {
|
|
1198
1498
|
if (file.isGlobal) return join(templateRoot, file.from);
|
|
1199
1499
|
if (file.isShared) return join(templateRoot, "shared", file.from);
|
|
@@ -1291,6 +1591,35 @@ function tryGitInit(root) {
|
|
|
1291
1591
|
}
|
|
1292
1592
|
}
|
|
1293
1593
|
|
|
1594
|
+
//#endregion
|
|
1595
|
+
//#region src/utils/update-types.ts
|
|
1596
|
+
const downloads = { spicetify: {
|
|
1597
|
+
from: "https://raw.githubusercontent.com/spicetify/cli/main/globals.d.ts",
|
|
1598
|
+
to: "./src/types/spicetify.d.ts",
|
|
1599
|
+
action: (content) => content.replace("const React: any;", "const React: typeof import(\"react\");").replace("const ReactDOM: any;", "const ReactDOM: typeof import(\"react-dom/client\");").replace("const ReactDOMServer: any;", "const ReactDOMServer: typeof import(\"react-dom/server\");")
|
|
1600
|
+
} };
|
|
1601
|
+
async function updateTypes(isUpdating = true) {
|
|
1602
|
+
const s = spinner();
|
|
1603
|
+
s.start(`${isUpdating ? "Updating" : "Creating"} Types...`);
|
|
1604
|
+
await Promise.all(Object.entries(downloads).map(([name, download]) => downloadFile(name, download, isUpdating)));
|
|
1605
|
+
s.stop(`${isUpdating ? "Updated" : "Created"} Types!`);
|
|
1606
|
+
}
|
|
1607
|
+
async function downloadFile(name, { from, to, action }, isUpdating) {
|
|
1608
|
+
try {
|
|
1609
|
+
const res = await fetch(from);
|
|
1610
|
+
if (!res.ok) throw new Error(`HTTP Error: ${res.status} ${res.statusText}`);
|
|
1611
|
+
let text = await res.text();
|
|
1612
|
+
if (action) text = await action(text);
|
|
1613
|
+
await mkdir(dirname$1(to), { recursive: true });
|
|
1614
|
+
await writeFile$1(to, text, "utf8");
|
|
1615
|
+
const actionLog = isUpdating ? "updated" : "created";
|
|
1616
|
+
logger$2.log(`${name}.d.ts ${actionLog} (${from} -> ${to})`);
|
|
1617
|
+
} catch (e) {
|
|
1618
|
+
log.error(`${name} failed`);
|
|
1619
|
+
console.error(e);
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1294
1623
|
//#endregion
|
|
1295
1624
|
//#region src/create/index.ts
|
|
1296
1625
|
const isOnline = await getOnline();
|
|
@@ -1337,7 +1666,8 @@ async function createProject(cwd, options) {
|
|
|
1337
1666
|
options: templateOptions
|
|
1338
1667
|
});
|
|
1339
1668
|
},
|
|
1340
|
-
framework: async () => {
|
|
1669
|
+
framework: async ({ results: { template } }) => {
|
|
1670
|
+
if (template === "custom-app") return "react";
|
|
1341
1671
|
if (options.framework) return options.framework;
|
|
1342
1672
|
return await p.select({
|
|
1343
1673
|
message: "Select which framework you want to chose",
|
|
@@ -1407,6 +1737,7 @@ async function create$1(cwd, options) {
|
|
|
1407
1737
|
try {
|
|
1408
1738
|
mkdirp(cwd);
|
|
1409
1739
|
setupTemplateFiles(options, cwd);
|
|
1740
|
+
await updateTypes(false);
|
|
1410
1741
|
const pkgJSON = createPackageJSON(options);
|
|
1411
1742
|
writePackageJSON(pkgJSON, cwd);
|
|
1412
1743
|
chdir(cwd);
|
|
@@ -1617,7 +1948,7 @@ const injectHMRExtension = async (rootLink, wsLink, outFiles) => {
|
|
|
1617
1948
|
const extName = `sc-live-reload-helper.js`;
|
|
1618
1949
|
const spiceConfig = await getSpicetifyConfig();
|
|
1619
1950
|
const cleanup = () => {
|
|
1620
|
-
|
|
1951
|
+
logger$2.info(`Removing Live reload extension...`);
|
|
1621
1952
|
try {
|
|
1622
1953
|
runSpice([
|
|
1623
1954
|
"config",
|
|
@@ -1625,18 +1956,16 @@ const injectHMRExtension = async (rootLink, wsLink, outFiles) => {
|
|
|
1625
1956
|
`${extName}-`
|
|
1626
1957
|
]);
|
|
1627
1958
|
runSpice(["apply"]);
|
|
1628
|
-
logger$2.
|
|
1959
|
+
logger$2.info(pc.green(`${CHECK} Cleanup successful.`));
|
|
1629
1960
|
} catch (e) {
|
|
1630
|
-
|
|
1961
|
+
logger$2.error(pc.red(`${CROSS} Cleanup failed: `), e);
|
|
1631
1962
|
}
|
|
1632
1963
|
process.exit();
|
|
1633
1964
|
};
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
process.on("SIGTERM", cleanup);
|
|
1637
|
-
}
|
|
1965
|
+
process.on("SIGINT", cleanup);
|
|
1966
|
+
process.on("SIGTERM", cleanup);
|
|
1638
1967
|
try {
|
|
1639
|
-
logger$2.debug(`
|
|
1968
|
+
logger$2.debug(`Preparing Live reload extension...`);
|
|
1640
1969
|
const destDir = getExtensionDir();
|
|
1641
1970
|
mkdirp(destDir);
|
|
1642
1971
|
const outDir = resolve(destDir, extName);
|
|
@@ -1663,6 +1992,114 @@ const injectHMRExtension = async (rootLink, wsLink, outFiles) => {
|
|
|
1663
1992
|
logger$2.error(pc.red(`${CROSS} Failed to inject HMR helper: ${err instanceof Error ? err.message : String(err)}`));
|
|
1664
1993
|
}
|
|
1665
1994
|
};
|
|
1995
|
+
const injectHMRCustomApp = async (rootLink, wsLink, outFiles, config) => {
|
|
1996
|
+
if (config.template !== "custom-app") throw new Error("only supports custom-app templates");
|
|
1997
|
+
const identifier = getEnName(config.name);
|
|
1998
|
+
const spiceConfig = await getSpicetifyConfig();
|
|
1999
|
+
const customAppId = urlSlugify(identifier);
|
|
2000
|
+
runSpice([
|
|
2001
|
+
"config",
|
|
2002
|
+
"extensions",
|
|
2003
|
+
"sc-live-reload-helper.js-"
|
|
2004
|
+
]);
|
|
2005
|
+
const cleanup = () => {
|
|
2006
|
+
logger$2.info(`Removing Live reload custom-app...`);
|
|
2007
|
+
try {
|
|
2008
|
+
runSpice([
|
|
2009
|
+
"config",
|
|
2010
|
+
"custom_apps",
|
|
2011
|
+
`${customAppId}-`
|
|
2012
|
+
]);
|
|
2013
|
+
runSpice(["apply"]);
|
|
2014
|
+
logger$2.info(pc.green(`${CHECK} Cleanup successful.`));
|
|
2015
|
+
} catch (e) {
|
|
2016
|
+
logger$2.error(pc.red(`${CROSS} Cleanup failed: `), e);
|
|
2017
|
+
}
|
|
2018
|
+
process.exit();
|
|
2019
|
+
};
|
|
2020
|
+
process.on("SIGINT", cleanup);
|
|
2021
|
+
process.on("SIGTERM", cleanup);
|
|
2022
|
+
try {
|
|
2023
|
+
logger$2.debug(`Preparing Live reload custom-app...`);
|
|
2024
|
+
const destDir = resolve(getCustomAppsDir(), customAppId);
|
|
2025
|
+
mkdirp(destDir);
|
|
2026
|
+
const indexJsPath = resolve(destDir, "index.js");
|
|
2027
|
+
const extensionJsPath = resolve(destDir, outFiles.jsExtension ?? "extension.js");
|
|
2028
|
+
writeFileSync(indexJsPath, `const React = Spicetify.React;
|
|
2029
|
+
const waitForImport = () => {
|
|
2030
|
+
return import("${rootLink}/files/${outFiles.js}")
|
|
2031
|
+
.then((mod) => mod.default || mod.render)
|
|
2032
|
+
.catch((err) => {
|
|
2033
|
+
console.error("Failed to import app:", err);
|
|
2034
|
+
return null;
|
|
2035
|
+
});
|
|
2036
|
+
};
|
|
2037
|
+
|
|
2038
|
+
const AppWrapper = ({ appPromise }) => {
|
|
2039
|
+
const [App, setApp] = React.useState(null);
|
|
2040
|
+
|
|
2041
|
+
React.useEffect(() => {
|
|
2042
|
+
let mounted = true;
|
|
2043
|
+
|
|
2044
|
+
appPromise.then((app) => {
|
|
2045
|
+
if (mounted && app) {
|
|
2046
|
+
setApp(() => app);
|
|
2047
|
+
}
|
|
2048
|
+
});
|
|
2049
|
+
|
|
2050
|
+
return () => {
|
|
2051
|
+
mounted = false;
|
|
2052
|
+
};
|
|
2053
|
+
}, [appPromise]);
|
|
2054
|
+
|
|
2055
|
+
if (!App) {
|
|
2056
|
+
return React.createElement(
|
|
2057
|
+
"div",
|
|
2058
|
+
{ className: "loading" },
|
|
2059
|
+
"Loading app..."
|
|
2060
|
+
);
|
|
2061
|
+
}
|
|
2062
|
+
|
|
2063
|
+
return React.createElement(App);
|
|
2064
|
+
};
|
|
2065
|
+
|
|
2066
|
+
const render = () => {
|
|
2067
|
+
const appPromise = waitForImport();
|
|
2068
|
+
return React.createElement(AppWrapper, { appPromise });
|
|
2069
|
+
};
|
|
2070
|
+
`);
|
|
2071
|
+
const { code: extensionCode } = await transform(readFileSync(liveReloadFilePath, "utf8"), {
|
|
2072
|
+
loader: "js",
|
|
2073
|
+
define: {
|
|
2074
|
+
_SERVER_URL: JSON.stringify(rootLink),
|
|
2075
|
+
_HOT_RELOAD_LINK: JSON.stringify(wsLink),
|
|
2076
|
+
_JS_PATH: JSON.stringify(`/files/${outFiles.jsExtension ?? "extension.js"}`),
|
|
2077
|
+
_CSS_PATH: JSON.stringify(outFiles.css ? `/files/${outFiles.css}` : `/files/app.css`)
|
|
2078
|
+
}
|
|
2079
|
+
});
|
|
2080
|
+
writeFileSync(extensionJsPath, extensionCode);
|
|
2081
|
+
const manifestPath = resolve(destDir, "manifest.json");
|
|
2082
|
+
const manifest = {
|
|
2083
|
+
name: identifier,
|
|
2084
|
+
subfiles: [],
|
|
2085
|
+
subfiles_extension: [outFiles.jsExtension ?? "extension.js"],
|
|
2086
|
+
icon: config.icon.default,
|
|
2087
|
+
"active-icon": config.icon.active ?? config.icon.default
|
|
2088
|
+
};
|
|
2089
|
+
writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
2090
|
+
if (spiceConfig) {
|
|
2091
|
+
runSpice([
|
|
2092
|
+
"config",
|
|
2093
|
+
"custom_apps",
|
|
2094
|
+
customAppId
|
|
2095
|
+
]);
|
|
2096
|
+
runSpice(["apply"]);
|
|
2097
|
+
}
|
|
2098
|
+
logger$2.debug(pc.green(`${CHECK} Live reload custom-app injected successfully.`));
|
|
2099
|
+
} catch (err) {
|
|
2100
|
+
logger$2.error(pc.red(`${CROSS} Failed to inject HMR custom-app: ${err instanceof Error ? err.message : String(err)}`));
|
|
2101
|
+
}
|
|
2102
|
+
};
|
|
1666
2103
|
|
|
1667
2104
|
//#endregion
|
|
1668
2105
|
//#region src/dev/index.ts
|
|
@@ -1685,11 +2122,9 @@ async function dev$1(options) {
|
|
|
1685
2122
|
port: options.port ?? config.serverConfig.port
|
|
1686
2123
|
});
|
|
1687
2124
|
await server.start();
|
|
1688
|
-
const outFiles =
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
};
|
|
1692
|
-
await injectHMRExtension(server.link, server.wsLink, outFiles);
|
|
2125
|
+
const outFiles = getOutFiles(config);
|
|
2126
|
+
if (config.template === "custom-app") await injectHMRCustomApp(server.link, server.wsLink, outFiles, config);
|
|
2127
|
+
else await injectHMRExtension(server.link, server.wsLink, outFiles);
|
|
1693
2128
|
ctx = await context(getJSDevOptions(config, {
|
|
1694
2129
|
...options,
|
|
1695
2130
|
outFiles,
|
|
@@ -1712,10 +2147,7 @@ async function dev$1(options) {
|
|
|
1712
2147
|
});
|
|
1713
2148
|
}
|
|
1714
2149
|
function getJSDevOptions(config, options) {
|
|
1715
|
-
const entryPoints = (
|
|
1716
|
-
if (config.template === "theme") return [config.entry.js, config.entry.css];
|
|
1717
|
-
return [config.entry];
|
|
1718
|
-
})();
|
|
2150
|
+
const entryPoints = getEntryPoints(config);
|
|
1719
2151
|
const minify = false;
|
|
1720
2152
|
const cache = {
|
|
1721
2153
|
files: /* @__PURE__ */ new Map(),
|
|
@@ -1734,7 +2166,7 @@ function getJSDevOptions(config, options) {
|
|
|
1734
2166
|
],
|
|
1735
2167
|
define: {
|
|
1736
2168
|
[DEV_MODE_VAR_NAME]: "true",
|
|
1737
|
-
[config.devModeVarName]: "true",
|
|
2169
|
+
...config.devModeVarName ? { [config.devModeVarName]: "true" } : {},
|
|
1738
2170
|
...config.esbuildOptions.define
|
|
1739
2171
|
},
|
|
1740
2172
|
plugins: [...config.esbuildOptions?.plugins ? config.esbuildOptions.plugins : [], ...getCommonPlugins({
|
|
@@ -1743,7 +2175,9 @@ function getJSDevOptions(config, options) {
|
|
|
1743
2175
|
cache,
|
|
1744
2176
|
buildOptions: {
|
|
1745
2177
|
copy: true,
|
|
1746
|
-
|
|
2178
|
+
apply: false,
|
|
2179
|
+
applyOnce: false,
|
|
2180
|
+
remove: config.template !== "custom-app",
|
|
1747
2181
|
outDir
|
|
1748
2182
|
},
|
|
1749
2183
|
dev: true,
|
|
@@ -1765,12 +2199,17 @@ const dev = new Command("dev").description("Develop your spicetify project").opt
|
|
|
1765
2199
|
await dev$1(safeParse(CLIOptionsSchema, opts));
|
|
1766
2200
|
});
|
|
1767
2201
|
|
|
2202
|
+
//#endregion
|
|
2203
|
+
//#region src/commands/update-types.ts
|
|
2204
|
+
const update_types = new Command("update-types").description("Update Spicetify Types").action(async () => {
|
|
2205
|
+
await updateTypes();
|
|
2206
|
+
});
|
|
2207
|
+
|
|
1768
2208
|
//#endregion
|
|
1769
2209
|
//#region src/bin.ts
|
|
1770
2210
|
logger$2.debug(`Env: ${JSON.stringify(env, null, 2)}\n`);
|
|
1771
2211
|
const command = new Command();
|
|
1772
|
-
create.alias("init");
|
|
1773
|
-
command.addCommand(create).addCommand(build).addCommand(dev);
|
|
2212
|
+
command.addCommand(create.alias("init")).addCommand(build$1).addCommand(dev).addCommand(update_types);
|
|
1774
2213
|
command.parse();
|
|
1775
2214
|
|
|
1776
2215
|
//#endregion
|