meno-core 1.1.3 → 1.1.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 (50) hide show
  1. package/dist/chunks/{chunk-LOZL5HOF.js → chunk-UOF4MCAD.js} +64 -2
  2. package/dist/chunks/chunk-UOF4MCAD.js.map +7 -0
  3. package/dist/chunks/{chunk-3JXK2QFU.js → chunk-ZEDLSHYQ.js} +2 -2
  4. package/dist/lib/client/index.js +46 -28
  5. package/dist/lib/client/index.js.map +2 -2
  6. package/dist/lib/server/index.js +1267 -233
  7. package/dist/lib/server/index.js.map +4 -4
  8. package/dist/lib/shared/index.js +3 -1
  9. package/dist/lib/shared/index.js.map +1 -1
  10. package/lib/client/core/ComponentBuilder.test.ts +32 -0
  11. package/lib/client/core/ComponentBuilder.ts +30 -0
  12. package/lib/client/theme.test.ts +2 -2
  13. package/lib/client/theme.ts +28 -26
  14. package/lib/server/createServer.ts +5 -1
  15. package/lib/server/cssAudit.test.ts +270 -0
  16. package/lib/server/cssAudit.ts +815 -0
  17. package/lib/server/deMirror.test.ts +61 -0
  18. package/lib/server/deMirror.ts +317 -0
  19. package/lib/server/fileWatcher.test.ts +23 -0
  20. package/lib/server/fileWatcher.ts +6 -1
  21. package/lib/server/index.ts +19 -0
  22. package/lib/server/routes/api/core-routes.ts +18 -0
  23. package/lib/server/routes/api/cssAudit.test.ts +101 -0
  24. package/lib/server/routes/api/cssAudit.ts +138 -0
  25. package/lib/server/routes/api/deMirror.test.ts +105 -0
  26. package/lib/server/routes/api/deMirror.ts +127 -0
  27. package/lib/server/routes/api/pages.ts +2 -2
  28. package/lib/server/services/componentService.test.ts +43 -0
  29. package/lib/server/services/componentService.ts +49 -12
  30. package/lib/server/services/pageService.test.ts +15 -0
  31. package/lib/server/services/pageService.ts +44 -22
  32. package/lib/server/ssr/htmlGenerator.test.ts +29 -0
  33. package/lib/server/ssr/htmlGenerator.ts +83 -3
  34. package/lib/server/themeCssCodec.test.ts +67 -0
  35. package/lib/server/themeCssCodec.ts +67 -5
  36. package/lib/server/webflow/templateWrapper.ts +54 -0
  37. package/lib/shared/cssGeneration.test.ts +28 -0
  38. package/lib/shared/interfaces/contentProvider.ts +9 -0
  39. package/lib/shared/types/cms.ts +1 -0
  40. package/lib/shared/utilityClassConfig.ts +2 -0
  41. package/lib/shared/utilityClassMapper.test.ts +53 -0
  42. package/lib/shared/utilityClassNames.ts +74 -0
  43. package/lib/shared/utils/fileUtils.ts +11 -0
  44. package/lib/shared/validation/schemas.ts +1 -0
  45. package/lib/shared/viewportUnits.test.ts +34 -1
  46. package/lib/shared/viewportUnits.ts +43 -0
  47. package/package.json +3 -1
  48. package/vite.config.ts +4 -1
  49. package/dist/chunks/chunk-LOZL5HOF.js.map +0 -7
  50. /package/dist/chunks/{chunk-3JXK2QFU.js.map → chunk-ZEDLSHYQ.js.map} +0 -0
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from 'bun:test';
2
- import { rewriteViewportUnits } from './viewportUnits';
2
+ import { rewriteViewportUnits, rewriteViewportUnitsInStylesheet } from './viewportUnits';
3
3
 
4
4
  describe('rewriteViewportUnits', () => {
5
5
  it('rewrites basic vh values in declarations', () => {
@@ -102,3 +102,36 @@ describe('rewriteViewportUnits', () => {
102
102
  expect(rewriteViewportUnits('top: 0vh;')).toBe('top: calc(var(--design-vh, 1vh) * 0);');
103
103
  });
104
104
  });
105
+
106
+ describe('rewriteViewportUnitsInStylesheet', () => {
107
+ it('preserves an escaped selector carrying a DECIMAL viewport unit', () => {
108
+ // `text-[clamp(56px,9.5vw,150px)]` escapes to `…9\.5vw…`; the value-level
109
+ // rewriter matches `.5vw` after the backslash and corrupts the selector, so
110
+ // the browser drops the rule. Stylesheet-scoped rewrite touches only bodies.
111
+ const rule = '.text-\\[clamp\\(56px\\,9\\.5vw\\,150px\\)\\] { font-size: clamp(56px,9.5vw,150px); }';
112
+ expect(rewriteViewportUnitsInStylesheet(rule)).toBe(
113
+ '.text-\\[clamp\\(56px\\,9\\.5vw\\,150px\\)\\] { font-size: clamp(56px,calc(var(--design-vw, 1vw) * 9.5),150px); }',
114
+ );
115
+ // Rationale guard: the raw value rewriter alone corrupts this selector.
116
+ expect(rewriteViewportUnits(rule).split('{')[0]).toContain('calc(');
117
+ });
118
+
119
+ it('preserves an escaped selector when a viewport unit follows a no-space comma', () => {
120
+ const rule = '.p-\\[80px_clamp\\(20px\\,5vw\\,64px\\)\\] { padding: 80px clamp(20px,5vw,64px); }';
121
+ expect(rewriteViewportUnitsInStylesheet(rule)).toBe(
122
+ '.p-\\[80px_clamp\\(20px\\,5vw\\,64px\\)\\] { padding: 80px clamp(20px,calc(var(--design-vw, 1vw) * 5),64px); }',
123
+ );
124
+ });
125
+
126
+ it('rewrites declaration values inside @media blocks while keeping nested selectors literal', () => {
127
+ const sheet = '@media (max-width: 767px) { .h-\\[50dvh\\] { height: 50dvh; } }';
128
+ expect(rewriteViewportUnitsInStylesheet(sheet)).toBe(
129
+ '@media (max-width: 767px) { .h-\\[50dvh\\] { height: calc(var(--design-dvh, 1dvh) * 50); } }',
130
+ );
131
+ });
132
+
133
+ it('is a no-op on empty / brace-free input', () => {
134
+ expect(rewriteViewportUnitsInStylesheet('')).toBe('');
135
+ expect(rewriteViewportUnitsInStylesheet('.a { color: red; }')).toBe('.a { color: red; }');
136
+ });
137
+ });
@@ -53,6 +53,49 @@ export function rewriteViewportUnits(input: string): string {
53
53
  });
54
54
  }
