erudit 4.2.0-dev.1 → 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 (62) 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/aside/minor/news/AsideMinorNews.vue +1 -1
  6. package/app/components/main/MainStickyHeader.vue +5 -2
  7. package/app/components/main/MainStickyHeaderPreamble.vue +5 -2
  8. package/app/components/main/MainTopicPartPage.vue +3 -2
  9. package/app/components/main/MainTopicPartSwitch.vue +18 -7
  10. package/app/components/main/connections/Deps.vue +1 -4
  11. package/app/components/main/connections/MainConnections.vue +9 -3
  12. package/app/components/main/contentStats/ItemLastChanged.vue +3 -32
  13. package/app/components/main/contentStats/MainContentStats.vue +3 -4
  14. package/app/components/preview/Preview.vue +8 -6
  15. package/app/components/preview/PreviewScreen.vue +9 -7
  16. package/app/components/preview/screen/Unique.vue +3 -2
  17. package/app/composables/ads.ts +1 -1
  18. package/app/composables/analytics.ts +1 -1
  19. package/app/composables/lastChanged.ts +38 -5
  20. package/app/composables/og.ts +5 -5
  21. package/app/composables/phrases.ts +2 -0
  22. package/app/composables/scrollUp.ts +3 -1
  23. package/app/pages/book/[...bookId].vue +3 -2
  24. package/app/pages/group/[...groupId].vue +3 -2
  25. package/app/pages/page/[...pageId].vue +4 -2
  26. package/app/plugins/appSetup/config.ts +1 -0
  27. package/app/plugins/appSetup/global.ts +3 -0
  28. package/app/plugins/appSetup/index.ts +4 -1
  29. package/app/plugins/devReload.client.ts +13 -0
  30. package/app/router.options.ts +17 -3
  31. package/app/styles/main.css +2 -2
  32. package/modules/erudit/dependencies.ts +16 -0
  33. package/modules/erudit/index.ts +8 -1
  34. package/modules/erudit/setup/autoImports.ts +143 -0
  35. package/modules/erudit/setup/elements/globalTemplate.ts +10 -2
  36. package/modules/erudit/setup/elements/setup.ts +8 -14
  37. package/modules/erudit/setup/elements/tagsTable.ts +2 -18
  38. package/modules/erudit/setup/fullRestart.ts +5 -3
  39. package/modules/erudit/setup/namesTable.ts +33 -0
  40. package/modules/erudit/setup/problemChecks/setup.ts +60 -0
  41. package/modules/erudit/setup/problemChecks/shared.ts +4 -0
  42. package/modules/erudit/setup/problemChecks/template.ts +33 -0
  43. package/modules/erudit/setup/runtimeConfig.ts +12 -7
  44. package/nuxt.config.ts +14 -6
  45. package/package.json +5 -6
  46. package/server/api/problemScript/[...problemScriptPath].ts +245 -52
  47. package/server/erudit/build.ts +10 -4
  48. package/server/erudit/content/global/build.ts +43 -3
  49. package/server/erudit/content/nav/build.ts +5 -5
  50. package/server/erudit/content/nav/front.ts +1 -0
  51. package/server/erudit/content/repository/deps.ts +45 -6
  52. package/server/erudit/content/resolve/index.ts +3 -3
  53. package/server/erudit/content/resolve/utils/contentError.ts +2 -2
  54. package/server/erudit/content/resolve/utils/insertContentResolved.ts +29 -27
  55. package/server/erudit/global.ts +5 -1
  56. package/server/erudit/importer.ts +69 -0
  57. package/server/erudit/index.ts +2 -2
  58. package/server/erudit/logger.ts +18 -10
  59. package/server/erudit/reloadSignal.ts +14 -0
  60. package/server/routes/_reload.ts +27 -0
  61. package/shared/types/contentConnections.ts +1 -0
  62. package/shared/types/frontContentNav.ts +2 -0
