veslx 0.1.29 → 0.1.31
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/bin/lib/build.ts +1 -0
- package/bin/lib/export.ts +1 -0
- package/bin/lib/serve.ts +1 -0
- package/dist/client/components/post-list.js +6 -4
- package/dist/client/components/post-list.js.map +1 -1
- package/package.json +1 -1
- package/plugin/src/plugin.ts +38 -24
- package/plugin/src/types.ts +2 -2
- package/src/components/post-list.tsx +7 -5
package/bin/lib/build.ts
CHANGED
package/bin/lib/export.ts
CHANGED
package/bin/lib/serve.ts
CHANGED
|
@@ -38,15 +38,17 @@ function PostList() {
|
|
|
38
38
|
return /* @__PURE__ */ jsx("div", { className: "py-24 text-center", children: /* @__PURE__ */ jsx("p", { className: "text-muted-foreground font-mono text-sm tracking-wide", children: "no entries" }) });
|
|
39
39
|
}
|
|
40
40
|
const sortMode = ((_a = veslxConfig.posts) == null ? void 0 : _a.sort) ?? "alpha";
|
|
41
|
-
if (sortMode === "date") {
|
|
41
|
+
if (sortMode === "date" || sortMode === "date-asc") {
|
|
42
|
+
const ascending = sortMode === "date-asc";
|
|
42
43
|
posts = posts.sort((a, b) => {
|
|
43
44
|
var _a2, _b;
|
|
44
45
|
const dateA = (_a2 = getFrontmatter(a)) == null ? void 0 : _a2.date;
|
|
45
46
|
const dateB = (_b = getFrontmatter(b)) == null ? void 0 : _b.date;
|
|
46
47
|
if (!dateA && !dateB) return a.name.localeCompare(b.name);
|
|
47
|
-
if (!dateA) return 1;
|
|
48
|
-
if (!dateB) return
|
|
49
|
-
|
|
48
|
+
if (!dateA) return -1;
|
|
49
|
+
if (!dateB) return 1;
|
|
50
|
+
const diff = new Date(dateA).getTime() - new Date(dateB).getTime();
|
|
51
|
+
return ascending ? diff : -diff;
|
|
50
52
|
});
|
|
51
53
|
} else {
|
|
52
54
|
posts = posts.sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"post-list.js","sources":["../../../src/components/post-list.tsx"],"sourcesContent":["import { useParams } from \"react-router-dom\";\nimport {\n type PostEntry,\n directoryToPostEntries,\n filterVisiblePosts,\n getFrontmatter,\n} from \"@/lib/content-classification\";\nimport { useDirectory } from \"../../plugin/src/client\";\nimport { ErrorDisplay } from \"./page-error\";\nimport Loading from \"./loading\";\nimport { PostListItem } from \"./post-list-item\";\nimport veslxConfig from \"virtual:veslx-config\";\n\n// Helper to format name for display (e.g., \"01-getting-started\" → \"Getting Started\")\nfunction formatName(name: string): string {\n return name\n .replace(/^\\d+-/, '')\n .replace(/-/g, ' ')\n .replace(/\\b\\w/g, c => c.toUpperCase());\n}\n\n// Helper to get link path from post\nfunction getLinkPath(post: PostEntry): string {\n if (post.file) {\n // Standalone MDX file\n return `/${post.file.path}`;\n } else if (post.slides && !post.readme) {\n // Folder with only slides\n return `/${post.slides.path}`;\n } else if (post.readme) {\n // Folder with readme\n return `/${post.readme.path}`;\n } else {\n // Fallback to folder path\n return `/${post.path}`;\n }\n}\n\nexport function PostList() {\n const { \"*\": path = \".\" } = useParams();\n\n const { directory, loading, error } = useDirectory(path)\n\n if (error) {\n return <ErrorDisplay error={error} path={path} />;\n }\n\n if (loading) {\n return (\n <Loading />\n )\n }\n\n if (!directory) {\n return (\n <div className=\"py-24 text-center\">\n <p className=\"text-muted-foreground font-mono text-sm tracking-wide\">no entries</p>\n </div>\n );\n }\n\n let posts = directoryToPostEntries(directory);\n\n if (posts.length === 0) {\n return (\n <div className=\"py-24 text-center\">\n <p className=\"text-muted-foreground font-mono text-sm tracking-wide\">no entries</p>\n </div>\n );\n }\n\n // Filter out hidden and draft posts\n posts = filterVisiblePosts(posts);\n\n if (posts.length === 0) {\n return (\n <div className=\"py-24 text-center\">\n <p className=\"text-muted-foreground font-mono text-sm tracking-wide\">no entries</p>\n </div>\n );\n }\n\n // Sort based on config\n const sortMode = veslxConfig.posts?.sort ?? 'alpha';\n if (sortMode === 'date') {\n // Sort by date
|
|
1
|
+
{"version":3,"file":"post-list.js","sources":["../../../src/components/post-list.tsx"],"sourcesContent":["import { useParams } from \"react-router-dom\";\nimport {\n type PostEntry,\n directoryToPostEntries,\n filterVisiblePosts,\n getFrontmatter,\n} from \"@/lib/content-classification\";\nimport { useDirectory } from \"../../plugin/src/client\";\nimport { ErrorDisplay } from \"./page-error\";\nimport Loading from \"./loading\";\nimport { PostListItem } from \"./post-list-item\";\nimport veslxConfig from \"virtual:veslx-config\";\n\n// Helper to format name for display (e.g., \"01-getting-started\" → \"Getting Started\")\nfunction formatName(name: string): string {\n return name\n .replace(/^\\d+-/, '')\n .replace(/-/g, ' ')\n .replace(/\\b\\w/g, c => c.toUpperCase());\n}\n\n// Helper to get link path from post\nfunction getLinkPath(post: PostEntry): string {\n if (post.file) {\n // Standalone MDX file\n return `/${post.file.path}`;\n } else if (post.slides && !post.readme) {\n // Folder with only slides\n return `/${post.slides.path}`;\n } else if (post.readme) {\n // Folder with readme\n return `/${post.readme.path}`;\n } else {\n // Fallback to folder path\n return `/${post.path}`;\n }\n}\n\nexport function PostList() {\n const { \"*\": path = \".\" } = useParams();\n\n const { directory, loading, error } = useDirectory(path)\n\n if (error) {\n return <ErrorDisplay error={error} path={path} />;\n }\n\n if (loading) {\n return (\n <Loading />\n )\n }\n\n if (!directory) {\n return (\n <div className=\"py-24 text-center\">\n <p className=\"text-muted-foreground font-mono text-sm tracking-wide\">no entries</p>\n </div>\n );\n }\n\n let posts = directoryToPostEntries(directory);\n\n if (posts.length === 0) {\n return (\n <div className=\"py-24 text-center\">\n <p className=\"text-muted-foreground font-mono text-sm tracking-wide\">no entries</p>\n </div>\n );\n }\n\n // Filter out hidden and draft posts\n posts = filterVisiblePosts(posts);\n\n if (posts.length === 0) {\n return (\n <div className=\"py-24 text-center\">\n <p className=\"text-muted-foreground font-mono text-sm tracking-wide\">no entries</p>\n </div>\n );\n }\n\n // Sort based on config\n const sortMode = veslxConfig.posts?.sort ?? 'alpha';\n if (sortMode === 'date' || sortMode === 'date-asc') {\n const ascending = sortMode === 'date-asc';\n // Sort by date, posts without dates go to the top\n posts = posts.sort((a, b) => {\n const dateA = getFrontmatter(a)?.date;\n const dateB = getFrontmatter(b)?.date;\n if (!dateA && !dateB) return a.name.localeCompare(b.name);\n if (!dateA) return -1;\n if (!dateB) return 1;\n const diff = new Date(dateA as string).getTime() - new Date(dateB as string).getTime();\n return ascending ? diff : -diff;\n });\n } else {\n // Alphanumeric sorting by name\n posts = posts.sort((a, b) => a.name.localeCompare(b.name));\n }\n\n return (\n <div className=\"space-y-1 not-prose\">\n {posts.map((post) => {\n const frontmatter = getFrontmatter(post);\n const title = (frontmatter?.title as string) || formatName(post.name);\n const description = frontmatter?.description as string | undefined;\n const date = frontmatter?.date ? new Date(frontmatter.date as string) : undefined;\n const linkPath = getLinkPath(post);\n const isSlides = linkPath.endsWith('SLIDES.mdx') || linkPath.endsWith('.slides.mdx');\n\n return (\n <PostListItem\n key={post.path}\n title={title}\n description={description}\n date={date}\n linkPath={linkPath}\n isSlides={isSlides}\n />\n );\n })}\n </div>\n );\n}\n"],"names":["_a"],"mappings":";;;;;;;AAcA,SAAS,WAAW,MAAsB;AACxC,SAAO,KACJ,QAAQ,SAAS,EAAE,EACnB,QAAQ,MAAM,GAAG,EACjB,QAAQ,SAAS,CAAA,MAAK,EAAE,aAAa;AAC1C;AAGA,SAAS,YAAY,MAAyB;AAC5C,MAAI,KAAK,MAAM;AAEb,WAAO,IAAI,KAAK,KAAK,IAAI;AAAA,EAC3B,WAAW,KAAK,UAAU,CAAC,KAAK,QAAQ;AAEtC,WAAO,IAAI,KAAK,OAAO,IAAI;AAAA,EAC7B,WAAW,KAAK,QAAQ;AAEtB,WAAO,IAAI,KAAK,OAAO,IAAI;AAAA,EAC7B,OAAO;AAEL,WAAO,IAAI,KAAK,IAAI;AAAA,EACtB;AACF;AAEO,SAAS,WAAW;;AACzB,QAAM,EAAE,KAAK,OAAO,IAAA,IAAQ,UAAA;AAE5B,QAAM,EAAE,WAAoB,UAAU,aAAa,IAAI;AAEvD,MAAI,OAAO;AACT,WAAO,oBAAC,cAAA,EAAa,OAAc,KAAA,CAAY;AAAA,EACjD;AAQA,MAAI,CAAC,WAAW;AACd,WACE,oBAAC,SAAI,WAAU,qBACb,8BAAC,KAAA,EAAE,WAAU,yDAAwD,UAAA,aAAA,CAAU,EAAA,CACjF;AAAA,EAEJ;AAEA,MAAI,QAAQ,uBAAuB,SAAS;AAE5C,MAAI,MAAM,WAAW,GAAG;AACtB,WACE,oBAAC,SAAI,WAAU,qBACb,8BAAC,KAAA,EAAE,WAAU,yDAAwD,UAAA,aAAA,CAAU,EAAA,CACjF;AAAA,EAEJ;AAGA,UAAQ,mBAAmB,KAAK;AAEhC,MAAI,MAAM,WAAW,GAAG;AACtB,WACE,oBAAC,SAAI,WAAU,qBACb,8BAAC,KAAA,EAAE,WAAU,yDAAwD,UAAA,aAAA,CAAU,EAAA,CACjF;AAAA,EAEJ;AAGA,QAAM,aAAW,iBAAY,UAAZ,mBAAmB,SAAQ;AAC5C,MAAI,aAAa,UAAU,aAAa,YAAY;AAClD,UAAM,YAAY,aAAa;AAE/B,YAAQ,MAAM,KAAK,CAAC,GAAG,MAAM;;AAC3B,YAAM,SAAQA,MAAA,eAAe,CAAC,MAAhB,gBAAAA,IAAmB;AACjC,YAAM,SAAQ,oBAAe,CAAC,MAAhB,mBAAmB;AACjC,UAAI,CAAC,SAAS,CAAC,cAAc,EAAE,KAAK,cAAc,EAAE,IAAI;AACxD,UAAI,CAAC,MAAO,QAAO;AACnB,UAAI,CAAC,MAAO,QAAO;AACnB,YAAM,OAAO,IAAI,KAAK,KAAe,EAAE,QAAA,IAAY,IAAI,KAAK,KAAe,EAAE,QAAA;AAC7E,aAAO,YAAY,OAAO,CAAC;AAAA,IAC7B,CAAC;AAAA,EACH,OAAO;AAEL,YAAQ,MAAM,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAAA,EAC3D;AAEA,6BACG,OAAA,EAAI,WAAU,uBACZ,UAAA,MAAM,IAAI,CAAC,SAAS;AACnB,UAAM,cAAc,eAAe,IAAI;AACvC,UAAM,SAAS,2CAAa,UAAoB,WAAW,KAAK,IAAI;AACpE,UAAM,cAAc,2CAAa;AACjC,UAAM,QAAO,2CAAa,QAAO,IAAI,KAAK,YAAY,IAAc,IAAI;AACxE,UAAM,WAAW,YAAY,IAAI;AACjC,UAAM,WAAW,SAAS,SAAS,YAAY,KAAK,SAAS,SAAS,aAAa;AAEnF,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QAEC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,MALK,KAAK;AAAA,IAAA;AAAA,EAQhB,CAAC,EAAA,CACH;AAEJ;"}
|
package/package.json
CHANGED
package/plugin/src/plugin.ts
CHANGED
|
@@ -281,33 +281,15 @@ export default function contentPlugin(contentDir: string, config?: VeslxConfig,
|
|
|
281
281
|
next()
|
|
282
282
|
}
|
|
283
283
|
|
|
284
|
+
// Virtual module ID for the modified CSS
|
|
285
|
+
const VIRTUAL_CSS_MODULE = '\0veslx:index.css'
|
|
286
|
+
|
|
284
287
|
return {
|
|
285
288
|
name: 'content',
|
|
286
289
|
enforce: 'pre',
|
|
287
290
|
|
|
288
|
-
// Inject @source directive for Tailwind to scan content directory for classes
|
|
289
|
-
transform(code, id) {
|
|
290
|
-
// Only process CSS files containing the tailwindcss import
|
|
291
|
-
if (!id.endsWith('.css')) return null
|
|
292
|
-
if (!code.includes('@import "tailwindcss"')) return null
|
|
293
|
-
|
|
294
|
-
// Calculate relative path from CSS file to content directory
|
|
295
|
-
const cssDir = path.dirname(id)
|
|
296
|
-
let relativeContentDir = path.relative(cssDir, dir)
|
|
297
|
-
relativeContentDir = relativeContentDir.replace(/\\/g, '/') // Windows compatibility
|
|
298
|
-
|
|
299
|
-
// Inject @source directive after the tailwindcss import
|
|
300
|
-
const sourceDirective = `@source "${relativeContentDir}";`
|
|
301
|
-
const modified = code.replace(
|
|
302
|
-
/(@import\s+["']tailwindcss["'];?)/,
|
|
303
|
-
`$1\n${sourceDirective}`
|
|
304
|
-
)
|
|
305
|
-
|
|
306
|
-
return { code: modified, map: null }
|
|
307
|
-
},
|
|
308
|
-
|
|
309
291
|
// Inject @content alias and fs.allow into Vite config
|
|
310
|
-
config() {
|
|
292
|
+
config(config, { command }) {
|
|
311
293
|
return {
|
|
312
294
|
resolve: {
|
|
313
295
|
alias: {
|
|
@@ -325,8 +307,18 @@ export default function contentPlugin(contentDir: string, config?: VeslxConfig,
|
|
|
325
307
|
}
|
|
326
308
|
},
|
|
327
309
|
|
|
328
|
-
//
|
|
329
|
-
resolveId(id) {
|
|
310
|
+
// Intercept CSS and virtual module imports
|
|
311
|
+
resolveId(id, importer) {
|
|
312
|
+
// Intercept index.css imported from main.tsx and redirect to our virtual module
|
|
313
|
+
// This allows us to inject @source directive for Tailwind to scan user content
|
|
314
|
+
if (id === './index.css' && importer?.endsWith('/src/main.tsx')) {
|
|
315
|
+
return VIRTUAL_CSS_MODULE
|
|
316
|
+
}
|
|
317
|
+
// Also catch the resolved path
|
|
318
|
+
if (id.endsWith('/src/index.css') && !id.startsWith('\0')) {
|
|
319
|
+
return VIRTUAL_CSS_MODULE
|
|
320
|
+
}
|
|
321
|
+
// Virtual modules for content
|
|
330
322
|
if (id === VIRTUAL_MODULE_ID) {
|
|
331
323
|
return RESOLVED_VIRTUAL_MODULE_ID
|
|
332
324
|
}
|
|
@@ -336,6 +328,28 @@ export default function contentPlugin(contentDir: string, config?: VeslxConfig,
|
|
|
336
328
|
},
|
|
337
329
|
|
|
338
330
|
load(id) {
|
|
331
|
+
// Serve the modified CSS content with @source directive
|
|
332
|
+
// This enables Tailwind v4 to scan the user's content directory for classes
|
|
333
|
+
if (id === VIRTUAL_CSS_MODULE) {
|
|
334
|
+
// Read the original CSS
|
|
335
|
+
const veslxRoot = path.dirname(path.dirname(__dirname))
|
|
336
|
+
const cssPath = path.join(veslxRoot, 'src/index.css')
|
|
337
|
+
const cssContent = fs.readFileSync(cssPath, 'utf-8')
|
|
338
|
+
|
|
339
|
+
// Use absolute path for @source directive
|
|
340
|
+
const absoluteContentDir = dir.replace(/\\/g, '/')
|
|
341
|
+
|
|
342
|
+
// Inject @source directive after the tailwindcss import
|
|
343
|
+
const sourceDirective = `@source "${absoluteContentDir}";`
|
|
344
|
+
const modified = cssContent.replace(
|
|
345
|
+
/(@import\s+["']tailwindcss["'];?)/,
|
|
346
|
+
`$1\n${sourceDirective}`
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
return modified
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Virtual module for content
|
|
339
353
|
if (id === RESOLVED_VIRTUAL_MODULE_ID) {
|
|
340
354
|
// Extract frontmatter from MDX files at build time (avoids MDX hook issues)
|
|
341
355
|
const frontmatterData = extractFrontmatters(dir);
|
package/plugin/src/types.ts
CHANGED
|
@@ -3,7 +3,7 @@ export interface SlidesConfig {
|
|
|
3
3
|
}
|
|
4
4
|
|
|
5
5
|
export interface PostsConfig {
|
|
6
|
-
sort?: 'date' | 'alpha';
|
|
6
|
+
sort?: 'date' | 'date-asc' | 'alpha';
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export interface SiteConfig {
|
|
@@ -26,7 +26,7 @@ export interface ResolvedSlidesConfig {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
export interface ResolvedPostsConfig {
|
|
29
|
-
sort: 'date' | 'alpha';
|
|
29
|
+
sort: 'date' | 'date-asc' | 'alpha';
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
export interface ResolvedSiteConfig {
|
|
@@ -82,15 +82,17 @@ export function PostList() {
|
|
|
82
82
|
|
|
83
83
|
// Sort based on config
|
|
84
84
|
const sortMode = veslxConfig.posts?.sort ?? 'alpha';
|
|
85
|
-
if (sortMode === 'date') {
|
|
86
|
-
|
|
85
|
+
if (sortMode === 'date' || sortMode === 'date-asc') {
|
|
86
|
+
const ascending = sortMode === 'date-asc';
|
|
87
|
+
// Sort by date, posts without dates go to the top
|
|
87
88
|
posts = posts.sort((a, b) => {
|
|
88
89
|
const dateA = getFrontmatter(a)?.date;
|
|
89
90
|
const dateB = getFrontmatter(b)?.date;
|
|
90
91
|
if (!dateA && !dateB) return a.name.localeCompare(b.name);
|
|
91
|
-
if (!dateA) return 1;
|
|
92
|
-
if (!dateB) return
|
|
93
|
-
|
|
92
|
+
if (!dateA) return -1;
|
|
93
|
+
if (!dateB) return 1;
|
|
94
|
+
const diff = new Date(dateA as string).getTime() - new Date(dateB as string).getTime();
|
|
95
|
+
return ascending ? diff : -diff;
|
|
94
96
|
});
|
|
95
97
|
} else {
|
|
96
98
|
// Alphanumeric sorting by name
|