@driftime/sanity-plugin-icon 0.2.0 → 0.4.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.
Files changed (56) hide show
  1. package/README.md +341 -113
  2. package/dist/adapters-Bx_MyEX_.js +633 -0
  3. package/dist/adapters-Bx_MyEX_.js.map +1 -0
  4. package/dist/drawing-ByrcBbWB.js +50 -0
  5. package/dist/drawing-ByrcBbWB.js.map +1 -0
  6. package/dist/index.d.ts +85 -33
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +746 -443
  9. package/dist/index.js.map +1 -1
  10. package/dist/names.d.ts +8 -0
  11. package/dist/names.d.ts.map +1 -0
  12. package/dist/names.js +0 -0
  13. package/dist/next-loader.d.ts +64 -0
  14. package/dist/next-loader.d.ts.map +1 -0
  15. package/dist/next-loader.js +102 -0
  16. package/dist/next-loader.js.map +1 -0
  17. package/dist/next.d.ts +43 -0
  18. package/dist/next.d.ts.map +1 -0
  19. package/dist/next.js +51 -0
  20. package/dist/next.js.map +1 -0
  21. package/dist/nodes-DAzj56Xn.js +139 -0
  22. package/dist/nodes-DAzj56Xn.js.map +1 -0
  23. package/dist/read-worker.d.ts +1 -0
  24. package/dist/read-worker.js +23 -0
  25. package/dist/read-worker.js.map +1 -0
  26. package/dist/registry-BEEpqK3z.js +13 -0
  27. package/dist/registry-BEEpqK3z.js.map +1 -0
  28. package/dist/registry-dxVzo7ZX.js +58 -0
  29. package/dist/registry-dxVzo7ZX.js.map +1 -0
  30. package/dist/render.d.ts +8 -9
  31. package/dist/render.d.ts.map +1 -1
  32. package/dist/render.js +5 -6
  33. package/dist/render.js.map +1 -1
  34. package/dist/sets-DVh8eYeP.js +258 -0
  35. package/dist/sets-DVh8eYeP.js.map +1 -0
  36. package/dist/sets-kepuObH8.js +90 -0
  37. package/dist/sets-kepuObH8.js.map +1 -0
  38. package/dist/sets.d.ts +50 -0
  39. package/dist/sets.d.ts.map +1 -0
  40. package/dist/sets.js +14 -0
  41. package/dist/sets.js.map +1 -0
  42. package/dist/types-qlHEYDoN.d.ts +20 -0
  43. package/dist/types-qlHEYDoN.d.ts.map +1 -0
  44. package/dist/utils-COPacfAq.js +48 -0
  45. package/dist/utils-COPacfAq.js.map +1 -0
  46. package/dist/vite-ifICG8y9.d.ts +98 -0
  47. package/dist/vite-ifICG8y9.d.ts.map +1 -0
  48. package/dist/vite.d.ts +2 -0
  49. package/dist/vite.js +88 -0
  50. package/dist/vite.js.map +1 -0
  51. package/package.json +58 -30
  52. package/LICENSE +0 -21
  53. package/dist/drawing-Bo0HTICv.js +0 -120
  54. package/dist/drawing-Bo0HTICv.js.map +0 -1
  55. package/dist/types-_D2jGm25.d.ts +0 -16
  56. package/dist/types-_D2jGm25.d.ts.map +0 -1
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Options for the build step that converts icon libraries for the Studio.
3
+ *
4
+ * @public
5
+ */
6
+ interface SanityIconBuildOptions {
7
+ /** Folders of the project's own SVG icons, keyed by the identifier fields use, such as `{ acme: "./icons" }`. */
8
+ custom?: Record<string, string>;
9
+ }
10
+ /**
11
+ * A Vite plugin, limited to the hooks the build step uses, so the package doesn't depend on Vite's own types.
12
+ *
13
+ * @public
14
+ */
15
+ interface SanityIconVitePlugin {
16
+ /** Name Vite reports the plugin under. */
17
+ name: string;
18
+ /**
19
+ * Keeps the placeholder registry out of Vite's pre-bundling in development, which would fix it in place, while the
20
+ * rest of the plugin and its dependencies are pre-bundled as usual.
21
+ *
22
+ * @returns The configuration to merge.
23
+ */
24
+ config: () => {
25
+ optimizeDeps: {
26
+ exclude: string[];
27
+ };
28
+ };
29
+ /**
30
+ * Reads the project folder and cache folder once Vite has resolved its configuration.
31
+ *
32
+ * @param config - Vite's resolved configuration.
33
+ */
34
+ configResolved: (config: {
35
+ root: string;
36
+ cacheDir: string;
37
+ }) => Promise<void>;
38
+ /**
39
+ * Watches the project's own icon folders in development, reloading the Studio when an icon is added, changed, or
40
+ * removed.
41
+ *
42
+ * @param server - Vite's development server.
43
+ */
44
+ configureServer: (server: {
45
+ watcher: {
46
+ add: (paths: string[]) => void;
47
+ on: (event: "all", listener: (event: string, file: string) => void) => void;
48
+ };
49
+ moduleGraph: {
50
+ invalidateAll: () => void;
51
+ };
52
+ ws: {
53
+ send: (payload: {
54
+ type: "full-reload";
55
+ }) => void;
56
+ };
57
+ }) => void;
58
+ /** Lists the installed icon names in the plugin's types when a build or dev server starts. */
59
+ buildStart: () => Promise<void>;
60
+ /**
61
+ * Claims the modules each set is loaded from.
62
+ *
63
+ * @param id - The module requested.
64
+ * @returns The module's resolved id, or undefined when it isn't a set.
65
+ */
66
+ resolveId: (id: string) => string | undefined;
67
+ /**
68
+ * Generates the registry in place of the placeholder, and each set's module.
69
+ *
70
+ * @param this - Vite's plugin context, used to watch the files the registry depends on.
71
+ * @param id - The resolved module id.
72
+ * @returns The module's code, or undefined when it isn't the build step's.
73
+ */
74
+ load: (this: {
75
+ addWatchFile?: (file: string) => void;
76
+ }, id: string) => Promise<string | undefined>;
77
+ }
78
+ /**
79
+ * Creates the Vite plugin that converts the project's installed icon libraries when the Studio starts or builds.
80
+ *
81
+ * @param options - Options for the build step.
82
+ * @returns The Vite plugin.
83
+ * @public
84
+ */
85
+ declare function iconLibraries(options?: SanityIconBuildOptions): SanityIconVitePlugin;
86
+ /**
87
+ * Adds the icon build step to a Vite configuration, for the `vite` option in `sanity.cli.ts` or a framework's own
88
+ * Vite settings.
89
+ *
90
+ * @param options - Options for the build step.
91
+ * @returns A function that extends the Vite configuration.
92
+ * @public
93
+ */
94
+ declare function withIcons(options?: SanityIconBuildOptions): <Config extends {
95
+ plugins?: unknown[];
96
+ }>(config: Config) => Config;
97
+ export { withIcons as i, SanityIconVitePlugin as n, iconLibraries as r, SanityIconBuildOptions as t };
98
+ //# sourceMappingURL=vite-ifICG8y9.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite-ifICG8y9.d.ts","names":["SanityIconBuildOptions","Record","SanityIconVitePlugin","Promise","iconLibraries","withIcons","Config"],"sources":["../src/build/vite.ts"],"mappings":"AAeA;;;;;UAAiBA;;EAEf,SAASC;;;;;;;UAQMC;;EAEf;;;;;;;EAOA;IAAgB;MAAgB;;;;;;;;EAMhC,iBAAiB;IAAU;IAAc;QAAuBC;;;;;;;EAOhE,kBAAkB;IAChB;MACE,MAAM;MACN,KAAK,cAAc,WAAW,eAAe;;IAE/C;MAAe;;IACf;MAAM,OAAO;QAAW;;;;;EAG1B,kBAAkBA;;;;;;;EAOlB,YAAY;;;;;;;;EAQZ,OAAO;IAAQ,gBAAgB;KAAyB,eAAeA;;;;;;;;;iBAazDC,cAAc,UAASJ,yBAA8BE;;;;;;;;;iBAkFrDG,UAAU,UAASL,0BACJ;EAAiB;GAAqB,QAAUM,WAASA"}
package/dist/vite.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import { i as withIcons, n as SanityIconVitePlugin, r as iconLibraries } from "./vite-ifICG8y9.js";
2
+ export { type SanityIconVitePlugin, iconLibraries, withIcons };
package/dist/vite.js ADDED
@@ -0,0 +1,88 @@
1
+ import { n as logger, r as pluginName } from "./sets-kepuObH8.js";
2
+ import { n as isDefined } from "./utils-COPacfAq.js";
3
+ import { n as isRegistryModule, t as createRegistryModule } from "./registry-dxVzo7ZX.js";
4
+ import { a as writeIconNames, i as loadIconSet, n as detectLibraries, r as listRegistryDependencies, t as describeReport } from "./sets-DVh8eYeP.js";
5
+ import path from "node:path";
6
+ /** Prefix of the module each set is loaded from, followed by its library and style. */
7
+ const setPrefix = `virtual:${pluginName}/set/`;
8
+ /**
9
+ * Creates the Vite plugin that converts the project's installed icon libraries when the Studio starts or builds.
10
+ *
11
+ * @param options - Options for the build step.
12
+ * @returns The Vite plugin.
13
+ * @public
14
+ */
15
+ function iconLibraries(options = {}) {
16
+ let root = process.cwd(), cacheDir, libraries = [];
17
+ return {
18
+ name: pluginName,
19
+ config() {
20
+ return { optimizeDeps: { exclude: [`${pluginName}/sets`] } };
21
+ },
22
+ async configResolved(config) {
23
+ ({root, cacheDir} = config), libraries = await detectLibraries(root, options.custom);
24
+ },
25
+ configureServer(server) {
26
+ let folders = Object.values(options.custom ?? {}).map((folder) => path.resolve(root, folder));
27
+ isDefined(folders) && (server.watcher.add(folders), server.watcher.on("all", (_event, file) => {
28
+ folders.some((folder) => file.startsWith(`${folder}${path.sep}`)) && (server.moduleGraph.invalidateAll(), server.ws.send({ type: "full-reload" }));
29
+ }));
30
+ },
31
+ async buildStart() {
32
+ let names = /* @__PURE__ */ new Map(), loaded = await Promise.all(libraries.flatMap((library) => library.styles.map(async ({ style }) => {
33
+ let { set } = await loadIconSet(library, style, {
34
+ root,
35
+ cacheDir
36
+ });
37
+ return {
38
+ library: library.adapter.library,
39
+ set
40
+ };
41
+ })));
42
+ for (let { library, set } of loaded) {
43
+ let libraryNames = names.get(library) ?? /* @__PURE__ */ new Set();
44
+ for (let icon of set.icons) libraryNames.add(icon.name);
45
+ names.set(library, libraryNames);
46
+ }
47
+ await writeIconNames(names);
48
+ },
49
+ resolveId(id) {
50
+ return id.startsWith(setPrefix) ? `\0${id}` : void 0;
51
+ },
52
+ async load(id) {
53
+ if (isRegistryModule(id)) {
54
+ libraries = await detectLibraries(root, options.custom);
55
+ let { files } = listRegistryDependencies(root, libraries);
56
+ for (let file of files) this.addWatchFile?.(file);
57
+ return createRegistryModule(libraries, (key) => `${setPrefix}${key}`);
58
+ }
59
+ if (!id.startsWith(`\0${setPrefix}`)) return;
60
+ let [libraryName, style] = id.slice(`\0${setPrefix}`.length).split("/"), library = libraries.find(({ adapter }) => adapter.library === libraryName);
61
+ if (!isDefined(library) || !isDefined(style)) return;
62
+ let { set, report } = await loadIconSet(library, style, {
63
+ root,
64
+ cacheDir
65
+ });
66
+ return logger.info(describeReport(library.adapter.label, style, report)), `export default JSON.parse(${JSON.stringify(JSON.stringify(set))});`;
67
+ }
68
+ };
69
+ }
70
+ /**
71
+ * Adds the icon build step to a Vite configuration, for the `vite` option in `sanity.cli.ts` or a framework's own
72
+ * Vite settings.
73
+ *
74
+ * @param options - Options for the build step.
75
+ * @returns A function that extends the Vite configuration.
76
+ * @public
77
+ */
78
+ function withIcons(options = {}) {
79
+ return function extendConfig(config) {
80
+ return {
81
+ ...config,
82
+ plugins: [...config.plugins ?? [], iconLibraries(options)]
83
+ };
84
+ };
85
+ }
86
+ export { iconLibraries, withIcons };
87
+
88
+ //# sourceMappingURL=vite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite.js","names":["path","isDefined","writeIconNames","createRegistryModule","isRegistryModule","DetectedLibrary","describeReport","detectLibraries","listRegistryDependencies","loadIconSet","logger","pluginName","SanityIconBuildOptions","custom","Record","SanityIconVitePlugin","name","config","optimizeDeps","exclude","configResolved","root","cacheDir","Promise","configureServer","server","watcher","add","paths","on","event","listener","file","moduleGraph","invalidateAll","ws","send","payload","type","buildStart","resolveId","id","load","this","addWatchFile","setPrefix","iconLibraries","options","process","cwd","undefined","libraries","folders","Object","values","map","folder","resolve","_event","some","startsWith","sep","names","Map","Set","loaded","all","flatMap","library","styles","style","set","adapter","libraryNames","get","icon","icons","files","key","libraryName","slice","length","split","find","report","info","label","JSON","stringify","withIcons","extendConfig","plugins","Config"],"sources":["../src/build/vite.ts"],"sourcesContent":["import path from \"node:path\";\n\nimport { isDefined } from \"@repo/lib/utils\";\n\nimport { writeIconNames } from \"@/build/names\";\nimport { createRegistryModule, isRegistryModule } from \"@/build/registry\";\nimport type { DetectedLibrary } from \"@/build/sets\";\nimport { describeReport, detectLibraries, listRegistryDependencies, loadIconSet } from \"@/build/sets\";\nimport { logger, pluginName } from \"@/config/defaults\";\n\n/**\n * Options for the build step that converts icon libraries for the Studio.\n *\n * @public\n */\nexport interface SanityIconBuildOptions {\n /** Folders of the project's own SVG icons, keyed by the identifier fields use, such as `{ acme: \"./icons\" }`. */\n custom?: Record<string, string>;\n}\n\n/**\n * A Vite plugin, limited to the hooks the build step uses, so the package doesn't depend on Vite's own types.\n *\n * @public\n */\nexport interface SanityIconVitePlugin {\n /** Name Vite reports the plugin under. */\n name: string;\n /**\n * Keeps the placeholder registry out of Vite's pre-bundling in development, which would fix it in place, while the\n * rest of the plugin and its dependencies are pre-bundled as usual.\n *\n * @returns The configuration to merge.\n */\n config: () => { optimizeDeps: { exclude: string[] } };\n /**\n * Reads the project folder and cache folder once Vite has resolved its configuration.\n *\n * @param config - Vite's resolved configuration.\n */\n configResolved: (config: { root: string; cacheDir: string }) => Promise<void>;\n /**\n * Watches the project's own icon folders in development, reloading the Studio when an icon is added, changed, or\n * removed.\n *\n * @param server - Vite's development server.\n */\n configureServer: (server: {\n watcher: {\n add: (paths: string[]) => void;\n on: (event: \"all\", listener: (event: string, file: string) => void) => void;\n };\n moduleGraph: { invalidateAll: () => void };\n ws: { send: (payload: { type: \"full-reload\" }) => void };\n }) => void;\n /** Lists the installed icon names in the plugin's types when a build or dev server starts. */\n buildStart: () => Promise<void>;\n /**\n * Claims the modules each set is loaded from.\n *\n * @param id - The module requested.\n * @returns The module's resolved id, or undefined when it isn't a set.\n */\n resolveId: (id: string) => string | undefined;\n /**\n * Generates the registry in place of the placeholder, and each set's module.\n *\n * @param this - Vite's plugin context, used to watch the files the registry depends on.\n * @param id - The resolved module id.\n * @returns The module's code, or undefined when it isn't the build step's.\n */\n load: (this: { addWatchFile?: (file: string) => void }, id: string) => Promise<string | undefined>;\n}\n\n/** Prefix of the module each set is loaded from, followed by its library and style. */\nconst setPrefix = `virtual:${pluginName}/set/`;\n\n/**\n * Creates the Vite plugin that converts the project's installed icon libraries when the Studio starts or builds.\n *\n * @param options - Options for the build step.\n * @returns The Vite plugin.\n * @public\n */\nexport function iconLibraries(options: SanityIconBuildOptions = {}): SanityIconVitePlugin {\n let root = process.cwd();\n let cacheDir: string | undefined = undefined;\n let libraries: DetectedLibrary[] = [];\n\n return {\n name: pluginName,\n config() {\n return { optimizeDeps: { exclude: [`${pluginName}/sets`] } };\n },\n async configResolved(config) {\n ({ root, cacheDir } = config);\n libraries = await detectLibraries(root, options.custom);\n },\n configureServer(server) {\n const folders = Object.values(options.custom ?? {}).map((folder) => path.resolve(root, folder));\n if (!isDefined(folders)) return;\n\n server.watcher.add(folders);\n server.watcher.on(\"all\", (_event, file) => {\n if (!folders.some((folder) => file.startsWith(`${folder}${path.sep}`))) return;\n\n server.moduleGraph.invalidateAll();\n server.ws.send({ type: \"full-reload\" });\n });\n },\n async buildStart() {\n const names = new Map<string, Set<string>>();\n const loaded = await Promise.all(\n libraries.flatMap((library) =>\n library.styles.map(async ({ style }) => {\n const { set } = await loadIconSet(library, style, { root, cacheDir });\n\n return { library: library.adapter.library, set };\n }),\n ),\n );\n\n for (const { library, set } of loaded) {\n const libraryNames = names.get(library) ?? new Set<string>();\n\n for (const icon of set.icons) libraryNames.add(icon.name);\n names.set(library, libraryNames);\n }\n\n await writeIconNames(names);\n },\n resolveId(id) {\n return id.startsWith(setPrefix) ? `\\0${id}` : undefined;\n },\n async load(id) {\n if (isRegistryModule(id)) {\n libraries = await detectLibraries(root, options.custom);\n\n // Vite treats watched files as imports in development, so folders are watched by the server instead.\n const { files } = listRegistryDependencies(root, libraries);\n for (const file of files) this.addWatchFile?.(file);\n\n return createRegistryModule(libraries, (key) => `${setPrefix}${key}`);\n }\n if (!id.startsWith(`\\0${setPrefix}`)) return undefined;\n\n const [libraryName, style] = id.slice(`\\0${setPrefix}`.length).split(\"/\");\n const library = libraries.find(({ adapter }) => adapter.library === libraryName);\n if (!isDefined(library) || !isDefined(style)) return undefined;\n\n const { set, report } = await loadIconSet(library, style, { root, cacheDir });\n logger.info(describeReport(library.adapter.label, style, report));\n\n return `export default JSON.parse(${JSON.stringify(JSON.stringify(set))});`;\n },\n };\n}\n\n/**\n * Adds the icon build step to a Vite configuration, for the `vite` option in `sanity.cli.ts` or a framework's own\n * Vite settings.\n *\n * @param options - Options for the build step.\n * @returns A function that extends the Vite configuration.\n * @public\n */\nexport function withIcons(options: SanityIconBuildOptions = {}) {\n return function extendConfig<Config extends { plugins?: unknown[] }>(config: Config): Config {\n return { ...config, plugins: [...(config.plugins ?? []), iconLibraries(options)] };\n };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,15 +1,26 @@
1
1
  {
2
2
  "name": "@driftime/sanity-plugin-icon",
3
- "version": "0.2.0",
4
- "description": "Lucide icons for Sanity Studio, stored as drawings and rendered without the library.",
3
+ "version": "0.4.0",
4
+ "description": "Icons for Sanity Studio, managed as content by the authors who use them.",
5
5
  "keywords": [
6
+ "central-icons",
7
+ "heroicons",
8
+ "hugeicons",
9
+ "icon",
10
+ "icon-picker",
11
+ "iconoir",
12
+ "icons",
13
+ "lucide",
14
+ "nucleo",
15
+ "phosphor-icons",
6
16
  "sanity",
7
- "sanity-plugin"
17
+ "sanity-plugin",
18
+ "svg",
19
+ "tabler-icons"
8
20
  ],
9
- "homepage": "https://github.com/driftime/sanity-plugin-icon",
21
+ "homepage": "https://github.com/driftime/sanity-plugins/tree/main/packages/icon#readme",
10
22
  "bugs": {
11
- "url": "https://github.com/driftime/sanity-plugin-icon/issues",
12
- "email": "kenny@driftime.com"
23
+ "url": "https://github.com/driftime/sanity-plugins/issues"
13
24
  },
14
25
  "license": "MIT",
15
26
  "author": {
@@ -19,7 +30,8 @@
19
30
  },
20
31
  "repository": {
21
32
  "type": "git",
22
- "url": "git+ssh://git@github.com/driftime/sanity-plugin-icon.git"
33
+ "url": "git+https://github.com/driftime/sanity-plugins.git",
34
+ "directory": "packages/icon"
23
35
  },
24
36
  "files": [
25
37
  "dist"
@@ -36,51 +48,67 @@
36
48
  "source": "./src/render.ts",
37
49
  "default": "./dist/render.js"
38
50
  },
51
+ "./vite": {
52
+ "source": "./src/vite.ts",
53
+ "default": "./dist/vite.js"
54
+ },
55
+ "./next": {
56
+ "source": "./src/next.ts",
57
+ "default": "./dist/next.js"
58
+ },
59
+ "./next-loader": {
60
+ "source": "./src/next-loader.ts",
61
+ "default": "./dist/next-loader.js"
62
+ },
63
+ "./read-worker": {
64
+ "source": "./src/read-worker.ts",
65
+ "default": "./dist/read-worker.js"
66
+ },
67
+ "./sets": {
68
+ "source": "./src/sets.ts",
69
+ "default": "./dist/sets.js"
70
+ },
71
+ "./names": {
72
+ "source": "./src/names.ts",
73
+ "default": "./dist/names.js"
74
+ },
39
75
  "./package.json": "./package.json"
40
76
  },
