nuxt-schema-org
Version:
The quickest and easiest way to build Schema.org graphs for Nuxt.
219 lines (214 loc) • 7.46 kB
JavaScript
import { useNuxt, createResolver, addTemplate, defineNuxtModule, useLogger, hasNuxtModule, addPlugin, hasNuxtModuleCompatibility, addServerPlugin, addComponent, addImports, addServerHandler } from '@nuxt/kit';
import { defineWebPage } from '@unhead/schema-org';
import { schemaOrgAutoImports, schemaOrgComponents } from '@unhead/schema-org/vue';
import { defu } from 'defu';
import { installNuxtSiteConfig } from 'nuxt-site-config/kit';
import { readPackageJSON } from 'pkg-types';
import { existsSync } from 'node:fs';
import { relative } from 'pathe';
const DEVTOOLS_UI_ROUTE = "/__nuxt-schema-org";
const DEVTOOLS_UI_LOCAL_PORT = 3030;
function setupDevToolsUI(options, resolve, nuxt = useNuxt()) {
const clientPath = resolve("./client");
const isProductionBuild = existsSync(clientPath);
if (isProductionBuild) {
nuxt.hook("vite:serverCreated", async (server) => {
const sirv = await import('sirv').then((r) => r.default || r);
server.middlewares.use(
DEVTOOLS_UI_ROUTE,
sirv(clientPath, { dev: true, single: true })
);
});
} else {
nuxt.hook("vite:extendConfig", (config) => {
config.server = config.server || {};
config.server.proxy = config.server.proxy || {};
config.server.proxy[DEVTOOLS_UI_ROUTE] = {
target: `http://localhost:${DEVTOOLS_UI_LOCAL_PORT}${DEVTOOLS_UI_ROUTE}`,
changeOrigin: true,
followRedirects: true,
rewrite: (path) => path.replace(DEVTOOLS_UI_ROUTE, "")
};
});
}
nuxt.hook("devtools:customTabs", (tabs) => {
tabs.push({
// unique identifier
name: "nuxt-schema-org",
// title to display in the tab
title: "Schema.org",
// any icon from Iconify, or a URL to an image
icon: "carbon:chart-relationship",
// iframe view
view: {
type: "iframe",
src: DEVTOOLS_UI_ROUTE
}
});
});
}
function extendTypes(module, template) {
const nuxt = useNuxt();
const { resolve } = createResolver(import.meta.url);
addTemplate({
filename: `module/${module}.d.ts`,
getContents: async () => {
const typesPath = relative(resolve(nuxt.options.rootDir, nuxt.options.buildDir, "module"), resolve("runtime/types"));
const s = await template({ typesPath });
return `// Generated by ${module}
${s}
export {}
`;
}
});
nuxt.hooks.hook("prepare:types", ({ references }) => {
references.push({ path: resolve(nuxt.options.buildDir, `module/${module}.d.ts`) });
});
}
const module = defineNuxtModule({
meta: {
name: "nuxt-schema-org",
configKey: "schemaOrg",
compatibility: {
nuxt: ">=3.16.0"
},
moduleDependencies: {
"@nuxtjs/i18n": {
version: ">=8",
optional: true
},
"nuxt-i18n-micro": {
version: ">=1",
optional: true
},
"nuxt-site-config": {
version: ">=3"
},
"@nuxt/content": {
version: ">=2",
optional: true
}
}
},
defaults(nuxt) {
return {
enabled: true,
defaults: true,
reactive: nuxt.options.dev || !nuxt.options.ssr,
minify: !nuxt.options.dev,
scriptAttributes: {
"data-nuxt-schema-org": true
}
};
},
async setup(config, nuxt) {
const { resolve } = createResolver(import.meta.url);
const { name, version } = await readPackageJSON(resolve("../package.json"));
const logger = useLogger(name);
logger.level = config.debug ? 4 : 3;
if (config.enabled === false) {
logger.debug("The module is disabled, skipping setup.");
return;
}
if (!nuxt.options.ssr && nuxt.options.dev)
logger.warn("You are using Schema.org with SSR disabled. This is not recommended, Google may not detect your Schema.org, and it adds extra page weight");
await installNuxtSiteConfig();
const runtimeConfig = {
reactive: config.reactive,
minify: config.minify,
scriptAttributes: config.scriptAttributes,
identity: config.identity,
version
};
if (config.reactive)
nuxt.options.runtimeConfig.public["nuxt-schema-org"] = runtimeConfig;
nuxt.options.runtimeConfig["nuxt-schema-org"] = runtimeConfig;
const pluginPath = hasNuxtModule("@nuxtjs/i18n") && nuxt.options.i18n?.locales ? "./runtime/app/plugins/i18n" : "./runtime/app/plugins";
addPlugin({
src: resolve(pluginPath, "init"),
mode: config.reactive ? "all" : "server"
});
if (config.defaults) {
addPlugin({
src: resolve(pluginPath, "defaults"),
mode: config.reactive ? "all" : "server"
});
}
nuxt.options.alias["#schema-org"] = resolve("./runtime");
const usingNuxtContent = hasNuxtModule("@nuxt/content");
const isNuxtContentV3 = usingNuxtContent && await hasNuxtModuleCompatibility("@nuxt/content", "^3");
const isNuxtContentV2 = usingNuxtContent && await hasNuxtModuleCompatibility("@nuxt/content", "^2");
if (isNuxtContentV3) {
nuxt.hooks.hook("content:file:afterParse", (ctx) => {
if (typeof ctx.content.schemaOrg === "undefined") {
return;
}
const content = ctx.content;
const nodes = Array.isArray(content.schemaOrg) ? content.schemaOrg : [defineWebPage(content.schemaOrg)];
const replaceType = (node) => {
if (node.type) {
node["@type"] = node.type;
delete node.type;
}
Object.entries(node).forEach(([, value]) => {
if (typeof value === "object") {
replaceType(value);
}
});
return node;
};
const script = {
type: "application/ld+json",
key: "schema-org-graph",
...config.scriptAttributes,
nodes: nodes.map(replaceType)
};
content.head = defu({ script: [script] }, content.head);
ctx.content = content;
});
} else if (isNuxtContentV2) {
addServerPlugin(resolve("./runtime/server/plugins/nuxt-content-v2"));
}
if (!config.reactive)
nuxt.options.optimization.treeShake.composables.client["nuxt-schema-org"] = schemaOrgAutoImports[0].imports;
for (const component of schemaOrgComponents) {
await addComponent({
name: component,
export: component,
chunkName: "nuxt-schema-org/components",
filePath: "@unhead/schema-org/vue"
});
}
addImports({
from: resolve("./runtime/app/composables/useSchemaOrg"),
name: "useSchemaOrg"
});
nuxt.hooks.hook("imports:sources", (autoImports) => {
schemaOrgAutoImports[0].imports = schemaOrgAutoImports[0].imports.filter((i) => i !== "useSchemaOrg");
autoImports.unshift(...schemaOrgAutoImports);
});
extendTypes("nuxt-schema-org", ({ typesPath }) => {
return `
declare module '@nuxt/schema' {
export interface RuntimeNuxtHooks {
'schema-org:meta': (meta: import('${typesPath}').MetaInput) => void | Promise<void>
}
}
declare module '#app' {
export interface RuntimeNuxtHooks {
'schema-org:meta': (meta: import('${typesPath}').MetaInput) => void | Promise<void>
}
}
`;
});
if (config.debug || nuxt.options.dev) {
addServerHandler({
route: "/__schema-org__/debug.json",
handler: resolve("./runtime/server/routes/__schema-org__/debug")
});
}
if (nuxt.options.dev)
setupDevToolsUI(config, resolve);
}
});
export { module as default };