@nuxt/scripts 0.5.1 → 0.6.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.
Files changed (68) hide show
  1. package/README.md +2 -2
  2. package/dist/client/200.html +19 -16
  3. package/dist/client/404.html +19 -16
  4. package/dist/client/_nuxt/B474tPdy.js +66 -0
  5. package/dist/client/_nuxt/{DMNuDhJC.js → BN0jYG70.js} +1 -1
  6. package/dist/client/_nuxt/Buoqr_Oi.js +2 -0
  7. package/dist/client/_nuxt/C2pTd85Q.js +1 -0
  8. package/dist/client/_nuxt/{DCjyzNN-.js → D4FSFK-1.js} +1 -1
  9. package/dist/client/_nuxt/DAP_O-zg.js +1 -0
  10. package/dist/client/_nuxt/{o8H-iTt6.js → DG8tVHNj.js} +1 -1
  11. package/dist/client/_nuxt/DKGhHdqV.js +31 -0
  12. package/dist/client/_nuxt/DVC_qXWv.js +1 -0
  13. package/dist/client/_nuxt/{NL97_oaV.js → GPY1wEXZ.js} +1 -1
  14. package/dist/client/_nuxt/RyEv5ndP.js +1 -0
  15. package/dist/client/_nuxt/VQMr7AbF.js +1 -0
  16. package/dist/client/_nuxt/builds/latest.json +1 -1
  17. package/dist/client/_nuxt/builds/meta/c409c2fe-d448-45a5-84ca-3e109a1e08ad.json +1 -0
  18. package/dist/client/_nuxt/cW9vKj3g.js +1 -0
  19. package/dist/client/_nuxt/entry.DvGwvmL9.css +1 -0
  20. package/dist/client/_nuxt/error-404.DXyehy0d.css +1 -0
  21. package/dist/client/_nuxt/error-500.a_92Fvyl.css +1 -0
  22. package/dist/client/index.html +19 -16
  23. package/dist/module.d.mts +3 -3
  24. package/dist/module.d.ts +3 -3
  25. package/dist/module.json +2 -2
  26. package/dist/module.mjs +191 -59
  27. package/dist/registry.mjs +15 -2
  28. package/dist/runtime/components/ScriptCarbonAds.vue +5 -4
  29. package/dist/runtime/components/ScriptCrisp.vue +84 -0
  30. package/dist/runtime/components/ScriptGoogleAdsense.vue +23 -4
  31. package/dist/runtime/components/ScriptGoogleMaps.vue +15 -14
  32. package/dist/runtime/components/ScriptIntercom.vue +93 -0
  33. package/dist/runtime/components/ScriptLemonSqueezy.vue +45 -0
  34. package/dist/runtime/components/ScriptLoadingIndicator.vue +12 -3
  35. package/dist/runtime/components/ScriptStripePricingTable.vue +30 -18
  36. package/dist/runtime/components/ScriptVimeoPlayer.vue +13 -3
  37. package/dist/runtime/components/ScriptYouTubePlayer.vue +13 -3
  38. package/dist/runtime/composables/useScript.js +6 -1
  39. package/dist/runtime/composables/useScriptEventPage.d.ts +5 -0
  40. package/dist/runtime/composables/{useAnalyticsPageEvent.js → useScriptEventPage.js} +1 -1
  41. package/dist/runtime/composables/{useConsentScriptTrigger.d.ts → useScriptTriggerConsent.d.ts} +1 -1
  42. package/dist/runtime/composables/{useConsentScriptTrigger.js → useScriptTriggerConsent.js} +1 -1
  43. package/dist/runtime/composables/{useElementScriptTrigger.d.ts → useScriptTriggerElement.d.ts} +1 -1
  44. package/dist/runtime/composables/{useElementScriptTrigger.js → useScriptTriggerElement.js} +5 -4
  45. package/dist/runtime/registry/crisp.d.ts +62 -0
  46. package/dist/runtime/registry/crisp.js +76 -0
  47. package/dist/runtime/registry/google-adsense.d.ts +1 -1
  48. package/dist/runtime/registry/intercom.d.ts +1 -1
  49. package/dist/runtime/registry/lemon-squeezy.d.ts +19 -12
  50. package/dist/runtime/registry/stripe.d.ts +0 -1
  51. package/dist/runtime/registry/vimeo-player.d.ts +1 -1
  52. package/dist/runtime/registry/youtube-player.d.ts +0 -1
  53. package/dist/runtime/types.d.ts +14 -8
  54. package/dist/runtime/utils.js +2 -1
  55. package/dist/types.d.mts +2 -10
  56. package/dist/types.d.ts +2 -10
  57. package/package.json +36 -28
  58. package/dist/client/_nuxt/Bn0B8vkx.js +0 -1
  59. package/dist/client/_nuxt/BvlfuC5v.js +0 -31
  60. package/dist/client/_nuxt/Bx89rGXK.js +0 -1
  61. package/dist/client/_nuxt/CoEqzGFw.js +0 -44
  62. package/dist/client/_nuxt/builds/meta/b4275645-e497-405e-b7c0-87516033342e.json +0 -1
  63. package/dist/client/_nuxt/entry.8LvBfYHJ.css +0 -1
  64. package/dist/client/_nuxt/error-404.Dbi768NS.css +0 -1
  65. package/dist/client/_nuxt/error-500.C23YdqXl.css +0 -1
  66. package/dist/client/_nuxt/jmWmhCGR.js +0 -1
  67. package/dist/runtime/components/ScriptLemonSqueezyButton.vue +0 -41
  68. package/dist/runtime/composables/useAnalyticsPageEvent.d.ts +0 -2
