vite-wp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +132 -0
- package/dist/astro.d.ts +5 -0
- package/dist/astro.js +27 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +40 -0
- package/dist/commands/dev.d.ts +1 -0
- package/dist/commands/dev.js +62 -0
- package/dist/commands/doctor.d.ts +6 -0
- package/dist/commands/doctor.js +139 -0
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +55 -0
- package/dist/commands/smoke.d.ts +1 -0
- package/dist/commands/smoke.js +138 -0
- package/dist/commands/types.d.ts +1 -0
- package/dist/commands/types.js +67 -0
- package/dist/config.d.ts +71 -0
- package/dist/config.js +74 -0
- package/dist/content.d.ts +44 -0
- package/dist/content.js +190 -0
- package/dist/dev-toolbar/vitewp-toolbar.d.ts +23 -0
- package/dist/dev-toolbar/vitewp-toolbar.js +144 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +2 -0
- package/dist/live.config.d.ts +64 -0
- package/dist/live.config.js +21 -0
- package/dist/runtime/astro.d.ts +4 -0
- package/dist/runtime/astro.js +69 -0
- package/dist/runtime/composer.d.ts +2 -0
- package/dist/runtime/composer.js +36 -0
- package/dist/runtime/php.d.ts +3 -0
- package/dist/runtime/php.js +79 -0
- package/dist/runtime/ports.d.ts +3 -0
- package/dist/runtime/ports.js +38 -0
- package/dist/runtime/process.d.ts +12 -0
- package/dist/runtime/process.js +51 -0
- package/dist/runtime/proxy.d.ts +6 -0
- package/dist/runtime/proxy.js +204 -0
- package/dist/runtime/wp-config.d.ts +3 -0
- package/dist/runtime/wp-config.js +82 -0
- package/dist/wordpress/client.d.ts +98 -0
- package/dist/wordpress/client.js +133 -0
- package/dist/wordpress/generated-types.d.ts +33 -0
- package/dist/wordpress/generated-types.js +2 -0
- package/dist/wordpress/menus.d.ts +24 -0
- package/dist/wordpress/menus.js +20 -0
- package/dist/wordpress/schemas.d.ts +58 -0
- package/dist/wordpress/schemas.js +39 -0
- package/dist/wordpress/templates.d.ts +19 -0
- package/dist/wordpress/templates.js +88 -0
- package/package.json +78 -0
- package/starter/.env.example +15 -0
- package/starter/astro.config.mjs +16 -0
- package/starter/composer.json +37 -0
- package/starter/src/env.d.ts +1 -0
- package/starter/src/live.config.ts +22 -0
- package/starter/src/pages/[...slug].astro +101 -0
- package/starter/src/templates/404.astro +11 -0
- package/starter/src/templates/pages/.gitkeep +0 -0
- package/starter/src/templates/pages/default.astro +12 -0
- package/starter/src/templates/pages/front-page.astro +13 -0
- package/starter/src/templates/partials/Header.astro +23 -0
- package/starter/src/templates/partials/Pagination.astro +21 -0
- package/starter/src/templates/post-types/.gitkeep +0 -0
- package/starter/src/templates/posts/.gitkeep +0 -0
- package/starter/src/templates/posts/archive.astro +25 -0
- package/starter/src/templates/posts/single.astro +12 -0
- package/starter/src/templates/search.astro +30 -0
- package/starter/src/templates/taxonomies/.gitkeep +0 -0
- package/starter/src/templates/taxonomies/taxonomy-[taxonomy].astro +28 -0
- package/starter/src/wordpress/client.ts +263 -0
- package/starter/src/wordpress/generated-types.ts +38 -0
- package/starter/src/wordpress/menus.ts +51 -0
- package/starter/src/wordpress/schemas.ts +44 -0
- package/starter/src/wordpress/templates.ts +113 -0
- package/starter/tsconfig.json +4 -0
- package/starter/vitewp.config.ts +29 -0
- package/starter/wordpress/content/mu-plugins/.gitkeep +0 -0
- package/starter/wordpress/content/mu-plugins/vitewp-bridge.php +560 -0
- package/starter/wordpress/content/themes/vitewp/functions.php +9 -0
- package/starter/wordpress/content/themes/vitewp/index.php +22 -0
- package/starter/wordpress/content/themes/vitewp/style.css +10 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, relative, resolve } from 'node:path';
|
|
3
|
+
import { loadViteWpConfig } from '../config.js';
|
|
4
|
+
export async function runTypes() {
|
|
5
|
+
const config = await loadViteWpConfig();
|
|
6
|
+
const schema = await fetchBridgeTypes(config.wordpress.url);
|
|
7
|
+
const output = resolve(config.root, config.types.output);
|
|
8
|
+
mkdirSync(dirname(output), { recursive: true });
|
|
9
|
+
writeFileSync(output, renderTypes(schema), 'utf8');
|
|
10
|
+
console.log(`✓ Generated WordPress types at ${relative(config.root, output)}`);
|
|
11
|
+
}
|
|
12
|
+
async function fetchBridgeTypes(baseUrl) {
|
|
13
|
+
const url = `${baseUrl.replace(/\/$/, '')}/wp-json/vitewp/v1/types`;
|
|
14
|
+
const response = await fetch(url);
|
|
15
|
+
if (!response.ok) {
|
|
16
|
+
throw new Error(`Could not fetch WordPress type metadata from ${url}: ${response.status} ${response.statusText}`);
|
|
17
|
+
}
|
|
18
|
+
return response.json();
|
|
19
|
+
}
|
|
20
|
+
function renderTypes(schema) {
|
|
21
|
+
const postTypeUnion = union(schema.postTypes.map((postType) => postType.name));
|
|
22
|
+
const taxonomyUnion = union(schema.taxonomies.map((taxonomy) => taxonomy.name));
|
|
23
|
+
const restBaseMap = objectType(schema.postTypes.map((postType) => [postType.name, stringLiteral(postType.restBase)]));
|
|
24
|
+
const archiveSlugMap = objectType(schema.postTypes.map((postType) => [postType.name, postType.archiveSlug ? stringLiteral(postType.archiveSlug) : 'null']));
|
|
25
|
+
const taxonomyRestBaseMap = objectType(schema.taxonomies.map((taxonomy) => [taxonomy.name, stringLiteral(taxonomy.restBase)]));
|
|
26
|
+
return `// Generated by ViteWP. Do not edit by hand.
|
|
27
|
+
|
|
28
|
+
export type WpPostType = ${postTypeUnion};
|
|
29
|
+
export type WpTaxonomy = ${taxonomyUnion};
|
|
30
|
+
|
|
31
|
+
export type WpRestBaseByPostType = ${restBaseMap};
|
|
32
|
+
export type WpArchiveSlugByPostType = ${archiveSlugMap};
|
|
33
|
+
export type WpRestBaseByTaxonomy = ${taxonomyRestBaseMap};
|
|
34
|
+
|
|
35
|
+
export interface WpRenderedField {
|
|
36
|
+
rendered: string;
|
|
37
|
+
protected?: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface WpContentItem<PostType extends WpPostType = WpPostType> {
|
|
41
|
+
id: number;
|
|
42
|
+
slug: string;
|
|
43
|
+
type: PostType;
|
|
44
|
+
link: string;
|
|
45
|
+
title: WpRenderedField;
|
|
46
|
+
content: WpRenderedField;
|
|
47
|
+
excerpt?: WpRenderedField;
|
|
48
|
+
date?: string;
|
|
49
|
+
modified?: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface WpArchiveItem<PostType extends WpPostType = WpPostType> extends WpContentItem<PostType> {}
|
|
53
|
+
`;
|
|
54
|
+
}
|
|
55
|
+
function union(values) {
|
|
56
|
+
if (values.length === 0)
|
|
57
|
+
return 'never';
|
|
58
|
+
return values.map(stringLiteral).join(' | ');
|
|
59
|
+
}
|
|
60
|
+
function objectType(entries) {
|
|
61
|
+
if (entries.length === 0)
|
|
62
|
+
return 'Record<string, never>';
|
|
63
|
+
return `{\n${entries.map(([key, value]) => ` ${JSON.stringify(key)}: ${value};`).join('\n')}\n}`;
|
|
64
|
+
}
|
|
65
|
+
function stringLiteral(value) {
|
|
66
|
+
return JSON.stringify(value);
|
|
67
|
+
}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export type WordPressRuntimeMode = 'local' | 'external';
|
|
2
|
+
export interface ViteWpConfig {
|
|
3
|
+
database?: {
|
|
4
|
+
driver?: 'mysql' | 'mariadb';
|
|
5
|
+
host?: string;
|
|
6
|
+
port?: number;
|
|
7
|
+
name?: string;
|
|
8
|
+
user?: string;
|
|
9
|
+
password?: string;
|
|
10
|
+
tablePrefix?: string;
|
|
11
|
+
};
|
|
12
|
+
wordpress?: {
|
|
13
|
+
mode?: WordPressRuntimeMode;
|
|
14
|
+
url?: string;
|
|
15
|
+
docroot?: string;
|
|
16
|
+
contentDir?: string;
|
|
17
|
+
};
|
|
18
|
+
composer?: {
|
|
19
|
+
install?: boolean;
|
|
20
|
+
wordpressPackage?: string;
|
|
21
|
+
};
|
|
22
|
+
templates?: {
|
|
23
|
+
directory?: string;
|
|
24
|
+
};
|
|
25
|
+
types?: {
|
|
26
|
+
output?: string;
|
|
27
|
+
};
|
|
28
|
+
dev?: {
|
|
29
|
+
phpHost?: string;
|
|
30
|
+
phpPort?: number;
|
|
31
|
+
astroHost?: string;
|
|
32
|
+
astroPort?: number;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export interface LoadedViteWpConfig {
|
|
36
|
+
root: string;
|
|
37
|
+
configFile?: string;
|
|
38
|
+
database: {
|
|
39
|
+
driver: 'mysql' | 'mariadb';
|
|
40
|
+
host: string;
|
|
41
|
+
port: number;
|
|
42
|
+
name: string;
|
|
43
|
+
user: string;
|
|
44
|
+
password: string;
|
|
45
|
+
tablePrefix: string;
|
|
46
|
+
};
|
|
47
|
+
wordpress: {
|
|
48
|
+
mode: WordPressRuntimeMode;
|
|
49
|
+
url: string;
|
|
50
|
+
docroot: string;
|
|
51
|
+
contentDir: string;
|
|
52
|
+
};
|
|
53
|
+
composer: {
|
|
54
|
+
install: boolean;
|
|
55
|
+
wordpressPackage: string;
|
|
56
|
+
};
|
|
57
|
+
templates: {
|
|
58
|
+
directory: string;
|
|
59
|
+
};
|
|
60
|
+
types: {
|
|
61
|
+
output: string;
|
|
62
|
+
};
|
|
63
|
+
dev: {
|
|
64
|
+
phpHost: string;
|
|
65
|
+
phpPort: number;
|
|
66
|
+
astroHost: string;
|
|
67
|
+
astroPort: number;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
export declare function defineConfig(config: ViteWpConfig): ViteWpConfig;
|
|
71
|
+
export declare function loadViteWpConfig(root?: string): Promise<LoadedViteWpConfig>;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { loadConfigFromFile, loadEnv } from 'vite';
|
|
4
|
+
const configFiles = [
|
|
5
|
+
'vitewp.config.ts',
|
|
6
|
+
'vitewp.config.mts',
|
|
7
|
+
'vitewp.config.js',
|
|
8
|
+
'vitewp.config.mjs',
|
|
9
|
+
];
|
|
10
|
+
export function defineConfig(config) {
|
|
11
|
+
return config;
|
|
12
|
+
}
|
|
13
|
+
export async function loadViteWpConfig(root = process.cwd()) {
|
|
14
|
+
loadDotEnv(root);
|
|
15
|
+
const configFile = configFiles
|
|
16
|
+
.map((file) => resolve(root, file))
|
|
17
|
+
.find((file) => existsSync(file));
|
|
18
|
+
let userConfig = {};
|
|
19
|
+
if (configFile) {
|
|
20
|
+
const loaded = await loadConfigFromFile({ command: 'serve', mode: 'development' }, configFile, root);
|
|
21
|
+
userConfig = (loaded?.config ?? {});
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
root,
|
|
25
|
+
configFile,
|
|
26
|
+
database: {
|
|
27
|
+
driver: userConfig.database?.driver ?? env('WP_DB_DRIVER', 'mysql'),
|
|
28
|
+
host: userConfig.database?.host ?? env('WP_DB_HOST', '127.0.0.1'),
|
|
29
|
+
port: userConfig.database?.port ?? Number(env('WP_DB_PORT', '3306')),
|
|
30
|
+
name: userConfig.database?.name ?? env('WP_DB_NAME', 'vitewp'),
|
|
31
|
+
user: userConfig.database?.user ?? env('WP_DB_USER', 'root'),
|
|
32
|
+
password: userConfig.database?.password ?? env('WP_DB_PASSWORD', ''),
|
|
33
|
+
tablePrefix: userConfig.database?.tablePrefix ?? env('WP_DB_TABLE_PREFIX', 'wp_'),
|
|
34
|
+
},
|
|
35
|
+
wordpress: {
|
|
36
|
+
mode: userConfig.wordpress?.mode ?? 'local',
|
|
37
|
+
url: userConfig.wordpress?.url ?? 'http://localhost:3000',
|
|
38
|
+
docroot: userConfig.wordpress?.docroot ?? 'wordpress/public',
|
|
39
|
+
contentDir: userConfig.wordpress?.contentDir ?? 'wordpress/content',
|
|
40
|
+
},
|
|
41
|
+
composer: {
|
|
42
|
+
install: userConfig.composer?.install ?? true,
|
|
43
|
+
wordpressPackage: userConfig.composer?.wordpressPackage ?? 'johnpbloch/wordpress',
|
|
44
|
+
},
|
|
45
|
+
templates: {
|
|
46
|
+
directory: userConfig.templates?.directory ?? 'src/templates',
|
|
47
|
+
},
|
|
48
|
+
types: {
|
|
49
|
+
output: userConfig.types?.output ?? 'src/wordpress/generated-types.ts',
|
|
50
|
+
},
|
|
51
|
+
dev: {
|
|
52
|
+
phpHost: userConfig.dev?.phpHost ?? env('VITEWP_PHP_HOST', '127.0.0.1'),
|
|
53
|
+
phpPort: userConfig.dev?.phpPort ?? envPort('VITEWP_PHP_PORT'),
|
|
54
|
+
astroHost: userConfig.dev?.astroHost ?? env('VITEWP_ASTRO_HOST', '127.0.0.1'),
|
|
55
|
+
astroPort: userConfig.dev?.astroPort ?? envPort('VITEWP_ASTRO_PORT'),
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function env(name, fallback) {
|
|
60
|
+
return process.env[name] ?? fallback;
|
|
61
|
+
}
|
|
62
|
+
function envPort(name) {
|
|
63
|
+
const value = process.env[name];
|
|
64
|
+
if (!value || value === 'auto') {
|
|
65
|
+
return 0;
|
|
66
|
+
}
|
|
67
|
+
return Number(value);
|
|
68
|
+
}
|
|
69
|
+
function loadDotEnv(root) {
|
|
70
|
+
const values = loadEnv(process.env.NODE_ENV ?? 'development', root, '');
|
|
71
|
+
for (const [key, value] of Object.entries(values)) {
|
|
72
|
+
process.env[key] ??= value;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { CacheHint, LiveDataCollection, LiveDataEntry } from 'astro';
|
|
2
|
+
import type { LiveLoader as AstroLiveLoader } from 'astro/loaders';
|
|
3
|
+
import { type WpMenu } from './wordpress/menus.js';
|
|
4
|
+
import { type WpContentItem, type WpResolvedRoute } from './wordpress/client.js';
|
|
5
|
+
export type LiveCacheHint = CacheHint;
|
|
6
|
+
export type LiveEntry<TData extends Record<string, any>> = LiveDataEntry<TData>;
|
|
7
|
+
export type LiveCollection<TData extends Record<string, any>> = LiveDataCollection<TData>;
|
|
8
|
+
export type LiveLoader<TData extends Record<string, any>, TEntryFilter extends Record<string, any> | never, TCollectionFilter extends Record<string, any> | never> = AstroLiveLoader<TData, TEntryFilter, TCollectionFilter>;
|
|
9
|
+
export interface WpRouteEntryFilter {
|
|
10
|
+
path?: string;
|
|
11
|
+
id?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface WpRouteCollectionFilter {
|
|
14
|
+
paths?: string[];
|
|
15
|
+
}
|
|
16
|
+
export declare function wpRouteLoader(): LiveLoader<WpResolvedRouteData, WpRouteEntryFilter, WpRouteCollectionFilter>;
|
|
17
|
+
export interface WpPostTypeLoaderOptions {
|
|
18
|
+
postType: string;
|
|
19
|
+
restBase?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface WpPostEntryFilter {
|
|
22
|
+
id?: string | number;
|
|
23
|
+
slug?: string;
|
|
24
|
+
}
|
|
25
|
+
export interface WpPostCollectionFilter {
|
|
26
|
+
page?: number;
|
|
27
|
+
perPage?: number;
|
|
28
|
+
search?: string;
|
|
29
|
+
slug?: string;
|
|
30
|
+
}
|
|
31
|
+
export declare function wpPostTypeLoader(options: WpPostTypeLoaderOptions): LiveLoader<WpContentItemData, WpPostEntryFilter, WpPostCollectionFilter>;
|
|
32
|
+
export interface WpMenuEntryFilter {
|
|
33
|
+
id?: string | number;
|
|
34
|
+
slug?: string;
|
|
35
|
+
location?: string;
|
|
36
|
+
}
|
|
37
|
+
export interface WpMenuCollectionFilter {
|
|
38
|
+
location?: string;
|
|
39
|
+
}
|
|
40
|
+
export declare function wpMenuLoader(): LiveLoader<WpMenuData, WpMenuEntryFilter, WpMenuCollectionFilter>;
|
|
41
|
+
type WpResolvedRouteData = WpResolvedRoute & Record<string, unknown>;
|
|
42
|
+
type WpContentItemData = WpContentItem & Record<string, unknown>;
|
|
43
|
+
type WpMenuData = WpMenu & Record<string, unknown>;
|
|
44
|
+
export {};
|
package/dist/content.js
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { getMenus } from './wordpress/menus.js';
|
|
2
|
+
import { getWordPressApiBase, resolveWordPressRoute, } from './wordpress/client.js';
|
|
3
|
+
export function wpRouteLoader() {
|
|
4
|
+
return {
|
|
5
|
+
name: 'vitewp:routes',
|
|
6
|
+
async loadEntry({ filter }) {
|
|
7
|
+
const path = filter.path ?? filter.id ?? '/';
|
|
8
|
+
const route = await resolveWordPressRoute(path);
|
|
9
|
+
if (!route) {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
id: normalizeRouteId(path),
|
|
14
|
+
data: route,
|
|
15
|
+
cacheHint: routeCacheHint(path, route),
|
|
16
|
+
};
|
|
17
|
+
},
|
|
18
|
+
async loadCollection({ filter }) {
|
|
19
|
+
const paths = filter?.paths ?? ['/'];
|
|
20
|
+
const entries = [];
|
|
21
|
+
for (const path of paths) {
|
|
22
|
+
const route = await resolveWordPressRoute(path);
|
|
23
|
+
if (route) {
|
|
24
|
+
entries.push({
|
|
25
|
+
id: normalizeRouteId(path),
|
|
26
|
+
data: route,
|
|
27
|
+
cacheHint: routeCacheHint(path, route),
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
entries,
|
|
33
|
+
cacheHint: mergeCacheHints(entries.map((entry) => entry.cacheHint)),
|
|
34
|
+
};
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export function wpPostTypeLoader(options) {
|
|
39
|
+
const restBase = options.restBase ?? defaultRestBase(options.postType);
|
|
40
|
+
return {
|
|
41
|
+
name: `vitewp:${options.postType}`,
|
|
42
|
+
async loadEntry({ filter }) {
|
|
43
|
+
const item = filter.id !== undefined
|
|
44
|
+
? await fetchWordPressItemById(restBase, filter.id)
|
|
45
|
+
: filter.slug
|
|
46
|
+
? await fetchWordPressItemBySlug(restBase, filter.slug)
|
|
47
|
+
: undefined;
|
|
48
|
+
if (!item) {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
return contentEntry(item);
|
|
52
|
+
},
|
|
53
|
+
async loadCollection({ filter }) {
|
|
54
|
+
const url = new URL(`${getWordPressApiBase()}/${restBase}`);
|
|
55
|
+
url.searchParams.set('_embed', '1');
|
|
56
|
+
url.searchParams.set('page', String(filter?.page ?? 1));
|
|
57
|
+
url.searchParams.set('per_page', String(filter?.perPage ?? 10));
|
|
58
|
+
if (filter?.search) {
|
|
59
|
+
url.searchParams.set('search', filter.search);
|
|
60
|
+
}
|
|
61
|
+
if (filter?.slug) {
|
|
62
|
+
url.searchParams.set('slug', filter.slug);
|
|
63
|
+
}
|
|
64
|
+
const { items } = await fetchWordPressList(url);
|
|
65
|
+
const entries = items.map(contentEntry);
|
|
66
|
+
return {
|
|
67
|
+
entries,
|
|
68
|
+
cacheHint: mergeCacheHints(entries.map((entry) => entry.cacheHint)),
|
|
69
|
+
};
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
export function wpMenuLoader() {
|
|
74
|
+
return {
|
|
75
|
+
name: 'vitewp:menus',
|
|
76
|
+
async loadEntry({ filter }) {
|
|
77
|
+
const payload = await getMenus();
|
|
78
|
+
const locationId = filter.location ? payload.locations[filter.location] : undefined;
|
|
79
|
+
const menu = payload.menus.find((candidate) => {
|
|
80
|
+
return candidate.id === Number(filter.id ?? locationId)
|
|
81
|
+
|| candidate.slug === filter.slug;
|
|
82
|
+
});
|
|
83
|
+
return menu ? menuEntry(menu) : undefined;
|
|
84
|
+
},
|
|
85
|
+
async loadCollection({ filter }) {
|
|
86
|
+
const payload = await getMenus();
|
|
87
|
+
const locationId = filter?.location ? payload.locations[filter.location] : undefined;
|
|
88
|
+
const menus = locationId
|
|
89
|
+
? payload.menus.filter((menu) => menu.id === locationId)
|
|
90
|
+
: payload.menus;
|
|
91
|
+
return {
|
|
92
|
+
entries: menus.map(menuEntry),
|
|
93
|
+
cacheHint: { tags: ['wordpress:menus'] },
|
|
94
|
+
};
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function defaultRestBase(postType) {
|
|
99
|
+
if (postType === 'post') {
|
|
100
|
+
return 'posts';
|
|
101
|
+
}
|
|
102
|
+
if (postType === 'page') {
|
|
103
|
+
return 'pages';
|
|
104
|
+
}
|
|
105
|
+
return postType;
|
|
106
|
+
}
|
|
107
|
+
async function fetchWordPressItemById(restBase, id) {
|
|
108
|
+
const url = new URL(`${getWordPressApiBase()}/${restBase}/${id}`);
|
|
109
|
+
url.searchParams.set('_embed', '1');
|
|
110
|
+
return fetchWordPressJson(url, [404]);
|
|
111
|
+
}
|
|
112
|
+
async function fetchWordPressItemBySlug(restBase, slug) {
|
|
113
|
+
const url = new URL(`${getWordPressApiBase()}/${restBase}`);
|
|
114
|
+
url.searchParams.set('_embed', '1');
|
|
115
|
+
url.searchParams.set('slug', slug);
|
|
116
|
+
const { items } = await fetchWordPressList(url);
|
|
117
|
+
return items[0];
|
|
118
|
+
}
|
|
119
|
+
async function fetchWordPressList(url) {
|
|
120
|
+
const response = await fetch(url);
|
|
121
|
+
if (!response.ok) {
|
|
122
|
+
throw new Error(`WordPress REST request failed: ${response.status} ${response.statusText}`);
|
|
123
|
+
}
|
|
124
|
+
const items = await response.json();
|
|
125
|
+
return {
|
|
126
|
+
items,
|
|
127
|
+
total: Number(response.headers.get('x-wp-total') ?? items.length),
|
|
128
|
+
totalPages: Number(response.headers.get('x-wp-totalpages') ?? 1),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
async function fetchWordPressJson(url, emptyStatuses = []) {
|
|
132
|
+
const response = await fetch(url);
|
|
133
|
+
if (emptyStatuses.includes(response.status)) {
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
if (!response.ok) {
|
|
137
|
+
throw new Error(`WordPress REST request failed: ${response.status} ${response.statusText}`);
|
|
138
|
+
}
|
|
139
|
+
return response.json();
|
|
140
|
+
}
|
|
141
|
+
function contentEntry(item) {
|
|
142
|
+
return {
|
|
143
|
+
id: String(item.id),
|
|
144
|
+
data: item,
|
|
145
|
+
cacheHint: {
|
|
146
|
+
tags: [`wordpress:${item.type}`, `wordpress:${item.type}:${item.id}`],
|
|
147
|
+
lastModified: item.modified ? new Date(item.modified) : undefined,
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
function menuEntry(menu) {
|
|
152
|
+
return {
|
|
153
|
+
id: String(menu.id),
|
|
154
|
+
data: menu,
|
|
155
|
+
cacheHint: {
|
|
156
|
+
tags: ['wordpress:menus', `wordpress:menu:${menu.slug}`],
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function routeCacheHint(path, route) {
|
|
161
|
+
if (route.kind === 'page' || route.kind === 'single') {
|
|
162
|
+
return {
|
|
163
|
+
tags: [`wordpress:route:${normalizeRouteId(path)}`, `wordpress:${route.postType}:${route.item.id}`],
|
|
164
|
+
lastModified: route.item.modified ? new Date(route.item.modified) : undefined,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
tags: [`wordpress:route:${normalizeRouteId(path)}`, `wordpress:${route.kind}`],
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function mergeCacheHints(hints) {
|
|
172
|
+
const tags = new Set();
|
|
173
|
+
let lastModified;
|
|
174
|
+
for (const hint of hints) {
|
|
175
|
+
hint?.tags?.forEach((tag) => tags.add(tag));
|
|
176
|
+
if (hint?.lastModified && (!lastModified || hint.lastModified > lastModified)) {
|
|
177
|
+
lastModified = hint.lastModified;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (tags.size === 0 && !lastModified) {
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
tags: [...tags],
|
|
185
|
+
lastModified,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
function normalizeRouteId(path) {
|
|
189
|
+
return path || '/';
|
|
190
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
interface ViteWpRouteInfo {
|
|
2
|
+
candidateTemplates: string[];
|
|
3
|
+
kind: string;
|
|
4
|
+
liveCollection: {
|
|
5
|
+
collection: string;
|
|
6
|
+
entryId: string;
|
|
7
|
+
cacheHint: unknown;
|
|
8
|
+
} | null;
|
|
9
|
+
matched: boolean;
|
|
10
|
+
page: number | null;
|
|
11
|
+
path: string;
|
|
12
|
+
postType: string | null;
|
|
13
|
+
slug: string | null;
|
|
14
|
+
template: string | null;
|
|
15
|
+
totalPages: number | null;
|
|
16
|
+
}
|
|
17
|
+
declare global {
|
|
18
|
+
interface Window {
|
|
19
|
+
__VITEWP_ROUTE_INFO__?: ViteWpRouteInfo;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
declare const _default: import("astro").DevToolbarApp;
|
|
23
|
+
export default _default;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { defineToolbarApp } from 'astro/toolbar';
|
|
2
|
+
export default defineToolbarApp({
|
|
3
|
+
init(canvas) {
|
|
4
|
+
render(canvas);
|
|
5
|
+
window.addEventListener('vitewp:route-info', () => render(canvas));
|
|
6
|
+
document.addEventListener('astro:after-swap', () => render(canvas));
|
|
7
|
+
},
|
|
8
|
+
});
|
|
9
|
+
function render(canvas) {
|
|
10
|
+
const info = window.__VITEWP_ROUTE_INFO__;
|
|
11
|
+
canvas.innerHTML = '';
|
|
12
|
+
const panel = document.createElement('astro-dev-toolbar-window');
|
|
13
|
+
panel.innerHTML = `
|
|
14
|
+
<style>
|
|
15
|
+
:host astro-dev-toolbar-window {
|
|
16
|
+
width: min(520px, calc(100vw - 48px));
|
|
17
|
+
max-height: min(640px, calc(100vh - 120px));
|
|
18
|
+
overflow-y: auto;
|
|
19
|
+
color-scheme: dark;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
header {
|
|
23
|
+
display: flex;
|
|
24
|
+
align-items: center;
|
|
25
|
+
justify-content: space-between;
|
|
26
|
+
gap: 16px;
|
|
27
|
+
margin-bottom: 18px;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
h1 {
|
|
31
|
+
color: #fff;
|
|
32
|
+
font-size: 22px;
|
|
33
|
+
line-height: 1.2;
|
|
34
|
+
margin: 0;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.badge {
|
|
38
|
+
border: 1px solid rgba(145, 152, 173, 0.4);
|
|
39
|
+
border-radius: 999px;
|
|
40
|
+
color: rgba(204, 206, 216, 1);
|
|
41
|
+
font-size: 12px;
|
|
42
|
+
padding: 4px 8px;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.grid {
|
|
46
|
+
display: grid;
|
|
47
|
+
gap: 10px;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.row {
|
|
51
|
+
border: 1px solid rgba(52, 56, 65, 1);
|
|
52
|
+
border-radius: 10px;
|
|
53
|
+
background: rgba(255, 255, 255, 0.04);
|
|
54
|
+
padding: 10px 12px;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.label {
|
|
58
|
+
color: rgba(145, 152, 173, 1);
|
|
59
|
+
display: block;
|
|
60
|
+
font-size: 12px;
|
|
61
|
+
margin-bottom: 4px;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
code {
|
|
65
|
+
color: rgba(224, 204, 250, 1);
|
|
66
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
67
|
+
font-size: 13px;
|
|
68
|
+
overflow-wrap: anywhere;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
ul {
|
|
72
|
+
display: grid;
|
|
73
|
+
gap: 6px;
|
|
74
|
+
margin: 8px 0 0;
|
|
75
|
+
padding-left: 18px;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
li {
|
|
79
|
+
color: rgba(204, 206, 216, 1);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
p {
|
|
83
|
+
color: rgba(204, 206, 216, 1);
|
|
84
|
+
line-height: 1.5;
|
|
85
|
+
margin: 0;
|
|
86
|
+
}
|
|
87
|
+
</style>
|
|
88
|
+
${info ? renderInfo(info) : renderEmptyState()}
|
|
89
|
+
`;
|
|
90
|
+
canvas.append(panel);
|
|
91
|
+
}
|
|
92
|
+
function renderInfo(info) {
|
|
93
|
+
return `
|
|
94
|
+
<header>
|
|
95
|
+
<h1>ViteWP route</h1>
|
|
96
|
+
<span class="badge">${escapeHtml(info.matched ? info.kind : '404')}</span>
|
|
97
|
+
</header>
|
|
98
|
+
<section class="grid">
|
|
99
|
+
${row('URL path', info.path)}
|
|
100
|
+
${row('Selected template', info.template ?? 'No template matched')}
|
|
101
|
+
${row('Live collection', info.liveCollection ? `${info.liveCollection.collection} → ${info.liveCollection.entryId}` : '—')}
|
|
102
|
+
${row('Post type', info.postType ?? '—')}
|
|
103
|
+
${row('Slug', info.slug ?? '—')}
|
|
104
|
+
${row('Pagination', paginationLabel(info))}
|
|
105
|
+
<div class="row">
|
|
106
|
+
<span class="label">Template candidates</span>
|
|
107
|
+
<ul>
|
|
108
|
+
${info.candidateTemplates.map((candidate) => `<li><code>${escapeHtml(candidate)}</code></li>`).join('')}
|
|
109
|
+
</ul>
|
|
110
|
+
</div>
|
|
111
|
+
</section>
|
|
112
|
+
`;
|
|
113
|
+
}
|
|
114
|
+
function renderEmptyState() {
|
|
115
|
+
return `
|
|
116
|
+
<header>
|
|
117
|
+
<h1>ViteWP route</h1>
|
|
118
|
+
<span class="badge">No route data</span>
|
|
119
|
+
</header>
|
|
120
|
+
<p>Open an Astro-rendered WordPress frontend route to see template hierarchy information here.</p>
|
|
121
|
+
`;
|
|
122
|
+
}
|
|
123
|
+
function row(label, value) {
|
|
124
|
+
return `
|
|
125
|
+
<div class="row">
|
|
126
|
+
<span class="label">${escapeHtml(label)}</span>
|
|
127
|
+
<code>${escapeHtml(value)}</code>
|
|
128
|
+
</div>
|
|
129
|
+
`;
|
|
130
|
+
}
|
|
131
|
+
function paginationLabel(info) {
|
|
132
|
+
if (!info.page && !info.totalPages) {
|
|
133
|
+
return '—';
|
|
134
|
+
}
|
|
135
|
+
return `${info.page ?? 1}${info.totalPages ? ` / ${info.totalPages}` : ''}`;
|
|
136
|
+
}
|
|
137
|
+
function escapeHtml(value) {
|
|
138
|
+
return value
|
|
139
|
+
.replaceAll('&', '&')
|
|
140
|
+
.replaceAll('<', '<')
|
|
141
|
+
.replaceAll('>', '>')
|
|
142
|
+
.replaceAll('"', '"')
|
|
143
|
+
.replaceAll("'", ''');
|
|
144
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { defineConfig } from './config.js';
|
|
2
|
+
export type { ViteWpConfig, LoadedViteWpConfig } from './config.js';
|
|
3
|
+
export { wpMenuLoader, wpPostTypeLoader, wpRouteLoader } from './content.js';
|
|
4
|
+
export type { LiveCacheHint, LiveCollection, LiveEntry, LiveLoader, WpMenuCollectionFilter, WpMenuEntryFilter, WpPostCollectionFilter, WpPostEntryFilter, WpPostTypeLoaderOptions, WpRouteCollectionFilter, WpRouteEntryFilter, } from './content.js';
|
package/dist/index.js
ADDED