@@ -13,7 +13,10 @@ export default defineNuxtPlugin({
13
13
  hooks: {
14
14
  'app:mounted': async () => {
15
15
  const { registerProseGlobals } = await import('#erudit/prose/global');
16
- registerProseGlobals();
16
+ await registerProseGlobals();
17
+
18
+ const { registerAutoImportGlobals } = await import('#erudit/autoImports');
19
+ await registerAutoImportGlobals();
17
20
 
18
21
  await setupHtmlBranding();
19
22
  await setupWelcomeMessage();
@@ -0,0 +1,13 @@
1
+ import { isDevLikeMode } from '@erudit-js/core/mode';
2
+
3
+ export default defineNuxtPlugin({
4
+ name: 'erudit-dev-reload',
5
+ setup() {
6
+ const runtimeConfig = useRuntimeConfig().public;
7
+ const mode = runtimeConfig.eruditMode as any;
8
+ if (!isDevLikeMode(mode) || !runtimeConfig.eruditReload) return;
9
+
10
+ const es = new EventSource('/_reload');
11
+ es.onmessage = () => window.location.reload();
12
+ },
13
+ });
@@ -2,9 +2,23 @@ import type { RouterOptions } from 'vue-router';
2
2
 
3
3
  export default {
4
4
  scrollBehavior(_to, _from, savedPosition) {
5
- if (!savedPosition && !_to.query.element) {
6
- return { top: 0 };
5
+ if (savedPosition) {
6
+ return savedPosition;
7
7
  }
8
- return savedPosition;
8
+ // On a hard reload, _from is START_LOCATION (matched is empty).
9
+ // Return false so the browser's native scroll restoration (history.scrollRestoration = 'auto')
10
+ // can restore the position without Vue Router fighting it with { top: 0 }.
11
+ if (!_from || _from.matched.length === 0) {
12
+ return false;
13
+ }
14
+ if (_to.query.element) {
15
+ return false;
16
+ }
17
+ // Only scroll to top when the page path actually changes,
18
+ // not for query-param-only replacements (e.g. ?q= from search input).
19
+ if (_to.path === _from.path) {
20
+ return false;
21
+ }
22
+ return { top: 0 };
9
23
  },
10
24
  } as RouterOptions;
@@ -27,7 +27,7 @@
27
27
  --text-main-2xl: calc(1.5 * var(--text-main));
28
28
 
29
29
  --default-transition-timing-function: cubic-bezier(0, 0, 0.5, 1);
30
- --default-transition-duration: calc(200ms * var(--duration-multiplier));
30
+ --default-transition-duration: calc(180ms * var(--duration-multiplier));
31
31
 
32
32
  --spacing-normal: calc(4 * var(--spacing));
33
33
  --spacing-big: calc(2 * var(--spacing-normal));
@@ -42,7 +42,7 @@
42
42
 
43
43
  --h-min-aside-item: 60px;
44
44
 
45
- --color-brand: light-dark(#00b886, #00dcb5);
45
+ --color-brand: light-dark(#00a545, #129adc);
46
46
  --color-bg-root: light-dark(#ebebeb, #141414);
47
47
  --color-bg-aside: light-dark(#f7f7f7, #1b1b1b);
48
48
  --color-bg-main: light-dark(#ffffff, #212121);
@@ -0,0 +1,16 @@
1
+ import type { Nuxt } from 'nuxt/schema';
2
+ import type { EruditDependencies } from '@erudit-js/core/dependencies';
3
+
4
+ export function addDependencies(
5
+ nuxt: Nuxt,
6
+ dependencies: EruditDependencies | undefined,
7
+ ) {
8
+ const transpile = (nuxt.options.build.transpile ||= []);
9
+ const optimizeDeps = nuxt.options.vite.optimizeDeps || {};
10
+ const optimizeDepsInclude = (optimizeDeps.include ||= []);
11
+
12
+ for (const [name, options] of Object.entries(dependencies ?? {})) {
13
+ if (options?.transpile) transpile.push(name);
14
+ if (options?.optimize) optimizeDepsInclude.push(name);
15
+ }
16
+ }
@@ -16,6 +16,9 @@ import {
16
16
  registerServerGlobals,
17
17
  } from './setup/globals';
18
18
  import { setupProseElements } from './setup/elements/setup';
19
+ import { setupProblemChecks } from './setup/problemChecks/setup';
20
+ import { setupAutoImports } from './setup/autoImports';
21
+ import { addDependencies } from './dependencies';
19
22
 
20
23
  export default defineNuxtModule({
21
24
  meta: { name: 'Erudit', configKey: 'erudit', version },
@@ -27,12 +30,16 @@ export default defineNuxtModule({
27
30
  await registerServerGlobals();
28
31
  await registerGlobalContentTypes();
29
32
 
30
- const { eruditRuntimeConfig, nuxtAugmentations } =
33
+ const { eruditConfig, eruditRuntimeConfig, nuxtAugmentations } =
31
34
  await setupEruditRuntimeConfig(nuxt);
32
35
  await setupWatchers(nuxt);
33
36
  await setupEruditFullRestart(nuxt);
34
37
  await setupEruditPublicAssets(nuxt);
35
38
  await setupProseElements(nuxt, eruditRuntimeConfig);
39
+ await setupProblemChecks(nuxt, eruditConfig);
40
+ await setupAutoImports(nuxt, eruditConfig);
41
+
42
+ addDependencies(nuxt, eruditConfig.dependencies);
36
43
 
37
44
  if (nuxtAugmentations) {
38
45
  for (const augment of nuxtAugmentations) {
@@ -0,0 +1,143 @@
1
+ import { writeFileSync } from 'node:fs';
2
+ import type { Nuxt } from 'nuxt/schema';
3
+ import { addTemplate, findPath } from 'nuxt/kit';
4
+ import { sn } from 'unslash';
5
+ import type { EruditConfig } from '@erudit-js/core/eruditConfig/config';
6
+
7
+ import { PROJECT_PATH } from '../env';
8
+ import { moduleLogger } from '../logger';
9
+ import { printGrayNamesTable } from './namesTable';
10
+
11
+ interface ResolvedAutoImport {
12
+ /** Config path as written by the user (e.g. './my-globals') */
13
+ configPath: string;
14
+ /** Absolute resolved path to the source file */
15
+ absPath: string;
16
+ /** #project/ relative path for TypeScript type declarations */
17
+ projectRelativePath: string;
18
+ /** Named export identifiers discovered from the file */
19
+ exportNames: string[];
20
+ }
21
+
22
+ export async function setupAutoImports(nuxt: Nuxt, eruditConfig: EruditConfig) {
23
+ const autoImportPaths = eruditConfig.autoImports ?? [];
24
+
25
+ if (autoImportPaths.length === 0) {
26
+ createEmptyTemplate(nuxt);
27
+ return;
28
+ }
29
+
30
+ const resolved: ResolvedAutoImport[] = [];
31
+
32
+ for (const configPath of autoImportPaths) {
33
+ const absPath = await findPath(configPath, {
34
+ cwd: PROJECT_PATH,
35
+ extensions: ['.ts', '.js'],
36
+ });
37
+
38
+ if (!absPath) {
39
+ throw new Error(`[autoImports] Failed to resolve path "${configPath}"!`);
40
+ }
41
+
42
+ // Discover named exports by dynamically importing the file
43
+ const mod = await import(absPath);
44
+ const exportNames = Object.keys(mod).filter((key) => key !== 'default');
45
+
46
+ if (exportNames.length === 0) {
47
+ moduleLogger.warn(
48
+ `[autoImports] File "${configPath}" has no named exports — skipping.`,
49
+ );
50
+ continue;
51
+ }
52
+
53
+ // Compute #project/ relative path for TypeScript type declarations
54
+ const normalizedAbsPath = absPath.replace(/\\/g, '/');
55
+ const normalizedProjectPath = PROJECT_PATH.replace(/\\/g, '/');
56
+ const projectRelativePath = normalizedAbsPath
57
+ .replace(normalizedProjectPath + '/', '')
58
+ .replace(/\.(ts|js)$/, '');
59
+
60
+ resolved.push({
61
+ configPath,
62
+ absPath: normalizedAbsPath,
63
+ projectRelativePath,
64
+ exportNames,
65
+ });
66
+ }
67
+
68
+ const allExportNames = resolved.flatMap((r) => r.exportNames);
69
+
70
+ createTemplate(nuxt, resolved, allExportNames);
71
+ createTypeDeclarations(resolved);
72
+
73
+ moduleLogger.success(
74
+ `Registered ${allExportNames.length} auto-import(s) from ${resolved.length} file(s)!`,
75
+ );
76
+
77
+ printGrayNamesTable(allExportNames);
78
+ }
79
+
80
+ function createEmptyTemplate(nuxt: Nuxt) {
81
+ addTemplate({
82
+ write: true,
83
+ filename: '#erudit/autoImports.ts',
84
+ getContents: () =>
85
+ `export const autoImportNames = new Set<string>();\nexport function registerAutoImportGlobals() { (globalThis as any).ERUDIT_GLOBAL = (globalThis as any).ERUDIT_GLOBAL || {}; }\n`,
86
+ });
87
+
88
+ const alias = (nuxt.options.alias ||= {});
89
+ alias['#erudit/autoImports'] =
90
+ nuxt.options.buildDir + '/#erudit/autoImports.ts';
91
+ }
92
+
93
+ function createTemplate(
94
+ nuxt: Nuxt,
95
+ resolved: ResolvedAutoImport[],
96
+ allExportNames: string[],
97
+ ) {
98
+ const importStatements = resolved
99
+ .map((r) => {
100
+ const names = r.exportNames.join(', ');
101
+ const importPath = r.absPath.replace(/\.(ts|js)$/, '');
102
+ return `import { ${names} } from '${importPath}';`;
103
+ })
104
+ .join('\n');
105
+
106
+ const template =
107
+ `
108
+ ${importStatements}
109
+
110
+ export const autoImportNames = new Set<string>(${JSON.stringify(allExportNames)});
111
+
112
+ export function registerAutoImportGlobals() {
113
+ (globalThis as any).ERUDIT_GLOBAL = (globalThis as any).ERUDIT_GLOBAL || {};
114
+ Object.assign((globalThis as any).ERUDIT_GLOBAL, {
115
+ ${allExportNames.join(',\n ')}
116
+ });
117
+ }
118
+ `.trim() + '\n';
119
+
120
+ addTemplate({
121
+ write: true,
122
+ filename: '#erudit/autoImports.ts',
123
+ getContents: () => template,
124
+ });
125
+
126
+ const alias = (nuxt.options.alias ||= {});
127
+ alias['#erudit/autoImports'] =
128
+ nuxt.options.buildDir + '/#erudit/autoImports.ts';
129
+ }
130
+
131
+ function createTypeDeclarations(resolved: ResolvedAutoImport[]) {
132
+ let dts = 'export {}\n\ndeclare global {\n';
133
+
134
+ for (const r of resolved) {
135
+ for (const name of r.exportNames) {
136
+ dts += ` const ${name}: typeof import('#project/${r.projectRelativePath}')['${name}']\n`;
137
+ }
138
+ }
139
+
140
+ dts += '}\n';
141
+
142
+ writeFileSync(sn(PROJECT_PATH, '.erudit/types/autoImports.d.ts'), dts);
143
+ }
@@ -53,17 +53,25 @@ export const elementsGlobals = {
53
53
  .join(',\n ')}
54
54
  }
55
55
 
56
+ export const eruditGlobalNames = new Set<string>([
57
+ ...Object.values(coreElements).flatMap((el: any) => (el.tags ?? []).map((t: any) => String(t.tagName))),
58
+ ...Object.keys(elementsGlobals),
59
+ 'jsx', 'jsxs', 'Fragment', 'defineProblemScript',
60
+ ]);
61
+
56
62
  export function registerProseGlobals() {
63
+ (globalThis as any).ERUDIT_GLOBAL = (globalThis as any).ERUDIT_GLOBAL || {};
64
+
57
65
  for (const coreElement of Object.values(coreElements)) {
58
66
  const tags = coreElement.tags || [];
59
67
  for (const tag of tags) {
60
- Object.assign(globalThis, {
68
+ Object.assign((globalThis as any).ERUDIT_GLOBAL, {
61
69
  [tag.tagName]: tag,
62
70
  });
63
71
  }
64
72
  }
65
73
 
66
- Object.assign(globalThis, {
74
+ Object.assign((globalThis as any).ERUDIT_GLOBAL, {
67
75
  // Make jsx runtime globally available (for prose generation in isolated modules like problem scripts)
68
76
  jsx,
69
77
  jsxs,
@@ -1,4 +1,3 @@
1
- import chalk from 'chalk';
2
1
  import type { Nuxt } from 'nuxt/schema';
3
2
  import { findPath } from 'nuxt/kit';
4
3
 
@@ -6,12 +5,13 @@ import type { ProseCoreElement } from '@erudit-js/prose';
6
5
 
7
6
  import type { EruditRuntimeConfig } from '../../../../shared/types/runtimeConfig';
8
7
  import { moduleLogger } from '../../logger';
8
+ import { printGrayNamesTable } from '../namesTable';
9
9
  import type { ElementData } from './shared';
10
- import { createTagsTable } from './tagsTable';
11
10
  import { createElementGlobalTypes } from './elementGlobalTypes';
12
11
  import { createGlobalTemplate } from './globalTemplate';
13
12
  import { createAppTemplate } from './appTemplate';
14
13
  import { PROJECT_PATH } from '../../env';
14
+ import { addDependencies } from '../../dependencies';
15
15
 
16
16
  const BUILTIN_ELEMENT_PATHS = [
17
17
  '@erudit-js/prose/elements/callout',
@@ -131,16 +131,7 @@ export async function setupProseElements(
131
131
  }
132
132
  }
133
133
 
134
- const transpile = (nuxt.options.build.transpile ||= []);
135
- const optimizeDeps = nuxt.options.vite.optimizeDeps || {};
136
- const optimizeDepsInclude = (optimizeDeps.include ||= []);
137
-
138
- for (const [name, options] of Object.entries(
139
- coreElement.dependencies ?? {},
140
- )) {
141
- if (options?.transpile) transpile.push(name);
142
- if (options?.optimize) optimizeDepsInclude.push(name);
143
- }
134
+ addDependencies(nuxt, coreElement.dependencies);
144
135
  }
145
136
 
146
137
  elementsData.push(elementData);
@@ -160,6 +151,9 @@ Registered ${elementsNumber} prose elements: ${schemasNumber} schema(s) and ${ta
160
151
  `.trim(),
161
152
  );
162
153
 
163
- const tagsTable = createTagsTable(elementsData);
164
- console.log(chalk.gray(tagsTable));
154
+ const tagNames = elementsData.flatMap((data) =>
155
+ data.coreElements.flatMap((coreElement) => Object.keys(coreElement.tags)),
156
+ );
157
+
158
+ printGrayNamesTable(tagNames.map((t) => `<${t}>`));
165
159
  }
@@ -1,28 +1,12 @@
1
1
  import type { ElementData } from './shared';
2
+ import { createNamesTable } from '../namesTable';
2
3
 
3
4
  export function createTagsTable(elementsData: ElementData[], columns = 4) {
4
5
  const tagNames = elementsData.flatMap((data) =>
5
6
  data.coreElements.flatMap((coreElement) => Object.keys(coreElement.tags)),
6
7
  );
7
8
 
8
- if (tagNames.length === 0) return '';
9
-
10
- const cols = Math.max(1, columns);
11
9
  const formatted = tagNames.map((t) => `<${t}>`);
12
- const maxLen = Math.max(...formatted.map((s) => s.length));
13
- const colWidth = maxLen + 2; // padding between columns
14
-
15
- const rows = Math.ceil(formatted.length / cols);
16
- const lines: string[] = [];
17
-
18
- for (let r = 0; r < rows; r++) {
19
- const start = r * cols;
20
- const rowItems = formatted.slice(start, start + cols);
21
- const padded = rowItems.map((s) => s.padEnd(colWidth, ' '));
22
- lines.push(padded.join('').replace(/\s+$/u, '')); // trim trailing spaces on the row
23
- }
24
-
25
- const tagTable = lines.join('\n');
26
10
 
27
- return tagTable;
11
+ return createNamesTable(formatted, columns);
28
12
  }
@@ -1,6 +1,6 @@
1
1
  import type { Nuxt } from '@nuxt/schema';
2
2
  import { debounce } from 'perfect-debounce';
3
- import chalk from 'chalk';
3
+ import { styleText } from 'node:util';
4
4
  import { sn } from 'unslash';
5
5
 
6
6
  import { ERUDIT_PACKAGE_WATCHER, ERUDIT_PROJECT_WATCHER } from '../watcher';
@@ -15,8 +15,10 @@ export async function setupEruditFullRestart(nuxt: Nuxt) {
15
15
  changedPaths.clear();
16
16
 
17
17
  moduleLogger.warn(
18
- `${chalk.yellow('Full restart due to critical file change(s):')}\n\n` +
19
- files.map((p, i) => chalk.gray(`${i + 1} -`) + ` "${p}"`).join('\n'),
18
+ `${styleText('yellow', 'Full restart due to critical file change(s):')}\n\n` +
19
+ files
20
+ .map((p, i) => styleText('gray', `${i + 1} -`) + ` "${p}"`)
21
+ .join('\n'),
20
22
  );
21
23
 
22
24
  await nuxt.callHook('close', nuxt);
@@ -0,0 +1,33 @@
1
+ import { styleText } from 'node:util';
2
+
3
+ export function createNamesTable(names: string[], columns = 4) {
4
+ if (names.length === 0) return '';
5
+
6
+ const cols = Math.max(1, columns);
7
+ const maxLen = Math.max(...names.map((s) => s.length));
8
+ const colWidth = maxLen + 2;
9
+
10
+ const rows = Math.ceil(names.length / cols);
11
+ const lines: string[] = [];
12
+
13
+ for (let r = 0; r < rows; r++) {
14
+ const start = r * cols;
15
+ const rowItems = names.slice(start, start + cols);
16
+ const padded = rowItems.map((s) => s.padEnd(colWidth, ' '));
17
+ lines.push(padded.join('').replace(/\s+$/u, ''));
18
+ }
19
+
20
+ return lines.join('\n');
21
+ }
22
+
23
+ export function printGrayNamesTable(names: string[], columns = 4) {
24
+ const table = createNamesTable(names, columns);
25
+ if (!table) return;
26
+
27
+ console.log(
28
+ table
29
+ .split('\n')
30
+ .map((line) => styleText('gray', line))
31
+ .join('\n'),
32
+ );
33
+ }
@@ -0,0 +1,60 @@
1
+ import type { Nuxt } from 'nuxt/schema';
2
+ import { findPath } from 'nuxt/kit';
3
+ import type { EruditConfig } from '@erudit-js/core/eruditConfig/config';
4
+ import type { ProblemChecker } from '@erudit-js/core/problemCheck';
5
+
6
+ import type { ResolvedProblemCheck } from './shared';
7
+ import { PROJECT_PATH } from '../../env';
8
+ import { moduleLogger } from '../../logger';
9
+ import { printGrayNamesTable } from '../namesTable';
10
+ import { createTemplate } from './template';
11
+
12
+ export async function setupProblemChecks(
13
+ nuxt: Nuxt,
14
+ eruditConfig: EruditConfig,
15
+ ) {
16
+ const resolvedProblemChecks: ResolvedProblemCheck[] = [];
17
+ const seenProblemChecks = new Set<string>();
18
+
19
+ for (const strProblemCheck of eruditConfig.problemChecks ?? []) {
20
+ const absPath = await findPath(strProblemCheck, {
21
+ cwd: PROJECT_PATH,
22
+ extensions: ['.ts', '.js'],
23
+ });
24
+
25
+ if (!absPath) {
26
+ throw new Error(
27
+ `Failed to resolve path for problem check "${strProblemCheck}"!`,
28
+ );
29
+ }
30
+
31
+ const defaultExport = (await import(absPath)).default as ProblemChecker;
32
+ const name = defaultExport?.name;
33
+
34
+ if (!name) {
35
+ throw new Error(
36
+ `Problem check at "${strProblemCheck}" does not have a name!`,
37
+ );
38
+ }
39
+
40
+ if (seenProblemChecks.has(name)) {
41
+ throw new Error(`Duplicate problem check name "${name}" found!`);
42
+ }
43
+
44
+ seenProblemChecks.add(name);
45
+ resolvedProblemChecks.push({
46
+ name,
47
+ absPath,
48
+ });
49
+ }
50
+
51
+ createTemplate(nuxt, resolvedProblemChecks);
52
+
53
+ if (resolvedProblemChecks.length > 0) {
54
+ moduleLogger.success(
55
+ `Registered ${resolvedProblemChecks.length} problem check(s)!`,
56
+ );
57
+
58
+ printGrayNamesTable(resolvedProblemChecks.map((c) => c.name));
59
+ }
60
+ }
@@ -0,0 +1,4 @@
1
+ export interface ResolvedProblemCheck {
2
+ name: string;
3
+ absPath: string;
4
+ }
@@ -0,0 +1,33 @@
1
+ import type { Nuxt } from 'nuxt/schema';
2
+ import { addTemplate } from 'nuxt/kit';
3
+
4
+ import type { ResolvedProblemCheck } from './shared';
5
+
6
+ export function createTemplate(
7
+ nuxt: Nuxt,
8
+ problemChecks: ResolvedProblemCheck[],
9
+ ) {
10
+ const template = `
11
+ import type { ProblemCheckers } from '@erudit-js/core/problemCheck';
12
+
13
+ ${problemChecks
14
+ .map(
15
+ (check) =>
16
+ `import ${check.name} from '${check.absPath.replace(/\.(ts|js)$/, '')}';`,
17
+ )
18
+ .join('\n')}
19
+
20
+ export const problemCheckers: ProblemCheckers = {
21
+ ${problemChecks.map((check) => `${check.name},`).join('\n ')}
22
+ }
23
+ `.trim();
24
+
25
+ addTemplate({
26
+ write: true,
27
+ filename: '#erudit/checks.ts',
28
+ getContents: () => template,
29
+ });
30
+
31
+ const alias = (nuxt.options.alias ||= {});
32
+ alias['#erudit/checks'] = nuxt.options.buildDir + '/#erudit/checks.ts';
33
+ }
@@ -2,8 +2,9 @@ import { existsSync } from 'node:fs';
2
2
  import type { Nuxt } from '@nuxt/schema';
3
3
  import { sn } from 'unslash';
4
4
  import type { EruditConfig } from '@erudit-js/core/eruditConfig/config';
5
+ import { isDevLikeMode } from '@erudit-js/core/mode';
5
6
 
6
- import { PROJECT_PATH } from '../env.js';
7
+ import { ERUDIT_MODE, PROJECT_PATH } from '../env.js';
7
8
  import { moduleLogger } from '../logger.js';
8
9
  import type {
9
10
  EruditPublicRuntimeConfig,
@@ -26,25 +27,28 @@ export async function setupEruditRuntimeConfig(nuxt: Nuxt) {
26
27
  // Erudit Runtime Config
27
28
  //
28
29
 
29
- nuxt.options.runtimeConfig.erudit = {
30
+ nuxt.options.runtimeConfig.erudit = (<EruditRuntimeConfig>{
30
31
  elements: eruditConfig.elements || [],
31
32
  countElements: eruditConfig.countElements || [],
32
33
  indexPage: eruditConfig.indexPage,
33
- } satisfies EruditRuntimeConfig;
34
+ }) satisfies EruditRuntimeConfig;
34
35
 
35
36
  //
36
37
  // Erudit Public Runtime Config
37
38
  //
38
39
 
39
- nuxt.options.runtimeConfig.public.erudit = {
40
+ nuxt.options.runtimeConfig.public.erudit = (<EruditPublicRuntimeConfig>{
40
41
  debug: {
41
42
  ads: eruditConfig.debug?.ads ?? false,
42
43
  log: eruditConfig.debug?.log ?? false,
43
44
  slowTransition: eruditConfig.debug?.slowTransition ?? false,
44
45
  fakeApi: {
45
- repository: eruditConfig.debug?.fakeApi?.repository ?? nuxt.options.dev,
46
+ repository:
47
+ eruditConfig.debug?.fakeApi?.repository ??
48
+ (nuxt.options.dev || isDevLikeMode(ERUDIT_MODE)),
46
49
  lastChanged:
47
- eruditConfig.debug?.fakeApi?.lastChanged ?? nuxt.options.dev,
50
+ eruditConfig.debug?.fakeApi?.lastChanged ??
51
+ (nuxt.options.dev || isDevLikeMode(ERUDIT_MODE)),
48
52
  },
49
53
  analytics: eruditConfig.debug?.analytics,
50
54
  },
@@ -85,7 +89,7 @@ export async function setupEruditRuntimeConfig(nuxt: Nuxt) {
85
89
  sponsors: eruditConfig.sponsors,
86
90
  ads: eruditConfig.ads,
87
91
  analytics: eruditConfig.analytics,
88
- } satisfies EruditPublicRuntimeConfig;
92
+ }) satisfies EruditPublicRuntimeConfig;
89
93
 
90
94
  //
91
95
  // Other
@@ -94,6 +98,7 @@ export async function setupEruditRuntimeConfig(nuxt: Nuxt) {
94
98
  nuxt.options.runtimeConfig.public.buildTime = Date.now();
95
99
 
96
100
  return {
101
+ eruditConfig,
97
102
  eruditRuntimeConfig: nuxt.options.runtimeConfig
98
103
  .erudit as EruditRuntimeConfig,
99
104
  eruditPublicRuntimeConfig: nuxt.options.runtimeConfig.public
package/nuxt.config.ts CHANGED
@@ -3,11 +3,6 @@ import { sn } from 'unslash';
3
3
 
4
4
  import { BASE_URL, ERUDIT_COMMAND, PROJECT_PATH } from './modules/erudit/env';
5
5
 
6
- /**
7
- * This is context-unaware or "static" Nuxt configuration.
8
- * The only thing that works here are aliases.
9
- * If you need to use context (e.g. paths to package/project or Erudit project config), use `./modules/erudit/setup/nuxtConfig.ts` file.
10
- */
11
6
  export default defineNuxtConfig({
12
7
  compatibilityDate: '2026-01-01',
13
8
  devtools: { enabled: true },
@@ -18,6 +13,11 @@ export default defineNuxtConfig({
18
13
  iconsDir: '#layers/erudit/app/assets/icons',
19
14
  },
20
15
  plugins: ['#layers/erudit/app/plugins/appSetup'],
16
+ typescript: {
17
+ nodeTsConfig: {
18
+ include: [sn(PROJECT_PATH, 'packages/erudit/modules/erudit/**/*.ts')],
19
+ },
20
+ },
21
21
  runtimeConfig: {
22
22
  eruditPath: '',
23
23
  projectPath: '',
@@ -25,6 +25,7 @@ export default defineNuxtConfig({
25
25
  public: {
26
26
  eruditVersion: '',
27
27
  eruditMode: '',
28
+ eruditReload: true,
28
29
  siteUrl: '',
29
30
  },
30
31
  },
@@ -93,7 +94,14 @@ export default defineNuxtConfig({
93
94
  },
94
95
  ],
95
96
  optimizeDeps: {
96
- noDiscovery: true,
97
+ include: [
98
+ '@vue/devtools-core',
99
+ '@vue/devtools-kit',
100
+ '@floating-ui/vue',
101
+ 'unslash',
102
+ 'tsprose',
103
+ 'tsprose/jsx-runtime',
104
+ ],
97
105
  },
98
106
  server: {
99
107
  fs: { strict: false },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "erudit",
3
- "version": "4.2.0-dev.1",
3
+ "version": "4.3.0-dev.1",
4
4
  "type": "module",
5
5
  "description": "🤓 CMS for perfect educational sites.",
6
6
  "license": "MIT",
@@ -24,15 +24,14 @@
24
24
  }
25
25
  },
26
26
  "dependencies": {
27
- "@erudit-js/cli": "4.2.0-dev.1",
28
- "@erudit-js/core": "4.2.0-dev.1",
29
- "@erudit-js/prose": "4.2.0-dev.1",
27
+ "@erudit-js/cli": "4.3.0-dev.1",
28
+ "@erudit-js/core": "4.3.0-dev.1",
29
+ "@erudit-js/prose": "4.3.0-dev.1",
30
30
  "unslash": "^2.0.0",
31
31
  "@floating-ui/vue": "^1.1.10",
32
- "tsprose": "^1.0.0",
32
+ "tsprose": "^1.0.1",
33
33
  "@tailwindcss/vite": "^4.2.0",
34
34
  "better-sqlite3": "^12.6.2",
35
- "chalk": "^5.6.2",
36
35
  "chokidar": "^5.0.0",
37
36
  "consola": "^3.4.2",
38
37
  "drizzle-kit": "^0.31.9",