@md-plugins/vite-ssg-plugin 0.1.0-rc.2 → 0.1.0-rc.3

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/README.md CHANGED
@@ -6,12 +6,16 @@ This package currently focuses on route inventory and static route output:
6
6
 
7
7
  - Normalize static route declarations.
8
8
  - Discover Q-Press Markdown routes from a `src/markdown` folder.
9
+ - Flatten static Vue Router route records for non-Markdown pages.
10
+ - Exclude routes from manifests or prerender passes.
9
11
  - Generate a route manifest during Vite builds.
10
12
  - Expose the same manifest through a virtual module.
11
13
  - Emit route-specific HTML files from the built app shell so static hosts can serve deep
12
14
  links without relying on a SPA fallback rewrite.
13
15
  - Accept a custom per-route renderer when a project is ready to generate fully prerendered
14
16
  route HTML.
17
+ - Crawl safe internal links, follow redirects, skip 404s, and write generation reports during
18
+ post-build prerendering when those behaviors are enabled.
15
19
 
16
20
  By default, generated route HTML uses the built `index.html` app shell. That makes the output
17
21
  usable on Netlify or other static hosts today. Q-Press projects can use `qpress-ssg` for
@@ -43,6 +47,25 @@ export default {
43
47
  }
44
48
  ```
45
49
 
50
+ To include static Vue Router routes:
51
+
52
+ ```ts
53
+ import { flattenStaticSsgRouterRoutes, viteSsgPlugin } from '@md-plugins/vite-ssg-plugin'
54
+ import routes from './src/router/routes'
55
+
56
+ export default {
57
+ plugins: [
58
+ viteSsgPlugin({
59
+ markdown: {
60
+ root: './src/markdown',
61
+ },
62
+ routes: flattenStaticSsgRouterRoutes(routes),
63
+ exclude: ['/drafts/private', /^\/admin/],
64
+ }),
65
+ ],
66
+ }
67
+ ```
68
+
46
69
  For Q-Press-style Markdown docs:
47
70
 
48
71
  ```ts
@@ -121,6 +144,10 @@ import { prerenderSsgRoutes } from '@md-plugins/vite-ssg-plugin'
121
144
 
