@spicetify/kit 0.1.0 → 0.3.0
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/check.js +9 -5
- package/dist/create.js +10 -11
- package/dist/from-theme.js +2 -1
- package/dist/stdlib-boundary.js +259 -0
- package/dist/version.js +8 -0
- package/package.json +52 -48
- package/vendor/shims/platform.d.ts +21201 -0
- package/vendor/shims/spicetify.d.ts +23 -23
- package/vendor/stdlib/lib/dialog-lifecycle.ts +53 -0
- package/vendor/stdlib/lib/floating-position.ts +57 -0
- package/vendor/stdlib/lib/floating.tsx +298 -0
- package/vendor/stdlib/lib/modal.tsx +20 -113
- package/vendor/stdlib/lib/popover.ts +26 -19
- package/vendor/stdlib/lib/primitives-classes.ts +12 -0
- package/vendor/stdlib/lib/primitives-vanilla.ts +27 -3
- package/vendor/stdlib/lib/primitives.tsx +176 -26
- package/vendor/stdlib/metadata.json +1 -1
- package/vendor/stdlib/mod.ts +8 -5
- package/vendor/stdlib/src/client.ts +65 -2
- package/vendor/stdlib/src/expose/Platform.ts +5 -2
- package/vendor/stdlib/src/registers/index.ts +40 -3
- package/vendor/stdlib/src/registers/panel-logic.ts +371 -0
- package/vendor/stdlib/src/registers/panel.ts +63 -151
- package/vendor/stdlib/src/registers/playbarButton.tsx +1 -1
- package/vendor/stdlib/src/registers/route.ts +2 -1
- package/vendor/stdlib/src/registers/settingsRow.tsx +4 -4
- package/vendor/stdlib/src/registers/settingsSection.ts +61 -41
- package/vendor/stdlib/src/registers/topbarLeftButton.tsx +1 -1
- package/vendor/stdlib/src/registers/topbarRightButton.tsx +3 -5
- package/vendor/stdlib/src/settings/corsProxy.tsx +102 -0
package/dist/check.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
14
14
|
import path from "node:path";
|
|
15
15
|
import { resolveModuleDir } from "./build.js";
|
|
16
|
+
import { checkExternalStdlibBoundary } from "./stdlib-boundary.js";
|
|
16
17
|
const SEMVER = /^\d+\.\d+\.\d+(?:[-+][\w.-]+)?$/;
|
|
17
18
|
// The shape of a client webpack hash: a separator-free alphanumeric token,
|
|
18
19
|
// 8+ chars, mixed case (real module classes are lower-kebab, e.g.
|
|
@@ -89,15 +90,15 @@ export function checkSource(rel, text, options = {}) {
|
|
|
89
90
|
});
|
|
90
91
|
}
|
|
91
92
|
// A second React instance breaks hooks/context identity (one-React
|
|
92
|
-
// rule).
|
|
93
|
-
//
|
|
93
|
+
// rule). The public stdlib barrel and the build-time bare-specifier shim
|
|
94
|
+
// both resolve to the client's captured instance.
|
|
94
95
|
if (/from\s+['"]https?:\/\/[^'"]*react[^'"]*['"]/.test(line) &&
|
|
95
96
|
!line.includes("jsx-runtime") &&
|
|
96
97
|
!/expose\/React/.test(rel)) {
|
|
97
98
|
out.push({
|
|
98
99
|
severity: "warn",
|
|
99
100
|
rule: "one-react",
|
|
100
|
-
message: "import React from stdlib's
|
|
101
|
+
message: "import React from stdlib's public barrel or the bare 'react' shim, never a second copy",
|
|
101
102
|
file: at,
|
|
102
103
|
});
|
|
103
104
|
}
|
|
@@ -256,11 +257,14 @@ export function checkModule(dir) {
|
|
|
256
257
|
];
|
|
257
258
|
}
|
|
258
259
|
findings.push(...checkMetadata(meta));
|
|
260
|
+
findings.push(...checkExternalStdlibBoundary(dir, meta));
|
|
259
261
|
findings.push(...checkEntryShim(dir, meta));
|
|
260
262
|
findings.push(...checkStructure(dir, meta));
|
|
261
|
-
const enforceClientBoundary = meta.kind === "extension";
|
|
262
263
|
for (const { file, text } of readSources(dir)) {
|
|
263
|
-
|
|
264
|
+
// The stdlib-boundary audit above owns ambient-client findings for full
|
|
265
|
+
// modules. Keep checkSource's standalone compatibility API but avoid a
|
|
266
|
+
// duplicate warning here.
|
|
267
|
+
findings.push(...checkSource(path.relative(dir, file), text, { clientCapabilities: false }));
|
|
264
268
|
}
|
|
265
269
|
return findings;
|
|
266
270
|
}
|
package/dist/create.js
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
16
16
|
import path from "node:path";
|
|
17
17
|
import { fileURLToPath } from "node:url";
|
|
18
|
+
import { KIT_DEPENDENCY_RANGE } from "./version.js";
|
|
18
19
|
// The stdlib range a fresh module declares tracks the stdlib it will compile
|
|
19
20
|
// against: the vendored copy in a published kit, the workspace copy in the
|
|
20
21
|
// monorepo. A hardcoded literal here went stale once (^0.3.0 outlived the
|
|
@@ -77,8 +78,7 @@ export default async function (ctx: ModuleRuntimeContext) {
|
|
|
77
78
|
return `${header}
|
|
78
79
|
import { client, createRegistrar } from "/modules/stdlib/mod.ts";
|
|
79
80
|
import type { ModuleRuntimeContext } from "/modules/stdlib/mod.ts";
|
|
80
|
-
import { React } from "/modules/stdlib/
|
|
81
|
-
import { NavLink } from "/modules/stdlib/src/registers/navlink.tsx";
|
|
81
|
+
import { NavLink, React } from "/modules/stdlib/mod.ts";
|
|
82
82
|
import { Button } from "/modules/stdlib/lib/primitives.js";
|
|
83
83
|
import { nowPlaying } from "./logic.ts";
|
|
84
84
|
|
|
@@ -104,9 +104,7 @@ export default async function (ctx: ModuleRuntimeContext) {
|
|
|
104
104
|
return `${header}
|
|
105
105
|
import { client, createRegistrar } from "/modules/stdlib/mod.ts";
|
|
106
106
|
import type { ModuleRuntimeContext } from "/modules/stdlib/mod.ts";
|
|
107
|
-
import { React } from "/modules/stdlib/
|
|
108
|
-
import { Platform } from "/modules/stdlib/src/expose/Platform.ts";
|
|
109
|
-
import { TopbarRightButton } from "/modules/stdlib/src/registers/topbarRightButton.tsx";
|
|
107
|
+
import { Platform, React } from "/modules/stdlib/mod.ts";
|
|
110
108
|
import { nowPlaying } from "./logic.ts";
|
|
111
109
|
|
|
112
110
|
const ROUTE = "${route}";
|
|
@@ -123,10 +121,11 @@ const Page = () => (
|
|
|
123
121
|
export default async function (ctx: ModuleRuntimeContext) {
|
|
124
122
|
const registrar = createRegistrar(ctx);
|
|
125
123
|
|
|
126
|
-
registrar.
|
|
127
|
-
"
|
|
128
|
-
|
|
129
|
-
|
|
124
|
+
registrar.placeButton("topbar-right", {
|
|
125
|
+
label: "${name}",
|
|
126
|
+
icon: ICON,
|
|
127
|
+
onClick: () => Platform.getHistory().push(ROUTE),
|
|
128
|
+
});
|
|
130
129
|
registrar.registerRoute(ROUTE, <Page />);
|
|
131
130
|
}
|
|
132
131
|
`;
|
|
@@ -270,7 +269,7 @@ notification = 1C1C30
|
|
|
270
269
|
dev: "spicetify-kit dev .",
|
|
271
270
|
check: "spicetify-kit check .",
|
|
272
271
|
},
|
|
273
|
-
devDependencies: { "@spicetify/kit":
|
|
272
|
+
devDependencies: { "@spicetify/kit": KIT_DEPENDENCY_RANGE },
|
|
274
273
|
}, null, "\t")}\n`);
|
|
275
274
|
writeFileSync(path.join(dir, ".gitignore"), "node_modules/\ndist/\n");
|
|
276
275
|
}
|
|
@@ -374,7 +373,7 @@ export async function load(ctx: ModuleRuntimeContext) {
|
|
|
374
373
|
test: 'node --test "test/*.test.mts"',
|
|
375
374
|
},
|
|
376
375
|
devDependencies: {
|
|
377
|
-
"@spicetify/kit":
|
|
376
|
+
"@spicetify/kit": KIT_DEPENDENCY_RANGE,
|
|
378
377
|
"@types/react": "^18",
|
|
379
378
|
"@types/react-dom": "^18",
|
|
380
379
|
"happy-dom": "^20",
|
package/dist/from-theme.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { cpSync, existsSync, mkdirSync, readdirSync, writeFileSync } from "node:fs";
|
|
15
15
|
import path from "node:path";
|
|
16
|
+
import { KIT_DEPENDENCY_RANGE } from "./version.js";
|
|
16
17
|
const USAGE = 'spicetify-kit from-theme <classic-theme-dir> [--name <id>] [--author "..."] [--bare]';
|
|
17
18
|
export async function runFromTheme(argv, cwd = process.cwd()) {
|
|
18
19
|
const source = argv.find((a) => !a.startsWith("--"));
|
|
@@ -76,7 +77,7 @@ export async function runFromTheme(argv, cwd = process.cwd()) {
|
|
|
76
77
|
dev: "spicetify-kit dev .",
|
|
77
78
|
},
|
|
78
79
|
devDependencies: {
|
|
79
|
-
"@spicetify/kit":
|
|
80
|
+
"@spicetify/kit": KIT_DEPENDENCY_RANGE,
|
|
80
81
|
},
|
|
81
82
|
}, null, "\t")}\n`);
|
|
82
83
|
writeFileSync(path.join(dir, ".gitignore"), "node_modules/\ndist/\n");
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2026 spicetify
|
|
3
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
+
*/
|
|
5
|
+
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
export const STDLIB_BOUNDARY_WARNING_RULES = ["ambient-client", "client-dom", "direct-map"];
|
|
8
|
+
const SOURCE_EXTENSIONS = new Set([".js", ".jsx", ".mjs", ".ts", ".tsx"]);
|
|
9
|
+
const STYLE_EXTENSIONS = new Set([".css", ".scss"]);
|
|
10
|
+
const PUBLIC_STDLIB_IMPORT = /^\/modules\/stdlib\/(?:mod\.(?:js|ts)|lib\/primitives(?:-classes|-vanilla)?\.(?:js|ts|tsx))$/;
|
|
11
|
+
const CLIENT_DOM_PATTERN = /(?:^|[^A-Za-z0-9_-])(?:Root__|main-[A-Za-z]|player-controls__|playback-progressbar|npv-)|\[data-testid(?:=|\])/;
|
|
12
|
+
const posix = (value) => value.split(path.sep).join("/");
|
|
13
|
+
const lineAt = (text, index) => text.slice(0, index).split("\n").length;
|
|
14
|
+
// Preserve string contents and character offsets while removing comments, so
|
|
15
|
+
// imports/selectors still match and diagnostics retain their original line.
|
|
16
|
+
function withoutComments(text) {
|
|
17
|
+
let out = "";
|
|
18
|
+
let state = "code";
|
|
19
|
+
let escaped = false;
|
|
20
|
+
for (let i = 0; i < text.length; i++) {
|
|
21
|
+
const char = text[i];
|
|
22
|
+
const next = text[i + 1];
|
|
23
|
+
if (state === "line") {
|
|
24
|
+
if (char === "\n") {
|
|
25
|
+
state = "code";
|
|
26
|
+
out += char;
|
|
27
|
+
}
|
|
28
|
+
else
|
|
29
|
+
out += " ";
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (state === "block") {
|
|
33
|
+
if (char === "*" && next === "/") {
|
|
34
|
+
out += " ";
|
|
35
|
+
i++;
|
|
36
|
+
state = "code";
|
|
37
|
+
}
|
|
38
|
+
else
|
|
39
|
+
out += char === "\n" ? "\n" : " ";
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (state === "code") {
|
|
43
|
+
if (char === "/" && next === "/") {
|
|
44
|
+
out += " ";
|
|
45
|
+
i++;
|
|
46
|
+
state = "line";
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (char === "/" && next === "*") {
|
|
50
|
+
out += " ";
|
|
51
|
+
i++;
|
|
52
|
+
state = "block";
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (char === "'")
|
|
56
|
+
state = "single";
|
|
57
|
+
else if (char === '"')
|
|
58
|
+
state = "double";
|
|
59
|
+
else if (char === "`")
|
|
60
|
+
state = "template";
|
|
61
|
+
out += char;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
out += char;
|
|
65
|
+
if (escaped) {
|
|
66
|
+
escaped = false;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (char === "\\") {
|
|
70
|
+
escaped = true;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if ((state === "single" && char === "'") ||
|
|
74
|
+
(state === "double" && char === '"') ||
|
|
75
|
+
(state === "template" && char === "`")) {
|
|
76
|
+
state = "code";
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return out;
|
|
80
|
+
}
|
|
81
|
+
function firstMatch(source, pattern) {
|
|
82
|
+
pattern.lastIndex = 0;
|
|
83
|
+
return pattern.exec(source);
|
|
84
|
+
}
|
|
85
|
+
export function inspectStdlibBoundaryFile(file, text) {
|
|
86
|
+
const extension = path.extname(file);
|
|
87
|
+
if (!SOURCE_EXTENSIONS.has(extension) && !STYLE_EXTENSIONS.has(extension))
|
|
88
|
+
return [];
|
|
89
|
+
const normalizedFile = posix(file);
|
|
90
|
+
const source = withoutComments(text);
|
|
91
|
+
const findings = [];
|
|
92
|
+
if (SOURCE_EXTENSIONS.has(extension)) {
|
|
93
|
+
const imports = /(?:\bfrom\s*|\bimport\s*(?:\(\s*)?)["'](\/modules\/stdlib\/[^"']+)["']/g;
|
|
94
|
+
for (const match of source.matchAll(imports)) {
|
|
95
|
+
if (PUBLIC_STDLIB_IMPORT.test(match[1]))
|
|
96
|
+
continue;
|
|
97
|
+
findings.push({
|
|
98
|
+
file: normalizedFile,
|
|
99
|
+
line: lineAt(source, match.index),
|
|
100
|
+
rule: "private-stdlib-import",
|
|
101
|
+
detail: `${match[1]} is private; import through /modules/stdlib/mod.js or the primitives kit`,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
const ambient = firstMatch(source, /\bSpicetify\s*(?:\?\.|\.)|\b(?:globalThis|window)\s*\.\s*Spicetify\b/);
|
|
105
|
+
if (ambient) {
|
|
106
|
+
findings.push({
|
|
107
|
+
file: normalizedFile,
|
|
108
|
+
line: lineAt(source, ambient.index),
|
|
109
|
+
rule: "ambient-client",
|
|
110
|
+
detail: "imports should use stdlib's typed client capability instead of ambient Spicetify.*",
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
const directMap = firstMatch(source, /\bMAP\s*\./);
|
|
114
|
+
if (directMap) {
|
|
115
|
+
findings.push({
|
|
116
|
+
file: normalizedFile,
|
|
117
|
+
line: lineAt(source, directMap.index),
|
|
118
|
+
rule: "direct-map",
|
|
119
|
+
detail: "MAP.* is direct client coupling; prefer a semantic stdlib surface",
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const clientDom = firstMatch(source, CLIENT_DOM_PATTERN);
|
|
124
|
+
if (clientDom) {
|
|
125
|
+
findings.push({
|
|
126
|
+
file: normalizedFile,
|
|
127
|
+
line: lineAt(source, clientDom.index),
|
|
128
|
+
rule: "client-dom",
|
|
129
|
+
detail: "Spotify-owned DOM selectors should be isolated and documented",
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
return findings;
|
|
133
|
+
}
|
|
134
|
+
function parseExceptions(meta) {
|
|
135
|
+
const configured = meta.stdlibBoundary;
|
|
136
|
+
if (configured === undefined)
|
|
137
|
+
return { exceptions: [], findings: [] };
|
|
138
|
+
const invalid = (message) => ({
|
|
139
|
+
severity: "error",
|
|
140
|
+
rule: "metadata.stdlib-boundary",
|
|
141
|
+
message,
|
|
142
|
+
file: "metadata.json",
|
|
143
|
+
});
|
|
144
|
+
if (typeof configured !== "object" || configured === null || Array.isArray(configured)) {
|
|
145
|
+
return { exceptions: [], findings: [invalid("stdlibBoundary must be an object")] };
|
|
146
|
+
}
|
|
147
|
+
const values = configured.exceptions;
|
|
148
|
+
if (!Array.isArray(values)) {
|
|
149
|
+
return { exceptions: [], findings: [invalid("stdlibBoundary.exceptions must be an array")] };
|
|
150
|
+
}
|
|
151
|
+
const exceptions = [];
|
|
152
|
+
const findings = [];
|
|
153
|
+
const allowedRules = new Set([
|
|
154
|
+
...STDLIB_BOUNDARY_WARNING_RULES,
|
|
155
|
+
"missing-stdlib-dependency",
|
|
156
|
+
]);
|
|
157
|
+
for (const [index, value] of values.entries()) {
|
|
158
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
159
|
+
findings.push(invalid(`stdlibBoundary.exceptions[${index}] must be an object`));
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
const candidate = value;
|
|
163
|
+
const validFile = typeof candidate.file === "string" &&
|
|
164
|
+
candidate.file.length > 0 &&
|
|
165
|
+
!candidate.file.includes("\\") &&
|
|
166
|
+
!path.posix.isAbsolute(candidate.file) &&
|
|
167
|
+
path.posix.normalize(candidate.file) === candidate.file &&
|
|
168
|
+
!candidate.file.startsWith("../");
|
|
169
|
+
const validRules = Array.isArray(candidate.rules) &&
|
|
170
|
+
candidate.rules.length > 0 &&
|
|
171
|
+
candidate.rules.every((rule) => typeof rule === "string" && allowedRules.has(rule));
|
|
172
|
+
if (!validFile || !validRules || typeof candidate.reason !== "string" || !candidate.reason.trim()) {
|
|
173
|
+
findings.push(invalid(`stdlibBoundary.exceptions[${index}] needs a normalized relative file, supported rules, and a reason`));
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
exceptions.push(candidate);
|
|
177
|
+
}
|
|
178
|
+
return { exceptions, findings };
|
|
179
|
+
}
|
|
180
|
+
function boundaryFiles(root) {
|
|
181
|
+
const files = [];
|
|
182
|
+
const walk = (directory) => {
|
|
183
|
+
for (const entry of readdirSync(directory)) {
|
|
184
|
+
if (["assets", "dist", "node_modules", "public"].includes(entry))
|
|
185
|
+
continue;
|
|
186
|
+
const full = path.join(directory, entry);
|
|
187
|
+
if (statSync(full).isDirectory())
|
|
188
|
+
walk(full);
|
|
189
|
+
else if (!/\.test\.[cm]?[jt]sx?$/.test(entry))
|
|
190
|
+
files.push(full);
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
walk(root);
|
|
194
|
+
return files;
|
|
195
|
+
}
|
|
196
|
+
export function checkExternalStdlibBoundary(root, metadata) {
|
|
197
|
+
if (typeof metadata !== "object" || metadata === null || Array.isArray(metadata))
|
|
198
|
+
return [];
|
|
199
|
+
const meta = metadata;
|
|
200
|
+
const parsed = parseExceptions(meta);
|
|
201
|
+
const out = [...parsed.findings];
|
|
202
|
+
const entries = meta.entries;
|
|
203
|
+
if (typeof entries?.js !== "string" || meta.name === "stdlib")
|
|
204
|
+
return out;
|
|
205
|
+
const isTheme = meta.kind === "theme" || (Array.isArray(meta.tags) && meta.tags.some((tag) => tag === "theme"));
|
|
206
|
+
const dependencies = meta.dependencies;
|
|
207
|
+
const missingDependency = !isTheme &&
|
|
208
|
+
typeof dependencies === "object" &&
|
|
209
|
+
dependencies !== null &&
|
|
210
|
+
!Array.isArray(dependencies) &&
|
|
211
|
+
typeof dependencies.stdlib !== "string";
|
|
212
|
+
const sources = boundaryFiles(root).flatMap((absolute) => {
|
|
213
|
+
const file = posix(path.relative(root, absolute));
|
|
214
|
+
return inspectStdlibBoundaryFile(file, readFileSync(absolute, "utf8"));
|
|
215
|
+
});
|
|
216
|
+
const exceptionAllows = (file, rule) => parsed.exceptions.some((item) => item.file === file && item.rules.includes(rule));
|
|
217
|
+
if (missingDependency && !exceptionAllows("metadata.json", "missing-stdlib-dependency")) {
|
|
218
|
+
out.push({
|
|
219
|
+
severity: "error",
|
|
220
|
+
rule: "stdlib-boundary.dependency",
|
|
221
|
+
message: "executable modules must declare a direct stdlib dependency",
|
|
222
|
+
file: "metadata.json",
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
for (const finding of sources) {
|
|
226
|
+
if (finding.rule === "private-stdlib-import") {
|
|
227
|
+
out.push({
|
|
228
|
+
severity: "error",
|
|
229
|
+
rule: "stdlib-boundary.private-import",
|
|
230
|
+
message: finding.detail,
|
|
231
|
+
file: `${finding.file}:${finding.line}`,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
else if (!isTheme && !exceptionAllows(finding.file, finding.rule)) {
|
|
235
|
+
out.push({
|
|
236
|
+
severity: "warn",
|
|
237
|
+
rule: `stdlib-boundary.${finding.rule}`,
|
|
238
|
+
message: `${finding.detail}; document an exception only when the coupling is intrinsic`,
|
|
239
|
+
file: `${finding.file}:${finding.line}`,
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
for (const exception of parsed.exceptions) {
|
|
244
|
+
for (const rule of exception.rules) {
|
|
245
|
+
const present = rule === "missing-stdlib-dependency"
|
|
246
|
+
? exception.file === "metadata.json" && missingDependency
|
|
247
|
+
: sources.some((finding) => finding.file === exception.file && finding.rule === rule);
|
|
248
|
+
if (!present) {
|
|
249
|
+
out.push({
|
|
250
|
+
severity: "error",
|
|
251
|
+
rule: "stdlib-boundary.stale-exception",
|
|
252
|
+
message: `${exception.file} no longer has ${rule}; remove the exception`,
|
|
253
|
+
file: "metadata.json",
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return out;
|
|
259
|
+
}
|
package/dist/version.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2026 Afonso Jorge Ramos
|
|
3
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
+
*/
|
|
5
|
+
// Kept in lockstep with package.json by release-please. Scaffolds must depend
|
|
6
|
+
// on the kit version that generated them, especially across pre-1.0 minors.
|
|
7
|
+
export const KIT_VERSION = "0.3.0"; // x-release-please-version
|
|
8
|
+
export const KIT_DEPENDENCY_RANGE = `^${KIT_VERSION}`;
|
package/package.json
CHANGED
|
@@ -1,49 +1,53 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
2
|
+
"name": "@spicetify/kit",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Developer kit for spicetify v3 modules: scaffold, build, hot-push, and pack",
|
|
5
|
+
"license": "GPL-3.0-or-later",
|
|
6
|
+
"homepage": "https://github.com/spicetify/modules#readme",
|
|
7
|
+
"bugs": {
|
|
8
|
+
"url": "https://github.com/spicetify/modules/issues"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/spicetify/modules.git",
|
|
13
|
+
"directory": "packages/kit"
|
|
14
|
+
},
|
|
15
|
+
"type": "module",
|
|
16
|
+
"bin": {
|
|
17
|
+
"spicetify-kit": "bin/spicetify-kit.js"
|
|
18
|
+
},
|
|
19
|
+
"exports": {
|
|
20
|
+
".": "./dist/cli.js",
|
|
21
|
+
"./create": "./dist/create.js",
|
|
22
|
+
"./build": "./dist/build.js",
|
|
23
|
+
"./dev": "./dist/dev.js",
|
|
24
|
+
"./pack": "./dist/pack.js",
|
|
25
|
+
"./from-theme": "./dist/from-theme.js",
|
|
26
|
+
"./check": "./dist/check.js",
|
|
27
|
+
"./vault": "./dist/vault.js",
|
|
28
|
+
"./install": "./dist/install.js"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"bin",
|
|
32
|
+
"dist",
|
|
33
|
+
"vendor"
|
|
34
|
+
],
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=22"
|
|
37
|
+
},
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"access": "public"
|
|
40
|
+
},
|
|
41
|
+
"spicetify": {
|
|
42
|
+
"stdlibVersion": "1.10.1"
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "tsc -p tsconfig.build.json",
|
|
46
|
+
"test": "node --test \"test/*.test.mts\"",
|
|
47
|
+
"prepack": "node scripts/sync-vendor.ts && tsc -p tsconfig.build.json"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"rolldown": "^1.2.0",
|
|
51
|
+
"sass-embedded": "^1.80.0"
|
|
52
|
+
}
|
|
53
|
+
}
|