@spicemod/creator 0.0.26 → 0.0.28

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.
Files changed (40) hide show
  1. package/dist/bin.mjs +188 -150
  2. package/dist/templates/custom-app/shared/spice.config.js +6 -0
  3. package/dist/templates/custom-app/shared/spice.config.ts +6 -0
  4. package/dist/templates/custom-app/shared/tsconfig.app.json +33 -0
  5. package/dist/templates/custom-app/shared/tsconfig.node.json +25 -0
  6. package/dist/templates/custom-app/ts/react/src/extension/index.tsx +1 -1
  7. package/dist/templates/extension/js/react/src/app.jsx +1 -1
  8. package/dist/templates/extension/shared/spice.config.js +6 -0
  9. package/dist/templates/extension/shared/spice.config.ts +6 -0
  10. package/dist/templates/extension/shared/tsconfig.app.json +33 -0
  11. package/dist/templates/extension/shared/tsconfig.node.json +25 -0
  12. package/dist/templates/extension/ts/react/src/app.tsx +1 -1
  13. package/dist/templates/hmrCustomApp.js +284 -0
  14. package/dist/templates/liveReload.js +298 -45
  15. package/dist/templates/theme/js/react/src/app.jsx +1 -1
  16. package/dist/templates/theme/shared/spice.config.js +6 -0
  17. package/dist/templates/theme/shared/spice.config.ts +6 -0
  18. package/dist/templates/theme/shared/tsconfig.app.json +33 -0
  19. package/dist/templates/theme/shared/tsconfig.node.json +25 -0
  20. package/dist/templates/theme/ts/react/src/app.tsx +1 -1
  21. package/package.json +1 -1
  22. package/templates/custom-app/shared/spice.config.js +6 -0
  23. package/templates/custom-app/shared/spice.config.ts +6 -0
  24. package/templates/custom-app/shared/tsconfig.app.json +33 -0
  25. package/templates/custom-app/shared/tsconfig.node.json +25 -0
  26. package/templates/custom-app/ts/react/src/extension/index.tsx +1 -1
  27. package/templates/extension/js/react/src/app.jsx +1 -1
  28. package/templates/extension/shared/spice.config.js +6 -0
  29. package/templates/extension/shared/spice.config.ts +6 -0
  30. package/templates/extension/shared/tsconfig.app.json +33 -0
  31. package/templates/extension/shared/tsconfig.node.json +25 -0
  32. package/templates/extension/ts/react/src/app.tsx +1 -1
  33. package/templates/hmrCustomApp.js +284 -0
  34. package/templates/liveReload.js +298 -45
  35. package/templates/theme/js/react/src/app.jsx +1 -1
  36. package/templates/theme/shared/spice.config.js +6 -0
  37. package/templates/theme/shared/spice.config.ts +6 -0
  38. package/templates/theme/shared/tsconfig.app.json +33 -0
  39. package/templates/theme/shared/tsconfig.node.json +25 -0
  40. package/templates/theme/ts/react/src/app.tsx +1 -1
package/dist/bin.mjs CHANGED
@@ -3,7 +3,7 @@ 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
5
  import { build, context, transform } from "esbuild";