122
145
  await prerenderSsgRoutes({
123
146
  outDir: 'dist/spa',
147
+ concurrency: 4,
148
+ crawlLinks: true,
149
+ redirects: 'follow',
150
+ notFound: 'skip',
124
151
  async renderRoute(route, { appHtml }) {
125
152
  const renderedAppHtml = await renderMyAppAt(route.path)
126
153
 
@@ -133,12 +160,20 @@ The helper reads `q-press-ssg-routes.json`, renders every route, and writes each
133
160
  `index.html` file. The renderer can be a Vue SSR renderer, a Quasar SSR adapter, or any
134
161
  project-specific static renderer.
135
162
 
163
+ The output directory is configurable. Q-Press defaults to `dist/spa` because that keeps existing
164
+ static-host deployments simple, but non-Q-Press projects can use another output folder.
165
+
166
+ By default, post-build prerendering writes `q-press-ssg-report.json`. Pass `reportFile: false` to
167
+ disable reports, or provide hooks such as `onRouteRendered`, `onPageGenerated`, and `afterGenerate`
168
+ for custom output.
169
+
136
170
  ## Vue / Quasar Build-Time Rendering
137
171
 
138
172
  For Q-Press apps, run the generated Q-Press command after a normal SPA build:
139
173
 
140
174
  ```bash
141
175
  pnpm build:ssg
176
+ pnpm preview:ssg
142
177
  ```
143
178
 
144
179
  Projects that already have a Quasar SSR bundle can opt into that renderer with
@@ -181,5 +216,4 @@ import ssgRouteManifest, { ssgRoutes } from 'virtual:md-plugins/ssg-routes'
181
216
  ## Next Steps
182
217
 
183
218
  - Add dynamic-route parameter expansion.
184
- - Define lazy client hydration behavior for examples and browser-only components.
185
- - Define how browser-only examples opt out of prerendering.
219
+ - Define project conventions for browser-only examples and lazy client hydration.
package/dist/index.d.mts CHANGED
@@ -27,6 +27,7 @@ interface SsgRouteManifest {
27
27
  routes: SsgRoute[];
28
28
  }
29
29
  type SsgRouteSource = SsgRouteInput[] | (() => MaybePromise<SsgRouteInput[]>);
30
+ type SsgRouteExclusion = string | RegExp;
30
31
  interface MarkdownSsgRoutesOptions {
31
32
  /**
32
33
  * Directory containing Markdown pages.
@@ -83,17 +84,95 @@ interface PrerenderSsgRoutesOptions extends SsgRouteHtmlOptions {
83
84
  * Manifest to use instead of reading one from disk.
84
85
  */
85
86
  manifest?: SsgRouteManifest;
87
+ /**
88
+ * Routes to skip while prerendering. String values are matched after route
89
+ * normalization; RegExp values are tested against normalized route paths.
90
+ */
91
+ exclude?: SsgRouteExclusion[];
92
+ /**
93
+ * Number of routes to prerender at the same time. Defaults to 1.
94
+ */
95
+ concurrency?: number;
96
+ /**
97
+ * Milliseconds to wait between prerender batches. Defaults to 0.
98
+ */
99
+ interval?: number;
100
+ /**
101
+ * Crawl rendered HTML for safe internal links and enqueue missing routes.
102
+ * Defaults to false.
103
+ */
104
+ crawlLinks?: boolean;
105
+ /**
106
+ * Redirect behavior for renderer errors with a string `url` property.
107
+ * Defaults to error to preserve strict generic behavior.
108
+ */
109
+ redirects?: 'error' | 'follow' | 'skip';
110
+ /**
111
+ * Not-found behavior for renderer errors with code/status/statusCode 404.
112
+ * Defaults to error to preserve strict generic behavior.
113
+ */
114
+ notFound?: 'error' | 'skip';
115
+ /**
116
+ * Optional JSON report file written inside outDir. Defaults to
117
+ * q-press-ssg-report.json. Pass false to disable report output.
118
+ */
119
+ reportFile?: string | false;
120
+ /**
121
+ * Hook after a route is rendered and transformed, before crawling and writing.
122
+ */
123
+ onRouteRendered?: SsgRouteRenderedHook;
124
+ /**
125
+ * Hook before a generated page is written. Can adjust html and output path.
126
+ */
127
+ onPageGenerated?: SsgPageGeneratedHook;
128
+ /**
129
+ * Hook after all routes have completed and the report has been assembled.
130
+ */
131
+ afterGenerate?: SsgAfterGenerateHook;
86
132
  }
87
133
  interface PrerenderedSsgRoute {
88
134
  path: string;
89
135
  htmlFile: string;
90
136
  bytes: number;
137
+ milliseconds?: number;
138
+ }
139
+ interface SkippedSsgRoute {
140
+ path: string;
141
+ reason: 'excluded' | 'not-found' | 'redirected' | 'skipped-redirect';
142
+ target?: string;
143
+ }
144
+ interface SsgGenerationWarning {
145
+ path: string;
146
+ message: string;
147
+ }
148
+ interface SsgGenerationReport {
149
+ generatedAt: string;
150
+ outDir: string;
151
+ manifestFile: string;
152
+ routeCount: number;
153
+ generated: PrerenderedSsgRoute[];
154
+ skipped: SkippedSsgRoute[];
155
+ warnings: SsgGenerationWarning[];
91
156
  }
92
157
  interface PrerenderSsgRoutesResult {
93
158
  manifest: SsgRouteManifest;
94
159
  outDir: string;
95
160
  routes: PrerenderedSsgRoute[];
161
+ skipped: SkippedSsgRoute[];
162
+ warnings: SsgGenerationWarning[];
163
+ report?: SsgGenerationReport;
96
164
  }
165
+ interface SsgGeneratedPage {
166
+ route: SsgRoute;
167
+ html: string;
168
+ htmlFile: string;
169
+ filePath: string;
170
+ }
171
+ type SsgRouteRenderedHook = (html: string, route: SsgRoute, context: SsgRouteRenderContext) => MaybePromise<string | void>;
172
+ type SsgPageGeneratedHook = (page: SsgGeneratedPage, context: SsgRouteRenderContext) => MaybePromise<Partial<Pick<SsgGeneratedPage, 'html' | 'htmlFile' | 'filePath'>> | void>;
173
+ type SsgAfterGenerateHook = (result: Omit<PrerenderSsgRoutesResult, 'report'> & {
174
+ report: SsgGenerationReport;
175
+ }) => MaybePromise<void>;
97
176
  interface VueSsgRouterAdapter {
98
177
  push?: (location: unknown) => MaybePromise<unknown>;
99
178
  replace?: (location: unknown) => MaybePromise<unknown>;
@@ -152,6 +231,10 @@ interface ViteSsgPluginOptions {
152
231
  * Static route declarations or a function that resolves them.
153
232
  */
154
233
  routes?: SsgRouteSource;
234
+ /**
235
+ * Routes to exclude from the generated manifest.
236
+ */
237
+ exclude?: SsgRouteExclusion[];
155
238
  /**
156
239
  * Optional Markdown route discovery. This can be combined with explicit routes.
157
240
  */
@@ -185,28 +268,94 @@ interface ViteSsgPluginOptions {
185
268
  virtualModuleId?: string;
186
269
  }
187
270
 
271
+ /**
272
+ * Escapes JSON so it can be embedded safely inside an HTML `<script>` tag.
273
+ */
188
274
  declare function escapeJsonForHtml(json: string): string;
275
+ /**
276
+ * Serializes an SSG route into the route payload script consumed during hydration.
277
+ */
189
278
  declare function createSsgRoutePayloadScript(route: SsgRoute): string;
279
+ /**
280
+ * Injects the route payload script into an HTML shell without duplicating it.
281
+ */
190
282
  declare function injectSsgRoutePayload(html: string, route: SsgRoute): string;
283
+ /**
284
+ * Creates the default per-route HTML shell for a static SSG route.
285
+ */
191
286
  declare function createSsgRouteHtml(route: SsgRoute, context: SsgRouteRenderContext, { injectRoutePayload }?: {
192
287
  injectRoutePayload?: boolean;
193
288
  }): string;
289
+ /**
290
+ * Renders one SSG route, optionally delegating to a framework renderer and HTML transform.
291
+ */
194
292
  declare function renderSsgRouteHtml(route: SsgRoute, context: SsgRouteRenderContext, options?: SsgRouteHtmlOptions): Promise<string>;
195
293
 
294
+ /**
295
+ * Converts a Markdown file path into the route path generated by Q-Press conventions.
296
+ */
196
297
  declare function markdownFileToRoutePath(markdownFile: string, { landingPage }?: Pick<MarkdownSsgRoutesOptions, 'landingPage'>): string;
298
+ /**
299
+ * Discovers Markdown files and converts them into SSG route inputs.
300
+ */
197
301
  declare function discoverMarkdownSsgRoutes({ root, include, exclude, landingPage, }: MarkdownSsgRoutesOptions): SsgRouteInput[];
198
302
 
199
- declare function prerenderSsgRoutes({ outDir, appHtmlFile, manifestFile, manifest, renderRoute, transformHtml, injectRoutePayload, }: PrerenderSsgRoutesOptions): Promise<PrerenderSsgRoutesResult>;
303
+ /**
304
+ * Prerenders every route in an SSG manifest into static HTML files.
305
+ *
306
+ * The renderer can use the default HTML-shell mode, a custom route renderer, or
307
+ * a framework-specific renderer such as the Vue adapter.
308
+ */
309
+ declare function prerenderSsgRoutes({ outDir, appHtmlFile, manifestFile, manifest, exclude, concurrency, interval, crawlLinks, redirects, notFound, reportFile, onRouteRendered, onPageGenerated, afterGenerate, renderRoute, transformHtml, injectRoutePayload, }: PrerenderSsgRoutesOptions): Promise<PrerenderSsgRoutesResult>;
200
310
 
201
311
  declare const defaultSsgManifestFile = "q-press-ssg-routes.json";
312
+ declare const defaultSsgReportFile = "q-press-ssg-report.json";
202
313
  declare const defaultSsgVirtualModuleId = "virtual:md-plugins/ssg-routes";
314
+ interface SsgRouterRouteLike {
315
+ path?: string;
316
+ children?: SsgRouterRouteLike[];
317
+ }
318
+ /**
319
+ * Normalizes a Vite base value for use in generated SSG manifests and links.
320
+ */
203
321
  declare function normalizeSsgBase(base?: string): string;
322
+ /**
323
+ * Normalizes a route path into an absolute path without query, hash, or trailing slash.
324
+ */
204
325
  declare function normalizeSsgRoutePath(path: string): string;
326
+ /**
327
+ * Checks whether a route path can be emitted as a static HTML file.
328
+ */
329
+ declare function isStaticSsgRoutePath(path: string): boolean;
330
+ /**
331
+ * Converts a route path to the HTML file emitted for that route.
332
+ */
205
333
  declare function routePathToHtmlFile(routePath: string): string;
334
+ /**
335
+ * Converts a route path into a stable id for manifest entries and diagnostics.
336
+ */
206
337
  declare function routePathToId(routePath: string): string;
338
+ /**
339
+ * Returns whether a normalized route path matches one of the configured exclusions.
340
+ */
341
+ declare function isSsgRouteExcluded(path: string, exclude?: SsgRouteExclusion[]): boolean;
342
+ /**
343
+ * Flattens Vue Router-style route records into static SSG route paths.
344
+ */
345
+ declare function flattenStaticSsgRouterRoutes(routes: SsgRouterRouteLike[], { base, exclude }?: {
346
+ base?: string;
347
+ exclude?: SsgRouteExclusion[];
348
+ }): string[];
349
+ /**
350
+ * Converts a string or object route input into a complete manifest route entry.
351
+ */
207
352
  declare function normalizeSsgRoute(input: SsgRouteInput): SsgRoute;
208
- declare function createSsgRouteManifest(routeInputs: SsgRouteInput[], { base }?: {
353
+ /**
354
+ * Builds and validates an SSG route manifest from route inputs.
355
+ */
356
+ declare function createSsgRouteManifest(routeInputs: SsgRouteInput[], { base, exclude }?: {
209
357
  base?: string;
358
+ exclude?: SsgRouteExclusion[];
210
359
  }): SsgRouteManifest;
211
360
 
212
361
  /**
@@ -214,11 +363,14 @@ declare function createSsgRouteManifest(routeInputs: SsgRouteInput[], { base }?:
214
363
  */
215
364
  declare function createVueSsgRouteRenderer(options: VueSsgRouteRendererOptions): SsgRouteRenderer;
216
365
  /**
217
- * Convenience wrapper for projects that prerender after a normal Vite/Quasar build.
366
+ * Prerenders Vite/Quasar routes with a Vue app factory in one call.
218
367
  */
219
368
  declare function prerenderVueSsgRoutes(options: PrerenderVueSsgRoutesOptions): Promise<PrerenderSsgRoutesResult>;
220
369
 
370
+ /**
371
+ * Creates the Vite plugin that emits SSG route manifests and optional route HTML shells.
372
+ */
221
373
  declare function viteSsgPlugin(options?: ViteSsgPluginOptions): Plugin;
222
374
 
223
- export { createSsgRouteHtml, createSsgRouteManifest, createSsgRoutePayloadScript, createVueSsgRouteRenderer, defaultSsgManifestFile, defaultSsgVirtualModuleId, discoverMarkdownSsgRoutes, escapeJsonForHtml, injectSsgRoutePayload, markdownFileToRoutePath, normalizeSsgBase, normalizeSsgRoute, normalizeSsgRoutePath, prerenderSsgRoutes, prerenderVueSsgRoutes, renderSsgRouteHtml, routePathToHtmlFile, routePathToId, viteSsgPlugin };
224
- export type { JsonPrimitive, JsonValue, MarkdownSsgRoutesOptions, MaybePromise, PrerenderSsgRoutesOptions, PrerenderSsgRoutesResult, PrerenderVueSsgRoutesOptions, PrerenderedSsgRoute, SsgRoute, SsgRouteHtmlOptions, SsgRouteHtmlTransformer, SsgRouteInput, SsgRouteManifest, SsgRouteMeta, SsgRouteObject, SsgRouteParams, SsgRouteRenderContext, SsgRouteRenderer, SsgRouteSource, ViteSsgPluginOptions, VueSsgAppFactory, VueSsgAppFactoryResult, VueSsgAppHtmlReplacer, VueSsgRenderToString, VueSsgRenderedAppHtmlTransformer, VueSsgRouteLocationResolver, VueSsgRouteRendererOptions, VueSsgRouterAdapter };
375
+ export { createSsgRouteHtml, createSsgRouteManifest, createSsgRoutePayloadScript, createVueSsgRouteRenderer, defaultSsgManifestFile, defaultSsgReportFile, defaultSsgVirtualModuleId, discoverMarkdownSsgRoutes, escapeJsonForHtml, flattenStaticSsgRouterRoutes, injectSsgRoutePayload, isSsgRouteExcluded, isStaticSsgRoutePath, markdownFileToRoutePath, normalizeSsgBase, normalizeSsgRoute, normalizeSsgRoutePath, prerenderSsgRoutes, prerenderVueSsgRoutes, renderSsgRouteHtml, routePathToHtmlFile, routePathToId, viteSsgPlugin };
376
+ export type { JsonPrimitive, JsonValue, MarkdownSsgRoutesOptions, MaybePromise, PrerenderSsgRoutesOptions, PrerenderSsgRoutesResult, PrerenderVueSsgRoutesOptions, PrerenderedSsgRoute, SkippedSsgRoute, SsgAfterGenerateHook, SsgGeneratedPage, SsgGenerationReport, SsgGenerationWarning, SsgPageGeneratedHook, SsgRoute, SsgRouteExclusion, SsgRouteHtmlOptions, SsgRouteHtmlTransformer, SsgRouteInput, SsgRouteManifest, SsgRouteMeta, SsgRouteObject, SsgRouteParams, SsgRouteRenderContext, SsgRouteRenderedHook, SsgRouteRenderer, SsgRouteSource, SsgRouterRouteLike, ViteSsgPluginOptions, VueSsgAppFactory, VueSsgAppFactoryResult, VueSsgAppHtmlReplacer, VueSsgRenderToString, VueSsgRenderedAppHtmlTransformer, VueSsgRouteLocationResolver, VueSsgRouteRendererOptions, VueSsgRouterAdapter };
package/dist/index.d.ts CHANGED
@@ -27,6 +27,7 @@ interface SsgRouteManifest {
27
27
  routes: SsgRoute[];
28
28
  }
29
29
  type SsgRouteSource = SsgRouteInput[] | (() => MaybePromise<SsgRouteInput[]>);
30
+ type SsgRouteExclusion = string | RegExp;
30
31
  interface MarkdownSsgRoutesOptions {
31
32
  /**
32
33
  * Directory containing Markdown pages.
@@ -83,17 +84,95 @@ interface PrerenderSsgRoutesOptions extends SsgRouteHtmlOptions {
83
84
  * Manifest to use instead of reading one from disk.
84
85
  */
85
86
  manifest?: SsgRouteManifest;
87
+ /**
88
+ * Routes to skip while prerendering. String values are matched after route
89
+ * normalization; RegExp values are tested against normalized route paths.
90
+ */
91
+ exclude?: SsgRouteExclusion[];
92
+ /**
93
+ * Number of routes to prerender at the same time. Defaults to 1.
94
+ */
95
+ concurrency?: number;
96
+ /**
97
+ * Milliseconds to wait between prerender batches. Defaults to 0.
98
+ */
99
+ interval?: number;
100
+ /**
101
+ * Crawl rendered HTML for safe internal links and enqueue missing routes.
102
+ * Defaults to false.
103
+ */
104
+ crawlLinks?: boolean;
105
+ /**
106
+ * Redirect behavior for renderer errors with a string `url` property.
107
+ * Defaults to error to preserve strict generic behavior.
108
+ */
109
+ redirects?: 'error' | 'follow' | 'skip';
110
+ /**
111
+ * Not-found behavior for renderer errors with code/status/statusCode 404.
112
+ * Defaults to error to preserve strict generic behavior.
113
+ */
114
+ notFound?: 'error' | 'skip';
115
+ /**
116
+ * Optional JSON report file written inside outDir. Defaults to
117
+ * q-press-ssg-report.json. Pass false to disable report output.
118
+ */
119
+ reportFile?: string | false;
120
+ /**
121
+ * Hook after a route is rendered and transformed, before crawling and writing.
122
+ */
123
+ onRouteRendered?: SsgRouteRenderedHook;
124
+ /**
125
+ * Hook before a generated page is written. Can adjust html and output path.
126
+ */
127
+ onPageGenerated?: SsgPageGeneratedHook;
128
+ /**
129
+ * Hook after all routes have completed and the report has been assembled.
130
+ */
131
+ afterGenerate?: SsgAfterGenerateHook;
86
132
  }
87
133
  interface PrerenderedSsgRoute {
88
134
  path: string;
89
135
  htmlFile: string;
90
136
  bytes: number;
137
+ milliseconds?: number;
138
+ }
139
+ interface SkippedSsgRoute {
140
+ path: string;
141
+ reason: 'excluded' | 'not-found' | 'redirected' | 'skipped-redirect';
142
+ target?: string;
143
+ }
144
+ interface SsgGenerationWarning {
145
+ path: string;
146
+ message: string;
147
+ }
148
+ interface SsgGenerationReport {
149
+ generatedAt: string;
150
+ outDir: string;
151
+ manifestFile: string;
152
+ routeCount: number;
153
+ generated: PrerenderedSsgRoute[];
154
+ skipped: SkippedSsgRoute[];
155
+ warnings: SsgGenerationWarning[];
91
156
  }
92
157
  interface PrerenderSsgRoutesResult {
93
158
  manifest: SsgRouteManifest;
94
159
  outDir: string;
95
160
  routes: PrerenderedSsgRoute[];
161
+ skipped: SkippedSsgRoute[];
162
+ warnings: SsgGenerationWarning[];
163
+ report?: SsgGenerationReport;
96
164
  }
165
+ interface SsgGeneratedPage {
166
+ route: SsgRoute;
167
+ html: string;
168
+ htmlFile: string;
169
+ filePath: string;
170
+ }
171
+ type SsgRouteRenderedHook = (html: string, route: SsgRoute, context: SsgRouteRenderContext) => MaybePromise<string | void>;
172
+ type SsgPageGeneratedHook = (page: SsgGeneratedPage, context: SsgRouteRenderContext) => MaybePromise<Partial<Pick<SsgGeneratedPage, 'html' | 'htmlFile' | 'filePath'>> | void>;
173
+ type SsgAfterGenerateHook = (result: Omit<PrerenderSsgRoutesResult, 'report'> & {
174
+ report: SsgGenerationReport;
175
+ }) => MaybePromise<void>;
97
176
  interface VueSsgRouterAdapter {
98
177
  push?: (location: unknown) => MaybePromise<unknown>;
99
178
  replace?: (location: unknown) => MaybePromise<unknown>;
@@ -152,6 +231,10 @@ interface ViteSsgPluginOptions {
152
231
  * Static route declarations or a function that resolves them.
153
232
  */
154
233
  routes?: SsgRouteSource;
234
+ /**
235
+ * Routes to exclude from the generated manifest.
236
+ */
237
+ exclude?: SsgRouteExclusion[];
155
238
  /**
156
239
  * Optional Markdown route discovery. This can be combined with explicit routes.
157
240
  */
@@ -185,28 +268,94 @@ interface ViteSsgPluginOptions {
185
268
  virtualModuleId?: string;
186
269
  }
187
270
 
271
+ /**
272
+ * Escapes JSON so it can be embedded safely inside an HTML `<script>` tag.
273
+ */
188
274
  declare function escapeJsonForHtml(json: string): string;
275
+ /**
276
+ * Serializes an SSG route into the route payload script consumed during hydration.
277
+ */
189
278
  declare function createSsgRoutePayloadScript(route: SsgRoute): string;
279
+ /**
280
+ * Injects the route payload script into an HTML shell without duplicating it.
281
+ */
190
282
  declare function injectSsgRoutePayload(html: string, route: SsgRoute): string;
283
+ /**
284
+ * Creates the default per-route HTML shell for a static SSG route.
285
+ */
191
286
  declare function createSsgRouteHtml(route: SsgRoute, context: SsgRouteRenderContext, { injectRoutePayload }?: {
192
287
  injectRoutePayload?: boolean;
193
288
  }): string;
289
+ /**
290
+ * Renders one SSG route, optionally delegating to a framework renderer and HTML transform.
291
+ */
194
292
  declare function renderSsgRouteHtml(route: SsgRoute, context: SsgRouteRenderContext, options?: SsgRouteHtmlOptions): Promise<string>;
195
293
 
294
+ /**
295
+ * Converts a Markdown file path into the route path generated by Q-Press conventions.
296
+ */
196
297
  declare function markdownFileToRoutePath(markdownFile: string, { landingPage }?: Pick<MarkdownSsgRoutesOptions, 'landingPage'>): string;
298
+ /**
299
+ * Discovers Markdown files and converts them into SSG route inputs.
300
+ */
197
301
  declare function discoverMarkdownSsgRoutes({ root, include, exclude, landingPage, }: MarkdownSsgRoutesOptions): SsgRouteInput[];
198
302
 
199
- declare function prerenderSsgRoutes({ outDir, appHtmlFile, manifestFile, manifest, renderRoute, transformHtml, injectRoutePayload, }: PrerenderSsgRoutesOptions): Promise<PrerenderSsgRoutesResult>;
303
+ /**
304
+ * Prerenders every route in an SSG manifest into static HTML files.
305
+ *
306
+ * The renderer can use the default HTML-shell mode, a custom route renderer, or
307
+ * a framework-specific renderer such as the Vue adapter.
308
+ */
309
+ declare function prerenderSsgRoutes({ outDir, appHtmlFile, manifestFile, manifest, exclude, concurrency, interval, crawlLinks, redirects, notFound, reportFile, onRouteRendered, onPageGenerated, afterGenerate, renderRoute, transformHtml, injectRoutePayload, }: PrerenderSsgRoutesOptions): Promise<PrerenderSsgRoutesResult>;
200
310
 
201
311
  declare const defaultSsgManifestFile = "q-press-ssg-routes.json";
312
+ declare const defaultSsgReportFile = "q-press-ssg-report.json";
202
313
  declare const defaultSsgVirtualModuleId = "virtual:md-plugins/ssg-routes";
314
+ interface SsgRouterRouteLike {
315
+ path?: string;
316
+ children?: SsgRouterRouteLike[];
317
+ }
318
+ /**
319
+ * Normalizes a Vite base value for use in generated SSG manifests and links.
320
+ */
203
321
  declare function normalizeSsgBase(base?: string): string;
322
+ /**
323
+ * Normalizes a route path into an absolute path without query, hash, or trailing slash.
324
+ */
204
325
  declare function normalizeSsgRoutePath(path: string): string;
326
+ /**
327
+ * Checks whether a route path can be emitted as a static HTML file.
328
+ */
329
+ declare function isStaticSsgRoutePath(path: string): boolean;
330
+ /**
331
+ * Converts a route path to the HTML file emitted for that route.
332
+ */
205
333
  declare function routePathToHtmlFile(routePath: string): string;
334
+ /**
335
+ * Converts a route path into a stable id for manifest entries and diagnostics.
336
+ */
206
337
  declare function routePathToId(routePath: string): string;
338
+ /**
339
+ * Returns whether a normalized route path matches one of the configured exclusions.
340
+ */
341
+ declare function isSsgRouteExcluded(path: string, exclude?: SsgRouteExclusion[]): boolean;
342
+ /**
343
+ * Flattens Vue Router-style route records into static SSG route paths.
344
+ */
345
+ declare function flattenStaticSsgRouterRoutes(routes: SsgRouterRouteLike[], { base, exclude }?: {
346
+ base?: string;
347
+ exclude?: SsgRouteExclusion[];
348
+ }): string[];
349
+ /**
350
+ * Converts a string or object route input into a complete manifest route entry.
351
+ */
207
352
  declare function normalizeSsgRoute(input: SsgRouteInput): SsgRoute;
208
- declare function createSsgRouteManifest(routeInputs: SsgRouteInput[], { base }?: {
353
+ /**
354
+ * Builds and validates an SSG route manifest from route inputs.
355
+ */
356
+ declare function createSsgRouteManifest(routeInputs: SsgRouteInput[], { base, exclude }?: {
209
357
  base?: string;
358
+ exclude?: SsgRouteExclusion[];
210
359
  }): SsgRouteManifest;
211
360
 
212
361
  /**
@@ -214,11 +363,14 @@ declare function createSsgRouteManifest(routeInputs: SsgRouteInput[], { base }?:
214
363
  */
215
364
  declare function createVueSsgRouteRenderer(options: VueSsgRouteRendererOptions): SsgRouteRenderer;
216
365
  /**
217
- * Convenience wrapper for projects that prerender after a normal Vite/Quasar build.
366
+ * Prerenders Vite/Quasar routes with a Vue app factory in one call.
218
367
  */
219
368
  declare function prerenderVueSsgRoutes(options: PrerenderVueSsgRoutesOptions): Promise<PrerenderSsgRoutesResult>;
220
369
 
370
+ /**
371
+ * Creates the Vite plugin that emits SSG route manifests and optional route HTML shells.
372
+ */
221
373
  declare function viteSsgPlugin(options?: ViteSsgPluginOptions): Plugin;
222
374
 
223
- export { createSsgRouteHtml, createSsgRouteManifest, createSsgRoutePayloadScript, createVueSsgRouteRenderer, defaultSsgManifestFile, defaultSsgVirtualModuleId, discoverMarkdownSsgRoutes, escapeJsonForHtml, injectSsgRoutePayload, markdownFileToRoutePath, normalizeSsgBase, normalizeSsgRoute, normalizeSsgRoutePath, prerenderSsgRoutes, prerenderVueSsgRoutes, renderSsgRouteHtml, routePathToHtmlFile, routePathToId, viteSsgPlugin };
224
- export type { JsonPrimitive, JsonValue, MarkdownSsgRoutesOptions, MaybePromise, PrerenderSsgRoutesOptions, PrerenderSsgRoutesResult, PrerenderVueSsgRoutesOptions, PrerenderedSsgRoute, SsgRoute, SsgRouteHtmlOptions, SsgRouteHtmlTransformer, SsgRouteInput, SsgRouteManifest, SsgRouteMeta, SsgRouteObject, SsgRouteParams, SsgRouteRenderContext, SsgRouteRenderer, SsgRouteSource, ViteSsgPluginOptions, VueSsgAppFactory, VueSsgAppFactoryResult, VueSsgAppHtmlReplacer, VueSsgRenderToString, VueSsgRenderedAppHtmlTransformer, VueSsgRouteLocationResolver, VueSsgRouteRendererOptions, VueSsgRouterAdapter };
375
+ export { createSsgRouteHtml, createSsgRouteManifest, createSsgRoutePayloadScript, createVueSsgRouteRenderer, defaultSsgManifestFile, defaultSsgReportFile, defaultSsgVirtualModuleId, discoverMarkdownSsgRoutes, escapeJsonForHtml, flattenStaticSsgRouterRoutes, injectSsgRoutePayload, isSsgRouteExcluded, isStaticSsgRoutePath, markdownFileToRoutePath, normalizeSsgBase, normalizeSsgRoute, normalizeSsgRoutePath, prerenderSsgRoutes, prerenderVueSsgRoutes, renderSsgRouteHtml, routePathToHtmlFile, routePathToId, viteSsgPlugin };
376
+ export type { JsonPrimitive, JsonValue, MarkdownSsgRoutesOptions, MaybePromise, PrerenderSsgRoutesOptions, PrerenderSsgRoutesResult, PrerenderVueSsgRoutesOptions, PrerenderedSsgRoute, SkippedSsgRoute, SsgAfterGenerateHook, SsgGeneratedPage, SsgGenerationReport, SsgGenerationWarning, SsgPageGeneratedHook, SsgRoute, SsgRouteExclusion, SsgRouteHtmlOptions, SsgRouteHtmlTransformer, SsgRouteInput, SsgRouteManifest, SsgRouteMeta, SsgRouteObject, SsgRouteParams, SsgRouteRenderContext, SsgRouteRenderedHook, SsgRouteRenderer, SsgRouteSource, SsgRouterRouteLike, ViteSsgPluginOptions, VueSsgAppFactory, VueSsgAppFactoryResult, VueSsgAppHtmlReplacer, VueSsgRenderToString, VueSsgRenderedAppHtmlTransformer, VueSsgRouteLocationResolver, VueSsgRouteRendererOptions, VueSsgRouterAdapter };
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { globSync } from 'tinyglobby';
2
2
  import { Buffer } from 'node:buffer';
3
- import { readFile, mkdir, writeFile, readdir } from 'node:fs/promises';
4
- import { resolve, join, dirname } from 'node:path';
3
+ import { readFile, writeFile, mkdir, readdir } from 'node:fs/promises';
4
+ import { resolve, join, dirname, posix } from 'node:path';
5
5
 
6
6
  function escapeJsonForHtml(json) {
7
7
  return json.replace(/</g, "\\u003C").replace(/>/g, "\\u003E").replace(/&/g, "\\u0026").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
@@ -77,6 +77,7 @@ function discoverMarkdownSsgRoutes({
77
77
  }
78
78
 
79
79
  const defaultSsgManifestFile = "q-press-ssg-routes.json";
80
+ const defaultSsgReportFile = "q-press-ssg-report.json";
80
81
  const defaultSsgVirtualModuleId = "virtual:md-plugins/ssg-routes";
81
82
  function normalizeSsgBase(base = "/") {
82
83
  const trimmed = base.trim();
@@ -107,6 +108,10 @@ function normalizeSsgRoutePath(path) {
107
108
  }
108
109
  return compacted.replace(/\/+$/, "");
109
110
  }
111
+ function isStaticSsgRoutePath(path) {
112
+ const normalized = normalizeSsgRoutePath(path);
113
+ return normalized.startsWith("/") && !normalized.startsWith("//") && !normalized.includes(":") && !normalized.includes("*") && !/\.[a-z0-9]+$/i.test(normalized);
114
+ }
110
115
  function routePathToHtmlFile(routePath) {
111
116
  const normalized = normalizeSsgRoutePath(routePath);
112
117
  if (normalized === "/") {
@@ -121,6 +126,37 @@ function routePathToId(routePath) {
121
126
  }
122
127
  return normalized.slice(1).replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-+|-+$/g, "");
123
128
  }
129
+ function isSsgRouteExcluded(path, exclude = []) {
130
+ const normalized = normalizeSsgRoutePath(path);
131
+ return exclude.some(
132
+ (pattern) => typeof pattern === "string" ? normalizeSsgRoutePath(pattern) === normalized : pattern.test(normalized)
133
+ );
134
+ }
135
+ function joinRoutePaths(parentPath, childPath) {
136
+ if (!childPath || childPath === "/") {
137
+ return parentPath || "/";
138
+ }
139
+ if (childPath.startsWith("/")) {
140
+ return childPath;
141
+ }
142
+ return `${parentPath.replace(/\/+$/, "")}/${childPath}` || "/";
143
+ }
144
+ function flattenStaticSsgRouterRoutes(routes, { base = "", exclude = [] } = {}) {
145
+ const routePaths = [];
146
+ function visit(route, parentPath) {
147
+ const path = joinRoutePaths(parentPath, route.path ?? "");
148
+ if (isStaticSsgRoutePath(path) && !isSsgRouteExcluded(path, exclude)) {
149
+ routePaths.push(path);
150
+ }
151
+ for (const child of route.children ?? []) {
152
+ visit(child, path);
153
+ }
154
+ }
155
+ for (const route of routes) {
156
+ visit(route, base);
157
+ }
158
+ return Array.from(new Set(routePaths.map(normalizeSsgRoutePath)));
159
+ }
124
160
  function normalizeSsgRoute(input) {
125
161
  const route = typeof input === "string" ? {
126
162
  path: input,
@@ -139,8 +175,8 @@ function normalizeSsgRoute(input) {
139
175
  ...Object.hasOwn(route, "data") ? { data: route.data } : {}
140
176
  };
141
177
  }
142
- function createSsgRouteManifest(routeInputs, { base = "/" } = {}) {
143
- const routes = routeInputs.map(normalizeSsgRoute);
178
+ function createSsgRouteManifest(routeInputs, { base = "/", exclude = [] } = {}) {
179
+ const routes = routeInputs.map(normalizeSsgRoute).filter((route) => !isSsgRouteExcluded(route.path, exclude));
144
180
  const routePaths = /* @__PURE__ */ new Set();
145
181
  for (const route of routes) {
146
182
  if (routePaths.has(route.path)) {
@@ -192,51 +228,263 @@ async function injectMissingCssAssets(appHtml, outDir, base) {
192
228
  `;
193
229
  return appHtml.includes("</head>") ? appHtml.replace("</head>", `${content}</head>`) : `${content}${appHtml}`;
194
230
  }
231
+ function clampConcurrency(concurrency) {
232
+ return Math.max(1, Math.floor(concurrency ?? 1));
233
+ }
234
+ function clampInterval(interval) {
235
+ return Math.max(0, Math.floor(interval ?? 0));
236
+ }
237
+ function wait(milliseconds) {
238
+ return new Promise((resolvePromise) => {
239
+ setTimeout(resolvePromise, milliseconds);
240
+ });
241
+ }
242
+ function isRecord(value) {
243
+ return typeof value === "object" && value !== null;
244
+ }
245
+ function getRedirectTarget(error) {
246
+ if (!isRecord(error)) {
247
+ return void 0;
248
+ }
249
+ return typeof error.url === "string" ? error.url : void 0;
250
+ }
251
+ function isNotFoundError(error) {
252
+ if (!isRecord(error)) {
253
+ return false;
254
+ }
255
+ return error.code === 404 || error.status === 404 || error.statusCode === 404;
256
+ }
257
+ function extractAnchorHrefs(html) {
258
+ const hrefs = [];
259
+ const anchorHrefRE = /<a\b[^>]*\bhref\s*=\s*(["'])(.*?)\1/gi;
260
+ let match;
261
+ while (match = anchorHrefRE.exec(html)) {
262
+ hrefs.push(match[2]);
263
+ }
264
+ return hrefs;
265
+ }
266
+ function isExternalHref(href) {
267
+ return /^[a-z][a-z0-9+.-]*:/i.test(href) || href.startsWith("//");
268
+ }
269
+ function stripBaseFromPath(path, base) {
270
+ if (base === "/" || base === "./" || base.startsWith("http://") || base.startsWith("https://")) {
271
+ return path;
272
+ }
273
+ const normalizedBase = normalizeSsgRoutePath(base);
274
+ if (path === normalizedBase) {
275
+ return "/";
276
+ }
277
+ return path.startsWith(`${normalizedBase}/`) ? path.slice(normalizedBase.length) : path;
278
+ }
279
+ function resolveHrefPath(hrefPath, fromRoutePath) {
280
+ if (hrefPath.startsWith("/")) {
281
+ return hrefPath;
282
+ }
283
+ const routeBase = fromRoutePath === "/" ? "/" : posix.dirname(fromRoutePath);
284
+ return posix.normalize(posix.join(routeBase, hrefPath));
285
+ }
286
+ function hrefToSsgRoutePath(href, base, fromRoutePath = "/") {
287
+ const trimmed = href.trim();
288
+ if (!trimmed || trimmed.startsWith("#") || isExternalHref(trimmed)) {
289
+ return void 0;
290
+ }
291
+ const withoutHash = trimmed.split("#")[0] ?? "";
292
+ const withoutQuery = withoutHash.split("?")[0] ?? "";
293
+ if (!withoutQuery || withoutQuery.startsWith(".")) {
294
+ return void 0;
295
+ }
296
+ try {
297
+ const routePath = stripBaseFromPath(resolveHrefPath(withoutQuery, fromRoutePath), base);
298
+ return isStaticSsgRoutePath(routePath) ? normalizeSsgRoutePath(routePath) : void 0;
299
+ } catch {
300
+ return void 0;
301
+ }
302
+ }
303
+ function extractSsgRouteLinks(html, base, fromRoutePath) {
304
+ return Array.from(
305
+ new Set(
306
+ extractAnchorHrefs(html).map((href) => hrefToSsgRoutePath(href, base, fromRoutePath)).filter((path) => path !== void 0)
307
+ )
308
+ );
309
+ }
310
+ function createReport({
311
+ generated,
312
+ manifest,
313
+ manifestFile,
314
+ outDir,
315
+ skipped,
316
+ warnings
317
+ }) {
318
+ return {
319
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
320
+ outDir,
321
+ manifestFile,
322
+ routeCount: manifest.routes.length,
323
+ generated,
324
+ skipped,
325
+ warnings
326
+ };
327
+ }
195
328
  async function prerenderSsgRoutes({
196
329
  outDir,
197
330
  appHtmlFile = "index.html",
198
331
  manifestFile = defaultSsgManifestFile,
199
332
  manifest,
333
+ exclude,
334
+ concurrency,
335
+ interval,
336
+ crawlLinks = false,
337
+ redirects = "error",
338
+ notFound = "error",
339
+ reportFile = defaultSsgReportFile,
340
+ onRouteRendered,
341
+ onPageGenerated,
342
+ afterGenerate,
200
343
  renderRoute,
201
344
  transformHtml,
202
345
  injectRoutePayload
203
346
  }) {
204
347
  const resolvedOutDir = resolve(outDir);
205
- const resolvedManifest = manifest ?? await readSsgRouteManifest(resolvedOutDir, manifestFile);
348
+ const rawManifest = manifest ?? await readSsgRouteManifest(resolvedOutDir, manifestFile);
349
+ const resolvedManifest = createSsgRouteManifest(rawManifest.routes, {
350
+ base: rawManifest.base,
351
+ exclude
352
+ });
206
353
  const appHtml = await injectMissingCssAssets(
207
354
  await readFile(join(resolvedOutDir, appHtmlFile), "utf8"),
208
355
  resolvedOutDir,
209
356
  resolvedManifest.base
210
357
  );
211
358
  const routes = [];
212
- for (const [routeIndex, route] of resolvedManifest.routes.entries()) {
213
- const html = await renderSsgRouteHtml(
214
- route,
215
- {
216
- appHtml,
217
- manifest: resolvedManifest,
218
- routeIndex
219
- },
220
- {
359
+ const skipped = rawManifest.routes.filter((route) => isSsgRouteExcluded(route.path, exclude ?? [])).map((route) => ({
360
+ path: route.path,
361
+ reason: "excluded"
362
+ }));
363
+ const warnings = [];
364
+ const queue = [...resolvedManifest.routes];
365
+ const enqueuedPaths = new Set(queue.map((route) => route.path));
366
+ const maxConcurrency = clampConcurrency(concurrency);
367
+ const batchInterval = clampInterval(interval);
368
+ function enqueueRoute(routeInput) {
369
+ const route = normalizeSsgRoute(routeInput);
370
+ if (isSsgRouteExcluded(route.path, exclude ?? []) || enqueuedPaths.has(route.path)) {
371
+ return void 0;
372
+ }
373
+ enqueuedPaths.add(route.path);
374
+ resolvedManifest.routes.push(route);
375
+ queue.push(route);
376
+ return route;
377
+ }
378
+ async function renderOne(route) {
379
+ const start = performance.now();
380
+ const routeIndex = resolvedManifest.routes.findIndex((entry) => entry.path === route.path);
381
+ const context = {
382
+ appHtml,
383
+ manifest: resolvedManifest,
384
+ routeIndex
385
+ };
386
+ try {
387
+ let html = await renderSsgRouteHtml(route, context, {
221
388
  renderRoute,
222
389
  transformHtml,
223
390
  injectRoutePayload
391
+ });
392
+ const renderedHtml = await onRouteRendered?.(html, route, context);
393
+ if (typeof renderedHtml === "string") {
394
+ html = renderedHtml;
224
395
  }
225
- );
226
- const htmlPath = join(resolvedOutDir, route.htmlFile);
227
- await mkdir(dirname(htmlPath), { recursive: true });
228
- await writeFile(htmlPath, html);
229
- routes.push({
230
- path: route.path,
231
- htmlFile: route.htmlFile,
232
- bytes: Buffer.byteLength(html)
233
- });
396
+ if (crawlLinks) {
397
+ for (const linkedRoute of extractSsgRouteLinks(html, resolvedManifest.base, route.path)) {
398
+ enqueueRoute(linkedRoute);
399
+ }
400
+ }
401
+ const htmlPath = join(resolvedOutDir, route.htmlFile);
402
+ const page = {
403
+ route,
404
+ html,
405
+ htmlFile: route.htmlFile,
406
+ filePath: htmlPath
407
+ };
408
+ const pageUpdate = await onPageGenerated?.(page, context);
409
+ const finalHtml = pageUpdate?.html ?? page.html;
410
+ const finalHtmlFile = pageUpdate?.htmlFile ?? page.htmlFile;
411
+ const finalFilePath = pageUpdate?.filePath ?? join(resolvedOutDir, finalHtmlFile);
412
+ await mkdir(dirname(finalFilePath), { recursive: true });
413
+ await writeFile(finalFilePath, finalHtml);
414
+ routes.push({
415
+ path: route.path,
416
+ htmlFile: finalHtmlFile,
417
+ bytes: Buffer.byteLength(finalHtml),
418
+ milliseconds: Math.round(performance.now() - start)
419
+ });
420
+ } catch (error) {
421
+ const redirectTarget = getRedirectTarget(error);
422
+ if (redirectTarget !== void 0) {
423
+ if (redirects === "follow") {
424
+ const target = hrefToSsgRoutePath(redirectTarget, resolvedManifest.base, route.path);
425
+ if (target) {
426
+ enqueueRoute(target);
427
+ }
428
+ skipped.push({
429
+ path: route.path,
430
+ reason: "redirected",
431
+ target: target ?? redirectTarget
432
+ });
433
+ return;
434
+ }
435
+ if (redirects === "skip") {
436
+ skipped.push({
437
+ path: route.path,
438
+ reason: "skipped-redirect",
439
+ target: redirectTarget
440
+ });
441
+ return;
442
+ }
443
+ }
444
+ if (isNotFoundError(error) && notFound === "skip") {
445
+ skipped.push({
446
+ path: route.path,
447
+ reason: "not-found"
448
+ });
449
+ return;
450
+ }
451
+ throw error;
452
+ }
234
453
  }
235
- return {
454
+ while (queue.length > 0) {
455
+ const batch = queue.splice(0, maxConcurrency);
456
+ await Promise.all(batch.map((route) => renderOne(route)));
457
+ if (queue.length > 0 && batchInterval > 0) {
458
+ await wait(batchInterval);
459
+ }
460
+ }
461
+ await writeFile(
462
+ join(resolvedOutDir, manifestFile),
463
+ `${JSON.stringify(resolvedManifest, null, 2)}
464
+ `
465
+ );
466
+ const report = createReport({
467
+ generated: routes,
236
468
  manifest: resolvedManifest,
469
+ manifestFile,
237
470
  outDir: resolvedOutDir,
238
- routes
471
+ skipped,
472
+ warnings
473
+ });
474
+ if (reportFile !== false) {
475
+ await writeFile(join(resolvedOutDir, reportFile), `${JSON.stringify(report, null, 2)}
476
+ `);
477
+ }
478
+ const result = {
479
+ manifest: resolvedManifest,
480
+ outDir: resolvedOutDir,
481
+ routes,
482
+ skipped,
483
+ warnings,
484
+ report
239
485
  };
486
+ await afterGenerate?.(result);
487
+ return result;
240
488
  }
241
489
 
242
490
  const vueServerRendererPackage = "@vue/server-renderer";
@@ -363,7 +611,8 @@ function viteSsgPlugin(options = {}) {
363
611
  async function refreshManifest() {
364
612
  const routes = await resolveRouteInputs(options);
365
613
  manifest = createSsgRouteManifest(routes, {
366
- base: options.base ?? config?.base ?? "/"
614
+ base: options.base ?? config?.base ?? "/",
615
+ exclude: options.exclude
367
616
  });
368
617
  return manifest;
369
618
  }
@@ -440,4 +689,4 @@ function viteSsgPlugin(options = {}) {
440
689
  };
441
690
  }
442
691
 
443
- export { createSsgRouteHtml, createSsgRouteManifest, createSsgRoutePayloadScript, createVueSsgRouteRenderer, defaultSsgManifestFile, defaultSsgVirtualModuleId, discoverMarkdownSsgRoutes, escapeJsonForHtml, injectSsgRoutePayload, markdownFileToRoutePath, normalizeSsgBase, normalizeSsgRoute, normalizeSsgRoutePath, prerenderSsgRoutes, prerenderVueSsgRoutes, renderSsgRouteHtml, routePathToHtmlFile, routePathToId, viteSsgPlugin };
692
+ export { createSsgRouteHtml, createSsgRouteManifest, createSsgRoutePayloadScript, createVueSsgRouteRenderer, defaultSsgManifestFile, defaultSsgReportFile, defaultSsgVirtualModuleId, discoverMarkdownSsgRoutes, escapeJsonForHtml, flattenStaticSsgRouterRoutes, injectSsgRoutePayload, isSsgRouteExcluded, isStaticSsgRoutePath, markdownFileToRoutePath, normalizeSsgBase, normalizeSsgRoute, normalizeSsgRoutePath, prerenderSsgRoutes, prerenderVueSsgRoutes, renderSsgRouteHtml, routePathToHtmlFile, routePathToId, viteSsgPlugin };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@md-plugins/vite-ssg-plugin",
3
- "version": "0.1.0-rc.2",
3
+ "version": "0.1.0-rc.3",
4
4
  "description": "A Vite plugin for @md-plugins static route inventory and future SSG output.",
5
5
  "keywords": [
6
6
  "markdown-it",