41
77
  "publishConfig": {
42
78
  "exports": {
43
79
  ".": "./dist/index.js",
44
80
  "./render": "./dist/render.js",
81
+ "./vite": "./dist/vite.js",
82
+ "./next": "./dist/next.js",
83
+ "./next-loader": "./dist/next-loader.js",
84
+ "./read-worker": "./dist/read-worker.js",
85
+ "./sets": "./dist/sets.js",
86
+ "./names": "./dist/names.js",
45
87
  "./package.json": "./package.json"
46
88
  },
47
89
  "access": "public"
48
90
  },
49
91
  "scripts": {
50
92
  "typecheck": "tsc",
51
- "check": "oxfmt --check && oxlint",
52
- "fix": "oxfmt && oxlint --fix",
53
93
  "dev": "plugin-kit link-watch",
54
- "watch": "pkg-utils watch --strict",
55
- "build": "bun run check && bun run typecheck && plugin-kit verify-package --silent && pkg-utils build --strict --check --clean && node --check dist/index.js",
56
- "prepublishOnly": "bun run build"
94
+ "build": "bun run typecheck && plugin-kit verify-package --silent && pkg-utils build --strict --check --clean && node --check dist/index.js && node --check dist/render.js && node --check dist/vite.js && node --check dist/next.js",
95
+ "prepublishOnly": "bun run build",
96
+ "push": "yalc push"
57
97
  },
