erudit 4.2.0 → 4.3.0-dev.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/app/components/Prose.vue +2 -0
  2. package/app/components/aside/major/contentNav/items/ContentNavTopic.vue +12 -1
  3. package/app/components/aside/major/search/SearchResult.vue +16 -2
  4. package/app/components/aside/minor/contributor/AsideMinorContributor.vue +7 -1
  5. package/app/components/main/MainStickyHeader.vue +5 -2
  6. package/app/components/main/MainStickyHeaderPreamble.vue +5 -2
  7. package/app/components/main/MainTopicPartPage.vue +3 -2
  8. package/app/components/main/MainTopicPartSwitch.vue +18 -7
  9. package/app/components/main/connections/Deps.vue +1 -4
  10. package/app/components/main/connections/MainConnections.vue +9 -3
  11. package/app/components/main/contentStats/ItemLastChanged.vue +3 -32
  12. package/app/components/main/contentStats/MainContentStats.vue +3 -4
  13. package/app/components/preview/Preview.vue +8 -6
  14. package/app/components/preview/PreviewScreen.vue +9 -7
  15. package/app/components/preview/screen/Unique.vue +3 -2
  16. package/app/composables/ads.ts +1 -1
  17. package/app/composables/analytics.ts +1 -1
  18. package/app/composables/lastChanged.ts +38 -5
  19. package/app/composables/og.ts +5 -5
  20. package/app/composables/scrollUp.ts +3 -1
  21. package/app/pages/book/[...bookId].vue +3 -2
  22. package/app/pages/group/[...groupId].vue +3 -2
  23. package/app/pages/page/[...pageId].vue +4 -2
  24. package/app/plugins/appSetup/config.ts +1 -0
  25. package/app/plugins/appSetup/global.ts +3 -0
  26. package/app/plugins/appSetup/index.ts +4 -1
  27. package/app/plugins/devReload.client.ts +13 -0
  28. package/app/router.options.ts +17 -3
  29. package/app/styles/main.css +2 -2
  30. package/modules/erudit/dependencies.ts +16 -0
  31. package/modules/erudit/index.ts +8 -1
  32. package/modules/erudit/setup/autoImports.ts +143 -0
  33. package/modules/erudit/setup/elements/globalTemplate.ts +10 -2
  34. package/modules/erudit/setup/elements/setup.ts +8 -14
  35. package/modules/erudit/setup/elements/tagsTable.ts +2 -18
  36. package/modules/erudit/setup/fullRestart.ts +5 -3
  37. package/modules/erudit/setup/namesTable.ts +33 -0
  38. package/modules/erudit/setup/problemChecks/setup.ts +60 -0
  39. package/modules/erudit/setup/problemChecks/shared.ts +4 -0
  40. package/modules/erudit/setup/problemChecks/template.ts +33 -0
  41. package/modules/erudit/setup/runtimeConfig.ts +12 -7
  42. package/nuxt.config.ts +14 -6
  43. package/package.json +5 -6
  44. package/server/api/problemScript/[...problemScriptPath].ts +245 -60
  45. package/server/erudit/build.ts +10 -4
  46. package/server/erudit/content/nav/build.ts +5 -5
  47. package/server/erudit/content/nav/front.ts +1 -0
  48. package/server/erudit/content/repository/deps.ts +33 -3
  49. package/server/erudit/content/resolve/index.ts +3 -3
  50. package/server/erudit/content/resolve/utils/contentError.ts +2 -2
  51. package/server/erudit/content/resolve/utils/insertContentResolved.ts +22 -5
  52. package/server/erudit/global.ts +5 -1
  53. package/server/erudit/importer.ts +69 -0
  54. package/server/erudit/index.ts +2 -2
  55. package/server/erudit/logger.ts +18 -10
  56. package/server/erudit/reloadSignal.ts +14 -0
  57. package/server/routes/_reload.ts +27 -0
  58. package/shared/types/contentConnections.ts +1 -0
  59. package/shared/types/frontContentNav.ts +2 -0
@@ -16,6 +16,29 @@ export type EruditServerImporter = Jiti['import'];
16
16
 
17
17
  export let jiti: Jiti;
18
18
 
