@vistagenic/vista 0.2.13 → 0.2.15
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/LICENSE +21 -0
- package/dist/server/project-alias-resolver.d.ts +4 -0
- package/dist/server/project-alias-resolver.js +217 -0
- package/dist/server/rsc-engine.js +6 -0
- package/dist/server/rsc-upstream.js +8 -2
- package/dist/server/static-generator.js +6 -0
- package/dist/server/typed-api-runtime.js +51 -0
- package/dist/theme/index.d.ts +2 -0
- package/dist/theme/index.js +9 -0
- package/dist/theme/theme-provider.d.ts +18 -0
- package/dist/theme/theme-provider.js +112 -0
- package/dist/theme/theme-script.d.ts +6 -0
- package/dist/theme/theme-script.js +16 -0
- package/package.json +8 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vista.js contributors
|
|
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.
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createProjectAliasResolver = createProjectAliasResolver;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const projectAliasCache = new Map();
|
|
10
|
+
function stripJsonComments(input) {
|
|
11
|
+
let result = '';
|
|
12
|
+
let inString = false;
|
|
13
|
+
let stringQuote = '';
|
|
14
|
+
let isEscaped = false;
|
|
15
|
+
let inLineComment = false;
|
|
16
|
+
let inBlockComment = false;
|
|
17
|
+
for (let index = 0; index < input.length; index++) {
|
|
18
|
+
const current = input[index];
|
|
19
|
+
const next = input[index + 1];
|
|
20
|
+
if (inLineComment) {
|
|
21
|
+
if (current === '\n') {
|
|
22
|
+
inLineComment = false;
|
|
23
|
+
result += current;
|
|
24
|
+
}
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (inBlockComment) {
|
|
28
|
+
if (current === '*' && next === '/') {
|
|
29
|
+
inBlockComment = false;
|
|
30
|
+
index++;
|
|
31
|
+
}
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (inString) {
|
|
35
|
+
result += current;
|
|
36
|
+
if (isEscaped) {
|
|
37
|
+
isEscaped = false;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (current === '\\') {
|
|
41
|
+
isEscaped = true;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (current === stringQuote) {
|
|
45
|
+
inString = false;
|
|
46
|
+
stringQuote = '';
|
|
47
|
+
}
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if ((current === '"' || current === "'") && !inString) {
|
|
51
|
+
inString = true;
|
|
52
|
+
stringQuote = current;
|
|
53
|
+
result += current;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (current === '/' && next === '/') {
|
|
57
|
+
inLineComment = true;
|
|
58
|
+
index++;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (current === '/' && next === '*') {
|
|
62
|
+
inBlockComment = true;
|
|
63
|
+
index++;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
result += current;
|
|
67
|
+
}
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
function isBareSpecifier(request) {
|
|
71
|
+
if (!request)
|
|
72
|
+
return false;
|
|
73
|
+
if (request.startsWith('.') || request.startsWith('/'))
|
|
74
|
+
return false;
|
|
75
|
+
return !/^[A-Za-z]:[\\/]/.test(request);
|
|
76
|
+
}
|
|
77
|
+
function resolveAliasTargetPath(candidatePath) {
|
|
78
|
+
const resolvedBase = path_1.default.resolve(candidatePath);
|
|
79
|
+
const extensions = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.json'];
|
|
80
|
+
const directCandidates = path_1.default.extname(resolvedBase)
|
|
81
|
+
? [resolvedBase]
|
|
82
|
+
: [resolvedBase, ...extensions.map((extension) => `${resolvedBase}${extension}`)];
|
|
83
|
+
for (const candidate of directCandidates) {
|
|
84
|
+
try {
|
|
85
|
+
if (fs_1.default.existsSync(candidate) && fs_1.default.statSync(candidate).isFile()) {
|
|
86
|
+
return candidate;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
// continue
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
if (fs_1.default.existsSync(resolvedBase) && fs_1.default.statSync(resolvedBase).isDirectory()) {
|
|
95
|
+
const packageJsonPath = path_1.default.join(resolvedBase, 'package.json');
|
|
96
|
+
if (fs_1.default.existsSync(packageJsonPath)) {
|
|
97
|
+
try {
|
|
98
|
+
const packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, 'utf-8'));
|
|
99
|
+
if (packageJson.main) {
|
|
100
|
+
const packageMainPath = resolveAliasTargetPath(path_1.default.join(resolvedBase, packageJson.main));
|
|
101
|
+
if (packageMainPath)
|
|
102
|
+
return packageMainPath;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
// ignore invalid package.json while resolving alias target
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
for (const extension of extensions) {
|
|
110
|
+
const indexPath = path_1.default.join(resolvedBase, `index${extension}`);
|
|
111
|
+
if (fs_1.default.existsSync(indexPath) && fs_1.default.statSync(indexPath).isFile()) {
|
|
112
|
+
return indexPath;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// continue
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
function createProjectAliasResolver(cwd, resolveFromWorkspace) {
|
|
123
|
+
if (projectAliasCache.has(cwd)) {
|
|
124
|
+
return projectAliasCache.get(cwd) ?? null;
|
|
125
|
+
}
|
|
126
|
+
const configPath = ['tsconfig.json', 'jsconfig.json']
|
|
127
|
+
.map((filename) => path_1.default.join(cwd, filename))
|
|
128
|
+
.find((filename) => fs_1.default.existsSync(filename));
|
|
129
|
+
if (!configPath) {
|
|
130
|
+
projectAliasCache.set(cwd, null);
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
let compilerOptions = null;
|
|
134
|
+
try {
|
|
135
|
+
const typescriptPath = resolveFromWorkspace('typescript', cwd);
|
|
136
|
+
const ts = require(typescriptPath);
|
|
137
|
+
const readResult = ts.readConfigFile(configPath, ts.sys.readFile);
|
|
138
|
+
if (!readResult.error) {
|
|
139
|
+
compilerOptions = readResult.config?.compilerOptions ?? null;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
// fallback to plain JSON parse below
|
|
144
|
+
}
|
|
145
|
+
if (!compilerOptions) {
|
|
146
|
+
try {
|
|
147
|
+
const rawConfig = fs_1.default.readFileSync(configPath, 'utf-8');
|
|
148
|
+
const parsedConfig = JSON.parse(stripJsonComments(rawConfig));
|
|
149
|
+
compilerOptions = parsedConfig.compilerOptions ?? null;
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
compilerOptions = null;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const configDir = path_1.default.dirname(configPath);
|
|
156
|
+
const rawPaths = compilerOptions?.paths;
|
|
157
|
+
if (!rawPaths || typeof rawPaths !== 'object') {
|
|
158
|
+
projectAliasCache.set(cwd, null);
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
const baseDir = path_1.default.resolve(configDir, typeof compilerOptions?.baseUrl === 'string' && compilerOptions.baseUrl.trim()
|
|
162
|
+
? compilerOptions.baseUrl
|
|
163
|
+
: '.');
|
|
164
|
+
const exactEntries = new Map();
|
|
165
|
+
const wildcardEntries = [];
|
|
166
|
+
for (const [pattern, targetsValue] of Object.entries(rawPaths)) {
|
|
167
|
+
const targets = Array.isArray(targetsValue)
|
|
168
|
+
? targetsValue.filter((entry) => typeof entry === 'string' && entry.trim().length > 0)
|
|
169
|
+
: [];
|
|
170
|
+
if (targets.length === 0)
|
|
171
|
+
continue;
|
|
172
|
+
if (pattern.includes('*')) {
|
|
173
|
+
const starIndex = pattern.indexOf('*');
|
|
174
|
+
wildcardEntries.push({
|
|
175
|
+
prefix: pattern.slice(0, starIndex),
|
|
176
|
+
suffix: pattern.slice(starIndex + 1),
|
|
177
|
+
targets,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
exactEntries.set(pattern, targets);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
const resolver = {
|
|
185
|
+
resolve(request) {
|
|
186
|
+
if (!isBareSpecifier(request))
|
|
187
|
+
return null;
|
|
188
|
+
const resolveTargets = (targets, wildcardValue) => {
|
|
189
|
+
for (const targetPattern of targets) {
|
|
190
|
+
const replacedTarget = wildcardValue === undefined ? targetPattern : targetPattern.replace('*', wildcardValue);
|
|
191
|
+
const candidate = resolveAliasTargetPath(path_1.default.resolve(baseDir, replacedTarget));
|
|
192
|
+
if (candidate) {
|
|
193
|
+
return candidate;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return null;
|
|
197
|
+
};
|
|
198
|
+
const exactMatch = exactEntries.get(request);
|
|
199
|
+
if (exactMatch) {
|
|
200
|
+
return resolveTargets(exactMatch);
|
|
201
|
+
}
|
|
202
|
+
for (const entry of wildcardEntries) {
|
|
203
|
+
if (!request.startsWith(entry.prefix) || !request.endsWith(entry.suffix)) {
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
const wildcardValue = request.slice(entry.prefix.length, request.length - entry.suffix.length);
|
|
207
|
+
const resolved = resolveTargets(entry.targets, wildcardValue);
|
|
208
|
+
if (resolved) {
|
|
209
|
+
return resolved;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return null;
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
projectAliasCache.set(cwd, resolver);
|
|
216
|
+
return resolver;
|
|
217
|
+
}
|
|
@@ -80,6 +80,7 @@ const request_context_1 = require("./request-context");
|
|
|
80
80
|
const app_router_runtime_1 = require("./app-router-runtime");
|
|
81
81
|
const fetch_policy_1 = require("./fetch-policy");
|
|
82
82
|
const vista_import_map_1 = require("./vista-import-map");
|
|
83
|
+
const project_alias_resolver_1 = require("./project-alias-resolver");
|
|
83
84
|
// Support CSS imports on server runtime
|
|
84
85
|
// - Regular .css: ignored (handled by PostCSS)
|
|
85
86
|
// - .module.css: return empty class mapping (webpack build handles real mappings)
|
|
@@ -273,10 +274,15 @@ function installSingleReactResolution(cwd) {
|
|
|
273
274
|
}
|
|
274
275
|
}
|
|
275
276
|
originalResolveFilename = CjsModule._resolveFilename;
|
|
277
|
+
const projectAliasResolver = (0, project_alias_resolver_1.createProjectAliasResolver)(cwd, resolveFromWorkspace);
|
|
276
278
|
CjsModule._resolveFilename = function (request, parent, isMain, options) {
|
|
277
279
|
const vistaResolvedPath = resolveVistaInternalRequest(request);
|
|
278
280
|
if (vistaResolvedPath)
|
|
279
281
|
return vistaResolvedPath;
|
|
282
|
+
const aliasResolvedPath = projectAliasResolver?.resolve(request);
|
|
283
|
+
if (aliasResolvedPath) {
|
|
284
|
+
return originalResolveFilename.call(this, aliasResolvedPath, parent, isMain, options);
|
|
285
|
+
}
|
|
280
286
|
if (request === 'react')
|
|
281
287
|
return reactPath;
|
|
282
288
|
if (request === 'react-dom')
|
|
@@ -20,6 +20,7 @@ const fetch_policy_1 = require("./fetch-policy");
|
|
|
20
20
|
const runtime_artifacts_1 = require("./runtime-artifacts");
|
|
21
21
|
const config_1 = require("../config");
|
|
22
22
|
const vista_import_map_1 = require("./vista-import-map");
|
|
23
|
+
const project_alias_resolver_1 = require("./project-alias-resolver");
|
|
23
24
|
// NOTE: RouteErrorBoundary and RouteSuspense are 'use client' components.
|
|
24
25
|
// Under --conditions react-server, React.Component is not available, so we
|
|
25
26
|
// must NOT import them at the top level. Instead we lazy-require them after
|
|
@@ -190,7 +191,7 @@ function isClientBoundaryFile(filename, transpiledSource) {
|
|
|
190
191
|
clientDirectiveCache.set(filename, isClient);
|
|
191
192
|
return isClient;
|
|
192
193
|
}
|
|
193
|
-
function installSingleReactResolution() {
|
|
194
|
+
function installSingleReactResolution(cwd) {
|
|
194
195
|
if (reactResolutionInstalled)
|
|
195
196
|
return;
|
|
196
197
|
let reactPath;
|
|
@@ -203,10 +204,15 @@ function installSingleReactResolution() {
|
|
|
203
204
|
return;
|
|
204
205
|
}
|
|
205
206
|
originalResolveFilename = CjsModule._resolveFilename;
|
|
207
|
+
const projectAliasResolver = (0, project_alias_resolver_1.createProjectAliasResolver)(cwd, resolveFromWorkspace);
|
|
206
208
|
CjsModule._resolveFilename = function (request, parent, isMain, options) {
|
|
207
209
|
const vistaResolvedPath = resolveVistaInternalRequest(request);
|
|
208
210
|
if (vistaResolvedPath)
|
|
209
211
|
return vistaResolvedPath;
|
|
212
|
+
const aliasResolvedPath = projectAliasResolver?.resolve(request);
|
|
213
|
+
if (aliasResolvedPath) {
|
|
214
|
+
return originalResolveFilename.call(this, aliasResolvedPath, parent, isMain, options);
|
|
215
|
+
}
|
|
210
216
|
if (request === 'react')
|
|
211
217
|
return reactPath;
|
|
212
218
|
if (request === 'react-dom')
|
|
@@ -472,7 +478,7 @@ function startUpstream() {
|
|
|
472
478
|
const isDev = process.env.NODE_ENV !== 'production';
|
|
473
479
|
const port = resolvePort(3101);
|
|
474
480
|
const vistaDirRoot = path_1.default.join(cwd, constants_1.BUILD_DIR);
|
|
475
|
-
installSingleReactResolution();
|
|
481
|
+
installSingleReactResolution(runtimeRoot);
|
|
476
482
|
setupTypeScriptRuntime(runtimeRoot);
|
|
477
483
|
const flightServerPath = resolveFromWorkspace('react-server-dom-webpack/server.node', cwd);
|
|
478
484
|
const flightServer = require(flightServerPath);
|
|
@@ -27,6 +27,7 @@ const spawn_permissions_1 = require("./spawn-permissions");
|
|
|
27
27
|
const config_1 = require("../config");
|
|
28
28
|
const ppr_1 = require("./ppr");
|
|
29
29
|
const vista_import_map_1 = require("./vista-import-map");
|
|
30
|
+
const project_alias_resolver_1 = require("./project-alias-resolver");
|
|
30
31
|
const CjsModule = require('module');
|
|
31
32
|
let staticRuntimeReady = false;
|
|
32
33
|
let reactResolutionInstalled = false;
|
|
@@ -72,10 +73,15 @@ function installSingleReactResolution(cwd) {
|
|
|
72
73
|
}
|
|
73
74
|
}
|
|
74
75
|
originalResolveFilename = CjsModule._resolveFilename;
|
|
76
|
+
const projectAliasResolver = (0, project_alias_resolver_1.createProjectAliasResolver)(cwd, resolveFromWorkspace);
|
|
75
77
|
CjsModule._resolveFilename = function (request, parent, isMain, options) {
|
|
76
78
|
const vistaResolvedPath = resolveVistaInternalRequest(request);
|
|
77
79
|
if (vistaResolvedPath)
|
|
78
80
|
return vistaResolvedPath;
|
|
81
|
+
const aliasResolvedPath = projectAliasResolver?.resolve(request);
|
|
82
|
+
if (aliasResolvedPath) {
|
|
83
|
+
return originalResolveFilename.call(this, aliasResolvedPath, parent, isMain, options);
|
|
84
|
+
}
|
|
79
85
|
if (request === 'react')
|
|
80
86
|
return reactPath;
|
|
81
87
|
if (request === 'react-dom')
|
|
@@ -22,6 +22,12 @@ const TYPED_API_ENTRYPOINTS = [
|
|
|
22
22
|
path_1.default.join('app', 'typed-api.js'),
|
|
23
23
|
path_1.default.join('app', 'typed-api.jsx'),
|
|
24
24
|
];
|
|
25
|
+
const METADATA_ROUTE_MAPPINGS = [
|
|
26
|
+
{ requestPath: '/robots.txt', stem: 'robots' },
|
|
27
|
+
{ requestPath: '/sitemap.xml', stem: 'sitemap' },
|
|
28
|
+
{ requestPath: '/manifest.webmanifest', stem: 'manifest' },
|
|
29
|
+
];
|
|
30
|
+
const ROUTE_FILE_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx'];
|
|
25
31
|
class BodyLimitError extends Error {
|
|
26
32
|
status = 413;
|
|
27
33
|
constructor(limitBytes) {
|
|
@@ -82,6 +88,44 @@ function normalizeRouteRequestPath(requestPath) {
|
|
|
82
88
|
}
|
|
83
89
|
return normalized.replace(/^\/+/, '').replace(/\/+$/, '');
|
|
84
90
|
}
|
|
91
|
+
function isRouteGroupDirectory(name) {
|
|
92
|
+
return /^\([\w-]+\)$/.test(name);
|
|
93
|
+
}
|
|
94
|
+
function resolveMetadataRoutePath(cwd, stem) {
|
|
95
|
+
const appDir = path_1.default.resolve(cwd, 'app');
|
|
96
|
+
const tryStemInDirectory = (dir) => {
|
|
97
|
+
for (const extension of ROUTE_FILE_EXTENSIONS) {
|
|
98
|
+
const candidate = path_1.default.join(dir, `${stem}${extension}`);
|
|
99
|
+
if (fs_1.default.existsSync(candidate)) {
|
|
100
|
+
return candidate;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
};
|
|
105
|
+
const directMatch = tryStemInDirectory(appDir);
|
|
106
|
+
if (directMatch) {
|
|
107
|
+
return directMatch;
|
|
108
|
+
}
|
|
109
|
+
const searchGroupDirectories = (dir) => {
|
|
110
|
+
const entries = fs_1.default
|
|
111
|
+
.readdirSync(dir, { withFileTypes: true })
|
|
112
|
+
.filter((entry) => entry.isDirectory() && isRouteGroupDirectory(entry.name))
|
|
113
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
114
|
+
for (const entry of entries) {
|
|
115
|
+
const groupDir = path_1.default.join(dir, entry.name);
|
|
116
|
+
const match = tryStemInDirectory(groupDir);
|
|
117
|
+
if (match) {
|
|
118
|
+
return match;
|
|
119
|
+
}
|
|
120
|
+
const nestedMatch = searchGroupDirectories(groupDir);
|
|
121
|
+
if (nestedMatch) {
|
|
122
|
+
return nestedMatch;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return null;
|
|
126
|
+
};
|
|
127
|
+
return searchGroupDirectories(appDir);
|
|
128
|
+
}
|
|
85
129
|
function hasMethodMatch(router, pathname, method) {
|
|
86
130
|
const normalized = method.toLowerCase();
|
|
87
131
|
return router.resolve(pathname, normalized) !== null;
|
|
@@ -307,6 +351,13 @@ function resolveLegacyApiRoutePath(cwd, requestPath) {
|
|
|
307
351
|
function resolveLegacyRouteHandlerPath(cwd, requestPath) {
|
|
308
352
|
const normalized = normalizeRouteRequestPath(requestPath);
|
|
309
353
|
const routeCandidates = [];
|
|
354
|
+
const metadataRoute = METADATA_ROUTE_MAPPINGS.find((entry) => entry.requestPath === String(requestPath || '').split('?')[0]);
|
|
355
|
+
if (metadataRoute) {
|
|
356
|
+
const resolvedMetadataPath = resolveMetadataRoutePath(cwd, metadataRoute.stem);
|
|
357
|
+
if (resolvedMetadataPath) {
|
|
358
|
+
routeCandidates.push(resolvedMetadataPath);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
310
361
|
if (normalized.startsWith('api/')) {
|
|
311
362
|
const apiRoute = normalized.slice('api/'.length);
|
|
312
363
|
routeCandidates.push(path_1.default.resolve(cwd, 'app', 'api', apiRoute, 'route.ts'), path_1.default.resolve(cwd, 'app', 'api', apiRoute, 'route.tsx'), path_1.default.resolve(cwd, 'app', 'api', apiRoute, 'route.js'), path_1.default.resolve(cwd, 'app', 'api', apiRoute, 'route.jsx'), path_1.default.resolve(cwd, 'app', 'api', `${apiRoute}.ts`), path_1.default.resolve(cwd, 'app', 'api', `${apiRoute}.tsx`), path_1.default.resolve(cwd, 'app', 'api', `${apiRoute}.js`), path_1.default.resolve(cwd, 'app', 'api', `${apiRoute}.jsx`));
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ThemeScript = exports.useTheme = exports.applyTheme = exports.ThemeProvider = void 0;
|
|
4
|
+
var theme_provider_1 = require("./theme-provider");
|
|
5
|
+
Object.defineProperty(exports, "ThemeProvider", { enumerable: true, get: function () { return theme_provider_1.ThemeProvider; } });
|
|
6
|
+
Object.defineProperty(exports, "applyTheme", { enumerable: true, get: function () { return theme_provider_1.applyTheme; } });
|
|
7
|
+
Object.defineProperty(exports, "useTheme", { enumerable: true, get: function () { return theme_provider_1.useTheme; } });
|
|
8
|
+
var theme_script_1 = require("./theme-script");
|
|
9
|
+
Object.defineProperty(exports, "ThemeScript", { enumerable: true, get: function () { return theme_script_1.ThemeScript; } });
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
export type ThemeMode = 'system' | 'light' | 'dark';
|
|
3
|
+
export type ResolvedTheme = 'light' | 'dark';
|
|
4
|
+
interface ThemeContextValue {
|
|
5
|
+
theme: ThemeMode;
|
|
6
|
+
resolvedTheme: ResolvedTheme;
|
|
7
|
+
setTheme: (theme: ThemeMode) => void;
|
|
8
|
+
cycleTheme: () => void;
|
|
9
|
+
mounted: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function applyTheme(theme: ThemeMode): void;
|
|
12
|
+
interface ThemeProviderProps {
|
|
13
|
+
children: ReactNode;
|
|
14
|
+
defaultTheme?: ThemeMode;
|
|
15
|
+
}
|
|
16
|
+
export declare function ThemeProvider({ children, defaultTheme, }: ThemeProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
export declare function useTheme(): ThemeContextValue;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.applyTheme = applyTheme;
|
|
5
|
+
exports.ThemeProvider = ThemeProvider;
|
|
6
|
+
exports.useTheme = useTheme;
|
|
7
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
|
+
const react_1 = require("react");
|
|
9
|
+
const THEME_STORAGE_KEY = 'vista-theme';
|
|
10
|
+
const THEME_ORDER = ['system', 'light', 'dark'];
|
|
11
|
+
const MEDIA_QUERY = '(prefers-color-scheme: dark)';
|
|
12
|
+
const ThemeContext = (0, react_1.createContext)(null);
|
|
13
|
+
function sanitizeTheme(value, fallback) {
|
|
14
|
+
if (value === 'system' || value === 'light' || value === 'dark') {
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
return fallback;
|
|
18
|
+
}
|
|
19
|
+
function getSystemTheme() {
|
|
20
|
+
if (typeof window === 'undefined') {
|
|
21
|
+
return 'dark';
|
|
22
|
+
}
|
|
23
|
+
return window.matchMedia(MEDIA_QUERY).matches ? 'dark' : 'light';
|
|
24
|
+
}
|
|
25
|
+
function resolveTheme(theme) {
|
|
26
|
+
return theme === 'system' ? getSystemTheme() : theme;
|
|
27
|
+
}
|
|
28
|
+
function applyTheme(theme) {
|
|
29
|
+
if (typeof document === 'undefined') {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const resolvedTheme = resolveTheme(theme);
|
|
33
|
+
const root = document.documentElement;
|
|
34
|
+
root.classList.remove('light', 'dark');
|
|
35
|
+
root.classList.add(resolvedTheme);
|
|
36
|
+
root.dataset.theme = theme;
|
|
37
|
+
root.style.colorScheme = resolvedTheme;
|
|
38
|
+
}
|
|
39
|
+
function ThemeProvider({ children, defaultTheme = 'system', }) {
|
|
40
|
+
const [theme, setThemeState] = (0, react_1.useState)(defaultTheme);
|
|
41
|
+
const [mounted, setMounted] = (0, react_1.useState)(false);
|
|
42
|
+
(0, react_1.useEffect)(() => {
|
|
43
|
+
const nextTheme = sanitizeTheme(window.localStorage.getItem(THEME_STORAGE_KEY), defaultTheme);
|
|
44
|
+
setThemeState(nextTheme);
|
|
45
|
+
applyTheme(nextTheme);
|
|
46
|
+
setMounted(true);
|
|
47
|
+
}, [defaultTheme]);
|
|
48
|
+
(0, react_1.useEffect)(() => {
|
|
49
|
+
if (!mounted)
|
|
50
|
+
return;
|
|
51
|
+
window.localStorage.setItem(THEME_STORAGE_KEY, theme);
|
|
52
|
+
applyTheme(theme);
|
|
53
|
+
}, [mounted, theme]);
|
|
54
|
+
(0, react_1.useEffect)(() => {
|
|
55
|
+
if (!mounted)
|
|
56
|
+
return;
|
|
57
|
+
const media = window.matchMedia(MEDIA_QUERY);
|
|
58
|
+
const handleMediaChange = () => {
|
|
59
|
+
const currentTheme = sanitizeTheme(window.localStorage.getItem(THEME_STORAGE_KEY), defaultTheme);
|
|
60
|
+
if (currentTheme === 'system') {
|
|
61
|
+
applyTheme('system');
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
const handleStorage = (event) => {
|
|
65
|
+
if (event.key !== THEME_STORAGE_KEY)
|
|
66
|
+
return;
|
|
67
|
+
const nextTheme = sanitizeTheme(event.newValue, defaultTheme);
|
|
68
|
+
setThemeState(nextTheme);
|
|
69
|
+
applyTheme(nextTheme);
|
|
70
|
+
};
|
|
71
|
+
if (typeof media.addEventListener === 'function') {
|
|
72
|
+
media.addEventListener('change', handleMediaChange);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
media.addListener(handleMediaChange);
|
|
76
|
+
}
|
|
77
|
+
window.addEventListener('storage', handleStorage);
|
|
78
|
+
return () => {
|
|
79
|
+
if (typeof media.removeEventListener === 'function') {
|
|
80
|
+
media.removeEventListener('change', handleMediaChange);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
media.removeListener(handleMediaChange);
|
|
84
|
+
}
|
|
85
|
+
window.removeEventListener('storage', handleStorage);
|
|
86
|
+
};
|
|
87
|
+
}, [defaultTheme, mounted]);
|
|
88
|
+
const setTheme = (0, react_1.useCallback)((nextTheme) => {
|
|
89
|
+
setThemeState(nextTheme);
|
|
90
|
+
}, []);
|
|
91
|
+
const cycleTheme = (0, react_1.useCallback)(() => {
|
|
92
|
+
setThemeState((currentTheme) => {
|
|
93
|
+
const index = THEME_ORDER.indexOf(currentTheme);
|
|
94
|
+
return THEME_ORDER[(index + 1) % THEME_ORDER.length];
|
|
95
|
+
});
|
|
96
|
+
}, []);
|
|
97
|
+
const value = (0, react_1.useMemo)(() => ({
|
|
98
|
+
theme,
|
|
99
|
+
resolvedTheme: resolveTheme(theme),
|
|
100
|
+
setTheme,
|
|
101
|
+
cycleTheme,
|
|
102
|
+
mounted,
|
|
103
|
+
}), [cycleTheme, mounted, setTheme, theme]);
|
|
104
|
+
return (0, jsx_runtime_1.jsx)(ThemeContext.Provider, { value: value, children: children });
|
|
105
|
+
}
|
|
106
|
+
function useTheme() {
|
|
107
|
+
const context = (0, react_1.useContext)(ThemeContext);
|
|
108
|
+
if (!context) {
|
|
109
|
+
throw new Error('useTheme must be used within a ThemeProvider.');
|
|
110
|
+
}
|
|
111
|
+
return context;
|
|
112
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ThemeScript = ThemeScript;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const THEME_STORAGE_KEY = 'vista-theme';
|
|
6
|
+
const WINDOW_ACCESS = "Function('return this')()";
|
|
7
|
+
const ROOT_ELEMENT_ACCESS = "runtime['doc'+'ument'].documentElement";
|
|
8
|
+
const STORAGE_ACCESS = "runtime['local'+'Storage']";
|
|
9
|
+
function getThemeScript(defaultTheme) {
|
|
10
|
+
return `(function(){var runtime=${WINDOW_ACCESS};var storageKey='${THEME_STORAGE_KEY}';var defaultTheme='${defaultTheme}';var mediaQuery='(prefers-color-scheme: dark)';function sanitize(value){return value==='system'||value==='light'||value==='dark'?value:defaultTheme;}function resolve(theme){if(theme==='system'){return runtime.matchMedia(mediaQuery).matches?'dark':'light';}return theme;}function apply(theme){var resolved=resolve(theme);var root=${ROOT_ELEMENT_ACCESS};root.classList.remove('light','dark');root.classList.add(resolved);root.dataset.theme=theme;root.style.colorScheme=resolved;}var stored=sanitize(${STORAGE_ACCESS}.getItem(storageKey));apply(stored);}());`;
|
|
11
|
+
}
|
|
12
|
+
function ThemeScript({ defaultTheme = 'system' }) {
|
|
13
|
+
return ((0, jsx_runtime_1.jsx)("script", { dangerouslySetInnerHTML: {
|
|
14
|
+
__html: getThemeScript(defaultTheme),
|
|
15
|
+
} }));
|
|
16
|
+
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vistagenic/vista",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.15",
|
|
4
4
|
"description": "The React Framework for Visionaries - Rust-powered SSR with Server Components",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
|
-
"url": "git+https://github.com/
|
|
9
|
+
"url": "git+https://github.com/vistakit/Vista-Js.git",
|
|
10
10
|
"directory": "packages/vista"
|
|
11
11
|
},
|
|
12
12
|
"author": "Vista Team",
|
|
@@ -68,6 +68,10 @@
|
|
|
68
68
|
"types": "./dist/font/local.d.ts",
|
|
69
69
|
"default": "./dist/font/local.js"
|
|
70
70
|
},
|
|
71
|
+
"./theme": {
|
|
72
|
+
"types": "./dist/theme/index.d.ts",
|
|
73
|
+
"default": "./dist/theme/index.js"
|
|
74
|
+
},
|
|
71
75
|
"./head": {
|
|
72
76
|
"react-server": "./dist/client/head.react-server.js",
|
|
73
77
|
"types": "./dist/client/head.d.ts",
|
|
@@ -144,5 +148,6 @@
|
|
|
144
148
|
"@types/webpack": "^5.28.5",
|
|
145
149
|
"@types/webpack-hot-middleware": "^2.25.9",
|
|
146
150
|
"typescript": "^5.7.2"
|
|
147
|
-
}
|
|
151
|
+
},
|
|
152
|
+
"gitHead": "9397c476040f8e0e79fba153a3f05bea5c7bdab9"
|
|
148
153
|
}
|