58
98
  "dependencies": {
59
- "@sanity/client": "^7.26.2",
99
+ "@sanity/client": "^7.26.2 || ^8.0.0",
60
100
  "@sanity/icons": "^5.2.1",
61
101
  "@sanity/ui": "^4.0.3",
62
- "lucide-static": "^1.43.0"
102
+ "svgo": "^4.1.0"
63
103
  },
64
104
  "devDependencies": {
65
- "@sanity/browserslist-config": "1.0.5",
66
- "@sanity/pkg-utils": "12.1.2",
67
- "@sanity/plugin-kit": "10.0.5",
68
- "@sanity/tsconfig": "2.2.1",
69
- "@types/node": "26.2.0",
70
- "@types/react": "19.2.18",
71
- "@types/react-dom": "19.2.4",
72
- "babel-plugin-react-compiler": "1.0.0",
73
- "oxfmt": "0.63.0",
74
- "oxlint": "1.78.0",
75
- "oxlint-tsgolint": "7.0.2001",
76
- "react": "19.2.8",
77
- "react-dom": "19.2.8",
78
- "sanity": "6.9.2",
79
- "typescript": "7.0.2"
105
+ "@repo/components": "workspace:*",
106
+ "@repo/lib": "workspace:*",
107
+ "@sanity/pkg-utils": "catalog:"
80
108
  },
