@vistagenic/vista 0.2.3 → 0.2.5
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/build-rsc.js +81 -5
- package/dist/bin/build.js +25 -5
- package/dist/build/manifest.js +23 -5
- package/dist/build/rsc/server-manifest.js +2 -2
- package/dist/client/link.d.ts +1 -1
- package/dist/client/link.js +30 -11
- package/dist/server/rsc-engine.js +158 -71
- package/dist/server/rsc-upstream.js +24 -19
- package/dist/server/static-generator.js +17 -2
- package/package.json +2 -3
- package/LICENSE +0 -21
package/dist/bin/build-rsc.js
CHANGED
|
@@ -50,6 +50,53 @@ function runPostCSS(cwd, vistaDir) {
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
|
+
function hasUseClientDirective(filePath) {
|
|
54
|
+
try {
|
|
55
|
+
const source = fs_1.default.readFileSync(filePath, 'utf-8');
|
|
56
|
+
return /^\s*['"]use client['"]\s*;?/m.test(source);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function collectUseClientFiles(dir, collected) {
|
|
63
|
+
if (!fs_1.default.existsSync(dir))
|
|
64
|
+
return;
|
|
65
|
+
const entries = fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
66
|
+
for (const entry of entries) {
|
|
67
|
+
const absolutePath = path_1.default.join(dir, entry.name);
|
|
68
|
+
if (entry.isDirectory()) {
|
|
69
|
+
collectUseClientFiles(absolutePath, collected);
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (!entry.isFile() || !entry.name.endsWith('.js')) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (hasUseClientDirective(absolutePath)) {
|
|
76
|
+
collected.add(path_1.default.resolve(absolutePath));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function resolvePackageRoot(cwd, packageName) {
|
|
81
|
+
try {
|
|
82
|
+
return path_1.default.dirname(require.resolve(`${packageName}/package.json`, { paths: [cwd] }));
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function collectFrameworkClientReferences(cwd) {
|
|
89
|
+
const roots = [resolvePackageRoot(cwd, 'vista'), resolvePackageRoot(cwd, '@vistagenic/vista')].filter((value) => Boolean(value));
|
|
90
|
+
if (roots.length === 0) {
|
|
91
|
+
return [];
|
|
92
|
+
}
|
|
93
|
+
const collected = new Set();
|
|
94
|
+
for (const packageRoot of roots) {
|
|
95
|
+
collectUseClientFiles(path_1.default.join(packageRoot, 'dist', 'client'), collected);
|
|
96
|
+
collectUseClientFiles(path_1.default.join(packageRoot, 'dist', 'components'), collected);
|
|
97
|
+
}
|
|
98
|
+
return Array.from(collected);
|
|
99
|
+
}
|
|
53
100
|
/**
|
|
54
101
|
* Generate the RSC-aware client entry file
|
|
55
102
|
*/
|
|
@@ -236,6 +283,15 @@ async function buildRSC(watch = false) {
|
|
|
236
283
|
}
|
|
237
284
|
}
|
|
238
285
|
}
|
|
286
|
+
// Include framework-level client boundaries (e.g. vista/link) so external
|
|
287
|
+
// package client modules resolve in React Flight manifests.
|
|
288
|
+
const frameworkClientReferences = collectFrameworkClientReferences(cwd);
|
|
289
|
+
if (frameworkClientReferences.length > 0) {
|
|
290
|
+
clientReferenceFiles = Array.from(new Set([...clientReferenceFiles, ...frameworkClientReferences]));
|
|
291
|
+
if (_debug) {
|
|
292
|
+
console.log(`[Vista JS RSC] Added ${frameworkClientReferences.length} framework client references`);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
239
295
|
// Generate manifests
|
|
240
296
|
if (_debug)
|
|
241
297
|
console.log('[vista:build] Generating manifests...');
|
|
@@ -301,13 +357,33 @@ async function buildRSC(watch = false) {
|
|
|
301
357
|
const clientConfig = (0, compiler_1.createClientWebpackConfig)(options);
|
|
302
358
|
const clientCompiler = (0, webpack_1.default)(clientConfig);
|
|
303
359
|
syncReactServerManifests(vistaDirs.root);
|
|
304
|
-
// Watch for CSS changes
|
|
360
|
+
// Watch for CSS + source changes that can affect Tailwind output.
|
|
305
361
|
try {
|
|
306
362
|
const chokidar = require('chokidar');
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
363
|
+
const styleWatchRoots = ['app', 'components', 'content', 'lib', 'ctx', 'data']
|
|
364
|
+
.map((entry) => path_1.default.join(cwd, entry))
|
|
365
|
+
.filter((entry) => fs_1.default.existsSync(entry));
|
|
366
|
+
let cssTimer = null;
|
|
367
|
+
const scheduleCSSBuild = () => {
|
|
368
|
+
if (cssTimer)
|
|
369
|
+
clearTimeout(cssTimer);
|
|
370
|
+
cssTimer = setTimeout(() => {
|
|
371
|
+
if (_debug)
|
|
372
|
+
console.log('[Vista JS RSC] Style source changed, rebuilding CSS...');
|
|
373
|
+
runPostCSS(cwd, vistaDirs.root);
|
|
374
|
+
}, 120);
|
|
375
|
+
};
|
|
376
|
+
chokidar
|
|
377
|
+
.watch(styleWatchRoots, {
|
|
378
|
+
ignoreInitial: true,
|
|
379
|
+
ignored: (watchedPath) => watchedPath.includes(`${path_1.default.sep}node_modules${path_1.default.sep}`) ||
|
|
380
|
+
watchedPath.includes(`${path_1.default.sep}.git${path_1.default.sep}`) ||
|
|
381
|
+
watchedPath.includes(`${path_1.default.sep}.vista${path_1.default.sep}`),
|
|
382
|
+
})
|
|
383
|
+
.on('all', (_event, changedPath) => {
|
|
384
|
+
if (/\.(?:css|[cm]?[jt]sx?|md|mdx)$/i.test(changedPath)) {
|
|
385
|
+
scheduleCSSBuild();
|
|
386
|
+
}
|
|
311
387
|
});
|
|
312
388
|
}
|
|
313
389
|
catch (e) {
|
package/dist/bin/build.js
CHANGED
|
@@ -380,13 +380,33 @@ async function buildClient(watch = false, onRebuild) {
|
|
|
380
380
|
// In watch mode, we return the compiler for use with dev middleware
|
|
381
381
|
// Initial CSS build
|
|
382
382
|
runPostCSS(cwd, vistaDir);
|
|
383
|
-
// Watch
|
|
383
|
+
// Watch CSS + source files that may affect Tailwind output.
|
|
384
384
|
const chokidar = require('chokidar');
|
|
385
385
|
try {
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
386
|
+
const styleWatchRoots = ['app', 'components', 'content', 'lib', 'ctx', 'data']
|
|
387
|
+
.map((entry) => path_1.default.join(cwd, entry))
|
|
388
|
+
.filter((entry) => fs_1.default.existsSync(entry));
|
|
389
|
+
let cssTimer = null;
|
|
390
|
+
const scheduleCSSBuild = () => {
|
|
391
|
+
if (cssTimer)
|
|
392
|
+
clearTimeout(cssTimer);
|
|
393
|
+
cssTimer = setTimeout(() => {
|
|
394
|
+
if (_debug)
|
|
395
|
+
console.log('Style source changed, rebuilding CSS...');
|
|
396
|
+
runPostCSS(cwd, vistaDir);
|
|
397
|
+
}, 120);
|
|
398
|
+
};
|
|
399
|
+
chokidar
|
|
400
|
+
.watch(styleWatchRoots, {
|
|
401
|
+
ignoreInitial: true,
|
|
402
|
+
ignored: (watchedPath) => watchedPath.includes(`${path_1.default.sep}node_modules${path_1.default.sep}`) ||
|
|
403
|
+
watchedPath.includes(`${path_1.default.sep}.git${path_1.default.sep}`) ||
|
|
404
|
+
watchedPath.includes(`${path_1.default.sep}.vista${path_1.default.sep}`),
|
|
405
|
+
})
|
|
406
|
+
.on('all', (_event, changedPath) => {
|
|
407
|
+
if (/\.(?:css|[cm]?[jt]sx?|md|mdx)$/i.test(changedPath)) {
|
|
408
|
+
scheduleCSSBuild();
|
|
409
|
+
}
|
|
390
410
|
});
|
|
391
411
|
}
|
|
392
412
|
catch (e) {
|
package/dist/build/manifest.js
CHANGED
|
@@ -103,11 +103,29 @@ function generateBuildManifest(vistaDir, buildId, pages = {}) {
|
|
|
103
103
|
return manifest;
|
|
104
104
|
}
|
|
105
105
|
function toRegexFromPattern(pattern) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
106
|
+
if (pattern === '/') {
|
|
107
|
+
return '^/$';
|
|
108
|
+
}
|
|
109
|
+
const normalized = pattern.startsWith('/') ? pattern.slice(1) : pattern;
|
|
110
|
+
const parts = normalized.split('/').filter(Boolean);
|
|
111
|
+
const regexParts = parts.map((part) => {
|
|
112
|
+
if (!part.startsWith(':')) {
|
|
113
|
+
return part.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
114
|
+
}
|
|
115
|
+
const dynamicMatch = /^:([a-zA-Z0-9_]+)(\*)?(\?)?$/.exec(part);
|
|
116
|
+
if (!dynamicMatch) {
|
|
117
|
+
return part.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
118
|
+
}
|
|
119
|
+
const [, paramName, isCatchAll, isOptional] = dynamicMatch;
|
|
120
|
+
if (isCatchAll && isOptional) {
|
|
121
|
+
return `(?<${paramName}>.*)`;
|
|
122
|
+
}
|
|
123
|
+
if (isCatchAll) {
|
|
124
|
+
return `(?<${paramName}>.+)`;
|
|
125
|
+
}
|
|
126
|
+
return `(?<${paramName}>[^/]+)`;
|
|
127
|
+
});
|
|
128
|
+
return `^/${regexParts.join('/')}$`;
|
|
111
129
|
}
|
|
112
130
|
function toRouteInfo(route) {
|
|
113
131
|
return {
|
|
@@ -282,8 +282,8 @@ function buildRoutes(components, appDir) {
|
|
|
282
282
|
// Static URL pattern + auto mode = static by default
|
|
283
283
|
renderMode = 'static';
|
|
284
284
|
}
|
|
285
|
-
else if (rt === 'dynamic' && hasStaticParams) {
|
|
286
|
-
// Dynamic URL pattern + generateStaticParams = can be statically generated
|
|
285
|
+
else if ((rt === 'dynamic' || rt === 'catch-all') && hasStaticParams) {
|
|
286
|
+
// Dynamic or catch-all URL pattern + generateStaticParams = can be statically generated
|
|
287
287
|
renderMode = 'static';
|
|
288
288
|
}
|
|
289
289
|
routes.push({
|
package/dist/client/link.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export interface LinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>,
|
|
|
17
17
|
shallow?: boolean;
|
|
18
18
|
/** Force href on child element */
|
|
19
19
|
passHref?: boolean;
|
|
20
|
-
/** Prefetch strategy: true =
|
|
20
|
+
/** Prefetch strategy: true = always, 'auto' = production-only (Next-like), false/null = off */
|
|
21
21
|
prefetch?: boolean | 'auto' | null;
|
|
22
22
|
/** Locale for internationalised routing */
|
|
23
23
|
locale?: string | false;
|
package/dist/client/link.js
CHANGED
|
@@ -102,16 +102,31 @@ function isInternalUrl(url) {
|
|
|
102
102
|
}
|
|
103
103
|
return true;
|
|
104
104
|
}
|
|
105
|
-
|
|
105
|
+
function resolvePrefetchBehavior(prefetch) {
|
|
106
|
+
if (prefetch === false || prefetch === null) {
|
|
107
|
+
return { viewport: false, intent: false };
|
|
108
|
+
}
|
|
109
|
+
if (prefetch === true) {
|
|
110
|
+
return { viewport: true, intent: true };
|
|
111
|
+
}
|
|
112
|
+
const isProduction = process.env.NODE_ENV === 'production';
|
|
113
|
+
return {
|
|
114
|
+
viewport: isProduction,
|
|
115
|
+
intent: isProduction,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
exports.Link = react_1.default.forwardRef(({ href, as, replace, scroll = true, shallow, passHref, prefetch = 'auto', legacyBehavior, children, onClick, onMouseEnter, onTouchStart, onNavigate, target, ...props }, ref) => {
|
|
106
119
|
// Try the RSC router first — if we're inside an RSCRouter, use
|
|
107
120
|
// Flight-based navigation. Otherwise fall back to the legacy router.
|
|
108
|
-
const rscRouter = (0,
|
|
109
|
-
const legacyRouter = (0,
|
|
110
|
-
const
|
|
121
|
+
const rscRouter = (0, react_1.useContext)(rsc_router_1.RSCRouterContext);
|
|
122
|
+
const legacyRouter = (0, react_1.useContext)(router_1.RouterContext);
|
|
123
|
+
const fallbackPathname = (0, router_1.usePathname)();
|
|
124
|
+
const pathname = rscRouter?.pathname ?? legacyRouter?.pathname ?? fallbackPathname;
|
|
111
125
|
const linkRef = (0, react_1.useRef)(null);
|
|
112
126
|
const targetPath = formatUrl(as || href);
|
|
113
127
|
const [isActive, setIsActive] = (0, react_1.useState)(false);
|
|
114
128
|
const internal = (0, react_1.useMemo)(() => isInternalUrl(targetPath), [targetPath]);
|
|
129
|
+
const prefetchBehavior = (0, react_1.useMemo)(() => resolvePrefetchBehavior(prefetch), [prefetch]);
|
|
115
130
|
// Combine refs
|
|
116
131
|
const setRefs = (0, react_1.useCallback)((node) => {
|
|
117
132
|
linkRef.current = node;
|
|
@@ -133,12 +148,14 @@ exports.Link = react_1.default.forwardRef(({ href, as, replace, scroll = true, s
|
|
|
133
148
|
}, [targetPath, pathname]);
|
|
134
149
|
// Prefetch on viewport intersection (skip for external links & auto mode)
|
|
135
150
|
(0, react_1.useEffect)(() => {
|
|
136
|
-
if (!
|
|
151
|
+
if (!prefetchBehavior.viewport)
|
|
137
152
|
return;
|
|
138
153
|
if (!internal)
|
|
139
154
|
return;
|
|
140
155
|
if (typeof window === 'undefined')
|
|
141
156
|
return;
|
|
157
|
+
if (pathname === targetPath)
|
|
158
|
+
return;
|
|
142
159
|
const element = linkRef.current;
|
|
143
160
|
if (!element)
|
|
144
161
|
return;
|
|
@@ -160,12 +177,12 @@ exports.Link = react_1.default.forwardRef(({ href, as, replace, scroll = true, s
|
|
|
160
177
|
});
|
|
161
178
|
observer.observe(element);
|
|
162
179
|
return () => observer.disconnect();
|
|
163
|
-
}, [
|
|
180
|
+
}, [prefetchBehavior.viewport, targetPath, pathname, rscRouter, internal]);
|
|
164
181
|
// Prefetch on hover
|
|
165
182
|
const handleMouseEnter = (0, react_1.useCallback)((e) => {
|
|
166
183
|
if (onMouseEnter)
|
|
167
184
|
onMouseEnter(e);
|
|
168
|
-
if (
|
|
185
|
+
if (prefetchBehavior.intent && internal && pathname !== targetPath) {
|
|
169
186
|
if (rscRouter) {
|
|
170
187
|
rscRouter.prefetch(targetPath);
|
|
171
188
|
}
|
|
@@ -173,12 +190,12 @@ exports.Link = react_1.default.forwardRef(({ href, as, replace, scroll = true, s
|
|
|
173
190
|
prefetchUrl(targetPath);
|
|
174
191
|
}
|
|
175
192
|
}
|
|
176
|
-
}, [onMouseEnter,
|
|
193
|
+
}, [onMouseEnter, prefetchBehavior.intent, targetPath, pathname, rscRouter, internal]);
|
|
177
194
|
// Prefetch on touch (mobile devices)
|
|
178
195
|
const handleTouchStart = (0, react_1.useCallback)((e) => {
|
|
179
196
|
if (onTouchStart)
|
|
180
197
|
onTouchStart(e);
|
|
181
|
-
if (
|
|
198
|
+
if (prefetchBehavior.intent && internal && pathname !== targetPath) {
|
|
182
199
|
if (rscRouter) {
|
|
183
200
|
rscRouter.prefetch(targetPath);
|
|
184
201
|
}
|
|
@@ -186,7 +203,7 @@ exports.Link = react_1.default.forwardRef(({ href, as, replace, scroll = true, s
|
|
|
186
203
|
prefetchUrl(targetPath);
|
|
187
204
|
}
|
|
188
205
|
}
|
|
189
|
-
}, [onTouchStart,
|
|
206
|
+
}, [onTouchStart, prefetchBehavior.intent, targetPath, pathname, rscRouter, internal]);
|
|
190
207
|
// Handle navigation
|
|
191
208
|
const handleClick = (0, react_1.useCallback)((e) => {
|
|
192
209
|
if (onClick)
|
|
@@ -204,6 +221,8 @@ exports.Link = react_1.default.forwardRef(({ href, as, replace, scroll = true, s
|
|
|
204
221
|
return;
|
|
205
222
|
if (!internal)
|
|
206
223
|
return; // external / mailto / tel
|
|
224
|
+
if (!rscRouter && !legacyRouter)
|
|
225
|
+
return; // No router provider -> allow native navigation
|
|
207
226
|
e.preventDefault();
|
|
208
227
|
if (onNavigate)
|
|
209
228
|
onNavigate();
|
|
@@ -216,7 +235,7 @@ exports.Link = react_1.default.forwardRef(({ href, as, replace, scroll = true, s
|
|
|
216
235
|
rscRouter.push(targetPath, { scroll });
|
|
217
236
|
}
|
|
218
237
|
}
|
|
219
|
-
else {
|
|
238
|
+
else if (legacyRouter) {
|
|
220
239
|
if (replace) {
|
|
221
240
|
legacyRouter.replace(targetPath, { scroll });
|
|
222
241
|
}
|
|
@@ -114,6 +114,27 @@ function resolvePort(raw, fallback) {
|
|
|
114
114
|
}
|
|
115
115
|
return value;
|
|
116
116
|
}
|
|
117
|
+
function normalizeModuleCachePath(filePath) {
|
|
118
|
+
return filePath.replace(/\\/g, '/').toLowerCase();
|
|
119
|
+
}
|
|
120
|
+
function shouldInvalidateDevModule(modulePath, cwd) {
|
|
121
|
+
const normalized = normalizeModuleCachePath(modulePath);
|
|
122
|
+
const rootPrefix = normalizeModuleCachePath(`${cwd}${path_1.default.sep}`);
|
|
123
|
+
if (!normalized.startsWith(rootPrefix))
|
|
124
|
+
return false;
|
|
125
|
+
if (normalized.includes('/node_modules/'))
|
|
126
|
+
return false;
|
|
127
|
+
if (normalized.includes(`/${constants_1.BUILD_DIR.toLowerCase()}/`))
|
|
128
|
+
return false;
|
|
129
|
+
return /\.(?:[cm]?[jt]sx?|json)$/i.test(normalized);
|
|
130
|
+
}
|
|
131
|
+
function clearProjectRequireCache(cwd) {
|
|
132
|
+
for (const key of Object.keys(require.cache)) {
|
|
133
|
+
if (!shouldInvalidateDevModule(key, cwd))
|
|
134
|
+
continue;
|
|
135
|
+
delete require.cache[key];
|
|
136
|
+
}
|
|
137
|
+
}
|
|
117
138
|
function resolveFromWorkspace(specifier, cwd) {
|
|
118
139
|
const searchRoots = [
|
|
119
140
|
cwd,
|
|
@@ -241,18 +262,23 @@ function cleanHotUpdateFiles(cwd) {
|
|
|
241
262
|
}
|
|
242
263
|
}
|
|
243
264
|
}
|
|
244
|
-
function findChunkFiles(cwd) {
|
|
265
|
+
function findChunkFiles(cwd, isDev) {
|
|
245
266
|
const chunksDir = path_1.default.join(cwd, constants_1.BUILD_DIR, 'static', 'chunks');
|
|
246
267
|
if (!fs_1.default.existsSync(chunksDir))
|
|
247
268
|
return [];
|
|
248
269
|
const files = fs_1.default
|
|
249
270
|
.readdirSync(chunksDir)
|
|
250
271
|
.filter((name) => name.endsWith('.js') && !name.endsWith('.map') && !name.includes('.hot-update.'));
|
|
272
|
+
// In dev, avoid loading stale production artifacts left from a previous build.
|
|
273
|
+
// Production chunks end with a hash suffix like `main-1a2b3c4d.js`.
|
|
274
|
+
const normalizedFiles = isDev
|
|
275
|
+
? files.filter((name) => !/-[0-9a-f]{8,}\.js$/i.test(name))
|
|
276
|
+
: files;
|
|
251
277
|
// Load webpack runtime first, then framework, then the rest alphabetically.
|
|
252
278
|
// This ensures the chunk registry (__webpack_require__) is available before
|
|
253
279
|
// any deferred chunk tries to self-register.
|
|
254
280
|
const priority = ['webpack.js', 'framework.js', 'vendor.js'];
|
|
255
|
-
return
|
|
281
|
+
return normalizedFiles.sort((a, b) => {
|
|
256
282
|
const ai = priority.indexOf(a);
|
|
257
283
|
const bi = priority.indexOf(b);
|
|
258
284
|
if (ai !== -1 && bi !== -1)
|
|
@@ -264,6 +290,41 @@ function findChunkFiles(cwd) {
|
|
|
264
290
|
return a.localeCompare(b);
|
|
265
291
|
});
|
|
266
292
|
}
|
|
293
|
+
function normalizeSSRManifest(manifest) {
|
|
294
|
+
if (!manifest || !manifest.moduleMap) {
|
|
295
|
+
return manifest;
|
|
296
|
+
}
|
|
297
|
+
const moduleMap = manifest.moduleMap;
|
|
298
|
+
const aliasEntries = [];
|
|
299
|
+
for (const [moduleKey, exportsMap] of Object.entries(moduleMap)) {
|
|
300
|
+
const normalizedExports = {};
|
|
301
|
+
for (const [exportName, rawEntry] of Object.entries(exportsMap || {})) {
|
|
302
|
+
const entry = rawEntry || {};
|
|
303
|
+
const normalizedId = String(entry.id ?? entry.specifier ?? moduleKey);
|
|
304
|
+
normalizedExports[exportName] = {
|
|
305
|
+
id: normalizedId,
|
|
306
|
+
chunks: Array.isArray(entry.chunks) ? entry.chunks : [],
|
|
307
|
+
name: entry.name || exportName,
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
moduleMap[moduleKey] = normalizedExports;
|
|
311
|
+
aliasEntries.push([moduleKey, normalizedExports]);
|
|
312
|
+
for (const normalizedEntry of Object.values(normalizedExports)) {
|
|
313
|
+
const aliasKey = String(normalizedEntry.id);
|
|
314
|
+
aliasEntries.push([aliasKey, normalizedExports]);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
for (const [aliasKey, exportsMap] of aliasEntries) {
|
|
318
|
+
if (!moduleMap[aliasKey]) {
|
|
319
|
+
moduleMap[aliasKey] = exportsMap;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return manifest;
|
|
323
|
+
}
|
|
324
|
+
function loadSSRManifestFromDisk(absolutePath) {
|
|
325
|
+
const manifest = JSON.parse(fs_1.default.readFileSync(absolutePath, 'utf-8'));
|
|
326
|
+
return normalizeSSRManifest(manifest);
|
|
327
|
+
}
|
|
267
328
|
function matchPattern(pathname, pattern) {
|
|
268
329
|
const patternParts = pattern.split('/').filter(Boolean);
|
|
269
330
|
const pathParts = pathname.split('/').filter(Boolean);
|
|
@@ -326,14 +387,10 @@ function extractParams(pathname, route) {
|
|
|
326
387
|
}
|
|
327
388
|
return params;
|
|
328
389
|
}
|
|
329
|
-
async function createRouteElement(route, context, isDev, rootLayout) {
|
|
390
|
+
async function createRouteElement(route, context, isDev, rootLayout, cwd) {
|
|
330
391
|
const { params, searchParams, req } = context;
|
|
331
392
|
if (isDev) {
|
|
332
|
-
|
|
333
|
-
if (key.includes(`${path_1.default.sep}app${path_1.default.sep}`)) {
|
|
334
|
-
delete require.cache[key];
|
|
335
|
-
}
|
|
336
|
-
}
|
|
393
|
+
clearProjectRequireCache(cwd);
|
|
337
394
|
}
|
|
338
395
|
const PageModule = require(route.pagePath);
|
|
339
396
|
const PageComponent = PageModule.default;
|
|
@@ -520,11 +577,21 @@ async function renderFlightToHTMLStream(upstreamOrigin, pathname, search, metada
|
|
|
520
577
|
const nodeStream = stream_1.Readable.fromWeb(upstream.body);
|
|
521
578
|
// 3. Decode Flight stream into a React tree
|
|
522
579
|
const flightResponse = flightSSRClient.createFromNodeStream(nodeStream, ssrManifest);
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
580
|
+
let element;
|
|
581
|
+
// In dev, decode the flight payload before React.use() render path.
|
|
582
|
+
// This keeps manifest mismatch errors in the request try/catch boundary
|
|
583
|
+
// instead of crashing the whole process with an uncaught exception.
|
|
584
|
+
if (isDev) {
|
|
585
|
+
const resolvedTree = await flightResponse;
|
|
586
|
+
element = react_1.default.createElement(react_1.default.Fragment, null, resolvedTree);
|
|
587
|
+
}
|
|
588
|
+
else {
|
|
589
|
+
// 4. Wrap in a component that consumes the Flight response
|
|
590
|
+
function FlightRoot() {
|
|
591
|
+
return react_1.default.use(flightResponse);
|
|
592
|
+
}
|
|
593
|
+
element = react_1.default.createElement(FlightRoot);
|
|
526
594
|
}
|
|
527
|
-
const element = react_1.default.createElement(FlightRoot);
|
|
528
595
|
// 5. Build script tags for client chunks
|
|
529
596
|
const scripts = chunkFiles
|
|
530
597
|
.map((chunk) => `<script defer src="${constants_1.STATIC_CHUNKS_PATH}${chunk}"></script>`)
|
|
@@ -669,26 +736,7 @@ function startRSCServer(options = {}) {
|
|
|
669
736
|
? ssrManifestLegacyPath
|
|
670
737
|
: null;
|
|
671
738
|
if (resolvedSSRManifestPath) {
|
|
672
|
-
ssrManifest =
|
|
673
|
-
// Normalise SSR manifest entries: ReactFlightWebpackPlugin generates
|
|
674
|
-
// {specifier, name} but react-server-dom-webpack/client.node expects
|
|
675
|
-
// {id, chunks, name}. Our VistaSSRManifestPatch webpack plugin handles
|
|
676
|
-
// this at build time, but in dev mode the manifest may be re-read from
|
|
677
|
-
// disk before the patch runs. Belt-and-suspenders fix:
|
|
678
|
-
if (ssrManifest && ssrManifest.moduleMap) {
|
|
679
|
-
const moduleMap = ssrManifest.moduleMap;
|
|
680
|
-
for (const exports of Object.values(moduleMap)) {
|
|
681
|
-
for (const [exportName, entry] of Object.entries(exports)) {
|
|
682
|
-
if (entry.specifier && !entry.id) {
|
|
683
|
-
exports[exportName] = {
|
|
684
|
-
id: entry.specifier,
|
|
685
|
-
chunks: [],
|
|
686
|
-
name: entry.name || exportName,
|
|
687
|
-
};
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
}
|
|
739
|
+
ssrManifest = loadSSRManifestFromDisk(resolvedSSRManifestPath);
|
|
692
740
|
}
|
|
693
741
|
else if (flightSSRClient) {
|
|
694
742
|
// Can't use Flight SSR without the manifest
|
|
@@ -814,21 +862,71 @@ function startRSCServer(options = {}) {
|
|
|
814
862
|
const pushSSE = (payload) => {
|
|
815
863
|
sseReloadClients.forEach((c) => c.write(`data: ${payload}\n\n`));
|
|
816
864
|
};
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
865
|
+
const watchExtPattern = /\.(?:[cm]?[jt]sx?|css|md|mdx|json)$/i;
|
|
866
|
+
const watchRoots = [
|
|
867
|
+
'app',
|
|
868
|
+
'components',
|
|
869
|
+
'content',
|
|
870
|
+
'lib',
|
|
871
|
+
'ctx',
|
|
872
|
+
'data',
|
|
873
|
+
'middleware.ts',
|
|
874
|
+
'vista.config.ts',
|
|
875
|
+
'content-collections.ts',
|
|
876
|
+
]
|
|
877
|
+
.map((entry) => path_1.default.join(cwd, entry))
|
|
878
|
+
.filter((entry) => fs_1.default.existsSync(entry));
|
|
821
879
|
let reloadTimer = null;
|
|
822
|
-
|
|
823
|
-
if (
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
880
|
+
const scheduleReload = () => {
|
|
881
|
+
if (reloadTimer)
|
|
882
|
+
clearTimeout(reloadTimer);
|
|
883
|
+
reloadTimer = setTimeout(() => {
|
|
884
|
+
(0, logger_1.logEvent)('Source changed, reloading...');
|
|
885
|
+
pushSSE('reload');
|
|
886
|
+
}, 140);
|
|
887
|
+
};
|
|
888
|
+
try {
|
|
889
|
+
const chokidar = require('chokidar');
|
|
890
|
+
const watcher = chokidar.watch(watchRoots, {
|
|
891
|
+
ignoreInitial: true,
|
|
892
|
+
ignored: (watchedPath) => watchedPath.includes(`${path_1.default.sep}node_modules${path_1.default.sep}`) ||
|
|
893
|
+
watchedPath.includes(`${path_1.default.sep}.git${path_1.default.sep}`) ||
|
|
894
|
+
watchedPath.includes(`${path_1.default.sep}${constants_1.BUILD_DIR}${path_1.default.sep}`),
|
|
895
|
+
});
|
|
896
|
+
watcher.on('all', (_event, filePath) => {
|
|
897
|
+
if (filePath && watchExtPattern.test(filePath)) {
|
|
898
|
+
scheduleReload();
|
|
899
|
+
}
|
|
900
|
+
});
|
|
901
|
+
fsWatcher = watcher;
|
|
902
|
+
}
|
|
903
|
+
catch {
|
|
904
|
+
const nativeWatchers = [];
|
|
905
|
+
const onChange = (_event, filePath) => {
|
|
906
|
+
if (filePath && watchExtPattern.test(filePath)) {
|
|
907
|
+
scheduleReload();
|
|
908
|
+
}
|
|
909
|
+
};
|
|
910
|
+
for (const watchPath of watchRoots) {
|
|
911
|
+
try {
|
|
912
|
+
const stat = fs_1.default.statSync(watchPath);
|
|
913
|
+
if (stat.isDirectory()) {
|
|
914
|
+
nativeWatchers.push(fs_1.default.watch(watchPath, { recursive: true }, onChange));
|
|
915
|
+
}
|
|
916
|
+
else {
|
|
917
|
+
nativeWatchers.push(fs_1.default.watch(watchPath, onChange));
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
catch {
|
|
921
|
+
// Skip missing or unsupported watch path.
|
|
922
|
+
}
|
|
830
923
|
}
|
|
831
|
-
|
|
924
|
+
fsWatcher = {
|
|
925
|
+
close: () => {
|
|
926
|
+
nativeWatchers.forEach((watcher) => watcher.close());
|
|
927
|
+
},
|
|
928
|
+
};
|
|
929
|
+
}
|
|
832
930
|
}
|
|
833
931
|
if (isDev && options.compiler) {
|
|
834
932
|
app.use((0, webpack_dev_middleware_1.default)(options.compiler, {
|
|
@@ -856,22 +954,7 @@ function startRSCServer(options = {}) {
|
|
|
856
954
|
}
|
|
857
955
|
// Reload SSR manifest on rebuild too
|
|
858
956
|
if (resolvedSSRManifestPath && fs_1.default.existsSync(resolvedSSRManifestPath)) {
|
|
859
|
-
ssrManifest =
|
|
860
|
-
// Normalise {specifier,name} → {id,chunks,name} (same as initial load)
|
|
861
|
-
if (ssrManifest && ssrManifest.moduleMap) {
|
|
862
|
-
const mm = ssrManifest.moduleMap;
|
|
863
|
-
for (const exports of Object.values(mm)) {
|
|
864
|
-
for (const [expName, entry] of Object.entries(exports)) {
|
|
865
|
-
if (entry.specifier && !entry.id) {
|
|
866
|
-
exports[expName] = {
|
|
867
|
-
id: entry.specifier,
|
|
868
|
-
chunks: [],
|
|
869
|
-
name: entry.name || expName,
|
|
870
|
-
};
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
}
|
|
957
|
+
ssrManifest = loadSSRManifestFromDisk(resolvedSSRManifestPath);
|
|
875
958
|
}
|
|
876
959
|
});
|
|
877
960
|
}
|
|
@@ -1082,17 +1165,21 @@ function startRSCServer(options = {}) {
|
|
|
1082
1165
|
// ==================================================================
|
|
1083
1166
|
if (useFlightSSR) {
|
|
1084
1167
|
try {
|
|
1168
|
+
if (isDev && resolvedSSRManifestPath && fs_1.default.existsSync(resolvedSSRManifestPath)) {
|
|
1169
|
+
try {
|
|
1170
|
+
ssrManifest = loadSSRManifestFromDisk(resolvedSSRManifestPath);
|
|
1171
|
+
}
|
|
1172
|
+
catch {
|
|
1173
|
+
// Manifest may be mid-write during compilation; keep the last good in-memory copy.
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1085
1176
|
// Metadata extraction: still done locally so we have <head> content
|
|
1086
1177
|
const rootLayout = (0, root_resolver_1.resolveRootLayout)(cwd, isDev);
|
|
1087
1178
|
const route = matchRoute(req.path, serverManifest.routes);
|
|
1088
1179
|
let metadataHtml = '';
|
|
1089
1180
|
if (route) {
|
|
1090
1181
|
if (isDev) {
|
|
1091
|
-
|
|
1092
|
-
if (key.includes(`${path_1.default.sep}app${path_1.default.sep}`)) {
|
|
1093
|
-
delete require.cache[key];
|
|
1094
|
-
}
|
|
1095
|
-
}
|
|
1182
|
+
clearProjectRequireCache(cwd);
|
|
1096
1183
|
}
|
|
1097
1184
|
const PageModule = require(route.pagePath);
|
|
1098
1185
|
let metadata = { ...(rootLayout.metadata || {}) };
|
|
@@ -1114,7 +1201,7 @@ function startRSCServer(options = {}) {
|
|
|
1114
1201
|
metadataHtml = metadata ? generateMetadataHtml(metadata) : '';
|
|
1115
1202
|
}
|
|
1116
1203
|
// Render the page via Flight stream → SSR
|
|
1117
|
-
await renderFlightToHTMLStream(upstreamOrigin, req.path, req.query ? new URLSearchParams(req.query).toString() : '', metadataHtml, findChunkFiles(cwd), rootLayout.mode, flightSSRClient, ssrManifest, res, isDev);
|
|
1204
|
+
await renderFlightToHTMLStream(upstreamOrigin, req.path, req.query ? new URLSearchParams(req.query).toString() : '', metadataHtml, findChunkFiles(cwd, isDev), rootLayout.mode, flightSSRClient, ssrManifest, res, isDev);
|
|
1118
1205
|
return;
|
|
1119
1206
|
}
|
|
1120
1207
|
catch (flightError) {
|
|
@@ -1182,7 +1269,7 @@ function startRSCServer(options = {}) {
|
|
|
1182
1269
|
res
|
|
1183
1270
|
.status(404)
|
|
1184
1271
|
.type('text/html')
|
|
1185
|
-
.send(createHtmlDocument(html, '', findChunkFiles(cwd), rootLayout.mode));
|
|
1272
|
+
.send(createHtmlDocument(html, '', findChunkFiles(cwd, isDev), rootLayout.mode));
|
|
1186
1273
|
return;
|
|
1187
1274
|
}
|
|
1188
1275
|
res.status(404).type('text/html').send((0, not_found_page_1.getStyledNotFoundHTML)());
|
|
@@ -1190,14 +1277,14 @@ function startRSCServer(options = {}) {
|
|
|
1190
1277
|
}
|
|
1191
1278
|
const params = extractParams(req.path, route);
|
|
1192
1279
|
const searchParams = Object.fromEntries(new URLSearchParams(req.query).entries());
|
|
1193
|
-
const { element, metadata, rootMode } = await createRouteElement(route, { params, searchParams, req }, isDev, rootLayout);
|
|
1280
|
+
const { element, metadata, rootMode } = await createRouteElement(route, { params, searchParams, req }, isDev, rootLayout, cwd);
|
|
1194
1281
|
const appHtml = (0, server_1.renderToString)(element);
|
|
1195
1282
|
const { generateMetadataHtml } = require('../metadata/generate');
|
|
1196
1283
|
const metadataHtml = metadata ? generateMetadataHtml(metadata) : '';
|
|
1197
1284
|
res
|
|
1198
1285
|
.status(200)
|
|
1199
1286
|
.type('text/html')
|
|
1200
|
-
.send(createHtmlDocument(appHtml, metadataHtml, findChunkFiles(cwd), rootMode));
|
|
1287
|
+
.send(createHtmlDocument(appHtml, metadataHtml, findChunkFiles(cwd, isDev), rootMode));
|
|
1201
1288
|
}
|
|
1202
1289
|
catch (error) {
|
|
1203
1290
|
if (error?.name === 'NotFoundError') {
|
|
@@ -1214,7 +1301,7 @@ function startRSCServer(options = {}) {
|
|
|
1214
1301
|
res
|
|
1215
1302
|
.status(404)
|
|
1216
1303
|
.type('text/html')
|
|
1217
|
-
.send(createHtmlDocument(html, '', findChunkFiles(cwd), rootLayout.mode));
|
|
1304
|
+
.send(createHtmlDocument(html, '', findChunkFiles(cwd, isDev), rootLayout.mode));
|
|
1218
1305
|
return;
|
|
1219
1306
|
}
|
|
1220
1307
|
res.status(404).type('text/html').send((0, not_found_page_1.getStyledNotFoundHTML)());
|
|
@@ -77,6 +77,25 @@ function resolveFromWorkspace(specifier, cwd) {
|
|
|
77
77
|
function normalizeModulePath(filePath) {
|
|
78
78
|
return filePath.replace(/\\/g, '/').toLowerCase();
|
|
79
79
|
}
|
|
80
|
+
function shouldInvalidateDevModule(modulePath, cwd) {
|
|
81
|
+
const normalized = normalizeModulePath(modulePath);
|
|
82
|
+
const rootPrefix = normalizeModulePath(`${cwd}${path_1.default.sep}`);
|
|
83
|
+
if (!normalized.startsWith(rootPrefix))
|
|
84
|
+
return false;
|
|
85
|
+
if (normalized.includes('/node_modules/'))
|
|
86
|
+
return false;
|
|
87
|
+
if (normalized.includes(`/${constants_1.BUILD_DIR.toLowerCase()}/`))
|
|
88
|
+
return false;
|
|
89
|
+
return /\.(?:[cm]?[jt]sx?|json)$/i.test(normalized);
|
|
90
|
+
}
|
|
91
|
+
function clearProjectRequireCache(cwd) {
|
|
92
|
+
for (const key of Object.keys(require.cache)) {
|
|
93
|
+
if (!shouldInvalidateDevModule(key, cwd))
|
|
94
|
+
continue;
|
|
95
|
+
delete require.cache[key];
|
|
96
|
+
clientDirectiveCache.delete(key);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
80
99
|
function setupTypeScriptRuntime(cwd) {
|
|
81
100
|
try {
|
|
82
101
|
const swcPath = require.resolve('@swc-node/register', { paths: [cwd] });
|
|
@@ -174,18 +193,10 @@ function installSingleReactResolution() {
|
|
|
174
193
|
function installClientLoadHook(cwd, createClientModuleProxy) {
|
|
175
194
|
if (installedClientLoadHook)
|
|
176
195
|
return;
|
|
177
|
-
const appDir = path_1.default.join(cwd, 'app');
|
|
178
|
-
const componentsDir = path_1.default.join(cwd, 'components');
|
|
179
|
-
const normalizedAppDir = normalizeModulePath(appDir);
|
|
180
|
-
const normalizedComponentsDir = normalizeModulePath(componentsDir);
|
|
181
196
|
originalCompile = CjsModule.prototype._compile;
|
|
182
197
|
CjsModule.prototype._compile = function (content, filename) {
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
const isInComponentsTree = normalized.startsWith(normalizedComponentsDir);
|
|
186
|
-
if ((isInAppTree || isInComponentsTree) &&
|
|
187
|
-
/\.[jt]sx?$/.test(filename) &&
|
|
188
|
-
isClientBoundaryFile(filename, content)) {
|
|
198
|
+
const isJavaScriptModule = /\.[jt]sx?$/.test(filename);
|
|
199
|
+
if (isJavaScriptModule && isClientBoundaryFile(filename, content)) {
|
|
189
200
|
const moduleId = (0, url_1.pathToFileURL)(filename).href;
|
|
190
201
|
this.exports = createClientModuleProxy(moduleId);
|
|
191
202
|
return;
|
|
@@ -254,16 +265,10 @@ function extractParams(pathname, route) {
|
|
|
254
265
|
}
|
|
255
266
|
return params;
|
|
256
267
|
}
|
|
257
|
-
async function createRouteElement(route, context, isDev) {
|
|
268
|
+
async function createRouteElement(route, context, isDev, cwd) {
|
|
258
269
|
const { params, searchParams, req } = context;
|
|
259
270
|
if (isDev) {
|
|
260
|
-
|
|
261
|
-
if (key.includes(`${path_1.default.sep}app${path_1.default.sep}`) ||
|
|
262
|
-
key.includes(`${path_1.default.sep}components${path_1.default.sep}`)) {
|
|
263
|
-
delete require.cache[key];
|
|
264
|
-
clientDirectiveCache.delete(key);
|
|
265
|
-
}
|
|
266
|
-
}
|
|
271
|
+
clearProjectRequireCache(cwd);
|
|
267
272
|
}
|
|
268
273
|
const PageModule = require(route.pagePath);
|
|
269
274
|
const PageComponent = PageModule.default;
|
|
@@ -429,7 +434,7 @@ function startUpstream() {
|
|
|
429
434
|
}
|
|
430
435
|
const params = extractParams(pathname, route);
|
|
431
436
|
const searchParams = Object.fromEntries(new URLSearchParams(req.query).entries());
|
|
432
|
-
const element = await createRouteElement(route, { params, searchParams, req }, isDev);
|
|
437
|
+
const element = await createRouteElement(route, { params, searchParams, req }, isDev, cwd);
|
|
433
438
|
res.setHeader('Content-Type', 'text/x-component');
|
|
434
439
|
res.setHeader('Vary', 'Accept');
|
|
435
440
|
const stream = flightServer.renderToPipeableStream(element, flightManifest, {
|
|
@@ -181,6 +181,21 @@ async function prerenderPage(urlPath, route, params, cwd) {
|
|
|
181
181
|
try {
|
|
182
182
|
const React = require('react');
|
|
183
183
|
const { renderToString } = require('react-dom/server');
|
|
184
|
+
const isAsyncComponent = (component) => {
|
|
185
|
+
return (typeof component === 'function' &&
|
|
186
|
+
component.constructor &&
|
|
187
|
+
component.constructor.name === 'AsyncFunction');
|
|
188
|
+
};
|
|
189
|
+
const renderComponent = async (component, props, child) => {
|
|
190
|
+
if (isAsyncComponent(component)) {
|
|
191
|
+
const asyncProps = child === undefined ? props : { ...props, children: child };
|
|
192
|
+
return component(asyncProps);
|
|
193
|
+
}
|
|
194
|
+
if (child === undefined) {
|
|
195
|
+
return React.createElement(component, props);
|
|
196
|
+
}
|
|
197
|
+
return React.createElement(component, props, child);
|
|
198
|
+
};
|
|
184
199
|
// Load page component from webpack-built server bundle
|
|
185
200
|
const pageModule = require(route.pagePath);
|
|
186
201
|
const PageComponent = pageModule.default;
|
|
@@ -189,14 +204,14 @@ async function prerenderPage(urlPath, route, params, cwd) {
|
|
|
189
204
|
return null;
|
|
190
205
|
}
|
|
191
206
|
// Build the element, passing params as props
|
|
192
|
-
let element =
|
|
207
|
+
let element = await renderComponent(PageComponent, { params: params || {} });
|
|
193
208
|
// Wrap in layouts (outside-in)
|
|
194
209
|
for (let i = route.layoutPaths.length - 1; i >= 0; i--) {
|
|
195
210
|
try {
|
|
196
211
|
const layoutModule = require(route.layoutPaths[i]);
|
|
197
212
|
const LayoutComponent = layoutModule.default;
|
|
198
213
|
if (LayoutComponent) {
|
|
199
|
-
element =
|
|
214
|
+
element = await renderComponent(LayoutComponent, { params: params || {}, searchParams: {} }, element);
|
|
200
215
|
}
|
|
201
216
|
}
|
|
202
217
|
catch {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vistagenic/vista",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
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",
|
|
@@ -132,6 +132,5 @@
|
|
|
132
132
|
"@types/webpack": "^5.28.5",
|
|
133
133
|
"@types/webpack-hot-middleware": "^2.25.9",
|
|
134
134
|
"typescript": "^5.7.2"
|
|
135
|
-
}
|
|
136
|
-
"gitHead": "4a9f1db26023c01465b997f6955b3d3a57b6ac84"
|
|
135
|
+
}
|
|
137
136
|
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
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.
|