package/dist/module.mjs CHANGED
@@ -1,8 +1,8 @@
1
- import { useNuxt, useLogger, addDevServerHandler, createResolver, addTemplate, tryUseNuxt, logger as logger$1, addImports, defineNuxtModule, addImportsDir, addComponentsDir, addPluginTemplate, addBuildPlugin, hasNuxtModule } from '@nuxt/kit';
1
+ import { useNuxt, useLogger, addDevServerHandler, createResolver, addTemplate, tryUseNuxt, logger as logger$1, addImports, defineNuxtModule, addBuildPlugin, addImportsDir, addComponentsDir, addPluginTemplate, hasNuxtModule } from '@nuxt/kit';
2
2
  import { resolvePackageJSON, readPackageJSON } from 'pkg-types';
3
3
  import { lt } from 'semver';
4
4
  import { resolvePath } from 'mlly';
5
- import { join, relative } from 'pathe';
5
+ import { join, relative, extname } from 'pathe';
6
6
  import { existsSync } from 'node:fs';
7
7
  import { pathToFileURL } from 'node:url';
8
8
  import { createUnplugin } from 'unplugin';
@@ -21,9 +21,12 @@ import { isCI, provider } from 'std-env';
21
21
  import { registry } from './registry.mjs';
22
22
  import { GooglaAnalyticsData, GoogleAnalytics, GoogleTagManagerData, GoogleTagManager } from 'third-party-capital';
23
23
  import { genTypeImport, genImport } from 'knitwork';
24
+ import { parse } from 'acorn';
25
+ import { transform } from 'esbuild';
24
26
 
25
27
  const DEVTOOLS_UI_ROUTE = "/__nuxt-scripts";
