nuxtseo-shared 5.2.6 → 5.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/devtools.mjs +133 -19
- package/dist/module.json +1 -1
- package/package.json +9 -8
package/dist/devtools.mjs
CHANGED
|
@@ -3,8 +3,9 @@ import { existsSync, readFileSync, readdirSync, mkdirSync, writeFileSync } from
|
|
|
3
3
|
import { createRequire } from 'node:module';
|
|
4
4
|
import { join, dirname } from 'node:path';
|
|
5
5
|
import process from 'node:process';
|
|
6
|
-
import { onDevToolsInitialized, extendServerRpc, addCustomTab } from '@nuxt/devtools-kit';
|
|
6
|
+
import { onDevToolsInitialized, extendServerRpc, addCustomTab, startSubprocess } from '@nuxt/devtools-kit';
|
|
7
7
|
import { useNuxt } from '@nuxt/kit';
|
|
8
|
+
import { detectPackageManager } from 'nypm';
|
|
8
9
|
import sirv from 'sirv';
|
|
9
10
|
import { modules } from './const.mjs';
|
|
10
11
|
import { detectNuxtSeoModules } from './kit.mjs';
|
|
@@ -17,22 +18,44 @@ function npmForSlug(slug) {
|
|
|
17
18
|
return modules.find((m) => m.slug === slug)?.npm;
|
|
18
19
|
}
|
|
19
20
|
const UNIFIED_CLIENT_ROUTE = "/__nuxt-seo-devtools";
|
|
21
|
+
const TOOLCHAIN_PACKAGE = "nuxtseo-layer-devtools";
|
|
20
22
|
const hashSet = (arr) => [...JSON.stringify(arr)].reduce((h, c) => h * 31 + c.charCodeAt(0) >>> 0, 7).toString(36);
|
|
21
23
|
function placeholderHtml() {
|
|
22
24
|
return `<!doctype html><html><head><meta charset="utf-8"><title>Nuxt SEO DevTools</title>
|
|
23
|
-
<style>html,body{margin:0;height:100%;font-family:'Hubot Sans',system-ui,sans-serif;background:oklch(98.4% 0.005 292);color:oklch(16% 0.036 292)}@media(prefers-color-scheme:dark){html,body{background:oklch(11% 0.029 292);color:oklch(96.8% 0.009 292)}}.wrap{height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:14px;padding:24px;text-align:center}.spin{width:34px;height:34px;border-radius:50%;border:3px solid color-mix(in oklab,oklch(54% 0.225 292) 25%,transparent);border-top-color:oklch(54% 0.225 292);animation:s .8s linear infinite}@keyframes s{to{transform:rotate(360deg)}}h1{font-size:15px;font-weight:600;margin:0}p{font-size:13px;opacity:.6;margin:0}.mods{display:flex;flex-wrap:wrap;gap:6px;justify-content:center;max-width:420px}.chip{font-size:11px;padding:2px 9px;border-radius:999px;background:color-mix(in oklab,oklch(54% 0.225 292) 14%,transparent);color:oklch(54% 0.225 292)}.step{font-size:12px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;opacity:.55;max-width:520px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-height:16px}.t{font-variant-numeric:tabular-nums;opacity:.85}.err .spin{border-top-color:oklch(60% 0.2 25);border-color:color-mix(in oklab,oklch(60% 0.2 25) 25%,transparent);animation:none}</style></head>
|
|
24
|
-
<body><div class="wrap" id="wrap"><div class="spin"></div><h1 id="title">
|
|
25
|
+
<style>html,body{margin:0;height:100%;font-family:'Hubot Sans',system-ui,sans-serif;background:oklch(98.4% 0.005 292);color:oklch(16% 0.036 292)}@media(prefers-color-scheme:dark){html,body{background:oklch(11% 0.029 292);color:oklch(96.8% 0.009 292)}}.wrap{height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:14px;padding:24px;text-align:center}.spin{width:34px;height:34px;border-radius:50%;border:3px solid color-mix(in oklab,oklch(54% 0.225 292) 25%,transparent);border-top-color:oklch(54% 0.225 292);animation:s .8s linear infinite}@keyframes s{to{transform:rotate(360deg)}}h1{font-size:15px;font-weight:600;margin:0}p{font-size:13px;opacity:.6;margin:0;max-width:440px}.mods{display:flex;flex-wrap:wrap;gap:6px;justify-content:center;max-width:420px}.chip{font-size:11px;padding:2px 9px;border-radius:999px;background:color-mix(in oklab,oklch(54% 0.225 292) 14%,transparent);color:oklch(54% 0.225 292)}.btn{font:inherit;font-size:13px;font-weight:600;padding:7px 16px;border-radius:8px;border:0;background:oklch(54% 0.225 292);color:#fff;cursor:pointer}.btn:hover{background:oklch(49% 0.225 292)}.btn:disabled{opacity:.6;cursor:default}.hide{display:none}.step{font-size:12px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;opacity:.55;max-width:520px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-height:16px}.t{font-variant-numeric:tabular-nums;opacity:.85}.err .spin{border-top-color:oklch(60% 0.2 25);border-color:color-mix(in oklab,oklch(60% 0.2 25) 25%,transparent);animation:none}</style></head>
|
|
26
|
+
<body><div class="wrap" id="wrap"><div class="spin" id="spin"></div><h1 id="title">Nuxt SEO DevTools</h1><div class="mods" id="mods"></div><p id="desc">Starting\u2026</p><button class="btn hide" id="install">Install</button><div class="step" id="step"></div></div>
|
|
25
27
|
<script>
|
|
26
28
|
const $=id=>document.getElementById(id)
|
|
27
|
-
|
|
29
|
+
const esc=s=>(s||'').replace(/[<>&]/g,c=>({'<':'<','>':'>','&':'&'}[c]))
|
|
30
|
+
let mods=false,clicked=false
|
|
31
|
+
$('install').addEventListener('click',async()=>{
|
|
32
|
+
clicked=true
|
|
33
|
+
$('install').disabled=true;$('install').classList.add('hide')
|
|
34
|
+
$('spin').classList.remove('hide')
|
|
35
|
+
$('title').textContent='Installing\u2026'
|
|
36
|
+
$('desc').textContent='Adding the dev-only DevTools UI to your project.'
|
|
37
|
+
try{await fetch('${UNIFIED_CLIENT_ROUTE}/__install')}catch{}
|
|
38
|
+
})
|
|
28
39
|
async function poll(){
|
|
29
40
|
try{
|
|
30
41
|
const j=await (await fetch('${UNIFIED_CLIENT_ROUTE}/__status')).json()
|
|
31
42
|
if(j.ready){location.reload();return}
|
|
32
|
-
if(!mods&&Array.isArray(j.modules)&&j.modules.length){mods=true;$('mods').innerHTML=j.modules.map(m=>'<span class="chip">'+m+'</span>').join('')}
|
|
33
|
-
if(j.failed){$('wrap').classList.add('err');$('title').textContent='DevTools
|
|
43
|
+
if(!mods&&Array.isArray(j.modules)&&j.modules.length){mods=true;$('mods').innerHTML=j.modules.map(m=>'<span class="chip">'+esc(m)+'</span>').join('')}
|
|
44
|
+
if(j.failed){$('wrap').classList.add('err');$('spin').classList.remove('hide');$('install').classList.add('hide');$('title').textContent='DevTools unavailable';$('desc').textContent='';$('step').textContent=j.step||'';return}
|
|
45
|
+
if(j.needsInstall&&!clicked){
|
|
46
|
+
const pkg=j.packageName||'nuxtseo-devtools'
|
|
47
|
+
$('spin').classList.add('hide')
|
|
48
|
+
$('install').classList.remove('hide');$('install').disabled=false;$('install').textContent='Install '+pkg
|
|
49
|
+
$('title').textContent='Nuxt SEO DevTools'
|
|
50
|
+
$('desc').textContent='The panel needs '+pkg+' (dev only). Installing it adds the package to your devDependencies; nothing is changed until you choose to install.'
|
|
51
|
+
$('step').textContent=''
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
$('spin').classList.remove('hide');$('install').classList.add('hide')
|
|
55
|
+
$('title').textContent=j.installing?'Installing\u2026':'Building Nuxt SEO DevTools\u2026'
|
|
56
|
+
$('desc').textContent=j.installing?'Adding the dev-only DevTools UI to your project.':'Assembling panels for your installed modules. This runs once.'
|
|
34
57
|
const t=j.elapsed?' \xB7 <span class="t">'+Math.round(j.elapsed/1000)+'s</span>':''
|
|
35
|
-
$('step').innerHTML=(j.step
|
|
58
|
+
$('step').innerHTML=esc(j.step)+t
|
|
36
59
|
}catch{}
|
|
37
60
|
}
|
|
38
61
|
setInterval(poll,800);poll()
|
|
@@ -109,6 +132,44 @@ function resolveNuxtCli(rootDir) {
|
|
|
109
132
|
return null;
|
|
110
133
|
}
|
|
111
134
|
const stripAnsi = (s) => s.replace(new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g"), "");
|
|
135
|
+
function toolchainInstalled(rootDir) {
|
|
136
|
+
try {
|
|
137
|
+
createRequire(join(rootDir, "index.js")).resolve(TOOLCHAIN_PACKAGE);
|
|
138
|
+
return true;
|
|
139
|
+
} catch {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
async function devAddArgs(rootDir) {
|
|
144
|
+
const pm = await detectPackageManager(rootDir).catch(() => null);
|
|
145
|
+
const name = pm?.name ?? "npm";
|
|
146
|
+
if (name === "npm")
|
|
147
|
+
return { command: "npm", args: ["install", "--save-dev", TOOLCHAIN_PACKAGE] };
|
|
148
|
+
if (name === "yarn")
|
|
149
|
+
return { command: "yarn", args: ["add", "--dev", TOOLCHAIN_PACKAGE] };
|
|
150
|
+
return { command: name, args: ["add", "-D", TOOLCHAIN_PACKAGE] };
|
|
151
|
+
}
|
|
152
|
+
async function installToolchain(rootDir, nuxt, onProgress) {
|
|
153
|
+
const { command, args } = await devAddArgs(rootDir);
|
|
154
|
+
onProgress(`Installing ${TOOLCHAIN_PACKAGE} with ${command}\u2026`);
|
|
155
|
+
return await new Promise((resolve) => {
|
|
156
|
+
const { getProcess } = startSubprocess(
|
|
157
|
+
{ command, args, cwd: rootDir },
|
|
158
|
+
{ id: "nuxt-seo:install-devtools", name: `Install ${TOOLCHAIN_PACKAGE}`, icon: "carbon:download" },
|
|
159
|
+
nuxt
|
|
160
|
+
);
|
|
161
|
+
const proc = getProcess();
|
|
162
|
+
if (!proc) {
|
|
163
|
+
resolve(false);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
proc.on("exit", (code) => resolve(code === 0 && toolchainInstalled(rootDir)));
|
|
167
|
+
proc.on("error", (err) => {
|
|
168
|
+
console.error(`[nuxt-seo] could not run "${command} ${args.join(" ")}": ${err.message}`);
|
|
169
|
+
resolve(false);
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
}
|
|
112
173
|
function generateAndBuild(cacheDir, rootDir, installed, hooks) {
|
|
113
174
|
const routes = ["/", ...installed.flatMap((m) => deriveRoutes(m.layerDir, m.slug))];
|
|
114
175
|
const extendsList = [resolveBaseLayer(installed), ...installed.map((m) => m.layerDir)];
|
|
@@ -133,24 +194,38 @@ export default defineNuxtConfig({
|
|
|
133
194
|
const cliBin = resolveNuxtCli(rootDir);
|
|
134
195
|
const child = cliBin ? spawn(process.execPath, [cliBin, "build"], { cwd: cacheDir, stdio: ["inherit", "pipe", "pipe"] }) : spawn("npx", ["nuxi", "build"], { cwd: cacheDir, stdio: ["inherit", "pipe", "pipe"], shell: true });
|
|
135
196
|
const statusLine = /^(?:[ℹ✔✓✨⚠✖✗●➜√]|\[\w)/;
|
|
136
|
-
const
|
|
137
|
-
|
|
197
|
+
const buffered = [];
|
|
198
|
+
let drawing = false;
|
|
199
|
+
const capture = (chunk) => {
|
|
200
|
+
buffered.push(chunk);
|
|
138
201
|
const step = stripAnsi(chunk.toString()).split("\n").map((l) => l.trim()).filter((l) => statusLine.test(l)).pop();
|
|
139
|
-
if (step)
|
|
202
|
+
if (step) {
|
|
140
203
|
hooks.onProgress(step);
|
|
204
|
+
process.stdout.write(`\r\x1B[2K[nuxt-seo] ${step}`);
|
|
205
|
+
drawing = true;
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
const endLine = () => {
|
|
209
|
+
if (drawing) {
|
|
210
|
+
process.stdout.write("\n");
|
|
211
|
+
drawing = false;
|
|
212
|
+
}
|
|
141
213
|
};
|
|
142
|
-
child.stdout?.on("data",
|
|
143
|
-
child.stderr?.on("data",
|
|
214
|
+
child.stdout?.on("data", capture);
|
|
215
|
+
child.stderr?.on("data", capture);
|
|
144
216
|
child.on("error", (err) => {
|
|
217
|
+
endLine();
|
|
145
218
|
console.error(`[nuxt-seo] could not build devtools client, the panel will stay unavailable: ${err.message}`);
|
|
146
219
|
hooks.onError();
|
|
147
220
|
});
|
|
148
221
|
child.on("exit", (code) => {
|
|
222
|
+
endLine();
|
|
149
223
|
if (code === 0) {
|
|
150
224
|
writeFileSync(join(cacheDir, ".installed-hash"), hashSet(installed.map((m) => m.slug).sort()));
|
|
151
225
|
hooks.onReady();
|
|
152
226
|
console.log("[nuxt-seo] devtools client ready");
|
|
153
227
|
} else {
|
|
228
|
+
process.stderr.write(Buffer.concat(buffered));
|
|
154
229
|
console.error(`[nuxt-seo] devtools client build exited with code ${code}, the panel will stay unavailable`);
|
|
155
230
|
hooks.onError();
|
|
156
231
|
}
|
|
@@ -169,7 +244,8 @@ function setupLayerModule(config, layerDir, nuxt) {
|
|
|
169
244
|
nuxt._seoDevtoolsInit = true;
|
|
170
245
|
const cacheDir = join(nuxt.options.rootDir, "node_modules/.cache/nuxt-seo-devtools");
|
|
171
246
|
const dist = join(cacheDir, "dist/devtools");
|
|
172
|
-
const state = { ready: false, building: false, failed: false, startedAt: 0, step: "", modules: [] };
|
|
247
|
+
const state = { ready: false, building: false, installing: false, needsInstall: false, failed: false, startedAt: 0, step: "", modules: [] };
|
|
248
|
+
const rootDir = nuxt.options.rootDir;
|
|
173
249
|
function ensureBuilt() {
|
|
174
250
|
if (state.ready || state.building)
|
|
175
251
|
return;
|
|
@@ -179,7 +255,7 @@ function setupLayerModule(config, layerDir, nuxt) {
|
|
|
179
255
|
state.step = "Starting build\u2026";
|
|
180
256
|
const installed = nuxt._seoDevtoolsLayers;
|
|
181
257
|
state.modules = installed.map((m) => m.title);
|
|
182
|
-
generateAndBuild(cacheDir,
|
|
258
|
+
generateAndBuild(cacheDir, rootDir, installed, {
|
|
183
259
|
onProgress: (step) => {
|
|
184
260
|
state.step = step;
|
|
185
261
|
},
|
|
@@ -187,13 +263,38 @@ function setupLayerModule(config, layerDir, nuxt) {
|
|
|
187
263
|
state.ready = true;
|
|
188
264
|
state.building = false;
|
|
189
265
|
},
|
|
190
|
-
onError: () => {
|
|
266
|
+
onError: (message) => {
|
|
191
267
|
state.building = false;
|
|
192
268
|
state.failed = true;
|
|
193
|
-
state.step = "Build failed, see the dev server logs";
|
|
269
|
+
state.step = message ?? "Build failed, see the dev server logs";
|
|
194
270
|
}
|
|
195
271
|
});
|
|
196
272
|
}
|
|
273
|
+
function startInstall() {
|
|
274
|
+
if (state.installing || state.building || state.ready)
|
|
275
|
+
return;
|
|
276
|
+
state.installing = true;
|
|
277
|
+
state.needsInstall = false;
|
|
278
|
+
state.failed = false;
|
|
279
|
+
state.startedAt = Date.now();
|
|
280
|
+
state.step = `Installing ${TOOLCHAIN_PACKAGE}\u2026`;
|
|
281
|
+
installToolchain(rootDir, nuxt, (step) => {
|
|
282
|
+
state.step = step;
|
|
283
|
+
}).then((ok) => {
|
|
284
|
+
state.installing = false;
|
|
285
|
+
if (ok) {
|
|
286
|
+
ensureBuilt();
|
|
287
|
+
} else {
|
|
288
|
+
state.failed = true;
|
|
289
|
+
state.step = `Could not install ${TOOLCHAIN_PACKAGE} \u2014 see the dev server logs`;
|
|
290
|
+
}
|
|
291
|
+
}).catch((err) => {
|
|
292
|
+
state.installing = false;
|
|
293
|
+
state.failed = true;
|
|
294
|
+
state.step = `Could not install ${TOOLCHAIN_PACKAGE} \u2014 see the dev server logs`;
|
|
295
|
+
console.error(`[nuxt-seo] devtools toolchain install failed: ${err.message}`);
|
|
296
|
+
});
|
|
297
|
+
}
|
|
197
298
|
nuxt.hook("modules:done", () => {
|
|
198
299
|
const installed = nuxt._seoDevtoolsLayers;
|
|
199
300
|
const key = hashSet(installed.map((m) => m.slug).sort());
|
|
@@ -202,19 +303,32 @@ function setupLayerModule(config, layerDir, nuxt) {
|
|
|
202
303
|
nuxt.hook("vite:serverCreated", (server) => {
|
|
203
304
|
const serve = sirv(dist, { dev: true, single: "200.html" });
|
|
204
305
|
server.middlewares.use(UNIFIED_CLIENT_ROUTE, (req, res, next) => {
|
|
205
|
-
|
|
206
|
-
|
|
306
|
+
const url = req.url || "/";
|
|
307
|
+
if (url.startsWith("/__install")) {
|
|
308
|
+
startInstall();
|
|
309
|
+
res.setHeader("content-type", "application/json");
|
|
310
|
+
return res.end(JSON.stringify({ installing: state.installing }));
|
|
311
|
+
}
|
|
312
|
+
if (url.startsWith("/__status")) {
|
|
313
|
+
if (!state.ready && !state.building && !state.installing) {
|
|
314
|
+
if (toolchainInstalled(rootDir))
|
|
315
|
+
ensureBuilt();
|
|
316
|
+
else
|
|
317
|
+
state.needsInstall = true;
|
|
318
|
+
}
|
|
207
319
|
res.setHeader("content-type", "application/json");
|
|
208
320
|
return res.end(JSON.stringify({
|
|
209
321
|
ready: state.ready,
|
|
210
322
|
failed: state.failed,
|
|
323
|
+
needsInstall: state.needsInstall,
|
|
324
|
+
installing: state.installing,
|
|
325
|
+
packageName: TOOLCHAIN_PACKAGE,
|
|
211
326
|
step: state.step,
|
|
212
327
|
modules: state.modules,
|
|
213
328
|
elapsed: state.startedAt ? Date.now() - state.startedAt : 0
|
|
214
329
|
}));
|
|
215
330
|
}
|
|
216
331
|
if (!state.ready) {
|
|
217
|
-
ensureBuilt();
|
|
218
332
|
res.setHeader("content-type", "text/html");
|
|
219
333
|
return res.end(placeholderHtml());
|
|
220
334
|
}
|
package/dist/module.json
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuxtseo-shared",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "5.
|
|
4
|
+
"version": "5.3.1",
|
|
5
5
|
"description": "Shared utilities for Nuxt SEO modules.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Harlan Wilton",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"@nuxt/schema": "^3.16.0 || ^4.0.0",
|
|
64
64
|
"nuxt": "^3.16.0 || ^4.0.0",
|
|
65
65
|
"nuxt-site-config": "^3.2.0 || ^4.0.0",
|
|
66
|
-
"vue": "^3.5.
|
|
66
|
+
"vue": "^3.5.38",
|
|
67
67
|
"zod": "^3.23.0 || ^4.0.0"
|
|
68
68
|
},
|
|
69
69
|
"peerDependenciesMeta": {
|
|
@@ -75,12 +75,13 @@
|
|
|
75
75
|
}
|
|
76
76
|
},
|
|
77
77
|
"dependencies": {
|
|
78
|
-
"@clack/prompts": "^1.
|
|
78
|
+
"@clack/prompts": "^1.6.0",
|
|
79
79
|
"@nuxt/devtools-kit": "4.0.0-alpha.3",
|
|
80
80
|
"@nuxt/kit": "^4.4.8",
|
|
81
81
|
"birpc": "^4.0.0",
|
|
82
82
|
"consola": "^3.4.2",
|
|
83
83
|
"defu": "^6.1.7",
|
|
84
|
+
"nypm": "^0.6.7",
|
|
84
85
|
"ofetch": "^1.5.1",
|
|
85
86
|
"pathe": "^2.0.3",
|
|
86
87
|
"pkg-types": "^2.3.1",
|
|
@@ -90,17 +91,17 @@
|
|
|
90
91
|
"ufo": "^1.6.4"
|
|
91
92
|
},
|
|
92
93
|
"devDependencies": {
|
|
93
|
-
"@arethetypeswrong/cli": "^0.18.
|
|
94
|
+
"@arethetypeswrong/cli": "^0.18.4",
|
|
94
95
|
"@nuxt/module-builder": "^1.0.2",
|
|
95
96
|
"@nuxt/schema": "^4.4.8",
|
|
96
97
|
"@nuxtjs/i18n": "^10.4.0",
|
|
97
98
|
"nitropack": "^2.13.4",
|
|
98
99
|
"nuxt": "^4.4.8",
|
|
99
|
-
"nuxt-site-config": "^4.0
|
|
100
|
+
"nuxt-site-config": "^4.1.0",
|
|
100
101
|
"typescript": "^6.0.3",
|
|
101
|
-
"vitest": "^4.1.
|
|
102
|
-
"vue": "^3.5.
|
|
103
|
-
"vue-tsc": "^3.3.
|
|
102
|
+
"vitest": "^4.1.9",
|
|
103
|
+
"vue": "^3.5.38",
|
|
104
|
+
"vue-tsc": "^3.3.5"
|
|
104
105
|
},
|
|
105
106
|
"scripts": {
|
|
106
107
|
"build": "nuxt-module-build build",
|