@stati/core 1.29.2 → 2.0.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 (104) hide show
  1. package/README.md +10 -13
  2. package/dist/config/loader.d.ts +9 -0
  3. package/dist/config/loader.d.ts.map +1 -1
  4. package/dist/config/loader.js +77 -1
  5. package/dist/core/build.d.ts.map +1 -1
  6. package/dist/core/build.js +39 -29
  7. package/dist/core/content.d.ts.map +1 -1
  8. package/dist/core/content.js +9 -7
  9. package/dist/core/dev.d.ts.map +1 -1
  10. package/dist/core/dev.js +156 -197
  11. package/dist/core/eta-plugin.d.ts +60 -0
  12. package/dist/core/eta-plugin.d.ts.map +1 -0
  13. package/dist/core/eta-plugin.js +143 -0
  14. package/dist/core/isg/builder.d.ts +37 -19
  15. package/dist/core/isg/builder.d.ts.map +1 -1
  16. package/dist/core/isg/builder.js +70 -153
  17. package/dist/core/isg/deps.d.ts +8 -70
  18. package/dist/core/isg/deps.d.ts.map +1 -1
  19. package/dist/core/isg/deps.js +10 -401
  20. package/dist/core/isg/hash.d.ts.map +1 -1
  21. package/dist/core/isg/hash.js +2 -1
  22. package/dist/core/isg/index.d.ts +2 -2
  23. package/dist/core/isg/index.d.ts.map +1 -1
  24. package/dist/core/isg/index.js +3 -3
  25. package/dist/core/isg/manifest.d.ts.map +1 -1
  26. package/dist/core/isg/manifest.js +6 -1
  27. package/dist/core/markdown.d.ts +1 -1
  28. package/dist/core/markdown.d.ts.map +1 -1
  29. package/dist/core/markdown.js +3 -2
  30. package/dist/core/template-index.d.ts +209 -0
  31. package/dist/core/template-index.d.ts.map +1 -0
  32. package/dist/core/template-index.js +484 -0
  33. package/dist/core/template-render.d.ts +137 -0
  34. package/dist/core/template-render.d.ts.map +1 -0
  35. package/dist/core/template-render.js +444 -0
  36. package/dist/core/templates.d.ts +29 -11
  37. package/dist/core/templates.d.ts.map +1 -1
  38. package/dist/core/templates.js +115 -196
  39. package/dist/core/utils/context-validation.utils.d.ts +10 -6
  40. package/dist/core/utils/context-validation.utils.d.ts.map +1 -1
  41. package/dist/core/utils/context-validation.utils.js +39 -16
  42. package/dist/core/utils/date.utils.d.ts +1 -1
  43. package/dist/core/utils/date.utils.js +1 -1
  44. package/dist/core/utils/dev-css-watcher.utils.d.ts.map +1 -1
  45. package/dist/core/utils/dev-css-watcher.utils.js +3 -2
  46. package/dist/core/utils/error-overlay.utils.d.ts.map +1 -1
  47. package/dist/core/utils/error-overlay.utils.js +7 -45
  48. package/dist/core/utils/front-matter.utils.d.ts +50 -0
  49. package/dist/core/utils/front-matter.utils.d.ts.map +1 -0
  50. package/dist/core/utils/front-matter.utils.js +157 -0
  51. package/dist/core/utils/index.d.ts +4 -5
  52. package/dist/core/utils/index.d.ts.map +1 -1
  53. package/dist/core/utils/index.js +5 -7
  54. package/dist/core/utils/paths.utils.d.ts +3 -3
  55. package/dist/core/utils/paths.utils.js +6 -6
  56. package/dist/core/utils/tailwind-inventory.utils.d.ts +2 -2
  57. package/dist/core/utils/tailwind-inventory.utils.d.ts.map +1 -1
  58. package/dist/core/utils/tailwind-inventory.utils.js +15 -6
  59. package/dist/core/utils/template-discovery.utils.d.ts +3 -14
  60. package/dist/core/utils/template-discovery.utils.d.ts.map +1 -1
  61. package/dist/core/utils/template-discovery.utils.js +2 -62
  62. package/dist/core/utils/template-errors.utils.d.ts +14 -1
  63. package/dist/core/utils/template-errors.utils.d.ts.map +1 -1
  64. package/dist/core/utils/template-errors.utils.js +45 -4
  65. package/dist/core/utils/theme.utils.d.ts +17 -0
  66. package/dist/core/utils/theme.utils.d.ts.map +1 -0
  67. package/dist/core/utils/theme.utils.js +59 -0
  68. package/dist/images/index.d.ts +1 -2
  69. package/dist/images/index.d.ts.map +1 -1
  70. package/dist/images/index.js +0 -1
  71. package/dist/images/markdown-rule.d.ts +1 -2
  72. package/dist/images/markdown-rule.d.ts.map +1 -1
  73. package/dist/images/og.d.ts +2 -15
  74. package/dist/images/og.d.ts.map +1 -1
  75. package/dist/images/og.js +4 -47
  76. package/dist/images/runtime.d.ts +24 -5
  77. package/dist/images/runtime.d.ts.map +1 -1
  78. package/dist/images/runtime.js +14 -12
  79. package/dist/index.d.ts +2 -1
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/metrics/noop.d.ts.map +1 -1
  82. package/dist/metrics/noop.js +4 -1
  83. package/dist/metrics/recorder.d.ts.map +1 -1
  84. package/dist/metrics/recorder.js +11 -8
  85. package/dist/metrics/types.d.ts +31 -5
  86. package/dist/metrics/types.d.ts.map +1 -1
  87. package/dist/metrics/utils/html-report.utils.d.ts +2 -1
  88. package/dist/metrics/utils/html-report.utils.d.ts.map +1 -1
  89. package/dist/metrics/utils/html-report.utils.js +1115 -1136
  90. package/dist/types/config.d.ts +18 -6
  91. package/dist/types/config.d.ts.map +1 -1
  92. package/dist/types/content.d.ts +83 -16
  93. package/dist/types/content.d.ts.map +1 -1
  94. package/dist/types/index.d.ts +4 -3
  95. package/dist/types/index.d.ts.map +1 -1
  96. package/dist/types/isg.d.ts +48 -2
  97. package/dist/types/isg.d.ts.map +1 -1
  98. package/package.json +6 -6
  99. package/dist/core/utils/callable-partials.utils.d.ts +0 -60
  100. package/dist/core/utils/callable-partials.utils.d.ts.map +0 -1
  101. package/dist/core/utils/callable-partials.utils.js +0 -127
  102. package/dist/core/utils/partial-validation.utils.d.ts +0 -9
  103. package/dist/core/utils/partial-validation.utils.d.ts.map +0 -1
  104. package/dist/core/utils/partial-validation.utils.js +0 -107
