dtwo 3.0.0 → 3.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.
Files changed (61) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +6 -2
  3. package/bin/dtwo.mjs +1 -1
  4. package/config.d.ts +1 -1
  5. package/dist/app/compat/idle-callback.d.ts +2 -0
  6. package/dist/app/compat/idle-callback.mjs +13 -0
  7. package/dist/app/components/dtwo-island.d.ts +33 -0
  8. package/dist/app/components/dtwo-island.mjs +62 -0
  9. package/dist/app/components/dtwo-link.d.ts +3 -3
  10. package/dist/app/components/dtwo-link.mjs +26 -28
  11. package/dist/app/components/dtwo-loading-indicator.d.ts +3 -3
  12. package/dist/app/components/dtwo-loading-indicator.mjs +8 -7
  13. package/dist/app/components/dtwo-root.kdu +10 -2
  14. package/dist/app/components/island-renderer.d.ts +20 -0
  15. package/dist/app/components/island-renderer.mjs +26 -0
  16. package/dist/app/components/layout.d.ts +1 -1
  17. package/dist/app/components/layout.mjs +12 -3
  18. package/dist/app/components/utils.mjs +1 -0
  19. package/dist/app/composables/asyncData.d.ts +4 -1
  20. package/dist/app/composables/asyncData.mjs +15 -2
  21. package/dist/app/composables/component.d.ts +1 -1
  22. package/dist/app/composables/cookie.d.ts +5 -4
  23. package/dist/app/composables/cookie.mjs +8 -2
  24. package/dist/app/composables/error.d.ts +1 -1
  25. package/dist/app/composables/fetch.d.ts +6 -6
  26. package/dist/app/composables/fetch.mjs +2 -1
  27. package/dist/app/composables/index.d.ts +2 -1
  28. package/dist/app/composables/index.mjs +2 -1
  29. package/dist/app/composables/ready.d.ts +1 -0
  30. package/dist/app/composables/ready.mjs +12 -0
  31. package/dist/app/composables/router.d.ts +1 -1
  32. package/dist/app/composables/router.mjs +5 -0
  33. package/dist/app/composables/ssr.d.ts +1 -1
  34. package/dist/app/config.mjs +1 -0
  35. package/dist/app/dtwo.d.ts +5 -4
  36. package/dist/app/plugins/cross-origin-prefetch.client.mjs +19 -14
  37. package/dist/app/plugins/router.mjs +13 -5
  38. package/dist/app/types/augments.d.ts +1 -1
  39. package/dist/components/runtime/server-component.d.ts +3 -0
  40. package/dist/components/runtime/server-component.mjs +87 -0
  41. package/dist/core/runtime/hole/renderer.d.ts +19 -0
  42. package/dist/core/runtime/hole/renderer.mjs +71 -9
  43. package/dist/head/runtime/components.d.ts +14 -14
  44. package/dist/head/runtime/components.mjs +12 -1
  45. package/dist/head/runtime/composables.d.ts +17 -0
  46. package/dist/head/runtime/composables.mjs +9 -0
  47. package/dist/head/runtime/lib/kduse-head.plugin.mjs +2 -1
  48. package/dist/index.mjs +298 -77
  49. package/dist/pages/runtime/composables.d.ts +1 -1
  50. package/dist/pages/runtime/page.d.ts +5 -1
  51. package/dist/pages/runtime/page.mjs +6 -3
  52. package/dist/pages/runtime/plugins/prefetch.client.mjs +36 -0
  53. package/dist/pages/runtime/{router.d.ts → plugins/router.d.ts} +1 -1
  54. package/dist/pages/runtime/{router.mjs → plugins/router.mjs} +11 -8
  55. package/dist/pages/runtime/utils.d.ts +1 -1
  56. package/dist/pages/runtime/utils.mjs +1 -1
  57. package/dist/pages/runtime/validate.mjs +4 -4
  58. package/package.json +51 -51
  59. package/types.d.ts +3 -0
  60. package/dist/head/runtime/lib/kdu-meta.plugin.mjs +0 -31
  61. /package/dist/{head/runtime/lib/kdu-meta.plugin.d.ts → pages/runtime/plugins/prefetch.client.d.ts} +0 -0
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { dirname, resolve, basename, extname, relative, normalize, isAbsolute, join } from 'pathe';
2
2
  import { createHooks, createDebugger } from 'hookable';
3
- import { useDtwo, resolveFiles, defineDtwoModule, addPlugin, addTemplate, addComponent, updateTemplates, addWitePlugin, addWebpackPlugin, findPath, isIgnored, resolveAlias, addPluginTemplate, logger, resolvePath, dtwoCtx, tryResolveModule, installModule, loadDtwoConfig, normalizeTemplate, compileTemplate, normalizePlugin, templateUtils, importModule } from '@dtwo/kit';
3
+ import { useDtwo, resolveFiles, defineDtwoModule, addPlugin, addTemplate, addComponent, updateTemplates, addWitePlugin, addWebpackPlugin, findPath, isIgnored, resolveAlias, addPluginTemplate, logger, resolvePath, createResolver, dtwoCtx, tryResolveModule, installModule, loadDtwoConfig, normalizeTemplate, compileTemplate, normalizePlugin, templateUtils, importModule } from '@dtwo/kit';
4
4
  import escapeRE from 'escape-string-regexp';
5
5
  import fse from 'fs-extra';
6
6
  import { encodePath, parseURL, stringifyQuery, parseQuery, joinURL, withTrailingSlash, withoutLeadingSlash } from 'ufo';
@@ -15,18 +15,21 @@ import MagicString from 'magic-string';
15
15
  import { globby } from 'globby';
16
16
  import { hyphenate } from '@kdujs/shared';
17
17
  import { parse, walk as walk$1, ELEMENT_NODE } from 'ultrahtml';
18
- import { defineKdimportPreset, createKdimport, toImports, scanDirExports } from 'kdimport';
18
+ import { defineKdimportPreset, createKdimport, scanDirExports } from 'kdimport';
19
19
  import { createRequire } from 'node:module';
20
20
  import { createTransformer } from 'unctx/transform';
21
21
  import { stripLiteral } from 'strip-literal';
22
22
  import { createHole, scanHandlers, writeTypes, build as build$1, prepare, copyPublicAssets, prerender, createDevServer } from 'holepack';
