meno-core 1.0.53 → 1.0.54
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/.claude/settings.local.json +1 -3
- package/bin/cli.ts +48 -57
- package/build-astro.ts +131 -113
- package/build-next.ts +109 -96
- package/build-static.test.ts +39 -10
- package/build-static.ts +120 -122
- package/dist/bin/cli.js +34 -38
- package/dist/bin/cli.js.map +2 -2
- package/dist/build-static.js +12 -11
- package/dist/chunks/chunk-2AR55GYH.js +42 -0
- package/dist/chunks/chunk-2AR55GYH.js.map +7 -0
- package/dist/chunks/{chunk-IGYR22T6.js → chunk-2FN4UOVO.js} +512 -239
- package/dist/chunks/chunk-2FN4UOVO.js.map +7 -0
- package/dist/chunks/chunk-3XER4E5W.js +168 -0
- package/dist/chunks/chunk-3XER4E5W.js.map +7 -0
- package/dist/chunks/{chunk-X754AHS5.js → chunk-5ETZFREW.js} +9 -12
- package/dist/chunks/chunk-5ETZFREW.js.map +7 -0
- package/dist/chunks/{chunk-2MHDV5BF.js → chunk-7E4IF5L7.js} +15 -21
- package/dist/chunks/chunk-7E4IF5L7.js.map +7 -0
- package/dist/chunks/{chunk-JGWFTO6P.js → chunk-7HWQUVTU.js} +1578 -1349
- package/dist/chunks/chunk-7HWQUVTU.js.map +7 -0
- package/dist/chunks/{chunk-O3NAGJP4.js → chunk-AE3QK5QW.js} +110 -21
- package/dist/chunks/chunk-AE3QK5QW.js.map +7 -0
- package/dist/chunks/{chunk-GZHGVVW3.js → chunk-F6KTJYGV.js} +7 -7
- package/dist/chunks/chunk-F6KTJYGV.js.map +7 -0
- package/dist/chunks/{chunk-WQFG7PAH.js → chunk-FZITJSSS.js} +2 -6
- package/dist/chunks/chunk-FZITJSSS.js.map +7 -0
- package/dist/chunks/{chunk-QB2LNO4W.js → chunk-GSYYA5GX.js} +2 -2
- package/dist/chunks/chunk-GSYYA5GX.js.map +7 -0
- package/dist/chunks/{chunk-YBLHKYFF.js → chunk-HIZMY3EP.js} +1 -1
- package/dist/chunks/chunk-HIZMY3EP.js.map +7 -0
- package/dist/chunks/{chunk-AZQYF6KE.js → chunk-I2WEGYA7.js} +41 -176
- package/dist/chunks/chunk-I2WEGYA7.js.map +7 -0
- package/dist/chunks/{chunk-I7YIGZXT.js → chunk-JNO3CNLJ.js} +6 -9
- package/dist/chunks/chunk-JNO3CNLJ.js.map +7 -0
- package/dist/chunks/{chunk-UB44F4Z2.js → chunk-NVRBTSQG.js} +2 -4
- package/dist/chunks/chunk-NVRBTSQG.js.map +7 -0
- package/dist/chunks/{chunk-JGP5A3Y5.js → chunk-Q4OBWKXG.js} +40 -33
- package/dist/chunks/chunk-Q4OBWKXG.js.map +7 -0
- package/dist/chunks/{chunk-R6XHAFBF.js → chunk-QTE32Y53.js} +250 -242
- package/dist/chunks/chunk-QTE32Y53.js.map +7 -0
- package/dist/chunks/{chunk-H3GJ4H2U.js → chunk-STDY3OVM.js} +214 -85
- package/dist/chunks/chunk-STDY3OVM.js.map +7 -0
- package/dist/chunks/configService-PRJZF7Y6.js +14 -0
- package/dist/chunks/{constants-STK2YBIW.js → constants-KIQEYMAM.js} +2 -2
- package/dist/chunks/{fs-JGINUXGL.js → fs-ZI5JEU7V.js} +2 -2
- package/dist/entries/server-router.js +14 -19
- package/dist/entries/server-router.js.map +2 -2
- package/dist/lib/client/index.js +638 -332
- package/dist/lib/client/index.js.map +2 -2
- package/dist/lib/server/index.js +177 -235
- package/dist/lib/server/index.js.map +2 -2
- package/dist/lib/shared/index.js +80 -44
- package/dist/lib/shared/index.js.map +2 -2
- package/dist/lib/shared/richtext/index.js +1 -1
- package/dist/lib/test-utils/index.js +38 -60
- package/dist/lib/test-utils/index.js.map +2 -2
- package/entries/client-router.tsx +10 -8
- package/entries/server-router.tsx +1 -7
- package/lib/client/ClientInitializer.ts +8 -8
- package/lib/client/ErrorBoundary.test.tsx +146 -143
- package/lib/client/ErrorBoundary.tsx +175 -127
- package/lib/client/componentRegistry.test.ts +96 -108
- package/lib/client/componentRegistry.ts +1 -2
- package/lib/client/contexts/ThemeContext.tsx +3 -2
- package/lib/client/core/ComponentBuilder.test.ts +513 -560
- package/lib/client/core/ComponentBuilder.ts +318 -146
- package/lib/client/core/ComponentRenderer.test.tsx +1 -2
- package/lib/client/core/ComponentRenderer.tsx +46 -33
- package/lib/client/core/builders/embedBuilder.ts +241 -53
- package/lib/client/core/builders/linkBuilder.ts +71 -44
- package/lib/client/core/builders/linkNodeBuilder.ts +78 -53
- package/lib/client/core/builders/listBuilder.ts +115 -86
- package/lib/client/core/builders/localeListBuilder.ts +95 -60
- package/lib/client/core/builders/types.ts +5 -5
- package/lib/client/core/cmsTemplateProcessor.ts +7 -7
- package/lib/client/elementRegistry.ts +3 -3
- package/lib/client/fontFamiliesService.test.ts +2 -10
- package/lib/client/fontFamiliesService.ts +3 -3
- package/lib/client/hmr/HMRManager.tsx +8 -0
- package/lib/client/hmrCssReload.ts +14 -8
- package/lib/client/hmrWebSocket.ts +9 -14
- package/lib/client/hooks/useColorVariables.test.ts +21 -21
- package/lib/client/hooks/useColorVariables.ts +12 -10
- package/lib/client/hooks/usePropertyAutocomplete.ts +3 -5
- package/lib/client/hooks/useVariables.ts +4 -4
- package/lib/client/hydration/HydrationUtils.test.ts +24 -25
- package/lib/client/hydration/HydrationUtils.ts +3 -4
- package/lib/client/i18nConfigService.test.ts +2 -7
- package/lib/client/i18nConfigService.ts +2 -2
- package/lib/client/meno-filter/MenoFilter.test.ts +19 -21
- package/lib/client/meno-filter/MenoFilter.ts +5 -9
- package/lib/client/meno-filter/bindings.ts +15 -40
- package/lib/client/meno-filter/init.ts +1 -1
- package/lib/client/meno-filter/renderer.ts +23 -29
- package/lib/client/meno-filter/script.generated.ts +1 -3
- package/lib/client/meno-filter/ui.ts +3 -5
- package/lib/client/meno-filter/updates.ts +15 -21
- package/lib/client/navigation.test.ts +159 -159
- package/lib/client/navigation.ts +0 -1
- package/lib/client/responsiveStyleResolver.test.ts +230 -228
- package/lib/client/responsiveStyleResolver.ts +13 -16
- package/lib/client/routing/RouteLoader.test.ts +23 -24
- package/lib/client/routing/RouteLoader.ts +22 -35
- package/lib/client/routing/Router.tsx +31 -3
- package/lib/client/scripts/ScriptExecutor.test.ts +157 -158
- package/lib/client/scripts/ScriptExecutor.ts +15 -33
- package/lib/client/services/PrefetchService.test.ts +2 -2
- package/lib/client/services/PrefetchService.ts +10 -24
- package/lib/client/styleProcessor.test.ts +9 -9
- package/lib/client/styleProcessor.ts +18 -15
- package/lib/client/styles/StyleInjector.test.ts +122 -115
- package/lib/client/styles/StyleInjector.ts +9 -6
- package/lib/client/styles/UtilityClassCollector.ts +19 -26
- package/lib/client/styles/cspNonce.test.ts +2 -5
- package/lib/client/templateEngine.test.ts +554 -482
- package/lib/client/templateEngine.ts +200 -64
- package/lib/client/theme.ts +0 -1
- package/lib/client/utils/toast.ts +0 -1
- package/lib/server/__integration__/api-routes.test.ts +8 -4
- package/lib/server/__integration__/cms-integration.test.ts +1 -4
- package/lib/server/__integration__/server-lifecycle.test.ts +2 -5
- package/lib/server/__integration__/ssr-rendering.test.ts +47 -37
- package/lib/server/__integration__/static-assets.test.ts +1 -1
- package/lib/server/__integration__/test-helpers.ts +84 -70
- package/lib/server/ab/generateFunctions.ts +12 -10
- package/lib/server/astro/cmsPageEmitter.ts +23 -38
- package/lib/server/astro/componentEmitter.ts +25 -36
- package/lib/server/astro/cssCollector.ts +10 -26
- package/lib/server/astro/nodeToAstro.test.ts +1750 -30
- package/lib/server/astro/nodeToAstro.ts +198 -187
- package/lib/server/astro/normalizeOrphanTemplateProps.test.ts +105 -109
- package/lib/server/astro/normalizeOrphanTemplateProps.ts +4 -12
- package/lib/server/astro/pageEmitter.ts +9 -13
- package/lib/server/astro/tailwindMapper.test.ts +10 -37
- package/lib/server/astro/tailwindMapper.ts +33 -40
- package/lib/server/astro/templateTransformer.ts +14 -17
- package/lib/server/createServer.ts +5 -17
- package/lib/server/cssGenerator.test.ts +35 -44
- package/lib/server/cssGenerator.ts +6 -17
- package/lib/server/fileWatcher.test.ts +124 -10
- package/lib/server/fileWatcher.ts +124 -118
- package/lib/server/index.ts +7 -1
- package/lib/server/jsonLoader.test.ts +39 -2
- package/lib/server/jsonLoader.ts +33 -31
- package/lib/server/middleware/cors.test.ts +20 -20
- package/lib/server/middleware/cors.ts +28 -4
- package/lib/server/middleware/errorHandler.test.ts +5 -3
- package/lib/server/middleware/errorHandler.ts +3 -8
- package/lib/server/middleware/index.ts +0 -1
- package/lib/server/middleware/logger.test.ts +7 -5
- package/lib/server/middleware/logger.ts +10 -22
- package/lib/server/pageCache.test.ts +76 -77
- package/lib/server/pageCache.ts +0 -1
- package/lib/server/projectContext.ts +4 -3
- package/lib/server/providers/fileSystemCMSProvider.test.ts +124 -95
- package/lib/server/providers/fileSystemCMSProvider.ts +35 -20
- package/lib/server/providers/fileSystemPageProvider.test.ts +84 -0
- package/lib/server/providers/fileSystemPageProvider.ts +31 -12
- package/lib/server/routes/api/cms.test.ts +26 -14
- package/lib/server/routes/api/cms.ts +9 -14
- package/lib/server/routes/api/components.ts +30 -34
- package/lib/server/routes/api/config.ts +0 -1
- package/lib/server/routes/api/core-routes.ts +47 -74
- package/lib/server/routes/api/functions.ts +8 -16
- package/lib/server/routes/api/index.ts +3 -6
- package/lib/server/routes/api/pages.ts +8 -11
- package/lib/server/routes/api/shared.test.ts +1 -1
- package/lib/server/routes/api/shared.ts +12 -9
- package/lib/server/routes/api/variables.test.ts +1 -3
- package/lib/server/routes/api/variables.ts +1 -1
- package/lib/server/routes/index.ts +23 -26
- package/lib/server/routes/pages.ts +34 -29
- package/lib/server/routes/static.ts +16 -4
- package/lib/server/runtime/bundler.ts +47 -32
- package/lib/server/runtime/fs.ts +3 -13
- package/lib/server/runtime/httpServer.ts +5 -9
- package/lib/server/services/ColorService.ts +32 -27
- package/lib/server/services/EnumService.test.ts +2 -6
- package/lib/server/services/EnumService.ts +7 -2
- package/lib/server/services/VariableService.test.ts +1 -5
- package/lib/server/services/VariableService.ts +6 -1
- package/lib/server/services/cmsService.test.ts +116 -78
- package/lib/server/services/cmsService.ts +24 -54
- package/lib/server/services/componentService.test.ts +122 -36
- package/lib/server/services/componentService.ts +54 -38
- package/lib/server/services/configService.test.ts +9 -31
- package/lib/server/services/configService.ts +20 -27
- package/lib/server/services/fileWatcherService.ts +40 -6
- package/lib/server/services/index.ts +0 -1
- package/lib/server/services/pageService.test.ts +1 -3
- package/lib/server/services/pageService.ts +14 -13
- package/lib/server/ssr/attributeBuilder.ts +16 -6
- package/lib/server/ssr/buildErrorOverlay.ts +11 -12
- package/lib/server/ssr/clientDataInjector.ts +7 -21
- package/lib/server/ssr/cmsSSRProcessor.ts +3 -6
- package/lib/server/ssr/cssCollector.ts +1 -1
- package/lib/server/ssr/errorOverlay.test.ts +1 -1
- package/lib/server/ssr/errorOverlay.ts +3 -9
- package/lib/server/ssr/htmlGenerator.nonce.test.ts +3 -9
- package/lib/server/ssr/htmlGenerator.test.ts +120 -43
- package/lib/server/ssr/htmlGenerator.ts +120 -85
- package/lib/server/ssr/imageMetadata.test.ts +3 -1
- package/lib/server/ssr/imageMetadata.ts +25 -19
- package/lib/server/ssr/jsCollector.test.ts +3 -13
- package/lib/server/ssr/jsCollector.ts +3 -8
- package/lib/server/ssr/liveReloadIntegration.test.ts +69 -28
- package/lib/server/ssr/metaTagGenerator.ts +2 -2
- package/lib/server/ssr/ssrRenderer.branches.test.ts +1103 -0
- package/lib/server/ssr/ssrRenderer.test.ts +196 -246
- package/lib/server/ssr/ssrRenderer.ts +609 -225
- package/lib/server/ssrRenderer.test.ts +1044 -950
- package/lib/server/utils/jsonLineMapper.test.ts +28 -28
- package/lib/server/utils/jsonLineMapper.ts +1 -1
- package/lib/server/validateStyleCoverage.ts +18 -20
- package/lib/server/webflow/buildWebflow.ts +41 -53
- package/lib/server/webflow/nodeToWebflow.test.ts +150 -218
- package/lib/server/webflow/nodeToWebflow.ts +195 -258
- package/lib/server/webflow/styleMapper.test.ts +15 -56
- package/lib/server/webflow/styleMapper.ts +33 -41
- package/lib/server/webflow/types.ts +1 -8
- package/lib/server/websocketManager.ts +16 -19
- package/lib/shared/attributeNodeUtils.test.ts +15 -15
- package/lib/shared/attributeNodeUtils.ts +5 -12
- package/lib/shared/breakpoints.ts +4 -11
- package/lib/shared/cmsQueryParser.test.ts +50 -42
- package/lib/shared/cmsQueryParser.ts +49 -31
- package/lib/shared/colorVariableUtils.test.ts +5 -5
- package/lib/shared/colorVariableUtils.ts +3 -8
- package/lib/shared/componentRefs.ts +1 -5
- package/lib/shared/constants.test.ts +3 -3
- package/lib/shared/constants.ts +4 -8
- package/lib/shared/cssGeneration.test.ts +262 -144
- package/lib/shared/cssGeneration.ts +189 -516
- package/lib/shared/cssNamedColors.ts +152 -30
- package/lib/shared/cssProperties.test.ts +5 -6
- package/lib/shared/cssProperties.ts +296 -112
- package/lib/shared/elementClassName.test.ts +109 -109
- package/lib/shared/elementClassName.ts +1 -1
- package/lib/shared/elementUtils.ts +12 -16
- package/lib/shared/errorLogger.ts +2 -10
- package/lib/shared/errors.test.ts +2 -13
- package/lib/shared/errors.ts +2 -8
- package/lib/shared/expressionEvaluator.test.ts +119 -0
- package/lib/shared/expressionEvaluator.ts +43 -24
- package/lib/shared/fontLoader.test.ts +19 -5
- package/lib/shared/friendlyError.ts +2 -3
- package/lib/shared/gradientUtils.test.ts +1 -5
- package/lib/shared/gradientUtils.ts +2 -6
- package/lib/shared/hrefRefs.ts +2 -10
- package/lib/shared/i18n.test.ts +1 -1
- package/lib/shared/i18n.ts +13 -34
- package/lib/shared/index.ts +4 -0
- package/lib/shared/inlineSvgStyleRules.ts +2 -4
- package/lib/shared/interactiveStyleMappings.test.ts +11 -33
- package/lib/shared/interactiveStyleMappings.ts +9 -16
- package/lib/shared/interactiveStyles.test.ts +165 -188
- package/lib/shared/interfaces/contentProvider.ts +1 -1
- package/lib/shared/itemTemplateUtils.test.ts +6 -12
- package/lib/shared/itemTemplateUtils.ts +19 -35
- package/lib/shared/jsonRepair.ts +8 -2
- package/lib/shared/libraryLoader.test.ts +15 -49
- package/lib/shared/libraryLoader.ts +7 -22
- package/lib/shared/netlifyLocale404.test.ts +179 -0
- package/lib/shared/netlifyLocale404.ts +110 -0
- package/lib/shared/nodeUtils.test.ts +24 -16
- package/lib/shared/nodeUtils.ts +49 -19
- package/lib/shared/pathArrayUtils.test.ts +1 -2
- package/lib/shared/pathArrayUtils.ts +1 -1
- package/lib/shared/pathSecurity.ts +1 -1
- package/lib/shared/pathUtils.test.ts +4 -6
- package/lib/shared/pathUtils.ts +42 -48
- package/lib/shared/paths/Path.test.ts +2 -2
- package/lib/shared/paths/Path.ts +0 -1
- package/lib/shared/paths/PathConverter.test.ts +1 -1
- package/lib/shared/paths/PathConverter.ts +14 -17
- package/lib/shared/paths/PathUtils.ts +9 -10
- package/lib/shared/paths/PathValidator.test.ts +2 -15
- package/lib/shared/paths/PathValidator.ts +11 -9
- package/lib/shared/paths/index.ts +1 -2
- package/lib/shared/propResolver.test.ts +240 -244
- package/lib/shared/propResolver.ts +14 -25
- package/lib/shared/pxToRem.test.ts +7 -6
- package/lib/shared/pxToRem.ts +2 -5
- package/lib/shared/registry/BaseNodeTypeRegistry.test.ts +9 -5
- package/lib/shared/registry/ClientRegistry.ts +0 -1
- package/lib/shared/registry/ComponentRegistry.test.ts +43 -29
- package/lib/shared/registry/ComponentRegistry.ts +9 -11
- package/lib/shared/registry/NodeTypeDefinition.ts +15 -8
- package/lib/shared/registry/RegistryManager.ts +1 -2
- package/lib/shared/registry/SSRRegistry.ts +0 -1
- package/lib/shared/registry/createNodeType.ts +7 -9
- package/lib/shared/registry/defineNodeType.ts +2 -6
- package/lib/shared/registry/index.ts +0 -1
- package/lib/shared/registry/nodeTypes/ComponentInstanceNodeType.ts +14 -15
- package/lib/shared/registry/nodeTypes/EmbedNodeType.ts +18 -11
- package/lib/shared/registry/nodeTypes/HtmlNodeType.ts +47 -18
- package/lib/shared/registry/nodeTypes/LinkNodeType.ts +21 -19
- package/lib/shared/registry/nodeTypes/ListNodeType.ts +78 -74
- package/lib/shared/registry/nodeTypes/LocaleListNodeType.ts +27 -21
- package/lib/shared/registry/nodeTypes/SlotMarkerType.ts +6 -7
- package/lib/shared/registry/nodeTypes/index.ts +10 -2
- package/lib/shared/responsiveScaling.test.ts +15 -31
- package/lib/shared/responsiveScaling.ts +55 -37
- package/lib/shared/responsiveStyleUtils.ts +11 -13
- package/lib/shared/richtext/htmlToTiptap.test.ts +23 -14
- package/lib/shared/richtext/htmlToTiptap.ts +1 -3
- package/lib/shared/richtext/tiptapToHtml.test.ts +5 -6
- package/lib/shared/richtext/types.ts +1 -8
- package/lib/shared/slugTranslator.test.ts +13 -13
- package/lib/shared/slugTranslator.ts +12 -16
- package/lib/shared/slugify.ts +9 -15
- package/lib/shared/styleNodeUtils.test.ts +8 -8
- package/lib/shared/styleNodeUtils.ts +5 -10
- package/lib/shared/styleUtils.test.ts +87 -61
- package/lib/shared/styleUtils.ts +5 -6
- package/lib/shared/themeDefaults.test.ts +11 -11
- package/lib/shared/themeDefaults.ts +3 -4
- package/lib/shared/tree/PathBuilder.test.ts +62 -138
- package/lib/shared/tree/PathBuilder.ts +49 -39
- package/lib/shared/treePathUtils.test.ts +2 -10
- package/lib/shared/treePathUtils.ts +54 -59
- package/lib/shared/types/api.ts +1 -2
- package/lib/shared/types/cms.ts +25 -21
- package/lib/shared/types/comment.ts +49 -16
- package/lib/shared/types/components.ts +27 -25
- package/lib/shared/types/errors.test.ts +1 -6
- package/lib/shared/types/errors.ts +3 -7
- package/lib/shared/types/experiments.ts +28 -28
- package/lib/shared/types/index.ts +2 -2
- package/lib/shared/types/styles.ts +0 -1
- package/lib/shared/types/variables.test.ts +4 -13
- package/lib/shared/types/variables.ts +48 -27
- package/lib/shared/types.ts +1 -2
- package/lib/shared/utilityClassConfig.ts +648 -321
- package/lib/shared/utilityClassMapper.test.ts +203 -92
- package/lib/shared/utilityClassMapper.ts +188 -246
- package/lib/shared/utilityClassNames.ts +326 -0
- package/lib/shared/utils.test.ts +2 -10
- package/lib/shared/utils.ts +19 -10
- package/lib/shared/validation/cmsValidators.ts +2 -1
- package/lib/shared/validation/commentValidators.test.ts +53 -0
- package/lib/shared/validation/commentValidators.ts +12 -1
- package/lib/shared/validation/propValidator.test.ts +18 -20
- package/lib/shared/validation/propValidator.ts +12 -17
- package/lib/shared/validation/schemas.test.ts +24 -33
- package/lib/shared/validation/schemas.ts +469 -344
- package/lib/shared/validation/validators.test.ts +1 -6
- package/lib/shared/validation/validators.ts +89 -68
- package/lib/shared/viewportUnits.integration.test.ts +14 -10
- package/lib/shared/viewportUnits.test.ts +11 -23
- package/lib/test-utils/dom-setup.ts +1 -1
- package/lib/test-utils/factories/ConsoleMockFactory.ts +3 -7
- package/lib/test-utils/factories/DomMockFactory.ts +7 -19
- package/lib/test-utils/factories/EventMockFactory.ts +7 -13
- package/lib/test-utils/factories/FetchMockFactory.ts +39 -57
- package/lib/test-utils/factories/ServerMockFactory.ts +5 -9
- package/lib/test-utils/factories/StoreMockFactory.ts +14 -25
- package/lib/test-utils/fixtures.ts +45 -45
- package/lib/test-utils/helpers/asyncHelpers.test.ts +15 -18
- package/lib/test-utils/helpers/asyncHelpers.ts +11 -20
- package/lib/test-utils/helpers.ts +1 -5
- package/lib/test-utils/index.ts +0 -4
- package/lib/test-utils/mockFactories.ts +12 -18
- package/lib/test-utils/mocks.ts +4 -2
- package/package.json +1 -1
- package/scripts/build-meno-filter.ts +1 -4
- package/vite.config.ts +4 -4
- package/dist/chunks/chunk-2MHDV5BF.js.map +0 -7
- package/dist/chunks/chunk-AZQYF6KE.js.map +0 -7
- package/dist/chunks/chunk-GZHGVVW3.js.map +0 -7
- package/dist/chunks/chunk-H3GJ4H2U.js.map +0 -7
- package/dist/chunks/chunk-I7YIGZXT.js.map +0 -7
- package/dist/chunks/chunk-IGYR22T6.js.map +0 -7
- package/dist/chunks/chunk-JGP5A3Y5.js.map +0 -7
- package/dist/chunks/chunk-JGWFTO6P.js.map +0 -7
- package/dist/chunks/chunk-O3NAGJP4.js.map +0 -7
- package/dist/chunks/chunk-QB2LNO4W.js.map +0 -7
- package/dist/chunks/chunk-R6XHAFBF.js.map +0 -7
- package/dist/chunks/chunk-UB44F4Z2.js.map +0 -7
- package/dist/chunks/chunk-WQFG7PAH.js.map +0 -7
- package/dist/chunks/chunk-X754AHS5.js.map +0 -7
- package/dist/chunks/chunk-YBLHKYFF.js.map +0 -7
- package/dist/chunks/configService-R3OGU2UD.js +0 -13
- /package/dist/chunks/{configService-R3OGU2UD.js.map → configService-PRJZF7Y6.js.map} +0 -0
- /package/dist/chunks/{constants-STK2YBIW.js.map → constants-KIQEYMAM.js.map} +0 -0
- /package/dist/chunks/{fs-JGINUXGL.js.map → fs-ZI5JEU7V.js.map} +0 -0
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tailwind-style utility class name parsing & value encoding.
|
|
3
|
+
*
|
|
4
|
+
* Shared by the forward mapper (utilityClassMapper.ts) and the CSS generator
|
|
5
|
+
* (cssGeneration.ts) so class names encode and decode identically everywhere.
|
|
6
|
+
*
|
|
7
|
+
* Class name grammar (after stripping an optional `breakpoint:` variant):
|
|
8
|
+
* - static utilities: `flex`, `hidden`, `justify-center`, `uppercase`, …
|
|
9
|
+
* - presets: `shadow-2`, `bg-gradient-1`, `border-preset-0`
|
|
10
|
+
* - arbitrary values: `p-[40px]`, `w-[50%]`, `m-[92px_0]` (spaces → `_`)
|
|
11
|
+
* - CSS variables: `bg-(--primary)`, `text-(length:--font-size-lg)`
|
|
12
|
+
* - named keywords: `w-full`, `h-screen`, `m-auto`, `rounded-full`
|
|
13
|
+
* - bare numbers: `z-10`, `order-2`, `opacity-50` (= 0.5)
|
|
14
|
+
* - arbitrary properties: `[clip-path:circle(50%)]`, `[background:…]`
|
|
15
|
+
* - hash fallback: `pt-h1y9pr6i` (value lives in the style-value registry)
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
prefixToCSSProperty,
|
|
20
|
+
staticUtilityReverse,
|
|
21
|
+
presetClassReverse,
|
|
22
|
+
keywordValues,
|
|
23
|
+
genericKeywordRoots,
|
|
24
|
+
integerValueRoots,
|
|
25
|
+
propertyOrder,
|
|
26
|
+
} from './utilityClassConfig';
|
|
27
|
+
import { isCssNamedColor } from './cssNamedColors';
|
|
28
|
+
|
|
29
|
+
/** Class roots sorted longest-first so `border-t-…` wins over `border-…`. */
|
|
30
|
+
export const SORTED_ROOTS: readonly string[] = Object.keys(prefixToCSSProperty).sort((a, b) => b.length - a.length);
|
|
31
|
+
|
|
32
|
+
/** Convert a camelCase style property to its kebab-case CSS name. */
|
|
33
|
+
export function camelToKebab(prop: string): string {
|
|
34
|
+
return prop.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// kebab-case CSS property → camelCase style property, for all known properties.
|
|
38
|
+
const kebabToCamelMap: Record<string, string> = (() => {
|
|
39
|
+
const map: Record<string, string> = {};
|
|
40
|
+
for (const prop of propertyOrder) {
|
|
41
|
+
map[camelToKebab(prop)] = prop;
|
|
42
|
+
}
|
|
43
|
+
return map;
|
|
44
|
+
})();
|
|
45
|
+
|
|
46
|
+
/** Convert a kebab-case CSS property back to its camelCase style property. */
|
|
47
|
+
export function cssPropertyToCamel(property: string): string {
|
|
48
|
+
return kebabToCamelMap[property] ?? property.replace(/-([a-z])/g, (_, c: string) => c.toUpperCase());
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Roots that decode to a different camelCase property than their kebab CSS
|
|
53
|
+
* property suggests (px → paddingInline, not padding).
|
|
54
|
+
*/
|
|
55
|
+
export const ROOT_CAMEL_OVERRIDES: Record<string, string> = {
|
|
56
|
+
px: 'paddingInline',
|
|
57
|
+
py: 'paddingBlock',
|
|
58
|
+
mx: 'marginInline',
|
|
59
|
+
my: 'marginBlock',
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
// Arbitrary value encoding (Tailwind bracket convention: spaces → underscores)
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Apply `transform` to every part of `input` that is NOT inside a `url(...)`
|
|
68
|
+
* segment. Tailwind preserves underscores inside url() — so do we, in both
|
|
69
|
+
* encode and decode directions.
|
|
70
|
+
*/
|
|
71
|
+
function transformOutsideUrl(input: string, transform: (segment: string) => string): string {
|
|
72
|
+
let result = '';
|
|
73
|
+
let i = 0;
|
|
74
|
+
const lower = input.toLowerCase();
|
|
75
|
+
while (i < input.length) {
|
|
76
|
+
const idx = lower.indexOf('url(', i);
|
|
77
|
+
if (idx === -1) {
|
|
78
|
+
result += transform(input.slice(i));
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
result += transform(input.slice(i, idx));
|
|
82
|
+
// Copy the url(...) segment verbatim up to its matching close paren
|
|
83
|
+
let depth = 0;
|
|
84
|
+
let j = idx + 3; // position of '('
|
|
85
|
+
for (; j < input.length; j++) {
|
|
86
|
+
if (input[j] === '(') depth++;
|
|
87
|
+
else if (input[j] === ')') {
|
|
88
|
+
depth--;
|
|
89
|
+
if (depth === 0) {
|
|
90
|
+
j++;
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
result += input.slice(idx, j);
|
|
96
|
+
i = j;
|
|
97
|
+
}
|
|
98
|
+
return result;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Encode a CSS value for a Tailwind arbitrary-value class: spaces become
|
|
103
|
+
* underscores (`92px 0` → `92px_0`). Returns null when the value can't be
|
|
104
|
+
* encoded reversibly (literal underscores outside url(), quotes, brackets,
|
|
105
|
+
* backslashes, or whitespace inside url()) — callers fall back to a
|
|
106
|
+
* hash class + style-value registry entry.
|
|
107
|
+
*/
|
|
108
|
+
export function encodeArbitraryValue(value: string): string | null {
|
|
109
|
+
let ok = true;
|
|
110
|
+
const encoded = transformOutsideUrl(value, (segment) => {
|
|
111
|
+
if (/[_"'\\[\]]/.test(segment)) ok = false;
|
|
112
|
+
return segment.replace(/\s+/g, '_');
|
|
113
|
+
});
|
|
114
|
+
if (!ok) return null;
|
|
115
|
+
// Whitespace/quotes surviving here can only come from inside url(...) —
|
|
116
|
+
// they would break the class attribute or the decode round-trip.
|
|
117
|
+
if (/[\s"'\\[\]]/.test(encoded.replace(/^\[|\]$/g, ''))) return null;
|
|
118
|
+
return encoded;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Decode a Tailwind arbitrary value: underscores → spaces (except inside url()). */
|
|
122
|
+
export function decodeArbitraryValue(encoded: string): string {
|
|
123
|
+
return transformOutsideUrl(encoded, (segment) => segment.replace(/_/g, ' '));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
// Value-type classification (for roots shared by several properties)
|
|
128
|
+
// ---------------------------------------------------------------------------
|
|
129
|
+
|
|
130
|
+
const FONT_SIZE_KEYWORDS = new Set([
|
|
131
|
+
'xx-small',
|
|
132
|
+
'x-small',
|
|
133
|
+
'small',
|
|
134
|
+
'medium',
|
|
135
|
+
'large',
|
|
136
|
+
'x-large',
|
|
137
|
+
'xx-large',
|
|
138
|
+
'xxx-large',
|
|
139
|
+
'larger',
|
|
140
|
+
'smaller',
|
|
141
|
+
]);
|
|
142
|
+
|
|
143
|
+
/** True when a value reads as a color (hex, color function, named color, var()). */
|
|
144
|
+
export function isColorValue(value: string): boolean {
|
|
145
|
+
if (value.startsWith('#') || value.startsWith('var(')) return true;
|
|
146
|
+
if (/^(rgb|rgba|hsl|hsla|oklch|oklab|lab|lch|hwb|color|color-mix)\(/i.test(value)) return true;
|
|
147
|
+
if (value === 'transparent' || value === 'currentColor' || value === 'currentcolor') return true;
|
|
148
|
+
return isCssNamedColor(value);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** True when a `text-…` value reads as a font-size (length/number/keyword). */
|
|
152
|
+
function isFontSizeValue(value: string): boolean {
|
|
153
|
+
if (/^[\d.+-]/.test(value)) return true;
|
|
154
|
+
if (/^(calc|clamp|min|max)\(/i.test(value)) return true;
|
|
155
|
+
return FONT_SIZE_KEYWORDS.has(value);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** True when a `font-…` value reads as a font-weight (numeric). */
|
|
159
|
+
function isFontWeightValue(value: string): boolean {
|
|
160
|
+
return /^\d+(\.\d+)?$/.test(value);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** True when a `bg-…` value reads as a background-image (url()/gradients). */
|
|
164
|
+
function isImageValue(value: string): boolean {
|
|
165
|
+
return /^(url|linear-gradient|radial-gradient|conic-gradient|repeating-linear-gradient|repeating-radial-gradient|repeating-conic-gradient|image-set|cross-fade|element)\(/i.test(
|
|
166
|
+
value,
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** True for a single length token like `2px`, `0.5rem`, `-4px`. */
|
|
171
|
+
function isLengthValue(value: string): boolean {
|
|
172
|
+
return /^-?[\d.]+[a-z%]*$/i.test(value);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Resolve the CSS property for a class root, disambiguating Tailwind's shared
|
|
177
|
+
* roots by value type (`text-[14px]` → font-size, `text-[#fff]` → color, …).
|
|
178
|
+
*/
|
|
179
|
+
export function resolveRootProperty(root: string, value: string): string {
|
|
180
|
+
switch (root) {
|
|
181
|
+
case 'text':
|
|
182
|
+
return isFontSizeValue(value) ? 'font-size' : 'color';
|
|
183
|
+
case 'font':
|
|
184
|
+
return isFontWeightValue(value) ? 'font-weight' : 'font-family';
|
|
185
|
+
case 'bg':
|
|
186
|
+
return isImageValue(value) ? 'background-image' : 'background-color';
|
|
187
|
+
case 'border':
|
|
188
|
+
return !value.includes(' ') && isColorValue(value) ? 'border-color' : 'border';
|
|
189
|
+
case 'outline': {
|
|
190
|
+
if (value.includes(' ') || value === 'none') return 'outline';
|
|
191
|
+
if (isColorValue(value)) return 'outline-color';
|
|
192
|
+
if (isLengthValue(value)) return 'outline-width';
|
|
193
|
+
return 'outline';
|
|
194
|
+
}
|
|
195
|
+
default:
|
|
196
|
+
return prefixToCSSProperty[root] ?? root;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// ---------------------------------------------------------------------------
|
|
201
|
+
// Breakpoint variant prefix (`tablet:p-[24px]`)
|
|
202
|
+
// ---------------------------------------------------------------------------
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Split an optional `breakpoint:` variant prefix off a class name.
|
|
206
|
+
* Arbitrary-property classes (`[grid-area:hero]`) never match — their colon
|
|
207
|
+
* sits inside brackets, and the prefix segment must be a bare identifier.
|
|
208
|
+
*/
|
|
209
|
+
export function splitVariantPrefix(className: string): { breakpoint: string | null; base: string } {
|
|
210
|
+
const match = className.match(/^([a-z][a-z0-9_-]*):(.+)$/);
|
|
211
|
+
if (match) {
|
|
212
|
+
return { breakpoint: match[1], base: match[2] };
|
|
213
|
+
}
|
|
214
|
+
return { breakpoint: null, base: className };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** Build the class variant prefix for a breakpoint name: `tablet` → `tablet:`. */
|
|
218
|
+
export function breakpointClassPrefix(breakpointName: string): string {
|
|
219
|
+
return `${breakpointName.toLowerCase()}:`;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// ---------------------------------------------------------------------------
|
|
223
|
+
// Class name parsing
|
|
224
|
+
// ---------------------------------------------------------------------------
|
|
225
|
+
|
|
226
|
+
export interface ParsedUtilityClass {
|
|
227
|
+
/** kebab-case CSS property the class targets */
|
|
228
|
+
property: string;
|
|
229
|
+
/** Decoded CSS value; null for hash classes (resolve via style-value registry) */
|
|
230
|
+
value: string | null;
|
|
231
|
+
/** The class root (`p`, `text`, `bg`, …); null for statics/presets/arbitrary properties */
|
|
232
|
+
root: string | null;
|
|
233
|
+
kind: 'static' | 'preset' | 'arbitrary' | 'var' | 'keyword' | 'numeric' | 'hash' | 'arbitrary-property';
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const HASH_VALUE_RE = /^h[0-9a-z]+$/;
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Parse a single (unprefixed) utility class name into its CSS property and
|
|
240
|
+
* decoded value. Returns null for class names that aren't Meno utilities.
|
|
241
|
+
*/
|
|
242
|
+
export function parseUtilityClass(className: string): ParsedUtilityClass | null {
|
|
243
|
+
const stat = staticUtilityReverse[className];
|
|
244
|
+
if (stat) {
|
|
245
|
+
return { property: camelToKebab(stat.prop), value: stat.value, root: null, kind: 'static' };
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const preset = presetClassReverse[className];
|
|
249
|
+
if (preset) {
|
|
250
|
+
return { property: camelToKebab(preset.prop), value: preset.value, root: null, kind: 'preset' };
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Arbitrary property: [clip-path:circle(50%)], [background:linear-gradient(…)]
|
|
254
|
+
const arbProp = className.match(/^\[([a-z-]+):(.+)\]$/);
|
|
255
|
+
if (arbProp) {
|
|
256
|
+
return {
|
|
257
|
+
property: arbProp[1],
|
|
258
|
+
value: decodeArbitraryValue(arbProp[2]),
|
|
259
|
+
root: null,
|
|
260
|
+
kind: 'arbitrary-property',
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
for (const root of SORTED_ROOTS) {
|
|
265
|
+
if (!className.startsWith(`${root}-`)) continue;
|
|
266
|
+
const rest = className.slice(root.length + 1);
|
|
267
|
+
if (!rest) continue;
|
|
268
|
+
const parsed = parseRootValue(root, rest);
|
|
269
|
+
if (parsed) return parsed;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function parseRootValue(root: string, rest: string): ParsedUtilityClass | null {
|
|
276
|
+
// Arbitrary value: root-[value]
|
|
277
|
+
if (rest.startsWith('[') && rest.endsWith(']') && rest.length > 2) {
|
|
278
|
+
const value = decodeArbitraryValue(rest.slice(1, -1));
|
|
279
|
+
return { property: resolveRootProperty(root, value), value, root, kind: 'arbitrary' };
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// CSS variable: root-(--name) / root-(length:--name)
|
|
283
|
+
const varMatch = rest.match(/^\((?:([a-z-]+):)?(--[\w-]+)\)$/);
|
|
284
|
+
if (varMatch) {
|
|
285
|
+
const hint = varMatch[1];
|
|
286
|
+
const value = `var(${varMatch[2]})`;
|
|
287
|
+
let property: string;
|
|
288
|
+
if (root === 'text') {
|
|
289
|
+
property = hint === 'length' ? 'font-size' : 'color';
|
|
290
|
+
} else if (root === 'font') {
|
|
291
|
+
// `font-*` is shared by font-weight and font-family; a var value is ambiguous, so the
|
|
292
|
+
// emitter tags weight vars with a `number:` hint (see utilityClassMapper). Default to
|
|
293
|
+
// font-family (the original behavior for an un-hinted var like `font-(--l-ff)`).
|
|
294
|
+
property = hint === 'number' ? 'font-weight' : 'font-family';
|
|
295
|
+
} else {
|
|
296
|
+
property = resolveRootProperty(root, value);
|
|
297
|
+
}
|
|
298
|
+
return { property, value, root, kind: 'var' };
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Named keyword: w-full, m-auto, rounded-full
|
|
302
|
+
const keyword = keywordValues[root]?.[rest];
|
|
303
|
+
if (keyword !== undefined) {
|
|
304
|
+
return { property: resolveRootProperty(root, keyword), value: keyword, root, kind: 'keyword' };
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Generic keyword roots: cursor-pointer, align-middle, origin-center, …
|
|
308
|
+
if (genericKeywordRoots.has(root) && /^[a-z][a-z-]*$/.test(rest)) {
|
|
309
|
+
return { property: resolveRootProperty(root, rest), value: rest, root, kind: 'keyword' };
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Bare numbers: z-10, order-2, flex-2; opacity uses Tailwind's 0–100 scale
|
|
313
|
+
if (integerValueRoots.has(root) && /^-?\d+$/.test(rest)) {
|
|
314
|
+
return { property: prefixToCSSProperty[root], value: rest, root, kind: 'numeric' };
|
|
315
|
+
}
|
|
316
|
+
if (root === 'opacity' && /^\d{1,3}$/.test(rest)) {
|
|
317
|
+
return { property: 'opacity', value: String(Number(rest) / 100), root, kind: 'numeric' };
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Hash fallback: value lives in the style-value registry
|
|
321
|
+
if (HASH_VALUE_RE.test(rest)) {
|
|
322
|
+
return { property: prefixToCSSProperty[root], value: null, root, kind: 'hash' };
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return null;
|
|
326
|
+
}
|
package/lib/shared/utils.test.ts
CHANGED
|
@@ -244,18 +244,10 @@ describe('utils', () => {
|
|
|
244
244
|
|
|
245
245
|
test('should handle complex children array', () => {
|
|
246
246
|
const node = {
|
|
247
|
-
children: [
|
|
248
|
-
{ element: 'div' },
|
|
249
|
-
{ element: 'span' },
|
|
250
|
-
'text',
|
|
251
|
-
],
|
|
247
|
+
children: [{ element: 'div' }, { element: 'span' }, 'text'],
|
|
252
248
|
};
|
|
253
249
|
normalizeChildrenArray(node);
|
|
254
|
-
expect(node.children).toEqual([
|
|
255
|
-
{ element: 'div' },
|
|
256
|
-
{ element: 'span' },
|
|
257
|
-
'text',
|
|
258
|
-
]);
|
|
250
|
+
expect(node.children).toEqual([{ element: 'div' }, { element: 'span' }, 'text']);
|
|
259
251
|
});
|
|
260
252
|
});
|
|
261
253
|
});
|
package/lib/shared/utils.ts
CHANGED
|
@@ -20,7 +20,7 @@ export function deepClone<T>(obj: T): T {
|
|
|
20
20
|
* @param ms - Milliseconds to wait
|
|
21
21
|
*/
|
|
22
22
|
export function wait(ms: number): Promise<void> {
|
|
23
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
23
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
/**
|
|
@@ -50,7 +50,20 @@ export function isEditableElement(): boolean {
|
|
|
50
50
|
// For inputs, only block shortcuts for text-accepting types
|
|
51
51
|
if (activeElement.tagName === 'INPUT') {
|
|
52
52
|
const inputType = (activeElement as HTMLInputElement).type.toLowerCase();
|
|
53
|
-
const textInputTypes = [
|
|
53
|
+
const textInputTypes = [
|
|
54
|
+
'text',
|
|
55
|
+
'password',
|
|
56
|
+
'email',
|
|
57
|
+
'number',
|
|
58
|
+
'search',
|
|
59
|
+
'tel',
|
|
60
|
+
'url',
|
|
61
|
+
'date',
|
|
62
|
+
'time',
|
|
63
|
+
'datetime-local',
|
|
64
|
+
'month',
|
|
65
|
+
'week',
|
|
66
|
+
];
|
|
54
67
|
// Allow shortcuts for: checkbox, radio, button, submit, reset, color, file, range, hidden
|
|
55
68
|
return textInputTypes.includes(inputType);
|
|
56
69
|
}
|
|
@@ -59,10 +72,7 @@ export function isEditableElement(): boolean {
|
|
|
59
72
|
// This handles rich text editors where focus may be on an internal element
|
|
60
73
|
let element: Element | null = activeElement;
|
|
61
74
|
while (element) {
|
|
62
|
-
if (
|
|
63
|
-
element.hasAttribute('contenteditable') ||
|
|
64
|
-
(element as HTMLElement).isContentEditable
|
|
65
|
-
) {
|
|
75
|
+
if (element.hasAttribute('contenteditable') || (element as HTMLElement).isContentEditable) {
|
|
66
76
|
return true;
|
|
67
77
|
}
|
|
68
78
|
element = element.parentElement;
|
|
@@ -75,7 +85,7 @@ export function isEditableElement(): boolean {
|
|
|
75
85
|
* Extract the parent path from a tree path
|
|
76
86
|
* Example: root_0_children_0 -> root_0, root_0_children_0_children_1 -> root_0_children_0
|
|
77
87
|
* Also accepts array paths: [0,0] -> "root_0", [0,0,1] -> "root_0_children_0"
|
|
78
|
-
*
|
|
88
|
+
*
|
|
79
89
|
* @param treePath - The tree path (string or Path array)
|
|
80
90
|
* @param rootPath - The root path constant (default: 'root_0')
|
|
81
91
|
* @returns The parent path, or rootPath if no parent found
|
|
@@ -83,12 +93,12 @@ export function isEditableElement(): boolean {
|
|
|
83
93
|
export function getParentPathFromTreePath(treePath: string | Path, rootPath: string = ROOT_0_STRING): string {
|
|
84
94
|
// Convert to array if needed
|
|
85
95
|
const pathArray = typeof treePath === 'string' ? stringToPath(treePath) : treePath;
|
|
86
|
-
|
|
96
|
+
|
|
87
97
|
// Handle root paths
|
|
88
98
|
if (isRootPathArray(pathArray)) {
|
|
89
99
|
return rootPath;
|
|
90
100
|
}
|
|
91
|
-
|
|
101
|
+
|
|
92
102
|
// Use array operations to get parent path
|
|
93
103
|
const parentArray = getParentPath(pathArray);
|
|
94
104
|
return pathToLegacyString(parentArray);
|
|
@@ -109,4 +119,3 @@ export function normalizeChildrenArray(node: { children?: any[] | any }): void {
|
|
|
109
119
|
}
|
|
110
120
|
// If already an array, no change needed
|
|
111
121
|
}
|
|
112
|
-
|
|
@@ -14,7 +14,8 @@ export const CMS_COLLECTION_ID_REGEX = /^[a-zA-Z0-9_-]+$/;
|
|
|
14
14
|
/**
|
|
15
15
|
* Zod schema for CMS collection ID validation
|
|
16
16
|
*/
|
|
17
|
-
export const CMSCollectionIdSchema = z
|
|
17
|
+
export const CMSCollectionIdSchema = z
|
|
18
|
+
.string()
|
|
18
19
|
.min(1, 'Collection ID is required')
|
|
19
20
|
.regex(CMS_COLLECTION_ID_REGEX, 'Collection ID must contain only letters, numbers, underscores, and hyphens');
|
|
20
21
|
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { describe, test, expect } from 'bun:test';
|
|
2
|
+
import { validateComment } from './commentValidators';
|
|
3
|
+
|
|
4
|
+
/** A minimal valid comment; callers override `anchor` to exercise schema variants. */
|
|
5
|
+
function baseComment(anchor: Record<string, unknown>): Record<string, unknown> {
|
|
6
|
+
return {
|
|
7
|
+
_id: 'c1',
|
|
8
|
+
_filename: 'c1.json',
|
|
9
|
+
_pagePath: 'index',
|
|
10
|
+
_seq: 1,
|
|
11
|
+
_createdAt: '2026-01-01T00:00:00.000Z',
|
|
12
|
+
_updatedAt: '2026-01-01T00:00:00.000Z',
|
|
13
|
+
_commitSha: null,
|
|
14
|
+
anchor,
|
|
15
|
+
status: 'open',
|
|
16
|
+
thread: [
|
|
17
|
+
{
|
|
18
|
+
id: 't1',
|
|
19
|
+
author: { login: 'local' },
|
|
20
|
+
createdAt: '2026-01-01T00:00:00.000Z',
|
|
21
|
+
text: 'hi',
|
|
22
|
+
statusChange: null,
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const pageLevelAnchor = {
|
|
29
|
+
nodePath: [0, 1],
|
|
30
|
+
nodeIdentity: { kind: 'node', name: 'div' },
|
|
31
|
+
offsetXPercent: 0.5,
|
|
32
|
+
offsetYPercent: 0.5,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
describe('validateComment — element anchor identity', () => {
|
|
36
|
+
test('accepts a comment WITHOUT a cmsItemIndexPath (element not in a list)', () => {
|
|
37
|
+
const result = validateComment(baseComment(pageLevelAnchor));
|
|
38
|
+
expect(result.valid).toBe(true);
|
|
39
|
+
if (result.valid) expect(result.data.anchor.cmsItemIndexPath).toBeUndefined();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('accepts and round-trips a comment WITH a cmsItemIndexPath', () => {
|
|
43
|
+
const cmsItemIndexPath = [0, 2];
|
|
44
|
+
const result = validateComment(baseComment({ ...pageLevelAnchor, cmsItemIndexPath }));
|
|
45
|
+
expect(result.valid).toBe(true);
|
|
46
|
+
if (result.valid) expect(result.data.anchor.cmsItemIndexPath).toEqual(cmsItemIndexPath);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('rejects a malformed cmsItemIndexPath (non-number entries)', () => {
|
|
50
|
+
const result = validateComment(baseComment({ ...pageLevelAnchor, cmsItemIndexPath: ['a', 'b'] }));
|
|
51
|
+
expect(result.valid).toBe(false);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
@@ -23,8 +23,19 @@ export const CommentNodeIdentitySchema = z.object({
|
|
|
23
23
|
label: z.string().optional(),
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
+
export const CommentNodeRefSegmentSchema = z.object({
|
|
27
|
+
kind: z.enum(['component', 'node']),
|
|
28
|
+
name: z.string().min(1),
|
|
29
|
+
nth: z.number().int().nonnegative(),
|
|
30
|
+
});
|
|
31
|
+
|
|
26
32
|
export const CommentAnchorSchema = z.object({
|
|
33
|
+
// Type + occurrence path (move-resilient source of truth). Must be declared
|
|
34
|
+
// (z.object strips unknown keys) so it round-trips to disk.
|
|
35
|
+
nodeRef: z.array(CommentNodeRefSegmentSchema).optional(),
|
|
27
36
|
nodePath: z.array(z.number().int().nonnegative()),
|
|
37
|
+
// CMS-list item path disambiguating which list copy the element belongs to.
|
|
38
|
+
cmsItemIndexPath: z.array(z.number().int().nonnegative()).optional(),
|
|
28
39
|
nodeIdentity: CommentNodeIdentitySchema,
|
|
29
40
|
offsetXPercent: z.number().min(0).max(1),
|
|
30
41
|
offsetYPercent: z.number().min(0).max(1),
|
|
@@ -53,7 +64,7 @@ export const CommentSchema = z.object({
|
|
|
53
64
|
});
|
|
54
65
|
|
|
55
66
|
function zodToValidationErrors(zodError: ZodError): ValidationError[] {
|
|
56
|
-
return zodError.errors.map(err => ({
|
|
67
|
+
return zodError.errors.map((err) => ({
|
|
57
68
|
path: err.path.join('.') || 'root',
|
|
58
69
|
message: err.message,
|
|
59
70
|
code: 'INVALID_TYPE' as const,
|
|
@@ -14,9 +14,9 @@ describe('Prop Validator', () => {
|
|
|
14
14
|
title: { type: 'string', default: 'Hello' },
|
|
15
15
|
};
|
|
16
16
|
const passedProps = { title: 'World' };
|
|
17
|
-
|
|
17
|
+
|
|
18
18
|
const result = validateComponentProps(propDefs, passedProps);
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
expect(result.valid).toBe(true);
|
|
21
21
|
expect(result.props.title).toBe('World');
|
|
22
22
|
});
|
|
@@ -26,9 +26,9 @@ describe('Prop Validator', () => {
|
|
|
26
26
|
count: { type: 'number', default: 0 },
|
|
27
27
|
};
|
|
28
28
|
const passedProps = { count: 42 };
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
const result = validateComponentProps(propDefs, passedProps);
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
expect(result.valid).toBe(true);
|
|
33
33
|
expect(result.props.count).toBe(42);
|
|
34
34
|
});
|
|
@@ -38,9 +38,9 @@ describe('Prop Validator', () => {
|
|
|
38
38
|
enabled: { type: 'boolean', default: false },
|
|
39
39
|
};
|
|
40
40
|
const passedProps = { enabled: true };
|
|
41
|
-
|
|
41
|
+
|
|
42
42
|
const result = validateComponentProps(propDefs, passedProps);
|
|
43
|
-
|
|
43
|
+
|
|
44
44
|
expect(result.valid).toBe(true);
|
|
45
45
|
expect(result.props.enabled).toBe(true);
|
|
46
46
|
});
|
|
@@ -54,9 +54,9 @@ describe('Prop Validator', () => {
|
|
|
54
54
|
},
|
|
55
55
|
};
|
|
56
56
|
const passedProps = { variant: 'secondary' };
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
const result = validateComponentProps(propDefs, passedProps);
|
|
59
|
-
|
|
59
|
+
|
|
60
60
|
expect(result.valid).toBe(true);
|
|
61
61
|
expect(result.props.variant).toBe('secondary');
|
|
62
62
|
});
|
|
@@ -68,9 +68,9 @@ describe('Prop Validator', () => {
|
|
|
68
68
|
title: { type: 'string', default: 'Hello' },
|
|
69
69
|
};
|
|
70
70
|
const passedProps = { title: 123 };
|
|
71
|
-
|
|
71
|
+
|
|
72
72
|
const result = validateComponentProps(propDefs, passedProps);
|
|
73
|
-
|
|
73
|
+
|
|
74
74
|
// Coercion makes this valid (123 -> "123")
|
|
75
75
|
expect(result.valid).toBe(true);
|
|
76
76
|
expect(result.props.title).toBe('123');
|
|
@@ -121,9 +121,9 @@ describe('Prop Validator', () => {
|
|
|
121
121
|
count: { type: 'number', default: 0 },
|
|
122
122
|
};
|
|
123
123
|
const passedProps = { count: '123' };
|
|
124
|
-
|
|
124
|
+
|
|
125
125
|
const result = validateComponentProps(propDefs, passedProps);
|
|
126
|
-
|
|
126
|
+
|
|
127
127
|
expect(result.valid).toBe(true);
|
|
128
128
|
expect(result.props.count).toBe(123);
|
|
129
129
|
expect(typeof result.props.count).toBe('number');
|
|
@@ -134,9 +134,9 @@ describe('Prop Validator', () => {
|
|
|
134
134
|
enabled: { type: 'boolean', default: false },
|
|
135
135
|
};
|
|
136
136
|
const passedProps = { enabled: 'true' };
|
|
137
|
-
|
|
137
|
+
|
|
138
138
|
const result = validateComponentProps(propDefs, passedProps);
|
|
139
|
-
|
|
139
|
+
|
|
140
140
|
expect(result.valid).toBe(true);
|
|
141
141
|
expect(result.props.enabled).toBe(true);
|
|
142
142
|
expect(typeof result.props.enabled).toBe('boolean');
|
|
@@ -243,9 +243,9 @@ describe('Prop Validator', () => {
|
|
|
243
243
|
unknownProp: 'allowed',
|
|
244
244
|
anotherUnknown: 123,
|
|
245
245
|
};
|
|
246
|
-
|
|
246
|
+
|
|
247
247
|
const result = validateComponentProps(propDefs, passedProps);
|
|
248
|
-
|
|
248
|
+
|
|
249
249
|
// Should be valid (unknown props don't fail validation)
|
|
250
250
|
expect(result.valid).toBe(true);
|
|
251
251
|
// Unknown props should be preserved
|
|
@@ -259,14 +259,12 @@ describe('Prop Validator', () => {
|
|
|
259
259
|
count: { type: 'number', default: 42 },
|
|
260
260
|
};
|
|
261
261
|
const passedProps = {};
|
|
262
|
-
|
|
262
|
+
|
|
263
263
|
const result = validateComponentProps(propDefs, passedProps);
|
|
264
|
-
|
|
264
|
+
|
|
265
265
|
expect(result.valid).toBe(true);
|
|
266
266
|
expect(result.props.title).toBe('Hello');
|
|
267
267
|
expect(result.props.count).toBe(42);
|
|
268
268
|
});
|
|
269
269
|
});
|
|
270
270
|
});
|
|
271
|
-
|
|
272
|
-
|