legal-markdown-js 3.3.2 → 3.3.4

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 (128) hide show
  1. package/dist/{browser-modern-wM0F4Tg1.js → browser-modern-BP5EJrCS.js} +1727 -1485
  2. package/dist/browser-modern-BP5EJrCS.js.map +1 -0
  3. package/dist/cli/interactive/index.js +4 -46
  4. package/dist/cli/interactive/index.js.map +1 -1
  5. package/dist/cli/interactive/prompts/file-selector.d.ts +1 -1
  6. package/dist/cli/interactive/prompts/file-selector.d.ts.map +1 -1
  7. package/dist/cli/interactive/prompts/file-selector.js +29 -53
  8. package/dist/cli/interactive/prompts/file-selector.js.map +1 -1
  9. package/dist/cli/interactive/service.d.ts +7 -2
  10. package/dist/cli/interactive/service.d.ts.map +1 -1
  11. package/dist/cli/interactive/service.js +58 -81
  12. package/dist/cli/interactive/service.js.map +1 -1
  13. package/dist/cli/service.d.ts +10 -1
  14. package/dist/cli/service.d.ts.map +1 -1
  15. package/dist/cli/service.js +52 -92
  16. package/dist/cli/service.js.map +1 -1
  17. package/dist/core/pipeline/context-builder.d.ts +138 -0
  18. package/dist/core/pipeline/context-builder.d.ts.map +1 -0
  19. package/dist/core/pipeline/context-builder.js +170 -0
  20. package/dist/core/pipeline/context-builder.js.map +1 -0
  21. package/dist/core/pipeline/format-generator.d.ts +158 -0
  22. package/dist/core/pipeline/format-generator.d.ts.map +1 -0
  23. package/dist/core/pipeline/format-generator.js +368 -0
  24. package/dist/core/pipeline/format-generator.js.map +1 -0
  25. package/dist/core/pipeline/index.d.ts +24 -0
  26. package/dist/core/pipeline/index.d.ts.map +1 -0
  27. package/dist/core/pipeline/index.js +27 -0
  28. package/dist/core/pipeline/index.js.map +1 -0
  29. package/dist/core/processors/clause-processor.d.ts +5 -0
  30. package/dist/core/processors/clause-processor.d.ts.map +1 -1
  31. package/dist/core/processors/clause-processor.js +11 -0
  32. package/dist/core/processors/clause-processor.js.map +1 -1
  33. package/dist/core/processors/date-processor.d.ts +5 -0
  34. package/dist/core/processors/date-processor.d.ts.map +1 -1
  35. package/dist/core/processors/date-processor.js +11 -0
  36. package/dist/core/processors/date-processor.js.map +1 -1
  37. package/dist/core/processors/header-processor.d.ts +5 -0
  38. package/dist/core/processors/header-processor.d.ts.map +1 -1
  39. package/dist/core/processors/header-processor.js +11 -0
  40. package/dist/core/processors/header-processor.js.map +1 -1
  41. package/dist/core/processors/import-processor.d.ts +10 -0
  42. package/dist/core/processors/import-processor.d.ts.map +1 -1
  43. package/dist/core/processors/import-processor.js +22 -0
  44. package/dist/core/processors/import-processor.js.map +1 -1
  45. package/dist/core/processors/mixin-processor.d.ts +5 -0
  46. package/dist/core/processors/mixin-processor.d.ts.map +1 -1
  47. package/dist/core/processors/mixin-processor.js +11 -0
  48. package/dist/core/processors/mixin-processor.js.map +1 -1
  49. package/dist/core/processors/reference-processor.d.ts +5 -0
  50. package/dist/core/processors/reference-processor.d.ts.map +1 -1
  51. package/dist/core/processors/reference-processor.js +11 -0
  52. package/dist/core/processors/reference-processor.js.map +1 -1
  53. package/dist/examples/imports/frontmatter-merging/output/contract-metadata.json +2 -2
  54. package/dist/examples/imports/frontmatter-merging/output/contract-metadata.yaml +2 -2
  55. package/dist/examples/imports/frontmatter-merging/templates/output/enterprise-contract-metadata.json +2 -2
  56. package/dist/examples/imports/frontmatter-merging/templates/output/enterprise-contract-metadata.yaml +2 -2
  57. package/dist/extensions/generators/html-generator.d.ts +18 -7
  58. package/dist/extensions/generators/html-generator.d.ts.map +1 -1
  59. package/dist/extensions/generators/html-generator.js +32 -23
  60. package/dist/extensions/generators/html-generator.js.map +1 -1
  61. package/dist/extensions/generators/pdf-generator.d.ts +60 -2
  62. package/dist/extensions/generators/pdf-generator.d.ts.map +1 -1
  63. package/dist/extensions/generators/pdf-generator.js +152 -0
  64. package/dist/extensions/generators/pdf-generator.js.map +1 -1
  65. package/dist/extensions/remark/legal-markdown-processor.d.ts +8 -2
  66. package/dist/extensions/remark/legal-markdown-processor.d.ts.map +1 -1
  67. package/dist/extensions/remark/legal-markdown-processor.js +57 -5
  68. package/dist/extensions/remark/legal-markdown-processor.js.map +1 -1
  69. package/dist/force-commands-parser-DDda8v0G.js +441 -0
  70. package/dist/force-commands-parser-DDda8v0G.js.map +1 -0
  71. package/dist/index.d.ts +2 -2
  72. package/dist/index.d.ts.map +1 -1
  73. package/dist/index.js +6 -5
  74. package/dist/index.js.map +1 -1
  75. package/dist/legal-markdown-browser.js +1 -1
  76. package/dist/legal-markdown.umd.min.js +1 -1
  77. package/dist/legal-markdown.umd.min.js.map +1 -1
  78. package/dist/plugins/remark/headers.d.ts.map +1 -1
  79. package/dist/plugins/remark/headers.js +50 -0
  80. package/dist/plugins/remark/headers.js.map +1 -1
  81. package/dist/plugins/remark/imports.d.ts +10 -0
  82. package/dist/plugins/remark/imports.d.ts.map +1 -1
  83. package/dist/plugins/remark/imports.js +212 -153
  84. package/dist/plugins/remark/imports.js.map +1 -1
  85. package/dist/plugins/remark/legal-headers-parser.d.ts.map +1 -1
  86. package/dist/plugins/remark/legal-headers-parser.js +5 -2
  87. package/dist/plugins/remark/legal-headers-parser.js.map +1 -1
  88. package/dist/plugins/remark/plugin-metadata-registry.d.ts +36 -0
  89. package/dist/plugins/remark/plugin-metadata-registry.d.ts.map +1 -0
  90. package/dist/plugins/remark/plugin-metadata-registry.js +141 -0
  91. package/dist/plugins/remark/plugin-metadata-registry.js.map +1 -0
  92. package/dist/plugins/remark/plugin-order-validator.d.ts +79 -0
  93. package/dist/plugins/remark/plugin-order-validator.d.ts.map +1 -0
  94. package/dist/plugins/remark/plugin-order-validator.js +311 -0
  95. package/dist/plugins/remark/plugin-order-validator.js.map +1 -0
  96. package/dist/plugins/remark/types.d.ts +165 -0
  97. package/dist/plugins/remark/types.d.ts.map +1 -0
  98. package/dist/plugins/remark/types.js +10 -0
  99. package/dist/plugins/remark/types.js.map +1 -0
  100. package/dist/types/content-formats.d.ts +169 -0
  101. package/dist/types/content-formats.d.ts.map +1 -0
  102. package/dist/types/content-formats.js +178 -0
  103. package/dist/types/content-formats.js.map +1 -0
  104. package/dist/types.d.ts +2 -0
  105. package/dist/types.d.ts.map +1 -1
  106. package/dist/types.js +1 -1
  107. package/dist/types.js.map +1 -1
  108. package/dist/web/{browser-modern-wM0F4Tg1.js → browser-modern-BP5EJrCS.js} +1727 -1485
  109. package/dist/web/browser-modern-BP5EJrCS.js.map +1 -0
  110. package/dist/web/force-commands-parser-DDda8v0G.js +441 -0
  111. package/dist/web/force-commands-parser-DDda8v0G.js.map +1 -0
  112. package/dist/web/index.js.map +1 -1
  113. package/dist/web/legal-markdown-browser.js +1 -1
  114. package/dist/web/legal-markdown.umd.min.js +1 -1
  115. package/dist/web/legal-markdown.umd.min.js.map +1 -1
  116. package/dist/web/types.js.map +1 -1
  117. package/package.json +12 -3
  118. package/scripts/check-confusable-symbols.cjs +159 -0
  119. package/dist/browser-modern-wM0F4Tg1.js.map +0 -1
  120. package/dist/cli/interactive/prompts/ftux-handler.d.ts +0 -20
  121. package/dist/cli/interactive/prompts/ftux-handler.d.ts.map +0 -1
  122. package/dist/cli/interactive/prompts/ftux-handler.js +0 -406
  123. package/dist/cli/interactive/prompts/ftux-handler.js.map +0 -1
  124. package/dist/force-commands-parser-CkTMgF1y.js +0 -204
  125. package/dist/force-commands-parser-CkTMgF1y.js.map +0 -1
  126. package/dist/web/browser-modern-wM0F4Tg1.js.map +0 -1
  127. package/dist/web/force-commands-parser-CkTMgF1y.js +0 -204
  128. package/dist/web/force-commands-parser-CkTMgF1y.js.map +0 -1
@@ -16720,6 +16720,9 @@ function initializeHeaderState() {
16720
16720
  customLevels: /* @__PURE__ */ new Map()
16721
16721
  };
16722
16722
  }