55
55
 
56
+ /**
57
+ * Apply {@link rewriteViewportUnits} to a whole stylesheet, rewriting ONLY the
58
+ * declaration bodies — never the selectors or at-rule preludes.
59
+ *
60
+ * {@link rewriteViewportUnits} is regex-based and can't reliably tell a
61
+ * declaration value's viewport unit from one buried in an *escaped*
62
+ * arbitrary-value class SELECTOR. The `VIEWPORT_UNIT_RE` lookbehind guards the
63
+ * common selector shapes (`.mh-100vh`, `.min-h-[100svh]`), but a class whose
64
+ * arbitrary value carries a viewport unit after an escaped `.` or `,` slips
65
+ * through — e.g. `text-[clamp(56px,9.5vw,150px)]` escapes to
66
+ * `.text-\[clamp\(56px\,9\.5vw\,150px\)\]`, where the `.5vw` sits right after a
67
+ * backslash and the rewriter injects `calc(...)` INTO the selector. The result
68
+ * is invalid CSS the browser drops entirely, so the rule — and the design-mode
69
+ * viewport pin it carries — silently vanishes and the element renders unstyled.
70
+ *
71
+ * Splitting selector text from declaration bodies at brace boundaries removes
72
+ * the ambiguity: every selector / prelude ends at a `{`, every declaration body
73
+ * ends at a `}`, so only body text is handed to the value rewriter. Correct
74
+ * through `@media` nesting too (a nested rule's selector still ends at its own
75
+ * `{`). Assumes braces only ever delimit blocks — true for the generated
76
+ * utility/interactive/theme sheets (none emit literal `{`/`}` inside a value or
77
+ * string). Mirror of meno-astro's `rewriteViewportUnitsInStylesheet` (keep in
78
+ * sync — see packages/astro/lib/integration/viewportUnits.ts).
79
+ */
80
+ export function rewriteViewportUnitsInStylesheet(css: string): string {
81
+ if (!css) return css;
82
+ let out = '';
83
+ let seg = '';
84
+ for (let i = 0; i < css.length; i++) {
85
+ const ch = css[i];
86
+ if (ch === '{') {
87
+ out += seg + ch; // `seg` is a selector / at-rule prelude — keep it literal
88
+ seg = '';
89
+ } else if (ch === '}') {
90
+ out += rewriteViewportUnits(seg) + ch; // `seg` is a declaration body — rewrite its values
91
+ seg = '';
92
+ } else {
93
+ seg += ch;
94
+ }
95
+ }
96
+ return out + seg; // trailing text outside any block — never a declaration value
97
+ }
98
+
56
99
  /**
57
100
  * Names of the CSS custom properties the runtime sets/clears to switch
58
101
  * between design-mode (stable px) and page-mode (no override, vh = actual
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "meno-core",
3
- "version": "1.1.3",
3
+ "version": "1.1.4",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "meno": "./dist/bin/cli.js"
@@ -23,6 +23,7 @@
23
23
  },
24
24
  "dependencies": {
25
25
  "@emotion/css": "^11.13.5",
26
+ "css-tree": "^3.2.1",
26
27
  "esbuild": "^0.25.0",
27
28
  "isomorphic-dompurify": "^2.32.0",
28
29
  "jsep": "^1.4.0",
@@ -33,6 +34,7 @@
33
34
  "zod": "^3.22.4"
34
35
  },
35
36
  "devDependencies": {
37
+ "@types/css-tree": "^2.3.11",
36
38
  "@types/markdown-it": "^14.1.2",
37
39
  "@types/react": "^18.2.0",
38
40
  "@types/react-dom": "^18.2.0",
package/vite.config.ts CHANGED
@@ -19,7 +19,10 @@ export default defineConfig({
19
19
  'react',
20
20
  'react-dom',
21
21
  'bun',
22
- // Node.js built-ins (server code)
22
+ // Node.js built-ins (server code) — bare and node:-prefixed specifiers.
23
+ // The regex covers all `node:`-prefixed imports (e.g. `node:path`) so they
24
+ // stay external instead of being browser-stubbed by Vite.
25
+ /^node:/,
23
26
  'path',
24
27
  'fs',
25
28
  'fs/promises',