6
- import { createReadStream, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
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";
9
9
  import { URL as URL$1, fileURLToPath } from "node:url";
@@ -13,7 +13,7 @@ import * as p from "@clack/prompts";
13
13
  import { cancel, log, spinner } from "@clack/prompts";
14
14
  import pc from "picocolors";
15
15
  import "dotenv/config";
16
- import { readFile, writeFile } from "node:fs/promises";
16
+ import { readFile, unlink, writeFile } from "node:fs/promises";
17
17
  import { parse } from "ini";
18
18
  import { gzipSync } from "node:zlib";
19
19
  import postcssMinify from "@csstools/postcss-minify";
@@ -137,13 +137,14 @@ const frameworks = frameworkTypes.map((name) => ({
137
137
  }));
138
138
  const frameworkOptions = toOptions(frameworks);
139
139
  const liveReloadFilePath = dist(`templates/liveReload.js`, import.meta.url);
140
+ const hmrCustomAppFilePath = dist(`templates/hmrCustomApp.js`, import.meta.url);
140
141
  const templateWrapperFilePath = dist("templates/wrapper.js", import.meta.url);
141
142
  const customAppEntryFilePath = dist("templates/customAppEntry.js", import.meta.url);
142
143
 
143
144
  //#endregion
144
145
  //#region package.json
145
146
  var name = "@spicemod/creator";
146
- var version = "0.0.26";
147
+ var version = "0.0.28";
147
148
 
148
149
  //#endregion
149
150
  //#region src/utils/common.ts
@@ -779,15 +780,48 @@ function getTime(start) {
779
780
  }
780
781
 
781
782
  //#endregion
782
- //#region src/esbuild/plugins/cleanDist.ts
783
- const cleanDist = (outDir) => ({
784
- name: "spice_internal__clean-dist",
783
+ //#region src/esbuild/plugins/buildErrorReporter.ts
784
+ function buildErrorReporter({ server }) {
785
+ return {
786
+ name: "spice_internal__build-error-reporter",
787
+ setup(build) {
788
+ build.onEnd(async (result) => {
789
+ const errors = result.errors;
790
+ const warnings = result.warnings;
791
+ if (errors.length === 0) {
792
+ server?.broadcast({
793
+ type: "build-success",
794
+ errors: [],
795
+ warnings
796
+ });
797
+ return;
798
+ }
799
+ server?.broadcast({
800
+ type: "build-error",
801
+ errors,
802
+ warnings
803
+ });
804
+ });
805
+ }
806
+ };
807
+ }
808
+
809
+ //#endregion
810
+ //#region src/esbuild/plugins/clean.ts
811
+ const clean = (cache, logger = createLogger("plugin:clean")) => ({
812
+ name: "clean",
785
813
  setup(build) {
814
+ const outdir = resolve(build.initialOptions.outdir || "./dist");
786
815
  build.onStart(() => {
787
- if (existsSync(outDir)) rmSync(outDir, {
788
- recursive: true,
789
- force: true
790
- });
816
+ if (fs.existsSync(outdir)) try {
817
+ fs.rmSync(outdir, {
818
+ recursive: true,
819
+ force: true
820
+ });
821
+ logger.log(`Removed: ${outdir}`);
822
+ } catch (err) {
823
+ logger.error(`Error removing ${outdir}:`, err);
824
+ }
791
825
  });
792
826
  }
793
827
  });
@@ -847,6 +881,37 @@ const externalGlobal = (externals, namespace = "spicetify-global") => {
847
881
 
848
882
  //#endregion
849
883
  //#region src/esbuild/plugins/spicetifyHandlers.ts
884
+ async function removeFile(logger, targetPath) {
885
+ try {
886
+ await unlink(targetPath);
887
+ logger.debug(pc.green(`${CHECK} Removed: ${targetPath}`));
888
+ } catch (err) {
889
+ if (err.code !== "ENOENT") logger.error(pc.red(`${CROSS} Failed to remove file: ${err instanceof Error ? err.message : String(err)}`));
890
+ }
891
+ }
892
+ async function copyFiles(logger, destDirs, cacheFiles) {
893
+ const tasks = [];
894
+ for (const filePath of cacheFiles.keys()) {
895
+ const fileData = cacheFiles.get(filePath);
896
+ if (!fileData) continue;
897
+ for (const destDir of destDirs) {
898
+ const targetPath = resolve(destDir, basename(filePath));
899
+ tasks.push((async () => {
900
+ await mkdirp(destDir);
901
+ await writeFile(targetPath, fileData.contents);
902
+ })());
903
+ }
904
+ }
905
+ await Promise.all(tasks);
906
+ }
907
+ async function removeDeletedFiles(logger, destDirs, removedFiles) {
908
+ const tasks = [];
909
+ for (const removedPath of removedFiles) for (const destDir of destDirs) {
910
+ const targetPath = resolve(destDir, basename(removedPath));
911
+ tasks.push(removeFile(logger, targetPath));
912
+ }
913
+ await Promise.all(tasks);
914
+ }
850
915
  const spicetifyHandler = ({ config, options, cache, logger = createLogger("plugin:spicetify-handler") }) => ({
851
916
  name: "spice_internal__spicetify-build-handler",
852
917
  async setup(build) {
@@ -855,82 +920,58 @@ const spicetifyHandler = ({ config, options, cache, logger = createLogger("plugi
855
920
  const isExtension = config.template === "extension";
856
921
  const isCustomApp = config.template === "custom-app";
857
922
  const identifier = isExtension ? `${urlSlugify(config.name)}.js` : urlSlugify(getEnName(config.name));
858
- if (env.skipSpicetify) {
859
- logger.info(pc.yellow("skipping spicetify operations"));
860
- build.onEnd(async (result) => {
861
- if (result.errors.length > 0) return;
862
- if (!cache.hasChanges || cache.changed.size === 0) return;
863
- const tasks = [];
864
- for (const filePath of cache.changed) {
865
- const fileData = cache.files.get(filePath);
866
- if (!fileData) continue;
867
- const targetPath = resolve(outDir, basename(filePath));
868
- tasks.push((async () => {
869
- await mkdirp(outDir);
870
- await writeFile(targetPath, fileData.contents);
871
- })());
872
- }
873
- try {
874
- await Promise.all(tasks);
875
- logger.debug(pc.green(`${CHECK} Built files written to ${outDir}`));
876
- } catch (err) {
877
- logger.error(pc.red(`${CROSS} Failed to write files: ${err instanceof Error ? err.message : String(err)}`));
923
+ const getDestDirs = () => {
924
+ const dirs = [resolve(outDir)];
925
+ if (copy) dirs.push(isExtension ? getExtensionDir() : isCustomApp ? resolve(getCustomAppsDir(), identifier) : resolve(getThemesDir(), identifier));
926
+ return dirs;
927
+ };
928
+ if (env.skipSpicetify) logger.info(pc.yellow("Skipping Spicetify operations..."));
929
+ else {
930
+ const spiceConfig = await getSpicetifyConfig();
931
+ logger.debug(pc.green("Spicetify Config: "), spiceConfig);
932
+ if (apply) {
933
+ const defaultTheme = spiceConfig?.Setting?.current_theme || "SpicetifyDefault";
934
+ const spiceIdentifier = remove ? `${identifier}-` : identifier;
935
+ runSpice([
936
+ "config",
937
+ isExtension ? "extensions" : isCustomApp ? "custom_apps" : "current_theme",
938
+ spiceIdentifier
939
+ ]);
940
+ if (!isExtension && !remove) {
941
+ const cleanup = () => {
942
+ if (isCustomApp) runSpice([
943
+ "config",
944
+ "custom_apps",
945
+ `${identifier}-`
946
+ ]);
947
+ else runSpice([
948
+ "config",
949
+ "current_theme",
950
+ defaultTheme
951
+ ]);
952
+ process.exit();
953
+ };
954
+ process.once("SIGINT", cleanup);
955
+ process.once("SIGTERM", cleanup);
878
956
  }
879
- });
880
- return;
881
- }
882
- const spiceConfig = await getSpicetifyConfig();
883
- logger.debug(pc.green("Spicetify Config: "), spiceConfig);
884
- if (apply) {
885
- const defaultTheme = spiceConfig?.Setting?.current_theme || "SpicetifyDefault";
886
- const spiceIdentifier = remove ? `${identifier}-` : identifier;
887
- runSpice([
888
- "config",
889
- isExtension ? "extensions" : isCustomApp ? "custom_apps" : "current_theme",
890
- spiceIdentifier
891
- ]);
892
- if (!isExtension && !remove) {
893
- const cleanup = () => {
894
- if (isCustomApp) runSpice([
895
- "config",
896
- "custom_apps",
897
- `${identifier}-`
898
- ]);
899
- else runSpice([
900
- "config",
901
- "current_theme",
902
- defaultTheme
903
- ]);
904
- process.exit();
905
- };
906
- process.once("SIGINT", cleanup);
907
- process.once("SIGTERM", cleanup);
908
957
  }
909
958
  }
910
959
  build.onEnd(async (result) => {
911
960
  if (result.errors.length > 0) return;
912
- if (!cache.hasChanges || cache.changed.size === 0) return;
913
- const destDirs = [resolve(outDir)];
914
- if (copy) destDirs.push(isExtension ? getExtensionDir() : isCustomApp ? resolve(getCustomAppsDir(), identifier) : resolve(getThemesDir(), identifier));
915
- const tasks = [];
916
- for (const filePath of cache.changed) {
917
- const fileData = cache.files.get(filePath);
918
- if (!fileData) continue;
919
- for (const destDir of destDirs) {
920
- const targetPath = resolve(destDir, basename(filePath));
921
- tasks.push((async () => {
922
- await mkdirp(destDir);
923
- await writeFile(targetPath, fileData.contents);
924
- })());
925
- }
926
- }
961
+ const destDirs = getDestDirs();
927
962
  try {
928
- await Promise.all(tasks);
929
- logger.debug(pc.green(`${CHECK} Changed files copied.`));
963
+ await copyFiles(logger, destDirs, cache.files);
964
+ logger.debug(pc.green(`${CHECK} Built files written to ${outDir}`));
930
965
  } catch (err) {
931
966
  logger.error(pc.red(`${CROSS} Failed to copy files: ${err instanceof Error ? err.message : String(err)}`));
932
967
  return;
933
968
  }
969
+ if (cache.removed.size > 0) {
970
+ await removeDeletedFiles(logger, destDirs, cache.removed);
971
+ cache.hasChanges = true;
972
+ }
973
+ cache.removed.clear();
974
+ if (env.skipSpicetify) return;
934
975
  if (apply && cache.hasChanges && (!applyOnce || !hasAppliedOnce)) {
935
976
  const { stdout, stderr, status } = runSpice(["apply"]);
936
977
  if (status !== 0) logger.error(pc.red(`${CROSS} Spicetify apply failed:`), stdout, stderr);
@@ -951,16 +992,32 @@ function wrapWithLoader({ config, cache, outFiles, server, dev = false, logger =
951
992
  name: namespace,
952
993
  setup(build$3) {
953
994
  if (build$3.initialOptions.write !== false) throw new Error(`[${namespace}] This plugin requires "write: false" in build options.`);
995
+ build$3.onStart(() => {
996
+ cache.changed.clear();
997
+ cache.removed.clear();
998
+ cache.hasChanges = false;
999
+ });
954
1000
  build$3.onEnd(async (res) => {
955
1001
  try {
956
1002
  if (res.errors.length > 0 || !res.outputFiles) return;
957
- cache.changed.clear();
958
- cache.hasChanges = false;
959
1003
  const filesChanged = [];
960
1004
  const outdir = resolve(build$3.initialOptions.outdir || "./dist");
961
1005
  const bundledCss = getBundledCss(res.outputFiles, outdir, type, dev);
962
1006
  const minify = build$3.initialOptions.minify;
963
1007
  const slug = varSlugify(`${name}_${version}`);
1008
+ const currentFilePaths = /* @__PURE__ */ new Set();
1009
+ for (const file of res.outputFiles) {
1010
+ const isJs = file.path.endsWith(".js");
1011
+ const isCss = file.path.endsWith(".css");
1012
+ if (!dev && isCss && type === "extension") continue;
1013
+ const relPath = file.path.slice(outdir.length);
1014
+ const isCustomAppExtension = type === "custom-app" && isExtensionDir(relPath);
1015
+ let targetName;
1016
+ if (isJs) targetName = isCustomAppExtension ? outFiles.jsExtension ?? "extension.js" : outFiles.js;
1017
+ else if (isCss && !isCustomAppExtension) targetName = outFiles.css;
1018
+ if (targetName) currentFilePaths.add(join(outdir, targetName));
1019
+ }
1020
+ for (const cachedPath of cache.files.keys()) if (!currentFilePaths.has(cachedPath)) cache.removed.add(cachedPath);
964
1021
  const transformPromises = res.outputFiles.map(async (file) => {
965
1022
  const isJs = file.path.endsWith(".js");
966
1023
  const isCss = file.path.endsWith(".css");
@@ -1120,8 +1177,9 @@ function getBundledCss(files, outdir, type, dev) {
1120
1177
  //#region src/esbuild/plugins/index.ts
1121
1178
  const plugins = {
1122
1179
  css,
1123
- cleanDist,
1180
+ clean,
1124
1181
  buildLogger,
1182
+ buildErrorReporter,
1125
1183
  externalGlobal,
1126
1184
  wrapWithLoader,
1127
1185
  spicetifyHandler
@@ -1140,13 +1198,20 @@ const defaultBuildOptions = {
1140
1198
  target: ["es2022", "chrome120"]
1141
1199
  };
1142
1200
  const getCommonPlugins = (opts) => {
1143
- const { template, minify, cache, buildOptions, outFiles, server, dev } = opts;
1201
+ const { template, minify, buildOptions, outFiles, server, dev } = opts;
1144
1202
  const inline = !dev && template === "extension";
1145
- return [
1203
+ const cache = {
1204
+ files: /* @__PURE__ */ new Map(),
1205
+ changed: /* @__PURE__ */ new Set(),
1206
+ removed: /* @__PURE__ */ new Set(),
1207
+ hasChanges: true
1208
+ };
1209
+ const p = [
1146
1210
  ...plugins.css({
1147
1211
  minify,
1148
1212
  inline
1149
1213
  }),
1214
+ plugins.clean(cache),
1150
1215
  plugins.externalGlobal({
1151
1216
  react: "Spicetify.React",
1152
1217
  "react-dom": "Spicetify.ReactDOM",
@@ -1168,6 +1233,8 @@ const getCommonPlugins = (opts) => {
1168
1233
  }),
1169
1234
  plugins.buildLogger({ cache })
1170
1235
  ];
1236
+ if (dev) p.push(plugins.buildErrorReporter({ server }));
1237
+ return p;
1171
1238
  };
1172
1239
  function getEntryPoints(config) {
1173
1240
  if (config.template === "theme") return [config.entry.js, config.entry.css];
@@ -1227,11 +1294,6 @@ function getJSBuildOptions(config, options) {
1227
1294
  const entryPoints = getEntryPoints(config);
1228
1295
  const minify = options.watch ? false : options.minify;
1229
1296
  const outDir = resolve(config.outDir);
1230
- const cache = {
1231
- files: /* @__PURE__ */ new Map(),
1232
- changed: /* @__PURE__ */ new Set(),
1233
- hasChanges: true
1234
- };
1235
1297
  const outFiles = getOutFiles(config);
1236
1298
  const overrides = {
1237
1299
  ...defaultBuildOptions,
@@ -1253,7 +1315,6 @@ function getJSBuildOptions(config, options) {
1253
1315
  plugins: [...config.esbuildOptions?.plugins ? config.esbuildOptions.plugins : [], ...getCommonPlugins({
1254
1316
  ...config,
1255
1317
  minify,
1256
- cache,
1257
1318
  buildOptions: {
1258
1319
  apply: options.apply,
1259
1320
  copy: options.copy,
@@ -1358,10 +1419,16 @@ function createPackageJSON(options) {
1358
1419
  dependencies: {},
1359
1420
  devDependencies: { "@spicetify/creator": "latest" }
1360
1421
  };
1361
- if (options.language === "ts") result.peerDependencies = {
1362
- ...result.peerDependencies,
1363
- typescript: "^5"
1364
- };
1422
+ if (options.language === "ts") {
1423
+ result.devDependencies = {
1424
+ ...result.devDependencies,
1425
+ ...options.packageManager === "bun" ? { "@types/bun": "^1.3.10" } : { "@types/node": "^25.5.0" }
1426
+ };
1427
+ result.peerDependencies = {
1428
+ ...result.peerDependencies,
1429
+ typescript: "^5"
1430
+ };
1431
+ }
1365
1432
  const slices = [FRAMEWORKS$1[options.framework], LINTERS$1[options.linter]];
1366
1433
  Object.values(INTERSECTIONS).forEach((intersection) => {
1367
1434
  if (intersection.condition(options)) slices.push(intersection);
@@ -1430,7 +1497,7 @@ const SHARED_FILES = (opts) => {
1430
1497
  {
1431
1498
  from: "DOT-gitignore",
1432
1499
  to: ".gitignore",
1433
- isShared: true
1500
+ isShared
1434
1501
  },
1435
1502
  {
1436
1503
  from: `spice.config.${ext(opts.language)}`,
@@ -1478,11 +1545,23 @@ const LANGUAGE_FILES = {
1478
1545
  to: "jsconfig.json",
1479
1546
  isShared: true
1480
1547
  }],
1481
- ts: [{
1482
- from: "tsconfig.json",
1483
- to: "tsconfig.json",
1484
- isShared: true
1485
- }]
1548
+ ts: [
1549
+ {
1550
+ from: "tsconfig.json",
1551
+ to: "tsconfig.json",
1552
+ isShared: true
1553
+ },
1554
+ {
1555
+ from: "tsconfig.app.json",
1556
+ to: "tsconfig.app.json",
1557
+ isShared: true
1558
+ },
1559
+ {
1560
+ from: "tsconfig.node.json",
1561
+ to: "tsconfig.node.json",
1562
+ isShared: true
1563
+ }
1564
+ ]
1486
1565
  };
1487
1566
  const FRAMEWORKS = {
1488
1567
  react: ({ language, template }) => {
@@ -1648,7 +1727,7 @@ function tryGitInit(root) {
1648
1727
  const downloads = { spicetify: {
1649
1728
  from: "https://raw.githubusercontent.com/spicetify/cli/main/globals.d.ts",
1650
1729
  to: "./src/types/spicetify.d.ts",
1651
- 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\");")
1730
+ 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 ReactJSX: any;", "const ReactJSX: typeof import(\"react/jsx-runtime\");").replace("const ReactDOMServer: any;", "const ReactDOMServer: typeof import(\"react-dom/server\");")
1652
1731
  } };
1653
1732
  async function updateTypes(isUpdating = true, cwd = process.cwd()) {
1654
1733
  const s = spinner();
@@ -1960,7 +2039,7 @@ async function createHmrServer(config, logger = createLogger("hmrServer")) {
1960
2039
  });
1961
2040
  });
1962
2041
  function broadcast(data) {
1963
- const message = typeof data === "string" ? data : JSON.stringify(data);
2042
+ const message = JSON.stringify(data);
1964
2043
  for (const client of clients) if (client.readyState === WebSocket.OPEN) client.send(message);
1965
2044
  }
1966
2045
  return {
@@ -2033,7 +2112,8 @@ const injectHMRExtension = async (rootLink, wsLink, outFiles) => {
2033
2112
  _SERVER_URL: JSON.stringify(rootLink),
2034
2113
  _HOT_RELOAD_LINK: JSON.stringify(wsLink),
2035
2114
  _JS_PATH: JSON.stringify(`/files/${outFiles.js}`),
2036
- _CSS_PATH: JSON.stringify(outFiles.css ? `/files/${outFiles.css}` : `/files/app.css`)
2115
+ _CSS_PATH: JSON.stringify(outFiles.css ? `/files/${outFiles.css}` : `/files/app.css`),
2116
+ _REMOVE_CMD: JSON.stringify(`spicetify config extensions sc-live-reload-helper.js- && spicetify apply`)
2037
2117
  }
2038
2118
  });
2039
2119
  writeFileSync(outDir, code);
@@ -2083,56 +2163,20 @@ const injectHMRCustomApp = async (rootLink, wsLink, outFiles, config) => {
2083
2163
  mkdirp(destDir);
2084
2164
  const indexJsPath = resolve(destDir, "index.js");
2085
2165
  const extensionJsPath = resolve(destDir, outFiles.jsExtension ?? "extension.js");
2086
- writeFileSync(indexJsPath, `const React = Spicetify.React;
2087
- const waitForImport = () => {
2088
- return import("${rootLink}/files/${outFiles.js}")
2089
- .then((mod) => mod.default || mod.render)
2090
- .catch((err) => {
2091
- console.error("Failed to import app:", err);
2092
- return null;
2093
- });
2094
- };
2095
-
2096
- const AppWrapper = ({ appPromise }) => {
2097
- const [App, setApp] = React.useState(null);
2098
-
2099
- React.useEffect(() => {
2100
- let mounted = true;
2101
-
2102
- appPromise.then((app) => {
2103
- if (mounted && app) {
2104
- setApp(() => app);
2105
- }
2106
- });
2107
-
2108
- return () => {
2109
- mounted = false;
2110
- };
2111
- }, [appPromise]);
2112
-
2113
- if (!App) {
2114
- return React.createElement(
2115
- "div",
2116
- { className: "loading" },
2117
- "Loading app..."
2118
- );
2119
- }
2120
-
2121
- return React.createElement(App);
2122
- };
2123
-
2124
- const render = () => {
2125
- const appPromise = waitForImport();
2126
- return React.createElement(AppWrapper, { appPromise });
2127
- };
2128
- `);
2166
+ const { code: transformedIndexCode } = await transform(readFileSync(hmrCustomAppFilePath, "utf8"), {
2167
+ loader: "js",
2168
+ define: { _IMPORT_LINK: JSON.stringify(`${rootLink}/files/${outFiles.js}`) },
2169
+ platform: "browser"
2170
+ });
2171
+ writeFileSync(indexJsPath, transformedIndexCode);
2129
2172
  const { code: extensionCode } = await transform(readFileSync(liveReloadFilePath, "utf8"), {
2130
2173
  loader: "js",
2131
2174
  define: {
2132
2175
  _SERVER_URL: JSON.stringify(rootLink),
2133
2176
  _HOT_RELOAD_LINK: JSON.stringify(wsLink),
2134
2177
  _JS_PATH: JSON.stringify(`/files/${outFiles.jsExtension ?? "extension.js"}`),
2135
- _CSS_PATH: JSON.stringify(outFiles.css ? `/files/${outFiles.css}` : `/files/app.css`)
2178
+ _CSS_PATH: JSON.stringify(outFiles.css ? `/files/${outFiles.css}` : `/files/app.css`),
2179
+ _REMOVE_CMD: JSON.stringify(`spicetify config custom_apps ${customAppId}- && spicetify apply`)
2136
2180
  }
2137
2181
  });
2138
2182
  writeFileSync(extensionJsPath, extensionCode);
@@ -2205,11 +2249,6 @@ async function dev$1(options) {
2205
2249
  function getJSDevOptions(config, options) {
2206
2250
  const entryPoints = getEntryPoints(config);
2207
2251
  const minify = false;
2208
- const cache = {
2209
- files: /* @__PURE__ */ new Map(),
2210
- changed: /* @__PURE__ */ new Set(),
2211
- hasChanges: true
2212
- };
2213
2252
  const overrides = {
2214
2253
  ...defaultBuildOptions,
2215
2254
  outdir: outDir,
@@ -2228,7 +2267,6 @@ function getJSDevOptions(config, options) {
2228
2267
  plugins: [...config.esbuildOptions?.plugins ? config.esbuildOptions.plugins : [], ...getCommonPlugins({
2229
2268
  ...config,
2230
2269
  minify,
2231
- cache,
2232
2270
  buildOptions: {
2233
2271
  copy: true,
2234
2272
  apply: false,
@@ -1,3 +1,4 @@
1
+ import { resolve } from "path";
1
2
  import { defineConfig } from "@spicetify/creator";
2
3
 
3
4
  // Learn more: {{config-reference-link}}
@@ -7,4 +8,9 @@ export default defineConfig({
7
8
  linter: "{{linter}}",
8
9
  template: "{{template}}",
9
10
  packageManager: "{{package-manager}}",
11
+ esbuildOptions: {
12
+ alias: {
13
+ "@": resolve(__dirname, "src"),
14
+ },
15
+ },
10
16
  });
@@ -1,3 +1,4 @@
1
+ import { resolve } from "path";
1
2
  import { defineConfig } from "@spicetify/creator";
2
3
 
3
4
  // Learn more: {{config-reference-link}}
@@ -7,4 +8,9 @@ export default defineConfig({
7
8
  linter: "{{linter}}",
8
9
  template: "{{template}}",
9
10
  packageManager: "{{package-manager}}",
11
+ esbuildOptions: {
12
+ alias: {
13
+ "@": resolve(__dirname, "src"),
14
+ },
15
+ },
10
16
  });
@@ -0,0 +1,33 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "useDefineForClassFields": true,
5
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
6
+ "module": "ESNext",
7
+ "types": ["@spicetify/creator/client"],
8
+ "skipLibCheck": true,
9
+
10
+ /* Bundler mode */
11
+ "moduleResolution": "bundler",
12
+ "allowImportingTsExtensions": true,
13
+ "verbatimModuleSyntax": true,
14
+ "moduleDetection": "force",
15
+ "noEmit": true,
16
+ "jsx": "react-jsx",
17
+
18
+ /* Linting */
19
+ "strict": true,
20
+ "noUnusedLocals": true,
21
+ "noUnusedParameters": true,
22
+ "erasableSyntaxOnly": true,
23
+ "noFallthroughCasesInSwitch": true,
24
+ "noUncheckedSideEffectImports": true,
25
+
26
+ // Paths
27
+ "baseUrl": ".",
28
+ "paths": {
29
+ "@/*": ["./src/*"]
30
+ }
31
+ },
32
+ "include": ["src"]
33
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2023",
4
+ "lib": ["ES2023"],
5
+ "module": "ESNext",
6
+ "types": ["node"],
7
+ "skipLibCheck": true,
8
+
9
+ /* Bundler mode */
10
+ "moduleResolution": "bundler",
11
+ "allowImportingTsExtensions": true,
12
+ "verbatimModuleSyntax": true,
13
+ "moduleDetection": "force",
14
+ "noEmit": true,
15
+
16
+ /* Linting */
17
+ "strict": true,
18
+ "noUnusedLocals": true,
19
+ "noUnusedParameters": true,
20
+ "erasableSyntaxOnly": true,
21
+ "noFallthroughCasesInSwitch": true,
22
+ "noUncheckedSideEffectImports": true
23
+ },
24
+ "include": ["spice.config.ts"]
25
+ }
@@ -1,6 +1,6 @@
1
1
  import "@/extension/app.css";
2
2
  import { createRoot } from "react-dom/client";
3
- // you can use aliases too ! (just add them to tsconfig.json)
3
+ // you can use aliases too ! (checkout tsconfig.app.json)
4
4
  import Onboarding from "@/components/Onboarding";
5
5
 
6
6
  const config = {
@@ -1,6 +1,6 @@
1
1
  import "@/app.css";
2
2
  import { createRoot } from "react-dom/client";
3
- // you can use aliases too ! (just add them to tsconfig.json)
3
+ // you can use aliases too ! (checkout tsconfig.app.json)
4
4
  import OnboardingCard from "@/components/Onboarding";
5
5
 
6
6
  const config = {
@@ -1,3 +1,4 @@
1
+ import { resolve } from "path";
1
2
  import { defineConfig } from "@spicetify/creator";
2
3
 
3
4
  // Learn more: {{config-reference-link}}
@@ -7,4 +8,9 @@ export default defineConfig({
7
8
  linter: "{{linter}}",
8
9
  template: "{{template}}",
9
10
  packageManager: "{{package-manager}}",
11
+ esbuildOptions: {
12
+ alias: {
13
+ "@": resolve(__dirname, "src"),
14
+ },
15
+ },
10
16
  });
@@ -1,3 +1,4 @@
1
+ import { resolve } from "path";
1
2
  import { defineConfig } from "@spicetify/creator";
2
3
 
3
4
  // Learn more: {{config-reference-link}}
@@ -7,4 +8,9 @@ export default defineConfig({
7
8
  linter: "{{linter}}",
8
9
  template: "{{template}}",
9
10
  packageManager: "{{package-manager}}",
11
+ esbuildOptions: {
12
+ alias: {
13
+ "@": resolve(__dirname, "src"),
14
+ },
15
+ },
10
16
  });