19
+ /** Cached preamble that destructures all ERUDIT_GLOBAL keys into local vars. */
20
+ let eruditGlobalPreamble: string | undefined;
21
+
22
+ function getEruditGlobalPreamble(): string {
23
+ if (eruditGlobalPreamble !== undefined) return eruditGlobalPreamble;
24
+
25
+ const eg = (globalThis as any).ERUDIT_GLOBAL;
26
+ if (!eg || typeof eg !== 'object') {
27
+ eruditGlobalPreamble = '';
28
+ return eruditGlobalPreamble;
29
+ }
30
+
31
+ const names = Object.keys(eg).filter((n) => /^[a-zA-Z_$]\w*$/.test(n));
32
+ if (names.length === 0) {
33
+ eruditGlobalPreamble = '';
34
+ return eruditGlobalPreamble;
35
+ }
36
+
37
+ eruditGlobalPreamble =
38
+ 'var { ' + names.join(', ') + ' } = globalThis.ERUDIT_GLOBAL;\n';
39
+ return eruditGlobalPreamble;
40
+ }
41
+
19
42
  export async function setupServerImporter() {
20
43
  const jitiId = ERUDIT.paths.project();
21
44
  const defaultJiti = createJiti(jitiId, createBaseJitiOptions());
@@ -38,6 +61,18 @@ export async function setupServerImporter() {
38
61
 
39
62
  let code = getDefaultCode(opts);
40
63
 
64
+ //
65
+ // Inject ERUDIT_GLOBAL preamble for project files
66
+ // Destructures all erudit globals (tags, defineX, jsx runtime, etc.)
67
+ // into local variables so bare identifiers resolve correctly.
68
+ //
69
+ if (filename.startsWith(ERUDIT.paths.project() + '/')) {
70
+ const preamble = getEruditGlobalPreamble();
71
+ if (preamble) {
72
+ code = preamble + code;
73
+ }
74
+ }
75
+
41
76
  //
42
77
  // Insert IDs in `defineDocument(...)` calls
43
78
  //
@@ -58,6 +93,40 @@ export async function setupServerImporter() {
58
93
 
59
94
  code = insertProblemScriptId(toRelPath(filename), code);
60
95
 
96
+ //
97
+ // Rebind problem script creator src to this file's path.
98
+ //
99
+ // When defineProblemScript is called inside a shared utility file (e.g.
100
+ // shared.tsx) and then re-exported through an entry file (e.g.
101
+ // my-script.tsx), jiti injects the *shared* file's path as the scriptSrc.
102
+ // That makes the client fetch `/api/problemScript/.../shared.js`, which has
103
+ // no default export and fails at runtime.
104
+ //
105
+ // After the module's own code runs we inspect its default export: if it is
106
+ // a ProblemScriptInstanceCreator (a function whose return value has a
107
+ // `.generate` method), we wrap it so every created instance gets its
108
+ // scriptSrc replaced with **this** file's relative path – the actual entry
109
+ // file the API route will serve.
110
+ //
111
+ if (!filename.startsWith(ERUDIT.paths.project() + '/')) {
112
+ return { code };
113
+ }
114
+
115
+ const relFilePath = toRelPath(filename).replace(/\.[jt]sx?$/, '');
116
+ code += `
117
+ ;(function() {
118
+ var _eruditFileSrc = ${JSON.stringify(relFilePath)};
119
+ var _def = exports.default;
120
+ if (typeof _def !== 'function') return;
121
+ exports.default = Object.assign(function() {
122
+ var instance = _def.apply(this, arguments);
123
+ if (instance && typeof instance === 'object' && typeof instance.generate === 'function') {
124
+ return Object.assign({}, instance, { scriptSrc: _eruditFileSrc });
125
+ }
126
+ return instance;
127
+ }, _def);
128
+ })();`;
129
+
61
130
  return { code };
62
131
  },
63
132
  });
@@ -1,4 +1,4 @@
1
- import chalk from 'chalk';
1
+ import { styleText } from 'node:util';
2
2
  import type { EruditMode } from '@erudit-js/core/mode';
3
3
 
4
4
  import { setupServerLogger } from './logger';