81
109
  "peerDependencies": {
82
110
  "react": "19",
83
- "sanity": "6"
111
+ "sanity": "^6.10.0"
84
112
  },
85
113
  "browserslist": "extends @sanity/browserslist-config",
86
114
  "engines": {
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Driftime®
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
@@ -1,120 +0,0 @@
1
- import { jsx } from "react/jsx-runtime";
2
- import { createElement } from "react";
3
- import { stegaClean } from "@sanity/client/stega";
4
- /**
5
- * Checks whether a value is defined and non-empty, treating `false`, empty strings, and objects with
6
- * no keys as absent. An array is absent unless one of its elements is itself present, while anything
7
- * built from a class counts as present on existence alone. Not intended for boolean flags.
8
- *
9
- * @param value - The value to check.
10
- * @returns True if the value is defined and not empty.
11
- */
12
- function isDefined(value) {
13
- if (value == null || value === !1) return !1;
14
- if (typeof value == "string") return value.trim() !== "";
15
- if (Array.isArray(value)) return value.some((element) => isDefined(element));
16
- if (typeof value == "object") {
17
- let prototype = Reflect.getPrototypeOf(value);
18
- return prototype !== Object.prototype && prototype !== null || Object.keys(value).length > 0;
19
- }
20
- return !0;
21
- }
22
- /**
23
- * Converts a string between different casing formats.
24
- *
25
- * @param value - The string to convert.
26
- * @param format - The target casing format.
27
- * @returns The converted string.
28
- */
29
- function convertCase(value, format) {
30
- let words = value.replaceAll(/(?<lower>[a-z0-9])(?<upper>[A-Z])/gu, "$<lower> $<upper>").replaceAll(/[-_\s]+/gu, " ").trim().toLowerCase();
31
- switch (format) {
32
- case "kebab": return words.replaceAll(/\s+/gu, "-");
33
- case "snake": return words.replaceAll(/\s+/gu, "_");
34
- case "camel": return words.replaceAll(/\s+(?<character>.)/gu, (_match, character) => character.toUpperCase());
35
- case "pascal": return words.replaceAll(/(?:^|\s+)(?<character>.)/gu, (_match, character) => character.toUpperCase());
36
- case "title": return words.replaceAll(/\b\w/gu, (character) => character.toUpperCase());
37
- case "sentence": return words.replaceAll(/^(?<character>.)/gu, (character) => character.toUpperCase());
38
- default: return value;
39
- }
40
- }
41
- /** Elements an icon's drawing may be built from, narrowed to those that describe a shape. */
42
- const iconElements = [
43
- "circle",
44
- "ellipse",
45
- "g",
46
- "line",
47
- "path",
48
- "polygon",
49
- "polyline",
50
- "rect"
51
- ], iconRootAttributes = {
52
- xmlns: "http://www.w3.org/2000/svg",
53
- width: 24,
54
- height: 24,
55
- viewBox: "0 0 24 24",
56
- fill: "none",
57
- stroke: "currentColor",
58
- strokeWidth: 2,
59
- strokeLinecap: "round",
60
- strokeLinejoin: "round"
61
- };
62
- /**
63
- * Checks whether a parsed value is an icon's drawing, since it arrives as whatever happened to be
64
- * stored. Shapes are matched against the known elements so nothing else reaches the renderer.
65
- *
66
- * @param value - The value to check.
67
- * @returns True if the value is a drawing.
68
- */
69
- function isIconNode(value) {
70
- return Array.isArray(value) ? value.every((element) => Array.isArray(element) && element.length === 2 && iconElements.some((name) => name === element[0]) && typeof element[1] == "object" && element[1] !== null) : !1;
71
- }
72
- /**
73
- * Reads an icon's drawing from an already-parsed value, for a library handing over its drawings as
74
- * data rather than as a document field.
75
- *
76
- * @param value - The value to read.
77
- * @returns The drawing, or undefined when the value is not one.
78
- */
79
- function parseIconNode(value) {
80
- return isIconNode(value) ? value : void 0;
81
- }
82
- /**
83
- * Reads an icon's drawing back from the form it is stored in, clearing the stega characters a Sanity
84
- * fetch leaves behind.
85
- *
86
- * @param value - The stored drawing.
87
- * @returns The drawing, or undefined when nothing readable was stored.
88
- */
89
- function resolveIconNode(value) {
90
- let cleaned = stegaClean(value);
91
- if (isDefined(cleaned)) try {
92
- return parseIconNode(JSON.parse(cleaned));
93
- } catch {
94
- return;
95
- }
96
- }
97
- /**
98
- * Writes an icon's drawing into the form it is stored in, so the shapes survive a round trip through
99
- * a document rather than a component reference that only resolves at build time.
100
- *
101
- * @param node - The drawing to store.
102
- * @returns The drawing ready to store.
103
- */
104
- function serialiseIconNode(node) {
105
- return JSON.stringify(node);
106
- }
107
- function Drawing({ node, ...props }) {
108
- return isDefined(node) ? /* @__PURE__ */ jsx("svg", {
109
- ...iconRootAttributes,
110
- "aria-hidden": "true",
111
- ...props,
112
- children: node.map(([element, attributes], index) => createElement(element, {
113
- ...attributes,
114
- key: String(index)
115
- }))
116
- }) : null;
117
- }
118
- export { convertCase as a, serialiseIconNode as i, parseIconNode as n, isDefined as o, resolveIconNode as r, Drawing as t };
119
-
120
- //# sourceMappingURL=drawing-Bo0HTICv.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"drawing-Bo0HTICv.js","names":["Nullable","isDefined","value","T","undefined","trim","Array","isArray","some","element","prototype","Reflect","getPrototypeOf","Object","keys","length","convertCase","format","words","replaceAll","toLowerCase","_match","character","toUpperCase","stegaClean","isDefined","iconElements","const","IconElement","element","attributes","Record","IconNode","iconRootAttributes","xmlns","width","height","viewBox","fill","stroke","strokeWidth","strokeLinecap","strokeLinejoin","isIconNode","value","Array","isArray","every","length","some","name","parseIconNode","undefined","resolveIconNode","cleaned","parsed","JSON","parse","serialiseIconNode","node","stringify","ComponentProps","createElement","IconNode","iconRootAttributes","isDefined","DrawingProps","Omit","node","Drawing","props","map","element","attributes","index","key","String"],"sources":["../src/lib/utils.ts","../src/lib/nodes.ts","../src/components/drawing.tsx"],"sourcesContent":["import type { Nullable } from \"@/types\";\n\n/**\n * Checks whether a value is defined and non-empty, treating `false`, empty strings, and objects with\n * no keys as absent. An array is absent unless one of its elements is itself present, while anything\n * built from a class counts as present on existence alone. Not intended for boolean flags.\n *\n * @param value - The value to check.\n * @returns True if the value is defined and not empty.\n */\nexport function isDefined<T>(value: Nullable<T> | false): value is T {\n if (value === undefined || value === null || value === false) return false;\n if (typeof value === \"string\") return value.trim() !== \"\";\n if (Array.isArray(value)) return value.some((element) => isDefined(element));\n\n if (typeof value === \"object\") {\n const prototype = Reflect.getPrototypeOf(value);\n if (prototype !== Object.prototype && prototype !== null) return true;\n\n return Object.keys(value).length > 0;\n }\n\n return true;\n}\n\n/**\n * Converts a string between different casing formats.\n *\n * @param value - The string to convert.\n * @param format - The target casing format.\n * @returns The converted string.\n */\nexport function convertCase(value: string, format: \"kebab\" | \"snake\" | \"camel\" | \"pascal\" | \"title\" | \"sentence\") {\n const words = value\n .replaceAll(/(?<lower>[a-z0-9])(?<upper>[A-Z])/gu, \"$<lower> $<upper>\")\n .replaceAll(/[-_\\s]+/gu, \" \")\n .trim()\n .toLowerCase();\n\n switch (format) {\n case \"kebab\": {\n return words.replaceAll(/\\s+/gu, \"-\");\n }\n case \"snake\": {\n return words.replaceAll(/\\s+/gu, \"_\");\n }\n case \"camel\": {\n return words.replaceAll(/\\s+(?<character>.)/gu, (_match, character: string) => character.toUpperCase());\n }\n case \"pascal\": {\n return words.replaceAll(/(?:^|\\s+)(?<character>.)/gu, (_match, character: string) => character.toUpperCase());\n }\n case \"title\": {\n return words.replaceAll(/\\b\\w/gu, (character) => character.toUpperCase());\n }\n case \"sentence\": {\n return words.replaceAll(/^(?<character>.)/gu, (character) => character.toUpperCase());\n }\n default: {\n return value;\n }\n }\n}\n","import { stegaClean } from \"@sanity/client/stega\";\n\nimport { isDefined } from \"@/lib/utils\";\n\n/** Elements an icon's drawing may be built from, narrowed to those that describe a shape. */\nconst iconElements = [\"circle\", \"ellipse\", \"g\", \"line\", \"path\", \"polygon\", \"polyline\", \"rect\"] as const;\n\n/** Single shape in an icon's drawing, paired with the attributes it is drawn with. */\ntype IconElement = [element: (typeof iconElements)[number], attributes: Record<string, string>];\n\n/** An icon's drawing, held as the shapes it is built from rather than as a component. */\nexport type IconNode = IconElement[];\n\n/**\n * Attributes given to the SVG an icon's shapes are drawn inside, mirroring the grid and stroke Lucide\n * draws on. A library working to different conventions would need its own frame, which is why a\n * stored icon carries only what sits inside this one.\n */\nexport const iconRootAttributes = {\n xmlns: \"http://www.w3.org/2000/svg\",\n width: 24,\n height: 24,\n viewBox: \"0 0 24 24\",\n fill: \"none\",\n stroke: \"currentColor\",\n strokeWidth: 2,\n strokeLinecap: \"round\",\n strokeLinejoin: \"round\",\n} as const;\n\n/**\n * Checks whether a parsed value is an icon's drawing, since it arrives as whatever happened to be\n * stored. Shapes are matched against the known elements so nothing else reaches the renderer.\n *\n * @param value - The value to check.\n * @returns True if the value is a drawing.\n */\nfunction isIconNode(value: unknown): value is IconNode {\n if (!Array.isArray(value)) return false;\n\n return value.every(\n (element: unknown) =>\n Array.isArray(element) &&\n element.length === 2 &&\n iconElements.some((name) => name === element[0]) &&\n typeof element[1] === \"object\" &&\n element[1] !== null,\n );\n}\n\n/**\n * Reads an icon's drawing from an already-parsed value, for a library handing over its drawings as\n * data rather than as a document field.\n *\n * @param value - The value to read.\n * @returns The drawing, or undefined when the value is not one.\n */\nexport function parseIconNode(value: unknown) {\n return isIconNode(value) ? value : undefined;\n}\n\n/**\n * Reads an icon's drawing back from the form it is stored in, clearing the stega characters a Sanity\n * fetch leaves behind.\n *\n * @param value - The stored drawing.\n * @returns The drawing, or undefined when nothing readable was stored.\n */\nexport function resolveIconNode(value: string | undefined) {\n const cleaned = stegaClean(value);\n if (!isDefined(cleaned)) return undefined;\n\n try {\n const parsed: unknown = JSON.parse(cleaned);\n\n return parseIconNode(parsed);\n } catch {\n return undefined;\n }\n}\n\n/**\n * Writes an icon's drawing into the form it is stored in, so the shapes survive a round trip through\n * a document rather than a component reference that only resolves at build time.\n *\n * @param node - The drawing to store.\n * @returns The drawing ready to store.\n */\nexport function serialiseIconNode(node: IconNode) {\n return JSON.stringify(node);\n}\n","// Runs inside a consumer's server components, where the compiler's memo cache hook cannot.\n\"use no memo\";\n\nimport type { ComponentProps } from \"react\";\nimport { createElement } from \"react\";\n\nimport type { IconNode } from \"@/lib/nodes\";\nimport { iconRootAttributes } from \"@/lib/nodes\";\nimport { isDefined } from \"@/lib/utils\";\n\nexport type DrawingProps = Omit<ComponentProps<\"svg\">, \"children\"> & { node: IconNode | undefined };\n\nexport function Drawing({ node, ...props }: DrawingProps) {\n if (!isDefined(node)) return null;\n\n return (\n <svg {...iconRootAttributes} aria-hidden=\"true\" {...props}>\n {node.map(([element, attributes], index) => createElement(element, { ...attributes, key: String(index) }))}\n </svg>\n );\n}\n"],"mappings":";;;;;;;;;;;AAUA,SAAgBC,UAAaC,OAAwC;CACnE,IAAIA,SAAiC,QAAQA,UAAU,IAAO,OAAO;CACrE,IAAI,OAAOA,SAAU,UAAU,OAAOA,MAAMG,KAAK,MAAM;CACvD,IAAIC,MAAMC,QAAQL,KAAK,GAAG,OAAOA,MAAMM,MAAMC,YAAYR,UAAUQ,OAAO,CAAC;CAE3E,IAAI,OAAOP,SAAU,UAAU;EAC7B,IAAMQ,YAAYC,QAAQC,eAAeV,KAAK;EAG9C,OAFIQ,cAAcG,OAAOH,aAAaA,cAAc,QAE7CG,OAAOC,KAAKZ,KAAK,CAAC,CAACa,SAAS;CACrC;CAEA,OAAO;AACT;;;;;;;;AASA,SAAgBC,YAAYd,OAAee,QAAuE;CAChH,IAAMC,QAAQhB,MACXiB,WAAW,uCAAuC,mBAAmB,CAAC,CACtEA,WAAW,aAAa,GAAG,CAAC,CAC5Bd,KAAK,CAAC,CACNe,YAAY;CAEf,QAAQH,QAAR;EACE,KAAK,SACH,OAAOC,MAAMC,WAAW,SAAS,GAAG;EAEtC,KAAK,SACH,OAAOD,MAAMC,WAAW,SAAS,GAAG;EAEtC,KAAK,SACH,OAAOD,MAAMC,WAAW,yBAAyBE,QAAQC,cAAsBA,UAAUC,YAAY,CAAC;EAExG,KAAK,UACH,OAAOL,MAAMC,WAAW,+BAA+BE,QAAQC,cAAsBA,UAAUC,YAAY,CAAC;EAE9G,KAAK,SACH,OAAOL,MAAMC,WAAW,WAAWG,cAAcA,UAAUC,YAAY,CAAC;EAE1E,KAAK,YACH,OAAOL,MAAMC,WAAW,uBAAuBG,cAAcA,UAAUC,YAAY,CAAC;EAEtF,SACE,OAAOrB;CAEX;AACF;;ACzDA,MAAMwB,eAAe;CAAC;CAAU;CAAW;CAAK;CAAQ;CAAQ;CAAW;CAAY;AAAM,GAahFO,qBAAqB;CAChCC,OAAO;CACPC,OAAO;CACPC,QAAQ;CACRC,SAAS;CACTC,MAAM;CACNC,QAAQ;CACRC,aAAa;CACbC,eAAe;CACfC,gBAAgB;AAClB;;;;;;;;AASA,SAASC,WAAWC,OAAmC;CAGrD,OAFKC,MAAMC,QAAQF,KAAK,IAEjBA,MAAMG,OACVlB,YACCgB,MAAMC,QAAQjB,OAAO,KACrBA,QAAQmB,WAAW,KACnBtB,aAAauB,MAAMC,SAASA,SAASrB,QAAQ,EAAE,KAC/C,OAAOA,QAAQ,MAAO,YACtBA,QAAQ,OAAO,IACnB,IATkC;AAUpC;;;;;;;;AASA,SAAgBsB,cAAcP,OAAgB;CAC5C,OAAOD,WAAWC,KAAK,IAAIA,QAAQQ,KAAAA;AACrC;;;;;;;;AASA,SAAgBC,gBAAgBT,OAA2B;CACzD,IAAMU,UAAU9B,WAAWoB,KAAK;CAC3BnB,cAAU6B,OAAO,GAEtB,IAAI;EAGF,OAAOH,cAFiBK,KAAKC,MAAMH,OAEdC,CAAM;CAC7B,QAAQ;EACN;CACF;AACF;;;;;;;;AASA,SAAgBG,kBAAkBC,MAAgB;CAChD,OAAOH,KAAKI,UAAUD,IAAI;AAC5B;AC9EA,SAAgBU,QAAQ,EAAED,MAAM,GAAGE,SAAuB;CAGxD,OAFKL,UAAUG,IAAI,IAGjB,oBAAC,OAAD;EAAK,GAAIJ;EAAoB,eAAY;EAAO,GAAIM;EACjDF,UAAAA,KAAKG,KAAK,CAACC,SAASC,aAAaC,UAAUZ,cAAcU,SAAS;GAAE,GAAGC;GAAYE,KAAKC,OAAOF,KAAK;EAAE,CAAC,CAAC;CACtG,CAAA,IALsB;AAO/B"}
@@ -1,16 +0,0 @@
1
- /** Type name of the icon object. */
2
- declare const iconTypeName = "icon";
3
- /**
4
- * An icon an author chose, stored as the name it was chosen by alongside the shapes it draws.
5
- *
6
- * @public
7
- */
8
- interface SanityIcon {
9
- _type: typeof iconTypeName;
10
- /** Name the icon goes by in the library it was taken from. */
11
- name?: string;
12
- /** Shapes the icon is drawn from, written when the icon is chosen. */
13
- node?: string;
14
- }
15
- export { iconTypeName as n, SanityIcon as t };
16
- //# sourceMappingURL=types-_D2jGm25.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"types-_D2jGm25.d.ts","names":[],"sources":["../src/types.ts"],"mappings":"AAIA;cAAa;;;;;;UAOI;EACf,cAAc;;EAEd;;EAEA"}