@parity-app/cli 0.6.2 → 0.7.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/bin.mjs +14 -15
- package/dist/{client-BUaE9b77.mjs → client-D5kbiMmn.mjs} +1 -1
- package/dist/index.mjs +1 -1
- package/dist/schema-DcQLoUPP.mjs +239 -0
- package/dist/src-BaOpaXkV.mjs +4 -0
- package/dist/{src-V9wTMkxO.mjs → src-CSah1pZ-.mjs} +605 -715
- package/dist/src-D2kRGtaF.mjs +129 -0
- package/dist/src-vUHtM2X4.mjs +1 -0
- package/dist/{src-BvKhu6dX.mjs → src-x0VQ8I6l.mjs} +260 -123
- package/package.json +1 -1
- package/skills/SKILL.md +16 -16
- package/dist/schema-zRNxotYl.mjs +0 -121
- package/dist/src-BIVDlIGc.mjs +0 -53
- package/dist/src-BgQ7gnXE.mjs +0 -3
- package/dist/src-CiqdFzei.mjs +0 -1
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { a as applyLibraryEdits, f as parseLibraryEdits, p as parseLibraryManifest, u as libraryManifestExtension } from "./src-CSah1pZ-.mjs";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { readFile, readdir } from "node:fs/promises";
|
|
4
|
+
import { dirname, join, resolve } from "node:path";
|
|
5
|
+
import { existsSync } from "node:fs";
|
|
6
|
+
import semver from "semver";
|
|
7
|
+
// ../core/src/jsonc.ts
|
|
8
|
+
/** Parses JSON with comments and trailing commas, as tsconfig files allow. Comment markers inside strings are kept. */
|
|
9
|
+
function parseJsonc(text) {
|
|
10
|
+
let output = "";
|
|
11
|
+
let index = 0;
|
|
12
|
+
while (index < text.length) {
|
|
13
|
+
const char = text[index];
|
|
14
|
+
if (char === "\"") {
|
|
15
|
+
const start = index++;
|
|
16
|
+
while (index < text.length && text[index] !== "\"") index += text[index] === "\\" ? 2 : 1;
|
|
17
|
+
output += text.slice(start, ++index);
|
|
18
|
+
} else if (char === "/" && text[index + 1] === "/") while (index < text.length && text[index] !== "\n") index++;
|
|
19
|
+
else if (char === "/" && text[index + 1] === "*") {
|
|
20
|
+
const end = text.indexOf("*/", index + 2);
|
|
21
|
+
index = end === -1 ? text.length : end + 2;
|
|
22
|
+
} else {
|
|
23
|
+
output += char;
|
|
24
|
+
index++;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return JSON.parse(output.replace(/,(\s*[}\]])/g, "$1"));
|
|
28
|
+
}
|
|
29
|
+
// ../registry/src/index.ts
|
|
30
|
+
const require = createRequire(import.meta.url);
|
|
31
|
+
/**
|
|
32
|
+
* The folder the registry's files are in. The published CLI bundles this module and ships the files beside it, where
|
|
33
|
+
* the package can't be resolved. Otherwise they're found through the package's own `package.json`, so it's the same
|
|
34
|
+
* whether this module runs from source or bundled into the app.
|
|
35
|
+
*/
|
|
36
|
+
function registryDir() {
|
|
37
|
+
const bundled = join(import.meta.dirname, "libraries");
|
|
38
|
+
if (existsSync(bundled)) return bundled;
|
|
39
|
+
return join(dirname(require.resolve("@parity-app/registry/package.json")), "libraries");
|
|
40
|
+
}
|
|
41
|
+
let cache;
|
|
42
|
+
/** Every file in the registry, read once. One that doesn't parse is skipped with a warning. */
|
|
43
|
+
function registryEntries(dir = registryDir()) {
|
|
44
|
+
if (dir !== registryDir()) return readEntries(dir);
|
|
45
|
+
cache ??= readEntries(dir);
|
|
46
|
+
return cache;
|
|
47
|
+
}
|
|
48
|
+
async function readEntries(dir) {
|
|
49
|
+
const files = (await readdir(dir).catch(() => [])).filter((file) => file.endsWith(libraryManifestExtension)).sort();
|
|
50
|
+
const entries = [];
|
|
51
|
+
for (const file of files) {
|
|
52
|
+
const path = join(dir, file);
|
|
53
|
+
try {
|
|
54
|
+
const manifest = parseLibraryManifest(JSON.parse(await readFile(path, "utf8")));
|
|
55
|
+
if (!manifest.package) throw new TypeError("/package: a registry file names the package it describes");
|
|
56
|
+
entries.push({
|
|
57
|
+
file,
|
|
58
|
+
path,
|
|
59
|
+
manifest
|
|
60
|
+
});
|
|
61
|
+
} catch (error) {
|
|
62
|
+
console.warn(`Skipping registry file ${file}: ${errorText(error)}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return entries;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The newest file for each package the registry describes, by the version it was made from, with its hand edits
|
|
69
|
+
* applied. A file whose edits can't be read is used as it is, with a warning.
|
|
70
|
+
*/
|
|
71
|
+
async function newestEntries(dir) {
|
|
72
|
+
const newest = /* @__PURE__ */ new Map();
|
|
73
|
+
for (const entry of await registryEntries(dir)) {
|
|
74
|
+
const name = entry.manifest.package.name;
|
|
75
|
+
const current = newest.get(name);
|
|
76
|
+
if (!current || compareGenerated(entry.manifest, current.manifest) > 0) newest.set(name, entry);
|
|
77
|
+
}
|
|
78
|
+
return Promise.all([...newest.values()].map(async (entry) => {
|
|
79
|
+
try {
|
|
80
|
+
return {
|
|
81
|
+
...entry,
|
|
82
|
+
manifest: (await withEdits(entry.path, entry.manifest)).manifest
|
|
83
|
+
};
|
|
84
|
+
} catch (error) {
|
|
85
|
+
console.warn(`Using registry file ${entry.file} without its edits: ${errorText(error)}`);
|
|
86
|
+
return entry;
|
|
87
|
+
}
|
|
88
|
+
}));
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* A library file at `path` with its hand edits applied: its own `edits`, or the shared file it names beside it. Edits
|
|
92
|
+
* for exports it doesn't have are its `orphans`, reported rather than dropped silently. Throws when the edits can't be
|
|
93
|
+
* read.
|
|
94
|
+
*/
|
|
95
|
+
async function withEdits(path, manifest) {
|
|
96
|
+
if (manifest.edits === void 0) return {
|
|
97
|
+
manifest,
|
|
98
|
+
orphans: []
|
|
99
|
+
};
|
|
100
|
+
return applyLibraryEdits(manifest, await readLibraryEdits(manifest.edits, dirname(path)));
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Hand edits as a library file writes them: inline, or the path of a shared file, which is read from `from` and may
|
|
104
|
+
* have comments.
|
|
105
|
+
*/
|
|
106
|
+
async function readLibraryEdits(edits, from) {
|
|
107
|
+
return parseLibraryEdits(typeof edits === "string" ? parseJsonc(await readFile(resolve(from, edits), "utf8")) : edits);
|
|
108
|
+
}
|
|
109
|
+
function errorText(error) {
|
|
110
|
+
return error instanceof Error ? error.message : String(error);
|
|
111
|
+
}
|
|
112
|
+
/** Whether a manifest describes this version of its package. A manifest without a range covers every version. */
|
|
113
|
+
function covers(manifest, version) {
|
|
114
|
+
const range = manifest.package?.range;
|
|
115
|
+
if (!range) return true;
|
|
116
|
+
if (!version) return false;
|
|
117
|
+
const parsed = semver.coerce(version, { includePrerelease: true });
|
|
118
|
+
return parsed !== null && semver.satisfies(parsed, range, { includePrerelease: true });
|
|
119
|
+
}
|
|
120
|
+
function compareGenerated(a, b) {
|
|
121
|
+
const left = semver.coerce(a.package?.version);
|
|
122
|
+
const right = semver.coerce(b.package?.version);
|
|
123
|
+
if (!left || !right) {
|
|
124
|
+
if (left) return 1;
|
|
125
|
+
return right ? -1 : 0;
|
|
126
|
+
}
|
|
127
|
+
return semver.compare(left, right);
|
|
128
|
+
}
|
|
129
|
+
export { registryEntries as a, registryDir as i, newestEntries as n, withEdits as o, readLibraryEdits as r, parseJsonc as s, covers as t };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { t as covers, n as newestEntries, i as registryDir, a as registryEntries } from "./src-D2kRGtaF.mjs";
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import "./src-
|
|
1
|
+
import { a as parseData, n as finiteNumber, o as plainObject, r as jsonText } from "./schema-DcQLoUPP.mjs";
|
|
2
|
+
import { E as collectCustomProperties, M as packageManifest, a as applyLibraryEdits, c as isSampleElement, f as parseLibraryEdits, j as lengthToPixels, l as isSampleRender, m as renderSlotOf, p as parseLibraryManifest, s as isMadeValue, u as libraryManifestExtension } from "./src-CSah1pZ-.mjs";
|
|
3
|
+
import { r as readLibraryEdits, s as parseJsonc } from "./src-D2kRGtaF.mjs";
|
|
4
4
|
import { a as publishedVersions, n as packageFile, o as repositoryFiles, r as publishedSource } from "./published-CdXoUgfD.mjs";
|
|
5
5
|
import { createRequire } from "node:module";
|
|
6
6
|
import { mkdir, readFile, readdir, realpath, rm, stat, writeFile } from "node:fs/promises";
|
|
7
7
|
import { basename, dirname, extname, join, posix, relative, resolve, sep } from "node:path";
|
|
8
8
|
import { promisify } from "node:util";
|
|
9
9
|
import { createHash } from "node:crypto";
|
|
10
|
-
import { existsSync, realpathSync } from "node:fs";
|
|
10
|
+
import { existsSync, realpathSync, statSync } from "node:fs";
|
|
11
11
|
import { homedir } from "node:os";
|
|
12
12
|
import { z } from "zod";
|
|
13
13
|
import { pathToFileURL } from "node:url";
|
|
@@ -528,28 +528,6 @@ function slashes(path) {
|
|
|
528
528
|
return path.replaceAll("\\", "/");
|
|
529
529
|
}
|
|
530
530
|
ts.TypeFlags.Any | ts.TypeFlags.Unknown | ts.TypeFlags.NonPrimitive | ts.TypeFlags.TypeParameter;
|
|
531
|
-
// ../project/src/jsonc.ts
|
|
532
|
-
/** Parses JSON with comments and trailing commas, as tsconfig files allow. Comment markers inside strings are kept. */
|
|
533
|
-
function parseJsonc(text) {
|
|
534
|
-
let output = "";
|
|
535
|
-
let index = 0;
|
|
536
|
-
while (index < text.length) {
|
|
537
|
-
const char = text[index];
|
|
538
|
-
if (char === "\"") {
|
|
539
|
-
const start = index++;
|
|
540
|
-
while (index < text.length && text[index] !== "\"") index += text[index] === "\\" ? 2 : 1;
|
|
541
|
-
output += text.slice(start, ++index);
|
|
542
|
-
} else if (char === "/" && text[index + 1] === "/") while (index < text.length && text[index] !== "\n") index++;
|
|
543
|
-
else if (char === "/" && text[index + 1] === "*") {
|
|
544
|
-
const end = text.indexOf("*/", index + 2);
|
|
545
|
-
index = end === -1 ? text.length : end + 2;
|
|
546
|
-
} else {
|
|
547
|
-
output += char;
|
|
548
|
-
index++;
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
return JSON.parse(output.replace(/,(\s*[}\]])/g, "$1"));
|
|
552
|
-
}
|
|
553
531
|
// ../tokens/src/dtcg-values.ts
|
|
554
532
|
/** A value written as CSS. */
|
|
555
533
|
const css = z.string();
|
|
@@ -935,6 +913,247 @@ new Intl.Collator("en", {
|
|
|
935
913
|
numeric: true,
|
|
936
914
|
sensitivity: "base"
|
|
937
915
|
});
|
|
916
|
+
// ../project/src/packages.ts
|
|
917
|
+
/**
|
|
918
|
+
* The package a folder's code gets for `name`, found the way Node finds packages: in `node_modules` here or in a folder
|
|
919
|
+
* above, with links followed. It reads the package's own package.json, which its `exports` may leave out of reach of
|
|
920
|
+
* `require.resolve`, and never falls back to NODE_PATH, which pnpm's bin shims point at Parity's own dependencies.
|
|
921
|
+
* Throws when the manifest isn't a package's.
|
|
922
|
+
*/
|
|
923
|
+
async function findPackage(from, name) {
|
|
924
|
+
for (let folder = resolve(from);; folder = dirname(folder)) {
|
|
925
|
+
const installedAt = join(folder, "node_modules", name);
|
|
926
|
+
const text = await readFile(join(installedAt, "package.json"), "utf8").catch(() => void 0);
|
|
927
|
+
if (text !== void 0) {
|
|
928
|
+
const manifest = readManifest(text, join(installedAt, "package.json"));
|
|
929
|
+
return {
|
|
930
|
+
dir: await realpath(installedAt),
|
|
931
|
+
installedAt,
|
|
932
|
+
manifest
|
|
933
|
+
};
|
|
934
|
+
}
|
|
935
|
+
if (dirname(folder) === folder) return void 0;
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
/** The package in a folder, or nothing when it has no package.json. Throws when the manifest isn't a package's. */
|
|
939
|
+
async function readPackage(dir) {
|
|
940
|
+
const file = join(dir, "package.json");
|
|
941
|
+
const text = await readFile(file, "utf8").catch(() => void 0);
|
|
942
|
+
return text === void 0 ? void 0 : {
|
|
943
|
+
dir,
|
|
944
|
+
manifest: readManifest(text, file)
|
|
945
|
+
};
|
|
946
|
+
}
|
|
947
|
+
function readManifest(text, file) {
|
|
948
|
+
try {
|
|
949
|
+
return parseData(jsonText(packageManifest), text);
|
|
950
|
+
} catch (error) {
|
|
951
|
+
throw new TypeError(`${file}: ${error instanceof Error ? error.message : String(error)}`);
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
/** A bare specifier's package and the entry point in it: `@acme/ui/button` is `@acme/ui` and `./button`. */
|
|
955
|
+
function splitSpecifier(specifier) {
|
|
956
|
+
const parts = specifier.split("/");
|
|
957
|
+
const length = specifier.startsWith("@") ? 2 : 1;
|
|
958
|
+
const rest = parts.slice(length).join("/");
|
|
959
|
+
return {
|
|
960
|
+
name: parts.slice(0, length).join("/"),
|
|
961
|
+
subpath: rest ? `./${rest}` : "."
|
|
962
|
+
};
|
|
963
|
+
}
|
|
964
|
+
const conditions = {
|
|
965
|
+
types: [
|
|
966
|
+
"types",
|
|
967
|
+
"import",
|
|
968
|
+
"default",
|
|
969
|
+
"require"
|
|
970
|
+
],
|
|
971
|
+
runtime: [
|
|
972
|
+
"node",
|
|
973
|
+
"import",
|
|
974
|
+
"default"
|
|
975
|
+
],
|
|
976
|
+
source: [
|
|
977
|
+
"source",
|
|
978
|
+
"types",
|
|
979
|
+
"import",
|
|
980
|
+
"default"
|
|
981
|
+
],
|
|
982
|
+
style: [
|
|
983
|
+
"style",
|
|
984
|
+
"import",
|
|
985
|
+
"default"
|
|
986
|
+
]
|
|
987
|
+
};
|
|
988
|
+
/** Which files a target may lead to for each use: declarations for types, anything but them otherwise. */
|
|
989
|
+
const accepts = {
|
|
990
|
+
types: (file) => isDeclaration(file),
|
|
991
|
+
runtime: (file) => !isDeclaration(file),
|
|
992
|
+
source: () => true,
|
|
993
|
+
style: (file) => !isDeclaration(file)
|
|
994
|
+
};
|
|
995
|
+
/**
|
|
996
|
+
* The file one of a package's entry points leads to for a use, or nothing when no file is there. `subpath` is the
|
|
997
|
+
* entry point as `exports` names it: `.` for the package itself, or one such as `./tailwind.css`, which can be written
|
|
998
|
+
* `tailwind.css`. A package that doesn't list an entry point in `exports` is read as older packages are: `types` or
|
|
999
|
+
* `typings` for declarations, `module` or `main` for its module, and `style` for its stylesheet.
|
|
1000
|
+
*/
|
|
1001
|
+
function packageEntry(pkg, use, subpath = ".") {
|
|
1002
|
+
const entry = subpath === "." || subpath.startsWith("./") ? subpath : `./${subpath}`;
|
|
1003
|
+
for (const candidate of entryCandidates(pkg.manifest, use, entry)) {
|
|
1004
|
+
const file = candidate && join(pkg.dir, candidate);
|
|
1005
|
+
if (file && isFile(file)) return file;
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
function entryCandidates(manifest, use, entry) {
|
|
1009
|
+
const root = entry === ".";
|
|
1010
|
+
const path = entry.slice(2);
|
|
1011
|
+
const exported = exportTarget(manifest.exports, entry, conditions[use], accepts[use]);
|
|
1012
|
+
switch (use) {
|
|
1013
|
+
case "types": return [
|
|
1014
|
+
exported,
|
|
1015
|
+
...root ? [manifest.types, manifest.typings] : [],
|
|
1016
|
+
...entryCandidates(manifest, "runtime", entry).map((file) => file && declarationOf(file)),
|
|
1017
|
+
root ? "index.d.ts" : `${path}/index.d.ts`,
|
|
1018
|
+
root ? void 0 : `${path}.d.ts`
|
|
1019
|
+
];
|
|
1020
|
+
case "runtime": return [
|
|
1021
|
+
exported,
|
|
1022
|
+
...root ? [manifest.module, ...moduleFiles(manifest.main)] : [],
|
|
1023
|
+
...root || manifest.exports !== void 0 ? [] : moduleFiles(path)
|
|
1024
|
+
];
|
|
1025
|
+
case "source": {
|
|
1026
|
+
const named = [exported, ...root ? [
|
|
1027
|
+
manifest.types,
|
|
1028
|
+
manifest.typings,
|
|
1029
|
+
manifest.module,
|
|
1030
|
+
manifest.main
|
|
1031
|
+
] : []].find((file) => file !== void 0);
|
|
1032
|
+
return [...named && isBuilt(named) ? sourcesOf(named) : [named], ...root ? [
|
|
1033
|
+
"src/index.tsx",
|
|
1034
|
+
"src/index.ts",
|
|
1035
|
+
"src/index.jsx",
|
|
1036
|
+
"src/index.js",
|
|
1037
|
+
"index.tsx",
|
|
1038
|
+
"index.ts"
|
|
1039
|
+
] : []];
|
|
1040
|
+
}
|
|
1041
|
+
case "style": return root ? [manifest.style, exported] : [exported, path];
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
/** A `main` as Node reads it: the file, the file with `.js`, or a folder's `index.js`; `index.js` without one. */
|
|
1045
|
+
function moduleFiles(main) {
|
|
1046
|
+
return main ? [
|
|
1047
|
+
main,
|
|
1048
|
+
`${main}.js`,
|
|
1049
|
+
`${main}/index.js`
|
|
1050
|
+
] : ["index.js"];
|
|
1051
|
+
}
|
|
1052
|
+
const stylesheetNames = [
|
|
1053
|
+
"tailwind.css",
|
|
1054
|
+
"styles.css",
|
|
1055
|
+
"style.css",
|
|
1056
|
+
"index.css",
|
|
1057
|
+
"theme.css"
|
|
1058
|
+
];
|
|
1059
|
+
/**
|
|
1060
|
+
* The stylesheet a project imports to use a package, as a path from the package: its `style` field, else the CSS its
|
|
1061
|
+
* `exports` lists, the usual names first. Only one that leads to a file counts.
|
|
1062
|
+
*/
|
|
1063
|
+
function packageStylesheet(pkg) {
|
|
1064
|
+
const { style, exports } = pkg.manifest;
|
|
1065
|
+
if (style && isFile(join(pkg.dir, style))) return style;
|
|
1066
|
+
const css = Object.keys(exports === void 0 ? {} : subpaths(exports)).filter((key) => key.endsWith(".css") && !key.includes("*")).map((key) => key.slice(2));
|
|
1067
|
+
const pick = stylesheetNames.find((name) => css.includes(name)) ?? css[0];
|
|
1068
|
+
return pick && packageEntry(pkg, "style", pick) ? pick : void 0;
|
|
1069
|
+
}
|
|
1070
|
+
/** `exports` by subpath: a map as written, or the package's one entry point, which is `.`. */
|
|
1071
|
+
function subpaths(exports) {
|
|
1072
|
+
if (typeof exports !== "object" || exports === null || Array.isArray(exports)) return { ".": exports };
|
|
1073
|
+
return Object.keys(exports).some((key) => key.startsWith(".")) ? exports : { ".": exports };
|
|
1074
|
+
}
|
|
1075
|
+
/**
|
|
1076
|
+
* Where `exports` leads for an entry point under these conditions: the entry point as written, else the longest
|
|
1077
|
+
* pattern that matches it (`./icons/*`), with what `*` stood for put in.
|
|
1078
|
+
*/
|
|
1079
|
+
function exportTarget(exports, entry, wanted, accept) {
|
|
1080
|
+
if (exports === void 0) return void 0;
|
|
1081
|
+
const map = subpaths(exports);
|
|
1082
|
+
if (Object.hasOwn(map, entry)) return resolveTarget(map[entry], wanted, accept);
|
|
1083
|
+
let best;
|
|
1084
|
+
for (const key of Object.keys(map)) {
|
|
1085
|
+
const star = key.indexOf("*");
|
|
1086
|
+
if (star === -1) continue;
|
|
1087
|
+
const [prefix, suffix] = [key.slice(0, star), key.slice(star + 1)];
|
|
1088
|
+
if (entry.length < key.length || !entry.startsWith(prefix) || !entry.endsWith(suffix)) continue;
|
|
1089
|
+
if (!best || prefix.length > best.prefix.length) best = {
|
|
1090
|
+
key,
|
|
1091
|
+
prefix,
|
|
1092
|
+
match: entry.slice(prefix.length, entry.length - suffix.length)
|
|
1093
|
+
};
|
|
1094
|
+
}
|
|
1095
|
+
if (!best) return void 0;
|
|
1096
|
+
const { key, match } = best;
|
|
1097
|
+
return resolveTarget(map[key], wanted, (file) => accept(file.replaceAll("*", match)))?.replaceAll("*", match);
|
|
1098
|
+
}
|
|
1099
|
+
/** The first file a target leads to under the wanted conditions, taken in the order they're wanted. */
|
|
1100
|
+
function resolveTarget(target, wanted, accept) {
|
|
1101
|
+
if (typeof target === "string") return accept(target) ? target : void 0;
|
|
1102
|
+
if (target === null) return void 0;
|
|
1103
|
+
if (Array.isArray(target)) {
|
|
1104
|
+
for (const each of target) {
|
|
1105
|
+
const found = resolveTarget(each, wanted, accept);
|
|
1106
|
+
if (found) return found;
|
|
1107
|
+
}
|
|
1108
|
+
return;
|
|
1109
|
+
}
|
|
1110
|
+
for (const condition of wanted) {
|
|
1111
|
+
const found = Object.hasOwn(target, condition) ? resolveTarget(target[condition], wanted, accept) : void 0;
|
|
1112
|
+
if (found) return found;
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
function isDeclaration(file) {
|
|
1116
|
+
return /\.d\.[cm]?ts$/.test(file);
|
|
1117
|
+
}
|
|
1118
|
+
/** The declarations TypeScript looks for beside a module: `index.d.ts` for `index.js`, `index.d.mts` for `index.mjs`. */
|
|
1119
|
+
function declarationOf(file) {
|
|
1120
|
+
const match = /\.([cm]?)jsx?$/.exec(file);
|
|
1121
|
+
return match ? `${file.slice(0, match.index)}.d.${match[1]}ts` : void 0;
|
|
1122
|
+
}
|
|
1123
|
+
/** Folders a package's build writes to, which its source isn't in. */
|
|
1124
|
+
const builtFolders = /* @__PURE__ */ new Set([
|
|
1125
|
+
"dist",
|
|
1126
|
+
"build",
|
|
1127
|
+
"lib",
|
|
1128
|
+
"out",
|
|
1129
|
+
"esm",
|
|
1130
|
+
"cjs"
|
|
1131
|
+
]);
|
|
1132
|
+
/** A file a build wrote: declarations, or anything in a build folder at the package's root. */
|
|
1133
|
+
function isBuilt(file) {
|
|
1134
|
+
return isDeclaration(file) || builtFolders.has(file.replace(/^\.\//, "").split("/")[0]);
|
|
1135
|
+
}
|
|
1136
|
+
const sourceExtensions = [
|
|
1137
|
+
".tsx",
|
|
1138
|
+
".ts",
|
|
1139
|
+
".jsx",
|
|
1140
|
+
".js"
|
|
1141
|
+
];
|
|
1142
|
+
/**
|
|
1143
|
+
* Where a built file's source may be, in the order to look: `dist/index.js` and `dist/index.d.ts` were built from
|
|
1144
|
+
* `src/index.tsx`, `src/index.ts`, `src/index.jsx` or `src/index.js`, and `dist/esm/button.js` from `src/button.tsx`.
|
|
1145
|
+
*/
|
|
1146
|
+
function sourcesOf(file) {
|
|
1147
|
+
const parts = file.replace(/^\.\//, "").split("/");
|
|
1148
|
+
let start = 0;
|
|
1149
|
+
while (start < parts.length - 1 && builtFolders.has(parts[start])) start++;
|
|
1150
|
+
const rest = parts.slice(start).join("/").replace(/\.d\.[cm]?ts$|\.[cm]?jsx?$/, "");
|
|
1151
|
+
const stem = start > 0 ? `src/${rest}` : rest;
|
|
1152
|
+
return sourceExtensions.map((extension) => stem + extension);
|
|
1153
|
+
}
|
|
1154
|
+
function isFile(path) {
|
|
1155
|
+
return statSync(path, { throwIfNoEntry: false })?.isFile() ?? false;
|
|
1156
|
+
}
|
|
938
1157
|
// ../project/src/styles.ts
|
|
939
1158
|
/**
|
|
940
1159
|
* Where an app keeps its global styles when neither `components.json` nor the entry says: Vite's and Create React App's
|
|
@@ -1047,21 +1266,6 @@ async function readText(path) {
|
|
|
1047
1266
|
function toPosix(path) {
|
|
1048
1267
|
return path.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
1049
1268
|
}
|
|
1050
|
-
// ../project/src/libraries.ts
|
|
1051
|
-
/**
|
|
1052
|
-
* Where a dependency is installed, found the way Node finds packages: in `node_modules` here or in a folder above. It
|
|
1053
|
-
* doesn't resolve `<name>/package.json`, because a package whose `exports` leaves that out can't be resolved that way.
|
|
1054
|
-
*/
|
|
1055
|
-
async function installedPackage(root, importName) {
|
|
1056
|
-
for (let folder = resolve(root);; folder = dirname(folder)) {
|
|
1057
|
-
const candidate = join(folder, "node_modules", importName);
|
|
1058
|
-
if (await isFile(join(candidate, "package.json"))) return realpath(candidate);
|
|
1059
|
-
if (dirname(folder) === folder) return void 0;
|
|
1060
|
-
}
|
|
1061
|
-
}
|
|
1062
|
-
async function isFile(path) {
|
|
1063
|
-
return stat(path).then((stats) => stats.isFile(), () => false);
|
|
1064
|
-
}
|
|
1065
1269
|
/** A sentence that ends this far in is kept whole, rather than its words cut down to the length. */
|
|
1066
1270
|
const shortestSentence = 20;
|
|
1067
1271
|
/**
|
|
@@ -3924,7 +4128,6 @@ const presets = {
|
|
|
3924
4128
|
id: style,
|
|
3925
4129
|
name: `Shadcn ${style.charAt(0).toUpperCase()}${style.slice(1)}`,
|
|
3926
4130
|
description: `Every shadcn component on Base UI, in the ${style} style, kept in step with upstream.`,
|
|
3927
|
-
styles: "tailwind.css",
|
|
3928
4131
|
examples: {
|
|
3929
4132
|
paths: ["apps/v4/examples/base/"],
|
|
3930
4133
|
anatomy: ["apps/v4/content/docs/components/base/"]
|
|
@@ -4627,16 +4830,16 @@ async function generateLibrary(options) {
|
|
|
4627
4830
|
}
|
|
4628
4831
|
async function describePackage(options, importName) {
|
|
4629
4832
|
const root = resolve(options.root);
|
|
4630
|
-
const
|
|
4631
|
-
if (!
|
|
4632
|
-
const
|
|
4833
|
+
const installed = await findPackage(root, importName);
|
|
4834
|
+
if (!installed) throw new Error(`${importName} isn't installed in ${root}. Install it with the project's package manager first.`);
|
|
4835
|
+
const { dir, manifest: pkg } = installed;
|
|
4633
4836
|
const name = pkg.name ?? importName;
|
|
4634
4837
|
const version = pkg.version ?? "0.0.0";
|
|
4635
4838
|
const preset = presets[name];
|
|
4636
4839
|
const notes = [...preset?.notes ?? []];
|
|
4637
4840
|
const iconImport = preset?.icons?.import;
|
|
4638
4841
|
const componentImport = iconImport ?? preset?.components;
|
|
4639
|
-
const entry =
|
|
4842
|
+
const entry = packageEntry(installed, "types", componentImport ? splitSpecifier(componentImport).subpath : ".")?.replaceAll("\\", "/");
|
|
4640
4843
|
if (!entry) notes.push("No type declarations were found, so no components could be read.");
|
|
4641
4844
|
const scan = entry ? findComponents({
|
|
4642
4845
|
entries: [entry],
|
|
@@ -4651,8 +4854,9 @@ async function describePackage(options, importName) {
|
|
|
4651
4854
|
const dataIcons = preset?.icons?.render !== void 0 && preset.icons.render !== "export";
|
|
4652
4855
|
const isIcons = preset?.icons !== void 0 || looksLikeIcons(scan);
|
|
4653
4856
|
const tokens = /* @__PURE__ */ new Map();
|
|
4654
|
-
const styles = preset?.styles ??
|
|
4655
|
-
|
|
4857
|
+
const styles = preset?.styles ?? packageStylesheet(installed);
|
|
4858
|
+
const stylesheet = styles && packageEntry(installed, "style", styles);
|
|
4859
|
+
if (stylesheet) for (const token of cssTokens(await readStylesheet(stylesheet))) tokens.set(token.var, token);
|
|
4656
4860
|
let breakpoints;
|
|
4657
4861
|
if (preset?.theme) try {
|
|
4658
4862
|
const theme = await preset.theme(loader(root, dir));
|
|
@@ -4700,7 +4904,7 @@ async function describePackage(options, importName) {
|
|
|
4700
4904
|
if (preset?.icons?.variants) icons.variants = preset.icons.variants;
|
|
4701
4905
|
if (!dataIcons) {
|
|
4702
4906
|
const load = loader(root, dir);
|
|
4703
|
-
const modules = await styleModules(name, typeof pkg.exports === "object" && pkg.exports !== null ? Object.keys(pkg.exports) : [], iconImport ?? name, load, await iconDrawer(load));
|
|
4907
|
+
const modules = await styleModules(name, typeof pkg.exports === "object" && pkg.exports !== null && !Array.isArray(pkg.exports) ? Object.keys(pkg.exports) : [], iconImport ?? name, load, await iconDrawer(load));
|
|
4704
4908
|
if (modules) {
|
|
4705
4909
|
const main = Object.keys(modules).find((style) => modules[style] === (iconImport ?? name));
|
|
4706
4910
|
icons.variants = {
|
|
@@ -4841,7 +5045,7 @@ async function reviseLibrary(generated, options) {
|
|
|
4841
5045
|
const edits = options.edits ?? kept;
|
|
4842
5046
|
if (edits !== void 0) {
|
|
4843
5047
|
manifest.edits = edits;
|
|
4844
|
-
const resolved =
|
|
5048
|
+
const resolved = await readLibraryEdits(edits, resolve(options.editsBase ?? options.root));
|
|
4845
5049
|
const { orphans } = applyLibraryEdits(parseLibraryManifest(manifest), resolved);
|
|
4846
5050
|
if (orphans.length > 0) report.orphanEdits = orphans;
|
|
4847
5051
|
}
|
|
@@ -4931,7 +5135,7 @@ function summarizeParts(relations) {
|
|
|
4931
5135
|
const appExtensions = [".tsx", ".jsx"];
|
|
4932
5136
|
async function describeApp(options) {
|
|
4933
5137
|
const root = resolve(options.root);
|
|
4934
|
-
const pkg =
|
|
5138
|
+
const pkg = (await readPackage(root))?.manifest ?? {};
|
|
4935
5139
|
const notes = [];
|
|
4936
5140
|
const tsconfig = readTsconfig(root);
|
|
4937
5141
|
const { files } = await appFiles(root, { componentExtensions: appExtensions }, tsconfig);
|
|
@@ -4988,8 +5192,8 @@ async function describeApp(options) {
|
|
|
4988
5192
|
async function appManifestPath(root) {
|
|
4989
5193
|
const existing = (await readdir(root)).find((file) => file.endsWith(libraryManifestExtension));
|
|
4990
5194
|
if (existing) return join(root, existing);
|
|
4991
|
-
const
|
|
4992
|
-
return join(root, `${
|
|
5195
|
+
const name = (await readPackage(root))?.manifest.name ?? basename(root);
|
|
5196
|
+
return join(root, `${name.replace(/^@[^/]+\//, "")}${libraryManifestExtension}`);
|
|
4993
5197
|
}
|
|
4994
5198
|
/**
|
|
4995
5199
|
* A generated manifest laid over the file already there: what generating finds replaces what it found before, and
|
|
@@ -5050,56 +5254,6 @@ function defaultRange(version) {
|
|
|
5050
5254
|
const [major = "0", minor = "0"] = version.split(/[.-]/);
|
|
5051
5255
|
return major === "0" ? `^0.${minor}.0` : `^${major}.0.0`;
|
|
5052
5256
|
}
|
|
5053
|
-
function subpath(specifier, packageName) {
|
|
5054
|
-
return specifier === packageName ? "." : `.${specifier.slice(packageName.length)}`;
|
|
5055
|
-
}
|
|
5056
|
-
/** The type declarations for one of a package's entry points, from `exports`, else `types`, else `index.d.ts`. */
|
|
5057
|
-
function typesEntry(dir, pkg, entry) {
|
|
5058
|
-
const exported = typeof pkg.exports === "object" && pkg.exports !== null ? pkg.exports : {};
|
|
5059
|
-
const condition = (value) => {
|
|
5060
|
-
if (typeof value === "string") return /\.d\.[cm]?ts$/.test(value) ? value : void 0;
|
|
5061
|
-
if (typeof value !== "object" || value === null) return void 0;
|
|
5062
|
-
const record = value;
|
|
5063
|
-
for (const key of [
|
|
5064
|
-
"types",
|
|
5065
|
-
"import",
|
|
5066
|
-
"default",
|
|
5067
|
-
"require"
|
|
5068
|
-
]) {
|
|
5069
|
-
const found = condition(record[key]);
|
|
5070
|
-
if (found) return found;
|
|
5071
|
-
}
|
|
5072
|
-
};
|
|
5073
|
-
const candidates = [
|
|
5074
|
-
condition(exported[entry]),
|
|
5075
|
-
entry === "." ? pkg.types ?? pkg.typings : void 0,
|
|
5076
|
-
entry === "." ? "index.d.ts" : `${entry.slice(2)}/index.d.ts`,
|
|
5077
|
-
entry === "." ? void 0 : `${entry.slice(2)}.d.ts`
|
|
5078
|
-
];
|
|
5079
|
-
for (const candidate of candidates) if (candidate && existsSync(join(dir, candidate))) return join(dir, candidate).replaceAll("\\", "/");
|
|
5080
|
-
}
|
|
5081
|
-
/** The stylesheet a project imports to use a package: its `style` field, else the CSS its `exports` list first. */
|
|
5082
|
-
function stylesheetOf(dir, pkg) {
|
|
5083
|
-
if (pkg.style && existsSync(join(dir, pkg.style))) return pkg.style;
|
|
5084
|
-
const css = (typeof pkg.exports === "object" && pkg.exports !== null ? Object.keys(pkg.exports) : []).filter((key) => key.endsWith(".css")).map((key) => key.slice(2));
|
|
5085
|
-
const pick = [
|
|
5086
|
-
"tailwind.css",
|
|
5087
|
-
"styles.css",
|
|
5088
|
-
"style.css",
|
|
5089
|
-
"index.css",
|
|
5090
|
-
"theme.css"
|
|
5091
|
-
].find((name) => css.includes(name)) ?? css[0];
|
|
5092
|
-
if (!pick) return void 0;
|
|
5093
|
-
const target = pkg.exports[`./${pick}`];
|
|
5094
|
-
const file = typeof target === "string" ? target : target?.default;
|
|
5095
|
-
return typeof file === "string" && existsSync(join(dir, file)) ? pick : void 0;
|
|
5096
|
-
}
|
|
5097
|
-
/** The file an export such as `tailwind.css` points at: `dist/tailwind.css` for Buildkit's styles. */
|
|
5098
|
-
function exportedFile(dir, pkg, specifier) {
|
|
5099
|
-
const target = (typeof pkg.exports === "object" && pkg.exports !== null ? pkg.exports : {})[`./${specifier}`];
|
|
5100
|
-
const file = typeof target === "string" ? target : target?.default;
|
|
5101
|
-
return join(dir, typeof file === "string" ? file : specifier);
|
|
5102
|
-
}
|
|
5103
5257
|
/** A DTCG token file the package ships, if it has one in the format; Buildkit's `tokens.json` isn't yet. */
|
|
5104
5258
|
async function dtcgIn(dir) {
|
|
5105
5259
|
for (const candidate of [
|
|
@@ -5230,25 +5384,8 @@ function resolveRequire(from, specifier) {
|
|
|
5230
5384
|
}
|
|
5231
5385
|
/** An ESM-only package's entry, from the `import` condition of its `exports`, which `require` can't see. */
|
|
5232
5386
|
async function resolveImport(from, specifier) {
|
|
5233
|
-
const name
|
|
5234
|
-
const
|
|
5235
|
-
|
|
5236
|
-
const pkg = JSON.parse(await readFile(join(dir, "package.json"), "utf8"));
|
|
5237
|
-
const exported = typeof pkg.exports === "object" && pkg.exports !== null ? pkg.exports : {};
|
|
5238
|
-
const pick = (value) => {
|
|
5239
|
-
if (typeof value === "string") return /\.d\.[cm]?ts$/.test(value) ? void 0 : value;
|
|
5240
|
-
if (typeof value !== "object" || value === null) return void 0;
|
|
5241
|
-
const record = value;
|
|
5242
|
-
for (const key of [
|
|
5243
|
-
"node",
|
|
5244
|
-
"import",
|
|
5245
|
-
"default"
|
|
5246
|
-
]) {
|
|
5247
|
-
const found = pick(record[key]);
|
|
5248
|
-
if (found) return found;
|
|
5249
|
-
}
|
|
5250
|
-
};
|
|
5251
|
-
const file = pick(exported[subpath(specifier, name)]) ?? (specifier === name ? pkg.module : void 0);
|
|
5252
|
-
return file ? join(dir, file) : void 0;
|
|
5387
|
+
const { name, subpath } = splitSpecifier(specifier);
|
|
5388
|
+
const installed = await findPackage(from, name);
|
|
5389
|
+
return installed && packageEntry(installed, "runtime", subpath);
|
|
5253
5390
|
}
|
|
5254
5391
|
export { appManifestPath, generateLibrary, manifestText, mergeManifest, parseLibraryEdits, reviseLibrary, iconVariants as t };
|
package/package.json
CHANGED
package/skills/SKILL.md
CHANGED
|
@@ -27,22 +27,22 @@ The same tools are MCP tools and `parity` commands in a terminal. They act on a
|
|
|
27
27
|
the active tab of the Parity window they used last. When the person says "this" or "what I've selected" without naming a
|
|
28
28
|
project, pass `tab: "active"` (`--tab active`) and read `get_app_state` for the selection.
|
|
29
29
|
|
|
30
|
-
| MCP | CLI | What it does
|
|
31
|
-
| --------------------- | ---------------------------- |
|
|
32
|
-
| `get_app_state` | `parity state` | Designs, the selection, modes, components and token names, and what Parity detected (builder and config, package manager, installed versions). Start here. |
|
|
33
|
-
| `get_status` | `parity status` | The revision, whether anything is unsaved and which files and tokens, the last save, and undo and redo; `summary` names them.
|
|
34
|
-
| `get_modes` | `parity modes [design]` | Viewports (the ids `Responsive` takes), modes (the ids renders take) and what each design shows; a design adds its frames.
|
|
35
|
-
| `execute` | `parity exec` | Runs a JavaScript snippet against the designs. Its changes land as one undo step, merged per design with edits made meanwhile.
|
|
36
|
-
| `screenshot` | `parity screenshot <design>` | Renders a design in its states and modes, offscreen, and returns the images.
|
|
37
|
-
| `find_problems` | `parity problems <design>` | Render errors, overflow, clipping and unknown components, per frame.
|
|
38
|
-
| `check_accessibility` | `parity a11y <design>` | axe-core against WCAG 2.2 AA, per frame, on the layers that break each rule.
|
|
39
|
-
| `get_console` | `parity console` | What the project's own code wrote to the console, on the canvas and offscreen.
|
|
40
|
-
| `get_tokens` | `parity tokens` | Tokens filtered by category, source or name, with values in every theme.
|
|
41
|
-
| `get_components` | `parity components` | Components, libraries, icon sets and exports left out or failing to render.
|
|
42
|
-
| `get_props` | `parity props <Component>` | Props from source, then what it goes inside and holds, its slots and an example to insert; `all` adds passed-through attributes.
|
|
43
|
-
| `get_tabs` | `parity tabs` | The open projects.
|
|
44
|
-
| `open_project` | `parity open <folder>` | Opens a project in a new tab.
|
|
45
|
-
| `read_skill` | `parity skill [file]` | This skill.
|
|
30
|
+
| MCP | CLI | What it does |
|
|
31
|
+
| --------------------- | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
32
|
+
| `get_app_state` | `parity state` | Designs, the selection, modes (the primary one marked), components and token names, and what Parity detected (builder and config, package manager, installed versions). Start here. |
|
|
33
|
+
| `get_status` | `parity status` | The revision, whether anything is unsaved and which files and tokens, the last save, and undo and redo; `summary` names them. |
|
|
34
|
+
| `get_modes` | `parity modes [design]` | Viewports (the ids `Responsive` takes), modes (the ids renders take) and what each design shows; a design adds its frames. |
|
|
35
|
+
| `execute` | `parity exec` | Runs a JavaScript snippet against the designs. Its changes land as one undo step, merged per design with edits made meanwhile. |
|
|
36
|
+
| `screenshot` | `parity screenshot <design>` | Renders a design in its states and modes, offscreen, and returns the images. |
|
|
37
|
+
| `find_problems` | `parity problems <design>` | Render errors, overflow, clipping and unknown components, per frame. |
|
|
38
|
+
| `check_accessibility` | `parity a11y <design>` | axe-core against WCAG 2.2 AA, per frame, on the layers that break each rule. |
|
|
39
|
+
| `get_console` | `parity console` | What the project's own code wrote to the console, on the canvas and offscreen. |
|
|
40
|
+
| `get_tokens` | `parity tokens` | Tokens filtered by category, source or name, with values in every theme. |
|
|
41
|
+
| `get_components` | `parity components` | Components, libraries, icon sets and exports left out or failing to render. |
|
|
42
|
+
| `get_props` | `parity props <Component>` | Props from source, then what it goes inside and holds, its slots and an example to insert; `all` adds passed-through attributes. |
|
|
43
|
+
| `get_tabs` | `parity tabs` | The open projects. |
|
|
44
|
+
| `open_project` | `parity open <folder>` | Opens a project in a new tab. |
|
|
45
|
+
| `read_skill` | `parity skill [file]` | This skill. |
|
|
46
46
|
|
|
47
47
|
The token, component and prop tools read what designs can use without writing a snippet in either transport.
|
|
48
48
|
`parity components` also says which of the app's exports aren't components and why, which components fail to draw on the
|