@umami/shiso 1.1.2 → 1.1.4
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/CHANGELOG.md +9 -0
- package/bin/shiso.mjs +1 -1
- package/dist/chunks/App.js +27152 -0
- package/dist/chunks/local.js +66 -0
- package/dist/entry-client.js +13 -0
- package/dist/entry-server.js +137 -0
- package/dist/search.js +44 -0
- package/package.json +10 -4
- package/scripts/build-runtime.mjs +114 -0
- package/scripts/check-package.mjs +4 -2
- package/src/App.tsx +1 -1
- package/src/entry-client.tsx +3 -9
- package/src/lib/vendor/use-sync-external-store-with-selector.ts +77 -0
- package/src/lib/vendor/use-sync-external-store.ts +1 -0
- package/types/client.d.ts +2 -2
- package/vite.config.ts +3 -6
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
//#region src/lib/search.ts
|
|
2
|
+
/**
|
|
3
|
+
* Restricts records to the active scope. Records without a scope id (from
|
|
4
|
+
* single-scope indexes or older caches) always pass.
|
|
5
|
+
*/
|
|
6
|
+
function filterRecordsByScope(records, context) {
|
|
7
|
+
if (!context?.scopeId) return records;
|
|
8
|
+
return records.filter((record) => !record.scopeId || record.scopeId === context.scopeId);
|
|
9
|
+
}
|
|
10
|
+
const SNIPPET_RADIUS = 60;
|
|
11
|
+
function makeSnippet(text, index, length) {
|
|
12
|
+
const start = Math.max(0, index - SNIPPET_RADIUS);
|
|
13
|
+
const end = Math.min(text.length, index + length + SNIPPET_RADIUS);
|
|
14
|
+
return `${start > 0 ? "…" : ""}${text.slice(start, end).trim()}${end < text.length ? "…" : ""}`;
|
|
15
|
+
}
|
|
16
|
+
function searchIndex(records, query, limit = 10) {
|
|
17
|
+
const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
18
|
+
if (!terms.length) return [];
|
|
19
|
+
const results = [];
|
|
20
|
+
for (const record of records) {
|
|
21
|
+
const page = record.page.toLowerCase();
|
|
22
|
+
const heading = (record.heading || "").toLowerCase();
|
|
23
|
+
const text = record.text.toLowerCase();
|
|
24
|
+
let score = 0;
|
|
25
|
+
let snippetAt = -1;
|
|
26
|
+
let snippetLength = 0;
|
|
27
|
+
let matched = true;
|
|
28
|
+
for (const term of terms) if (page.includes(term)) score += page === term ? 40 : 20;
|
|
29
|
+
else if (heading.includes(term)) score += heading === term ? 30 : 15;
|
|
30
|
+
else {
|
|
31
|
+
const index = text.indexOf(term);
|
|
32
|
+
if (index === -1) {
|
|
33
|
+
matched = false;
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
score += 5;
|
|
37
|
+
if (snippetAt === -1) {
|
|
38
|
+
snippetAt = index;
|
|
39
|
+
snippetLength = term.length;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (!matched || !score) continue;
|
|
43
|
+
results.push({
|
|
44
|
+
url: record.id ? `${record.url}#${record.id}` : record.url,
|
|
45
|
+
page: record.page,
|
|
46
|
+
heading: record.heading,
|
|
47
|
+
snippet: snippetAt >= 0 ? makeSnippet(record.text, snippetAt, snippetLength) : void 0,
|
|
48
|
+
score
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return results.sort((a, b) => b.score - a.score).slice(0, limit);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
//#endregion
|
|
55
|
+
//#region src/lib/search/providers/local.ts
|
|
56
|
+
/** Built-in provider backed by the section index generated during the build. */
|
|
57
|
+
function createLocalSearchProvider(_options = {}) {
|
|
58
|
+
let records = null;
|
|
59
|
+
return { async search(query, limit, context) {
|
|
60
|
+
records ||= import("@/lib/search-index.generated").then((module) => module.SEARCH_INDEX);
|
|
61
|
+
return searchIndex(filterRecordsByScope(await records, context), query, limit);
|
|
62
|
+
} };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
//#endregion
|
|
66
|
+
export { createLocalSearchProvider };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { h as BrowserRouter, p as BASE_URL, t as App } from "./chunks/App.js";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
|
|
4
|
+
//#region src/entry-client.tsx
|
|
5
|
+
function ShisoApp() {
|
|
6
|
+
return /* @__PURE__ */ jsx(BrowserRouter, {
|
|
7
|
+
basename: BASE_URL || void 0,
|
|
8
|
+
children: /* @__PURE__ */ jsx(App, {})
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
//#endregion
|
|
13
|
+
export { ShisoApp };
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { _ as createPath, a as docsSite, c as getSeo, d as getLastModified, f as getScopeForPage, g as Router, i as docsHomeUrl, l as siteName, m as toAbsoluteUrl, n as buildHead, o as getLocaleByPathname, p as BASE_URL, r as renderHeadToString, s as getRedirects, t as App, u as getDocModule, v as parsePath, y as ABSOLUTE_URL_REGEX } from "./chunks/App.js";
|
|
2
|
+
import * as React$1 from "react";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
import { renderToString } from "react-dom/server";
|
|
5
|
+
|
|
6
|
+
//#region ../../node_modules/.pnpm/react-router@8.3.0_react-do_c27277bcf657dc321048682bd02ab633/node_modules/react-router/dist/production/lib/dom/server.js
|
|
7
|
+
/**
|
|
8
|
+
* react-router v8.3.0
|
|
9
|
+
*
|
|
10
|
+
* Copyright (c) Remix Software Inc.
|
|
11
|
+
*
|
|
12
|
+
* This source code is licensed under the MIT license found in the
|
|
13
|
+
* LICENSE.md file in the root directory of this source tree.
|
|
14
|
+
*
|
|
15
|
+
* @license MIT
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* A {@link Router | `<Router>`} that may not navigate to any other {@link Location}.
|
|
19
|
+
* This is useful on the server where there is no stateful UI.
|
|
20
|
+
*
|
|
21
|
+
* @public
|
|
22
|
+
* @category Declarative Routers
|
|
23
|
+
* @mode declarative
|
|
24
|
+
* @param props Props
|
|
25
|
+
* @param {StaticRouterProps.basename} props.basename n/a
|
|
26
|
+
* @param {StaticRouterProps.children} props.children n/a
|
|
27
|
+
* @param {StaticRouterProps.location} props.location n/a
|
|
28
|
+
* @returns A React element that renders the static {@link Router | `<Router>`}
|
|
29
|
+
*/
|
|
30
|
+
function StaticRouter({ basename, children, location: locationProp = "/" }) {
|
|
31
|
+
if (typeof locationProp === "string") locationProp = parsePath(locationProp);
|
|
32
|
+
let action = "POP";
|
|
33
|
+
let location = {
|
|
34
|
+
pathname: locationProp.pathname || "/",
|
|
35
|
+
search: locationProp.search || "",
|
|
36
|
+
hash: locationProp.hash || "",
|
|
37
|
+
state: locationProp.state != null ? locationProp.state : null,
|
|
38
|
+
key: locationProp.key || "default",
|
|
39
|
+
mask: void 0
|
|
40
|
+
};
|
|
41
|
+
let staticNavigator = getStatelessNavigator();
|
|
42
|
+
return /* @__PURE__ */ React$1.createElement(Router, {
|
|
43
|
+
basename,
|
|
44
|
+
children,
|
|
45
|
+
location,
|
|
46
|
+
navigationType: action,
|
|
47
|
+
navigator: staticNavigator,
|
|
48
|
+
static: true,
|
|
49
|
+
useTransitions: false
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
function getStatelessNavigator() {
|
|
53
|
+
return {
|
|
54
|
+
createHref,
|
|
55
|
+
encodeLocation,
|
|
56
|
+
push(to) {
|
|
57
|
+
throw new Error(`You cannot use navigator.push() on the server because it is a stateless environment. This error was probably triggered when you did a \`navigate(${JSON.stringify(to)})\` somewhere in your app.`);
|
|
58
|
+
},
|
|
59
|
+
replace(to) {
|
|
60
|
+
throw new Error(`You cannot use navigator.replace() on the server because it is a stateless environment. This error was probably triggered when you did a \`navigate(${JSON.stringify(to)}, { replace: true })\` somewhere in your app.`);
|
|
61
|
+
},
|
|
62
|
+
go(delta) {
|
|
63
|
+
throw new Error(`You cannot use navigator.go() on the server because it is a stateless environment. This error was probably triggered when you did a \`navigate(${delta})\` somewhere in your app.`);
|
|
64
|
+
},
|
|
65
|
+
back() {
|
|
66
|
+
throw new Error("You cannot use navigator.back() on the server because it is a stateless environment.");
|
|
67
|
+
},
|
|
68
|
+
forward() {
|
|
69
|
+
throw new Error("You cannot use navigator.forward() on the server because it is a stateless environment.");
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function createHref(to) {
|
|
74
|
+
return typeof to === "string" ? to : createPath(to);
|
|
75
|
+
}
|
|
76
|
+
function encodeLocation(to) {
|
|
77
|
+
let href = typeof to === "string" ? to : createPath(to);
|
|
78
|
+
href = href.replace(/ $/, "%20");
|
|
79
|
+
let encoded = ABSOLUTE_URL_REGEX.test(href) ? new URL(href) : new URL(href, "http://localhost");
|
|
80
|
+
return {
|
|
81
|
+
pathname: encoded.pathname,
|
|
82
|
+
search: encoded.search,
|
|
83
|
+
hash: encoded.hash
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
//#endregion
|
|
88
|
+
//#region src/entry-server.tsx
|
|
89
|
+
/** Base-relative routes for every scope. The prerenderer prepends the deploy base itself. */
|
|
90
|
+
function getRoutes() {
|
|
91
|
+
return docsSite.pages.map((page) => page.url);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Source file for every routed page, so the prerenderer can publish raw
|
|
95
|
+
* markdown next to each HTML page (used by the contextual menu's copy/view
|
|
96
|
+
* options and by AI tools). `filePath` is a module key like
|
|
97
|
+
* "/content/docs/index.mdx", resolved against the project root.
|
|
98
|
+
*/
|
|
99
|
+
function getMarkdownPages() {
|
|
100
|
+
return docsSite.pages.map((page) => ({
|
|
101
|
+
route: page.url,
|
|
102
|
+
filePath: page.filePath
|
|
103
|
+
}));
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Absolute URLs for the sitemap, honoring `seo.indexing` and per-page
|
|
107
|
+
* noindex. Empty when `$shiso.siteUrl` is not configured, since a sitemap
|
|
108
|
+
* of relative URLs is invalid.
|
|
109
|
+
*/
|
|
110
|
+
function getSitemapEntries() {
|
|
111
|
+
const { indexing } = getSeo();
|
|
112
|
+
const entries = [];
|
|
113
|
+
for (const page of docsSite.pages) {
|
|
114
|
+
if ((page.hidden || getScopeForPage(docsSite, page).hidden) && indexing !== "all") continue;
|
|
115
|
+
if (getDocModule(page.filePath)?.frontmatter?.noindex === true) continue;
|
|
116
|
+
const url = toAbsoluteUrl(page.url);
|
|
117
|
+
if (url) entries.push({
|
|
118
|
+
url,
|
|
119
|
+
lastmod: getLastModified(page.filePath)
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
return entries;
|
|
123
|
+
}
|
|
124
|
+
function render(url) {
|
|
125
|
+
return {
|
|
126
|
+
html: renderToString(/* @__PURE__ */ jsx(StaticRouter, {
|
|
127
|
+
basename: BASE_URL || void 0,
|
|
128
|
+
location: `${BASE_URL}${url}`,
|
|
129
|
+
children: /* @__PURE__ */ jsx(App, {})
|
|
130
|
+
})),
|
|
131
|
+
head: renderHeadToString(buildHead(url)),
|
|
132
|
+
htmlAttrs: getLocaleByPathname(url)
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
//#endregion
|
|
137
|
+
export { docsHomeUrl, getMarkdownPages, getRedirects, getRoutes, getSitemapEntries, render, siteName };
|
package/dist/search.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
//#region src/lib/search/provider.ts
|
|
2
|
+
const factories = /* @__PURE__ */ new Map();
|
|
3
|
+
function normalizeProviderId(id) {
|
|
4
|
+
return id.trim().toLowerCase();
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Registers a runtime search provider. Call this before rendering the app.
|
|
8
|
+
* The returned cleanup function only removes this exact registration.
|
|
9
|
+
*/
|
|
10
|
+
function registerSearchProvider(id, factory) {
|
|
11
|
+
const providerId = normalizeProviderId(id);
|
|
12
|
+
if (!providerId || providerId === "local") throw new Error("Search provider ids must be non-empty and cannot replace the built-in \"local\" provider.");
|
|
13
|
+
factories.set(providerId, factory);
|
|
14
|
+
return () => {
|
|
15
|
+
if (factories.get(providerId) === factory) factories.delete(providerId);
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
async function createLocalProvider(options) {
|
|
19
|
+
const { createLocalSearchProvider } = await import("./chunks/local.js");
|
|
20
|
+
return createLocalSearchProvider(options);
|
|
21
|
+
}
|
|
22
|
+
/** Resolves a configured provider, falling back to local for unknown ids. */
|
|
23
|
+
async function resolveSearchProvider(id, options = {}) {
|
|
24
|
+
const requestedId = normalizeProviderId(id) || "local";
|
|
25
|
+
if (requestedId === "local") return {
|
|
26
|
+
provider: await createLocalProvider(options),
|
|
27
|
+
providerId: "local",
|
|
28
|
+
fellBack: false
|
|
29
|
+
};
|
|
30
|
+
const factory = factories.get(requestedId);
|
|
31
|
+
if (factory) return {
|
|
32
|
+
provider: await factory(options),
|
|
33
|
+
providerId: requestedId,
|
|
34
|
+
fellBack: false
|
|
35
|
+
};
|
|
36
|
+
return {
|
|
37
|
+
provider: await createLocalProvider({}),
|
|
38
|
+
providerId: "local",
|
|
39
|
+
fellBack: true
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
//#endregion
|
|
44
|
+
export { registerSearchProvider, resolveSearchProvider };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umami/shiso",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.4",
|
|
4
4
|
"description": "Open-source documentation framework for Markdown and MDX sites.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -9,18 +9,20 @@
|
|
|
9
9
|
"exports": {
|
|
10
10
|
"./client": {
|
|
11
11
|
"types": "./types/client.d.ts",
|
|
12
|
-
"default": "./
|
|
12
|
+
"default": "./dist/entry-client.js"
|
|
13
13
|
},
|
|
14
14
|
"./search": {
|
|
15
15
|
"types": "./types/search.d.ts",
|
|
16
|
-
"default": "./
|
|
16
|
+
"default": "./dist/search.js"
|
|
17
17
|
},
|
|
18
|
+
"./styles.css": "./src/styles/global.css",
|
|
18
19
|
"./schema": "./docs.schema.json",
|
|
19
20
|
"./docs.schema.json": "./docs.schema.json",
|
|
20
21
|
"./package.json": "./package.json"
|
|
21
22
|
},
|
|
22
23
|
"files": [
|
|
23
24
|
"bin",
|
|
25
|
+
"dist",
|
|
24
26
|
"scripts",
|
|
25
27
|
"src",
|
|
26
28
|
"types",
|
|
@@ -31,7 +33,8 @@
|
|
|
31
33
|
"README.md"
|
|
32
34
|
],
|
|
33
35
|
"scripts": {
|
|
34
|
-
"
|
|
36
|
+
"build:runtime": "node scripts/build-runtime.mjs",
|
|
37
|
+
"prepack": "pnpm build:runtime && pnpm test:package",
|
|
35
38
|
"test:package": "node scripts/check-package.mjs"
|
|
36
39
|
},
|
|
37
40
|
"engines": {
|
|
@@ -89,5 +92,8 @@
|
|
|
89
92
|
"peerDependencies": {
|
|
90
93
|
"react": "^19.0.0",
|
|
91
94
|
"react-dom": "^19.0.0"
|
|
95
|
+
},
|
|
96
|
+
"devDependencies": {
|
|
97
|
+
"rolldown": "^1.2.2"
|
|
92
98
|
}
|
|
93
99
|
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import fsSync from 'node:fs';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { rolldown } from 'rolldown';
|
|
5
|
+
|
|
6
|
+
const packageRoot = path.resolve(import.meta.dirname, '..');
|
|
7
|
+
const sourceRoot = path.join(packageRoot, 'src');
|
|
8
|
+
const outputRoot = path.join(packageRoot, 'dist');
|
|
9
|
+
|
|
10
|
+
const projectModules = new Set([
|
|
11
|
+
'@/generated/last-modified',
|
|
12
|
+
'@/lib/icon-registry.generated',
|
|
13
|
+
'@/lib/search-index.generated',
|
|
14
|
+
'virtual:shiso-docs-config',
|
|
15
|
+
]);
|
|
16
|
+
|
|
17
|
+
const peerModules = new Set(['react', 'react-dom']);
|
|
18
|
+
const sharedModules = new Set(['@mdx-js/react']);
|
|
19
|
+
const runtimeAliases = new Map([
|
|
20
|
+
['use-sync-external-store/shim', path.join(sourceRoot, 'lib/vendor/use-sync-external-store.ts')],
|
|
21
|
+
[
|
|
22
|
+
'use-sync-external-store/shim/with-selector',
|
|
23
|
+
path.join(sourceRoot, 'lib/vendor/use-sync-external-store-with-selector.ts'),
|
|
24
|
+
],
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
function isExternal(source) {
|
|
28
|
+
const packageName = source.startsWith('@')
|
|
29
|
+
? source.split('/').slice(0, 2).join('/')
|
|
30
|
+
: source.split('/')[0];
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
projectModules.has(source) ||
|
|
34
|
+
peerModules.has(packageName) ||
|
|
35
|
+
sharedModules.has(source) ||
|
|
36
|
+
source === '@umami/shiso/styles.css' ||
|
|
37
|
+
source.endsWith('.css')
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function resolveSourceModule(source) {
|
|
42
|
+
const base = path.join(sourceRoot, source.slice(2));
|
|
43
|
+
const candidates = [
|
|
44
|
+
base,
|
|
45
|
+
`${base}.ts`,
|
|
46
|
+
`${base}.tsx`,
|
|
47
|
+
`${base}.js`,
|
|
48
|
+
`${base}.jsx`,
|
|
49
|
+
path.join(base, 'index.ts'),
|
|
50
|
+
path.join(base, 'index.tsx'),
|
|
51
|
+
path.join(base, 'index.js'),
|
|
52
|
+
path.join(base, 'index.jsx'),
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
return candidates.find(candidate => {
|
|
56
|
+
try {
|
|
57
|
+
return fsSync.statSync(candidate).isFile();
|
|
58
|
+
} catch {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const bundle = await rolldown({
|
|
65
|
+
input: {
|
|
66
|
+
'entry-client': path.join(sourceRoot, 'entry-client.tsx'),
|
|
67
|
+
'entry-server': path.join(sourceRoot, 'entry-server.tsx'),
|
|
68
|
+
search: path.join(sourceRoot, 'lib/search/provider.ts'),
|
|
69
|
+
},
|
|
70
|
+
external: isExternal,
|
|
71
|
+
plugins: [
|
|
72
|
+
{
|
|
73
|
+
name: 'shiso-runtime-aliases',
|
|
74
|
+
resolveId(source) {
|
|
75
|
+
if (runtimeAliases.has(source)) {
|
|
76
|
+
return runtimeAliases.get(source);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (projectModules.has(source)) {
|
|
80
|
+
return { id: source, external: true };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (!source.startsWith('@/')) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const resolved = resolveSourceModule(source);
|
|
88
|
+
|
|
89
|
+
if (!resolved) {
|
|
90
|
+
throw new Error(`Could not resolve runtime module "${source}".`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return resolved;
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
transform: {
|
|
98
|
+
jsx: 'react-jsx',
|
|
99
|
+
target: 'es2022',
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
await fs.rm(outputRoot, { recursive: true, force: true });
|
|
104
|
+
await bundle.write({
|
|
105
|
+
dir: outputRoot,
|
|
106
|
+
format: 'esm',
|
|
107
|
+
entryFileNames: '[name].js',
|
|
108
|
+
chunkFileNames: 'chunks/[name].js',
|
|
109
|
+
minify: false,
|
|
110
|
+
sourcemap: false,
|
|
111
|
+
});
|
|
112
|
+
await bundle.close();
|
|
113
|
+
|
|
114
|
+
console.log(`Built the Shiso runtime at ${path.relative(process.cwd(), outputRoot)}`);
|
|
@@ -5,11 +5,13 @@ const root = path.resolve(import.meta.dirname, '..');
|
|
|
5
5
|
|
|
6
6
|
for (const relativeFile of [
|
|
7
7
|
'bin/shiso.mjs',
|
|
8
|
+
'dist/entry-client.js',
|
|
9
|
+
'dist/entry-server.js',
|
|
10
|
+
'dist/search.js',
|
|
8
11
|
'docs.schema.json',
|
|
9
12
|
'scripts/lib/mdast.mjs',
|
|
10
13
|
'scripts/lib/slug.mjs',
|
|
11
|
-
'src/
|
|
12
|
-
'src/entry-server.tsx',
|
|
14
|
+
'src/styles/global.css',
|
|
13
15
|
'types/client.d.ts',
|
|
14
16
|
'types/search.d.ts',
|
|
15
17
|
'vite.config.ts',
|
package/src/App.tsx
CHANGED
|
@@ -3,7 +3,7 @@ import '@fontsource/inter/700.css';
|
|
|
3
3
|
import '@fontsource/inter/800.css';
|
|
4
4
|
import '@fontsource/jetbrains-mono/400.css';
|
|
5
5
|
import 'highlight.js/styles/github.css';
|
|
6
|
-
import '
|
|
6
|
+
import '@umami/shiso/styles.css';
|
|
7
7
|
|
|
8
8
|
import { MDXProvider } from '@mdx-js/react';
|
|
9
9
|
import { Navigate, Route, Routes } from 'react-router';
|
package/src/entry-client.tsx
CHANGED
|
@@ -1,17 +1,11 @@
|
|
|
1
|
-
import { hydrateRoot } from 'react-dom/client';
|
|
2
1
|
import { BrowserRouter } from 'react-router';
|
|
3
2
|
import { App } from '@/App';
|
|
4
3
|
import { BASE_URL } from '@/lib/paths';
|
|
5
4
|
|
|
6
|
-
export function
|
|
7
|
-
|
|
8
|
-
throw new Error('Shiso could not find the root element.');
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
return hydrateRoot(
|
|
12
|
-
element,
|
|
5
|
+
export function ShisoApp() {
|
|
6
|
+
return (
|
|
13
7
|
<BrowserRouter basename={BASE_URL || undefined}>
|
|
14
8
|
<App />
|
|
15
|
-
</BrowserRouter
|
|
9
|
+
</BrowserRouter>
|
|
16
10
|
);
|
|
17
11
|
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React's MIT-licensed use-sync-external-store selector shim, adapted to use
|
|
3
|
+
* the native React 19 hook that Shiso already requires as a peer dependency.
|
|
4
|
+
*/
|
|
5
|
+
import { useDebugValue, useEffect, useMemo, useRef, useSyncExternalStore } from 'react';
|
|
6
|
+
|
|
7
|
+
interface SelectionState<Selection> {
|
|
8
|
+
hasValue: boolean;
|
|
9
|
+
value: Selection | null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function useSyncExternalStoreWithSelector<Snapshot, Selection>(
|
|
13
|
+
subscribe: (onStoreChange: () => void) => () => void,
|
|
14
|
+
getSnapshot: () => Snapshot,
|
|
15
|
+
getServerSnapshot: (() => Snapshot) | undefined,
|
|
16
|
+
selector: (snapshot: Snapshot) => Selection,
|
|
17
|
+
isEqual?: (left: Selection, right: Selection) => boolean,
|
|
18
|
+
): Selection {
|
|
19
|
+
const stateRef = useRef<SelectionState<Selection> | null>(null);
|
|
20
|
+
|
|
21
|
+
if (stateRef.current === null) {
|
|
22
|
+
stateRef.current = { hasValue: false, value: null };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const state = stateRef.current;
|
|
26
|
+
const [getSelection, getServerSelection] = useMemo(() => {
|
|
27
|
+
let hasMemo = false;
|
|
28
|
+
let memoizedSnapshot: Snapshot;
|
|
29
|
+
let memoizedSelection: Selection;
|
|
30
|
+
|
|
31
|
+
const memoizedSelector = (nextSnapshot: Snapshot) => {
|
|
32
|
+
if (!hasMemo) {
|
|
33
|
+
hasMemo = true;
|
|
34
|
+
memoizedSnapshot = nextSnapshot;
|
|
35
|
+
const nextSelection = selector(nextSnapshot);
|
|
36
|
+
|
|
37
|
+
if (isEqual && state.hasValue && isEqual(state.value as Selection, nextSelection)) {
|
|
38
|
+
memoizedSelection = state.value as Selection;
|
|
39
|
+
return memoizedSelection;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
memoizedSelection = nextSelection;
|
|
43
|
+
return memoizedSelection;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (Object.is(memoizedSnapshot, nextSnapshot)) {
|
|
47
|
+
return memoizedSelection;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const nextSelection = selector(nextSnapshot);
|
|
51
|
+
|
|
52
|
+
if (isEqual?.(memoizedSelection, nextSelection)) {
|
|
53
|
+
memoizedSnapshot = nextSnapshot;
|
|
54
|
+
return memoizedSelection;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
memoizedSnapshot = nextSnapshot;
|
|
58
|
+
memoizedSelection = nextSelection;
|
|
59
|
+
return memoizedSelection;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
return [
|
|
63
|
+
() => memoizedSelector(getSnapshot()),
|
|
64
|
+
getServerSnapshot ? () => memoizedSelector(getServerSnapshot()) : undefined,
|
|
65
|
+
] as const;
|
|
66
|
+
}, [getSnapshot, getServerSnapshot, selector, isEqual, state]);
|
|
67
|
+
|
|
68
|
+
const value = useSyncExternalStore(subscribe, getSelection, getServerSelection);
|
|
69
|
+
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
state.hasValue = true;
|
|
72
|
+
state.value = value;
|
|
73
|
+
}, [state, value]);
|
|
74
|
+
|
|
75
|
+
useDebugValue(value);
|
|
76
|
+
return value;
|
|
77
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useSyncExternalStore } from 'react';
|
package/types/client.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
2
|
|
|
3
|
-
export function
|
|
3
|
+
export function ShisoApp(): ReactElement;
|
package/vite.config.ts
CHANGED
|
@@ -281,7 +281,6 @@ function shisoHtml(getDocsConfig: () => DocsConfig): Plugin {
|
|
|
281
281
|
};
|
|
282
282
|
}
|
|
283
283
|
|
|
284
|
-
const PACKAGE_ROOT = fileURLToPath(new URL('.', import.meta.url));
|
|
285
284
|
const MDX_REACT_ENTRY = fileURLToPath(import.meta.resolve('@mdx-js/react'));
|
|
286
285
|
const LUCIDE_REACT_ENTRY = path.join(
|
|
287
286
|
path.dirname(fileURLToPath(import.meta.resolve('lucide-react/package.json'))),
|
|
@@ -298,10 +297,9 @@ export default defineConfig(async () => {
|
|
|
298
297
|
|
|
299
298
|
return {
|
|
300
299
|
optimizeDeps: {
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
],
|
|
300
|
+
// The published runtime is already bundled ESM and contains project-time
|
|
301
|
+
// virtual imports that are resolved by the plugins below.
|
|
302
|
+
exclude: ['@umami/shiso'],
|
|
305
303
|
},
|
|
306
304
|
plugins: [
|
|
307
305
|
configModule.plugin,
|
|
@@ -340,7 +338,6 @@ export default defineConfig(async () => {
|
|
|
340
338
|
find: '@/generated/last-modified',
|
|
341
339
|
replacement: path.join(generatedRoot, 'last-modified.ts'),
|
|
342
340
|
},
|
|
343
|
-
{ find: '@', replacement: path.join(PACKAGE_ROOT, 'src') },
|
|
344
341
|
],
|
|
345
342
|
dedupe: ['react', 'react-dom'],
|
|
346
343
|
},
|