16723
+ function getLevelCssClass(level) {
16724
+ return `legal-header-level-${level}`;
16725
+ }
16723
16726
  function processHeader(node, config, state, options) {
16724
16727
  const { noReset, noIndent, debug } = options;
16725
16728
  const level = node.depth;
@@ -16727,6 +16730,29 @@ function processHeader(node, config, state, options) {
16727
16730
  updateHeaderState(level, state, noReset);
16728
16731
  const number = getHeaderNumber(level, state);
16729
16732
  const headerText = formatHeaderText(node, format, number, state, { noIndent, debug });
16733
+ if (!node.data) {
16734
+ node.data = {};
16735
+ }
16736
+ const nodeData = node.data;
16737
+ if (!nodeData.hProperties) {
16738
+ nodeData.hProperties = {};
16739
+ }
16740
+ const cssClass = getLevelCssClass(level);
16741
+ const existingClass = nodeData.hProperties.className;
16742
+ if (existingClass) {
16743
+ if (Array.isArray(existingClass)) {
16744
+ if (!existingClass.includes(cssClass)) {
16745
+ existingClass.push(cssClass);
16746
+ }
16747
+ } else if (typeof existingClass === "string") {
16748
+ const classes = existingClass.split(" ");
16749
+ if (!classes.includes(cssClass)) {
16750
+ nodeData.hProperties.className = [...classes, cssClass];
16751
+ }
16752
+ }
16753
+ } else {
16754
+ nodeData.hProperties.className = cssClass;
16755
+ }
16730
16756
  if (headerText !== null) {
16731
16757
  const hasIndentation = headerText.startsWith(" ");
16732
16758
  if (hasIndentation) {
@@ -16737,6 +16763,7 @@ function processHeader(node, config, state, options) {
16737
16763
  }
16738
16764
  if (debug) {
16739
16765
  console.log(`[remarkHeaders] Processed level ${level} header:`, headerText);
16766
+ console.log(`[remarkHeaders] Added CSS class:`, cssClass);
16740
16767
  }
16741
16768
  }
16742
16769
  function getHeaderFormat(level, config) {
@@ -17055,16 +17082,16 @@ const remarkClauses = (options) => {
17055
17082
  }
17056
17083
  visit(tree, (node) => {
17057
17084
  if (node.type === "text") {
17058
- processTextNode$2(node, metadata, debug, enableFieldTracking);
17085
+ processTextNode$1(node, metadata, debug, enableFieldTracking);
17059
17086
  } else if (node.type === "html" && "value" in node) {
17060
17087
  processHtmlNode(node, metadata, debug, enableFieldTracking);
17061
17088
  } else if (node.type === "paragraph") {
17062
- processParagraphNode$2(node, metadata, debug, enableFieldTracking);
17089
+ processParagraphNode$1(node, metadata, debug, enableFieldTracking);
17063
17090
  }
17064
17091
  });
17065
17092
  };
17066
17093
  };
17067
- function processTextNode$2(node, metadata, debug, enableFieldTracking) {
17094
+ function processTextNode$1(node, metadata, debug, enableFieldTracking) {
17068
17095
  const originalText = node.value;
17069
17096
  const conditionalBlocks = extractConditionalBlocks(originalText);
17070
17097
  if (debug && originalText.includes("{{#")) {
@@ -17109,10 +17136,10 @@ function processHtmlNode(node, metadata, debug, enableFieldTracking) {
17109
17136
  }
17110
17137
  node.value = processedHtml;
17111
17138
  }
17112
- function processParagraphNode$2(node, metadata, debug, enableFieldTracking) {
17139
+ function processParagraphNode$1(node, metadata, debug, enableFieldTracking) {
17113
17140
  node.children.forEach((child) => {
17114
17141
  if (child.type === "text") {
17115
- processTextNode$2(child, metadata, debug, enableFieldTracking);
17142
+ processTextNode$1(child, metadata, debug, enableFieldTracking);
17116
17143
  }
17117
17144
  });
17118
17145
  }
@@ -17845,14 +17872,14 @@ const remarkMixins = (options) => {
17845
17872
  };
17846
17873
  visit(tree, (node) => {
17847
17874
  if (node.type === "text") {
17848
- processTextNode$1(node, context);
17875
+ processTextNode(node, context);
17849
17876
  } else if (node.type === "paragraph") {
17850
- processParagraphNode$1(node, context);
17877
+ processParagraphNode(node, context);
17851
17878
  }
17852
17879
  });
17853
17880
  };
17854
17881
  };
17855
- function processTextNode$1(node, context) {
17882
+ function processTextNode(node, context) {
17856
17883
  const originalText = node.value;
17857
17884
  const mixinDirectives = extractMixinDirectives(originalText);
17858
17885
  if (mixinDirectives.length === 0) {
@@ -17869,10 +17896,10 @@ function processTextNode$1(node, context) {
17869
17896
  }
17870
17897
  node.value = processedText;
17871
17898
  }
17872
- function processParagraphNode$1(node, context) {
17899
+ function processParagraphNode(node, context) {
17873
17900
  node.children.forEach((child) => {
17874
17901
  if (child.type === "text") {
17875
- processTextNode$1(child, context);
17902
+ processTextNode(child, context);
17876
17903
  }
17877
17904
  });
17878
17905
  }
@@ -21023,1623 +21050,1795 @@ function checkNestedConflicts(key, importedValue, currentFlat, logOperations) {
21023
21050
  return { hasConflict: false, conflictedField: "" };
21024
21051
  }
21025
21052
 
21026
- function escapeHtmlAttribute$1(value) {
21027
- return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#39;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
21028
- }
21029
- function processTemplateLoops(content, metadata, context, enableFieldTracking = true) {
21030
- const loopBlocks = findLoopBlocks(content);
21031
- if (loopBlocks.length === 0) {
21032
- return content;
21033
- }
21034
- let processedContent = content;
21035
- for (let i = loopBlocks.length - 1; i >= 0; i--) {
21036
- const loopBlock = loopBlocks[i];
21037
- const expandedContent = expandLoopBlock(loopBlock, metadata, context, enableFieldTracking);
21038
- processedContent = processedContent.slice(0, loopBlock.start) + expandedContent + processedContent.slice(loopBlock.end);
21053
+ const remarkImports = (options) => {
21054
+ const {
21055
+ basePath = ".",
21056
+ mergeMetadata = true,
21057
+ debug = false,
21058
+ maxDepth = 10,
21059
+ timeoutMs = 3e4,
21060
+ filterReserved = true,
21061
+ validateTypes = true,
21062
+ logImportOperations = false,
21063
+ onMetadataMerged,
21064
+ importStack = []
21065
+ } = options;
21066
+ return async (tree) => {
21067
+ const startTime = Date.now();
21068
+ if (debug) {
21069
+ console.log("[remarkImports] Processing imports with options:", {
21070
+ basePath,
21071
+ mergeMetadata,
21072
+ maxDepth,
21073
+ timeoutMs,
21074
+ filterReserved,
21075
+ validateTypes,
21076
+ currentDepth: importStack.length
21077
+ });
21078
+ }
21079
+ const context = {
21080
+ depth: importStack.length,
21081
+ maxDepth,
21082
+ basePath,
21083
+ mergeMetadata,
21084
+ debug,
21085
+ startTime,
21086
+ timeoutMs,
21087
+ filterReserved,
21088
+ validateTypes,
21089
+ logImportOperations,
21090
+ onMetadataMerged,
21091
+ importStack: [...importStack],
21092
+ contentCache: /* @__PURE__ */ new Map(),
21093
+ importedMetadataList: [],
21094
+ importedFiles: [],
21095
+ accumulatedMetadata: {}
21096
+ // Initialize with empty metadata
21097
+ };
21098
+ const replacements = [];
21099
+ visit(tree, "paragraph", (node, index, parent) => {
21100
+ if (!parent || index === void 0) return;
21101
+ for (const child of node.children) {
21102
+ if (child.type === "text") {
21103
+ const directives = extractImportDirectives(child.value);
21104
+ if (directives.length > 0) {
21105
+ replacements.push({
21106
+ parent,
21107
+ index,
21108
+ nodes: []
21109
+ // Will be filled in later
21110
+ });
21111
+ return SKIP;
21112
+ }
21113
+ }
21114
+ }
21115
+ });
21116
+ for (const replacement of replacements) {
21117
+ const paragraph = replacement.parent.children[replacement.index];
21118
+ const processedNodes = await processParagraphWithImports(paragraph, context);
21119
+ replacement.nodes = processedNodes;
21120
+ }
21121
+ for (let i = replacements.length - 1; i >= 0; i--) {
21122
+ const { parent, index, nodes } = replacements[i];
21123
+ parent.children.splice(index, 1, ...nodes);
21124
+ }
21125
+ if (context.mergeMetadata && context.importedMetadataList.length > 0) {
21126
+ const mergedResult = performSequentialMerge(context);
21127
+ tree._importedMetadata = mergedResult.metadata;
21128
+ tree._importStats = mergedResult.stats;
21129
+ }
21130
+ };
21131
+ };
21132
+ async function processParagraphWithImports(paragraph, context) {
21133
+ const results = [];
21134
+ for (const child of paragraph.children) {
21135
+ if (child.type !== "text") {
21136
+ results.push(child);
21137
+ continue;
21138
+ }
21139
+ const text = child.value;
21140
+ const directives = extractImportDirectives(text);
21141
+ if (directives.length === 0) {
21142
+ results.push(child);
21143
+ continue;
21144
+ }
21145
+ if (context.debug) {
21146
+ console.log(`[remarkImports] Found ${directives.length} import directives`);
21147
+ }
21148
+ let lastIndex = 0;
21149
+ for (const directive of directives) {
21150
+ if (directive.start > lastIndex) {
21151
+ const beforeText = text.substring(lastIndex, directive.start);
21152
+ if (beforeText.trim()) {
21153
+ results.push({
21154
+ type: "text",
21155
+ value: beforeText
21156
+ });
21157
+ }
21158
+ }
21159
+ const importedNodes = await processImportDirectiveToAST(directive, context);
21160
+ results.push(...importedNodes);
21161
+ lastIndex = directive.end;
21162
+ }
21163
+ if (lastIndex < text.length) {
21164
+ const afterText = text.substring(lastIndex);
21165
+ if (afterText.trim()) {
21166
+ results.push({
21167
+ type: "text",
21168
+ value: afterText
21169
+ });
21170
+ }
21171
+ }
21039
21172
  }
21040
- return processedContent;
21173
+ return results;
21041
21174
  }
21042
- function findLoopBlocks(content) {
21043
- const loopBlocks = [];
21044
- const loopPattern = /\{\{#([\w.]+)\}\}([\s\S]*?)\{\{\/\1\}\}/g;
21045
- const conditionalPattern = /\{\{#if\s*([^}]+)\}\}([\s\S]*?)\{\{\/if\}\}/g;
21175
+ function extractImportDirectives(text) {
21176
+ const directives = [];
21177
+ const importRegex = /@import\s+([^\s#]+)(?:#([^\s]+))?/g;
21046
21178
  let match;
21047
- while ((match = loopPattern.exec(content)) !== null) {
21048
- const [fullMatch, variable, loopContent] = match;
21049
- loopBlocks.push({
21050
- variable,
21051
- content: loopContent,
21052
- start: match.index,
21053
- end: match.index + fullMatch.length,
21054
- fullMatch
21055
- });
21056
- }
21057
- while ((match = conditionalPattern.exec(content)) !== null) {
21058
- const [fullMatch, condition, conditionalContent] = match;
21059
- loopBlocks.push({
21060
- variable: `if ${condition}`,
21061
- // Mark as conditional
21062
- content: conditionalContent,
21179
+ while ((match = importRegex.exec(text)) !== null) {
21180
+ const [fullMatch, filePath, section] = match;
21181
+ directives.push({
21182
+ filePath: filePath.trim(),
21183
+ section: section?.trim(),
21063
21184
  start: match.index,
21064
21185
  end: match.index + fullMatch.length,
21065
21186
  fullMatch
21066
21187
  });
21067
21188
  }
21068
- return loopBlocks;
21189
+ return directives;
21069
21190
  }
21070
- function expandLoopBlock(loopBlock, metadata, parentContext, enableFieldTracking = true) {
21071
- const { variable, content } = loopBlock;
21072
- if (variable.startsWith("if ")) {
21073
- const condition = variable.substring(3).trim();
21074
- const conditionValue = evaluateCondition(condition, metadata);
21075
- fieldTracker.trackField(`if ${condition}`, {
21076
- value: conditionValue,
21077
- hasLogic: true,
21078
- mixinUsed: "conditional"
21079
- });
21080
- const elseIndex = content.indexOf("{{else}}");
21081
- if (elseIndex !== -1) {
21082
- const ifContent = content.substring(0, elseIndex);
21083
- const elseContent = content.substring(elseIndex + 8);
21084
- const selectedContent = conditionValue ? ifContent : elseContent;
21085
- return processTemplateContent(selectedContent, metadata, parentContext, enableFieldTracking);
21086
- } else {
21087
- if (conditionValue) {
21088
- return processTemplateContent(content, metadata, parentContext, enableFieldTracking);
21089
- } else {
21090
- return "";
21191
+ async function parseImportedContentToAST(content) {
21192
+ const processor = unified().use(remarkParse);
21193
+ const tree = processor.parse(content);
21194
+ return tree.children;
21195
+ }
21196
+ async function processImportDirectiveToAST(directive, context) {
21197
+ if (context.depth >= context.maxDepth) {
21198
+ console.warn(
21199
+ `[remarkImports] Maximum import depth (${context.maxDepth}) reached for file "${directive.filePath}"`
21200
+ );
21201
+ return [{ type: "text", value: directive.fullMatch }];
21202
+ }
21203
+ const absolutePath = pathBrowserifyExports.resolve(context.basePath, directive.filePath);
21204
+ const normalizedPath = pathBrowserifyExports.normalize(absolutePath);
21205
+ if (context.importStack.includes(normalizedPath)) {
21206
+ console.warn(`[remarkImports] Circular import detected: ${normalizedPath}`);
21207
+ return [{ type: "text", value: directive.fullMatch }];
21208
+ }
21209
+ if (context.debug) {
21210
+ console.log(
21211
+ `[remarkImports] Processing import "${directive.filePath}" (resolved: ${normalizedPath})`
21212
+ );
21213
+ }
21214
+ const fileContent = loadFileContent(normalizedPath, context);
21215
+ if (!fileContent) {
21216
+ console.warn(`[remarkImports] Import file not found: ${directive.filePath}`);
21217
+ return [{ type: "text", value: directive.fullMatch }];
21218
+ }
21219
+ let contentToImport = fileContent;
21220
+ if (context.mergeMetadata) {
21221
+ const { content, metadata } = parseYamlFrontMatter(fileContent, false);
21222
+ contentToImport = content;
21223
+ if (Object.keys(metadata).length > 0) {
21224
+ context.importedMetadataList.push({
21225
+ metadata,
21226
+ source: normalizedPath
21227
+ });
21228
+ context.accumulatedMetadata = {
21229
+ ...context.accumulatedMetadata,
21230
+ ...metadata
21231
+ };
21232
+ if (!context.importedFiles.includes(normalizedPath)) {
21233
+ context.importedFiles.push(normalizedPath);
21234
+ }
21235
+ if (context.debug) {
21236
+ console.log(
21237
+ `[remarkImports] Collected metadata from ${directive.filePath}:`,
21238
+ Object.keys(metadata)
21239
+ );
21091
21240
  }
21092
21241
  }
21093
21242
  }
21094
- const loopValue = resolveLoopVariable(variable, metadata, parentContext);
21095
- fieldTracker.trackField(variable, {
21096
- value: loopValue,
21097
- hasLogic: true,
21098
- mixinUsed: "loop"
21099
- });
21100
- if (Array.isArray(loopValue)) {
21101
- return expandArrayLoop(
21102
- variable,
21103
- content,
21104
- loopValue,
21105
- metadata,
21106
- parentContext,
21107
- enableFieldTracking
21108
- );
21109
- } else if (loopValue) {
21110
- return expandConditionalBlock(
21111
- variable,
21112
- content,
21113
- loopValue,
21114
- metadata,
21115
- parentContext,
21116
- enableFieldTracking
21117
- );
21118
- } else {
21119
- return "";
21243
+ if (directive.section) {
21244
+ contentToImport = extractSection(contentToImport, directive.section, context.debug);
21245
+ }
21246
+ const nestedContext = {
21247
+ ...context,
21248
+ depth: context.depth + 1,
21249
+ importStack: [...context.importStack, normalizedPath],
21250
+ basePath: pathBrowserifyExports.dirname(normalizedPath)
21251
+ // Update base path for relative imports
21252
+ };
21253
+ const processedContent = await processNestedImportsToAST(contentToImport, nestedContext);
21254
+ if (context.mergeMetadata && Object.keys(context.accumulatedMetadata).length > 0) {
21255
+ return await expandMixinsInAST(processedContent, context.accumulatedMetadata);
21120
21256
  }
21257
+ return processedContent;
21121
21258
  }
21122
- function expandArrayLoop(variable, content, items, metadata, parentContext, enableFieldTracking = true) {
21123
- const expandedParts = [];
21124
- for (let i = 0; i < items.length; i++) {
21125
- const item = items[i];
21126
- const loopContext = {
21127
- variable,
21128
- item,
21129
- index: i,
21130
- total: items.length,
21131
- parent: parentContext
21259
+ function performSequentialMerge(context) {
21260
+ if (context.importedMetadataList.length === 0) {
21261
+ return {
21262
+ metadata: {},
21263
+ stats: void 0
21132
21264
  };
21133
- const enhancedMetadata = createEnhancedMetadata(metadata, item, loopContext);
21134
- let processedContent = content;
21135
- processedContent = processTemplateLoops(
21136
- processedContent,
21137
- enhancedMetadata,
21138
- loopContext,
21139
- enableFieldTracking
21265
+ }
21266
+ if (Date.now() - context.startTime > context.timeoutMs) {
21267
+ throw new Error(
21268
+ `Import processing timed out after ${context.timeoutMs}ms. This may indicate complex nested imports or slow file operations.`
21140
21269
  );
21141
- processedContent = processItemMixins(
21142
- processedContent,
21143
- enhancedMetadata,
21144
- loopContext,
21145
- enableFieldTracking
21146
- );
21147
- processedContent = convertMarkdownListToHtml(processedContent);
21148
- expandedParts.push(processedContent);
21149
21270
  }
21150
- return expandedParts.join("\n");
21151
- }
21152
- function expandConditionalBlock(variable, content, value, metadata, parentContext, enableFieldTracking = true) {
21153
- const enhancedMetadata = {
21154
- ...metadata,
21155
- [variable]: value
21271
+ const mergeOptions = {
21272
+ filterReserved: context.filterReserved,
21273
+ validateTypes: context.validateTypes,
21274
+ logOperations: context.logImportOperations,
21275
+ includeStats: true,
21276
+ timeoutMs: Math.max(1e3, context.timeoutMs - (Date.now() - context.startTime))
21156
21277
  };
21157
- let processedContent = processTemplateLoops(
21158
- content,
21159
- enhancedMetadata,
21160
- parentContext,
21161
- enableFieldTracking
21162
- );
21163
- processedContent = processItemMixins(
21164
- processedContent,
21165
- enhancedMetadata,
21166
- parentContext,
21167
- enableFieldTracking
21168
- );
21169
- return processedContent;
21170
- }
21171
- function resolveLoopVariable(variable, metadata, parentContext) {
21172
- const resolvedFromMetadata = resolvePath(metadata, variable);
21173
- if (resolvedFromMetadata !== void 0) {
21174
- return resolvedFromMetadata;
21278
+ const metadataList = context.importedMetadataList.map((item) => item.metadata);
21279
+ if (context.debug) {
21280
+ console.log(
21281
+ `[remarkImports] Performing sequential merge of ${metadataList.length} metadata objects`
21282
+ );
21175
21283
  }
21176
- if (parentContext && parentContext.item) {
21177
- const resolvedFromContext = resolvePath(parentContext.item, variable);
21178
- if (resolvedFromContext !== void 0) {
21179
- return resolvedFromContext;
21284
+ try {
21285
+ const result = mergeSequentially({}, metadataList, mergeOptions);
21286
+ if (context.onMetadataMerged && Object.keys(result.metadata).length > 0) {
21287
+ context.onMetadataMerged(result.metadata, "merged-imports");
21180
21288
  }
21181
- }
21182
- let ctx = parentContext;
21183
- while (ctx && ctx.parent) {
21184
- ctx = ctx.parent;
21185
- if (ctx.item) {
21186
- const resolvedFromNestedContext = resolvePath(ctx.item, variable);
21187
- if (resolvedFromNestedContext !== void 0) {
21188
- return resolvedFromNestedContext;
21189
- }
21289
+ return result;
21290
+ } catch (error) {
21291
+ if (context.debug) {
21292
+ console.warn("[remarkImports] Sequential merge failed:", error);
21190
21293
  }
21294
+ throw error;
21191
21295
  }
21192
- return void 0;
21193
21296
  }
21194
- function createEnhancedMetadata(metadata, item, context) {
21195
- const enhanced = { ...metadata };
21196
- if (item && typeof item === "object" && !Array.isArray(item)) {
21197
- Object.assign(enhanced, item);
21297
+ function loadFileContent(filePath, context) {
21298
+ if (context.contentCache.has(filePath)) {
21299
+ return context.contentCache.get(filePath);
21198
21300
  }
21199
- enhanced["."] = item;
21200
- enhanced["@index"] = context.index;
21201
- enhanced["@total"] = context.total;
21202
- enhanced["@first"] = context.index === 0;
21203
- enhanced["@last"] = context.index === context.total - 1;
21204
- return enhanced;
21205
- }
21206
- function evaluateHelperExpression(expression, metadata) {
21207
21301
  try {
21208
- const match = expression.match(/^(\w+)\((.*)\)$/);
21209
- if (!match) return void 0;
21210
- const [, helperName, argsString] = match;
21211
- const helper = extensionHelpers[helperName];
21212
- if (!helper || typeof helper !== "function") {
21213
- return void 0;
21214
- }
21215
- const args = argsString ? parseHelperArguments(argsString, metadata) : [];
21216
- return helper(...args);
21302
+ if (existsSync(filePath)) ;
21217
21303
  } catch (error) {
21218
- console.error(`Error evaluating helper expression: ${expression}`, error);
21219
- return void 0;
21220
- }
21221
- }
21222
- function parseHelperArguments(argsString, metadata) {
21223
- const args = [];
21224
- let currentArg = "";
21225
- let parenDepth = 0;
21226
- let inQuotes = false;
21227
- let quoteChar = "";
21228
- for (let i = 0; i < argsString.length; i++) {
21229
- const char = argsString[i];
21230
- if ((char === '"' || char === "'") && !inQuotes) {
21231
- inQuotes = true;
21232
- quoteChar = char;
21233
- currentArg += char;
21234
- } else if (char === quoteChar && inQuotes) {
21235
- inQuotes = false;
21236
- quoteChar = "";
21237
- currentArg += char;
21238
- } else if (char === "(" && !inQuotes) {
21239
- parenDepth++;
21240
- currentArg += char;
21241
- } else if (char === ")" && !inQuotes) {
21242
- parenDepth--;
21243
- currentArg += char;
21244
- } else if (char === "," && parenDepth === 0 && !inQuotes) {
21245
- args.push(resolveHelperArgument(currentArg.trim(), metadata));
21246
- currentArg = "";
21247
- } else {
21248
- currentArg += char;
21304
+ if (context.debug) {
21305
+ console.warn(`[remarkImports] Failed to load import file "${filePath}":`, error);
21249
21306
  }
21250
21307
  }
21251
- if (currentArg.trim()) {
21252
- args.push(resolveHelperArgument(currentArg.trim(), metadata));
21253
- }
21254
- return args;
21308
+ return null;
21255
21309
  }
21256
- function resolveHelperArgument(arg, metadata) {
21257
- if (arg.startsWith('"') && arg.endsWith('"') || arg.startsWith("'") && arg.endsWith("'")) {
21258
- return arg.slice(1, -1);
21259
- }
21260
- if (arg === "true") return true;
21261
- if (arg === "false") return false;
21262
- if (/^\d+$/.test(arg)) {
21263
- return parseInt(arg, 10);
21264
- }
21265
- if (/^\d+\.\d+$/.test(arg)) {
21266
- return parseFloat(arg);
21310
+ function extractSection(content, sectionName, debug) {
21311
+ const lines = content.split("\n");
21312
+ let sectionStart = -1;
21313
+ let sectionEnd = lines.length;
21314
+ let sectionLevel = 0;
21315
+ for (let i = 0; i < lines.length; i++) {
21316
+ const line = lines[i].trim();
21317
+ const headerMatch = line.match(/^(#{1,6})\s+(.+)$/);
21318
+ if (headerMatch) {
21319
+ const [, hashes, title] = headerMatch;
21320
+ const level = hashes.length;
21321
+ if (title.toLowerCase() === sectionName.toLowerCase()) {
21322
+ sectionStart = i + 1;
21323
+ sectionLevel = level;
21324
+ if (debug) {
21325
+ console.log(`[remarkImports] Found section '${sectionName}' at line ${i}`);
21326
+ }
21327
+ break;
21328
+ }
21329
+ }
21267
21330
  }
21268
- if (arg === "@today") {
21269
- return metadata["@today"] ? new Date(metadata["@today"]) : /* @__PURE__ */ new Date();
21331
+ if (sectionStart === -1) {
21332
+ if (debug) {
21333
+ console.warn(`[remarkImports] Section '${sectionName}' not found`);
21334
+ }
21335
+ return content;
21270
21336
  }
21271
- if (arg.includes("(") && arg.includes(")")) {
21272
- const result = evaluateHelperExpression(arg, metadata);
21273
- if (result !== void 0) {
21274
- return result;
21337
+ for (let i = sectionStart; i < lines.length; i++) {
21338
+ const line = lines[i].trim();
21339
+ const headerMatch = line.match(/^(#{1,6})\s+(.+)$/);
21340
+ if (headerMatch) {
21341
+ const [, hashes] = headerMatch;
21342
+ const level = hashes.length;
21343
+ if (level <= sectionLevel) {
21344
+ sectionEnd = i;
21345
+ break;
21346
+ }
21275
21347
  }
21276
21348
  }
21277
- return resolvePath(metadata, arg);
21349
+ return lines.slice(sectionStart, sectionEnd).join("\n").trim();
21278
21350
  }
21279
- function resolvePath(obj, path) {
21280
- const keys = path.split(".");
21281
- let current = obj;
21282
- for (const key of keys) {
21283
- if (current && typeof current === "object" && key in current) {
21284
- current = current[key];
21351
+ async function processNestedImportsToAST(content, context) {
21352
+ const nodes = await parseImportedContentToAST(content);
21353
+ const hasImports = nodes.some((node) => {
21354
+ if (node.type === "paragraph") {
21355
+ return node.children.some(
21356
+ (child) => child.type === "text" && extractImportDirectives(child.value).length > 0
21357
+ );
21358
+ }
21359
+ return false;
21360
+ });
21361
+ if (!hasImports) {
21362
+ return nodes;
21363
+ }
21364
+ const processedNodes = [];
21365
+ for (const node of nodes) {
21366
+ if (node.type === "paragraph") {
21367
+ const paragraph = node;
21368
+ const hasDirectives = paragraph.children.some(
21369
+ (child) => child.type === "text" && extractImportDirectives(child.value).length > 0
21370
+ );
21371
+ if (hasDirectives) {
21372
+ const importedNodes = await processParagraphWithImports(paragraph, context);
21373
+ processedNodes.push(...importedNodes);
21374
+ } else {
21375
+ processedNodes.push(node);
21376
+ }
21285
21377
  } else {
21286
- return void 0;
21378
+ processedNodes.push(node);
21287
21379
  }
21288
21380
  }
21289
- return current;
21381
+ return processedNodes;
21290
21382
  }
21291
- function processItemMixins(content, metadata, context, enableFieldTracking = true) {
21292
- const mixinPattern = /\{\{([^}]+)\}\}/g;
21293
- return content.replace(mixinPattern, (match, variable) => {
21294
- const trimmedVar = variable.trim();
21295
- if (trimmedVar.includes("?") && trimmedVar.includes(":")) {
21296
- const result = processConditionalExpression(trimmedVar, metadata, enableFieldTracking);
21297
- if (enableFieldTracking) {
21298
- return `<span class="highlight">${result}</span>`;
21299
- }
21300
- return result;
21301
- }
21302
- if (trimmedVar.includes("(") && trimmedVar.includes(")")) {
21303
- const result = evaluateHelperExpression(trimmedVar, metadata);
21304
- if (result !== void 0) {
21305
- fieldTracker.trackField(trimmedVar, {
21306
- value: result,
21307
- hasLogic: true,
21308
- mixinUsed: "helper"
21309
- });
21310
- if (enableFieldTracking) {
21311
- const escapedField = escapeHtmlAttribute$1(trimmedVar);
21312
- const stringResult = String(result);
21313
- return `<span class="highlight"><span class="imported-value" data-field="${escapedField}">${stringResult}</span></span>`;
21383
+ async function expandMixinsInAST(nodes, metadata) {
21384
+ const processedNodes = [];
21385
+ for (const node of nodes) {
21386
+ if (node.type === "paragraph") {
21387
+ const paragraph = node;
21388
+ const processedChildren = [];
21389
+ for (const child of paragraph.children) {
21390
+ if (child.type === "text") {
21391
+ processedChildren.push(child);
21392
+ } else {
21393
+ processedChildren.push(child);
21314
21394
  }
21315
- return String(result);
21316
- }
21317
- }
21318
- const value = resolveVariablePath(trimmedVar, metadata);
21319
- if (value === void 0 || value === null) {
21320
- if (enableFieldTracking) {
21321
- return `<span class="legal-field missing-value" data-field="${escapeHtmlAttribute$1(trimmedVar)}">[[${trimmedVar}]]</span>`;
21322
21395
  }
21323
- return `{{${trimmedVar}}}`;
21324
- }
21325
- if (enableFieldTracking) {
21326
- return `<span class="imported-value" data-field="${escapeHtmlAttribute$1(trimmedVar)}">${String(value)}</span>`;
21327
- }
21328
- return String(value);
21329
- });
21330
- }
21331
- function processConditionalExpression(expression, metadata, enableFieldTracking = true) {
21332
- const questionIndex = findOperatorIndex(expression, "?");
21333
- const colonIndex = findOperatorIndex(expression, ":", questionIndex);
21334
- if (questionIndex === -1 || colonIndex === -1) {
21335
- return expression;
21336
- }
21337
- const condition = expression.substring(0, questionIndex).trim();
21338
- const truePart = expression.substring(questionIndex + 1, colonIndex).trim();
21339
- const falsePart = expression.substring(colonIndex + 1).trim();
21340
- const conditionValue = resolveVariablePath(condition, metadata);
21341
- const selectedPart = conditionValue ? truePart : falsePart;
21342
- if (selectedPart.startsWith('"') && selectedPart.endsWith('"') || // eslint-disable-next-line quotes
21343
- selectedPart.startsWith("'") && selectedPart.endsWith("'")) {
21344
- const unquotedValue = selectedPart.slice(1, -1);
21345
- if (enableFieldTracking) {
21346
- return `<span class="imported-value" data-field="${escapeHtmlAttribute$1(expression)}">${unquotedValue}</span>`;
21396
+ processedNodes.push({
21397
+ ...paragraph,
21398
+ children: processedChildren
21399
+ });
21400
+ } else {
21401
+ processedNodes.push(node);
21347
21402
  }
21348
- return unquotedValue;
21349
21403
  }
21350
- const processedValue = processExpression(selectedPart, metadata);
21351
- if (enableFieldTracking) {
21352
- return `<span class="imported-value" data-field="${escapeHtmlAttribute$1(expression)}">${processedValue}</span>`;
21353
- }
21354
- return processedValue;
21404
+ return processedNodes;
21355
21405
  }
21356
- function findOperatorIndex(expression, operator, startIndex = 0) {
21357
- let inQuotes = false;
21358
- let quoteChar = "";
21359
- for (let i = startIndex; i < expression.length; i++) {
21360
- const char = expression[i];
21361
- if (!inQuotes && (char === '"' || char === "'")) {
21362
- inQuotes = true;
21363
- quoteChar = char;
21364
- } else if (inQuotes && char === quoteChar) {
21365
- inQuotes = false;
21366
- quoteChar = "";
21367
- } else if (!inQuotes && char === operator) {
21368
- return i;
21369
- }
21370
- }
21371
- return -1;
21372
- }
21373
- function processExpression(expression, metadata) {
21374
- if (expression.includes("+") && (expression.includes('"') || expression.includes("'"))) {
21375
- return evaluateStringConcatenation(expression, metadata);
21376
- }
21377
- if (expression.includes("*") || expression.includes("/") || expression.includes("+") || expression.includes("-")) {
21378
- return evaluateMathematicalExpression(expression, metadata);
21379
- }
21380
- const value = resolveVariablePath(expression, metadata);
21381
- return value !== void 0 ? String(value) : expression;
21382
- }
21383
- function evaluateStringConcatenation(expression, metadata) {
21384
- try {
21385
- let processedExpression = expression.trim();
21386
- const variablePattern = /\b[a-zA-Z_][a-zA-Z0-9_.]*\b/g;
21387
- const variables = /* @__PURE__ */ new Set();
21388
- let match;
21389
- let inQuotes = false;
21390
- let quoteChar = "";
21391
- for (let i = 0; i < expression.length; i++) {
21392
- const char = expression[i];
21393
- if (!inQuotes && (char === '"' || char === "'")) {
21394
- inQuotes = true;
21395
- quoteChar = char;
21396
- } else if (inQuotes && char === quoteChar) {
21397
- inQuotes = false;
21398
- quoteChar = "";
21399
- }
21400
- }
21401
- while ((match = variablePattern.exec(expression)) !== null) {
21402
- const beforeMatch = expression.substring(0, match.index);
21403
- const openQuotes = (beforeMatch.match(/"/g) || []).length;
21404
- const openSingleQuotes = (beforeMatch.match(/'/g) || []).length;
21405
- if (openQuotes % 2 === 0 && openSingleQuotes % 2 === 0) {
21406
- variables.add(match[0]);
21407
- }
21408
- }
21409
- for (const variable of variables) {
21410
- const value = resolveVariablePath(variable, metadata);
21411
- if (value !== void 0) {
21412
- const regex = new RegExp(`\\b${variable.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`, "g");
21413
- if (typeof value === "string") {
21414
- processedExpression = processedExpression.replace(regex, `"${value}"`);
21415
- } else {
21416
- processedExpression = processedExpression.replace(regex, String(value));
21417
- }
21418
- } else {
21419
- return expression;
21420
- }
21421
- }
21422
- const result = new Function(`"use strict"; return (${processedExpression})`)();
21423
- return String(result);
21424
- } catch (error) {
21425
- return expression;
21426
- }
21406
+
21407
+ const LEGAL_HEADER_PATTERN = /^(l{1,9})\.\s+(.+)$/;
21408
+ function getHeadingLevel(pattern) {
21409
+ return pattern.length;
21427
21410
  }
21428
- function evaluateMathematicalExpression(expression, metadata) {
21429
- try {
21430
- let processedExpression = expression;
21431
- const variablePattern = /[a-zA-Z_][a-zA-Z0-9_.]*(?![a-zA-Z0-9_.])/g;
21432
- const variables = /* @__PURE__ */ new Set();
21433
- let match;
21434
- while ((match = variablePattern.exec(expression)) !== null) {
21435
- variables.add(match[0]);
21436
- }
21437
- for (const variable of variables) {
21438
- const value = resolveVariablePath(variable, metadata);
21439
- if (value !== void 0 && !isNaN(Number(value))) {
21440
- const regex = new RegExp(`\\b${variable.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`, "g");
21441
- processedExpression = processedExpression.replace(regex, String(value));
21442
- } else {
21443
- return expression;
21444
- }
21445
- }
21446
- if (!/^[\d\s+\-*/().]+$/.test(processedExpression)) {
21447
- return expression;
21411
+ function isLegalHeader(node) {
21412
+ if (node.children.length === 1 && node.children[0].type === "text") {
21413
+ const textNode = node.children[0];
21414
+ const match = textNode.value.match(LEGAL_HEADER_PATTERN);
21415
+ if (match) {
21416
+ const [, levelPattern, headerText] = match;
21417
+ return {
21418
+ level: getHeadingLevel(levelPattern),
21419
+ text: headerText.trim()
21420
+ };
21448
21421
  }
21449
- const result = new Function(`"use strict"; return (${processedExpression})`)();
21450
- return typeof result === "number" && !isNaN(result) ? String(result) : expression;
21451
- } catch (error) {
21452
- return expression;
21453
21422
  }
21454
- }
21455
- function resolveVariablePath(path, metadata, context) {
21456
- if (path === ".") {
21457
- return metadata["."];
21458
- }
21459
- const parts = path.split(".");
21460
- let current = metadata;
21461
- for (const part of parts) {
21462
- if (current && typeof current === "object" && Object.prototype.hasOwnProperty.call(current, part)) {
21463
- current = current[part];
21464
- } else {
21465
- return void 0;
21423
+ if (node.children.length > 0 && node.children[0].type === "text") {
21424
+ const firstChild = node.children[0];
21425
+ const match = firstChild.value.match(/^(l{1,5})\.\s*/);
21426
+ if (match) {
21427
+ const [fullMatch, levelPattern] = match;
21428
+ const remainingText = firstChild.value.slice(fullMatch.length);
21429
+ const otherText = node.children.slice(1).map((child) => {
21430
+ if (child.type === "text") return child.value;
21431
+ if (child.type === "html") return child.value || "";
21432
+ return "";
21433
+ }).join("");
21434
+ return {
21435
+ level: getHeadingLevel(levelPattern),
21436
+ text: (remainingText + otherText).trim()
21437
+ };
21466
21438
  }
21467
21439
  }
21468
- return current;
21469
- }
21470
- function convertMarkdownListToHtml(content) {
21471
- const trimmedContent = content.trim();
21472
- if (trimmedContent.startsWith("- ")) {
21473
- const listItemContent = trimmedContent.substring(2).trim();
21474
- return `<li>${listItemContent}</li>`;
21475
- }
21476
- return content;
21477
- }
21478
- function evaluateCondition(condition, metadata) {
21479
- const value = resolveVariablePath(condition, metadata);
21480
- if (value === null || value === void 0) return false;
21481
- if (typeof value === "boolean") return value;
21482
- if (typeof value === "number") return value !== 0;
21483
- if (typeof value === "string") return value.length > 0;
21484
- if (Array.isArray(value)) return value.length > 0;
21485
- if (typeof value === "object") return Object.keys(value).length > 0;
21486
- return Boolean(value);
21487
- }
21488
- function processTemplateContent(content, metadata, context, enableFieldTracking = true) {
21489
- let processedContent = processTemplateLoops(content, metadata, context, enableFieldTracking);
21490
- processedContent = processedContent.replace(/\{\{([^#/][^}]*)\}\}/g, (match, variable) => {
21491
- const trimmedVar = variable.trim();
21492
- const value = resolveVariablePath(trimmedVar, metadata);
21493
- if (value !== void 0) {
21494
- if (enableFieldTracking) {
21495
- const isEmptyValue = value === null || value === "" || typeof value === "string" && value.trim() === "";
21496
- const cssClass = isEmptyValue ? "legal-field missing-value" : "legal-field imported-value";
21497
- fieldTracker.trackField(trimmedVar, {
21498
- value,
21499
- originalValue: match,
21500
- hasLogic: false
21501
- });
21502
- return `<span class="${cssClass}" data-field="${escapeHtmlAttribute$1(trimmedVar)}">${String(value)}</span>`;
21503
- }
21504
- return String(value);
21505
- }
21506
- return match;
21507
- });
21508
- return processedContent;
21509
- }
21510
-
21511
- function escapeHtmlAttribute(value) {
21512
- return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#39;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
21440
+ return null;
21513
21441
  }
21514
- function processMixins(content, metadata, options = {}) {
21515
- if (options.noMixins) {
21516
- return content;
21517
- }
21518
- const mixinPattern = /\{\{([^}]+)\}\}/g;
21519
- function resolvePath(obj, path) {
21520
- if (path === ".") {
21521
- return obj?.["."];
21522
- }
21523
- return path.split(".").reduce((current, part) => {
21524
- const match = part.match(/^(\w+)\[(\d+)\]$/);
21442
+ function convertToHeading(node, level, text) {
21443
+ const heading = {
21444
+ type: "heading",
21445
+ depth: level,
21446
+ children: parseMarkdownInlineFormatting(text)
21447
+ };
21448
+ heading.data = {
21449
+ isLegalHeader: true,
21450
+ hProperties: {}
21451
+ // Initialize for remarkHeaders to add CSS classes
21452
+ };
21453
+ if (node.children.length > 1 || node.children[0].type === "text" && node.children[0].value.includes("<span")) {
21454
+ const firstChild = node.children[0];
21455
+ if (firstChild.type === "text") {
21456
+ const match = firstChild.value.match(/^(l{1,5})\.\s*/);
21525
21457
  if (match) {
21526
- const [, key, index] = match;
21527
- return current?.[key]?.[parseInt(index, 10)];
21528
- }
21529
- return current?.[part];
21530
- }, obj);
21531
- }
21532
- function evaluateHelperExpression(expression, metadata2) {
21533
- try {
21534
- const match = expression.match(/^(\w+)\((.*)\)$/);
21535
- if (!match) return void 0;
21536
- const [, helperName, argsString] = match;
21537
- const helper = extensionHelpers[helperName];
21538
- if (!helper || typeof helper !== "function") {
21539
- return void 0;
21540
- }
21541
- const args = parseArguments(argsString, metadata2);
21542
- return helper(...args);
21543
- } catch (error) {
21544
- console.warn(`Error evaluating helper expression: ${expression}`, error);
21545
- return void 0;
21546
- }
21547
- }
21548
- function parseArguments(argsString, metadata2) {
21549
- if (!argsString.trim()) return [];
21550
- const args = [];
21551
- let current = "";
21552
- let inQuotes = false;
21553
- let quoteChar = "";
21554
- let parenDepth = 0;
21555
- for (let i = 0; i < argsString.length; i++) {
21556
- const char = argsString[i];
21557
- if (!inQuotes && (char === '"' || char === "'")) {
21558
- inQuotes = true;
21559
- quoteChar = char;
21560
- current += char;
21561
- continue;
21562
- }
21563
- if (inQuotes && char === quoteChar) {
21564
- inQuotes = false;
21565
- quoteChar = "";
21566
- current += char;
21567
- continue;
21568
- }
21569
- if (!inQuotes && char === "(") {
21570
- parenDepth++;
21571
- current += char;
21572
- continue;
21573
- }
21574
- if (!inQuotes && char === ")") {
21575
- parenDepth--;
21576
- current += char;
21577
- continue;
21578
- }
21579
- if (!inQuotes && char === "," && parenDepth === 0) {
21580
- args.push(parseArgument(current.trim(), metadata2));
21581
- current = "";
21582
- continue;
21583
- }
21584
- current += char;
21585
- }
21586
- if (current.trim()) {
21587
- args.push(parseArgument(current.trim(), metadata2));
21588
- }
21589
- return args;
21590
- }
21591
- function parseArgument(arg, metadata2) {
21592
- if (arg.startsWith('"') && arg.endsWith('"') || arg.startsWith("'") && arg.endsWith("'")) {
21593
- return arg.slice(1, -1);
21594
- }
21595
- if (/^-?\d+(\.\d+)?$/.test(arg)) {
21596
- return parseFloat(arg);
21597
- }
21598
- if (arg === "true") return true;
21599
- if (arg === "false") return false;
21600
- if (arg === "null") return null;
21601
- if (arg === "undefined") return void 0;
21602
- if (arg === "@today") {
21603
- return metadata2["@today"] ? new Date(metadata2["@today"]) : /* @__PURE__ */ new Date();
21604
- }
21605
- if (arg.includes("(") && arg.includes(")")) {
21606
- const result = evaluateHelperExpression(arg, metadata2);
21607
- if (result !== void 0) {
21608
- return result;
21609
- }
21610
- }
21611
- return resolvePath(metadata2, arg);
21612
- }
21613
- function evaluateExpression(expression, metadata2) {
21614
- try {
21615
- let cleanExpression = expression.trim();
21616
- const variables = Object.keys(metadata2);
21617
- variables.forEach((varName) => {
21618
- const value = metadata2[varName];
21619
- const regex = new RegExp(`\\b${varName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`, "g");
21620
- if (typeof value === "string") {
21621
- cleanExpression = cleanExpression.replace(regex, `"${value}"`);
21622
- } else if (typeof value === "number") {
21623
- cleanExpression = cleanExpression.replace(regex, String(value));
21624
- } else if (value === null || value === void 0) {
21625
- cleanExpression = cleanExpression.replace(regex, "null");
21626
- } else {
21627
- cleanExpression = cleanExpression.replace(regex, JSON.stringify(value));
21628
- }
21629
- });
21630
- if (cleanExpression.includes("+") && cleanExpression.includes('"')) {
21631
- const result = Function(`"use strict"; return (${cleanExpression})`)();
21632
- return String(result);
21633
- } else if (/^[\d\s+\-*/().]+$/.test(cleanExpression)) {
21634
- const result = Function(`"use strict"; return (${cleanExpression})`)();
21635
- return String(result);
21458
+ const [fullMatch] = match;
21459
+ firstChild.value = firstChild.value.slice(fullMatch.length);
21460
+ heading.children = node.children.filter((child) => {
21461
+ if (child.type === "text" && child.value.trim() === "") {
21462
+ return false;
21463
+ }
21464
+ return true;
21465
+ });
21636
21466
  }
21637
- return expression;
21638
- } catch (error) {
21639
- return expression;
21640
21467
  }
21641
21468
  }
21642
- function replaceMixins(text, depth = 0) {
21643
- if (depth > 10) {
21644
- return text;
21645
- }
21646
- return text.replace(mixinPattern, (match, variable) => {
21647
- return processVariable(match, variable, depth);
21648
- });
21649
- }
21650
- function processVariable(match, variable, depth) {
21651
- const trimmedVar = variable.trim();
21652
- if (trimmedVar.includes("(") && trimmedVar.includes(")")) {
21653
- const result = evaluateHelperExpression(trimmedVar, metadata);
21654
- if (result !== void 0) {
21655
- fieldTracker.trackField(trimmedVar, {
21656
- value: result,
21657
- hasLogic: true,
21658
- mixinUsed: "helper"
21659
- });
21660
- if (options.enableFieldTrackingInMarkdown) {
21661
- return `<span class="highlight"><span class="imported-value" data-field="${escapeHtmlAttribute(trimmedVar)}">${String(result)}</span></span>`;
21662
- }
21663
- return String(result);
21664
- }
21665
- if (options.enableFieldTrackingInMarkdown) {
21666
- return `<span class="legal-field highlight"><span class="legal-field missing-value" data-field="${escapeHtmlAttribute(trimmedVar)}">[[${trimmedVar}]]</span></span>`;
21667
- }
21668
- return `{{${trimmedVar}}}`;
21469
+ return heading;
21470
+ }
21471
+ const remarkLegalHeadersParser = (options = {}) => {
21472
+ const { debug = false } = options;
21473
+ return (tree) => {
21474
+ if (debug) {
21475
+ console.log("🔍 [remarkLegalHeadersParser] Parsing legal header syntax");
21669
21476
  }
21670
- if (trimmedVar.includes("?")) {
21671
- const questionIndex = trimmedVar.indexOf("?");
21672
- const colonIndex = trimmedVar.indexOf(":", questionIndex);
21673
- if (colonIndex !== -1) {
21674
- const condition = trimmedVar.substring(0, questionIndex).trim();
21675
- const truePart = trimmedVar.substring(questionIndex + 1, colonIndex).trim();
21676
- const falsePart = trimmedVar.substring(colonIndex + 1).trim();
21677
- const conditionValue = resolvePath(metadata, condition);
21678
- const selectedPart = conditionValue ? truePart : falsePart;
21679
- fieldTracker.trackField(condition, {
21680
- value: conditionValue,
21681
- hasLogic: true,
21682
- mixinUsed: "conditional"
21683
- });
21684
- if (selectedPart) {
21685
- let processedPart;
21686
- if (selectedPart.includes("+") || selectedPart.includes("*") || selectedPart.includes("-") || selectedPart.includes("/")) {
21687
- processedPart = evaluateExpression(selectedPart, metadata);
21688
- if (processedPart === selectedPart) {
21689
- processedPart = replaceMixins(selectedPart, depth + 1);
21477
+ let convertedCount = 0;
21478
+ const nodesToReplace = [];
21479
+ visit(tree, "paragraph", (node, index, parent) => {
21480
+ if (parent && typeof index === "number") {
21481
+ if (node.children.length === 1 && node.children[0].type === "text") {
21482
+ const textNode = node.children[0];
21483
+ const lines = textNode.value.split("\n");
21484
+ const hasLegalHeaders = lines.some((line) => LEGAL_HEADER_PATTERN.test(line));
21485
+ if (hasLegalHeaders) {
21486
+ const newNodes = [];
21487
+ const nonHeaderLines = [];
21488
+ for (const line of lines) {
21489
+ const match = line.match(LEGAL_HEADER_PATTERN);
21490
+ if (match) {
21491
+ if (nonHeaderLines.length > 0) {
21492
+ newNodes.push({
21493
+ type: "paragraph",
21494
+ children: [
21495
+ {
21496
+ type: "text",
21497
+ value: nonHeaderLines.join("\n")
21498
+ }
21499
+ ]
21500
+ });
21501
+ nonHeaderLines.length = 0;
21502
+ }
21503
+ const [, levelPattern, headerText] = match;
21504
+ const level = getHeadingLevel(levelPattern);
21505
+ if (debug) {
21506
+ console.log(
21507
+ `🔄 [remarkLegalHeadersParser] Converting "${line.substring(0, 50)}..." to level ${level} heading`
21508
+ );
21509
+ }
21510
+ const headingNode = {
21511
+ type: "heading",
21512
+ depth: level,
21513
+ children: parseMarkdownInlineFormatting(headerText.trim())
21514
+ };
21515
+ headingNode.data = { isLegalHeader: true };
21516
+ newNodes.push(headingNode);
21517
+ convertedCount++;
21518
+ } else {
21519
+ nonHeaderLines.push(line);
21520
+ }
21690
21521
  }
21691
- } else {
21692
- processedPart = replaceMixins(selectedPart, depth + 1);
21522
+ if (nonHeaderLines.length > 0) {
21523
+ newNodes.push({
21524
+ type: "paragraph",
21525
+ children: [
21526
+ {
21527
+ type: "text",
21528
+ value: nonHeaderLines.join("\n")
21529
+ }
21530
+ ]
21531
+ });
21532
+ }
21533
+ if (newNodes.length > 0) {
21534
+ nodesToReplace.push({ parent, index, newNodes });
21535
+ }
21536
+ }
21537
+ } else if (node.children.length > 1) {
21538
+ let fullText = "";
21539
+ const childrenMap = /* @__PURE__ */ new Map();
21540
+ for (let i = 0; i < node.children.length; i++) {
21541
+ const child = node.children[i];
21542
+ const start = fullText.length;
21543
+ if (child.type === "text") {
21544
+ fullText += child.value;
21545
+ } else if (child.type === "emphasis") {
21546
+ const emphasisText = child.children.map((c) => c.value || "").join("");
21547
+ fullText += `_${emphasisText}_`;
21548
+ } else if (child.type === "strong") {
21549
+ const strongText = child.children.map((c) => c.value || "").join("");
21550
+ fullText += `__${strongText}__`;
21551
+ } else if (child.type === "link") {
21552
+ const linkText = child.children.map((c) => c.value || "").join("");
21553
+ const url = child.url || "";
21554
+ fullText += `[${linkText}](${url})`;
21555
+ } else if (child.type === "inlineCode") {
21556
+ fullText += `\`${child.value || ""}\``;
21557
+ } else {
21558
+ fullText += child.value || "";
21559
+ }
21560
+ const end = fullText.length;
21561
+ childrenMap.set(i, { start, end, child });
21693
21562
  }
21694
- if (options.enableFieldTrackingInMarkdown) {
21695
- return `<span class="highlight"><span class="imported-value" data-field="${escapeHtmlAttribute(trimmedVar)}">${processedPart}</span></span>`;
21563
+ const lines = fullText.split("\n");
21564
+ const hasLegalHeaders = lines.some((line) => LEGAL_HEADER_PATTERN.test(line));
21565
+ if (hasLegalHeaders) {
21566
+ const newNodes = [];
21567
+ const nonHeaderLines = [];
21568
+ for (const line of lines) {
21569
+ const match = line.match(LEGAL_HEADER_PATTERN);
21570
+ if (match) {
21571
+ if (nonHeaderLines.length > 0) {
21572
+ newNodes.push({
21573
+ type: "paragraph",
21574
+ children: [
21575
+ {
21576
+ type: "text",
21577
+ value: nonHeaderLines.join("\n")
21578
+ }
21579
+ ]
21580
+ });
21581
+ nonHeaderLines.length = 0;
21582
+ }
21583
+ const [, levelPattern, headerText] = match;
21584
+ const level = getHeadingLevel(levelPattern);
21585
+ if (debug) {
21586
+ console.log(
21587
+ `🔄 [remarkLegalHeadersParser] Converting complex "${line.substring(0, 50)}..." to level ${level} heading`
21588
+ );
21589
+ }
21590
+ const headingNode = {
21591
+ type: "heading",
21592
+ depth: level,
21593
+ children: parseMarkdownInlineFormatting(headerText.trim())
21594
+ };
21595
+ headingNode.data = { isLegalHeader: true };
21596
+ newNodes.push(headingNode);
21597
+ convertedCount++;
21598
+ } else {
21599
+ nonHeaderLines.push(line);
21600
+ }
21601
+ }
21602
+ if (nonHeaderLines.length > 0) {
21603
+ newNodes.push({
21604
+ type: "paragraph",
21605
+ children: [
21606
+ {
21607
+ type: "text",
21608
+ value: nonHeaderLines.join("\n")
21609
+ }
21610
+ ]
21611
+ });
21612
+ }
21613
+ if (newNodes.length > 0) {
21614
+ nodesToReplace.push({ parent, index, newNodes });
21615
+ }
21696
21616
  }
21697
- return processedPart;
21617
+ }
21618
+ } else {
21619
+ if (debug) {
21620
+ const firstChildText = node.children[0]?.type === "text" ? node.children[0].value : "<non-text>";
21621
+ console.log(`🔍 [remarkLegalHeadersParser] Single-line paragraph: "${firstChildText}"`);
21622
+ }
21623
+ const headerInfo = isLegalHeader(node);
21624
+ if (headerInfo && parent && typeof index === "number") {
21625
+ if (debug) {
21626
+ const firstChildText = node.children[0].type === "text" ? node.children[0].value : "";
21627
+ console.log(
21628
+ `🔄 [remarkLegalHeadersParser] Converting "${firstChildText.substring(0, 50)}..." to level ${headerInfo.level} heading`
21629
+ );
21630
+ }
21631
+ const heading = convertToHeading(node, headerInfo.level, headerInfo.text);
21632
+ nodesToReplace.push({ parent, index, newNodes: [heading] });
21633
+ convertedCount++;
21698
21634
  }
21699
21635
  }
21700
- if (options.enableFieldTrackingInMarkdown) {
21701
- return `<span class="legal-field highlight"><span class="legal-field missing-value" data-field="${escapeHtmlAttribute(trimmedVar)}">[[${trimmedVar}]]</span></span>`;
21702
- }
21703
- return `{{${trimmedVar}}}`;
21704
- }
21705
- const value = resolvePath(metadata, trimmedVar);
21706
- if (value === void 0 || value === null) {
21707
- fieldTracker.trackField(trimmedVar, {
21708
- value: void 0,
21709
- hasLogic: false
21710
- });
21711
- if (options.enableFieldTrackingInMarkdown) {
21712
- return `<span class="legal-field missing-value" data-field="${escapeHtmlAttribute(trimmedVar)}">[[${trimmedVar}]]</span>`;
21713
- }
21714
- return `{{${trimmedVar}}}`;
21715
- }
21716
- fieldTracker.trackField(trimmedVar, {
21717
- value,
21718
- hasLogic: false
21719
21636
  });
21720
- const stringValue = String(value);
21721
- if (mixinPattern.test(stringValue)) {
21722
- const nestedResult = replaceMixins(stringValue, depth + 1);
21723
- if (options.enableFieldTrackingInMarkdown) {
21724
- return `<span class="imported-value" data-field="${escapeHtmlAttribute(trimmedVar)}">${nestedResult}</span>`;
21725
- }
21726
- return nestedResult;
21727
- }
21728
- if (options.enableFieldTrackingInMarkdown) {
21729
- return `<span class="imported-value" data-field="${escapeHtmlAttribute(trimmedVar)}">${stringValue}</span>`;
21637
+ for (let i = nodesToReplace.length - 1; i >= 0; i--) {
21638
+ const { parent, index, newNodes } = nodesToReplace[i];
21639
+ parent.children.splice(index, 1, ...newNodes);
21730
21640
  }
21731
- return stringValue;
21732
- }
21733
- const loopProcessedContent = processTemplateLoops(
21734
- content,
21735
- metadata,
21736
- void 0,
21737
- options.enableFieldTrackingInMarkdown || false
21738
- );
21739
- return replaceMixins(loopProcessedContent);
21740
- }
21741
-
21742
- const MAX_HEADER_LEVEL = 9;
21743
- const FUNCTION_CALL_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_]*\s*\(/;
21744
- const CONDITIONAL_PATTERN = /\?.*:/;
21745
- const remarkImports = (options) => {
21746
- const {
21747
- basePath = ".",
21748
- mergeMetadata = true,
21749
- debug = false,
21750
- maxDepth = 10,
21751
- timeoutMs = 3e4,
21752
- filterReserved = true,
21753
- validateTypes = true,
21754
- logImportOperations = false,
21755
- onMetadataMerged,
21756
- importStack = []
21757
- } = options;
21758
- return (tree) => {
21759
- const startTime = Date.now();
21760
21641
  if (debug) {
21761
- console.log("[remarkImports] Processing imports with options:", {
21762
- basePath,
21763
- mergeMetadata,
21764
- maxDepth,
21765
- timeoutMs,
21766
- filterReserved,
21767
- validateTypes,
21768
- currentDepth: importStack.length
21769
- });
21770
- }
21771
- const context = {
21772
- depth: importStack.length,
21773
- maxDepth,
21774
- basePath,
21775
- mergeMetadata,
21776
- debug,
21777
- startTime,
21778
- timeoutMs,
21779
- filterReserved,
21780
- validateTypes,
21781
- logImportOperations,
21782
- onMetadataMerged,
21783
- importStack: [...importStack],
21784
- contentCache: /* @__PURE__ */ new Map(),
21785
- importedMetadataList: [],
21786
- importedFiles: [],
21787
- accumulatedMetadata: {}
21788
- // Initialize with empty metadata
21789
- };
21790
- visit(tree, (node) => {
21791
- if (node.type === "text") {
21792
- processTextNode(node, context);
21793
- } else if (node.type === "paragraph") {
21794
- processParagraphNode(node, context);
21795
- }
21796
- });
21797
- if (context.mergeMetadata && context.importedMetadataList.length > 0) {
21798
- const mergedResult = performSequentialMerge(context);
21799
- tree._importedMetadata = mergedResult.metadata;
21800
- tree._importStats = mergedResult.stats;
21642
+ console.log(`✅ [remarkLegalHeadersParser] Converted ${convertedCount} legal headers`);
21801
21643
  }
21802
21644
  };
21803
21645
  };
21804
- function processTextNode(node, context) {
21805
- const originalText = node.value;
21806
- const importDirectives = extractImportDirectives(originalText);
21807
- if (importDirectives.length === 0) {
21808
- return;
21809
- }
21810
- if (context.debug) {
21811
- console.log(`[remarkImports] Found ${importDirectives.length} import directives in text node`);
21812
- }
21813
- let processedText = originalText;
21814
- for (let i = importDirectives.length - 1; i >= 0; i--) {
21815
- const directive = importDirectives[i];
21816
- const result = processImportDirective(directive, context);
21817
- processedText = processedText.substring(0, directive.start) + result + processedText.substring(directive.end);
21818
- }
21819
- node.value = processedText;
21820
- }
21821
- function processParagraphNode(node, context) {
21822
- node.children.forEach((child) => {
21823
- if (child.type === "text") {
21824
- processTextNode(child, context);
21825
- }
21826
- });
21827
- }
21828
- function extractImportDirectives(text) {
21829
- const directives = [];
21830
- const importRegex = /@import\s+([^\s#]+)(?:#([^\s]+))?/g;
21831
- let match;
21832
- while ((match = importRegex.exec(text)) !== null) {
21833
- const [fullMatch, filePath, section] = match;
21834
- directives.push({
21835
- filePath: filePath.trim(),
21836
- section: section?.trim(),
21837
- start: match.index,
21838
- end: match.index + fullMatch.length,
21839
- fullMatch
21840
- });
21841
- }
21842
- return directives;
21843
- }
21844
- function processImportDirective(directive, context) {
21845
- if (context.depth >= context.maxDepth) {
21846
- console.warn(
21847
- `[remarkImports] Maximum import depth (${context.maxDepth}) reached for file "${directive.filePath}"`
21848
- );
21849
- return directive.fullMatch;
21850
- }
21851
- const absolutePath = pathBrowserifyExports.resolve(context.basePath, directive.filePath);
21852
- const normalizedPath = pathBrowserifyExports.normalize(absolutePath);
21853
- if (context.importStack.includes(normalizedPath)) {
21854
- console.warn(`[remarkImports] Circular import detected: ${normalizedPath}`);
21855
- return directive.fullMatch;
21646
+ function parseMarkdownInlineFormatting(text) {
21647
+ const children = [];
21648
+ const patterns = [
21649
+ { regex: /\[([^\]]+)\]\([^)]+\)/g, type: "link" },
21650
+ // [text](url) - extract just the text
21651
+ { regex: /`([^`]+)`/g, type: "inlineCode" },
21652
+ // `code`
21653
+ { regex: /\*\*(.+?)\*\*/g, type: "strong" },
21654
+ // **bold**
21655
+ { regex: /__(.+?)__/g, type: "strong" },
21656
+ // __bold__
21657
+ { regex: /\*(.+?)\*/g, type: "emphasis" },
21658
+ // *italic*
21659
+ { regex: /_(.+?)_/g, type: "emphasis" }
21660
+ // _italic_
21661
+ ];
21662
+ const allMatches = [];
21663
+ for (const pattern of patterns) {
21664
+ let match;
21665
+ while ((match = pattern.regex.exec(text)) !== null) {
21666
+ allMatches.push({
21667
+ start: match.index,
21668
+ end: match.index + match[0].length,
21669
+ type: pattern.type,
21670
+ content: match[1]
21671
+ });
21672
+ }
21856
21673
  }
21857
- if (context.debug) {
21858
- console.log(
21859
- `[remarkImports] Processing import "${directive.filePath}" (resolved: ${normalizedPath})`
21860
- );
21674
+ allMatches.sort((a, b) => a.start - b.start);
21675
+ const validMatches = [];
21676
+ let lastEnd = 0;
21677
+ for (const match of allMatches) {
21678
+ if (match.start >= lastEnd) {
21679
+ validMatches.push(match);
21680
+ lastEnd = match.end;
21681
+ }
21861
21682
  }
21862
- const fileContent = loadFileContent(normalizedPath, context);
21863
- if (!fileContent) {
21864
- console.warn(`[remarkImports] Import file not found: ${directive.filePath}`);
21865
- return directive.fullMatch;
21683
+ let pos = 0;
21684
+ for (const match of validMatches) {
21685
+ if (pos < match.start) {
21686
+ const beforeText = text.slice(pos, match.start);
21687
+ if (beforeText) {
21688
+ children.push({
21689
+ type: "text",
21690
+ value: beforeText
21691
+ });
21692
+ }
21693
+ }
21694
+ if (match.type === "inlineCode") {
21695
+ children.push({
21696
+ type: "inlineCode",
21697
+ value: match.content
21698
+ });
21699
+ } else if (match.type === "link") {
21700
+ children.push({
21701
+ type: "text",
21702
+ value: match.content
21703
+ });
21704
+ } else {
21705
+ children.push({
21706
+ type: match.type,
21707
+ children: [
21708
+ {
21709
+ type: "text",
21710
+ value: match.content
21711
+ }
21712
+ ]
21713
+ });
21714
+ }
21715
+ pos = match.end;
21866
21716
  }
21867
- let contentToImport = fileContent;
21868
- if (context.mergeMetadata) {
21869
- const { content, metadata } = parseYamlFrontMatter(fileContent, false);
21870
- contentToImport = content;
21871
- if (Object.keys(metadata).length > 0) {
21872
- context.importedMetadataList.push({
21873
- metadata,
21874
- source: normalizedPath
21717
+ if (pos < text.length) {
21718
+ const remainingText = text.slice(pos);
21719
+ if (remainingText) {
21720
+ children.push({
21721
+ type: "text",
21722
+ value: remainingText
21875
21723
  });
21876
- context.accumulatedMetadata = {
21877
- ...context.accumulatedMetadata,
21878
- ...metadata
21879
- };
21880
- if (!context.importedFiles.includes(normalizedPath)) {
21881
- context.importedFiles.push(normalizedPath);
21882
- }
21883
- if (context.debug) {
21884
- console.log(
21885
- `[remarkImports] Collected metadata from ${directive.filePath}:`,
21886
- Object.keys(metadata)
21887
- );
21888
- }
21889
21724
  }
21890
21725
  }
21891
- if (directive.section) {
21892
- contentToImport = extractSection(contentToImport, directive.section, context.debug);
21726
+ if (children.length === 0) {
21727
+ return [
21728
+ {
21729
+ type: "text",
21730
+ value: text
21731
+ }
21732
+ ];
21893
21733
  }
21894
- const nestedContext = {
21895
- ...context,
21896
- depth: context.depth + 1,
21897
- importStack: [...context.importStack, normalizedPath],
21898
- basePath: pathBrowserifyExports.dirname(normalizedPath)
21899
- // Update base path for relative imports
21900
- };
21901
- return processNestedImports(contentToImport, nestedContext);
21734
+ return children;
21902
21735
  }
21903
- function performSequentialMerge(context) {
21904
- if (context.importedMetadataList.length === 0) {
21905
- return {
21906
- metadata: {},
21907
- stats: void 0
21908
- };
21909
- }
21910
- if (Date.now() - context.startTime > context.timeoutMs) {
21911
- throw new Error(
21912
- `Import processing timed out after ${context.timeoutMs}ms. This may indicate complex nested imports or slow file operations.`
21913
- );
21914
- }
21915
- const mergeOptions = {
21916
- filterReserved: context.filterReserved,
21917
- validateTypes: context.validateTypes,
21918
- logOperations: context.logImportOperations,
21919
- includeStats: true,
21920
- timeoutMs: Math.max(1e3, context.timeoutMs - (Date.now() - context.startTime))
21921
- };
21922
- const metadataList = context.importedMetadataList.map((item) => item.metadata);
21923
- if (context.debug) {
21924
- console.log(
21925
- `[remarkImports] Performing sequential merge of ${metadataList.length} metadata objects`
21926
- );
21736
+
21737
+ const EXTENDED_DATE_PATTERN = /@today((?:[+-]\d+[dmy]?(?:\[[^\]]+\])?)|(?:[+-]\d+[dmy]?)|(?:\[[^\]]+\]))?/g;
21738
+ function parseDateToken(token) {
21739
+ if (!token) return { arithmetic: null, format: null, isValid: true };
21740
+ if (!/^[+-]?\d*[dmy]?(\[[^\]]+\])?$/.test(token)) {
21741
+ return { arithmetic: null, format: null, isValid: false };
21927
21742
  }
21928
- try {
21929
- const result = mergeSequentially({}, metadataList, mergeOptions);
21930
- if (context.onMetadataMerged && Object.keys(result.metadata).length > 0) {
21931
- context.onMetadataMerged(result.metadata, "merged-imports");
21932
- }
21933
- return result;
21934
- } catch (error) {
21935
- if (context.debug) {
21936
- console.warn("[remarkImports] Sequential merge failed:", error);
21743
+ const formatMatch = token.match(/\[([^\]]+)\]/);
21744
+ const format = formatMatch ? formatMatch[1] : null;
21745
+ const arithmeticPart = token.replace(/\[([^\]]+)\]/, "");
21746
+ let arithmetic = null;
21747
+ if (arithmeticPart) {
21748
+ const arithmeticMatch = arithmeticPart.match(/^([+-])(\d+)([dmy]?)$/);
21749
+ if (arithmeticMatch) {
21750
+ const [, sign, amount, suffix] = arithmeticMatch;
21751
+ const numAmount = parseInt(amount) * (sign === "-" ? -1 : 1);
21752
+ switch (suffix) {
21753
+ case "d":
21754
+ case "":
21755
+ arithmetic = { type: "days", amount: numAmount };
21756
+ break;
21757
+ case "m":
21758
+ arithmetic = { type: "months", amount: numAmount };
21759
+ break;
21760
+ case "y":
21761
+ arithmetic = { type: "years", amount: numAmount };
21762
+ break;
21763
+ default:
21764
+ arithmetic = { type: "days", amount: numAmount };
21765
+ }
21766
+ } else if (arithmeticPart !== "") {
21767
+ return { arithmetic: null, format: null, isValid: false };
21937
21768
  }
21938
- throw error;
21939
21769
  }
21770
+ return { arithmetic, format, isValid: true };
21940
21771
  }
21941
- function loadFileContent(filePath, context) {
21942
- if (context.contentCache.has(filePath)) {
21943
- return context.contentCache.get(filePath);
21944
- }
21945
- try {
21946
- if (existsSync(filePath)) ;
21947
- } catch (error) {
21948
- if (context.debug) {
21949
- console.warn(`[remarkImports] Failed to load import file "${filePath}":`, error);
21950
- }
21772
+ function applyDateArithmetic(baseDate, arithmetic) {
21773
+ if (!arithmetic) return baseDate;
21774
+ switch (arithmetic.type) {
21775
+ case "days":
21776
+ return addDays(baseDate, arithmetic.amount);
21777
+ case "months":
21778
+ return addMonths(baseDate, arithmetic.amount);
21779
+ case "years":
21780
+ return addYears(baseDate, arithmetic.amount);
21781
+ default:
21782
+ return baseDate;
21951
21783
  }
21952
- return null;
21953
21784
  }
21954
- function extractSection(content, sectionName, debug) {
21955
- const lines = content.split("\n");
21956
- let sectionStart = -1;
21957
- let sectionEnd = lines.length;
21958
- let sectionLevel = 0;
21959
- for (let i = 0; i < lines.length; i++) {
21960
- const line = lines[i].trim();
21961
- const headerMatch = line.match(/^(#{1,6})\s+(.+)$/);
21962
- if (headerMatch) {
21963
- const [, hashes, title] = headerMatch;
21964
- const level = hashes.length;
21965
- if (title.toLowerCase() === sectionName.toLowerCase()) {
21966
- sectionStart = i + 1;
21967
- sectionLevel = level;
21968
- if (debug) {
21969
- console.log(`[remarkImports] Found section '${sectionName}' at line ${i}`);
21970
- }
21971
- break;
21972
- }
21973
- }
21785
+ function getOrdinalSuffix(day) {
21786
+ if (day >= 11 && day <= 13) {
21787
+ return "th";
21974
21788
  }
21975
- if (sectionStart === -1) {
21976
- if (debug) {
21977
- console.warn(`[remarkImports] Section '${sectionName}' not found`);
21978
- }
21979
- return content;
21789
+ switch (day % 10) {
21790
+ case 1:
21791
+ return "st";
21792
+ case 2:
21793
+ return "nd";
21794
+ case 3:
21795
+ return "rd";
21796
+ default:
21797
+ return "th";
21980
21798
  }
21981
- for (let i = sectionStart; i < lines.length; i++) {
21982
- const line = lines[i].trim();
21983
- const headerMatch = line.match(/^(#{1,6})\s+(.+)$/);
21984
- if (headerMatch) {
21985
- const [, hashes] = headerMatch;
21986
- const level = hashes.length;
21987
- if (level <= sectionLevel) {
21988
- sectionEnd = i;
21989
- break;
21990
- }
21799
+ }
21800
+ function formatDateBasic(date, format) {
21801
+ const year = date.getFullYear();
21802
+ const month = String(date.getMonth() + 1).padStart(2, "0");
21803
+ const day = String(date.getDate()).padStart(2, "0");
21804
+ const dayNumber = date.getDate();
21805
+ const monthNames = [
21806
+ "January",
21807
+ "February",
21808
+ "March",
21809
+ "April",
21810
+ "May",
21811
+ "June",
21812
+ "July",
21813
+ "August",
21814
+ "September",
21815
+ "October",
21816
+ "November",
21817
+ "December"
21818
+ ];
21819
+ switch (format.toLowerCase()) {
21820
+ case "iso":
21821
+ case "yyyy-mm-dd":
21822
+ return `${year}-${month}-${day}`;
21823
+ case "us":
21824
+ case "mm/dd/yyyy":
21825
+ return `${month}/${day}/${year}`;
21826
+ case "eu":
21827
+ case "european":
21828
+ case "dd/mm/yyyy":
21829
+ return `${day}/${month}/${year}`;
21830
+ case "long":
21831
+ return `${monthNames[date.getMonth()]} ${dayNumber}, ${year}`;
21832
+ case "medium":
21833
+ return `${monthNames[date.getMonth()].substring(0, 3)} ${dayNumber}, ${year}`;
21834
+ case "short":
21835
+ return `${monthNames[date.getMonth()].substring(0, 3)} ${dayNumber}, ${String(year).substring(2)}`;
21836
+ case "legal": {
21837
+ const suffix = getOrdinalSuffix(dayNumber);
21838
+ return `${monthNames[date.getMonth()]} ${dayNumber}${suffix}, ${year}`;
21991
21839
  }
21840
+ default:
21841
+ return `${year}-${month}-${day}`;
21992
21842
  }
21993
- return lines.slice(sectionStart, sectionEnd).join("\n").trim();
21994
- }
21995
- function expandMixinsWithTracking(content, metadata) {
21996
- const mixinPattern = /\{\{([^}]+)\}\}/g;
21997
- return content.replace(mixinPattern, (match, fieldExpression) => {
21998
- const trimmedField = fieldExpression.trim();
21999
- let value;
22000
- let hasValue = false;
22001
- try {
22002
- const expandedValue = processMixins(match, metadata, {});
22003
- if (expandedValue !== match) {
22004
- value = expandedValue;
22005
- hasValue = true;
22006
- }
22007
- } catch (error) {
22008
- }
22009
- if (hasValue) {
22010
- const hasLogic = FUNCTION_CALL_PATTERN.test(trimmedField) || // Function call: formatPercent(...)
22011
- CONDITIONAL_PATTERN.test(trimmedField);
22012
- const cssClass = hasLogic ? "legal-field highlight" : "legal-field imported-value";
22013
- return `<span class="${cssClass}" data-field="${trimmedField}">${value}</span>`;
22014
- } else {
22015
- return match;
22016
- }
22017
- });
22018
21843
  }
22019
- function stripHtmlComments(content) {
22020
- let processed = content;
22021
- processed = processed.replace(/<!--[\s\S]*?-->/g, "");
22022
- const headerPattern = `l{1,${MAX_HEADER_LEVEL}}`;
22023
- processed = processed.replace(
22024
- new RegExp(
22025
- `<(p|div|span)(?!\\s+[a-z])\\s*>\\s*(${headerPattern}\\.\\s+[^\\n]+)\\s*<\\/\\1>`,
22026
- "gi"
22027
- ),
22028
- "$2"
22029
- );
22030
- processed = processed.replace(
22031
- new RegExp(`<(p|div|span)(?!\\s+[a-z])\\s*>(${headerPattern}\\.\\s+[^<]+)<\\/\\1>`, "gi"),
22032
- "$2"
22033
- );
22034
- return processed;
21844
+ function getDateFieldCssClass(hasArithmetic) {
21845
+ return hasArithmetic ? "legal-field highlight" : "legal-field imported-value";
22035
21846
  }
22036
- function processNestedImports(content, context) {
22037
- const importDirectives = extractImportDirectives(content);
22038
- let processedContent = content;
22039
- if (importDirectives.length > 0) {
22040
- for (let i = importDirectives.length - 1; i >= 0; i--) {
22041
- const directive = importDirectives[i];
22042
- const result = processImportDirective(directive, context);
22043
- processedContent = processedContent.substring(0, directive.start) + result + processedContent.substring(directive.end);
22044
- }
22045
- }
22046
- if (context.mergeMetadata && Object.keys(context.accumulatedMetadata).length > 0) {
22047
- processedContent = expandMixinsWithTracking(processedContent, context.accumulatedMetadata);
21847
+ function formatDateValue(value, originalToken, enableFieldTracking = false, hasArithmetic = false) {
21848
+ if (!enableFieldTracking) {
21849
+ return value;
22048
21850
  }
22049
- return stripHtmlComments(processedContent);
22050
- }
22051
-
22052
- const LEGAL_HEADER_PATTERN = /^(l{1,9})\.\s+(.+)$/;
22053
- function getHeadingLevel(pattern) {
22054
- return pattern.length;
21851
+ const cssClass = getDateFieldCssClass(hasArithmetic);
21852
+ const fieldName = `date.${originalToken.replace(/[@[\]]/g, "")}`;
21853
+ return `<span class="${cssClass}" data-field="${fieldName.replace(/"/g, "&quot;")}">${value}</span>`;
22055
21854
  }
22056
- function isLegalHeader(node) {
22057
- if (node.children.length === 1 && node.children[0].type === "text") {
22058
- const textNode = node.children[0];
22059
- const match = textNode.value.match(LEGAL_HEADER_PATTERN);
22060
- if (match) {
22061
- const [, levelPattern, headerText] = match;
22062
- return {
22063
- level: getHeadingLevel(levelPattern),
22064
- text: headerText.trim()
22065
- };
21855
+ function processDateReferencesInAST(root, metadata, enableFieldTracking = false) {
21856
+ visit(root, "text", (node, index, parent) => {
21857
+ const originalValue = node.value;
21858
+ let modifiedValue = originalValue;
21859
+ let hasChanges = false;
21860
+ modifiedValue = modifiedValue.replace(EXTENDED_DATE_PATTERN, (match, token) => {
21861
+ try {
21862
+ const { arithmetic, format: tokenFormat, isValid } = parseDateToken(token);
21863
+ if (!isValid) {
21864
+ return match;
21865
+ }
21866
+ let date = /* @__PURE__ */ new Date();
21867
+ const hasArithmetic = !!arithmetic;
21868
+ if (arithmetic) {
21869
+ date = applyDateArithmetic(date, arithmetic);
21870
+ }
21871
+ const format = tokenFormat || metadata["date-format"] || "YYYY-MM-DD";
21872
+ const formattedDate = formatDateBasic(date, format);
21873
+ fieldTracker.trackField(`date.${match.replace(/[@[\]]/g, "")}`, {
21874
+ value: formattedDate,
21875
+ originalValue: match,
21876
+ hasLogic: hasArithmetic
21877
+ });
21878
+ hasChanges = true;
21879
+ return formatDateValue(formattedDate, match, enableFieldTracking, hasArithmetic);
21880
+ } catch (error) {
21881
+ console.warn(`Error processing date reference ${match}:`, error);
21882
+ return match;
21883
+ }
21884
+ });
21885
+ if (hasChanges) {
21886
+ if (enableFieldTracking && modifiedValue.includes("<span")) {
21887
+ if (parent && typeof index === "number") {
21888
+ const htmlNode = {
21889
+ type: "html",
21890
+ value: modifiedValue
21891
+ };
21892
+ parent.children[index] = htmlNode;
21893
+ }
21894
+ } else {
21895
+ node.value = modifiedValue;
21896
+ }
22066
21897
  }
22067
- }
22068
- if (node.children.length > 0 && node.children[0].type === "text") {
22069
- const firstChild = node.children[0];
22070
- const match = firstChild.value.match(/^(l{1,5})\.\s*/);
22071
- if (match) {
22072
- const [fullMatch, levelPattern] = match;
22073
- const remainingText = firstChild.value.slice(fullMatch.length);
22074
- const otherText = node.children.slice(1).map((child) => {
22075
- if (child.type === "text") return child.value;
22076
- if (child.type === "html") return child.value || "";
22077
- return "";
22078
- }).join("");
22079
- return {
22080
- level: getHeadingLevel(levelPattern),
22081
- text: (remainingText + otherText).trim()
22082
- };
21898
+ });
21899
+ visit(root, "html", (node) => {
21900
+ if (!node.value || !node.value.includes("@today")) {
21901
+ return;
22083
21902
  }
22084
- }
22085
- return null;
22086
- }
22087
- function convertToHeading(node, level, text) {
22088
- const heading = {
22089
- type: "heading",
22090
- depth: level,
22091
- children: parseMarkdownInlineFormatting(text)
22092
- };
22093
- heading.data = { isLegalHeader: true };
22094
- if (node.children.length > 1 || node.children[0].type === "text" && node.children[0].value.includes("<span")) {
22095
- const firstChild = node.children[0];
22096
- if (firstChild.type === "text") {
22097
- const match = firstChild.value.match(/^(l{1,5})\.\s*/);
22098
- if (match) {
22099
- const [fullMatch] = match;
22100
- firstChild.value = firstChild.value.slice(fullMatch.length);
22101
- heading.children = node.children.filter((child) => {
22102
- if (child.type === "text" && child.value.trim() === "") {
22103
- return false;
21903
+ let modifiedValue = node.value;
21904
+ let hasChanges = false;
21905
+ modifiedValue = modifiedValue.replace(
21906
+ EXTENDED_DATE_PATTERN,
21907
+ (match, formatOverride) => {
21908
+ try {
21909
+ const date = /* @__PURE__ */ new Date();
21910
+ const format = formatOverride || metadata["date-format"] || "YYYY-MM-DD";
21911
+ const formattedDate = formatDateBasic(date, format);
21912
+ fieldTracker.trackField(`date.${match.replace(/[@[\]]/g, "")}`, {
21913
+ value: formattedDate,
21914
+ originalValue: match,
21915
+ hasLogic: false
21916
+ });
21917
+ hasChanges = true;
21918
+ if (enableFieldTracking) {
21919
+ const cssClass = getDateFieldCssClass(false);
21920
+ const fieldName = `date.${match.replace(/[@[\]]/g, "")}`;
21921
+ return `<span class="${cssClass}" data-field="${fieldName.replace(/"/g, "&quot;")}">${formattedDate}</span>`;
21922
+ } else {
21923
+ return formattedDate;
22104
21924
  }
22105
- return true;
22106
- });
21925
+ } catch (error) {
21926
+ console.warn(`Error processing date reference ${match}:`, error);
21927
+ return match;
21928
+ }
22107
21929
  }
21930
+ );
21931
+ if (hasChanges) {
21932
+ node.value = modifiedValue;
22108
21933
  }
22109
- }
22110
- return heading;
21934
+ });
22111
21935
  }
22112
- const remarkLegalHeadersParser = (options = {}) => {
22113
- const { debug = false } = options;
21936
+ const remarkDates = (options) => {
21937
+ const { metadata, debug = false, enableFieldTracking = false } = options;
22114
21938
  return (tree) => {
22115
21939
  if (debug) {
22116
- console.log("🔍 [remarkLegalHeadersParser] Parsing legal header syntax");
21940
+ console.log("📅 Processing date references with remark plugin");
22117
21941
  }
22118
- let convertedCount = 0;
22119
- const nodesToReplace = [];
22120
- visit(tree, "paragraph", (node, index, parent) => {
22121
- if (parent && typeof index === "number") {
22122
- if (node.children.length === 1 && node.children[0].type === "text") {
22123
- const textNode = node.children[0];
22124
- const lines = textNode.value.split("\n");
22125
- const hasLegalHeaders = lines.some((line) => LEGAL_HEADER_PATTERN.test(line));
22126
- if (hasLegalHeaders) {
22127
- const newNodes = [];
22128
- const nonHeaderLines = [];
22129
- for (const line of lines) {
22130
- const match = line.match(LEGAL_HEADER_PATTERN);
22131
- if (match) {
22132
- if (nonHeaderLines.length > 0) {
22133
- newNodes.push({
22134
- type: "paragraph",
22135
- children: [
22136
- {
22137
- type: "text",
22138
- value: nonHeaderLines.join("\n")
22139
- }
22140
- ]
22141
- });
22142
- nonHeaderLines.length = 0;
22143
- }
22144
- const [, levelPattern, headerText] = match;
22145
- const level = getHeadingLevel(levelPattern);
22146
- if (debug) {
22147
- console.log(
22148
- `🔄 [remarkLegalHeadersParser] Converting "${line.substring(0, 50)}..." to level ${level} heading`
22149
- );
22150
- }
22151
- const headingNode = {
22152
- type: "heading",
22153
- depth: level,
22154
- children: parseMarkdownInlineFormatting(headerText.trim())
22155
- };
22156
- headingNode.data = { isLegalHeader: true };
22157
- newNodes.push(headingNode);
22158
- convertedCount++;
22159
- } else {
22160
- nonHeaderLines.push(line);
22161
- }
22162
- }
22163
- if (nonHeaderLines.length > 0) {
22164
- newNodes.push({
22165
- type: "paragraph",
22166
- children: [
22167
- {
22168
- type: "text",
22169
- value: nonHeaderLines.join("\n")
22170
- }
22171
- ]
22172
- });
22173
- }
22174
- if (newNodes.length > 0) {
22175
- nodesToReplace.push({ parent, index, newNodes });
22176
- }
21942
+ processDateReferencesInAST(tree, metadata, enableFieldTracking);
21943
+ if (debug) {
21944
+ console.log(" Date reference processing completed");
21945
+ }
21946
+ };
21947
+ };
21948
+
21949
+ class PluginOrderValidator {
21950
+ registry;
21951
+ constructor(registry) {
21952
+ this.registry = registry;
21953
+ }
21954
+ /**
21955
+ * Validate plugin execution order
21956
+ *
21957
+ * @param pluginNames - List of plugin names in execution order
21958
+ * @param options - Validation options
21959
+ * @returns Validation result with errors and warnings
21960
+ */
21961
+ validate(pluginNames, options = {}) {
21962
+ const { throwOnError = true, logWarnings = true, strictMode = false, debug = false } = options;
21963
+ const errors = [];
21964
+ const warnings = [];
21965
+ if (debug) {
21966
+ console.log("[PluginOrderValidator] Validating plugin order:", pluginNames);
21967
+ }
21968
+ const positions = /* @__PURE__ */ new Map();
21969
+ pluginNames.forEach((name, index) => {
21970
+ positions.set(name, index);
21971
+ });
21972
+ for (let i = 0; i < pluginNames.length; i++) {
21973
+ const pluginName = pluginNames[i];
21974
+ const metadata = this.registry.get(pluginName);
21975
+ if (!metadata) {
21976
+ if (strictMode) {
21977
+ warnings.push({
21978
+ type: "unknown-plugin",
21979
+ plugin: pluginName,
21980
+ message: `Plugin "${pluginName}" has no metadata declaration`
21981
+ });
21982
+ }
21983
+ continue;
21984
+ }
21985
+ if (debug) {
21986
+ console.log(`[PluginOrderValidator] Checking constraints for ${pluginName}`);
21987
+ }
21988
+ if (metadata.runBefore) {
21989
+ for (const beforePlugin of metadata.runBefore) {
21990
+ const beforePos = positions.get(beforePlugin);
21991
+ if (beforePos !== void 0 && beforePos <= i) {
21992
+ errors.push({
21993
+ type: "dependency-violation",
21994
+ plugin: pluginName,
21995
+ relatedPlugin: beforePlugin,
21996
+ message: `"${pluginName}" must run BEFORE "${beforePlugin}", but it appears after it in the pipeline`
21997
+ });
22177
21998
  }
22178
- } else if (node.children.length > 1) {
22179
- let fullText = "";
22180
- const childrenMap = /* @__PURE__ */ new Map();
22181
- for (let i = 0; i < node.children.length; i++) {
22182
- const child = node.children[i];
22183
- const start = fullText.length;
22184
- if (child.type === "text") {
22185
- fullText += child.value;
22186
- } else if (child.type === "emphasis") {
22187
- const emphasisText = child.children.map((c) => c.value || "").join("");
22188
- fullText += `_${emphasisText}_`;
22189
- } else if (child.type === "strong") {
22190
- const strongText = child.children.map((c) => c.value || "").join("");
22191
- fullText += `__${strongText}__`;
22192
- } else if (child.type === "link") {
22193
- const linkText = child.children.map((c) => c.value || "").join("");
22194
- const url = child.url || "";
22195
- fullText += `[${linkText}](${url})`;
22196
- } else if (child.type === "inlineCode") {
22197
- fullText += `\`${child.value || ""}\``;
22198
- } else {
22199
- fullText += child.value || "";
22200
- }
22201
- const end = fullText.length;
22202
- childrenMap.set(i, { start, end, child });
21999
+ }
22000
+ }
22001
+ if (metadata.runAfter) {
22002
+ for (const afterPlugin of metadata.runAfter) {
22003
+ const afterPos = positions.get(afterPlugin);
22004
+ if (afterPos !== void 0 && afterPos >= i) {
22005
+ errors.push({
22006
+ type: "dependency-violation",
22007
+ plugin: pluginName,
22008
+ relatedPlugin: afterPlugin,
22009
+ message: `"${pluginName}" must run AFTER "${afterPlugin}", but it appears before it in the pipeline`
22010
+ });
22203
22011
  }
22204
- const lines = fullText.split("\n");
22205
- const hasLegalHeaders = lines.some((line) => LEGAL_HEADER_PATTERN.test(line));
22206
- if (hasLegalHeaders) {
22207
- const newNodes = [];
22208
- const nonHeaderLines = [];
22209
- for (const line of lines) {
22210
- const match = line.match(LEGAL_HEADER_PATTERN);
22211
- if (match) {
22212
- if (nonHeaderLines.length > 0) {
22213
- newNodes.push({
22214
- type: "paragraph",
22215
- children: [
22216
- {
22217
- type: "text",
22218
- value: nonHeaderLines.join("\n")
22219
- }
22220
- ]
22221
- });
22222
- nonHeaderLines.length = 0;
22223
- }
22224
- const [, levelPattern, headerText] = match;
22225
- const level = getHeadingLevel(levelPattern);
22226
- if (debug) {
22227
- console.log(
22228
- `🔄 [remarkLegalHeadersParser] Converting complex "${line.substring(0, 50)}..." to level ${level} heading`
22229
- );
22230
- }
22231
- const headingNode = {
22232
- type: "heading",
22233
- depth: level,
22234
- children: parseMarkdownInlineFormatting(headerText.trim())
22235
- };
22236
- headingNode.data = { isLegalHeader: true };
22237
- newNodes.push(headingNode);
22238
- convertedCount++;
22239
- } else {
22240
- nonHeaderLines.push(line);
22012
+ }
22013
+ }
22014
+ if (metadata.conflicts) {
22015
+ for (const conflictPlugin of metadata.conflicts) {
22016
+ if (positions.has(conflictPlugin)) {
22017
+ errors.push({
22018
+ type: "conflict",
22019
+ plugin: pluginName,
22020
+ relatedPlugin: conflictPlugin,
22021
+ message: `"${pluginName}" conflicts with "${conflictPlugin}" - they cannot be used together`
22022
+ });
22023
+ }
22024
+ }
22025
+ }
22026
+ }
22027
+ for (const [name, metadata] of this.registry.entries()) {
22028
+ if (metadata.required && !positions.has(name)) {
22029
+ warnings.push({
22030
+ type: "missing-required",
22031
+ plugin: name,
22032
+ message: `Required plugin "${name}" is missing from the pipeline`
22033
+ });
22034
+ }
22035
+ }
22036
+ const circularErrors = this.detectCircularDependencies(pluginNames);
22037
+ errors.push(...circularErrors);
22038
+ if (logWarnings && warnings.length > 0) {
22039
+ for (const warning of warnings) {
22040
+ console.warn(`[PluginOrderValidator] WARNING: ${warning.message}`);
22041
+ }
22042
+ }
22043
+ const valid = errors.length === 0;
22044
+ let suggestedOrder;
22045
+ if (!valid) {
22046
+ try {
22047
+ suggestedOrder = this.topologicalSort(pluginNames);
22048
+ if (debug) {
22049
+ console.log("[PluginOrderValidator] Suggested order:", suggestedOrder);
22050
+ }
22051
+ } catch (error) {
22052
+ if (debug) {
22053
+ console.error("[PluginOrderValidator] Failed to generate suggested order:", error);
22054
+ }
22055
+ }
22056
+ }
22057
+ const result = {
22058
+ valid,
22059
+ errors,
22060
+ warnings,
22061
+ suggestedOrder
22062
+ };
22063
+ if (!valid && throwOnError) {
22064
+ const errorMessages = errors.map((e) => e.message).join("\n - ");
22065
+ throw new Error(
22066
+ `Plugin order validation failed:
22067
+ - ${errorMessages}${suggestedOrder ? `
22068
+
22069
+ Suggested order: ${suggestedOrder.join(" → ")}` : ""}`
22070
+ );
22071
+ }
22072
+ return result;
22073
+ }
22074
+ /**
22075
+ * Detect circular dependencies in plugin metadata
22076
+ *
22077
+ * @param pluginNames - List of plugin names
22078
+ * @returns List of errors for circular dependencies found
22079
+ */
22080
+ detectCircularDependencies(pluginNames) {
22081
+ const errors = [];
22082
+ const visited = /* @__PURE__ */ new Set();
22083
+ const recursionStack = /* @__PURE__ */ new Set();
22084
+ const hasCycle = (plugin, path) => {
22085
+ if (recursionStack.has(plugin)) {
22086
+ const cyclePath = [...path, plugin].join(" → ");
22087
+ errors.push({
22088
+ type: "circular-dependency",
22089
+ plugin,
22090
+ message: `Circular dependency detected: ${cyclePath}`
22091
+ });
22092
+ return true;
22093
+ }
22094
+ if (visited.has(plugin)) {
22095
+ return false;
22096
+ }
22097
+ visited.add(plugin);
22098
+ recursionStack.add(plugin);
22099
+ const metadata = this.registry.get(plugin);
22100
+ if (metadata) {
22101
+ if (metadata.runAfter) {
22102
+ for (const dep of metadata.runAfter) {
22103
+ if (pluginNames.includes(dep)) {
22104
+ if (hasCycle(dep, [...path, plugin])) {
22105
+ recursionStack.delete(plugin);
22106
+ return true;
22241
22107
  }
22242
22108
  }
22243
- if (nonHeaderLines.length > 0) {
22244
- newNodes.push({
22245
- type: "paragraph",
22246
- children: [
22247
- {
22248
- type: "text",
22249
- value: nonHeaderLines.join("\n")
22250
- }
22251
- ]
22252
- });
22253
- }
22254
- if (newNodes.length > 0) {
22255
- nodesToReplace.push({ parent, index, newNodes });
22256
- }
22257
22109
  }
22258
22110
  }
22259
- } else {
22260
- if (debug) {
22261
- const firstChildText = node.children[0]?.type === "text" ? node.children[0].value : "<non-text>";
22262
- console.log(`🔍 [remarkLegalHeadersParser] Single-line paragraph: "${firstChildText}"`);
22111
+ }
22112
+ recursionStack.delete(plugin);
22113
+ return false;
22114
+ };
22115
+ for (const plugin of pluginNames) {
22116
+ if (!visited.has(plugin)) {
22117
+ hasCycle(plugin, []);
22118
+ }
22119
+ }
22120
+ return errors;
22121
+ }
22122
+ /**
22123
+ * Perform topological sort on plugins to find a valid execution order
22124
+ *
22125
+ * Uses Kahn's algorithm for topological sorting.
22126
+ *
22127
+ * @param pluginNames - List of plugin names to sort
22128
+ * @returns Sorted list of plugin names
22129
+ * @throws Error if plugins have circular dependencies
22130
+ */
22131
+ topologicalSort(pluginNames) {
22132
+ const adjList = /* @__PURE__ */ new Map();
22133
+ const inDegree = /* @__PURE__ */ new Map();
22134
+ for (const plugin of pluginNames) {
22135
+ adjList.set(plugin, /* @__PURE__ */ new Set());
22136
+ inDegree.set(plugin, 0);
22137
+ }
22138
+ for (const plugin of pluginNames) {
22139
+ const metadata = this.registry.get(plugin);
22140
+ if (!metadata) continue;
22141
+ if (metadata.runBefore) {
22142
+ for (const beforePlugin of metadata.runBefore) {
22143
+ if (pluginNames.includes(beforePlugin)) {
22144
+ adjList.get(plugin).add(beforePlugin);
22145
+ inDegree.set(beforePlugin, inDegree.get(beforePlugin) + 1);
22146
+ }
22263
22147
  }
22264
- const headerInfo = isLegalHeader(node);
22265
- if (headerInfo && parent && typeof index === "number") {
22266
- if (debug) {
22267
- const firstChildText = node.children[0].type === "text" ? node.children[0].value : "";
22268
- console.log(
22269
- `🔄 [remarkLegalHeadersParser] Converting "${firstChildText.substring(0, 50)}..." to level ${headerInfo.level} heading`
22270
- );
22148
+ }
22149
+ if (metadata.runAfter) {
22150
+ for (const afterPlugin of metadata.runAfter) {
22151
+ if (pluginNames.includes(afterPlugin)) {
22152
+ adjList.get(afterPlugin).add(plugin);
22153
+ inDegree.set(plugin, inDegree.get(plugin) + 1);
22271
22154
  }
22272
- const heading = convertToHeading(node, headerInfo.level, headerInfo.text);
22273
- nodesToReplace.push({ parent, index, newNodes: [heading] });
22274
- convertedCount++;
22275
22155
  }
22276
22156
  }
22277
- });
22278
- for (let i = nodesToReplace.length - 1; i >= 0; i--) {
22279
- const { parent, index, newNodes } = nodesToReplace[i];
22280
- parent.children.splice(index, 1, ...newNodes);
22281
22157
  }
22282
- if (debug) {
22283
- console.log(`✅ [remarkLegalHeadersParser] Converted ${convertedCount} legal headers`);
22158
+ const queue = [];
22159
+ const result = [];
22160
+ for (const [plugin, degree] of inDegree.entries()) {
22161
+ if (degree === 0) {
22162
+ queue.push(plugin);
22163
+ }
22284
22164
  }
22285
- };
22165
+ while (queue.length > 0) {
22166
+ const current = queue.shift();
22167
+ result.push(current);
22168
+ for (const neighbor of adjList.get(current)) {
22169
+ const newDegree = inDegree.get(neighbor) - 1;
22170
+ inDegree.set(neighbor, newDegree);
22171
+ if (newDegree === 0) {
22172
+ queue.push(neighbor);
22173
+ }
22174
+ }
22175
+ }
22176
+ if (result.length !== pluginNames.length) {
22177
+ throw new Error("Cannot generate topological sort: circular dependencies detected");
22178
+ }
22179
+ return result;
22180
+ }
22181
+ /**
22182
+ * Get metadata for a plugin
22183
+ *
22184
+ * @param pluginName - Name of the plugin
22185
+ * @returns Plugin metadata or undefined if not found
22186
+ */
22187
+ getMetadata(pluginName) {
22188
+ return this.registry.get(pluginName);
22189
+ }
22190
+ /**
22191
+ * Add or update metadata for a plugin
22192
+ *
22193
+ * @param metadata - Plugin metadata to add/update
22194
+ */
22195
+ registerPlugin(metadata) {
22196
+ this.registry.set(metadata.name, metadata);
22197
+ }
22198
+ }
22199
+ function createPluginRegistry(metadataList) {
22200
+ const registry = /* @__PURE__ */ new Map();
22201
+ for (const metadata of metadataList) {
22202
+ registry.set(metadata.name, metadata);
22203
+ }
22204
+ return registry;
22205
+ }
22206
+
22207
+ const PLUGIN_METADATA_LIST = [
22208
+ // Phase 1: Import Processing
22209
+ {
22210
+ name: "remarkImports",
22211
+ description: "Process @import directives and insert content as AST nodes",
22212
+ runBefore: [
22213
+ "remarkTemplateFields",
22214
+ "remarkLegalHeadersParser",
22215
+ "remarkFieldTracking",
22216
+ "remarkMixins"
22217
+ ],
22218
+ required: false,
22219
+ version: "2.0.0"
22220
+ // Version 2.0 uses AST-based insertion
22221
+ },
22222
+ // Phase 2: Mixin and Field Processing
22223
+ {
22224
+ name: "remarkMixins",
22225
+ description: "Process mixin definitions and expansions",
22226
+ runAfter: ["remarkImports"],
22227
+ runBefore: ["remarkTemplateFields"],
22228
+ required: false
22229
+ },
22230
+ {
22231
+ name: "remarkTemplateFields",
22232
+ description: "Process {{field}} template patterns and resolve values",
22233
+ runAfter: ["remarkImports", "remarkMixins"],
22234
+ runBefore: ["remarkFieldTracking"],
22235
+ required: true
22236
+ },
22237
+ // Phase 3: Header Processing
22238
+ {
22239
+ name: "remarkLegalHeadersParser",
22240
+ description: "Parse legal header markers (l., ll., lll.) into structured headers",
22241
+ runAfter: ["remarkImports"],
22242
+ runBefore: ["remarkHeaders"],
22243
+ required: true
22244
+ },
22245
+ {
22246
+ name: "remarkHeaders",
22247
+ description: "Process headers and add CSS classes (legal-header-level-X)",
22248
+ runAfter: ["remarkLegalHeadersParser"],
22249
+ required: true
22250
+ },
22251
+ // Phase 4: Field Tracking
22252
+ {
22253
+ name: "remarkFieldTracking",
22254
+ description: "Track template fields for highlighting and analysis",
22255
+ runAfter: ["remarkTemplateFields"],
22256
+ required: false
22257
+ },
22258
+ // Phase 5: Cross-References and Clauses
22259
+ {
22260
+ name: "remarkCrossReferences",
22261
+ description: "Process cross-references between document sections",
22262
+ // Must run BEFORE remarkHeaders to extract |key| patterns before headers removes them
22263
+ runBefore: ["remarkHeaders"],
22264
+ runAfter: ["remarkLegalHeadersParser"],
22265
+ // After headers are parsed, before they're processed
22266
+ required: false
22267
+ },
22268
+ {
22269
+ name: "remarkCrossReferencesAst",
22270
+ description: "AST-based cross-reference processing (alternative to remarkCrossReferences)",
22271
+ runAfter: ["remarkHeaders", "remarkLegalHeadersParser"],
22272
+ conflicts: ["remarkCrossReferences"],
22273
+ // Can't use both
22274
+ required: false
22275
+ },
22276
+ {
22277
+ name: "remarkClauses",
22278
+ description: "Process conditional clauses ({{#if}}, {{#unless}})",
22279
+ // Note: Can run before or after remarkTemplateFields depending on use case
22280
+ required: false
22281
+ },
22282
+ // Phase 6: Date Processing
22283
+ {
22284
+ name: "remarkDates",
22285
+ description: "Process date fields and formatting",
22286
+ // Note: Can run before or after remarkTemplateFields depending on use case
22287
+ required: false
22288
+ },
22289
+ // Phase 7: Signature Lines
22290
+ {
22291
+ name: "remarkSignatureLines",
22292
+ description: "Process signature line markers",
22293
+ required: false
22294
+ },
22295
+ // Utility Plugins
22296
+ {
22297
+ name: "remarkDebugAst",
22298
+ description: "Debug plugin for visualizing AST structure",
22299
+ required: false
22300
+ }
22301
+ ];
22302
+ const GLOBAL_PLUGIN_REGISTRY = createPluginRegistry(PLUGIN_METADATA_LIST);
22303
+
22304
+ const DEFAULT_OPTIONS = {
22305
+ minUnderscores: 10,
22306
+ addCssClass: true,
22307
+ cssClassName: "signature-line",
22308
+ debug: false
22286
22309
  };
22287
- function parseMarkdownInlineFormatting(text) {
22288
- const children = [];
22289
- const patterns = [
22290
- { regex: /\[([^\]]+)\]\([^)]+\)/g, type: "link" },
22291
- // [text](url) - extract just the text
22292
- { regex: /`([^`]+)`/g, type: "inlineCode" },
22293
- // `code`
22294
- { regex: /\*\*(.+?)\*\*/g, type: "strong" },
22295
- // **bold**
22296
- { regex: /__(.+?)__/g, type: "strong" },
22297
- // __bold__
22298
- { regex: /\*(.+?)\*/g, type: "emphasis" },
22299
- // *italic*
22300
- { regex: /_(.+?)_/g, type: "emphasis" }
22301
- // _italic_
22302
- ];
22303
- const allMatches = [];
22304
- for (const pattern of patterns) {
22305
- let match;
22306
- while ((match = pattern.regex.exec(text)) !== null) {
22307
- allMatches.push({
22308
- start: match.index,
22309
- end: match.index + match[0].length,
22310
- type: pattern.type,
22311
- content: match[1]
22310
+ function sanitizeCssClassName(className) {
22311
+ const validPattern = /^[a-zA-Z_][\w-]*$/;
22312
+ if (!className || !validPattern.test(className)) {
22313
+ console.warn(
22314
+ `[signature-lines] Invalid CSS class name "${className}", using default "signature-line"`
22315
+ );
22316
+ return "signature-line";
22317
+ }
22318
+ return className;
22319
+ }
22320
+ const remarkSignatureLines = (options = {}) => {
22321
+ const config = { ...DEFAULT_OPTIONS, ...options };
22322
+ const safeCssClassName = sanitizeCssClassName(config.cssClassName);
22323
+ return (tree) => {
22324
+ if (config.debug) {
22325
+ console.log("[signature-lines] Processing tree...");
22326
+ }
22327
+ visit(tree, "text", (node, index, parent) => {
22328
+ if (index === void 0 || !parent) return;
22329
+ const text = node.value;
22330
+ const underscorePattern = new RegExp(`_{${config.minUnderscores},}`, "g");
22331
+ if (!underscorePattern.test(text)) {
22332
+ return;
22333
+ }
22334
+ if (config.debug) {
22335
+ console.log("[signature-lines] Found signature line in text:", text);
22336
+ }
22337
+ if (!config.addCssClass) {
22338
+ return;
22339
+ }
22340
+ const processedText = text.replace(underscorePattern, (match) => {
22341
+ if (config.debug) {
22342
+ console.log(`[signature-lines] Wrapping ${match.length} underscores`);
22343
+ }
22344
+ return `<span class="${safeCssClassName}">${match}</span>`;
22312
22345
  });
22346
+ const htmlNode = {
22347
+ type: "html",
22348
+ value: processedText
22349
+ };
22350
+ parent.children[index] = htmlNode;
22351
+ });
22352
+ if (config.debug) {
22353
+ console.log("[signature-lines] Processing complete");
22313
22354
  }
22355
+ };
22356
+ };
22357
+
22358
+ function escapeHtmlAttribute(value) {
22359
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#39;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
22360
+ }
22361
+ function processTemplateLoops(content, metadata, context, enableFieldTracking = true) {
22362
+ const loopBlocks = findLoopBlocks(content);
22363
+ if (loopBlocks.length === 0) {
22364
+ return content;
22314
22365
  }
22315
- allMatches.sort((a, b) => a.start - b.start);
22316
- const validMatches = [];
22317
- let lastEnd = 0;
22318
- for (const match of allMatches) {
22319
- if (match.start >= lastEnd) {
22320
- validMatches.push(match);
22321
- lastEnd = match.end;
22322
- }
22366
+ let processedContent = content;
22367
+ for (let i = loopBlocks.length - 1; i >= 0; i--) {
22368
+ const loopBlock = loopBlocks[i];
22369
+ const expandedContent = expandLoopBlock(loopBlock, metadata, context, enableFieldTracking);
22370
+ processedContent = processedContent.slice(0, loopBlock.start) + expandedContent + processedContent.slice(loopBlock.end);
22323
22371
  }
22324
- let pos = 0;
22325
- for (const match of validMatches) {
22326
- if (pos < match.start) {
22327
- const beforeText = text.slice(pos, match.start);
22328
- if (beforeText) {
22329
- children.push({
22330
- type: "text",
22331
- value: beforeText
22332
- });
22333
- }
22334
- }
22335
- if (match.type === "inlineCode") {
22336
- children.push({
22337
- type: "inlineCode",
22338
- value: match.content
22339
- });
22340
- } else if (match.type === "link") {
22341
- children.push({
22342
- type: "text",
22343
- value: match.content
22344
- });
22372
+ return processedContent;
22373
+ }
22374
+ function findLoopBlocks(content) {
22375
+ const loopBlocks = [];
22376
+ const loopPattern = /\{\{#([\w.]+)\}\}([\s\S]*?)\{\{\/\1\}\}/g;
22377
+ const conditionalPattern = /\{\{#if\s*([^}]+)\}\}([\s\S]*?)\{\{\/if\}\}/g;
22378
+ let match;
22379
+ while ((match = loopPattern.exec(content)) !== null) {
22380
+ const [fullMatch, variable, loopContent] = match;
22381
+ loopBlocks.push({
22382
+ variable,
22383
+ content: loopContent,
22384
+ start: match.index,
22385
+ end: match.index + fullMatch.length,
22386
+ fullMatch
22387
+ });
22388
+ }
22389
+ while ((match = conditionalPattern.exec(content)) !== null) {
22390
+ const [fullMatch, condition, conditionalContent] = match;
22391
+ loopBlocks.push({
22392
+ variable: `if ${condition}`,
22393
+ // Mark as conditional
22394
+ content: conditionalContent,
22395
+ start: match.index,
22396
+ end: match.index + fullMatch.length,
22397
+ fullMatch
22398
+ });
22399
+ }
22400
+ return loopBlocks;
22401
+ }
22402
+ function expandLoopBlock(loopBlock, metadata, parentContext, enableFieldTracking = true) {
22403
+ const { variable, content } = loopBlock;
22404
+ if (variable.startsWith("if ")) {
22405
+ const condition = variable.substring(3).trim();
22406
+ const conditionValue = evaluateCondition(condition, metadata);
22407
+ fieldTracker.trackField(`if ${condition}`, {
22408
+ value: conditionValue,
22409
+ hasLogic: true,
22410
+ mixinUsed: "conditional"
22411
+ });
22412
+ const elseIndex = content.indexOf("{{else}}");
22413
+ if (elseIndex !== -1) {
22414
+ const ifContent = content.substring(0, elseIndex);
22415
+ const elseContent = content.substring(elseIndex + 8);
22416
+ const selectedContent = conditionValue ? ifContent : elseContent;
22417
+ return processTemplateContent(selectedContent, metadata, parentContext, enableFieldTracking);
22345
22418
  } else {
22346
- children.push({
22347
- type: match.type,
22348
- children: [
22349
- {
22350
- type: "text",
22351
- value: match.content
22352
- }
22353
- ]
22354
- });
22419
+ if (conditionValue) {
22420
+ return processTemplateContent(content, metadata, parentContext, enableFieldTracking);
22421
+ } else {
22422
+ return "";
22423
+ }
22355
22424
  }
22356
- pos = match.end;
22357
22425
  }
22358
- if (pos < text.length) {
22359
- const remainingText = text.slice(pos);
22360
- if (remainingText) {
22361
- children.push({
22362
- type: "text",
22363
- value: remainingText
22364
- });
22365
- }
22426
+ const loopValue = resolveLoopVariable(variable, metadata, parentContext);
22427
+ fieldTracker.trackField(variable, {
22428
+ value: loopValue,
22429
+ hasLogic: true,
22430
+ mixinUsed: "loop"
22431
+ });
22432
+ if (Array.isArray(loopValue)) {
22433
+ return expandArrayLoop(
22434
+ variable,
22435
+ content,
22436
+ loopValue,
22437
+ metadata,
22438
+ parentContext,
22439
+ enableFieldTracking
22440
+ );
22441
+ } else if (loopValue) {
22442
+ return expandConditionalBlock(
22443
+ variable,
22444
+ content,
22445
+ loopValue,
22446
+ metadata,
22447
+ parentContext,
22448
+ enableFieldTracking
22449
+ );
22450
+ } else {
22451
+ return "";
22366
22452
  }
22367
- if (children.length === 0) {
22368
- return [
22369
- {
22370
- type: "text",
22371
- value: text
22372
- }
22373
- ];
22453
+ }
22454
+ function expandArrayLoop(variable, content, items, metadata, parentContext, enableFieldTracking = true) {
22455
+ const expandedParts = [];
22456
+ for (let i = 0; i < items.length; i++) {
22457
+ const item = items[i];
22458
+ const loopContext = {
22459
+ variable,
22460
+ item,
22461
+ index: i,
22462
+ total: items.length,
22463
+ parent: parentContext
22464
+ };
22465
+ const enhancedMetadata = createEnhancedMetadata(metadata, item, loopContext);
22466
+ let processedContent = content;
22467
+ processedContent = processTemplateLoops(
22468
+ processedContent,
22469
+ enhancedMetadata,
22470
+ loopContext,
22471
+ enableFieldTracking
22472
+ );
22473
+ processedContent = processItemMixins(
22474
+ processedContent,
22475
+ enhancedMetadata,
22476
+ loopContext,
22477
+ enableFieldTracking
22478
+ );
22479
+ processedContent = convertMarkdownListToHtml(processedContent);
22480
+ expandedParts.push(processedContent);
22374
22481
  }
22375
- return children;
22482
+ return expandedParts.join("\n");
22376
22483
  }
22377
-
22378
- const EXTENDED_DATE_PATTERN = /@today((?:[+-]\d+[dmy]?(?:\[[^\]]+\])?)|(?:[+-]\d+[dmy]?)|(?:\[[^\]]+\]))?/g;
22379
- function parseDateToken(token) {
22380
- if (!token) return { arithmetic: null, format: null, isValid: true };
22381
- if (!/^[+-]?\d*[dmy]?(\[[^\]]+\])?$/.test(token)) {
22382
- return { arithmetic: null, format: null, isValid: false };
22484
+ function expandConditionalBlock(variable, content, value, metadata, parentContext, enableFieldTracking = true) {
22485
+ const enhancedMetadata = {
22486
+ ...metadata,
22487
+ [variable]: value
22488
+ };
22489
+ let processedContent = processTemplateLoops(
22490
+ content,
22491
+ enhancedMetadata,
22492
+ parentContext,
22493
+ enableFieldTracking
22494
+ );
22495
+ processedContent = processItemMixins(
22496
+ processedContent,
22497
+ enhancedMetadata,
22498
+ parentContext,
22499
+ enableFieldTracking
22500
+ );
22501
+ return processedContent;
22502
+ }
22503
+ function resolveLoopVariable(variable, metadata, parentContext) {
22504
+ const resolvedFromMetadata = resolvePath(metadata, variable);
22505
+ if (resolvedFromMetadata !== void 0) {
22506
+ return resolvedFromMetadata;
22383
22507
  }
22384
- const formatMatch = token.match(/\[([^\]]+)\]/);
22385
- const format = formatMatch ? formatMatch[1] : null;
22386
- const arithmeticPart = token.replace(/\[([^\]]+)\]/, "");
22387
- let arithmetic = null;
22388
- if (arithmeticPart) {
22389
- const arithmeticMatch = arithmeticPart.match(/^([+-])(\d+)([dmy]?)$/);
22390
- if (arithmeticMatch) {
22391
- const [, sign, amount, suffix] = arithmeticMatch;
22392
- const numAmount = parseInt(amount) * (sign === "-" ? -1 : 1);
22393
- switch (suffix) {
22394
- case "d":
22395
- case "":
22396
- arithmetic = { type: "days", amount: numAmount };
22397
- break;
22398
- case "m":
22399
- arithmetic = { type: "months", amount: numAmount };
22400
- break;
22401
- case "y":
22402
- arithmetic = { type: "years", amount: numAmount };
22403
- break;
22404
- default:
22405
- arithmetic = { type: "days", amount: numAmount };
22508
+ if (parentContext && parentContext.item) {
22509
+ const resolvedFromContext = resolvePath(parentContext.item, variable);
22510
+ if (resolvedFromContext !== void 0) {
22511
+ return resolvedFromContext;
22512
+ }
22513
+ }
22514
+ let ctx = parentContext;
22515
+ while (ctx && ctx.parent) {
22516
+ ctx = ctx.parent;
22517
+ if (ctx.item) {
22518
+ const resolvedFromNestedContext = resolvePath(ctx.item, variable);
22519
+ if (resolvedFromNestedContext !== void 0) {
22520
+ return resolvedFromNestedContext;
22406
22521
  }
22407
- } else if (arithmeticPart !== "") {
22408
- return { arithmetic: null, format: null, isValid: false };
22409
22522
  }
22410
22523
  }
22411
- return { arithmetic, format, isValid: true };
22524
+ return void 0;
22412
22525
  }
22413
- function applyDateArithmetic(baseDate, arithmetic) {
22414
- if (!arithmetic) return baseDate;
22415
- switch (arithmetic.type) {
22416
- case "days":
22417
- return addDays(baseDate, arithmetic.amount);
22418
- case "months":
22419
- return addMonths(baseDate, arithmetic.amount);
22420
- case "years":
22421
- return addYears(baseDate, arithmetic.amount);
22422
- default:
22423
- return baseDate;
22526
+ function createEnhancedMetadata(metadata, item, context) {
22527
+ const enhanced = { ...metadata };
22528
+ if (item && typeof item === "object" && !Array.isArray(item)) {
22529
+ Object.assign(enhanced, item);
22424
22530
  }
22531
+ enhanced["."] = item;
22532
+ enhanced["@index"] = context.index;
22533
+ enhanced["@total"] = context.total;
22534
+ enhanced["@first"] = context.index === 0;
22535
+ enhanced["@last"] = context.index === context.total - 1;
22536
+ return enhanced;
22425
22537
  }
22426
- function getOrdinalSuffix(day) {
22427
- if (day >= 11 && day <= 13) {
22428
- return "th";
22429
- }
22430
- switch (day % 10) {
22431
- case 1:
22432
- return "st";
22433
- case 2:
22434
- return "nd";
22435
- case 3:
22436
- return "rd";
22437
- default:
22438
- return "th";
22538
+ function evaluateHelperExpression(expression, metadata) {
22539
+ try {
22540
+ const match = expression.match(/^(\w+)\((.*)\)$/);
22541
+ if (!match) return void 0;
22542
+ const [, helperName, argsString] = match;
22543
+ const helper = extensionHelpers[helperName];
22544
+ if (!helper || typeof helper !== "function") {
22545
+ return void 0;
22546
+ }
22547
+ const args = argsString ? parseHelperArguments(argsString, metadata) : [];
22548
+ return helper(...args);
22549
+ } catch (error) {
22550
+ console.error(`Error evaluating helper expression: ${expression}`, error);
22551
+ return void 0;
22439
22552
  }
22440
22553
  }
22441
- function formatDateBasic(date, format) {
22442
- const year = date.getFullYear();
22443
- const month = String(date.getMonth() + 1).padStart(2, "0");
22444
- const day = String(date.getDate()).padStart(2, "0");
22445
- const dayNumber = date.getDate();
22446
- const monthNames = [
22447
- "January",
22448
- "February",
22449
- "March",
22450
- "April",
22451
- "May",
22452
- "June",
22453
- "July",
22454
- "August",
22455
- "September",
22456
- "October",
22457
- "November",
22458
- "December"
22459
- ];
22460
- switch (format.toLowerCase()) {
22461
- case "iso":
22462
- case "yyyy-mm-dd":
22463
- return `${year}-${month}-${day}`;
22464
- case "us":
22465
- case "mm/dd/yyyy":
22466
- return `${month}/${day}/${year}`;
22467
- case "eu":
22468
- case "european":
22469
- case "dd/mm/yyyy":
22470
- return `${day}/${month}/${year}`;
22471
- case "long":
22472
- return `${monthNames[date.getMonth()]} ${dayNumber}, ${year}`;
22473
- case "medium":
22474
- return `${monthNames[date.getMonth()].substring(0, 3)} ${dayNumber}, ${year}`;
22475
- case "short":
22476
- return `${monthNames[date.getMonth()].substring(0, 3)} ${dayNumber}, ${String(year).substring(2)}`;
22477
- case "legal": {
22478
- const suffix = getOrdinalSuffix(dayNumber);
22479
- return `${monthNames[date.getMonth()]} ${dayNumber}${suffix}, ${year}`;
22554
+ function parseHelperArguments(argsString, metadata) {
22555
+ const args = [];
22556
+ let currentArg = "";
22557
+ let parenDepth = 0;
22558
+ let inQuotes = false;
22559
+ let quoteChar = "";
22560
+ for (let i = 0; i < argsString.length; i++) {
22561
+ const char = argsString[i];
22562
+ if ((char === '"' || char === "'") && !inQuotes) {
22563
+ inQuotes = true;
22564
+ quoteChar = char;
22565
+ currentArg += char;
22566
+ } else if (char === quoteChar && inQuotes) {
22567
+ inQuotes = false;
22568
+ quoteChar = "";
22569
+ currentArg += char;
22570
+ } else if (char === "(" && !inQuotes) {
22571
+ parenDepth++;
22572
+ currentArg += char;
22573
+ } else if (char === ")" && !inQuotes) {
22574
+ parenDepth--;
22575
+ currentArg += char;
22576
+ } else if (char === "," && parenDepth === 0 && !inQuotes) {
22577
+ args.push(resolveHelperArgument(currentArg.trim(), metadata));
22578
+ currentArg = "";
22579
+ } else {
22580
+ currentArg += char;
22480
22581
  }
22481
- default:
22482
- return `${year}-${month}-${day}`;
22483
22582
  }
22583
+ if (currentArg.trim()) {
22584
+ args.push(resolveHelperArgument(currentArg.trim(), metadata));
22585
+ }
22586
+ return args;
22484
22587
  }
22485
- function getDateFieldCssClass(hasArithmetic) {
22486
- return hasArithmetic ? "legal-field highlight" : "legal-field imported-value";
22588
+ function resolveHelperArgument(arg, metadata) {
22589
+ if (arg.startsWith('"') && arg.endsWith('"') || arg.startsWith("'") && arg.endsWith("'")) {
22590
+ return arg.slice(1, -1);
22591
+ }
22592
+ if (arg === "true") return true;
22593
+ if (arg === "false") return false;
22594
+ if (/^\d+$/.test(arg)) {
22595
+ return parseInt(arg, 10);
22596
+ }
22597
+ if (/^\d+\.\d+$/.test(arg)) {
22598
+ return parseFloat(arg);
22599
+ }
22600
+ if (arg === "@today") {
22601
+ return metadata["@today"] ? new Date(metadata["@today"]) : /* @__PURE__ */ new Date();
22602
+ }
22603
+ if (arg.includes("(") && arg.includes(")")) {
22604
+ const result = evaluateHelperExpression(arg, metadata);
22605
+ if (result !== void 0) {
22606
+ return result;
22607
+ }
22608
+ }
22609
+ return resolvePath(metadata, arg);
22487
22610
  }
22488
- function formatDateValue(value, originalToken, enableFieldTracking = false, hasArithmetic = false) {
22489
- if (!enableFieldTracking) {
22490
- return value;
22611
+ function resolvePath(obj, path) {
22612
+ const keys = path.split(".");
22613
+ let current = obj;
22614
+ for (const key of keys) {
22615
+ if (current && typeof current === "object" && key in current) {
22616
+ current = current[key];
22617
+ } else {
22618
+ return void 0;
22619
+ }
22491
22620
  }
22492
- const cssClass = getDateFieldCssClass(hasArithmetic);
22493
- const fieldName = `date.${originalToken.replace(/[@[\]]/g, "")}`;
22494
- return `<span class="${cssClass}" data-field="${fieldName.replace(/"/g, "&quot;")}">${value}</span>`;
22621
+ return current;
22495
22622
  }
22496
- function processDateReferencesInAST(root, metadata, enableFieldTracking = false) {
22497
- visit(root, "text", (node, index, parent) => {
22498
- const originalValue = node.value;
22499
- let modifiedValue = originalValue;
22500
- let hasChanges = false;
22501
- modifiedValue = modifiedValue.replace(EXTENDED_DATE_PATTERN, (match, token) => {
22502
- try {
22503
- const { arithmetic, format: tokenFormat, isValid } = parseDateToken(token);
22504
- if (!isValid) {
22505
- return match;
22506
- }
22507
- let date = /* @__PURE__ */ new Date();
22508
- const hasArithmetic = !!arithmetic;
22509
- if (arithmetic) {
22510
- date = applyDateArithmetic(date, arithmetic);
22511
- }
22512
- const format = tokenFormat || metadata["date-format"] || "YYYY-MM-DD";
22513
- const formattedDate = formatDateBasic(date, format);
22514
- fieldTracker.trackField(`date.${match.replace(/[@[\]]/g, "")}`, {
22515
- value: formattedDate,
22516
- originalValue: match,
22517
- hasLogic: hasArithmetic
22518
- });
22519
- hasChanges = true;
22520
- return formatDateValue(formattedDate, match, enableFieldTracking, hasArithmetic);
22521
- } catch (error) {
22522
- console.warn(`Error processing date reference ${match}:`, error);
22523
- return match;
22524
- }
22525
- });
22526
- if (hasChanges) {
22527
- if (enableFieldTracking && modifiedValue.includes("<span")) {
22528
- if (parent && typeof index === "number") {
22529
- const htmlNode = {
22530
- type: "html",
22531
- value: modifiedValue
22532
- };
22533
- parent.children[index] = htmlNode;
22534
- }
22535
- } else {
22536
- node.value = modifiedValue;
22623
+ function processItemMixins(content, metadata, context, enableFieldTracking = true) {
22624
+ const mixinPattern = /\{\{([^}]+)\}\}/g;
22625
+ return content.replace(mixinPattern, (match, variable) => {
22626
+ const trimmedVar = variable.trim();
22627
+ if (trimmedVar.includes("?") && trimmedVar.includes(":")) {
22628
+ const result = processConditionalExpression(trimmedVar, metadata, enableFieldTracking);
22629
+ if (enableFieldTracking) {
22630
+ return `<span class="highlight">${result}</span>`;
22537
22631
  }
22632
+ return result;
22538
22633
  }
22539
- });
22540
- visit(root, "html", (node) => {
22541
- if (!node.value || !node.value.includes("@today")) {
22542
- return;
22543
- }
22544
- let modifiedValue = node.value;
22545
- let hasChanges = false;
22546
- modifiedValue = modifiedValue.replace(
22547
- EXTENDED_DATE_PATTERN,
22548
- (match, formatOverride) => {
22549
- try {
22550
- const date = /* @__PURE__ */ new Date();
22551
- const format = formatOverride || metadata["date-format"] || "YYYY-MM-DD";
22552
- const formattedDate = formatDateBasic(date, format);
22553
- fieldTracker.trackField(`date.${match.replace(/[@[\]]/g, "")}`, {
22554
- value: formattedDate,
22555
- originalValue: match,
22556
- hasLogic: false
22557
- });
22558
- hasChanges = true;
22559
- if (enableFieldTracking) {
22560
- const cssClass = getDateFieldCssClass(false);
22561
- const fieldName = `date.${match.replace(/[@[\]]/g, "")}`;
22562
- return `<span class="${cssClass}" data-field="${fieldName.replace(/"/g, "&quot;")}">${formattedDate}</span>`;
22563
- } else {
22564
- return formattedDate;
22565
- }
22566
- } catch (error) {
22567
- console.warn(`Error processing date reference ${match}:`, error);
22568
- return match;
22634
+ if (trimmedVar.includes("(") && trimmedVar.includes(")")) {
22635
+ const result = evaluateHelperExpression(trimmedVar, metadata);
22636
+ if (result !== void 0) {
22637
+ fieldTracker.trackField(trimmedVar, {
22638
+ value: result,
22639
+ hasLogic: true,
22640
+ mixinUsed: "helper"
22641
+ });
22642
+ if (enableFieldTracking) {
22643
+ const escapedField = escapeHtmlAttribute(trimmedVar);
22644
+ const stringResult = String(result);
22645
+ return `<span class="highlight"><span class="imported-value" data-field="${escapedField}">${stringResult}</span></span>`;
22569
22646
  }
22647
+ return String(result);
22570
22648
  }
22571
- );
22572
- if (hasChanges) {
22573
- node.value = modifiedValue;
22574
22649
  }
22650
+ const value = resolveVariablePath(trimmedVar, metadata);
22651
+ if (value === void 0 || value === null) {
22652
+ if (enableFieldTracking) {
22653
+ return `<span class="legal-field missing-value" data-field="${escapeHtmlAttribute(trimmedVar)}">[[${trimmedVar}]]</span>`;
22654
+ }
22655
+ return `{{${trimmedVar}}}`;
22656
+ }
22657
+ if (enableFieldTracking) {
22658
+ return `<span class="imported-value" data-field="${escapeHtmlAttribute(trimmedVar)}">${String(value)}</span>`;
22659
+ }
22660
+ return String(value);
22575
22661
  });
22576
22662
  }
22577
- const remarkDates = (options) => {
22578
- const { metadata, debug = false, enableFieldTracking = false } = options;
22579
- return (tree) => {
22580
- if (debug) {
22581
- console.log("📅 Processing date references with remark plugin");
22663
+ function processConditionalExpression(expression, metadata, enableFieldTracking = true) {
22664
+ const questionIndex = findOperatorIndex(expression, "?");
22665
+ const colonIndex = findOperatorIndex(expression, ":", questionIndex);
22666
+ if (questionIndex === -1 || colonIndex === -1) {
22667
+ return expression;
22668
+ }
22669
+ const condition = expression.substring(0, questionIndex).trim();
22670
+ const truePart = expression.substring(questionIndex + 1, colonIndex).trim();
22671
+ const falsePart = expression.substring(colonIndex + 1).trim();
22672
+ const conditionValue = resolveVariablePath(condition, metadata);
22673
+ const selectedPart = conditionValue ? truePart : falsePart;
22674
+ if (selectedPart.startsWith('"') && selectedPart.endsWith('"') || // eslint-disable-next-line quotes
22675
+ selectedPart.startsWith("'") && selectedPart.endsWith("'")) {
22676
+ const unquotedValue = selectedPart.slice(1, -1);
22677
+ if (enableFieldTracking) {
22678
+ return `<span class="imported-value" data-field="${escapeHtmlAttribute(expression)}">${unquotedValue}</span>`;
22582
22679
  }
22583
- processDateReferencesInAST(tree, metadata, enableFieldTracking);
22584
- if (debug) {
22585
- console.log("✅ Date reference processing completed");
22680
+ return unquotedValue;
22681
+ }
22682
+ const processedValue = processExpression(selectedPart, metadata);
22683
+ if (enableFieldTracking) {
22684
+ return `<span class="imported-value" data-field="${escapeHtmlAttribute(expression)}">${processedValue}</span>`;
22685
+ }
22686
+ return processedValue;
22687
+ }
22688
+ function findOperatorIndex(expression, operator, startIndex = 0) {
22689
+ let inQuotes = false;
22690
+ let quoteChar = "";
22691
+ for (let i = startIndex; i < expression.length; i++) {
22692
+ const char = expression[i];
22693
+ if (!inQuotes && (char === '"' || char === "'")) {
22694
+ inQuotes = true;
22695
+ quoteChar = char;
22696
+ } else if (inQuotes && char === quoteChar) {
22697
+ inQuotes = false;
22698
+ quoteChar = "";
22699
+ } else if (!inQuotes && char === operator) {
22700
+ return i;
22586
22701
  }
22587
- };
22588
- };
22589
-
22590
- const DEFAULT_OPTIONS = {
22591
- minUnderscores: 10,
22592
- addCssClass: true,
22593
- cssClassName: "signature-line",
22594
- debug: false
22595
- };
22596
- function sanitizeCssClassName(className) {
22597
- const validPattern = /^[a-zA-Z_][\w-]*$/;
22598
- if (!className || !validPattern.test(className)) {
22599
- console.warn(
22600
- `[signature-lines] Invalid CSS class name "${className}", using default "signature-line"`
22601
- );
22602
- return "signature-line";
22603
22702
  }
22604
- return className;
22703
+ return -1;
22605
22704
  }
22606
- const remarkSignatureLines = (options = {}) => {
22607
- const config = { ...DEFAULT_OPTIONS, ...options };
22608
- const safeCssClassName = sanitizeCssClassName(config.cssClassName);
22609
- return (tree) => {
22610
- if (config.debug) {
22611
- console.log("[signature-lines] Processing tree...");
22705
+ function processExpression(expression, metadata) {
22706
+ if (expression.includes("+") && (expression.includes('"') || expression.includes("'"))) {
22707
+ return evaluateStringConcatenation(expression, metadata);
22708
+ }
22709
+ if (expression.includes("*") || expression.includes("/") || expression.includes("+") || expression.includes("-")) {
22710
+ return evaluateMathematicalExpression(expression, metadata);
22711
+ }
22712
+ const value = resolveVariablePath(expression, metadata);
22713
+ return value !== void 0 ? String(value) : expression;
22714
+ }
22715
+ function evaluateStringConcatenation(expression, metadata) {
22716
+ try {
22717
+ let processedExpression = expression.trim();
22718
+ const variablePattern = /\b[a-zA-Z_][a-zA-Z0-9_.]*\b/g;
22719
+ const variables = /* @__PURE__ */ new Set();
22720
+ let match;
22721
+ let inQuotes = false;
22722
+ let quoteChar = "";
22723
+ for (let i = 0; i < expression.length; i++) {
22724
+ const char = expression[i];
22725
+ if (!inQuotes && (char === '"' || char === "'")) {
22726
+ inQuotes = true;
22727
+ quoteChar = char;
22728
+ } else if (inQuotes && char === quoteChar) {
22729
+ inQuotes = false;
22730
+ quoteChar = "";
22731
+ }
22612
22732
  }
22613
- visit(tree, "text", (node, index, parent) => {
22614
- if (index === void 0 || !parent) return;
22615
- const text = node.value;
22616
- const underscorePattern = new RegExp(`_{${config.minUnderscores},}`, "g");
22617
- if (!underscorePattern.test(text)) {
22618
- return;
22733
+ while ((match = variablePattern.exec(expression)) !== null) {
22734
+ const beforeMatch = expression.substring(0, match.index);
22735
+ const openQuotes = (beforeMatch.match(/"/g) || []).length;
22736
+ const openSingleQuotes = (beforeMatch.match(/'/g) || []).length;
22737
+ if (openQuotes % 2 === 0 && openSingleQuotes % 2 === 0) {
22738
+ variables.add(match[0]);
22619
22739
  }
22620
- if (config.debug) {
22621
- console.log("[signature-lines] Found signature line in text:", text);
22740
+ }
22741
+ for (const variable of variables) {
22742
+ const value = resolveVariablePath(variable, metadata);
22743
+ if (value !== void 0) {
22744
+ const regex = new RegExp(`\\b${variable.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`, "g");
22745
+ if (typeof value === "string") {
22746
+ processedExpression = processedExpression.replace(regex, `"${value}"`);
22747
+ } else {
22748
+ processedExpression = processedExpression.replace(regex, String(value));
22749
+ }
22750
+ } else {
22751
+ return expression;
22622
22752
  }
22623
- if (!config.addCssClass) {
22624
- return;
22753
+ }
22754
+ const result = new Function(`"use strict"; return (${processedExpression})`)();
22755
+ return String(result);
22756
+ } catch (error) {
22757
+ return expression;
22758
+ }
22759
+ }
22760
+ function evaluateMathematicalExpression(expression, metadata) {
22761
+ try {
22762
+ let processedExpression = expression;
22763
+ const variablePattern = /[a-zA-Z_][a-zA-Z0-9_.]*(?![a-zA-Z0-9_.])/g;
22764
+ const variables = /* @__PURE__ */ new Set();
22765
+ let match;
22766
+ while ((match = variablePattern.exec(expression)) !== null) {
22767
+ variables.add(match[0]);
22768
+ }
22769
+ for (const variable of variables) {
22770
+ const value = resolveVariablePath(variable, metadata);
22771
+ if (value !== void 0 && !isNaN(Number(value))) {
22772
+ const regex = new RegExp(`\\b${variable.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`, "g");
22773
+ processedExpression = processedExpression.replace(regex, String(value));
22774
+ } else {
22775
+ return expression;
22625
22776
  }
22626
- const processedText = text.replace(underscorePattern, (match) => {
22627
- if (config.debug) {
22628
- console.log(`[signature-lines] Wrapping ${match.length} underscores`);
22629
- }
22630
- return `<span class="${safeCssClassName}">${match}</span>`;
22631
- });
22632
- const htmlNode = {
22633
- type: "html",
22634
- value: processedText
22635
- };
22636
- parent.children[index] = htmlNode;
22637
- });
22638
- if (config.debug) {
22639
- console.log("[signature-lines] Processing complete");
22640
22777
  }
22641
- };
22642
- };
22778
+ if (!/^[\d\s+\-*/().]+$/.test(processedExpression)) {
22779
+ return expression;
22780
+ }
22781
+ const result = new Function(`"use strict"; return (${processedExpression})`)();
22782
+ return typeof result === "number" && !isNaN(result) ? String(result) : expression;
22783
+ } catch (error) {
22784
+ return expression;
22785
+ }
22786
+ }
22787
+ function resolveVariablePath(path, metadata, context) {
22788
+ if (path === ".") {
22789
+ return metadata["."];
22790
+ }
22791
+ const parts = path.split(".");
22792
+ let current = metadata;
22793
+ for (const part of parts) {
22794
+ if (current && typeof current === "object" && Object.prototype.hasOwnProperty.call(current, part)) {
22795
+ current = current[part];
22796
+ } else {
22797
+ return void 0;
22798
+ }
22799
+ }
22800
+ return current;
22801
+ }
22802
+ function convertMarkdownListToHtml(content) {
22803
+ const trimmedContent = content.trim();
22804
+ if (trimmedContent.startsWith("- ")) {
22805
+ const listItemContent = trimmedContent.substring(2).trim();
22806
+ return `<li>${listItemContent}</li>`;
22807
+ }
22808
+ return content;
22809
+ }
22810
+ function evaluateCondition(condition, metadata) {
22811
+ const value = resolveVariablePath(condition, metadata);
22812
+ if (value === null || value === void 0) return false;
22813
+ if (typeof value === "boolean") return value;
22814
+ if (typeof value === "number") return value !== 0;
22815
+ if (typeof value === "string") return value.length > 0;
22816
+ if (Array.isArray(value)) return value.length > 0;
22817
+ if (typeof value === "object") return Object.keys(value).length > 0;
22818
+ return Boolean(value);
22819
+ }
22820
+ function processTemplateContent(content, metadata, context, enableFieldTracking = true) {
22821
+ let processedContent = processTemplateLoops(content, metadata, context, enableFieldTracking);
22822
+ processedContent = processedContent.replace(/\{\{([^#/][^}]*)\}\}/g, (match, variable) => {
22823
+ const trimmedVar = variable.trim();
22824
+ const value = resolveVariablePath(trimmedVar, metadata);
22825
+ if (value !== void 0) {
22826
+ if (enableFieldTracking) {
22827
+ const isEmptyValue = value === null || value === "" || typeof value === "string" && value.trim() === "";
22828
+ const cssClass = isEmptyValue ? "legal-field missing-value" : "legal-field imported-value";
22829
+ fieldTracker.trackField(trimmedVar, {
22830
+ value,
22831
+ originalValue: match,
22832
+ hasLogic: false
22833
+ });
22834
+ return `<span class="${cssClass}" data-field="${escapeHtmlAttribute(trimmedVar)}">${String(value)}</span>`;
22835
+ }
22836
+ return String(value);
22837
+ }
22838
+ return match;
22839
+ });
22840
+ return processedContent;
22841
+ }
22643
22842
 
22644
22843
  var define_process_default = { env: { NODE_ENV: "production", DEBUG: false } };
22645
22844
  function exportMetadata(metadata, format, outputPath) {
@@ -22707,6 +22906,10 @@ function filterMetadataForExport(metadata) {
22707
22906
  return result;
22708
22907
  }
22709
22908
 
22909
+ function asMarkdown(content) {
22910
+ return content;
22911
+ }
22912
+
22710
22913
  const unsafeWithoutUnderscores = [
22711
22914
  { character: " ", after: "[\\r\\n]", inConstruct: "phrasing" },
22712
22915
  { character: " ", before: "[\\r\\n]", inConstruct: "phrasing" },
@@ -22848,6 +23051,7 @@ function escapeTemplateUnderscores(content) {
22848
23051
  }
22849
23052
  function createLegalMarkdownProcessor(metadata, options) {
22850
23053
  const processor = unified().use(remarkParse);
23054
+ const pluginOrder = [];
22851
23055
  if (!options.noImports) {
22852
23056
  processor.use(remarkImports, {
22853
23057
  basePath: options.basePath || ".",
@@ -22932,6 +23136,42 @@ function createLegalMarkdownProcessor(metadata, options) {
22932
23136
  unsafe: unsafeWithoutUnderscores
22933
23137
  // Use filtered unsafe patterns without underscores
22934
23138
  });
23139
+ if (!options.noImports) pluginOrder.push("remarkImports");
23140
+ if (!options.noHeaders) pluginOrder.push("remarkLegalHeadersParser");
23141
+ if (!options.noMixins) pluginOrder.push("remarkMixins");
23142
+ if (!options.noClauses) pluginOrder.push("remarkClauses");
23143
+ pluginOrder.push("remarkDates");
23144
+ pluginOrder.push("remarkSignatureLines");
23145
+ pluginOrder.push("remarkTemplateFields");
23146
+ if (!options.disableCrossReferences && !options.noReferences) {
23147
+ pluginOrder.push("remarkCrossReferences");
23148
+ }
23149
+ if (!options.noHeaders) pluginOrder.push("remarkHeaders");
23150
+ if (options.debug || options.validatePluginOrder) {
23151
+ try {
23152
+ const validator = new PluginOrderValidator(GLOBAL_PLUGIN_REGISTRY);
23153
+ const result = validator.validate(pluginOrder, {
23154
+ throwOnError: false,
23155
+ // Don't throw, just log warnings
23156
+ logWarnings: options.debug || options.validatePluginOrder,
23157
+ strictMode: false,
23158
+ debug: options.debug
23159
+ });
23160
+ if (!result.valid && options.debug) {
23161
+ console.warn("[LegalMarkdownProcessor] Plugin order validation warnings detected");
23162
+ for (const error of result.errors) {
23163
+ console.warn(` - ${error.message}`);
23164
+ }
23165
+ if (result.suggestedOrder) {
23166
+ console.warn(" Suggested order:", result.suggestedOrder.join(" → "));
23167
+ }
23168
+ }
23169
+ } catch (error) {
23170
+ if (options.debug) {
23171
+ console.warn("[LegalMarkdownProcessor] Plugin order validation failed:", error);
23172
+ }
23173
+ }
23174
+ }
22935
23175
  return processor;
22936
23176
  }
22937
23177
  async function processLegalMarkdownWithRemark(content, options = {}) {
@@ -22979,7 +23219,7 @@ async function processLegalMarkdownWithRemark(content, options = {}) {
22979
23219
  );
22980
23220
  }
22981
23221
  return {
22982
- content: contentWithEscapedTemplates,
23222
+ content: asMarkdown(contentWithEscapedTemplates),
22983
23223
  metadata: { ...yamlMetadata, ...options.additionalMetadata },
22984
23224
  stats: {
22985
23225
  processingTime: Date.now() - startTime,
@@ -22992,7 +23232,7 @@ async function processLegalMarkdownWithRemark(content, options = {}) {
22992
23232
  }
22993
23233
  if (options.yamlOnly) {
22994
23234
  return {
22995
- content: contentWithEscapedTemplates,
23235
+ content: asMarkdown(contentWithEscapedTemplates),
22996
23236
  metadata: yamlMetadata,
22997
23237
  stats: {
22998
23238
  processingTime: Date.now() - startTime,
@@ -23007,7 +23247,7 @@ async function processLegalMarkdownWithRemark(content, options = {}) {
23007
23247
  ...yamlMetadata,
23008
23248
  ...options.additionalMetadata
23009
23249
  };
23010
- const { parseForceCommands, applyForceCommands } = await import('./force-commands-parser-CkTMgF1y.js');
23250
+ const { parseForceCommands, applyForceCommands } = await import('./force-commands-parser-DDda8v0G.js');
23011
23251
  let updatedOptions = { ...options };
23012
23252
  if (combinedMetadata.force_commands && typeof combinedMetadata.force_commands === "string") {
23013
23253
  const forceCommands = parseForceCommands(combinedMetadata.force_commands, combinedMetadata);
@@ -23087,8 +23327,8 @@ async function processLegalMarkdownWithRemark(content, options = {}) {
23087
23327
  }
23088
23328
  const result = await processor.process(preprocessedContent);
23089
23329
  const processedContent = String(result);
23090
- const processedTree = result.history[0];
23091
- const importedMetadata = processedTree?._importedMetadata || {};
23330
+ const processedTree = result.value;
23331
+ const importedMetadata = result.history[0]?._importedMetadata || {};
23092
23332
  const finalMetadata = {
23093
23333
  ...importedMetadata,
23094
23334
  ...combinedMetadata
@@ -23136,8 +23376,10 @@ async function processLegalMarkdownWithRemark(content, options = {}) {
23136
23376
  console.log(`📋 Fields tracked: ${fieldsTracked}`);
23137
23377
  }
23138
23378
  return {
23139
- content: processedContent,
23379
+ content: asMarkdown(processedContent),
23140
23380
  metadata: finalMetadata,
23381
+ ast: processedTree,
23382
+ // Cached AST for Phase 3
23141
23383
  exportedFiles: exportedFiles.length > 0 ? exportedFiles : void 0,
23142
23384
  fieldReport,
23143
23385
  stats: {
@@ -23196,5 +23438,5 @@ if (typeof window !== "undefined" && window.DEBUG_LEGAL_MARKDOWN) {
23196
23438
  });
23197
23439
  }
23198
23440
 
23199
- export { LegalMarkdown as L, processLegalMarkdown as a, processLegalMarkdownSync as b, logger as l, processMixins as p };
23200
- //# sourceMappingURL=browser-modern-wM0F4Tg1.js.map
23441
+ export { LegalMarkdown as L, processLegalMarkdown as a, processLegalMarkdownSync as b, extensionHelpers as e, fieldTracker as f, logger as l, processTemplateLoops as p };
23442
+ //# sourceMappingURL=browser-modern-BP5EJrCS.js.map