@ethlete/core 5.0.0-next.58 → 5.0.0-next.59

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # @ethlete/core
2
2
 
3
+ ## 5.0.0-next.59
4
+
5
+ ### Patch Changes
6
+
7
+ - The legacy theme stylesheets carry the page's `ngCspNonce`, and `et-structured-data` no longer writes a static `style` attribute, so both pass a strict `style-src`.
8
+ - `markdownToHtml` renders block and table-cell alignment as an `et-rte-align-*` class instead of an inline `text-align` style, which a strict `style-src` dropped; `htmlToMarkdown` reads both.
9
+
3
10
  ## 5.0.0-next.58
4
11
 
5
12
  ### Minor Changes
@@ -3470,7 +3470,9 @@ const parseSeparatorAligns = (line) => parseTableRow(line).map((cell) => {
3470
3470
  });
3471
3471
  /** The GFM separator token for a column alignment. */
3472
3472
  const separatorFor = (align) => align === 'center' ? ':---:' : align === 'right' ? '---:' : align === 'left' ? ':---' : '---';
3473
- const alignStyle = (align) => (align ? ` style="text-align: ${align}"` : '');
3473
+ const alignClass = (align) => (align ? ` class="et-rte-align-${align}"` : '');
3474
+ const alignOf = (attrs) => (/\bclass\s*=\s*["'][^"']*\bet-rte-align-([a-z]+)/i.exec(attrs)?.[1] ??
3475
+ /\bstyle\s*=\s*["'][^"']*text-align:\s*([a-z]+)/i.exec(attrs)?.[1])?.toLowerCase() ?? null;
3474
3476
  /** Wraps `content` in an emphasis `marker`, hoisting boundary whitespace outside the delimiters -
3475
3477
  * CommonMark emphasis must not face whitespace on the inside (`** fett**` doesn't parse), and
3476
3478
  * ` ` counts as whitespace there too. Whitespace-only content stays unwrapped. */
@@ -3737,11 +3739,11 @@ const markdownToHtml = (markdown) => {
3737
3739
  // Rebuild it instead of passing it through verbatim: only the alignment survives on the tag
3738
3740
  // (no other attributes, e.g. event handlers) and the inner markup is reduced to the editor's
3739
3741
  // own inline vocabulary.
3740
- const aligned = /^<(p|h[1-6]|div)\b[^>]*\bstyle=["'][^"']*text-align:\s*([a-z]+)[^>]*>([\s\S]*)<\/\1>$/i.exec(trimmed);
3741
- if (aligned) {
3742
+ const aligned = /^<(p|h[1-6]|div)\b([^>]*)>([\s\S]*)<\/\1>$/i.exec(trimmed);
3743
+ const align = aligned ? alignOf(aligned[2] ?? '') : null;
3744
+ if (aligned && align) {
3742
3745
  const tag = (aligned[1] ?? 'p').toLowerCase();
3743
- const align = (aligned[2] ?? 'left').toLowerCase();
3744
- return `<${tag} style="text-align: ${align}">${sanitizeInlineHtml(aligned[3] ?? '')}</${tag}>`;
3746
+ return `<${tag}${alignClass(align)}>${sanitizeInlineHtml(aligned[3] ?? '')}</${tag}>`;
3745
3747
  }
3746
3748
  // Heading
3747
3749
  const headingMatch = trimmed.match(/^(#{1,6})\s+(.+)$/);
@@ -3758,13 +3760,13 @@ const markdownToHtml = (markdown) => {
3758
3760
  const aligns = parseSeparatorAligns(tableLines[1] ?? '');
3759
3761
  const headers = parseTableRow(tableLines[0] ?? '');
3760
3762
  const thead = `<thead><tr>${headers
3761
- .map((h, i) => `<th${alignStyle(aligns[i] ?? null)}>${processInline(h)}</th>`)
3763
+ .map((h, i) => `<th${alignClass(aligns[i] ?? null)}>${processInline(h)}</th>`)
3762
3764
  .join('')}</tr></thead>`;
3763
3765
  const bodyRows = tableLines.slice(2);
3764
3766
  const tbody = bodyRows.length > 0
3765
3767
  ? `<tbody>${bodyRows
3766
3768
  .map((row) => `<tr>${parseTableRow(row)
3767
- .map((cell, i) => `<td${alignStyle(aligns[i] ?? null)}>${processInline(cell)}</td>`)
3769
+ .map((cell, i) => `<td${alignClass(aligns[i] ?? null)}>${processInline(cell)}</td>`)
3768
3770
  .join('')}</tr>`)
3769
3771
  .join('')}</tbody>`
3770
3772
  : '';
@@ -3805,7 +3807,10 @@ const htmlToMarkdown = (html) => {
3805
3807
  // inner markup stays HTML) and round-trip via a placeholder - extracted before the block passes
3806
3808
  // below rewrite them, restored after the final tag-strip.
3807
3809
  const alignedBlocks = [];
3808
- md = md.replace(/<(p|h[1-6]|div)\b[^>]*\bstyle="[^"]*text-align[^"]*"[^>]*>[\s\S]*?<\/\1>/gi, (match) => makePlaceholder('ALIGN', alignedBlocks.push(match) - 1));
3810
+ md = md.replace(/<(p|h[1-6]|div)\b([^>]*\b(?:style="[^"]*text-align|class="[^"]*\bet-rte-align-)[^>]*)>([\s\S]*?)<\/\1>/gi, (_, tag, attrs, inner) => {
3811
+ const name = tag.toLowerCase();
3812
+ return makePlaceholder('ALIGN', alignedBlocks.push(`<${name} style="text-align: ${alignOf(attrs)}">${inner}</${name}>`) - 1);
3813
+ });
3809
3814
  // Code blocks - process before inline code
3810
3815
  md = md.replace(/<pre[^>]*><code[^>]*class="language-([^"]+)"[^>]*>([\s\S]*?)<\/code><\/pre>/gi, (_, lang, code) => `\n\`\`\`${lang}\n${unescapeHtml(code.trim())}\n\`\`\`\n`);
3811
3816
  md = md.replace(/<pre[^>]*><code[^>]*>([\s\S]*?)<\/code><\/pre>/gi, (_, code) => `\n\`\`\`\n${unescapeHtml(code.trim())}\n\`\`\`\n`);
@@ -3859,8 +3864,8 @@ const htmlToMarkdown = (html) => {
3859
3864
  const extractCells = (row) => [...row.matchAll(/<t[hd][^>]*>([\s\S]*?)<\/t[hd]>/gi)].map((m) => stripTags((m[1] ?? '').replace(/<br\s*\/?>/gi, ' ')).trim());
3860
3865
  // GFM alignment is per column, read from the header cells' text-align styles
3861
3866
  const extractAligns = (row) => [...row.matchAll(/<t[hd]([^>]*)>/gi)].map((m) => {
3862
- const align = /text-align:\s*(left|center|right)/i.exec(m[1] ?? '')?.[1]?.toLowerCase();
3863
- return align ?? null;
3867
+ const align = alignOf(m[1] ?? '');
3868
+ return align === 'left' || align === 'center' || align === 'right' ? align : null;
3864
3869
  });
3865
3870
  const theadMatch = tableContent.match(/<thead[^>]*>([\s\S]*?)<\/thead>/i);
3866
3871
  const firstTrMatch = tableContent.match(/<tr[^>]*>([\s\S]*?)<\/tr>/i);
@@ -6236,7 +6241,7 @@ class StructuredDataComponent {
6236
6241
  ...(ngDevMode ? [{ debugName: "jsonLD" }] : /* istanbul ignore next */ []));
6237
6242
  }
6238
6243
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StructuredDataComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6239
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.6", type: StructuredDataComponent, isStandalone: true, selector: "et-structured-data", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "innerHTML": "jsonLD()" }, styleAttribute: "display: none" }, ngImport: i0, template: '', isInline: true, encapsulation: i0.ViewEncapsulation.None }); }
6244
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.6", type: StructuredDataComponent, isStandalone: true, selector: "et-structured-data", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "style.display": "\"none\"", "innerHTML": "jsonLD()" } }, ngImport: i0, template: '', isInline: true, encapsulation: i0.ViewEncapsulation.None }); }
6240
6245
  }
6241
6246
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: StructuredDataComponent, decorators: [{
6242
6247
  type: Component,
@@ -6245,7 +6250,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
6245
6250
  template: '',
6246
6251
  encapsulation: ViewEncapsulation.None,
6247
6252
  host: {
6248
- style: 'display: none',
6253
+ '[style.display]': '"none"',
6249
6254
  '[innerHTML]': 'jsonLD()',
6250
6255
  },
6251
6256
  }]
@@ -9703,6 +9708,14 @@ const injectDefaultColorTheme = () => {
9703
9708
  return theme;
9704
9709
  };
9705
9710
 
9711
+ /** Reads the nonce where Angular's default `CSP_NONCE` does, since these run outside any injector. */
9712
+ const createNoncedStyle = () => {
9713
+ const style = document.createElement('style');
9714
+ const nonce = document.body?.querySelector('[ngCspNonce]')?.getAttribute('ngCspNonce');
9715
+ if (nonce)
9716
+ style.setAttribute('nonce', nonce);
9717
+ return style;
9718
+ };
9706
9719
  /**
9707
9720
  * @deprecated Migrate to Tailwind v4. Intent to remove in v6.
9708
9721
  */
@@ -9788,7 +9801,7 @@ const createRootThemeCss = (themes) => {
9788
9801
  }
9789
9802
  `;
9790
9803
  document.getElementById('et-root-themes')?.remove();
9791
- const style = document.createElement('style');
9804
+ const style = createNoncedStyle();
9792
9805
  style.id = `et-root-themes`;
9793
9806
  style.appendChild(document.createTextNode(css));
9794
9807
  document.head.appendChild(style);
@@ -9815,7 +9828,7 @@ const createThemeStyle = (theme, isAlt) => {
9815
9828
  }
9816
9829
  `;
9817
9830
  document.getElementById(`${classPrefix}--${cssThemeName}`)?.remove();
9818
- const style = document.createElement('style');
9831
+ const style = createNoncedStyle();
9819
9832
  style.id = `${classPrefix}--${cssThemeName}`;
9820
9833
  style.appendChild(document.createTextNode(css));
9821
9834
  document.head.appendChild(style);
@@ -10061,7 +10074,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
10061
10074
 
10062
10075
  // Generated from package.json by tools/scripts/generate-version.js - do not edit.
10063
10076
  /** The version of `@ethlete/core` this build was cut from. */
10064
- const CORE_VERSION = '5.0.0-next.58';
10077
+ const CORE_VERSION = '5.0.0-next.59';
10065
10078
 
10066
10079
  /**
10067
10080
  * Generated bundle index. Do not edit.