eddev 2.0.0-beta.31 → 2.0.0-beta.34

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.
@@ -1,5 +1,6 @@
1
1
  import { RouteMetaTag } from "../lib/routing";
2
2
  type Props = {
3
+ debugKey?: string;
3
4
  tags: RouteMetaTag[];
4
5
  };
5
6
  export declare function MetaTags(props: Props): import("react/jsx-runtime").JSX.Element;
@@ -1,17 +1,17 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { Fragment } from "react/jsx-runtime";
1
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
3
2
  export function MetaTags(props) {
4
- return (_jsx(Fragment, { children: props.tags?.map((tag, i) => {
5
- const Component = tag.tagName;
6
- const props = { ...tag.attributes };
7
- if (tag.inner) {
8
- if (tag.tagName === "title") {
9
- props.children = tag.inner;
10
- }
11
- else {
12
- props.dangerouslySetInnerHTML = { __html: tag.inner };
13
- }
3
+ return (_jsx(_Fragment, { children: props.tags?.map((tag, i) => {
4
+ const Tag = tag.tagName;
5
+ const tagProps = Array.isArray(tag.attributes) ? {} : { ...tag.attributes };
6
+ if (tag.tagName === "title") {
7
+ tagProps.children = tag.inner ?? "";
14
8
  }
15
- return _jsx(Component, { ...props }, i);
9
+ else if (tag.tagName === "script" || !!tag.inner) {
10
+ tagProps.dangerouslySetInnerHTML = { __html: tag.inner ?? "" };
11
+ }
12
+ if (tagProps.async === "") {
13
+ tagProps.async = true;
14
+ }
15
+ return _jsx(Tag, { "data-test": props.debugKey + "_" + i, ...tagProps }, i);
16
16
  }) }));
17
17
  }
@@ -1,7 +1,7 @@
1
1
  import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useEffect, useMemo, useRef, useState } from "react";
3
- import { useTailwindConfig } from "../hooks/useTailwind.js";
4
3
  import { usePersistState } from "../hooks/usePersistState.js";
4
+ import { useTailwindConfig } from "../hooks/useTailwind.js";
5
5
  function parseBreakpointMin(breakpoint) {
6
6
  return parseInt(breakpoint);
7
7
  }
@@ -1,6 +1,7 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import "../../../../../css/devtools.css";
3
3
  import { BreakpointIndicator } from "./BreakpointIndicator.js";
4
+ import { GridIndicator } from "./GridIndicator";
4
5
  export default function DevUI() {
5
- return (_jsx("div", { className: "eddev-ui", children: _jsx(BreakpointIndicator, {}) }));
6
+ return (_jsxs("div", { className: "eddev-ui", children: [_jsx(BreakpointIndicator, {}), _jsx(GridIndicator, {})] }));
6
7
  }
@@ -0,0 +1 @@
1
+ export declare function GridIndicator(): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,24 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useEffect } from "react";
3
+ import { usePersistState } from "../hooks/usePersistState.js";
4
+ export function GridIndicator() {
5
+ const [enabled, setEnabled] = usePersistState("gridOverlayEnabled", false);
6
+ useEffect(() => {
7
+ const handleKeyDown = (e) => {
8
+ if (e.key === "g" && e.ctrlKey) {
9
+ setEnabled((e) => !e);
10
+ }
11
+ };
12
+ window.addEventListener("keydown", handleKeyDown);
13
+ return () => {
14
+ window.removeEventListener("keydown", handleKeyDown);
15
+ };
16
+ }, []);
17
+ const cols = [];
18
+ for (let i = 0; i < 12; i++) {
19
+ cols.push(_jsx("div", { style: { height: "100%", backgroundColor: "rgba(255,0,0,0.1)" } }));
20
+ }
21
+ if (!enabled)
22
+ return null;
23
+ return (_jsx("div", { style: { position: "fixed", inset: 0, zIndex: 999999999, pointerEvents: "none" }, children: _jsx("div", { className: "grid-auto", style: { height: "100%" }, children: cols }) }));
24
+ }
@@ -1 +1 @@
1
- export declare function usePersistState<T>(id: string, initial: T): [T, (value: T) => void];
1
+ export declare function usePersistState<T>(id: string, initial: T): [T, (value: T | ((current: T) => T)) => void];
@@ -24,8 +24,17 @@ export function usePersistState(id, initial) {
24
24
  return [
25
25
  value,
26
26
  (value) => {
27
- set(id, value);
28
- setValue(value);
27
+ if (typeof value === "function") {
28
+ return setValue((cur) => {
29
+ const next = value(cur);
30
+ set(id, next);
31
+ return next;
32
+ });
33
+ }
34
+ else {
35
+ set(id, value);
36
+ setValue(value);
37
+ }
29
38
  },
30
39
  ];
31
40
  }
@@ -1,14 +1,15 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useEffect } from "react";
3
- import { dynamic } from "../dynamic/dynamic.js";
2
+ import { lazy, useEffect } from "react";
4
3
  import { usePersistState } from "./hooks/usePersistState.js";
5
4
  import { useIsSSR } from "../routing/hooks/useIsSSR.js";
6
- const DevUI = dynamic(() => import("./components/DevUI.js"));
5
+ const DevUI = lazy(() => import("./components/DevUI.js"));
7
6
  export function DevUILoader() {
8
7
  const ssr = useIsSSR();
9
- const [render, setRender] = usePersistState("enable_devui", typeof window !== "undefined" && (env.dev || window["ENABLE_DEV_UI"]));
8
+ const [render, setRender] = usePersistState("enable_devui", false);
10
9
  useEffect(() => {
11
- if (document.location.search.includes("activate-dev-ui")) {
10
+ if (document.location.search.includes("activate-dev-ui") ||
11
+ window["ENABLE_DEV_UI"] ||
12
+ (env.client && env.dev)) {
12
13
  setRender(true);
13
14
  }
14
15
  }, []);
@@ -6,6 +6,11 @@ export type RouteMetaTag = {
6
6
  attributes: Record<string, string>;
7
7
  inner?: string;
8
8
  };
9
+ export type TrackerTags = {
10
+ head?: RouteMetaTag[];
11
+ body?: RouteMetaTag[];
12
+ footer?: RouteMetaTag[];
13
+ };
9
14
  export interface RouteData {
10
15
  view: string;
11
16
  editLink?: string;
@@ -19,6 +24,9 @@ export interface RouteData {
19
24
  footer: RouteMetaTag[];
20
25
  };
21
26
  }
27
+ export interface RouteDataWithTrackers extends RouteData {
28
+ trackers?: TrackerTags;
29
+ }
22
30
  export type RouteReturnState<T = {}> = {
23
31
  scrollTop: number;
24
32
  scrollLeft: number;
@@ -1,3 +1,5 @@
1
- import type { RouteData } from "../lib/routing/types.js";
1
+ import type { RouteData, TrackerTags } from "../lib/routing/types.js";
2
2
  import { ServerContext } from "./server-context.js";
3
- export declare function renderPageToSSRStream(pathname: string, initialData: RouteData, serverContext: ServerContext): Promise<unknown>;
3
+ export declare function renderPageToSSRStream(pathname: string, initialData: RouteData & {
4
+ trackers?: TrackerTags;
5
+ }, serverContext: ServerContext): Promise<unknown>;
@@ -22,7 +22,7 @@ export async function renderPageToSSRStream(pathname, initialData, serverContext
22
22
  console.error(err);
23
23
  },
24
24
  bootstrapModules: [clientManifest.inputs[clientManifest.handler].output.path],
25
- bootstrapScriptContent: `window.manifest = ${JSON.stringify(await clientManifest.json())}; window._PAGE_DATA = ${JSON.stringify(initialData)}`,
25
+ bootstrapScriptContent: `window._PAGE_DATA = ${JSON.stringify(initialData)};`,
26
26
  });
27
27
  });
28
28
  return stream;
@@ -1,6 +1,7 @@
1
1
  import { RequestHeaders } from "vinxi/http";
2
2
  import { UrlReplacerConf } from "./utils/replace-host.js";
3
3
  import { Manifest } from "vinxi/dist/types/types/manifest";
4
+ import { TrackerTags } from "../lib/routing/types.js";
4
5
  export type ServerContextArgs = {
5
6
  dev: boolean;
6
7
  origin: string;
@@ -9,6 +10,13 @@ export type ServerContextArgs = {
9
10
  export type ServerContextRuntime = {
10
11
  getManifest: (name: string) => Manifest;
11
12
  };
13
+ /**
14
+ * Data returned from WordPress origin /_appdata route
15
+ */
16
+ type ServerAppData = {
17
+ appData: any;
18
+ trackers: TrackerTags;
19
+ };
12
20
  export declare class ServerContext {
13
21
  dev: boolean;
14
22
  origin: string;
@@ -26,9 +34,7 @@ export declare class ServerContext {
26
34
  headers?: RequestHeaders;
27
35
  withAppData?: boolean;
28
36
  }): Promise<Response>;
29
- fetchAppData(req?: {
30
- headers?: RequestHeaders;
31
- }): Promise<any>;
37
+ fetchAppData(): Promise<ServerAppData>;
32
38
  extractRequestHeaders(req?: RequestHeaders): Partial<Record<import("vinxi/http").HTTPHeaderName, string | undefined>>;
33
39
  fetchNamedQuery(req: {
34
40
  name: string;
@@ -41,3 +47,4 @@ export declare class ServerContext {
41
47
  headers: RequestHeaders;
42
48
  }): Promise<Response>;
43
49
  }
50
+ export {};
@@ -76,27 +76,32 @@ export class ServerContext {
76
76
  _ssr: "1",
77
77
  _debug: this.dev ? "1" : undefined,
78
78
  });
79
- return this.fetchOrigin(fetchUrl, {
79
+ // console.log("Fetching route data", req.pathname)
80
+ const result = this.fetchOrigin(fetchUrl, {
80
81
  cache: "no-cache",
81
82
  replaceUrls: true,
82
83
  headers: {
83
84
  "Content-Type": "application/json",
84
85
  Accept: "application/json",
85
- ...this.extractRequestHeaders(req.headers),
86
86
  },
87
87
  });
88
+ // console.log("Finished fetching route data")
89
+ return result;
88
90
  }
89
- async fetchAppData(req) {
91
+ async fetchAppData() {
92
+ // console.log("Fetching app data")
90
93
  const response = await this.fetchOrigin("/_appdata", {
91
94
  cache: "no-cache",
92
95
  replaceUrls: true,
93
96
  headers: {
94
97
  "Content-Type": "application/json",
95
98
  Accept: "application/json",
96
- ...(req?.headers ? this.extractRequestHeaders(req.headers) : {}),
97
99
  },
98
100
  });
99
- return response.json();
101
+ // console.log("Decoding app data")
102
+ const result = await response.json();
103
+ // console.log("Done with app done")
104
+ return result;
100
105
  }
101
106
  extractRequestHeaders(req) {
102
107
  const headers = {};
@@ -1 +1 @@
1
- export declare const VERSION = "2.0.0-beta.31";
1
+ export declare const VERSION = "2.0.0-beta.34";
@@ -1 +1 @@
1
- export const VERSION = "2.0.0-beta.31";
1
+ export const VERSION = "2.0.0-beta.34";
@@ -25,12 +25,14 @@ export class DevServer {
25
25
  }
26
26
  async start() {
27
27
  console.setWorking(true);
28
+ // console.log("Setting up vinxi codegen")
28
29
  const codegen = createVinxiCodegen({
29
30
  mode: "development",
30
31
  project: this.project,
31
32
  serverless: true,
32
33
  });
33
34
  await codegen.runAndWatch();
35
+ // console.log("Has run once")
34
36
  const app = createVinxiApp({
35
37
  mode: "development",
36
38
  origin: this.origin,
@@ -45,7 +47,9 @@ export class DevServer {
45
47
  process.env.NITRO_PRESET ??
46
48
  process.env.NITRO_TARGET ??
47
49
  (process.versions.bun !== undefined ? "bun" : "node-server");
50
+ // console.log("Ensuring self signed cert")
48
51
  const keys = await ensureSelfSignedCert(this.hostname, this.project.rootDir);
52
+ // console.log("Done")
49
53
  const httpsConfig = {
50
54
  cert: keys.certFile,
51
55
  key: keys.keyFile,
@@ -61,6 +65,7 @@ export class DevServer {
61
65
  devApp = args.devApp;
62
66
  },
63
67
  });
68
+ // console.log("Creating dev server")
64
69
  await createDevServer(app, {
65
70
  force: false,
66
71
  devtools: false,
@@ -71,11 +76,13 @@ export class DevServer {
71
76
  preset: preset,
72
77
  https: httpsConfig,
73
78
  });
79
+ // console.log("Rnning listen hook")
74
80
  // @ts-ignore
75
81
  await app.hooks.callHook("app:dev:server:listener:creating", {
76
82
  app,
77
83
  devApp,
78
84
  });
85
+ // console.log("Creating another listener")
79
86
  const listener = await devApp.listen(this.port, {
80
87
  hostname: this.hostname,
81
88
  port: this.port,
@@ -88,6 +95,7 @@ export class DevServer {
88
95
  devApp,
89
96
  listener,
90
97
  });
98
+ // console.log("Listening")
91
99
  console.setWorking(false);
92
100
  console.setState({
93
101
  serverlessUrl: this.hostOrigin,
@@ -1,4 +1,4 @@
1
- import { code } from "ts-poet";
1
+ import { code, imp } from "ts-poet";
2
2
  import { FSCodegen } from "../utils/fs-codegen.js";
3
3
  export function getVinxiFolder(opts) {
4
4
  return (opts.mode === "development" ? "dev" : "prod") + (opts.serverless ? "" : "-spa");
@@ -261,11 +261,11 @@ export function createVinxiCodegen(opts) {
261
261
  }
262
262
 
263
263
  try {
264
- const [appData, response] = await Promise.all([
264
+ const [{appData, trackers}, response] = await Promise.all([
265
265
  serverContext.fetchAppData(),
266
266
  serverContext.fetchRouteData({
267
267
  pathname: url.pathname,
268
- withAppData: true,
268
+ withAppData: false,
269
269
  headers: {},
270
270
  query: {},
271
271
  }),
@@ -275,6 +275,7 @@ export function createVinxiCodegen(opts) {
275
275
  try {
276
276
  data = await response.json()
277
277
  data.appData = appData
278
+ data.trackers = trackers
278
279
  } catch (err) {
279
280
  data = {
280
281
  view: "_error",
@@ -410,12 +411,17 @@ export function createVinxiCodegen(opts) {
410
411
  return code /* ts */ `
411
412
  import { viewManifestReader } from 'eddev/_internal'
412
413
  import { dynamic } from 'eddev/dynamic'
413
- const manifest = {${Object.entries(viewManifest.views)
414
- .map(([name, view]) => {
415
- const importStatement = `dynamic(() => import(${JSON.stringify("../../../" + view.fileName)}))`;
416
- return JSON.stringify(view.slug) + ": " + importStatement;
417
- })
418
- .join(",\n")}}
414
+ const manifest = {${Object.entries(viewManifest.views).map(([name, view]) => {
415
+ const src = "../../../" + view.fileName;
416
+ let importStatement;
417
+ if (name.startsWith("_")) {
418
+ importStatement = imp(name + "=" + src);
419
+ }
420
+ else {
421
+ importStatement = code `dynamic(() => import(${JSON.stringify(src)}))`;
422
+ }
423
+ return code `${JSON.stringify(view.slug)}: ${importStatement},\n`;
424
+ })}}
419
425
 
420
426
  viewManifestReader.value = manifest
421
427
 
@@ -1,5 +1,5 @@
1
1
  import { codegen } from "@graphql-codegen/core";
2
- import { Kind } from "graphql";
2
+ import { Kind, visit } from "graphql";
3
3
  import { NoUnusedFragmentsRule, specifiedRules, validate } from "graphql/validation/index.js";
4
4
  import { join, relative, resolve } from "path";
5
5
  import { ProjectEnvUtils } from "../project/env.js";
@@ -62,6 +62,11 @@ export class GraphQLGenerator {
62
62
  this.project = project;
63
63
  this.schemaLoader = new GraphQLSchemaLoader(ProjectEnvUtils.get("DEBUG_GRAPHQL_URL"));
64
64
  this.fileLoader = createGraphQLFileLoader(this.project, [
65
+ {
66
+ baseFolder: "queries/fragments",
67
+ pattern: "**/*.graphql",
68
+ key: "fragments",
69
+ },
65
70
  {
66
71
  baseFolder: "blocks",
67
72
  pattern: "**/*.graphql",
@@ -72,11 +77,6 @@ export class GraphQLGenerator {
72
77
  pattern: "**/*.graphql",
73
78
  key: "views",
74
79
  },
75
- {
76
- baseFolder: "queries/fragments",
77
- pattern: "**/*.graphql",
78
- key: "fragments",
79
- },
80
80
  {
81
81
  baseFolder: "queries",
82
82
  pattern: "**/*.graphql",
@@ -345,6 +345,44 @@ export class GraphQLGenerator {
345
345
  * Use all rules, except for No Unused Fragments — since we prepend those fragments to every test
346
346
  */
347
347
  const validationRules = specifiedRules.filter((rule) => rule !== NoUnusedFragmentsRule);
348
+ /**
349
+ * Get all the base fragment definitions
350
+ */
351
+ const baseFragments = new Map();
352
+ Object.values(graphQLFiles.fragments)
353
+ .map((file) => file?.ast?.definitions ?? [])
354
+ .filter((def) => def.length > 0)
355
+ .flat()
356
+ .filter((n) => n.kind === Kind.FRAGMENT_DEFINITION)
357
+ .forEach((node) => baseFragments.set(node.name.value, node));
358
+ function findFragments(defs, map = new Map()) {
359
+ for (let def of defs) {
360
+ if (def.kind === Kind.FRAGMENT_DEFINITION) {
361
+ map.set(def.name.value, def);
362
+ }
363
+ }
364
+ for (let def of defs) {
365
+ visit(def, {
366
+ FragmentSpread: {
367
+ enter: (node) => {
368
+ const match = baseFragments.get(node.name.value);
369
+ if (match && !map.has(node.name.value)) {
370
+ map.set(node.name.value, match);
371
+ findFragments([match], map);
372
+ }
373
+ },
374
+ },
375
+ });
376
+ }
377
+ return {
378
+ fragments: [...map.values()],
379
+ definitions: defs.filter((n) => n.kind !== Kind.FRAGMENT_DEFINITION),
380
+ };
381
+ }
382
+ /**
383
+ * Only report the same error once (mostly for preventing repeats of the same fragment errors)
384
+ */
385
+ const errorsShown = new Map();
348
386
  /**
349
387
  * Validate everything
350
388
  */
@@ -356,19 +394,25 @@ export class GraphQLGenerator {
356
394
  }
357
395
  else {
358
396
  // Find all fragments
359
- const fragments = Object.values(graphQLFiles.fragments)
360
- .map((file) => file.ast.definitions)
361
- .filter((def) => def.length > 0)
362
- .filter((def) => def !== file.ast.definitions)
363
- .flat();
397
+ const { fragments, definitions } = findFragments(file?.ast?.definitions ?? []);
364
398
  const validationAST = {
365
399
  kind: Kind.DOCUMENT,
366
- definitions: [...fragments, ...file.ast.definitions],
400
+ definitions: [...fragments, ...definitions],
367
401
  loc: file.ast.loc,
368
402
  };
369
403
  const errors = validate(schema, validationAST, validationRules);
370
404
  if (errors.length > 0) {
371
405
  for (let error of errors) {
406
+ if (error.nodes && error.nodes[0]) {
407
+ const node = error.nodes[0];
408
+ if (errorsShown.has(node) && errorsShown.get(node)?.includes(error.message)) {
409
+ continue;
410
+ }
411
+ else {
412
+ errorsShown.set(node, [...(errorsShown.get(node) ?? []), error.message]);
413
+ }
414
+ }
415
+ // console.log(error.nodes?.map((n) => n.kind))
372
416
  report.push(new GraphQLValidationError(error.message, path, error.locations?.[0].line || 1, error.locations?.[0].column || 1, error.source?.body || ""));
373
417
  }
374
418
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eddev",
3
- "version": "2.0.0-beta.31",
3
+ "version": "2.0.0-beta.34",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -102,6 +102,7 @@
102
102
  "obj-console": "^1.0.2",
103
103
  "object-code": "^1.3.3",
104
104
  "qs": "^6.13.0",
105
+ "react-super-seo": "^1.1.9",
105
106
  "ts-poet": "^6.6.0",
106
107
  "ufo": "^1.3.1",
107
108
  "undent": "^0.1.0",