26
- const DEVTOOLS_UI_LOCAL_PORT = 3030;
28
+ const DEVTOOLS_UI_LOCAL_PORT = 3300;
29
+
27
30
  function setupDevToolsUI(options, resolve, nuxt = useNuxt()) {
28
31
  const clientPath = resolve("./client");
29
32
  const isProductionBuild = existsSync(clientPath);
@@ -87,7 +90,9 @@ function NuxtScriptBundleTransformer(options) {
87
90
  walk(ast, {
88
91
  enter(_node) {
89
92
  const calleeName = _node.callee?.name;
90
- const isValidCallee = calleeName === "useScript" || calleeName?.startsWith("useScript") && /^[A-Z]$/.test(calleeName?.charAt(9));
93
+ if (!calleeName)
94
+ return;
95
+ const isValidCallee = calleeName === "useScript" || calleeName?.startsWith("useScript") && /^[A-Z]$/.test(calleeName?.charAt(9)) && !calleeName.startsWith("useScriptTrigger") && !calleeName.startsWith("useScriptEvent");
91
96
  if (_node.type === "CallExpression" && _node.callee.type === "Identifier" && isValidCallee) {
92
97
  const fnName = _node.callee?.name;
93
98
  const node = _node;
@@ -357,6 +362,8 @@ function installNuxtModule(name, options) {
357
362
  }, { rootDir: nuxt.options.rootDir, searchPaths: nuxt.options.modulesDir, ...options });
358
363
  }
359
364
 
365
+ const HEAD_VAR = "__head";
366
+ const INJECTHEAD_CODE = `const ${HEAD_VAR} = injectHead()`;
360
367
  function getTpcScriptContent(input) {
361
368
  const nuxt = useNuxt();
362
369
  if (!input.data.scripts)
@@ -364,6 +371,7 @@ function getTpcScriptContent(input) {
364
371
  const mainScript = input.data.scripts?.find(({ key }) => key === input.tpcKey);
365
372
  if (!mainScript)
366
373
  throw new Error(`no main script found for ${input.tpcKey} in third-party-capital`);
374
+ const mainScriptOptions = getScriptInputOption(mainScript);
367
375
  const imports = /* @__PURE__ */ new Set([
368
376
  "import { withQuery } from 'ufo'",
369
377
  "import { useRegistryScript } from '#nuxt-scripts-utils'",
@@ -379,37 +387,26 @@ function getTpcScriptContent(input) {
379
387
  imports.add(genImport("#nuxt-scripts-validator", ["object", "any"]));
380
388
  chunks.push(`const OptionSchema = object({${mainScript.params?.map((p) => `${p}: any()`)}})`);
381
389
  }
382
- if (input.augmentWindowTypes) {
383
- chunks.push(`
390
+ chunks.push(`
384
391
  declare global {
385
392
  interface Window extends ${input.tpcTypeImport} {}
386
393
  }`);
387
- }
388
394
  const clientInitCode = [];
389
- const runtimeHead = {
390
- script: [],
391
- link: input.data.stylesheets?.map((s) => ({ ref: "stylesheet", href: s })) || []
392
- };
393
395
  if (input.data.stylesheets) {
394
- runtimeHead.link.push(...input.data.stylesheets.map((s) => ({ href: s, ref: "stylesheet" })));
396
+ if (!functionBody.includes(INJECTHEAD_CODE)) {
397
+ functionBody.unshift(INJECTHEAD_CODE);
398
+ }
399
+ functionBody.push(`${HEAD_VAR}.push({link: ${JSON.stringify(input.data.stylesheets.map((s) => ({ ref: "stylesheet", href: s })))}})`);
395
400
  }
396
401
  for (const script of input.data.scripts) {
397
402
  if ("code" in script)
398
403
  clientInitCode.push(replaceTokenToRuntime(script.code));
399
404
  if (script === mainScript)
400
405
  continue;
401
- if ("url" in script && script.url) {
402
- if (!runtimeHead.script)
403
- runtimeHead.script = [];
404
- runtimeHead.script.push({
405
- src: script.url,
406
- async: true
407
- });
406
+ if ("url" in script) {
407
+ functionBody.push(`${HEAD_VAR}.push({scripts:{ async: true, src: ${script.url} }},${JSON.stringify(getScriptInputOption(script))})`);
408
408
  }
409
409
  }
410
- if (runtimeHead.script.length || runtimeHead.link.length) {
411
- functionBody.push(`useHead(${JSON.stringify(runtimeHead)})`);
412
- }
413
410
  chunks.push(`export type Input = RegistryScriptInput${hasParams ? "<typeof OptionSchema>" : ""}`);
414
411
  chunks.push(`
415
412
  export function ${input.scriptFunctionName}<T extends ${input.tpcTypeImport}>(_options?: Input) {
@@ -421,7 +418,8 @@ ${functionBody.join("\n")}
421
418
  ${nuxt.options.dev ? "schema: OptionSchema," : ""}
422
419
  scriptOptions: {
423
420
  use: ${input.use.toString()},
424
- stub: import.meta.client ? undefined : ${input.stub.toString()}
421
+ stub: import.meta.client ? undefined : ${input.stub.toString()},
422
+ ${mainScriptOptions ? `...(${JSON.stringify(mainScriptOptions)})` : ""}
425
423
  },
426
424
  ${clientInitCode.length ? `clientInit: import.meta.server ? undefined : () => {${clientInitCode.join("\n")}},` : ""}
427
425
  }), _options)
@@ -432,6 +430,17 @@ ${functionBody.join("\n")}
432
430
  function replaceTokenToRuntime(code) {
433
431
  return code.split(";").map((c) => c.replaceAll(/'?\{\{(.*?)\}\}'?/g, "options.$1")).join(";");
434
432
  }
433
+ function getScriptInputOption(script) {
434
+ if (script.location === "body") {
435
+ if (script.action === "append") {
436
+ return { tagPosition: "bodyClose" };
437
+ }
438
+ return { tagPosition: "bodyOpen" };
439
+ }
440
+ if (script.action === "append") {
441
+ return { tagPriority: 1 };
442
+ }
443
+ }
435
444
 
436
445
  function googleAnalitycsRegistry() {
437
446
  const nuxt = useNuxt();
@@ -448,11 +457,11 @@ function googleAnalitycsRegistry() {
448
457
  return fn === "dataLayer" ? [] : void 0;
449
458
  },
450
459
  tpcKey: "gtag",
451
- tpcTypeImport: "GoogleAnalyticsApi",
452
- augmentWindowTypes: true
460
+ tpcTypeImport: "GoogleAnalyticsApi"
453
461
  });
454
462
  },
455
- filename: "nuxt-scripts/tpc/google-analytics.ts"
463
+ filename: "nuxt-scripts/tpc/google-analytics.ts",
464
+ write: true
456
465
  });
457
466
  addImports({
458
467
  from: dst,
@@ -493,11 +502,11 @@ function googleTagManagerRegistry() {
493
502
  return fn === "dataLayer" ? [] : void 0;
494
503
  },
495
504
  tpcKey: "gtm",
496
- tpcTypeImport: "GoogleTagManagerApi",
497
- augmentWindowTypes: true
505
+ tpcTypeImport: "GoogleTagManagerApi"
498
506
  });
499
507
  },
500
- filename: "nuxt-scripts/tpc/google-tag-manager.ts"
508
+ filename: "nuxt-scripts/tpc/google-tag-manager.ts",
509
+ write: true
501
510
  });
502
511
  addImports({
503
512
  from: dst,
@@ -524,6 +533,142 @@ function googleTagManagerRegistry() {
524
533
  });
525
534
  }
526
535
 
536
+ const SCRIPT_RE = /<script[^>]*>([\s\S]*)<\/script>/;
537
+ const checkScripts = () => createUnplugin(() => {
538
+ return {
539
+ name: "nuxt-scripts:check-scripts",
540
+ enforce: "pre",
541
+ async transform(code, id) {
542
+ if (!code.includes("useScript"))
543
+ return;
544
+ const extName = extname(id);
545
+ if (extName === ".vue") {
546
+ const scriptAst = await extractScriptContentAst(code);
547
+ if (scriptAst) {
548
+ analyzeNodes(id, scriptAst);
549
+ }
550
+ } else if (extName === ".ts" || extName === ".js") {
551
+ if (!code.includes("defineComponent"))
552
+ return;
553
+ let result = code;
554
+ if (extName === ".ts") {
555
+ result = (await transform(code, { loader: "ts" })).code;
556
+ }
557
+ const setupFunction = extractSetupFunction(result);
558
+ if (setupFunction) {
559
+ analyzeNodes(id, setupFunction);
560
+ }
561
+ }
562
+ return void 0;
563
+ }
564
+ };
565
+ });
566
+ function analyzeNodes(id, nodes) {
567
+ let name;
568
+ for (const node of nodes) {
569
+ if (name) {
570
+ if (isAwaitingLoad(name, node)) {
571
+ throw new Error("Awaiting load should not be used at top level of a composable or <script>");
572
+ }
573
+ } else {
574
+ if (node.type === "VariableDeclaration") {
575
+ name = findScriptVar(node.declarations[0]);
576
+ }
577
+ }
578
+ }
579
+ }
580
+ function findScriptVar(scriptDeclaration) {
581
+ if (scriptDeclaration.id.type === "ObjectPattern") {
582
+ for (const property of scriptDeclaration.id.properties) {
583
+ if (property.type === "Property" && property.key.type === "Identifier" && property.key.name === "$script" && property.value.type === "Identifier") {
584
+ return property.value.name;
585
+ }
586
+ }
587
+ } else if (scriptDeclaration.id.type === "Identifier") {
588
+ return scriptDeclaration.id.name;
589
+ }
590
+ }
591
+ function isAwaitingLoad(name, node) {
592
+ if (node.type === "ExpressionStatement" && node.expression.type === "AwaitExpression") {
593
+ const arg = node.expression.argument;
594
+ if (arg.type === "CallExpression") {
595
+ const callee = arg.callee;
596
+ if (callee.type === "MemberExpression" && callee.object.type === "Identifier" && callee.object.name === name) {
597
+ if (callee.property.type === "Identifier" && callee.property.name === "load") {
598
+ return true;
599
+ }
600
+ }
601
+ }
602
+ }
603
+ }
604
+ async function extractScriptContentAst(code) {
605
+ const scriptCode = code.match(SCRIPT_RE);
606
+ return scriptCode ? parse((await transform(scriptCode[1], { loader: "ts" })).code, {
607
+ ecmaVersion: "latest",
608
+ sourceType: "module"
609
+ }).body : void 0;
610
+ }
611
+ function extractSetupFunction(code) {
612
+ const ast = parse(code, {
613
+ ecmaVersion: "latest",
614
+ sourceType: "module"
615
+ });
616
+ const defaultExport = ast.body.find((node) => node.type === "ExportDefaultDeclaration");
617
+ if (defaultExport && defaultExport.declaration.type === "CallExpression" && defaultExport.declaration.callee.type === "Identifier" && defaultExport.declaration.callee.name === "defineComponent") {
618
+ const arg = defaultExport.declaration.arguments[0];
619
+ if (arg && arg.type === "ObjectExpression") {
620
+ const setupProperty = arg.properties.find((prop) => prop.type === "Property" && prop.key.type === "Identifier" && prop.key.name === "setup");
621
+ if (setupProperty) {
622
+ const setupValue = setupProperty.value;
623
+ if (setupValue.type === "FunctionExpression") {
624
+ return setupValue.body.body;
625
+ }
626
+ }
627
+ }
628
+ }
629
+ }
630
+
631
+ function templatePlugin(config, registry) {
632
+ if (Array.isArray(config.globals)) {
633
+ config.globals = Object.fromEntries(config.globals.map((i) => [hash(i), i]));
634
+ logger.warn("The `globals` array option is deprecated, please convert to an object.");
635
+ }
636
+ const imports = ["useScript", "defineNuxtPlugin"];
637
+ const inits = [];
638
+ for (const [k, c] of Object.entries(config.registry || {})) {
639
+ const importDefinition = registry.find((i) => i.import.name === `useScript${k.substring(0, 1).toUpperCase() + k.substring(1)}`);
640
+ if (importDefinition) {
641
+ imports.unshift(importDefinition.import.name);
642
+ const args = (typeof c !== "object" ? {} : c) || {};
643
+ if (c === "mock")
644
+ args.scriptOptions = { trigger: "manual", skipValidation: true };
645
+ inits.push(`const ${k} = ${importDefinition.import.name}(${JSON.stringify(args)})`);
646
+ }
647
+ }
648
+ for (const [k, c] of Object.entries(config.globals || {})) {
649
+ if (typeof c === "string") {
650
+ inits.push(`const ${k} = useScript(${JSON.stringify({ src: c, key: k })}, { use: () => ({ ${k}: window.${k} }) })`);
651
+ } else if (Array.isArray(c) && c.length === 2) {
652
+ inits.push(`const ${k} = useScript(${JSON.stringify({ key: k, ...typeof c[0] === "string" ? { src: c[0] } : c[0] })}, { ...${JSON.stringify(c[1])}, use: () => ({ ${k}: window.${k} } }) )`);
653
+ } else {
654
+ inits.push(`const ${k} = useScript(${JSON.stringify({ key: k, ...c })}, { use: () => ({ ${k}: window.${k} }) })`);
655
+ }
656
+ }
657
+ return [
658
+ `import { ${imports.join(", ")} } from '#imports'`,
659
+ "",
660
+ `export default defineNuxtPlugin({`,
661
+ ` name: "scripts:init",`,
662
+ ` env: { islands: false },`,
663
+ ` parallel: true,`,
664
+ ` setup() {`,
665
+ ...inits.map((i) => ` ${i}`),
666
+ ` return { provide: { $scripts: { ${[...Object.keys(config.globals || {}), ...Object.keys(config.registry || {})].join(", ")} } } }`,
667
+ ` }`,
668
+ `})`
669
+ ].join("\n");
670
+ }
671
+
527
672
  const module = defineNuxtModule({
528
673
  meta: {
529
674
  name: "@nuxt/scripts",
@@ -543,6 +688,10 @@ const module = defineNuxtModule({
543
688
  async setup(config, nuxt) {
544
689
  const { resolve } = createResolver(import.meta.url);
545
690
  const { version, name } = await readPackageJSON(resolve("../package.json"));
691
+ if (!config.enabled) {
692
+ logger.debug("The module is disabled, skipping setup.");
693
+ return;
694
+ }
546
695
  const unheadPath = await resolvePath("@unhead/vue").catch(() => void 0);
547
696
  if (unheadPath) {
548
697
  const { version: unheadVersion } = await readPackageJSON(join(unheadPath, "package.json"));
@@ -551,15 +700,16 @@ const module = defineNuxtModule({
551
700
  return;
552
701
  }
553
702
  }
554
- if (!config.enabled) {
555
- logger.debug("The module is disabled, skipping setup.");
556
- return;
557
- }
703
+ addBuildPlugin(checkScripts());
558
704
  nuxt.options.alias["#nuxt-scripts-validator"] = resolve(`./runtime/validation/${nuxt.options.dev || nuxt.options._prepare ? "valibot" : "mock"}`);
559
705
  nuxt.options.alias["#nuxt-scripts"] = resolve("./runtime/types");
560
706
  nuxt.options.alias["#nuxt-scripts-utils"] = resolve("./runtime/utils");
561
707
  nuxt.options.runtimeConfig["nuxt-scripts"] = { version };
562
- nuxt.options.runtimeConfig.public["nuxt-scripts"] = { defaultScriptOptions: config.defaultScriptOptions };
708
+ nuxt.options.runtimeConfig.public["nuxt-scripts"] = {
709
+ // expose for devtools
710
+ version: nuxt.options.dev ? version : void 0,
711
+ defaultScriptOptions: config.defaultScriptOptions
712
+ };
563
713
  addImportsDir([
564
714
  resolve("./runtime/composables"),
565
715
  // auto-imports aren't working without this for some reason
@@ -575,18 +725,19 @@ const module = defineNuxtModule({
575
725
  nuxt.hooks.hook("modules:done", async () => {
576
726
  const registryScripts = [...scripts];
577
727
  await nuxt.hooks.callHook("scripts:registry", registryScripts);
578
- const withComposables = registryScripts.filter((i) => !!i.import?.name);
579
- addImports(withComposables.map((i) => {
728
+ const registryScriptsWithImport = registryScripts.filter((i) => !!i.import?.name);
729
+ addImports(registryScriptsWithImport.map((i) => {
580
730
  return {
581
731
  priority: -1,
582
732
  ...i.import
583
733
  };
584
734
  }));
585
- const newScripts = withComposables.filter((i) => !scripts.some((r) => r.import?.name === i.import.name));
735
+ const newScripts = registryScriptsWithImport.filter((i) => !scripts.some((r) => r.import?.name === i.import.name));
586
736
  extendTypes(name, async ({ typesPath }) => {
587
737
  let types = `
588
738
  declare module '#app' {
589
739
  interface NuxtApp {
740
+ $scripts: Record<${[...Object.keys(config.globals || {}), ...Object.keys(config.registry || {})].map((k) => `'${k}'`).concat(["string"]).join(" | ")}, Pick<(import('#nuxt-scripts').NuxtAppScript), '$script'> & Record<string, any>>
590
741
  _scripts: Record<string, (import('#nuxt-scripts').NuxtAppScript)>
591
742
  }
592
743
  interface RuntimeNuxtHooks {
@@ -610,30 +761,11 @@ ${newScripts.map((i) => {
610
761
  }
611
762
  return types;
612
763
  });
613
- if (config.globals?.length || Object.keys(config.registry || {}).length) {
764
+ if (Object.keys(config.globals || {}).length || Object.keys(config.registry || {}).length) {
614
765
  addPluginTemplate({
615
766
  filename: `modules/${name.replace("/", "-")}.mjs`,
616
767
  getContents() {
617
- const imports = ["useScript", "defineNuxtPlugin"];
618
- const inits = [];
619
- for (const [k, c] of Object.entries(config.registry || {})) {
620
- const importDefinition = withComposables.find((i) => i.import.name === `useScript${k.substring(0, 1).toUpperCase() + k.substring(1)}`);
621
- if (importDefinition) {
622
- imports.unshift(importDefinition.import.name);
623
- const args = (typeof c !== "object" ? {} : c) || {};
624
- if (c === "mock")
625
- args.scriptOptions = { trigger: "manual", skipValidation: true };
626
- inits.push(`${importDefinition.import.name}(${JSON.stringify(args)});`);
627
- }
628
- }
629
- return `import { ${imports.join(", ")} } from '#imports'
630
- export default defineNuxtPlugin({
631
- name: "${name}:init",
632
- setup() {
633
- ${(config.globals || []).map((g) => !Array.isArray(g) ? ` useScript("${g.toString()}")` : g.length === 2 ? ` useScript(${JSON.stringify(g[0])}, ${JSON.stringify(g[1])} })` : ` useScript(${JSON.stringify(g[0])})`).join("\n")}
634
- ${inits.join("\n ")}
635
- }
636
- })`;
768
+ return templatePlugin(config, registryScriptsWithImport);
637
769
  }
638
770
  });
639
771
  }
@@ -641,7 +773,7 @@ ${(config.globals || []).map((g) => !Array.isArray(g) ? ` useScript("${g.toSt
641
773
  const { normalizeScriptData } = setupPublicAssetStrategy(config.assets);
642
774
  const moduleInstallPromises = /* @__PURE__ */ new Map();
643
775
  addBuildPlugin(NuxtScriptBundleTransformer({
644
- scripts: withComposables,
776
+ scripts: registryScriptsWithImport,
645
777
  defaultBundle: config.defaultScriptOptions?.bundle,
646
778
  moduleDetected(module) {
647
779
  if (nuxt.options.dev && module !== "@nuxt/scripts" && !moduleInstallPromises.has(module) && !hasNuxtModule(module))
package/dist/registry.mjs CHANGED
@@ -110,8 +110,8 @@ const registry = (resolve) => {
110
110
  scriptBundling(options) {
111
111
  return joinURL(`https://widget.intercom.io/widget`, options?.app_id || "");
112
112
  },
113
- logo: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8.968 8.972" width="64" height="64"><path d="M7.853 0h-6.73C.496 0-.002.498-.002 1.117v6.73a1.12 1.12 0 0 0 1.126 1.126h6.73c.618 0 1.117-.498 1.117-1.117v-6.73A1.119 1.119 0 0 0 7.853 0zM5.68 1.645c0-.17.13-.3.3-.3s.3.13.3.3v3.998c0 .17-.13.3-.3.3s-.3-.13-.3-.3zm-1.495-.15c0-.17.13-.3.3-.3s.3.13.3.3v4.336c0 .17-.13.3-.3.3s-.3-.13-.3-.3zm-1.495.15c0-.17.13-.3.3-.3s.3.13.3.3v3.998c0 .17-.13.3-.3.3s-.3-.13-.3-.3zm-1.495.598c0-.17.13-.3.3-.3s.3.13.3.3v2.692c0 .17-.13.3-.3.3s-.3-.13-.3-.3zm6.48 4.566c-.05.04-1.156.967-3.2.967s-3.14-.927-3.2-.967a.29.29 0 0 1-.03-.419.29.29 0 0 1 .419-.03c.02 0 1.007.817 2.8.817 1.814 0 2.79-.817 2.79-.827.13-.1.32-.1.42.03a.3.3 0 0 1-.02.429zm.1-1.874c0 .17-.13.3-.3.3s-.3-.13-.3-.3V2.243c0-.17.13-.3.3-.3s.3.13.3.3z" fill="#1f8ded"/></svg>`,
114
- category: "marketing",
113
+ logo: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8.968 8.972"><path d="M7.853 0h-6.73C.496 0-.002.498-.002 1.117v6.73a1.12 1.12 0 0 0 1.126 1.126h6.73c.618 0 1.117-.498 1.117-1.117v-6.73A1.119 1.119 0 0 0 7.853 0zM5.68 1.645c0-.17.13-.3.3-.3s.3.13.3.3v3.998c0 .17-.13.3-.3.3s-.3-.13-.3-.3zm-1.495-.15c0-.17.13-.3.3-.3s.3.13.3.3v4.336c0 .17-.13.3-.3.3s-.3-.13-.3-.3zm-1.495.15c0-.17.13-.3.3-.3s.3.13.3.3v3.998c0 .17-.13.3-.3.3s-.3-.13-.3-.3zm-1.495.598c0-.17.13-.3.3-.3s.3.13.3.3v2.692c0 .17-.13.3-.3.3s-.3-.13-.3-.3zm6.48 4.566c-.05.04-1.156.967-3.2.967s-3.14-.927-3.2-.967a.29.29 0 0 1-.03-.419.29.29 0 0 1 .419-.03c.02 0 1.007.817 2.8.817 1.814 0 2.79-.817 2.79-.827.13-.1.32-.1.42.03a.3.3 0 0 1-.02.429zm.1-1.874c0 .17-.13.3-.3.3s-.3-.13-.3-.3V2.243c0-.17.13-.3.3-.3s.3.13.3.3z" fill="#1f8ded"/></svg>`,
114
+ category: "support",
115
115
  import: {
116
116
  name: "useScriptIntercom",
117
117
  from: resolve("./runtime/registry/intercom")
@@ -193,6 +193,19 @@ const registry = (resolve) => {
193
193
  from: resolve("./runtime/registry/google-maps")
194
194
  }
195
195
  },
196
+ // chat
197
+ {
198
+ label: "Crisp",
199
+ category: "support",
200
+ logo: {
201
+ light: `<svg height="30" width="35" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><filter id="a" height="138.7%" width="131.4%" x="-15.7%" y="-15.1%"><feMorphology in="SourceAlpha" operator="dilate" radius="1" result="shadowSpreadOuter1"/><feOffset dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1"/><feGaussianBlur in="shadowOffsetOuter1" result="shadowBlurOuter1" stdDeviation="1"/><feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1"/><feColorMatrix in="shadowBlurOuter1" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.07 0"/></filter><path id="b" d="M14.23 20.46l-9.65 1.1L3 5.12 30.07 2l1.58 16.46-9.37 1.07-3.5 5.72-4.55-4.8z"/></defs><g fill="none" fill-rule="evenodd"><use fill="#000" filter="url(#a)" xlink:href="#b"/><use fill="#1972f5" stroke="#1972f5" stroke-width="2" xlink:href="#b"/></g></svg>`,
202
+ dark: `<svg height="30" width="35" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><filter id="a" height="138.7%" width="131.4%" x="-15.7%" y="-15.1%"><feMorphology in="SourceAlpha" operator="dilate" radius="1" result="shadowSpreadOuter1"/><feOffset dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1"/><feGaussianBlur in="shadowOffsetOuter1" result="shadowBlurOuter1" stdDeviation="1"/><feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1"/><feColorMatrix in="shadowBlurOuter1" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.07 0"/></filter><path id="b" d="M14.23 20.46l-9.65 1.1L3 5.12 30.07 2l1.58 16.46-9.37 1.07-3.5 5.72-4.55-4.8z"/></defs><g fill="none" fill-rule="evenodd"><use fill="#000" filter="url(#a)" xlink:href="#b"/><use fill="#fff" stroke="#fff" stroke-width="2" xlink:href="#b"/></g></svg>`
203
+ },
204
+ import: {
205
+ name: "useScriptCrisp",
206
+ from: resolve("./runtime/registry/crisp")
207
+ }
208
+ },
196
209
  // other
197
210
  {
198
211
  label: "NPM",
@@ -1,6 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import { withQuery } from 'ufo'
3
- import { onBeforeUnmount, onMounted, ref, useElementScriptTrigger } from '#imports'
3
+ import { useScriptTriggerElement } from '../composables/useScriptTriggerElement'
4
+ import { onBeforeUnmount, onMounted, ref } from '#imports'
4
5
  import type { ElementScriptTrigger } from '#nuxt-scripts'
5
6
 
6
7
  const props = defineProps<{
@@ -14,7 +15,7 @@ const props = defineProps<{
14
15
 
15
16
  const emit = defineEmits<{
16
17
  error: [error: string | Event]
17
- load: []
18
+ ready: [HTMLScriptElement]
18
19
  }>()
19
20
 
20
21
  const attrId = `_carbonads_js`
@@ -40,7 +41,7 @@ function loadCarbon() {
40
41
  }
41
42
  script.onload = () => {
42
43
  status.value = 'loaded'
43
- emit('load')
44
+ emit('ready', script)
44
45
  }
45
46
  carbonadsEl.value.appendChild(script)
46
47
  }
@@ -50,7 +51,7 @@ onMounted(() => {
50
51
  loadCarbon()
51
52
  }
52
53
  else {
53
- useElementScriptTrigger({ trigger: props.trigger, el: carbonadsEl })
54
+ useScriptTriggerElement({ trigger: props.trigger, el: carbonadsEl })
54
55
  }
55
56
  })
56
57
 
@@ -0,0 +1,84 @@
1
+ <script setup lang="ts">
2
+ import { useScriptTriggerElement } from '../composables/useScriptTriggerElement'
3
+ import { useScriptCrisp } from '../registry/crisp'
4
+ import { ref, onMounted, onBeforeUnmount, watch } from '#imports'
5
+ import type { ElementScriptTrigger } from '#nuxt-scripts'
6
+
7
+ const props = withDefaults(defineProps<{
8
+ /**
9
+ * Defines the trigger event to load the script.
10
+ */
11
+ trigger?: ElementScriptTrigger
12
+ id: string
13
+ runtimeConfig?: {
14
+ locale: string
15
+ }
16
+ tokenId?: string
17
+ cookieDomain?: string
18
+ cookieExpiry?: number
19
+ }>(), {
20
+ trigger: 'click',
21
+ })
22
+
23
+ const emits = defineEmits<{
24
+ // our emit
25
+ ready: [e: ReturnType<typeof useScriptCrisp>]
26
+ error: []
27
+ }>()
28
+
29
+ const rootEl = ref(null)
30
+ const trigger = useScriptTriggerElement({ trigger: props.trigger, el: rootEl })
31
+
32
+ const isReady = ref(false)
33
+ const crisp = useScriptCrisp({
34
+ id: props.id,
35
+ runtimeConfig: props.runtimeConfig,
36
+ tokenId: props.tokenId,
37
+ cookieDomain: props.cookieDomain,
38
+ cookieExpiry: props.cookieExpiry,
39
+ scriptOptions: {
40
+ trigger,
41
+ },
42
+ })
43
+ if (props.trigger === 'click')
44
+ crisp.do('chat:open')
45
+ const { $script } = crisp
46
+
47
+ defineExpose({
48
+ crisp,
49
+ })
50
+
51
+ let observer: MutationObserver
52
+ onMounted(() => {
53
+ watch($script.status, (status) => {
54
+ if (status === 'loaded') {
55
+ observer = new MutationObserver(() => {
56
+ if (document.getElementById('crisp-chatbox')) {
57
+ isReady.value = true
58
+ emits('ready', crisp)
59
+ observer.disconnect()
60
+ }
61
+ })
62
+ observer.observe(document.body, { childList: true, subtree: true })
63
+ }
64
+ else if (status === 'error') {
65
+ emits('error')
66
+ }
67
+ })
68
+ })
69
+ onBeforeUnmount(() => {
70
+ observer?.disconnect()
71
+ })
72
+ </script>
73
+
74
+ <template>
75
+ <div
76
+ ref="rootEl"
77
+ :style="{ display: isReady ? 'none' : 'block' }"
78
+ >
79
+ <slot :ready="isReady" />
80
+ <slot v-if="$script.status.value === 'awaitingLoad'" name="awaitingLoad" />
81
+ <slot v-else-if="$script.status.value === 'loading' || !isReady" name="loading" />
82
+ <slot v-else-if="$script.status.value === 'error'" name="error" />
83
+ </div>
84
+ </template>
@@ -1,5 +1,7 @@
1
1
  <script setup lang="ts">
2
- import { callOnce, onMounted, ref, useElementScriptTrigger, useScriptGoogleAdsense } from '#imports'
2
+ import { useScriptTriggerElement } from '../composables/useScriptTriggerElement'
3
+ import { useScriptGoogleAdsense } from '../registry/google-adsense'
4
+ import { callOnce, onMounted, ref, watch } from '#imports'
3
5
  import type { ElementScriptTrigger } from '#nuxt-scripts'
4
6
 
5
7
  const props = withDefaults(defineProps<{
@@ -16,20 +18,36 @@ const props = withDefaults(defineProps<{
16
18
  dataFullWidthResponsive: true,
17
19
  })
18
20
 
21
+ const emits = defineEmits<{
22
+ // our emit
23
+ ready: [e: ReturnType<typeof useScriptGoogleAdsense>]
24
+ error: []
25
+ }>()
26
+
19
27
  const rootEl = ref(null)
20
- const trigger = useElementScriptTrigger({ trigger: props.trigger, el: rootEl })
28
+ const trigger = useScriptTriggerElement({ trigger: props.trigger, el: rootEl })
21
29
 
22
- const { $script } = useScriptGoogleAdsense({
30
+ const instance = useScriptGoogleAdsense({
23
31
  client: props.dataAdClient,
24
32
  scriptOptions: {
25
33
  trigger,
26
34
  },
27
35
  })
28
36
 
37
+ const { $script } = instance
38
+
29
39
  onMounted(() => {
30
40
  callOnce(() => {
31
41
  (window.adsbygoogle = window.adsbygoogle || []).push({})
32
42
  })
43
+ watch(instance.$script.status, () => {
44
+ if (instance.$script.status.value === 'loaded') {
45
+ emits('ready', instance)
46
+ }
47
+ else if (instance.$script.status.value === 'error') {
48
+ emits('error')
49
+ }
50
+ })
33
51
  })
34
52
  </script>
35
53
 
@@ -45,6 +63,7 @@ onMounted(() => {
45
63
  v-bind="{ ...$attrs }"
46
64
  >
47
65
  <slot v-if="$script.status.value === 'awaitingLoad'" name="awaitingLoad" />
48
- <slot v-if="$script.status.value === 'loading'" name="loading" />
66
+ <slot v-else-if="$script.status.value === 'loading'" name="loading" />
67
+ <slot v-else-if="$script.status.value === 'error'" name="error" />
49
68
  </ins>
50
69
  </template>
@@ -1,21 +1,15 @@
1
1
  <script lang="ts" setup>
2
2
  /// <reference types="google.maps" />
3
- import {
4
- type HTMLAttributes,
5
- type ImgHTMLAttributes,
6
- type Ref,
7
- type ReservedProps,
8
- computed,
9
- onBeforeUnmount,
10
- onMounted,
11
- ref,
12
- watch,
13
- } from 'vue'
14
- import { type QueryObject, withQuery } from 'ufo'
3
+ import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
4
+ import type { HTMLAttributes, ImgHTMLAttributes, Ref, ReservedProps } from 'vue'
5
+ import { withQuery } from 'ufo'
6
+ import type { QueryObject } from 'ufo'
15
7
  import { defu } from 'defu'
16
8
  import type { ElementScriptTrigger } from '../types'
17
9
  import { scriptRuntimeConfig } from '../utils'
18
- import { resolveComponent, useElementScriptTrigger, useHead, useScriptGoogleMaps } from '#imports'
10
+ import { useScriptTriggerElement } from '../composables/useScriptTriggerElement'
11
+ import { useScriptGoogleMaps } from '../registry/google-maps'
12
+ import { resolveComponent, useHead } from '#imports'
19
13
 
20
14
  interface PlaceholderOptions {
21
15
  width?: string | number
@@ -91,6 +85,7 @@ const props = withDefaults(defineProps<{
91
85
  const emits = defineEmits<{
92
86
  // our emit
93
87
  ready: [e: Ref<google.maps.Map | undefined>]
88
+ error: []
94
89
  }>()
95
90
 
96
91
  const apiKey = props.apiKey || scriptRuntimeConfig('googleMaps')?.apiKey
@@ -106,7 +101,7 @@ const mapEl = ref<HTMLElement>()
106
101
  const { $script } = useScriptGoogleMaps({
107
102
  apiKey: props.apiKey,
108
103
  scriptOptions: {
109
- trigger: useElementScriptTrigger({ trigger: props.trigger, el: rootEl }),
104
+ trigger: useScriptTriggerElement({ trigger: props.trigger, el: rootEl }),
110
105
  },
111
106
  })
112
107
 
@@ -142,6 +137,11 @@ onMounted(() => {
142
137
  watch(ready, (v) => {
143
138
  v && emits('ready', map)
144
139
  })
140
+ watch($script.status, () => {
141
+ if ($script.status.value === 'error') {
142
+ emits('error')
143
+ }
144
+ })
145
145
  // create the map
146
146
  $script.then(async (instance) => {
147
147
  const maps = await instance.maps as any as typeof google.maps // some weird type issue here
@@ -259,6 +259,7 @@ onBeforeUnmount(() => {
259
259
  <ScriptLoadingIndicator color="black" />
260
260
  </slot>
261
261
  <slot v-if="$script.status.value === 'awaitingLoad'" name="awaitingLoad" />
262
+ <slot v-else-if="$script.status.value === 'error'" name="error" />
262
263
  <slot />
263
264
  </div>
264
265
  </template>