23
- import defu from 'defu';
23
+ import defu, { defu as defu$1 } from 'defu';
24
24
  import { dynamicEventHandler } from 'h3';
25
25
  import { createHeadCore } from 'kdhead';
26
26
  import { renderSSRHead } from '@kdhead/ssr';
27
+ import { rm, mkdir, writeFile } from 'node:fs/promises';
27
28
  import chokidar from 'chokidar';
28
29
  import { debounce } from 'perfect-debounce';
29
- import { generateTypes, resolveSchema } from 'untyped';
30
+ import { resolveSchema, generateMarkdown, generateTypes } from 'untyped';
31
+ import untypedPlugin from 'untyped/babel-plugin';
32
+ import jiti from 'jiti';
30
33
  import { hash } from 'ohash';
31
34
 
32
35
  let _distDir = dirname(fileURLToPath(import.meta.url));
@@ -192,26 +195,30 @@ function prepareRoutes(routes, parent) {
192
195
  return routes;
193
196
  }
194
197
  function normalizeRoutes(routes, metaImports = /* @__PURE__ */ new Set()) {
198
+ const dtwo = useDtwo();
195
199
  return {
196
200
  imports: metaImports,
197
- routes: genArrayFromRaw(routes.map((route) => {
198
- const file = normalize(route.file);
199
- const metaImportName = genSafeVariableName(file) + "Meta";
201
+ routes: genArrayFromRaw(routes.map((page) => {
202
+ const file = normalize(page.file);
203
+ const metaImportName = genSafeVariableName(relative(dtwo.options.rootDir, file)) + "Meta";
200
204
  metaImports.add(genImport(`${file}?macro=true`, [{ name: "default", as: metaImportName }]));
201
205
  let aliasCode = `${metaImportName}?.alias || []`;
202
- if (Array.isArray(route.alias) && route.alias.length) {
203
- aliasCode = `${JSON.stringify(route.alias)}.concat(${aliasCode})`;
204
- }
205
- return {
206
- ...Object.fromEntries(Object.entries(route).map(([key, value]) => [key, JSON.stringify(value)])),
207
- name: `${metaImportName}?.name ?? ${route.name ? JSON.stringify(route.name) : "undefined"}`,
208
- path: `${metaImportName}?.path ?? ${JSON.stringify(route.path)}`,
209
- children: route.children ? normalizeRoutes(route.children, metaImports).routes : [],
210
- meta: route.meta ? `{...(${metaImportName} || {}), ...${JSON.stringify(route.meta)}}` : metaImportName,
206
+ if (Array.isArray(page.alias) && page.alias.length) {
207
+ aliasCode = `${JSON.stringify(page.alias)}.concat(${aliasCode})`;
208
+ }
209
+ const route = {
210
+ ...Object.fromEntries(Object.entries(page).map(([key, value]) => [key, JSON.stringify(value)])),
211
+ file: void 0,
212
+ name: `${metaImportName}?.name ?? ${page.name ? JSON.stringify(page.name) : "undefined"}`,
213
+ path: `${metaImportName}?.path ?? ${JSON.stringify(page.path)}`,
214
+ children: page.children ? normalizeRoutes(page.children, metaImports).routes : [],
215
+ meta: page.meta ? `{...(${metaImportName} || {}), ...${JSON.stringify(page.meta)}}` : metaImportName,
211
216
  alias: aliasCode,
212
- redirect: route.redirect ? JSON.stringify(route.redirect) : `${metaImportName}?.redirect || undefined`,
217
+ redirect: page.redirect ? JSON.stringify(page.redirect) : `${metaImportName}?.redirect || undefined`,
213
218
  component: genDynamicImport(file, { interopDefault: true })
214
219
  };
220
+ delete route.file;
221
+ return route;
215
222
  }))
216
223
  };
217
224
  }
@@ -227,7 +234,7 @@ if (import.meta.hot) {
227
234
  Object.assign(__dtwo_page_meta, mod)
228
235
  })
229
236
  }
230
- // Webpack
237
+ // webpack
231
238
  if (import.meta.webpackHot) {
232
239
  import.meta.webpackHot.accept((err) => {
233
240
  if (err) { window.location = window.location.href }
@@ -500,7 +507,8 @@ const pagesModule = defineDtwoModule({
500
507
  };
501
508
  addWitePlugin(PageMetaPlugin.wite(pageMetaOptions));
502
509
  addWebpackPlugin(PageMetaPlugin.webpack(pageMetaOptions));
503
- addPlugin(resolve(runtimeDir, "router"));
510
+ addPlugin(resolve(runtimeDir, "plugins/prefetch.client"));
511
+ addPlugin(resolve(runtimeDir, "plugins/router"));
504
512
  const getSources = (pages) => pages.flatMap(
505
513
  (p) => [relative(dtwo.options.srcDir, p.file), ...getSources(p.children || [])]
506
514
  );
@@ -543,6 +551,7 @@ const pagesModule = defineDtwoModule({
543
551
  `const configRouterOptions = ${configRouterOptions}`,
544
552
  "export default {",
545
553
  "...configRouterOptions,",
554
+ // We need to reverse spreading order to respect layers priority
546
555
  ...routerOptionsFiles.map((_, index) => `...routerOptions${index},`).reverse(),
547
556
  "}"
548
557
  ].join("\n");
@@ -605,6 +614,7 @@ const metaModule = defineDtwoModule({
605
614
  name: componentName,
606
615
  filePath: componentsPath,
607
616
  export: componentName,
617
+ // kebab case version of these tags is not valid
608
618
  kebabName: componentName
609
619
  });
610
620
  }
@@ -643,11 +653,12 @@ export default defineDtwoPlugin(dtwoApp => {
643
653
  }
644
654
  };
645
655
  const componentsTemplate = {
656
+ // components.[server|client].mjs'
646
657
  getContents({ options }) {
647
658
  const imports = /* @__PURE__ */ new Set();
648
659
  imports.add("import { defineAsyncComponent } from 'kdu'");
649
660
  let num = 0;
650
- const components = options.getComponents(options.mode).flatMap((c) => {
661
+ const components = options.getComponents(options.mode).filter((c) => !c.island).flatMap((c) => {
651
662
  const exp = c.export === "default" ? "c.default || c" : `c['${c.export}']`;
652
663
  const comment = createImportMagicComments(c);
653
664
  const isClient = c.mode === "client";
@@ -667,15 +678,32 @@ const componentsTemplate = {
667
678
  return [
668
679
  ...imports,
669
680
  ...components,
670
- `export const componentNames = ${JSON.stringify(options.getComponents().map((c) => c.pascalName))}`
681
+ `export const componentNames = ${JSON.stringify(options.getComponents().filter((c) => !c.island).map((c) => c.pascalName))}`
671
682
  ].join("\n");
672
683
  }
673
684
  };
685
+ const componentsIslandsTemplate = {
686
+ // components.islands.mjs'
687
+ getContents({ options }) {
688
+ const components = options.getComponents();
689
+ const islands = components.filter(
690
+ (component) => component.island || // .server components without a corresponding .client component will need to be rendered as an island
691
+ component.mode === "server" && !components.some((c) => c.pascalName === component.pascalName && c.mode === "client")
692
+ );
693
+ return islands.map(
694
+ (c) => {
695
+ const exp = c.export === "default" ? "c.default || c" : `c['${c.export}']`;
696
+ const comment = createImportMagicComments(c);
697
+ return `export const ${c.pascalName} = defineAsyncComponent(${genDynamicImport(c.filePath, { comment })}.then(c => ${exp}))`;
698
+ }
699
+ ).join("\n");
700
+ }
701
+ };
674
702
  const componentsTypeTemplate = {
675
703
  filename: "components.d.ts",
676
704
  getContents: ({ options, dtwo }) => {
677
705
  const buildDir = dtwo.options.buildDir;
678
- const componentTypes = options.getComponents().map((c) => [
706
+ const componentTypes = options.getComponents().filter((c) => !c.island).map((c) => [
679
707
  c.pascalName,
680
708
  `typeof ${genDynamicImport(isAbsolute(c.filePath) ? relative(buildDir, c.filePath).replace(/(?<=\w)\.(?!kdu)\w+$/g, "") : c.filePath.replace(/(?<=\w)\.(?!kdu)\w+$/g, ""), { wrapper: false })}['${c.export}']`
681
709
  ]);
@@ -716,9 +744,10 @@ async function scanComponents(dirs, srcDir) {
716
744
  dir.pathPrefix !== false ? splitByCase(relative(dir.path, dirname(filePath))) : []
717
745
  );
718
746
  let fileName = basename(filePath, extname(filePath));
719
- const global = /\.(global)$/.test(fileName) || dir.global;
720
- const mode = fileName.match(/(?<=\.)(client|server)(\.global)?$/)?.[1] || "all";
721
- fileName = fileName.replace(/(\.(client|server))?(\.global)?$/, "");
747
+ const island = /\.(island)(\.global)?$/.test(fileName) || dir.island;
748
+ const global = /\.(global)(\.island)?$/.test(fileName) || dir.global;
749
+ const mode = island ? "server" : fileName.match(/(?<=\.)(client|server)(\.global|\.island)*$/)?.[1] || "all";
750
+ fileName = fileName.replace(/(\.(client|server))?(\.global|\.island)*$/, "");
722
751
  if (fileName.toLowerCase() === "index") {
723
752
  fileName = dir.pathPrefix === false ? basename(dirname(filePath)) : "";
724
753
  }
@@ -744,10 +773,13 @@ async function scanComponents(dirs, srcDir) {
744
773
  const shortPath = relative(srcDir, filePath);
745
774
  const chunkName = "components/" + kebabName + suffix;
746
775
  let component = {
776
+ // inheritable from directory configuration
747
777
  mode,
748
778
  global,
779
+ island,
749
780
  prefetch: Boolean(dir.prefetch),
750
781
  preload: Boolean(dir.preload),
782
+ // specific to the file
751
783
  filePath,
752
784
  pascalName,
753
785
  kebabName,
@@ -787,6 +819,7 @@ function isKduTemplate(id) {
787
819
  const loaderPlugin = createUnplugin((options) => {
788
820
  const exclude = options.transform?.exclude || [];
789
821
  const include = options.transform?.include || [];
822
+ const serverComponentRuntime = resolve(distDir, "components/runtime/server-component");
790
823
  return {
791
824
  name: "dtwo:components-loader",
792
825
  enforce: "post",
@@ -810,6 +843,15 @@ const loaderPlugin = createUnplugin((options) => {
810
843
  if (component) {
811
844
  let identifier = map.get(component) || `__dtwo_component_${num++}`;
812
845
  map.set(component, identifier);
846
+ const isServerOnly = component.mode === "server" && !components.some((c) => c.pascalName === component.pascalName && c.mode === "client");
847
+ if (isServerOnly) {
848
+ imports.add(genImport(serverComponentRuntime, [{ name: "createServerComponent" }]));
849
+ imports.add(`const ${identifier} = createServerComponent(${JSON.stringify(name)})`);
850
+ if (!options.experimentalComponentIslands) {
851
+ console.warn(`Standalone server components (\`${name}\`) are not yet supported without enabling \`experimental.componentIslands\`.`);
852
+ }
853
+ return identifier;
854
+ }
813
855
  const isClientOnly = component.mode === "client";
814
856
  if (isClientOnly) {
815
857
  imports.add(genImport("#app/components/client-only", [{ name: "createClientOnly" }]));
@@ -845,10 +887,13 @@ const loaderPlugin = createUnplugin((options) => {
845
887
  function findComponent(components, name, mode) {
846
888
  const id = pascalCase(name).replace(/["']/g, "");
847
889
  const component = components.find((component2) => id === component2.pascalName && ["all", mode, void 0].includes(component2.mode));
848
- if (!component && components.some((component2) => id === component2.pascalName)) {
849
- return components.find((component2) => component2.pascalName === "ServerPlaceholder");
890
+ if (component) {
891
+ return component;
892
+ }
893
+ if (mode === "server" && !component) {
894
+ return components.find((c) => c.pascalName === "ServerPlaceholder");
850
895
  }
851
- return component;
896
+ return components.find((component2) => id === component2.pascalName);
852
897
  }
853
898
 
854
899
  const PLACEHOLDER_RE = /^(k-slot|#)(fallback|placeholder)/;
@@ -912,7 +957,7 @@ const isDirectory = (p) => {
912
957
  function compareDirByPathLength({ path: pathA }, { path: pathB }) {
913
958
  return pathB.split(/[\\/]/).filter(Boolean).length - pathA.split(/[\\/]/).filter(Boolean).length;
914
959
  }
915
- const DEFAULT_COMPONENTS_DIRS_RE = /\/components$|\/components\/global$/;
960
+ const DEFAULT_COMPONENTS_DIRS_RE = /\/components(\/global|\/islands)?$/;
916
961
  const componentsModule = defineDtwoModule({
917
962
  meta: {
918
963
  name: "components",
@@ -935,6 +980,7 @@ const componentsModule = defineDtwoModule({
935
980
  }
936
981
  if (dir === true || dir === void 0) {
937
982
  return [
983
+ { path: resolve(cwd, "components/islands"), island: true },
938
984
  { path: resolve(cwd, "components/global"), global: true },
939
985
  { path: resolve(cwd, "components") }
940
986
  ];
@@ -968,13 +1014,16 @@ const componentsModule = defineDtwoModule({
968
1014
  return {
969
1015
  global: componentOptions.global,
970
1016
  ...dirOptions,
1017
+ // TODO
971
1018
  enabled: true,
972
1019
  path: dirPath,
973
1020
  extensions,
974
1021
  pattern: dirOptions.pattern || `**/*.{${extensions.join(",")},}`,
975
1022
  ignore: [
976
1023
  "**/*{M,.m,-m}ixin.{js,ts,jsx,tsx}",
1024
+ // ignore mixins
977
1025
  "**/*.d.ts",
1026
+ // .d.ts files
978
1027
  ...dirOptions.ignore || []
979
1028
  ],
980
1029
  transpile: transpile === "auto" ? dirPath.includes("node_modules") : transpile
@@ -990,6 +1039,11 @@ const componentsModule = defineDtwoModule({
990
1039
  addPluginTemplate({ ...componentsPluginTemplate, options: { getComponents } });
991
1040
  addTemplate({ ...componentsTemplate, filename: "components.server.mjs", options: { getComponents, mode: "server" } });
992
1041
  addTemplate({ ...componentsTemplate, filename: "components.client.mjs", options: { getComponents, mode: "client" } });
1042
+ if (dtwo.options.experimental.componentIslands) {
1043
+ addTemplate({ ...componentsIslandsTemplate, filename: "components.islands.mjs", options: { getComponents } });
1044
+ } else {
1045
+ addTemplate({ filename: "components.islands.mjs", getContents: () => "export default {}" });
1046
+ }
993
1047
  dtwo.hook("wite:extendConfig", (config, { isClient }) => {
994
1048
  const mode = isClient ? "client" : "server";
995
1049
  config.resolve.alias["#components"] = resolve(dtwo.options.buildDir, `components.${mode}.mjs`);
@@ -1049,7 +1103,8 @@ const componentsModule = defineDtwoModule({
1049
1103
  config.plugins.push(loaderPlugin.wite({
1050
1104
  sourcemap: dtwo.options.sourcemap[mode],
1051
1105
  getComponents,
1052
- mode
1106
+ mode,
1107
+ experimentalComponentIslands: dtwo.options.experimental.componentIslands
1053
1108
  }));
1054
1109
  if (dtwo.options.experimental.treeshakeClientOnly && isServer) {
1055
1110
  config.plugins.push(TreeShakeTemplatePlugin.wite({
@@ -1065,7 +1120,8 @@ const componentsModule = defineDtwoModule({
1065
1120
  config.plugins.push(loaderPlugin.webpack({
1066
1121
  sourcemap: dtwo.options.sourcemap[mode],
1067
1122
  getComponents,
1068
- mode
1123
+ mode,
1124
+ experimentalComponentIslands: dtwo.options.experimental.componentIslands
1069
1125
  }));
1070
1126
  if (dtwo.options.experimental.treeshakeClientOnly && mode === "server") {
1071
1127
  config.plugins.push(TreeShakeTemplatePlugin.webpack({
@@ -1116,12 +1172,16 @@ const TransformPlugin = createUnplugin(({ ctx, options, sourcemap }) => {
1116
1172
  });
1117
1173
 
1118
1174
  const commonPresets = [
1175
+ // #head
1119
1176
  defineKdimportPreset({
1120
1177
  from: "#head",
1121
1178
  imports: [
1122
- "useHead"
1179
+ "useHead",
1180
+ "useSeoMeta",
1181
+ "useServerSeoMeta"
1123
1182
  ]
1124
1183
  }),
1184
+ // kdu-demi (mocked)
1125
1185
  defineKdimportPreset({
1126
1186
  from: "kdu-demi",
1127
1187
  imports: [
@@ -1135,6 +1195,7 @@ const appPreset = defineKdimportPreset({
1135
1195
  imports: [
1136
1196
  "useAsyncData",
1137
1197
  "useLazyAsyncData",
1198
+ "useDtwoData",
1138
1199
  "refreshDtwoData",
1139
1200
  "clearDtwoData",
1140
1201
  "defineDtwoComponent",
@@ -1149,6 +1210,7 @@ const appPreset = defineKdimportPreset({
1149
1210
  "useRequestEvent",
1150
1211
  "setResponseStatus",
1151
1212
  "setPageLayout",
1213
+ "onDtwoReady",
1152
1214
  "useRouter",
1153
1215
  "useRoute",
1154
1216
  "defineDtwoRouteMiddleware",
@@ -1182,12 +1244,14 @@ const routerPreset = defineKdimportPreset({
1182
1244
  const kduPreset = defineKdimportPreset({
1183
1245
  from: "kdu",
1184
1246
  imports: [
1247
+ // <script setup>
1185
1248
  "withCtx",
1186
1249
  "withDirectives",
1187
1250
  "withKeys",
1188
1251
  "withMemo",
1189
1252
  "withModifiers",
1190
1253
  "withScopeId",
1254
+ // Lifecycle
1191
1255
  "onActivated",
1192
1256
  "onBeforeMount",
1193
1257
  "onBeforeUnmount",
@@ -1200,6 +1264,7 @@ const kduPreset = defineKdimportPreset({
1200
1264
  "onServerPrefetch",
1201
1265
  "onUnmounted",
1202
1266
  "onUpdated",
1267
+ // Reactivity
1203
1268
  "computed",
1204
1269
  "customRef",
1205
1270
  "isProxy",
@@ -1222,10 +1287,12 @@ const kduPreset = defineKdimportPreset({
1222
1287
  "watch",
1223
1288
  "watchEffect",
1224
1289
  "isShallow",
1290
+ // effect
1225
1291
  "effect",
1226
1292
  "effectScope",
1227
1293
  "getCurrentScope",
1228
1294
  "onScopeDispose",
1295
+ // Component
1229
1296
  "defineComponent",
1230
1297
  "defineAsyncComponent",
1231
1298
  "resolveComponent",
@@ -1262,19 +1329,21 @@ const importsModule = defineDtwoModule({
1262
1329
  transform: {
1263
1330
  include: [],
1264
1331
  exclude: void 0
1265
- }
1332
+ },
1333
+ virtualImports: ["#imports"]
1266
1334
  },
1267
1335
  async setup(options, dtwo) {
1268
1336
  const presets = JSON.parse(JSON.stringify(options.presets));
1269
1337
  await dtwo.callHook("imports:sources", presets);
1270
1338
  const ctx = createKdimport({
1271
- presets,
1272
- imports: options.imports,
1273
- virtualImports: ["#imports"],
1339
+ ...options,
1274
1340
  addons: {
1275
- kduTemplate: options.autoImport
1276
- }
1341
+ kduTemplate: options.autoImport,
1342
+ ...options.addons
1343
+ },
1344
+ presets
1277
1345
  });
1346
+ await dtwo.callHook("imports:context", ctx);
1278
1347
  let composablesDirs = [];
1279
1348
  for (const layer of dtwo.options._layers) {
1280
1349
  composablesDirs.push(resolve(layer.config.srcDir, "composables"));
@@ -1293,28 +1362,17 @@ const importsModule = defineDtwoModule({
1293
1362
  getContents: async () => await ctx.toExports() + '\nif (process.dev) { console.warn("[dtwo] `#imports` should be transformed with real imports. There seems to be something wrong with the imports plugin.") }'
1294
1363
  });
1295
1364
  dtwo.options.alias["#imports"] = join(dtwo.options.buildDir, "imports");
1296
- if (dtwo.options.dev && options.global) {
1297
- addPluginTemplate({
1298
- filename: "imports.mjs",
1299
- getContents: async () => {
1300
- const imports = await ctx.getImports();
1301
- const importStatement = toImports(imports);
1302
- const globalThisSet = imports.map((i) => `globalThis.${i.as} = ${i.as};`).join("\n");
1303
- return `${importStatement}
1304
-
1305
- ${globalThisSet}
1306
-
1307
- export default () => {};`;
1308
- }
1309
- });
1310
- } else {
1311
- addWitePlugin(TransformPlugin.wite({ ctx, options, sourcemap: dtwo.options.sourcemap.server || dtwo.options.sourcemap.client }));
1312
- addWebpackPlugin(TransformPlugin.webpack({ ctx, options, sourcemap: dtwo.options.sourcemap.server || dtwo.options.sourcemap.client }));
1313
- }
1365
+ addWitePlugin(TransformPlugin.wite({ ctx, options, sourcemap: dtwo.options.sourcemap.server || dtwo.options.sourcemap.client }));
1366
+ addWebpackPlugin(TransformPlugin.webpack({ ctx, options, sourcemap: dtwo.options.sourcemap.server || dtwo.options.sourcemap.client }));
1367
+ const priorities = dtwo.options._layers.map((layer, i) => [layer.config.srcDir, -i]).sort(([a], [b]) => b.length - a.length);
1314
1368
  const regenerateImports = async () => {
1315
1369
  ctx.clearDynamicImports();
1316
1370
  await ctx.modifyDynamicImports(async (imports) => {
1317
- imports.push(...await scanDirExports(composablesDirs));
1371
+ const composableImports = await scanDirExports(composablesDirs);
1372
+ for (const i of composableImports) {
1373
+ i.priority = i.priority || priorities.find(([dir]) => i.from.startsWith(dir))?.[1];
1374
+ }
1375
+ imports.push(...composableImports);
1318
1376
  await dtwo.callHook("imports:extend", imports);
1319
1377
  });
1320
1378
  };
@@ -1368,7 +1426,7 @@ function addDeclarationTemplates(ctx, options) {
1368
1426
  });
1369
1427
  }
1370
1428
 
1371
- const version = "3.0.0";
1429
+ const version = "3.1.0";
1372
1430
 
1373
1431
  const _require = createRequire(import.meta.url);
1374
1432
  const kduAppPatterns = (dtwo) => [
@@ -1428,7 +1486,7 @@ const UnctxTransformPlugin = (dtwo) => {
1428
1486
  app = _app;
1429
1487
  });
1430
1488
  return createUnplugin((options = {}) => ({
1431
- name: "unctx:transfrom",
1489
+ name: "unctx:transform",
1432
1490
  enforce: "post",
1433
1491
  transformInclude(id) {
1434
1492
  id = normalize(id).replace(/\?.*$/, "");
@@ -1449,7 +1507,7 @@ const UnctxTransformPlugin = (dtwo) => {
1449
1507
  const TreeShakePlugin = createUnplugin((options) => {
1450
1508
  const COMPOSABLE_RE = new RegExp(`($\\s+)(${options.treeShake.join("|")})(?=\\()`, "gm");
1451
1509
  return {
1452
- name: "dtwo:server-treeshake:transfrom",
1510
+ name: "dtwo:server-treeshake:transform",
1453
1511
  enforce: "post",
1454
1512
  transformInclude(id) {
1455
1513
  const { pathname, search } = parseURL(decodeURIComponent(pathToFileURL(id).href));
@@ -1481,9 +1539,9 @@ const TreeShakePlugin = createUnplugin((options) => {
1481
1539
  });
1482
1540
 
1483
1541
  const DevOnlyPlugin = createUnplugin((options) => {
1484
- const DEVONLY_COMP_RE = /<dev-?only>(:?[\s\S]*)<\/dev-?only>/gmi;
1542
+ const DEVONLY_COMP_RE = /<(dev-only|DevOnly)>[\s\S]*?<\/(dev-only|DevOnly)>/g;
1485
1543
  return {
1486
- name: "dtwo:server-devonly:transfrom",
1544
+ name: "dtwo:server-devonly:transform",
1487
1545
  enforce: "pre",
1488
1546
  transformInclude(id) {
1489
1547
  const { pathname, search } = parseURL(decodeURIComponent(pathToFileURL(id).href));
@@ -1538,6 +1596,20 @@ async function initHole(dtwo) {
1538
1596
  srcDir: dtwo.options.serverDir,
1539
1597
  dev: dtwo.options.dev,
1540
1598
  buildDir: dtwo.options.buildDir,
1599
+ imports: {
1600
+ imports: [
1601
+ {
1602
+ as: "__buildAssetsURL",
1603
+ name: "buildAssetsURL",
1604
+ from: resolve(distDir, "core/runtime/hole/paths")
1605
+ },
1606
+ {
1607
+ as: "__publicAssetsURL",
1608
+ name: "publicAssetsURL",
1609
+ from: resolve(distDir, "core/runtime/hole/paths")
1610
+ }
1611
+ ]
1612
+ },
1541
1613
  esbuild: {
1542
1614
  options: {
1543
1615
  exclude: [
@@ -1589,6 +1661,7 @@ async function initHole(dtwo) {
1589
1661
  "@dtwo/",
1590
1662
  dtwo.options.buildDir
1591
1663
  ],
1664
+ ...dtwo.options.build.transpile.filter((i) => typeof i === "string"),
1592
1665
  "dtwo/dist",
1593
1666
  "dtwo3/dist",
1594
1667
  distDir
@@ -1600,13 +1673,16 @@ async function initHole(dtwo) {
1600
1673
  "kdu/server-renderer": "kdu/server-renderer",
1601
1674
  kdu: await resolvePath(`kdu/dist/kdu.cjs${dtwo.options.dev ? "" : ".prod"}.js`)
1602
1675
  },
1676
+ // Kdu 3 mocks
1603
1677
  "estree-walker": "unenv/runtime/mock/proxy",
1604
1678
  "@babel/parser": "unenv/runtime/mock/proxy",
1605
1679
  "@kdujs/compiler-core": "unenv/runtime/mock/proxy",
1606
1680
  "@kdujs/compiler-dom": "unenv/runtime/mock/proxy",
1607
1681
  "@kdujs/compiler-ssr": "unenv/runtime/mock/proxy",
1608
1682
  "@kdujs/devtools-api": "kdu-devtools-stub",
1683
+ // Paths
1609
1684
  "#paths": resolve(distDir, "core/runtime/hole/paths"),
1685
+ // Dtwo aliases
1610
1686
  ...dtwo.options.alias
1611
1687
  },
1612
1688
  replace: {
@@ -1615,10 +1691,12 @@ async function initHole(dtwo) {
1615
1691
  "process.env.DTWO_NO_SCRIPTS": !!dtwo.options.experimental.noScripts && !dtwo.options.dev,
1616
1692
  "process.env.DTWO_INLINE_STYLES": !!dtwo.options.experimental.inlineSSRStyles,
1617
1693
  "process.env.DTWO_PAYLOAD_EXTRACTION": !!dtwo.options.experimental.payloadExtraction,
1694
+ "process.env.DTWO_COMPONENT_ISLANDS": !!dtwo.options.experimental.componentIslands,
1618
1695
  "process.dev": dtwo.options.dev,
1619
1696
  __KDU_PROD_DEVTOOLS__: false
1620
1697
  },
1621
1698
  rollupConfig: {
1699
+ output: {},
1622
1700
  plugins: []
1623
1701
  }
1624
1702
  });
@@ -1632,6 +1710,8 @@ async function initHole(dtwo) {
1632
1710
  if (!dtwo.options.experimental.inlineSSRStyles) {
1633
1711
  holeConfig.virtual["#build/dist/server/styles.mjs"] = "export default {}";
1634
1712
  }
1713
+ holeConfig.rollupConfig.plugins = await holeConfig.rollupConfig.plugins || [];
1714
+ holeConfig.rollupConfig.plugins = Array.isArray(holeConfig.rollupConfig.plugins) ? holeConfig.rollupConfig.plugins : [holeConfig.rollupConfig.plugins];
1635
1715
  holeConfig.rollupConfig.plugins.push(
1636
1716
  ImportProtectionPlugin.rollup({
1637
1717
  rootDir: dtwo.options.rootDir,
@@ -1674,7 +1754,9 @@ async function initHole(dtwo) {
1674
1754
  await copyPublicAssets(hole);
1675
1755
  await prerender(hole);
1676
1756
  if (!dtwo.options._generate) {
1757
+ logger.restoreAll();
1677
1758
  await build$1(hole);
1759
+ logger.wrapAll();
1678
1760
  } else {
1679
1761
  const distDir2 = resolve(dtwo.options.rootDir, "dist");
1680
1762
  if (!existsSync(distDir2)) {
@@ -1703,6 +1785,126 @@ async function initHole(dtwo) {
1703
1785
  }
1704
1786
  }
1705
1787
 
1788
+ const schemaModule = defineDtwoModule({
1789
+ meta: {
1790
+ name: "dtwo-config-schema"
1791
+ },
1792
+ async setup(_, dtwo) {
1793
+ if (!dtwo.options.experimental.configSchema) {
1794
+ return;
1795
+ }
1796
+ const resolver = createResolver(import.meta.url);
1797
+ const _resolveSchema = jiti(dirname(import.meta.url), {
1798
+ esmResolve: true,
1799
+ interopDefault: true,
1800
+ cache: false,
1801
+ requireCache: false,
1802
+ transformOptions: {
1803
+ babel: {
1804
+ plugins: [untypedPlugin]
1805
+ }
1806
+ }
1807
+ });
1808
+ dtwo.hook("prepare:types", (ctx) => {
1809
+ ctx.references.push({ path: "dtwo-config-schema" });
1810
+ ctx.references.push({ path: "schema/dtwo.schema.d.ts" });
1811
+ });
1812
+ let schema;
1813
+ dtwo.hook("modules:done", async () => {
1814
+ schema = await resolveSchema$1();
1815
+ });
1816
+ dtwo.hooks.hook("build:done", async () => {
1817
+ await dtwo.hooks.callHook("schema:beforeWrite", schema);
1818
+ await writeSchema(schema);
1819
+ await dtwo.hooks.callHook("schema:written");
1820
+ });
1821
+ if (dtwo.options.dev) {
1822
+ const filesToWatch = await Promise.all(dtwo.options._layers.map(
1823
+ (layer) => resolver.resolve(layer.config.rootDir, "dtwo.schema.*")
1824
+ ));
1825
+ const watcher = chokidar.watch(filesToWatch, {
1826
+ ...dtwo.options.watchers.chokidar,
1827
+ ignoreInitial: true
1828
+ });
1829
+ const onChange = debounce(async () => {
1830
+ schema = await resolveSchema$1();
1831
+ await dtwo.hooks.callHook("schema:beforeWrite", schema);
1832
+ await writeSchema(schema);
1833
+ await dtwo.hooks.callHook("schema:written");
1834
+ });
1835
+ watcher.on("all", onChange);
1836
+ dtwo.hook("close", () => watcher.close());
1837
+ }
1838
+ async function resolveSchema$1() {
1839
+ globalThis.defineDtwoSchema = (val) => val;
1840
+ const schemaDefs = [dtwo.options.$schema];
1841
+ for (const layer of dtwo.options._layers) {
1842
+ const filePath = await resolver.resolvePath(resolve(layer.config.rootDir, "dtwo.schema"));
1843
+ if (filePath && existsSync(filePath)) {
1844
+ let loadedConfig;
1845
+ try {
1846
+ loadedConfig = _resolveSchema(filePath);
1847
+ } catch (err) {
1848
+ console.warn(
1849
+ "[dtwo-config-schema] Unable to load schema from",
1850
+ filePath,
1851
+ err
1852
+ );
1853
+ continue;
1854
+ }
1855
+ schemaDefs.push(loadedConfig);
1856
+ }
1857
+ }
1858
+ await dtwo.hooks.callHook("schema:extend", schemaDefs);
1859
+ const schemas = await Promise.all(
1860
+ schemaDefs.map((schemaDef) => resolveSchema(schemaDef))
1861
+ );
1862
+ const schema2 = defu$1(...schemas);
1863
+ await dtwo.hooks.callHook("schema:resolved", schema2);
1864
+ return schema2;
1865
+ }
1866
+ async function writeSchema(schema2) {
1867
+ const isEmptySchema = !schema2.properties || Object.keys(schema2.properties).length === 0;
1868
+ if (isEmptySchema) {
1869
+ await rm(resolve(dtwo.options.buildDir, "schema"), { recursive: true }).catch(() => {
1870
+ });
1871
+ return;
1872
+ }
1873
+ await mkdir(resolve(dtwo.options.buildDir, "schema"), { recursive: true });
1874
+ await writeFile(
1875
+ resolve(dtwo.options.buildDir, "schema/dtwo.schema.json"),
1876
+ JSON.stringify(schema2, null, 2),
1877
+ "utf8"
1878
+ );
1879
+ const markdown = "# Dtwo Custom Config Schema" + generateMarkdown(schema2);
1880
+ await writeFile(
1881
+ resolve(dtwo.options.buildDir, "schema/dtwo.schema.md"),
1882
+ markdown,
1883
+ "utf8"
1884
+ );
1885
+ const _types = generateTypes(schema2, {
1886
+ addExport: true,
1887
+ interfaceName: "DtwoCustomSchema",
1888
+ partial: true
1889
+ });
1890
+ const types = _types + `
1891
+ export type CustomAppConfig = Exclude<DtwoCustomSchema['appConfig'], undefined>
1892
+
1893
+ declare module '@dtwo/schema' {
1894
+ interface DtwoConfig extends DtwoCustomSchema {}
1895
+ interface DtwoOptions extends DtwoCustomSchema {}
1896
+ interface AppConfigInput extends CustomAppConfig {}
1897
+ interface AppConfig extends CustomAppConfig {}
1898
+ }`;
1899
+ const typesPath = resolve(
1900
+ dtwo.options.buildDir,
1901
+ "schema/dtwo.schema.d.ts"
1902
+ );
1903
+ await writeFile(typesPath, types, "utf8");
1904
+ }
1905
+ }
1906
+ });
1907
+
1706
1908
  function createDtwo(options) {
1707
1909
  const hooks = createHooks();
1708
1910
  const dtwo = {
@@ -1739,6 +1941,7 @@ async function initDtwo(dtwo) {
1739
1941
  });
1740
1942
  const config = {
1741
1943
  rootDir: dtwo.options.rootDir,
1944
+ // Exclude top-level resolutions by plugins
1742
1945
  exclude: [join(dtwo.options.rootDir, "index.html")],
1743
1946
  patterns: kduAppPatterns(dtwo)
1744
1947
  };
@@ -1806,6 +2009,12 @@ async function initDtwo(dtwo) {
1806
2009
  name: "DtwoLoadingIndicator",
1807
2010
  filePath: resolve(dtwo.options.appDir, "components/dtwo-loading-indicator")
1808
2011
  });
2012
+ if (dtwo.options.experimental.componentIslands) {
2013
+ addComponent({
2014
+ name: "DtwoIsland",
2015
+ filePath: resolve(dtwo.options.appDir, "components/dtwo-island")
2016
+ });
2017
+ }
1809
2018
  if (!dtwo.options.dev && dtwo.options.experimental.payloadExtraction) {
1810
2019
  addPlugin(resolve(dtwo.options.appDir, "plugins/payload.client"));
1811
2020
  }
@@ -1841,6 +2050,7 @@ async function loadDtwo(opts) {
1841
2050
  include: options._layers.filter((i) => i.cwd && i.cwd.includes("node_modules")).map((i) => new RegExp(`(^|\\/)${escapeRE(i.cwd.split("node_modules/").pop())}(\\/|$)(?!node_modules\\/)`))
1842
2051
  }
1843
2052
  }]);
2053
+ options._modules.push(schemaModule);
1844
2054
  options.modulesDir.push(resolve(options.workspaceDir, "node_modules"));
1845
2055
  options.modulesDir.push(resolve(pkgDir, "node_modules"));
1846
2056
  options.build.transpile.push("@dtwo/ui-templates");
@@ -1957,13 +2167,16 @@ const schemaTemplate = {
1957
2167
  importName: m.entryPath || m.meta?.name
1958
2168
  })).filter((m) => m.configKey && m.name && !adHocModules.includes(m.name));
1959
2169
  const relativeRoot = relative(resolve(dtwo.options.buildDir, "types"), dtwo.options.rootDir);
2170
+ const getImportName = (name) => (name.startsWith(".") ? "./" + join(relativeRoot, name) : name).replace(/\.\w+$/, "");
2171
+ const modules = moduleInfo.map((meta) => [genString(meta.configKey), getImportName(meta.importName)]);
1960
2172
  return [
1961
2173
  "import { DtwoModule } from '@dtwo/schema'",
1962
2174
  "declare module '@dtwo/schema' {",
1963
2175
  " interface DtwoConfig {",
1964
- ...moduleInfo.filter(Boolean).map(
1965
- (meta) => ` [${genString(meta.configKey)}]?: typeof ${genDynamicImport(meta.importName.startsWith(".") ? "./" + join(relativeRoot, meta.importName) : meta.importName, { wrapper: false })}.default extends DtwoModule<infer O> ? Partial<O> : Record<string, any>`
2176
+ ...modules.map(
2177
+ ([configKey, importName]) => ` [${configKey}]?: typeof ${genDynamicImport(importName, { wrapper: false })}.default extends DtwoModule<infer O> ? Partial<O> : Record<string, any>`
1966
2178
  ),
2179
+ modules.length > 0 ? ` modules?: (DtwoModule | string | [DtwoModule | string, Record<string, any>] | ${modules.map(([configKey, importName]) => `[${genString(importName)}, DtwoConfig[${configKey}]]`).join(" | ")})[],` : "",
1967
2180
  " }",
1968
2181
  generateTypes(
1969
2182
  await resolveSchema(Object.fromEntries(Object.entries(dtwo.options.runtimeConfig).filter(([key]) => key !== "public"))),
@@ -2064,6 +2277,7 @@ const publicPathTemplate = {
2064
2277
  " const publicBase = appConfig.cdnURL || appConfig.baseURL",
2065
2278
  " return path.length ? joinURL(publicBase, ...path) : publicBase",
2066
2279
  "}",
2280
+ // On server these are registered directly in packages/dtwo/src/core/runtime/hole/renderer.ts
2067
2281
  "if (process.client) {",
2068
2282
  " globalThis.__buildAssetsURL = buildAssetsURL",
2069
2283
  " globalThis.__publicAssetsURL = publicAssetsURL",
@@ -2080,9 +2294,11 @@ const dtwoConfigTemplate = {
2080
2294
  function _resolveId(id) {
2081
2295
  return resolvePath$1(id, {
2082
2296
  url: [
2297
+ // @ts-ignore
2083
2298
  global.__DTWO_PREPATHS__,
2084
2299
  import.meta.url,
2085
2300
  process.cwd(),
2301
+ // @ts-ignore
2086
2302
  global.__DTWO_PATHS__
2087
2303
  ]
2088
2304
  });
@@ -2090,22 +2306,22 @@ function _resolveId(id) {
2090
2306
 
2091
2307
  const defaultTemplates = {
2092
2308
  __proto__: null,
2093
- kduShim: kduShim,
2094
2309
  appComponentTemplate: appComponentTemplate,
2095
- rootComponentTemplate: rootComponentTemplate,
2096
- errorComponentTemplate: errorComponentTemplate,
2097
- cssTemplate: cssTemplate,
2310
+ appConfigDeclarationTemplate: appConfigDeclarationTemplate,
2311
+ appConfigTemplate: appConfigTemplate,
2312
+ clientConfigTemplate: clientConfigTemplate,
2098
2313
  clientPluginTemplate: clientPluginTemplate,
2099
- serverPluginTemplate: serverPluginTemplate,
2100
- pluginsDeclaration: pluginsDeclaration,
2101
- schemaTemplate: schemaTemplate,
2314
+ cssTemplate: cssTemplate,
2315
+ dtwoConfigTemplate: dtwoConfigTemplate,
2316
+ errorComponentTemplate: errorComponentTemplate,
2317
+ kduShim: kduShim,
2102
2318
  layoutTemplate: layoutTemplate,
2103
2319
  middlewareTemplate: middlewareTemplate,
2104
- clientConfigTemplate: clientConfigTemplate,
2105
- appConfigDeclarationTemplate: appConfigDeclarationTemplate,
2106
- appConfigTemplate: appConfigTemplate,
2320
+ pluginsDeclaration: pluginsDeclaration,
2107
2321
  publicPathTemplate: publicPathTemplate,
2108
- dtwoConfigTemplate: dtwoConfigTemplate
2322
+ rootComponentTemplate: rootComponentTemplate,
2323
+ schemaTemplate: schemaTemplate,
2324
+ serverPluginTemplate: serverPluginTemplate
2109
2325
  };
2110
2326
 
2111
2327
  function createApp(dtwo, options = {}) {
@@ -2141,7 +2357,10 @@ async function generateApp(dtwo, app, options = {}) {
2141
2357
  async function resolveApp(dtwo, app) {
2142
2358
  if (!app.mainComponent) {
2143
2359
  app.mainComponent = await findPath(
2144
- dtwo.options._layers.flatMap((layer) => [`${layer.config.srcDir}/App`, `${layer.config.srcDir}/app`])
2360
+ dtwo.options._layers.flatMap((layer) => [
2361
+ join(layer.config.srcDir, "App"),
2362
+ join(layer.config.srcDir, "app")
2363
+ ])
2145
2364
  );
2146
2365
  }
2147
2366
  if (!app.mainComponent) {
@@ -2151,7 +2370,9 @@ async function resolveApp(dtwo, app) {
2151
2370
  app.rootComponent = await findPath(["~/app.root", resolve(dtwo.options.appDir, "components/dtwo-root.kdu")]);
2152
2371
  }
2153
2372
  if (!app.errorComponent) {
2154
- app.errorComponent = await findPath(["~/error"]) || resolve(dtwo.options.appDir, "components/dtwo-error-page.kdu");
2373
+ app.errorComponent = await findPath(
2374
+ dtwo.options._layers.map((layer) => join(layer.config.srcDir, "error"))
2375
+ ) ?? resolve(dtwo.options.appDir, "components/dtwo-error-page.kdu");
2155
2376
  }
2156
2377
  app.layouts = {};
2157
2378
  for (const config of dtwo.options._layers.map((layer) => layer.config)) {