@@ -2,54 +2,14 @@
2
2
  * Error overlay utility for displaying pretty errors in development mode.
3
3
  * Provides a user-friendly interface for template rendering and build errors.
4
4
  */
5
+ import { STATI_FAVICON_HREF, STATI_MARK_PATH, STATI_THEME_STYLES } from './theme.utils.js';
5
6
  const ERROR_HEADLINES = {
6
7
  template: 'Template rendering failed',
7
8
  build: 'Build process failed',
8
9
  markdown: 'Markdown processing failed',
9
10
  config: 'Configuration error',
10
11
  };
11
- const BOLT_PATH = 'M13 10V3L4 14h7v7l9-11h-7z';
12
- /** Inline favicon, so the overlay never requests the site's own favicon. */
13
- const FAVICON_HREF = `data:image/svg+xml,${encodeURIComponent(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#2563eb" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="${BOLT_PATH}"/></svg>`)}`;
14
- const OVERLAY_STYLES = `
15
- :root {
16
- --canvas: 255 255 255;
17
- --raised: 248 250 252;
18
- --sunken: 246 248 250;
19
- --ink: 11 13 18;
20
- --muted: 71 85 105;
21
- --faint: 91 101 119;
22
- --line: 226 232 240;
23
- --line-strong: 203 213 225;
24
- --accent: 37 99 235;
25
- --accent-ink: 29 78 216;
26
- --on-accent: 255 255 255;
27
- --danger: 185 28 28;
28
- --danger-wash: 254 242 242;
29
- --font-display: 'Archivo', 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
30
- --font-sans: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
31
- --font-mono: 'Fira Code', ui-monospace, 'Cascadia Code', Menlo, Monaco, Consolas, 'Liberation Mono', monospace;
32
- color-scheme: light;
33
- }
34
-
35
- @media (prefers-color-scheme: dark) {
36
- :root {
37
- --canvas: 2 6 23;
38
- --raised: 15 23 42;
39
- --sunken: 11 18 32;
40
- --ink: 241 245 249;
41
- --muted: 148 163 184;
42
- --faint: 131 145 167;
43
- --line: 30 41 59;
44
- --line-strong: 51 65 85;
45
- --accent: 96 165 250;
46
- --accent-ink: 147 197 253;
47
- --on-accent: 2 6 23;
48
- --danger: 248 113 113;
49
- --danger-wash: 38 12 20;
50
- color-scheme: dark;
51
- }
52
- }
12
+ const OVERLAY_STYLES = `${STATI_THEME_STYLES}
53
13
 
54
14
  *,
55
15
  *::before,
@@ -723,7 +683,7 @@ export function createErrorOverlay(error, requestPath) {
723
683
  <meta charset="UTF-8">
724
684
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
725
685
  <meta name="color-scheme" content="light dark">
726
- <link rel="icon" href="${FAVICON_HREF}">
686
+ <link rel="icon" href="${STATI_FAVICON_HREF}">
727
687
  <title>Stati Development Error</title>
728
688
  <style>${OVERLAY_STYLES}
729
689
  </style>
@@ -732,7 +692,7 @@ export function createErrorOverlay(error, requestPath) {
732
692
  <header class="bar">
733
693
  <div class="shell">
734
694
  <div class="brand">
735
- <svg class="brand-mark" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="${BOLT_PATH}"/></svg>
695
+ <svg class="brand-mark" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="${STATI_MARK_PATH}"/></svg>
736
696
  <span class="brand-name">Stati</span>
737
697
  <span class="brand-rule" aria-hidden="true"></span>
738
698
  <span class="brand-context">Development error</span>
@@ -984,7 +944,9 @@ function getErrorSuggestions(error, type) {
984
944
  }
985
945
  }
986
946
  else if (type === 'markdown') {
987
- if (message.includes('front-matter') || message.includes('yaml')) {
947
+ if (message.includes('front matter') ||
948
+ message.includes('front-matter') ||
949
+ message.includes('yaml')) {
988
950
  suggestions.push('Check the YAML front-matter syntax for errors');
989
951
  suggestions.push('Ensure front-matter is properly enclosed in --- markers');
990
952
  suggestions.push('Verify that all YAML values are properly quoted if they contain special characters');
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Front matter: the block between `---` lines at the start of a Markdown file, in YAML.
3
+ * @module core/utils/front-matter
4
+ */
5
+ /**
6
+ * A Markdown file split into its front matter and its body.
7
+ */
8
+ export interface ParsedFrontMatter {
9
+ /** The front matter's keys and values; empty when the file has none */
10
+ data: Record<string, unknown>;
11
+ /** The Markdown after the front matter */
12
+ content: string;
13
+ }
14
+ /**
15
+ * Thrown for front matter that cannot be read: invalid YAML, YAML that is not a set of
16
+ * `key: value` lines, or a language other than YAML after the opening `---`.
17
+ */
18
+ export declare class FrontMatterError extends Error {
19
+ readonly filePath: string;
20
+ readonly line?: number | undefined;
21
+ readonly column?: number | undefined;
22
+ /**
23
+ * @param message - What is wrong, with the file and the line
24
+ * @param filePath - The Markdown file
25
+ * @param line - The line of the problem in the file, 1-based
26
+ * @param column - The column of the problem, 1-based
27
+ */
28
+ constructor(message: string, filePath: string, line?: number | undefined, column?: number | undefined);
29
+ }
30
+ /**
31
+ * Splits a Markdown file into its front matter and its body.
32
+ *
33
+ * Front matter starts with `---` as the file's first line, `---yaml` included, and ends at the
34
+ * next line that starts with `---`; without one, the whole file is front matter. A file that
35
+ * starts with `----` or any other text has none. A byte order mark is dropped.
36
+ *
37
+ * @param source - The file's text
38
+ * @param filePath - The file, for error messages
39
+ * @returns The front matter as an object, and the Markdown after it
40
+ * @throws {FrontMatterError} When the front matter is not valid YAML, is YAML of another shape
41
+ * than `key: value` lines, or names another language after `---`
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * parseFrontMatter('---\ntitle: Home\n---\n# Welcome', 'site/index.md');
46
+ * // { data: { title: 'Home' }, content: '# Welcome' }
47
+ * ```
48
+ */
49
+ export declare function parseFrontMatter(source: string, filePath: string): ParsedFrontMatter;
50
+ //# sourceMappingURL=front-matter.utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"front-matter.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/front-matter.utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,0CAA0C;IAC1C,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;aASvB,QAAQ,EAAE,MAAM;aAChB,IAAI,CAAC,EAAE,MAAM;aACb,MAAM,CAAC,EAAE,MAAM;IAVjC;;;;;OAKG;gBAED,OAAO,EAAE,MAAM,EACC,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,MAAM,YAAA,EACb,MAAM,CAAC,EAAE,MAAM,YAAA;CAKlC;AAUD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,iBAAiB,CAwCpF"}
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Front matter: the block between `---` lines at the start of a Markdown file, in YAML.
3
+ * @module core/utils/front-matter
4
+ */
5
+ import { relative } from 'node:path';
6
+ import { load, YAMLException } from 'js-yaml';
7
+ /**
8
+ * Thrown for front matter that cannot be read: invalid YAML, YAML that is not a set of
9
+ * `key: value` lines, or a language other than YAML after the opening `---`.
10
+ */
11
+ export class FrontMatterError extends Error {
12
+ filePath;
13
+ line;
14
+ column;
15
+ /**
16
+ * @param message - What is wrong, with the file and the line
17
+ * @param filePath - The Markdown file
18
+ * @param line - The line of the problem in the file, 1-based
19
+ * @param column - The column of the problem, 1-based
20
+ */
21
+ constructor(message, filePath, line, column) {
22
+ super(message);
23
+ this.filePath = filePath;
24
+ this.line = line;
25
+ this.column = column;
26
+ this.name = 'FrontMatterError';
27
+ }
28
+ }
29
+ const DELIMITER = '---';
30
+ /** The closing delimiter: the first line that starts with `---` */
31
+ const CLOSING_DELIMITER = `\n${DELIMITER}`;
32
+ /** Comment lines, which alone leave the front matter empty */
33
+ const COMMENT_LINES = /^\s*#[^\n]+/gm;
34
+ /** A `key: value` line whose unquoted value contains `: `, which YAML reads as a nested key */
35
+ const UNQUOTED_COLON_VALUE = /^\s*([^\s:#'"][^:]*):\s+([^\s'"[{|>&*!#].*?:(?:\s.*)?)$/;
36
+ /**
37
+ * Splits a Markdown file into its front matter and its body.
38
+ *
39
+ * Front matter starts with `---` as the file's first line, `---yaml` included, and ends at the
40
+ * next line that starts with `---`; without one, the whole file is front matter. A file that
41
+ * starts with `----` or any other text has none. A byte order mark is dropped.
42
+ *
43
+ * @param source - The file's text
44
+ * @param filePath - The file, for error messages
45
+ * @returns The front matter as an object, and the Markdown after it
46
+ * @throws {FrontMatterError} When the front matter is not valid YAML, is YAML of another shape
47
+ * than `key: value` lines, or names another language after `---`
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * parseFrontMatter('---\ntitle: Home\n---\n# Welcome', 'site/index.md');
52
+ * // { data: { title: 'Home' }, content: '# Welcome' }
53
+ * ```
54
+ */
55
+ export function parseFrontMatter(source, filePath) {
56
+ const text = source.charCodeAt(0) === 0xfeff ? source.slice(1) : source;
57
+ if (!text.startsWith(DELIMITER) || text.charAt(DELIMITER.length) === '-') {
58
+ return { data: {}, content: text };
59
+ }
60
+ // The rest of the opening line can name the language, as in ---yaml
61
+ let rest = text.slice(DELIMITER.length);
62
+ const openingLineEnd = rest.search(/\r?\n/);
63
+ const language = (openingLineEnd === -1 ? rest : rest.slice(0, openingLineEnd)).trim();
64
+ if (language !== '') {
65
+ rest = rest.slice(openingLineEnd === -1 ? rest.length : openingLineEnd);
66
+ }
67
+ const closeIndex = rest.indexOf(CLOSING_DELIMITER);
68
+ const block = closeIndex === -1 ? rest : rest.slice(0, closeIndex);
69
+ let content = '';
70
+ if (closeIndex !== -1) {
71
+ content = rest.slice(closeIndex + CLOSING_DELIMITER.length);
72
+ if (content.startsWith('\r'))
73
+ content = content.slice(1);
74
+ if (content.startsWith('\n'))
75
+ content = content.slice(1);
76
+ }
77
+ if (block.replace(COMMENT_LINES, '').trim() === '') {
78
+ return { data: {}, content };
79
+ }
80
+ try {
81
+ return { data: parseBlock(block, language.toLowerCase(), filePath), content };
82
+ }
83
+ catch (error) {
84
+ // Without a closing line the whole file is front matter: most likely the --- is a
85
+ // horizontal rule, or the closing line is missing
86
+ if (closeIndex === -1 && error instanceof FrontMatterError) {
87
+ throw new FrontMatterError(`Invalid front matter in ${displayPath(filePath)}: the file starts with a --- line, which starts front matter, and no later --- line ends it. Add the closing --- line, or start the file with *** for a horizontal rule.`, filePath, 1);
88
+ }
89
+ throw error;
90
+ }
91
+ }
92
+ /**
93
+ * Parses the text between the delimiters. Its first line is the file's first line.
94
+ */
95
+ function parseBlock(block, language, filePath) {
96
+ const where = `Invalid front matter in ${displayPath(filePath)}`;
97
+ let data;
98
+ if (language === '' || language === 'yaml' || language === 'yml') {
99
+ try {
100
+ data = load(block);
101
+ }
102
+ catch (error) {
103
+ throw describeYamlError(error, block, filePath, where);
104
+ }
105
+ }
106
+ else if (language === 'json') {
107
+ try {
108
+ data = JSON.parse(block);
109
+ }
110
+ catch (error) {
111
+ const reason = error instanceof Error ? error.message : String(error);
112
+ throw new FrontMatterError(`${where}: ${reason}.`, filePath);
113
+ }
114
+ }
115
+ else {
116
+ throw new FrontMatterError(`${where} at line 1: Stati reads front matter in YAML, not ${language}. Remove "${language}" after the opening ---.`, filePath, 1);
117
+ }
118
+ if (!isMapping(data)) {
119
+ throw new FrontMatterError(`${where}: front matter must be "key: value" lines, such as "title: My page", not ${describeData(data)}.`, filePath);
120
+ }
121
+ return data;
122
+ }
123
+ function describeYamlError(error, block, filePath, where) {
124
+ if (!(error instanceof YAMLException)) {
125
+ const reason = error instanceof Error ? error.message : String(error);
126
+ return new FrontMatterError(`${where}: ${reason}.`, filePath);
127
+ }
128
+ // The block starts on the file's first line, so its line numbers are the file's
129
+ const line = error.mark.line + 1;
130
+ const column = error.mark.column + 1;
131
+ const offending = UNQUOTED_COLON_VALUE.exec(block.split(/\r?\n/)[error.mark.line] ?? '');
132
+ const hint = offending
133
+ ? ` Quote a value that contains a colon, such as ${offending[1]}: ${JSON.stringify(offending[2].trim())}.`
134
+ : '';
135
+ return new FrontMatterError(`${where} at line ${line}, column ${column}: ${error.reason}.${hint}`, filePath, line, column);
136
+ }
137
+ /** Whether a parsed value is a YAML mapping, which js-yaml loads as a plain object */
138
+ function isMapping(value) {
139
+ return (typeof value === 'object' && value !== null && Object.getPrototypeOf(value) === Object.prototype);
140
+ }
141
+ function describeData(value) {
142
+ if (value === null || value === undefined)
143
+ return 'null';
144
+ if (Array.isArray(value))
145
+ return 'a list';
146
+ if (value instanceof Date)
147
+ return 'a date';
148
+ if (typeof value === 'string')
149
+ return `the text ${JSON.stringify(value)}`;
150
+ if (typeof value === 'object')
151
+ return 'binary data';
152
+ return `the ${typeof value} ${String(value)}`;
153
+ }
154
+ /** A file for messages: its path from the project directory */
155
+ function displayPath(filePath) {
156
+ return relative(process.cwd(), filePath).replace(/\\/g, '/') || filePath;
157
+ }
@@ -4,16 +4,15 @@
4
4
  */
5
5
  export { readFile, writeFile, pathExists, ensureDir, remove, copyFile, readdir, stat, lstat, realpath, readFileBuffer, readFileBufferSync, statSyncOrNull, listFilesRecursiveSync, } from './fs.utils.js';
6
6
  export { resolveSrcDir, resolveOutDir, resolveStaticDir, resolveCacheDir, resolveDevPaths, normalizeTemplatePath, resolveSrcPath, resolveOutPath, resolveStaticPath, normalizePathForComparison, isPathWithinDirectory, getPageOutputFile, } from './paths.utils.js';
7
- export { discoverLayout, isCollectionIndexPage, isVirtualPage, getFirstPaginationPage, getCollectionPathForPage, } from './template-discovery.utils.js';
7
+ export { isCollectionIndexPage, isVirtualPage, getFirstPaginationPage, getCollectionPathForPage, } from './template-discovery.utils.js';
8
8
  export { propValue } from './template.utils.js';
9
9
  export { slugify } from './slugify.utils.js';
10
10
  export { trackTailwindClass, enableInventoryTracking, disableInventoryTracking, clearInventory, getInventory, getInventorySize, isTrackingEnabled, writeTailwindClassInventory, isTailwindUsed, resetTailwindDetection, loadPreviousInventory, } from './tailwind-inventory.utils.js';
11
- export { createValidatingPartialsProxy } from './partial-validation.utils.js';
11
+ export { parseFrontMatter, FrontMatterError } from './front-matter.utils.js';
12
+ export type { ParsedFrontMatter } from './front-matter.utils.js';
12
13
  export { createValidatingContextProxy, isPassthroughProperty, parseUnknownPropertyMessage, UnknownTemplatePropertyError, } from './context-validation.utils.js';
13
14
  export { editDistance, findSimilarNames } from './similarity.utils.js';
14
- export { makeCallablePartial, wrapPartialsAsCallable } from './callable-partials.utils.js';
15
- export type { CallablePartial } from './callable-partials.utils.js';
16
- export { TemplateError, parseEtaError, createTemplateError } from './template-errors.utils.js';
15
+ export { TemplateError, TemplateUsageError, parseEtaError, createTemplateError, } from './template-errors.utils.js';
17
16
  export { createNavigationHelpers } from './navigation-helpers.utils.js';
18
17
  export { getMimeType, parseRequestPath, resolveNotFoundPage, resolveRequestFile, resolvePrettyUrl, mergeServerOptions, assertPortAvailable, toPortUnavailableError, PortUnavailableError, } from './server.utils.js';
19
18
  export type { PrettyUrlResult, MergedServerOptions, MergeServerOptionsParams, PortUnavailableCode, } from './server.utils.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/utils/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/utils/index.ts"],"names":[],"mappings":"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"}
@@ -6,24 +6,22 @@
6
6
  export { readFile, writeFile, pathExists, ensureDir, remove, copyFile, readdir, stat, lstat, realpath, readFileBuffer, readFileBufferSync, statSyncOrNull, listFilesRecursiveSync, } from './fs.utils.js';
7
7
  // Path resolution utilities
8
8
  export { resolveSrcDir, resolveOutDir, resolveStaticDir, resolveCacheDir, resolveDevPaths, normalizeTemplatePath, resolveSrcPath, resolveOutPath, resolveStaticPath, normalizePathForComparison, isPathWithinDirectory, getPageOutputFile, } from './paths.utils.js';
9
- // Template discovery utilities
10
- export { discoverLayout, isCollectionIndexPage, isVirtualPage, getFirstPaginationPage, getCollectionPathForPage, } from './template-discovery.utils.js';
9
+ // Page list utilities (collection index pages, virtual pages)
10
+ export { isCollectionIndexPage, isVirtualPage, getFirstPaginationPage, getCollectionPathForPage, } from './template-discovery.utils.js';
11
11
  // Template utilities
12
12
  export { propValue } from './template.utils.js';
13
13
  // Slugify utilities
14
14
  export { slugify } from './slugify.utils.js';
15
15
  // Tailwind inventory utilities
16
16
  export { trackTailwindClass, enableInventoryTracking, disableInventoryTracking, clearInventory, getInventory, getInventorySize, isTrackingEnabled, writeTailwindClassInventory, isTailwindUsed, resetTailwindDetection, loadPreviousInventory, } from './tailwind-inventory.utils.js';
17
- // Partial validation utilities
18
- export { createValidatingPartialsProxy } from './partial-validation.utils.js';
17
+ // Front matter parsing
18
+ export { parseFrontMatter, FrontMatterError } from './front-matter.utils.js';
19
19
  // Template context validation utilities
20
20
  export { createValidatingContextProxy, isPassthroughProperty, parseUnknownPropertyMessage, UnknownTemplatePropertyError, } from './context-validation.utils.js';
21
21
  // String similarity utilities
22
22
  export { editDistance, findSimilarNames } from './similarity.utils.js';
23
- // Callable partial utilities
24
- export { makeCallablePartial, wrapPartialsAsCallable } from './callable-partials.utils.js';
25
23
  // Template error utilities
26
- export { TemplateError, parseEtaError, createTemplateError } from './template-errors.utils.js';
24
+ export { TemplateError, TemplateUsageError, parseEtaError, createTemplateError, } from './template-errors.utils.js';
27
25
  // Navigation helper utilities
28
26
  export { createNavigationHelpers } from './navigation-helpers.utils.js';
29
27
  // Server utilities
@@ -5,19 +5,19 @@ import type { StatiConfig } from '../../types/index.js';
5
5
  */
6
6
  /**
7
7
  * Resolves the absolute source directory path from configuration.
8
- * @param config - Stati configuration
8
+ * @param config - Stati configuration; `srcDir` defaults to `site`, as `loadConfig()` sets it
9
9
  * @returns Absolute path to the source directory
10
10
  */
11
11
  export declare function resolveSrcDir(config: StatiConfig): string;
12
12
  /**
13
13
  * Resolves the absolute output directory path from configuration.
14
- * @param config - Stati configuration
14
+ * @param config - Stati configuration; `outDir` defaults to `dist`
15
15
  * @returns Absolute path to the output directory
16
16
  */
17
17
  export declare function resolveOutDir(config: StatiConfig): string;
18
18
  /**
19
19
  * Resolves the absolute static directory path from configuration.
20
- * @param config - Stati configuration
20
+ * @param config - Stati configuration; `staticDir` defaults to `public`
21
21
  * @returns Absolute path to the static directory
22
22
  */
23
23
  export declare function resolveStaticDir(config: StatiConfig): string;
@@ -6,27 +6,27 @@ import { DEFAULT_SRC_DIR, DEFAULT_OUT_DIR, DEFAULT_STATIC_DIR, CACHE_DIR_NAME, }
6
6
  */
7
7
  /**
8
8
  * Resolves the absolute source directory path from configuration.
9
- * @param config - Stati configuration
9
+ * @param config - Stati configuration; `srcDir` defaults to `site`, as `loadConfig()` sets it
10
10
  * @returns Absolute path to the source directory
11
11
  */
12
12
  export function resolveSrcDir(config) {
13
- return join(process.cwd(), config.srcDir);
13
+ return join(process.cwd(), config.srcDir ?? DEFAULT_SRC_DIR);
14
14
  }
15
15
  /**
16
16
  * Resolves the absolute output directory path from configuration.
17
- * @param config - Stati configuration
17
+ * @param config - Stati configuration; `outDir` defaults to `dist`
18
18
  * @returns Absolute path to the output directory
19
19
  */
20
20
  export function resolveOutDir(config) {
21
- return join(process.cwd(), config.outDir);
21
+ return join(process.cwd(), config.outDir ?? DEFAULT_OUT_DIR);
22
22
  }
23
23
  /**
24
24
  * Resolves the absolute static directory path from configuration.
25
- * @param config - Stati configuration
25
+ * @param config - Stati configuration; `staticDir` defaults to `public`
26
26
  * @returns Absolute path to the static directory
27
27
  */
28
28
  export function resolveStaticDir(config) {
29
- return join(process.cwd(), config.staticDir);
29
+ return join(process.cwd(), config.staticDir ?? DEFAULT_STATIC_DIR);
30
30
  }
31
31
  /**
32
32
  * Resolves the absolute cache directory path.
@@ -8,8 +8,8 @@
8
8
  */
9
9
  /**
10
10
  * Detects if Tailwind CSS is being used in the project by checking for:
11
- * 1. tailwind.config.js or tailwind.config.ts
12
- * 2. tailwindcss in package.json dependencies
11
+ * 1. tailwind.config.js, .ts, .mjs or .cjs (Tailwind CSS 3)
12
+ * 2. tailwindcss, @tailwindcss/cli or @tailwindcss/postcss in package.json dependencies
13
13
  *
14
14
  * Results are cached to avoid repeated file system checks.
15
15
  *
@@ -1 +1 @@
1
- {"version":3,"file":"tailwind-inventory.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/tailwind-inventory.utils.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAuBH;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CAAC,WAAW,GAAE,MAAsB,GAAG,OAAO,CAAC,OAAO,CAAC,CA4C1F;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAED;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAkC7E;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAI1D;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,IAAI,MAAM,EAAE,CAEvC;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAoCD,wBAAsB,2BAA2B,CAC/C,QAAQ,EAAE,MAAM,EAChB,eAAe,UAAQ,GACtB,OAAO,CAAC,MAAM,CAAC,CA8DjB"}
1
+ {"version":3,"file":"tailwind-inventory.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/tailwind-inventory.utils.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA6BH;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CAAC,WAAW,GAAE,MAAsB,GAAG,OAAO,CAAC,OAAO,CAAC,CA4C1F;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAED;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAkC7E;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAI1D;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,IAAI,MAAM,EAAE,CAEvC;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAqCD,wBAAsB,2BAA2B,CAC/C,QAAQ,EAAE,MAAM,EAChB,eAAe,UAAQ,GACtB,OAAO,CAAC,MAAM,CAAC,CAiEjB"}
@@ -23,10 +23,15 @@ let inventoryTrackingEnabled = false;
23
23
  * Cached result of Tailwind detection to avoid repeated file system checks.
24
24
  */
25
25
  let tailwindDetected = null;
26
+ /**
27
+ * Packages that mean a project uses Tailwind CSS. Tailwind CSS 4 runs its CLI and
28
+ * PostCSS plugin from separate packages and needs no config file.
29
+ */
30
+ const TAILWIND_PACKAGES = ['tailwindcss', '@tailwindcss/cli', '@tailwindcss/postcss'];
26
31
  /**
27
32
  * Detects if Tailwind CSS is being used in the project by checking for:
28
- * 1. tailwind.config.js or tailwind.config.ts
29
- * 2. tailwindcss in package.json dependencies
33
+ * 1. tailwind.config.js, .ts, .mjs or .cjs (Tailwind CSS 3)
34
+ * 2. tailwindcss, @tailwindcss/cli or @tailwindcss/postcss in package.json dependencies
30
35
  *
31
36
  * Results are cached to avoid repeated file system checks.
32
37
  *
@@ -60,7 +65,7 @@ export async function isTailwindUsed(projectRoot = process.cwd()) {
60
65
  ...packageJson.dependencies,
61
66
  ...packageJson.devDependencies,
62
67
  };
63
- if (deps.tailwindcss) {
68
+ if (TAILWIND_PACKAGES.some((name) => deps[name])) {
64
69
  tailwindDetected = true;
65
70
  return true;
66
71
  }
@@ -176,7 +181,8 @@ export function isTrackingEnabled() {
176
181
  * Writes the Tailwind class inventory to an HTML file that Tailwind can scan.
177
182
  *
178
183
  * The generated file contains all tracked classes in a hidden div.
179
- * This file should be added to Tailwind's content configuration.
184
+ * Tailwind must scan it: an `@source` rule in the CSS file (Tailwind CSS 4) or an
185
+ * entry in the `content` array of tailwind.config.js (Tailwind CSS 3).
180
186
  * Skips write if the inventory hasn't changed (dev mode optimization).
181
187
  *
182
188
  * @param cacheDir - Directory where the inventory file should be written (typically .stati/)
@@ -228,10 +234,13 @@ export async function writeTailwindClassInventory(cacheDir, skipIfUnchanged = fa
228
234
  Auto-generated by Stati - DO NOT EDIT
229
235
 
230
236
  This file contains dynamically-generated Tailwind classes extracted from your templates.
231
- It ensures that Tailwind's JIT compiler generates CSS for classes built with template
237
+ It ensures that Tailwind generates CSS for classes built with template
232
238
  variables (e.g., from-\${color}-50).
233
239
 
234
- Add this file to your tailwind.config.js content array:
240
+ Tailwind CSS 4: add an @source rule for this file to your CSS file. From src/styles.css:
241
+ @source '../.stati/tailwind-classes.html';
242
+
243
+ Tailwind CSS 3: add this file to your tailwind.config.js content array:
235
244
  content: [
236
245
  './site/**/*.{md,eta,html}',
237
246
  './.stati/tailwind-classes.html'
@@ -1,7 +1,7 @@
1
- import type { StatiConfig, PageModel } from '../../types/index.js';
1
+ import type { PageModel } from '../../types/index.js';
2
2
  /**
3
- * Shared template discovery utilities.
4
- * Extracted from templates.ts and isg/deps.ts to eliminate duplication.
3
+ * Page list utilities shared by rendering and the ISG cache: collection index pages, virtual
4
+ * pages and collection paths. Layout discovery uses the template index (core/template-index.ts).
5
5
  */
6
6
  /**
7
7
  * Whether a page was created by Stati rather than loaded from a Markdown file: a page expanded
@@ -38,17 +38,6 @@ export declare function getFirstPaginationPage(page: PageModel, allPages: PageMo
38
38
  * @returns True if the page is a collection index page
39
39
  */
40
40
  export declare function isCollectionIndexPage(page: PageModel, allPages: PageModel[]): boolean;
41
- /**
42
- * Discovers layout files by searching up the directory hierarchy.
43
- * Supports both explicit layout specification and automatic discovery.
44
- *
45
- * @param pagePath - The path to the page (relative to srcDir)
46
- * @param config - Stati configuration
47
- * @param explicitLayout - Explicit layout name from front-matter
48
- * @param isIndexPage - Whether this is an aggregation/index page (enables index.eta lookup)
49
- * @returns The layout file path (relative to srcDir) or null if none found
50
- */
51
- export declare function discoverLayout(pagePath: string, config: StatiConfig, explicitLayout?: string, isIndexPage?: boolean): Promise<string | null>;
52
41
  /**
53
42
  * Gets the collection path for a given page URL.
54
43
  * For index pages, returns the page's URL. For child pages, returns the parent directory.
@@ -1 +1 @@
1
- {"version":3,"file":"template-discovery.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/template-discovery.utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAInE;;;GAGG;AAEH;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAEtD;AAiED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,SAAS,EACf,QAAQ,EAAE,SAAS,EAAE,GACpB,SAAS,GAAG,SAAS,CAMvB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,OAAO,CAoBrF;AAED;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CAClC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,WAAW,EACnB,cAAc,CAAC,EAAE,MAAM,EACvB,WAAW,CAAC,EAAE,OAAO,GACpB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAsDxB;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAkBhE"}
1
+ {"version":3,"file":"template-discovery.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/template-discovery.utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEtD;;;GAGG;AAEH;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAEtD;AAiED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,SAAS,EACf,QAAQ,EAAE,SAAS,EAAE,GACpB,SAAS,GAAG,SAAS,CAMvB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,OAAO,CAoBrF;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAkBhE"}
@@ -1,10 +1,6 @@
1
- import { join, dirname, posix } from 'node:path';
2
- import { pathExists } from './fs.utils.js';
3
- import { LAYOUT_TEMPLATE } from '../../constants.js';
4
- import { resolveSrcDir } from './paths.utils.js';
5
1
  /**
6
- * Shared template discovery utilities.
7
- * Extracted from templates.ts and isg/deps.ts to eliminate duplication.
2
+ * Page list utilities shared by rendering and the ISG cache: collection index pages, virtual
3
+ * pages and collection paths. Layout discovery uses the template index (core/template-index.ts).
8
4
  */
9
5
  /**
10
6
  * Whether a page was created by Stati rather than loaded from a Markdown file: a page expanded
@@ -110,62 +106,6 @@ export function isCollectionIndexPage(page, allPages) {
110
106
  const containingUrl = getPageListIndex(allPages).prefixes.get(pageUrlAsDir);
111
107
  return containingUrl === null || (containingUrl !== undefined && containingUrl !== page.url);
112
108
  }
113
- /**
114
- * Discovers layout files by searching up the directory hierarchy.
115
- * Supports both explicit layout specification and automatic discovery.
116
- *
117
- * @param pagePath - The path to the page (relative to srcDir)
118
- * @param config - Stati configuration
119
- * @param explicitLayout - Explicit layout name from front-matter
120
- * @param isIndexPage - Whether this is an aggregation/index page (enables index.eta lookup)
121
- * @returns The layout file path (relative to srcDir) or null if none found
122
- */
123
- export async function discoverLayout(pagePath, config, explicitLayout, isIndexPage) {
124
- // Early return if required config values are missing
125
- if (!config.srcDir) {
126
- return null;
127
- }
128
- const srcDir = resolveSrcDir(config);
129
- // If explicit layout is specified, use it
130
- if (explicitLayout) {
131
- const layoutPath = join(srcDir, `${explicitLayout}.eta`);
132
- if (await pathExists(layoutPath)) {
133
- return `${explicitLayout}.eta`;
134
- }
135
- }
136
- // Get the directory of the current page
137
- const pageDir = dirname(pagePath);
138
- const pathSegments = pageDir === '.' ? [] : pageDir.split(/[/\\]/); // Handle both separators
139
- // Search for layout.eta from current directory up to root
140
- const dirsToSearch = [];
141
- // Add current directory if not root
142
- if (pathSegments.length > 0) {
143
- for (let i = pathSegments.length; i > 0; i--) {
144
- dirsToSearch.push(pathSegments.slice(0, i).join('/'));
145
- }
146
- }
147
- // Add root directory
148
- dirsToSearch.push('');
149
- for (const dir of dirsToSearch) {
150
- // For index pages, first check for index.eta in each directory
151
- if (isIndexPage) {
152
- const indexLayoutPath = dir ? join(srcDir, dir, 'index.eta') : join(srcDir, 'index.eta');
153
- if (await pathExists(indexLayoutPath)) {
154
- // Return relative path with forward slashes for Eta
155
- const relativePath = dir ? `${dir}/index.eta` : 'index.eta';
156
- return posix.normalize(relativePath);
157
- }
158
- }
159
- // Then check for layout.eta as fallback
160
- const layoutPath = dir ? join(srcDir, dir, LAYOUT_TEMPLATE) : join(srcDir, LAYOUT_TEMPLATE);
161
- if (await pathExists(layoutPath)) {
162
- // Return relative path with forward slashes for Eta
163
- const relativePath = dir ? `${dir}/${LAYOUT_TEMPLATE}` : LAYOUT_TEMPLATE;
164
- return posix.normalize(relativePath);
165
- }
166
- }
167
- return null;
168
- }
169
109
  /**
170
110
  * Gets the collection path for a given page URL.
171
111
  * For index pages, returns the page's URL. For child pages, returns the parent directory.
@@ -19,12 +19,25 @@ export declare class TemplateError extends Error {
19
19
  */
20
20
  toErrorDetails(): Promise<ErrorDetails>;
21
21
  }
22
+ /**
23
+ * Thrown by Stati's template functions, such as `stati.include()` and `stati.layout()`, for a
24
+ * mistake at the call: an unknown template name, props that are not an object, Eta's own
25
+ * `include()`. Thrown inside a template, so Eta's debug mode adds the calling template and line
26
+ * to the message, and keeps this error as its `cause`.
27
+ */
28
+ export declare class TemplateUsageError extends Error {
29
+ /** Fixes to show in the error overlay */
30
+ readonly suggestions: string[];
31
+ constructor(message: string, suggestions?: string[]);
32
+ }
22
33
  /**
23
34
  * Parses Eta template errors to extract file location information.
24
35
  */
25
36
  export declare function parseEtaError(error: Error, templatePath?: string): TemplateError;
26
37
  /**
27
- * Enhances a generic error with template-specific context.
38
+ * Enhances a generic error with template-specific context. An error that wraps a
39
+ * {@link TemplateError}, as Eta's debug mode does for an error from a nested template, returns
40
+ * the innermost one, which points at the template that failed.
28
41
  */
29
42
  export declare function createTemplateError(error: Error, templatePath?: string, _line?: number, _column?: number): TemplateError;
30
43
  //# sourceMappingURL=template-errors.utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"template-errors.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/template-errors.utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAG7D;;GAEG;AACH,qBAAa,aAAc,SAAQ,KAAK;IACtC,SAAgB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClC,SAAgB,IAAI,CAAC,EAAE,MAAM,CAAC;IAC9B,SAAgB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChC,SAAgB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClC,8EAA8E;IAC9E,SAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvC,SAAgB,OAAO,CAAC,EAAE;QACxB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;KAClB,CAAC;gBAGA,OAAO,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM,EACjB,WAAW,CAAC,EAAE,MAAM,EAAE;IAWxB;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,YAAY,CAAC;CAoE9C;AAsCD;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,aAAa,CAoDhF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,KAAK,EACZ,YAAY,CAAC,EAAE,MAAM,EACrB,KAAK,CAAC,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,GACf,aAAa,CAMf"}
1
+ {"version":3,"file":"template-errors.utils.d.ts","sourceRoot":"","sources":["../../../src/core/utils/template-errors.utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAG7D;;GAEG;AACH,qBAAa,aAAc,SAAQ,KAAK;IACtC,SAAgB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClC,SAAgB,IAAI,CAAC,EAAE,MAAM,CAAC;IAC9B,SAAgB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChC,SAAgB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClC,8EAA8E;IAC9E,SAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvC,SAAgB,OAAO,CAAC,EAAE;QACxB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;KAClB,CAAC;gBAGA,OAAO,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM,EACjB,WAAW,CAAC,EAAE,MAAM,EAAE;IAWxB;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,YAAY,CAAC;CAoE9C;AAED;;;;;GAKG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,yCAAyC;IACzC,SAAgB,WAAW,EAAE,MAAM,EAAE,CAAC;gBAE1B,OAAO,EAAE,MAAM,EAAE,WAAW,GAAE,MAAM,EAAO;CAKxD;AAyDD;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,aAAa,CAyDhF;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,KAAK,EACZ,YAAY,CAAC,EAAE,MAAM,EACrB,KAAK,CAAC,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,GACf,aAAa,CAQf"}