@@ -72,7 +72,7 @@ async function setupServer() {
72
72
  await setupServerDatabase();
73
73
  await setupServerRepository();
74
74
  await setupServerContentNav();
75
- ERUDIT.log.success(chalk.green('Setup Complete!'));
75
+ ERUDIT.log.success(styleText('green', 'Setup Complete!'));
76
76
 
77
77
  await tryServerWatchProject();
78
78
  await buildServerErudit();
@@ -1,4 +1,4 @@
1
- import chalk from 'chalk';
1
+ import { styleText } from 'node:util';
2
2
  import { brandColorTitle } from '@erudit-js/core/brandTerminal';
3
3
 
4
4
  interface Logger {
@@ -15,8 +15,12 @@ export type EruditServerLogger = Logger & {
15
15
  };
16
16
 
17
17
  export async function setupServerLogger() {
18
- const serverLogger = createLogger(brandColorTitle + ' Server');
19
- const serverDebugLogger = createLogger(brandColorTitle + ' Server Debug');
18
+ const serverLogger = createLogger(
19
+ `${brandColorTitle}${styleText('gray', ' Server')}`,
20
+ );
21
+ const serverDebugLogger = createLogger(
22
+ `${brandColorTitle}${styleText('gray', ' Server Debug')}`,
23
+ );
20
24
  const debugLogEnabled = !!ERUDIT.config.public.debug.log;
21
25
 
22
26
  ERUDIT.log = new Proxy(serverLogger, {
@@ -42,32 +46,36 @@ export async function setupServerLogger() {
42
46
  }
43
47
 
44
48
  function createLogger(tag: string): Logger {
45
- const formattedTag = chalk.gray(`[${tag}]`);
49
+ const formattedTag = `${styleText('gray', '[')}${tag}${styleText('gray', ']')}`;
46
50
 
47
51
  return {
48
52
  info(message: any) {
49
- console.log(`${formattedTag} ${chalk.blueBright('ℹ')} ${message}`);
53
+ console.log(`${formattedTag} ${styleText('blueBright', 'ℹ')} ${message}`);
50
54
  },
51
55
  start(message: any) {
52
- console.log(`${formattedTag} ${chalk.magentaBright('◐')} ${message}`);
56
+ console.log(
57
+ `${formattedTag} ${styleText('magentaBright', '◐')} ${message}`,
58
+ );
53
59
  },
54
60
  success(message: any) {
55
- console.log(`${formattedTag} ${chalk.greenBright('✔')} ${message}`);
61
+ console.log(
62
+ `${formattedTag} ${styleText('greenBright', '✔')} ${message}`,
63
+ );
56
64
  },
57
65
  warn(message: any) {
58
66
  console.log(
59
- `${formattedTag} ${chalk.bgYellowBright.black(' WARN ')} ${message}`,
67
+ `${formattedTag} ${styleText(['bgYellowBright', 'black'], ' WARN ')} ${message}`,
60
68
  );
61
69
  },
62
70
  error(message: any) {
63
71
  console.log();
64
72
  console.log(
65
- `${formattedTag} ${chalk.bgRed.whiteBright(' ERROR ')} ${message}`,
73
+ `${formattedTag} ${styleText(['bgRed', 'whiteBright'], ' ERROR ')} ${message}`,
66
74
  );
67
75
  console.log();
68
76
  },
69
77
  stress(message: any) {
70
- return chalk.cyanBright(message);
78
+ return styleText('cyanBright', String(message));
71
79
  },
72
80
  };
73
81
  }
@@ -0,0 +1,14 @@
1
+ type ReloadCallback = () => void;
2
+
3
+ const subscribers = new Set<ReloadCallback>();
4
+
5
+ export function subscribeReload(callback: ReloadCallback): () => void {
6
+ subscribers.add(callback);
7
+ return () => subscribers.delete(callback);
8
+ }
9
+
10
+ export function triggerReload(): void {
11
+ for (const callback of subscribers) {
12
+ callback();
13
+ }
14
+ }
@@ -0,0 +1,27 @@
1
+ import { isDevLikeMode } from '@erudit-js/core/mode';
2
+ import { subscribeReload } from '../erudit/reloadSignal';
3
+
4
+ export default defineEventHandler((event) => {
5
+ if (!isDevLikeMode(ERUDIT.mode)) {
6
+ throw createError({ statusCode: 404 });
7
+ }
8
+
9
+ const { res, req } = event.node;
10
+
11
+ setResponseHeaders(event, {
12
+ 'Content-Type': 'text/event-stream',
13
+ 'Cache-Control': 'no-cache',
14
+ Connection: 'keep-alive',
15
+ });
16
+
17
+ res.write(': connected\n\n');
18
+
19
+ const unsub = subscribeReload(() => {
20
+ res.write('data: reload\n\n');
21
+ });
22
+
23
+ req.on('close', unsub);
24
+
25
+ // Keep connection open
26
+ return new Promise<void>(() => {});
27
+ });
@@ -24,6 +24,7 @@ export interface ContentAutoDep extends BaseContentDep {
24
24
  export interface ContentHardDep extends BaseContentDep {
25
25
  type: 'hard';
26
26
  reason: string;
27
+ uniques?: ContentDepUnique[];
27
28
  }
28
29
 
29
30
  export type ContentDep = ContentAutoDep | ContentHardDep;
@@ -1,4 +1,5 @@
1
1
  import type { ContentFlags } from '@erudit-js/core/content/flags';
2
+ import type { TopicPart } from '@erudit-js/core/content/topic';
2
3
 
3
4
  export interface FrontContentNavItemBase {
4
5
  shortId: string;
@@ -9,6 +10,7 @@ export interface FrontContentNavItemBase {
9
10
 
10
11
  export interface FrontContentNavTopic extends FrontContentNavItemBase {
11
12
  type: 'topic';
13
+ parts: TopicPart[];
12
14
  }
13
15
 
14
16
  export interface FrontContentNavPage extends FrontContentNavItemBase {