@tamagui/style-grammar 0.0.0-bootstrap.0 → 3.0.0-beta.643.1
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/LICENSE +21 -0
- package/README.md +33 -1
- package/dist/cjs/backgroundFamily.cjs +176 -0
- package/dist/cjs/backgroundFamily.native.cjs +195 -0
- package/dist/cjs/backgroundFamily.native.js +197 -0
- package/dist/cjs/backgroundFamily.native.js.map +1 -0
- package/dist/cjs/borderFamily.cjs +169 -0
- package/dist/cjs/borderFamily.native.cjs +247 -0
- package/dist/cjs/borderFamily.native.js +249 -0
- package/dist/cjs/borderFamily.native.js.map +1 -0
- package/dist/cjs/candidate.cjs +608 -0
- package/dist/cjs/candidate.native.cjs +821 -0
- package/dist/cjs/candidate.native.js +823 -0
- package/dist/cjs/candidate.native.js.map +1 -0
- package/dist/cjs/candidateTarget.cjs +50 -0
- package/dist/cjs/candidateTarget.native.cjs +88 -0
- package/dist/cjs/candidateTarget.native.js +90 -0
- package/dist/cjs/candidateTarget.native.js.map +1 -0
- package/dist/cjs/clauseCapability.cjs +130 -0
- package/dist/cjs/clauseCapability.native.cjs +152 -0
- package/dist/cjs/clauseCapability.native.js +154 -0
- package/dist/cjs/clauseCapability.native.js.map +1 -0
- package/dist/cjs/clausePrecedence.cjs +153 -0
- package/dist/cjs/clausePrecedence.native.cjs +204 -0
- package/dist/cjs/clausePrecedence.native.js +206 -0
- package/dist/cjs/clausePrecedence.native.js.map +1 -0
- package/dist/cjs/clauseSources.cjs +47 -0
- package/dist/cjs/clauseSources.native.cjs +49 -0
- package/dist/cjs/clauseSources.native.js +51 -0
- package/dist/cjs/clauseSources.native.js.map +1 -0
- package/dist/cjs/config.cjs +130 -0
- package/dist/cjs/config.native.cjs +185 -0
- package/dist/cjs/config.native.js +187 -0
- package/dist/cjs/config.native.js.map +1 -0
- package/dist/cjs/evaluateProgram.cjs +65 -0
- package/dist/cjs/evaluateProgram.native.cjs +70 -0
- package/dist/cjs/evaluateProgram.native.js +72 -0
- package/dist/cjs/evaluateProgram.native.js.map +1 -0
- package/dist/cjs/fontShorthand.cjs +207 -0
- package/dist/cjs/fontShorthand.native.cjs +260 -0
- package/dist/cjs/fontShorthand.native.js +262 -0
- package/dist/cjs/fontShorthand.native.js.map +1 -0
- package/dist/cjs/geometricShorthand.cjs +122 -0
- package/dist/cjs/geometricShorthand.native.cjs +128 -0
- package/dist/cjs/geometricShorthand.native.js +130 -0
- package/dist/cjs/geometricShorthand.native.js.map +1 -0
- package/dist/cjs/index.cjs +19 -0
- package/dist/cjs/index.native.cjs +21 -0
- package/dist/cjs/index.native.js +23 -0
- package/dist/cjs/index.native.js.map +1 -0
- package/dist/cjs/lowerProgram.cjs +205 -0
- package/dist/cjs/lowerProgram.native.cjs +257 -0
- package/dist/cjs/lowerProgram.native.js +259 -0
- package/dist/cjs/lowerProgram.native.js.map +1 -0
- package/dist/cjs/mergeFlatValues.cjs +50 -0
- package/dist/cjs/mergeFlatValues.native.cjs +71 -0
- package/dist/cjs/mergeFlatValues.native.js +73 -0
- package/dist/cjs/mergeFlatValues.native.js.map +1 -0
- package/dist/cjs/modifierRegistry.cjs +246 -0
- package/dist/cjs/modifierRegistry.native.cjs +426 -0
- package/dist/cjs/modifierRegistry.native.js +428 -0
- package/dist/cjs/modifierRegistry.native.js.map +1 -0
- package/dist/cjs/payloadShape.cjs +69 -0
- package/dist/cjs/payloadShape.native.cjs +71 -0
- package/dist/cjs/payloadShape.native.js +73 -0
- package/dist/cjs/payloadShape.native.js.map +1 -0
- package/dist/cjs/programEligibility.cjs +46 -0
- package/dist/cjs/programEligibility.native.cjs +48 -0
- package/dist/cjs/programEligibility.native.js +50 -0
- package/dist/cjs/programEligibility.native.js.map +1 -0
- package/dist/cjs/programHash.cjs +86 -0
- package/dist/cjs/programHash.native.cjs +106 -0
- package/dist/cjs/programHash.native.js +108 -0
- package/dist/cjs/programHash.native.js.map +1 -0
- package/dist/cjs/programs.cjs +128 -0
- package/dist/cjs/programs.native.cjs +220 -0
- package/dist/cjs/programs.native.js +222 -0
- package/dist/cjs/programs.native.js.map +1 -0
- package/dist/cjs/registry.cjs +629 -0
- package/dist/cjs/registry.native.cjs +635 -0
- package/dist/cjs/registry.native.js +637 -0
- package/dist/cjs/registry.native.js.map +1 -0
- package/dist/cjs/resolvePayload.cjs +296 -0
- package/dist/cjs/resolvePayload.native.cjs +302 -0
- package/dist/cjs/resolvePayload.native.js +304 -0
- package/dist/cjs/resolvePayload.native.js.map +1 -0
- package/dist/cjs/runtime.cjs +50 -0
- package/dist/cjs/runtime.native.cjs +52 -0
- package/dist/cjs/runtime.native.js +54 -0
- package/dist/cjs/runtime.native.js.map +1 -0
- package/dist/cjs/safeAreaVariables.cjs +42 -0
- package/dist/cjs/safeAreaVariables.native.cjs +44 -0
- package/dist/cjs/safeAreaVariables.native.js +46 -0
- package/dist/cjs/safeAreaVariables.native.js.map +1 -0
- package/dist/cjs/scanFlatValue.cjs +181 -0
- package/dist/cjs/scanFlatValue.native.cjs +183 -0
- package/dist/cjs/scanFlatValue.native.js +185 -0
- package/dist/cjs/scanFlatValue.native.js.map +1 -0
- package/dist/cjs/serializePayload.cjs +149 -0
- package/dist/cjs/serializePayload.native.cjs +187 -0
- package/dist/cjs/serializePayload.native.js +189 -0
- package/dist/cjs/serializePayload.native.js.map +1 -0
- package/dist/cjs/stateModifiers.cjs +43 -0
- package/dist/cjs/stateModifiers.native.cjs +45 -0
- package/dist/cjs/stateModifiers.native.js +47 -0
- package/dist/cjs/stateModifiers.native.js.map +1 -0
- package/dist/cjs/states.cjs +97 -0
- package/dist/cjs/states.native.cjs +131 -0
- package/dist/cjs/states.native.js +133 -0
- package/dist/cjs/states.native.js.map +1 -0
- package/dist/cjs/table.cjs +54 -0
- package/dist/cjs/table.native.cjs +135 -0
- package/dist/cjs/table.native.js +137 -0
- package/dist/cjs/table.native.js.map +1 -0
- package/dist/cjs/textDecorationFamily.cjs +129 -0
- package/dist/cjs/textDecorationFamily.native.cjs +199 -0
- package/dist/cjs/textDecorationFamily.native.js +201 -0
- package/dist/cjs/textDecorationFamily.native.js.map +1 -0
- package/dist/cjs/tooling.cjs +26 -0
- package/dist/cjs/tooling.native.cjs +28 -0
- package/dist/cjs/tooling.native.js +30 -0
- package/dist/cjs/tooling.native.js.map +1 -0
- package/dist/cjs/toolingAnnotations.cjs +105 -0
- package/dist/cjs/toolingAnnotations.native.cjs +133 -0
- package/dist/cjs/toolingAnnotations.native.js +135 -0
- package/dist/cjs/toolingAnnotations.native.js.map +1 -0
- package/dist/cjs/toolingDiagnostics.cjs +459 -0
- package/dist/cjs/toolingDiagnostics.native.cjs +788 -0
- package/dist/cjs/toolingDiagnostics.native.js +790 -0
- package/dist/cjs/toolingDiagnostics.native.js.map +1 -0
- package/dist/cjs/toolingRegistry.cjs +159 -0
- package/dist/cjs/toolingRegistry.native.cjs +164 -0
- package/dist/cjs/toolingRegistry.native.js +166 -0
- package/dist/cjs/toolingRegistry.native.js.map +1 -0
- package/dist/cjs/transformFamily.cjs +425 -0
- package/dist/cjs/transformFamily.native.cjs +517 -0
- package/dist/cjs/transformFamily.native.js +519 -0
- package/dist/cjs/transformFamily.native.js.map +1 -0
- package/dist/cjs/transition.cjs +459 -0
- package/dist/cjs/transition.native.cjs +499 -0
- package/dist/cjs/transition.native.js +501 -0
- package/dist/cjs/transition.native.js.map +1 -0
- package/dist/cjs/transitionAlign.cjs +115 -0
- package/dist/cjs/transitionAlign.native.cjs +154 -0
- package/dist/cjs/transitionAlign.native.js +156 -0
- package/dist/cjs/transitionAlign.native.js.map +1 -0
- package/dist/cjs/transitionLegacy.cjs +217 -0
- package/dist/cjs/transitionLegacy.native.cjs +260 -0
- package/dist/cjs/transitionLegacy.native.js +262 -0
- package/dist/cjs/transitionLegacy.native.js.map +1 -0
- package/dist/cjs/transitionNative.cjs +281 -0
- package/dist/cjs/transitionNative.native.cjs +285 -0
- package/dist/cjs/transitionNative.native.js +287 -0
- package/dist/cjs/transitionNative.native.js.map +1 -0
- package/dist/cjs/unitlessNumbers.cjs +86 -0
- package/dist/cjs/unitlessNumbers.native.cjs +106 -0
- package/dist/cjs/unitlessNumbers.native.js +108 -0
- package/dist/cjs/unitlessNumbers.native.js.map +1 -0
- package/dist/cjs/v6ThemeNames.cjs +47 -0
- package/dist/cjs/v6ThemeNames.native.cjs +53 -0
- package/dist/cjs/v6ThemeNames.native.js +55 -0
- package/dist/cjs/v6ThemeNames.native.js.map +1 -0
- package/dist/cjs/valueParser.cjs +148 -0
- package/dist/cjs/valueParser.native.cjs +150 -0
- package/dist/cjs/valueParser.native.js +152 -0
- package/dist/cjs/valueParser.native.js.map +1 -0
- package/dist/cjs/valueTypes.cjs +38 -0
- package/dist/cjs/valueTypes.native.cjs +40 -0
- package/dist/cjs/valueTypes.native.js +42 -0
- package/dist/cjs/valueTypes.native.js.map +1 -0
- package/dist/esm/backgroundFamily.mjs +150 -0
- package/dist/esm/backgroundFamily.mjs.map +1 -0
- package/dist/esm/backgroundFamily.native.js +167 -0
- package/dist/esm/backgroundFamily.native.js.map +1 -0
- package/dist/esm/borderFamily.mjs +146 -0
- package/dist/esm/borderFamily.mjs.map +1 -0
- package/dist/esm/borderFamily.native.js +222 -0
- package/dist/esm/borderFamily.native.js.map +1 -0
- package/dist/esm/candidate.mjs +581 -0
- package/dist/esm/candidate.mjs.map +1 -0
- package/dist/esm/candidate.native.js +792 -0
- package/dist/esm/candidate.native.js.map +1 -0
- package/dist/esm/candidateTarget.mjs +29 -0
- package/dist/esm/candidateTarget.mjs.map +1 -0
- package/dist/esm/candidateTarget.native.js +65 -0
- package/dist/esm/candidateTarget.native.js.map +1 -0
- package/dist/esm/clauseCapability.mjs +107 -0
- package/dist/esm/clauseCapability.mjs.map +1 -0
- package/dist/esm/clauseCapability.native.js +127 -0
- package/dist/esm/clauseCapability.native.js.map +1 -0
- package/dist/esm/clausePrecedence.mjs +121 -0
- package/dist/esm/clausePrecedence.mjs.map +1 -0
- package/dist/esm/clausePrecedence.native.js +170 -0
- package/dist/esm/clausePrecedence.native.js.map +1 -0
- package/dist/esm/clauseSources.mjs +23 -0
- package/dist/esm/clauseSources.mjs.map +1 -0
- package/dist/esm/clauseSources.native.js +23 -0
- package/dist/esm/clauseSources.native.js.map +1 -0
- package/dist/esm/config.mjs +103 -0
- package/dist/esm/config.mjs.map +1 -0
- package/dist/esm/config.native.js +156 -0
- package/dist/esm/config.native.js.map +1 -0
- package/dist/esm/evaluateProgram.mjs +45 -0
- package/dist/esm/evaluateProgram.mjs.map +1 -0
- package/dist/esm/evaluateProgram.native.js +48 -0
- package/dist/esm/evaluateProgram.native.js.map +1 -0
- package/dist/esm/fontShorthand.mjs +184 -0
- package/dist/esm/fontShorthand.mjs.map +1 -0
- package/dist/esm/fontShorthand.native.js +235 -0
- package/dist/esm/fontShorthand.native.js.map +1 -0
- package/dist/esm/geometricShorthand.mjs +102 -0
- package/dist/esm/geometricShorthand.mjs.map +1 -0
- package/dist/esm/geometricShorthand.native.js +106 -0
- package/dist/esm/geometricShorthand.native.js.map +1 -0
- package/dist/esm/index.mjs +1 -0
- package/dist/esm/index.native.js +1 -0
- package/dist/esm/lowerProgram.mjs +182 -0
- package/dist/esm/lowerProgram.mjs.map +1 -0
- package/dist/esm/lowerProgram.native.js +232 -0
- package/dist/esm/lowerProgram.native.js.map +1 -0
- package/dist/esm/mergeFlatValues.mjs +27 -0
- package/dist/esm/mergeFlatValues.mjs.map +1 -0
- package/dist/esm/mergeFlatValues.native.js +46 -0
- package/dist/esm/mergeFlatValues.native.js.map +1 -0
- package/dist/esm/modifierRegistry.mjs +219 -0
- package/dist/esm/modifierRegistry.mjs.map +1 -0
- package/dist/esm/modifierRegistry.native.js +397 -0
- package/dist/esm/modifierRegistry.native.js.map +1 -0
- package/dist/esm/payloadShape.mjs +49 -0
- package/dist/esm/payloadShape.mjs.map +1 -0
- package/dist/esm/payloadShape.native.js +49 -0
- package/dist/esm/payloadShape.native.js.map +1 -0
- package/dist/esm/programEligibility.mjs +22 -0
- package/dist/esm/programEligibility.mjs.map +1 -0
- package/dist/esm/programEligibility.native.js +22 -0
- package/dist/esm/programEligibility.native.js.map +1 -0
- package/dist/esm/programHash.mjs +62 -0
- package/dist/esm/programHash.mjs.map +1 -0
- package/dist/esm/programHash.native.js +80 -0
- package/dist/esm/programHash.native.js.map +1 -0
- package/dist/esm/programs.mjs +103 -0
- package/dist/esm/programs.mjs.map +1 -0
- package/dist/esm/programs.native.js +193 -0
- package/dist/esm/programs.native.js.map +1 -0
- package/dist/esm/registry.mjs +595 -0
- package/dist/esm/registry.mjs.map +1 -0
- package/dist/esm/registry.native.js +599 -0
- package/dist/esm/registry.native.js.map +1 -0
- package/dist/esm/resolvePayload.mjs +273 -0
- package/dist/esm/resolvePayload.mjs.map +1 -0
- package/dist/esm/resolvePayload.native.js +277 -0
- package/dist/esm/resolvePayload.native.js.map +1 -0
- package/dist/esm/runtime.mjs +63 -0
- package/dist/esm/runtime.native.js +63 -0
- package/dist/esm/safeAreaVariables.mjs +18 -0
- package/dist/esm/safeAreaVariables.mjs.map +1 -0
- package/dist/esm/safeAreaVariables.native.js +18 -0
- package/dist/esm/safeAreaVariables.native.js.map +1 -0
- package/dist/esm/scanFlatValue.mjs +160 -0
- package/dist/esm/scanFlatValue.mjs.map +1 -0
- package/dist/esm/scanFlatValue.native.js +160 -0
- package/dist/esm/scanFlatValue.native.js.map +1 -0
- package/dist/esm/serializePayload.mjs +125 -0
- package/dist/esm/serializePayload.mjs.map +1 -0
- package/dist/esm/serializePayload.native.js +161 -0
- package/dist/esm/serializePayload.native.js.map +1 -0
- package/dist/esm/stateModifiers.mjs +19 -0
- package/dist/esm/stateModifiers.mjs.map +1 -0
- package/dist/esm/stateModifiers.native.js +19 -0
- package/dist/esm/stateModifiers.native.js.map +1 -0
- package/dist/esm/states.mjs +69 -0
- package/dist/esm/states.mjs.map +1 -0
- package/dist/esm/states.native.js +101 -0
- package/dist/esm/states.native.js.map +1 -0
- package/dist/esm/table.mjs +31 -0
- package/dist/esm/table.mjs.map +1 -0
- package/dist/esm/table.native.js +110 -0
- package/dist/esm/table.native.js.map +1 -0
- package/dist/esm/textDecorationFamily.mjs +106 -0
- package/dist/esm/textDecorationFamily.mjs.map +1 -0
- package/dist/esm/textDecorationFamily.native.js +174 -0
- package/dist/esm/textDecorationFamily.native.js.map +1 -0
- package/dist/esm/tooling.mjs +15 -0
- package/dist/esm/tooling.native.js +15 -0
- package/dist/esm/toolingAnnotations.mjs +85 -0
- package/dist/esm/toolingAnnotations.mjs.map +1 -0
- package/dist/esm/toolingAnnotations.native.js +111 -0
- package/dist/esm/toolingAnnotations.native.js.map +1 -0
- package/dist/esm/toolingDiagnostics.mjs +428 -0
- package/dist/esm/toolingDiagnostics.mjs.map +1 -0
- package/dist/esm/toolingDiagnostics.native.js +755 -0
- package/dist/esm/toolingDiagnostics.native.js.map +1 -0
- package/dist/esm/toolingRegistry.mjs +135 -0
- package/dist/esm/toolingRegistry.mjs.map +1 -0
- package/dist/esm/toolingRegistry.native.js +138 -0
- package/dist/esm/toolingRegistry.native.js.map +1 -0
- package/dist/esm/transformFamily.mjs +392 -0
- package/dist/esm/transformFamily.mjs.map +1 -0
- package/dist/esm/transformFamily.native.js +482 -0
- package/dist/esm/transformFamily.native.js.map +1 -0
- package/dist/esm/transition.mjs +434 -0
- package/dist/esm/transition.mjs.map +1 -0
- package/dist/esm/transition.native.js +472 -0
- package/dist/esm/transition.native.js.map +1 -0
- package/dist/esm/transitionAlign.mjs +95 -0
- package/dist/esm/transitionAlign.mjs.map +1 -0
- package/dist/esm/transitionAlign.native.js +132 -0
- package/dist/esm/transitionAlign.native.js.map +1 -0
- package/dist/esm/transitionLegacy.mjs +197 -0
- package/dist/esm/transitionLegacy.mjs.map +1 -0
- package/dist/esm/transitionLegacy.native.js +238 -0
- package/dist/esm/transitionLegacy.native.js.map +1 -0
- package/dist/esm/transitionNative.mjs +257 -0
- package/dist/esm/transitionNative.mjs.map +1 -0
- package/dist/esm/transitionNative.native.js +259 -0
- package/dist/esm/transitionNative.native.js.map +1 -0
- package/dist/esm/unitlessNumbers.mjs +65 -0
- package/dist/esm/unitlessNumbers.mjs.map +1 -0
- package/dist/esm/unitlessNumbers.native.js +83 -0
- package/dist/esm/unitlessNumbers.native.js.map +1 -0
- package/dist/esm/v6ThemeNames.mjs +23 -0
- package/dist/esm/v6ThemeNames.mjs.map +1 -0
- package/dist/esm/v6ThemeNames.native.js +27 -0
- package/dist/esm/v6ThemeNames.native.js.map +1 -0
- package/dist/esm/valueParser.mjs +125 -0
- package/dist/esm/valueParser.mjs.map +1 -0
- package/dist/esm/valueParser.native.js +125 -0
- package/dist/esm/valueParser.native.js.map +1 -0
- package/dist/esm/valueTypes.mjs +14 -0
- package/dist/esm/valueTypes.mjs.map +1 -0
- package/dist/esm/valueTypes.native.js +14 -0
- package/dist/esm/valueTypes.native.js.map +1 -0
- package/package.json +98 -4
- package/src/backgroundFamily.ts +182 -0
- package/src/borderFamily.ts +172 -0
- package/src/candidate.ts +691 -0
- package/src/candidateTarget.ts +67 -0
- package/src/clauseCapability.ts +209 -0
- package/src/clausePrecedence.ts +198 -0
- package/src/clauseSources.ts +29 -0
- package/src/config.ts +169 -0
- package/src/evaluateProgram.ts +91 -0
- package/src/fontShorthand.ts +177 -0
- package/src/geometricShorthand.ts +105 -0
- package/src/index.ts +1 -0
- package/src/lowerProgram.ts +398 -0
- package/src/modifierRegistry.ts +340 -0
- package/src/payloadShape.ts +92 -0
- package/src/programEligibility.ts +46 -0
- package/src/programHash.ts +128 -0
- package/src/programs.ts +95 -0
- package/src/registry.ts +367 -0
- package/src/resolvePayload.ts +480 -0
- package/src/runtime.ts +32 -0
- package/src/safeAreaVariables.ts +19 -0
- package/src/scanFlatValue.ts +327 -0
- package/src/serializePayload.ts +199 -0
- package/src/stateModifiers.ts +36 -0
- package/src/states.ts +128 -0
- package/src/table.ts +47 -0
- package/src/textDecorationFamily.ts +119 -0
- package/src/tooling.ts +8 -0
- package/src/toolingAnnotations.ts +151 -0
- package/src/toolingDiagnostics.ts +735 -0
- package/src/toolingRegistry.ts +130 -0
- package/src/transformFamily.ts +590 -0
- package/src/transition.ts +553 -0
- package/src/transitionAlign.ts +165 -0
- package/src/transitionLegacy.ts +237 -0
- package/src/transitionNative.ts +357 -0
- package/src/unitlessNumbers.ts +61 -0
- package/src/v6ThemeNames.ts +40 -0
- package/src/valueParser.ts +193 -0
- package/src/valueTypes.ts +136 -0
- package/types/backgroundFamily.d.ts +17 -0
- package/types/backgroundFamily.d.ts.map +11 -0
- package/types/borderFamily.d.ts +23 -0
- package/types/borderFamily.d.ts.map +11 -0
- package/types/candidate.d.ts +52 -0
- package/types/candidate.d.ts.map +11 -0
- package/types/candidateTarget.d.ts +32 -0
- package/types/candidateTarget.d.ts.map +11 -0
- package/types/clauseCapability.d.ts +69 -0
- package/types/clauseCapability.d.ts.map +11 -0
- package/types/clausePrecedence.d.ts +30 -0
- package/types/clausePrecedence.d.ts.map +11 -0
- package/types/clauseSources.d.ts +12 -0
- package/types/clauseSources.d.ts.map +11 -0
- package/types/config.d.ts +44 -0
- package/types/config.d.ts.map +11 -0
- package/types/evaluateProgram.d.ts +27 -0
- package/types/evaluateProgram.d.ts.map +11 -0
- package/types/fontShorthand.d.ts +25 -0
- package/types/fontShorthand.d.ts.map +11 -0
- package/types/geometricShorthand.d.ts +21 -0
- package/types/geometricShorthand.d.ts.map +11 -0
- package/types/index.d.ts +3 -0
- package/types/index.d.ts.map +11 -0
- package/types/lowerProgram.d.ts +73 -0
- package/types/lowerProgram.d.ts.map +11 -0
- package/types/mergeFlatValues.d.ts +14 -0
- package/types/mergeFlatValues.d.ts.map +11 -0
- package/types/modifierRegistry.d.ts +70 -0
- package/types/modifierRegistry.d.ts.map +11 -0
- package/types/payloadShape.d.ts +15 -0
- package/types/payloadShape.d.ts.map +11 -0
- package/types/programEligibility.d.ts +23 -0
- package/types/programEligibility.d.ts.map +11 -0
- package/types/programHash.d.ts +20 -0
- package/types/programHash.d.ts.map +11 -0
- package/types/programs.d.ts +18 -0
- package/types/programs.d.ts.map +11 -0
- package/types/registry.d.ts +24 -0
- package/types/registry.d.ts.map +11 -0
- package/types/resolvePayload.d.ts +75 -0
- package/types/resolvePayload.d.ts.map +11 -0
- package/types/runtime.d.ts +34 -0
- package/types/runtime.d.ts.map +11 -0
- package/types/safeAreaVariables.d.ts +5 -0
- package/types/safeAreaVariables.d.ts.map +11 -0
- package/types/scanFlatValue.d.ts +29 -0
- package/types/scanFlatValue.d.ts.map +11 -0
- package/types/serializePayload.d.ts +13 -0
- package/types/serializePayload.d.ts.map +11 -0
- package/types/stateModifiers.d.ts +14 -0
- package/types/stateModifiers.d.ts.map +11 -0
- package/types/states.d.ts +22 -0
- package/types/states.d.ts.map +11 -0
- package/types/table.d.ts +4 -0
- package/types/table.d.ts.map +11 -0
- package/types/textDecorationFamily.d.ts +23 -0
- package/types/textDecorationFamily.d.ts.map +11 -0
- package/types/tooling.d.ts +10 -0
- package/types/tooling.d.ts.map +11 -0
- package/types/toolingAnnotations.d.ts +29 -0
- package/types/toolingAnnotations.d.ts.map +11 -0
- package/types/toolingDiagnostics.d.ts +114 -0
- package/types/toolingDiagnostics.d.ts.map +11 -0
- package/types/toolingRegistry.d.ts +10 -0
- package/types/toolingRegistry.d.ts.map +11 -0
- package/types/transformFamily.d.ts +83 -0
- package/types/transformFamily.d.ts.map +11 -0
- package/types/transition.d.ts +65 -0
- package/types/transition.d.ts.map +11 -0
- package/types/transitionAlign.d.ts +15 -0
- package/types/transitionAlign.d.ts.map +11 -0
- package/types/transitionLegacy.d.ts +9 -0
- package/types/transitionLegacy.d.ts.map +11 -0
- package/types/transitionNative.d.ts +41 -0
- package/types/transitionNative.d.ts.map +11 -0
- package/types/unitlessNumbers.d.ts +3 -0
- package/types/unitlessNumbers.d.ts.map +11 -0
- package/types/v6ThemeNames.d.ts +7 -0
- package/types/v6ThemeNames.d.ts.map +11 -0
- package/types/valueParser.d.ts +36 -0
- package/types/valueParser.d.ts.map +11 -0
- package/types/valueTypes.d.ts +66 -0
- package/types/valueTypes.d.ts.map +11 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
// The universal flat value parser.
|
|
2
|
+
//
|
|
3
|
+
// value := base? clause*
|
|
4
|
+
// clause := modifier (":" modifier)* ":" payload
|
|
5
|
+
//
|
|
6
|
+
// See plans/dom-tailwind-flat-values.md — "The universal value grammar". The
|
|
7
|
+
// parser only splits: `base` and every `payload` come back as trimmed raw CSS
|
|
8
|
+
// component-value sequences with no interpretation and no token resolution.
|
|
9
|
+
//
|
|
10
|
+
// The split itself is `scanFlatValue`, the one lexer every implementation of
|
|
11
|
+
// this grammar drives; this file is what that split MEANS, which is the part
|
|
12
|
+
// the registry decides. Read `scanFlatValue.ts` for why a top-level colon,
|
|
13
|
+
// brace or semicolon is structural and everything inside a string or a paren is
|
|
14
|
+
// not.
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
scanFlatValue,
|
|
18
|
+
type FlatScanErrorCode,
|
|
19
|
+
type FlatValueVisitor,
|
|
20
|
+
} from './scanFlatValue'
|
|
21
|
+
import type {
|
|
22
|
+
ModifierRegistryView,
|
|
23
|
+
ParsedClause,
|
|
24
|
+
ValueParseError,
|
|
25
|
+
ValueParseErrorCode,
|
|
26
|
+
ValueParseResult,
|
|
27
|
+
} from './valueTypes'
|
|
28
|
+
|
|
29
|
+
const CHAR_COLON = 58
|
|
30
|
+
|
|
31
|
+
const noClauses: readonly ParsedClause[] = Object.freeze([])
|
|
32
|
+
|
|
33
|
+
export interface ValueSourceSpan {
|
|
34
|
+
kind: 'base' | 'payload' | 'modifier' | 'word'
|
|
35
|
+
start: number
|
|
36
|
+
end: number
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface ValueParseWithSourceSpans {
|
|
40
|
+
result: ValueParseResult
|
|
41
|
+
spans: readonly ValueSourceSpan[]
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* NOT A SAFETY CHECK. A successful parse says the value is well-formed FLAT
|
|
46
|
+
* VALUE SYNTAX. It does not say the value is safe to interpolate into CSS.
|
|
47
|
+
*
|
|
48
|
+
* `scanFlatValue` tracks the same constructs CSS's tokenizer does (comments,
|
|
49
|
+
* strings that a newline ends, `url()`, paren nesting), so a value whose
|
|
50
|
+
* delimiters do not actually close is an error rather than silently trusted.
|
|
51
|
+
* That covers the constructs a value can leave OPEN; it is not an injection
|
|
52
|
+
* guard. `emitValue` and `getStyleObject` used to call a
|
|
53
|
+
* `carriesTopLevelInjection` guard independently; it was removed by owner
|
|
54
|
+
* decision, on the grounds that a style value is authored rather than user
|
|
55
|
+
* input, and the web lowering emits payloads verbatim by contract.
|
|
56
|
+
*
|
|
57
|
+
* So the rule lives upstream of this file: never put a user-controlled string
|
|
58
|
+
* in a style value. A payload carrying `;}` closes its own rule and everything
|
|
59
|
+
* after it is a selector block the author never wrote. Do not gate emission on
|
|
60
|
+
* `parseValue(...).ok` either; it answers a syntax question, not a safety one.
|
|
61
|
+
*/
|
|
62
|
+
export function parseValue(
|
|
63
|
+
input: string,
|
|
64
|
+
registry: ModifierRegistryView
|
|
65
|
+
): ValueParseResult {
|
|
66
|
+
return parseValueInternal(input, registry)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Parses through the runtime scanner while also retaining source boundaries for
|
|
71
|
+
* editor tooling. The ordinary runtime path does not allocate these spans.
|
|
72
|
+
*/
|
|
73
|
+
export function parseValueWithSourceSpans(
|
|
74
|
+
input: string,
|
|
75
|
+
registry: ModifierRegistryView
|
|
76
|
+
): ValueParseWithSourceSpans {
|
|
77
|
+
const spans: ValueSourceSpan[] = []
|
|
78
|
+
return {
|
|
79
|
+
result: parseValueInternal(input, registry, spans),
|
|
80
|
+
spans,
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function scanErrorMessage(code: FlatScanErrorCode, input: string, index: number): string {
|
|
85
|
+
if (code === 'invalid-character') {
|
|
86
|
+
return `"${input[index]}" cannot appear in a value: it would end the declaration or rule`
|
|
87
|
+
}
|
|
88
|
+
if (code === 'unterminated-string') {
|
|
89
|
+
return `unterminated ${input[index]} string`
|
|
90
|
+
}
|
|
91
|
+
if (code === 'unterminated-comment') {
|
|
92
|
+
return 'unterminated "/*" comment: it would swallow the rules after this one'
|
|
93
|
+
}
|
|
94
|
+
if (code === 'stray-comment-close') {
|
|
95
|
+
return 'stray "*/": it would close a comment opened somewhere else'
|
|
96
|
+
}
|
|
97
|
+
return 'unterminated "(" in value'
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function parseValueInternal(
|
|
101
|
+
input: string,
|
|
102
|
+
registry: ModifierRegistryView,
|
|
103
|
+
sourceSpans?: ValueSourceSpan[]
|
|
104
|
+
): ValueParseResult {
|
|
105
|
+
let errors: ValueParseError[] | null = null
|
|
106
|
+
let base: string | null = null
|
|
107
|
+
let clauses: ParsedClause[] | null = null
|
|
108
|
+
// modifiers of the clause whose payload is currently being collected
|
|
109
|
+
let pending: string[] | null = null
|
|
110
|
+
// where the base, or the current payload, starts before trimming, which is
|
|
111
|
+
// what an empty payload has to point its diagnostic at
|
|
112
|
+
let segmentStart = 0
|
|
113
|
+
|
|
114
|
+
const addError = (
|
|
115
|
+
code: ValueParseErrorCode,
|
|
116
|
+
index: number,
|
|
117
|
+
message: string,
|
|
118
|
+
modifier?: string
|
|
119
|
+
): void => {
|
|
120
|
+
;(errors ||= []).push(
|
|
121
|
+
modifier === undefined
|
|
122
|
+
? { code, index, message }
|
|
123
|
+
: { code, index, message, modifier }
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const visitor: FlatValueVisitor = {
|
|
128
|
+
segment(start, end, isBase) {
|
|
129
|
+
sourceSpans?.push({ kind: isBase ? 'base' : 'payload', start, end })
|
|
130
|
+
if (isBase) {
|
|
131
|
+
base = start < end ? input.slice(start, end) : null
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
if (start >= end) {
|
|
135
|
+
addError(
|
|
136
|
+
'empty-payload',
|
|
137
|
+
segmentStart,
|
|
138
|
+
`the "${pending!.join(':')}:" clause has no value`
|
|
139
|
+
)
|
|
140
|
+
return
|
|
141
|
+
}
|
|
142
|
+
;(clauses ||= []).push({ modifiers: pending!, payload: input.slice(start, end) })
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
// a clause word ended: everything before its last top-level colon is the
|
|
146
|
+
// modifier chain, everything after it begins the payload
|
|
147
|
+
chain(chainStart, chainEnd) {
|
|
148
|
+
const modifiers: string[] = []
|
|
149
|
+
let nameStart = chainStart
|
|
150
|
+
for (let index = chainStart; index <= chainEnd; index++) {
|
|
151
|
+
if (index !== chainEnd && input.charCodeAt(index) !== CHAR_COLON) continue
|
|
152
|
+
if (index === nameStart) {
|
|
153
|
+
addError('empty-modifier', index, 'a modifier chain has an empty segment')
|
|
154
|
+
} else {
|
|
155
|
+
const name = input.slice(nameStart, index)
|
|
156
|
+
sourceSpans?.push({ kind: 'modifier', start: nameStart, end: index })
|
|
157
|
+
if (registry.get(name) === undefined) {
|
|
158
|
+
addError(
|
|
159
|
+
'unregistered-modifier',
|
|
160
|
+
nameStart,
|
|
161
|
+
`"${name}" is not a registered modifier`,
|
|
162
|
+
name
|
|
163
|
+
)
|
|
164
|
+
}
|
|
165
|
+
modifiers.push(name)
|
|
166
|
+
}
|
|
167
|
+
nameStart = index + 1
|
|
168
|
+
}
|
|
169
|
+
pending = modifiers
|
|
170
|
+
segmentStart = chainEnd + 1
|
|
171
|
+
return true
|
|
172
|
+
},
|
|
173
|
+
|
|
174
|
+
error(code, index) {
|
|
175
|
+
addError(code, index, scanErrorMessage(code, input, index))
|
|
176
|
+
},
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (sourceSpans) {
|
|
180
|
+
// only the trailing bare word is a completion position the spans have no
|
|
181
|
+
// other name for: every earlier word is inside a base or a payload span
|
|
182
|
+
visitor.word = (start, end, isChain) => {
|
|
183
|
+
if (!isChain && end === input.length) {
|
|
184
|
+
sourceSpans.push({ kind: 'word', start, end })
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
scanFlatValue(input, visitor)
|
|
190
|
+
|
|
191
|
+
if (errors !== null) return { ok: false, errors }
|
|
192
|
+
return { ok: true, value: { base, clauses: clauses ?? noClauses } }
|
|
193
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// The shared IR for the universal flat value grammar.
|
|
2
|
+
//
|
|
3
|
+
// This file is the contract between the value parser, the per-longhand program
|
|
4
|
+
// merge engine, the compiler, and every frontend (props, Tailwind classes,
|
|
5
|
+
// styled skins). See plans/dom-tailwind-flat-values.md on main — "The universal
|
|
6
|
+
// value grammar" and "Programs and merging" — for the full design. The rules
|
|
7
|
+
// that matter are restated here so this file can be read standalone.
|
|
8
|
+
//
|
|
9
|
+
// Grammar:
|
|
10
|
+
//
|
|
11
|
+
// value := base? clause*
|
|
12
|
+
// clause := modifier (":" modifier)* ":" payload
|
|
13
|
+
//
|
|
14
|
+
// - `base` and each `payload` are plain CSS component-value sequences. They are
|
|
15
|
+
// NOT interpreted here; the parser only splits, it never resolves.
|
|
16
|
+
// - Clause detection is purely syntactic: a top-level colon (outside strings
|
|
17
|
+
// and outside any paren nesting, url() included) always starts a clause. A
|
|
18
|
+
// top-level colon is never valid inside a CSS component value, so this is
|
|
19
|
+
// unambiguous with zero configuration.
|
|
20
|
+
// - The identifier chain before the colon must be entirely registered
|
|
21
|
+
// modifiers. An unregistered modifier is a hard parse error (`hver:` never
|
|
22
|
+
// silently becomes part of a value).
|
|
23
|
+
// - An empty payload is a parse error. A clause with no preceding base is fine
|
|
24
|
+
// (`bg="hover:red"`).
|
|
25
|
+
//
|
|
26
|
+
// Programs:
|
|
27
|
+
//
|
|
28
|
+
// - Every authored prop expands (via shorthand + family tables) to one or more
|
|
29
|
+
// CSS longhands. Each longhand accumulates one program in authored order.
|
|
30
|
+
// Contributions merge by base or normalized condition-set slot, so a later
|
|
31
|
+
// clause replaces only that slot and unrelated earlier clauses persist.
|
|
32
|
+
|
|
33
|
+
export type ModifierKind =
|
|
34
|
+
| 'state'
|
|
35
|
+
| 'theme'
|
|
36
|
+
| 'media'
|
|
37
|
+
| 'platform'
|
|
38
|
+
| 'group'
|
|
39
|
+
/** container queries: `@sm:` nearest container, `@sm/card:` named */
|
|
40
|
+
| 'container'
|
|
41
|
+
|
|
42
|
+
/** Maximum distinct non-platform conditions one emitted clause can encode. */
|
|
43
|
+
export const grammarMaxNonPlatformDepth = 5
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* What the parser needs to know about registered modifiers. Implementations
|
|
47
|
+
* project the user config (media keys, root themes, platform names) plus the
|
|
48
|
+
* built-in state vocabulary into this view. Parameterized modifiers
|
|
49
|
+
* (`group-<name>-<state>`) are the implementation's concern — `get` receives
|
|
50
|
+
* the full authored spelling.
|
|
51
|
+
*/
|
|
52
|
+
export interface ModifierRegistryView {
|
|
53
|
+
get(name: string): ModifierKind | undefined
|
|
54
|
+
/**
|
|
55
|
+
* Config-derived modifier trie lookup for authoring one canonical chain.
|
|
56
|
+
* Implementations that only parse values may omit it; tooling refuses to
|
|
57
|
+
* invent a completion vocabulary when it is absent.
|
|
58
|
+
*/
|
|
59
|
+
next?(modifiers: readonly string[]): readonly string[]
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** one `modifiers ":" payload` clause, modifiers in authored order */
|
|
63
|
+
export interface ParsedClause {
|
|
64
|
+
modifiers: readonly string[]
|
|
65
|
+
/** trimmed raw CSS component-value sequence; never empty */
|
|
66
|
+
payload: string
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface ParsedValue {
|
|
70
|
+
/** trimmed unconditional leading value, or null when the string starts with a clause */
|
|
71
|
+
base: string | null
|
|
72
|
+
clauses: readonly ParsedClause[]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export type ValueParseErrorCode =
|
|
76
|
+
| 'unregistered-modifier'
|
|
77
|
+
| 'empty-payload'
|
|
78
|
+
| 'empty-modifier'
|
|
79
|
+
| 'unterminated-string'
|
|
80
|
+
| 'unterminated-function'
|
|
81
|
+
/**
|
|
82
|
+
* an opened comment that never closes. `getAllRules()` joins rules into one
|
|
83
|
+
* blob for SSR, so an open comment blanks the rules after it until some later
|
|
84
|
+
* close delimiter turns them back on
|
|
85
|
+
*/
|
|
86
|
+
| 'unterminated-comment'
|
|
87
|
+
/** a close delimiter with nothing open, which closes another rule's comment */
|
|
88
|
+
| 'stray-comment-close'
|
|
89
|
+
/**
|
|
90
|
+
* a top-level `{`, `}`, or `;` — never valid in a CSS component value, and
|
|
91
|
+
* rejecting them here is what makes rule/selector injection through a
|
|
92
|
+
* payload structurally impossible in the web lowering
|
|
93
|
+
*/
|
|
94
|
+
| 'invalid-character'
|
|
95
|
+
|
|
96
|
+
export interface ValueParseError {
|
|
97
|
+
code: ValueParseErrorCode
|
|
98
|
+
/** character offset into the source string where the error was detected */
|
|
99
|
+
index: number
|
|
100
|
+
message: string
|
|
101
|
+
/** the offending modifier spelling, for unregistered-modifier */
|
|
102
|
+
modifier?: string
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export type ValueParseResult =
|
|
106
|
+
| { ok: true; value: ParsedValue }
|
|
107
|
+
| { ok: false; errors: readonly ValueParseError[] }
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* CSS-wide keywords and universal values that resolve as literal CSS even when
|
|
111
|
+
* ident resolution is config-first. A token may never take one of these names;
|
|
112
|
+
* config creation must reject it. Shared by the parser's ident resolution and
|
|
113
|
+
* config-time validation.
|
|
114
|
+
*/
|
|
115
|
+
export const reservedCssIdents: ReadonlySet<string> = new Set([
|
|
116
|
+
'inherit',
|
|
117
|
+
'initial',
|
|
118
|
+
'unset',
|
|
119
|
+
'revert',
|
|
120
|
+
'none',
|
|
121
|
+
'auto',
|
|
122
|
+
'transparent',
|
|
123
|
+
'currentColor',
|
|
124
|
+
])
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* One longhand's merged program: which CSS longhand, the parsed value that owns
|
|
128
|
+
* it, and which authored prop contributed it (for diagnostics and devtools).
|
|
129
|
+
*/
|
|
130
|
+
export interface LonghandProgram {
|
|
131
|
+
/** camelCase longhand matching React Native / react style keys */
|
|
132
|
+
property: string
|
|
133
|
+
value: ParsedValue
|
|
134
|
+
/** the authored prop name that won this longhand */
|
|
135
|
+
sourceProp: string
|
|
136
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ParsedValue } from "./valueTypes";
|
|
2
|
+
export declare const namedCssColors: ReadonlySet<string>;
|
|
3
|
+
export declare function splitTopLevelComponents(value: string): string[];
|
|
4
|
+
export declare function classifyComponent(component: string, colorTokens: ReadonlySet<string>): "color" | "image" | null;
|
|
5
|
+
export declare function splitBackgroundValue(value: ParsedValue, colorTokens: ReadonlySet<string>): {
|
|
6
|
+
entries: Array<{
|
|
7
|
+
property: "backgroundColor" | "backgroundImage";
|
|
8
|
+
value: ParsedValue;
|
|
9
|
+
}>;
|
|
10
|
+
errors: Array<{
|
|
11
|
+
code: "unsupported-bg-component";
|
|
12
|
+
component: string;
|
|
13
|
+
where: "base" | number;
|
|
14
|
+
}>;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
//# sourceMappingURL=backgroundFamily.d.ts.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"mappings": "AAAA,cAA4B,mBAAmB;AAE/C,OAAO,cAAM,gBAAgB;AAmB7B,OAAO,iBAAS,wBAAwB;AAiDxC,OAAO,iBAAS,kBACd,mBACA,aAAa,sBACZ,UAAU;AAyBb,OAAO,iBAAS,qBACd,OAAO,aACP,aAAa,sBACZ;CACD,SAAS,MAAM;EACb,UAAU,oBAAoB;EAC9B,OAAO;;CAET,QAAQ,MAAM;EACZ,MAAM;EACN;EACA,OAAO",
|
|
3
|
+
"names": [],
|
|
4
|
+
"sources": [
|
|
5
|
+
"src/backgroundFamily.ts"
|
|
6
|
+
],
|
|
7
|
+
"version": 3,
|
|
8
|
+
"sourcesContent": [
|
|
9
|
+
"import type { ParsedClause, ParsedValue } from './valueTypes'\n\nexport const namedCssColors: ReadonlySet<string> = new Set(\n 'aliceblue antiquewhite aqua aquamarine azure beige bisque black blanchedalmond blue blueviolet brown burlywood cadetblue chartreuse chocolate coral cornflowerblue cornsilk crimson cyan darkblue darkcyan darkgoldenrod darkgray darkgreen darkgrey darkkhaki darkmagenta darkolivegreen darkorange darkorchid darkred darksalmon darkseagreen darkslateblue darkslategray darkslategrey darkturquoise darkviolet deeppink deepskyblue dimgray dimgrey dodgerblue firebrick floralwhite forestgreen fuchsia gainsboro ghostwhite gold goldenrod gray green greenyellow grey honeydew hotpink indianred indigo ivory khaki lavender lavenderblush lawngreen lemonchiffon lightblue lightcoral lightcyan lightgoldenrodyellow lightgray lightgreen lightgrey lightpink lightsalmon lightseagreen lightskyblue lightslategray lightslategrey lightsteelblue lightyellow lime limegreen linen magenta maroon mediumaquamarine mediumblue mediumorchid mediumpurple mediumseagreen mediumslateblue mediumspringgreen mediumturquoise mediumvioletred midnightblue mintcream mistyrose moccasin navajowhite navy oldlace olive olivedrab orange orangered orchid palegoldenrod palegreen paleturquoise palevioletred papayawhip peachpuff peru pink plum powderblue purple rebeccapurple red rosybrown royalblue saddlebrown salmon sandybrown seagreen seashell sienna silver skyblue slateblue slategray slategrey snow springgreen steelblue tan teal thistle tomato turquoise violet wheat white whitesmoke yellow yellowgreen'.split(\n ' '\n )\n)\n\nconst colorFunctions = new Set([\n 'rgb',\n 'rgba',\n 'hsl',\n 'hsla',\n 'hwb',\n 'lab',\n 'lch',\n 'oklab',\n 'oklch',\n 'color',\n])\n\nexport function splitTopLevelComponents(value: string): string[] {\n const components: string[] = []\n let current = ''\n let depth = 0\n let quote = ''\n let escaped = false\n\n for (let index = 0; index < value.length; index++) {\n const char = value[index]\n if (escaped) {\n current += char\n escaped = false\n continue\n }\n if (char === '\\\\') {\n current += char\n escaped = true\n continue\n }\n if (quote) {\n current += char\n if (char === quote) quote = ''\n continue\n }\n if (char === '\"' || char === \"'\") {\n current += char\n quote = char\n continue\n }\n if (char === '(') depth++\n if (char === ')') depth--\n\n const code = value.charCodeAt(index)\n const whitespace =\n code === 9 || code === 10 || code === 12 || code === 13 || code === 32\n if (depth === 0 && whitespace) {\n if (current) {\n components.push(current)\n current = ''\n }\n } else {\n current += char\n }\n }\n\n if (current) components.push(current)\n return components\n}\n\nexport function classifyComponent(\n component: string,\n colorTokens: ReadonlySet<string>\n): 'color' | 'image' | null {\n const functionMatch = /^([a-z][a-z0-9-]*)\\([\\s\\S]*\\)$/i.exec(component)\n if (functionMatch) {\n const name = functionMatch[1].toLowerCase()\n if (name === 'url' || name === 'image-set' || name.endsWith('-gradient')) {\n return 'image'\n }\n if (colorFunctions.has(name)) return 'color'\n }\n\n if (/^#[\\da-f]{3}(?:[\\da-f]{1}|[\\da-f]{3}(?:[\\da-f]{2})?)?$/i.test(component)) {\n return 'color'\n }\n\n const tokenName = component.replace(/\\/\\d+$/, '')\n if (colorTokens.has(tokenName)) return 'color'\n\n const lower = component.toLowerCase()\n if (lower === 'transparent' || lower === 'currentcolor' || namedCssColors.has(lower)) {\n return 'color'\n }\n\n return null\n}\n\nexport function splitBackgroundValue(\n value: ParsedValue,\n colorTokens: ReadonlySet<string>\n): {\n entries: Array<{\n property: 'backgroundColor' | 'backgroundImage'\n value: ParsedValue\n }>\n errors: Array<{\n code: 'unsupported-bg-component'\n component: string\n where: 'base' | number\n }>\n} {\n const colorProgram: { base: string | null; clauses: ParsedClause[] } = {\n base: null,\n clauses: [],\n }\n const imageProgram: { base: string | null; clauses: ParsedClause[] } = {\n base: null,\n clauses: [],\n }\n const errors: Array<{\n code: 'unsupported-bg-component'\n component: string\n where: 'base' | number\n }> = []\n let hasColor = false\n let hasImage = false\n\n for (let clauseIndex = -1; clauseIndex < value.clauses.length; clauseIndex++) {\n const isBase = clauseIndex === -1\n const payload = isBase ? value.base : value.clauses[clauseIndex].payload\n if (payload === null) continue\n\n let colorComponent: string | null = null\n let imageComponent: string | null = null\n const where = isBase ? 'base' : clauseIndex\n\n for (const component of splitTopLevelComponents(payload)) {\n const kind = classifyComponent(component, colorTokens)\n if (kind === 'image' && imageComponent === null) {\n imageComponent = component\n } else if (kind === 'color' && colorComponent === null) {\n colorComponent = component\n } else {\n errors.push({ code: 'unsupported-bg-component', component, where })\n }\n }\n\n if (colorComponent !== null) {\n hasColor = true\n if (isBase) {\n colorProgram.base = colorComponent\n } else {\n colorProgram.clauses.push({\n modifiers: value.clauses[clauseIndex].modifiers,\n payload: colorComponent,\n })\n }\n }\n\n if (imageComponent !== null) {\n hasImage = true\n if (isBase) {\n imageProgram.base = imageComponent\n } else {\n imageProgram.clauses.push({\n modifiers: value.clauses[clauseIndex].modifiers,\n payload: imageComponent,\n })\n }\n }\n }\n\n const entries: Array<{\n property: 'backgroundColor' | 'backgroundImage'\n value: ParsedValue\n }> = []\n if (hasColor) entries.push({ property: 'backgroundColor', value: colorProgram })\n if (hasImage) entries.push({ property: 'backgroundImage', value: imageProgram })\n\n return { entries, errors }\n}\n"
|
|
10
|
+
]
|
|
11
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ParsedValue } from "./valueTypes";
|
|
2
|
+
interface BorderFamilyTargets {
|
|
3
|
+
width: readonly string[];
|
|
4
|
+
style: readonly string[];
|
|
5
|
+
color: readonly string[];
|
|
6
|
+
}
|
|
7
|
+
/** authored prop -> the true longhands each component kind lands on */
|
|
8
|
+
export declare const borderFamilyTargets: Readonly<Record<string, BorderFamilyTargets>>;
|
|
9
|
+
export interface BorderFamilyError {
|
|
10
|
+
code: "unsupported-border-component";
|
|
11
|
+
component: string;
|
|
12
|
+
where: "base" | number;
|
|
13
|
+
}
|
|
14
|
+
export declare function splitBorderValue(property: string, value: ParsedValue, colorTokens: ReadonlySet<string>): {
|
|
15
|
+
entries: Array<{
|
|
16
|
+
property: string;
|
|
17
|
+
value: ParsedValue;
|
|
18
|
+
}>;
|
|
19
|
+
errors: BorderFamilyError[];
|
|
20
|
+
};
|
|
21
|
+
export {};
|
|
22
|
+
|
|
23
|
+
//# sourceMappingURL=borderFamily.d.ts.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"mappings": "AASA,cAA4B,mBAAmB;UAErC,oBAAoB;CAC5B;CACA;CACA;;;AAMF,OAAO,cAAM,qBAAqB,SAAS,eAAe;AAuE1D,iBAAiB,kBAAkB;CACjC,MAAM;CACN;CACA,OAAO;;AAST,OAAO,iBAAS,iBACd,kBACA,OAAO,aACP,aAAa,sBACZ;CACD,SAAS,MAAM;EAAE;EAAkB,OAAO;;CAC1C,QAAQ",
|
|
3
|
+
"names": [],
|
|
4
|
+
"sources": [
|
|
5
|
+
"src/borderFamily.ts"
|
|
6
|
+
],
|
|
7
|
+
"version": 3,
|
|
8
|
+
"sourcesContent": [
|
|
9
|
+
"// The border family: value-dependent splitting of the resetting CSS border\n// shorthands into per-longhand programs, the background family's model applied\n// to width/style/color. A program must never sit on a resetting shorthand —\n// the order-free cross-program encoding depends on every program owning one\n// true longhand — so `border=\"2px solid red hover:blue\"` becomes width, style,\n// and color programs per side, and a later `borderColor` restates exactly the\n// color longhands.\n\nimport { classifyComponent, splitTopLevelComponents } from './backgroundFamily'\nimport type { ParsedClause, ParsedValue } from './valueTypes'\n\ninterface BorderFamilyTargets {\n width: readonly string[]\n style: readonly string[]\n color: readonly string[]\n}\n\nconst sides = ['Top', 'Right', 'Bottom', 'Left'] as const\n\n/** authored prop -> the true longhands each component kind lands on */\nexport const borderFamilyTargets: Readonly<Record<string, BorderFamilyTargets>> = {\n border: {\n width: sides.map((side) => `border${side}Width`),\n style: sides.map((side) => `border${side}Style`),\n color: sides.map((side) => `border${side}Color`),\n },\n ...Object.fromEntries(\n sides.map((side) => [\n `border${side}`,\n {\n width: [`border${side}Width`],\n style: [`border${side}Style`],\n color: [`border${side}Color`],\n },\n ])\n ),\n outline: {\n width: ['outlineWidth'],\n style: ['outlineStyle'],\n color: ['outlineColor'],\n },\n // logical shorthands split into CSS logical longhands. web lowers them\n // verbatim; RN has no logical block/inline border properties, so native\n // evaluation diagnoses and drops these longhands (no silent physical\n // approximation — the mapping depends on writing mode)\n borderBlock: {\n width: ['borderBlockStartWidth', 'borderBlockEndWidth'],\n style: ['borderBlockStartStyle', 'borderBlockEndStyle'],\n color: ['borderBlockStartColor', 'borderBlockEndColor'],\n },\n borderInline: {\n width: ['borderInlineStartWidth', 'borderInlineEndWidth'],\n style: ['borderInlineStartStyle', 'borderInlineEndStyle'],\n color: ['borderInlineStartColor', 'borderInlineEndColor'],\n },\n}\n\nconst lineStyles = new Set([\n 'none',\n 'hidden',\n 'dotted',\n 'dashed',\n 'solid',\n 'double',\n 'groove',\n 'ridge',\n 'inset',\n 'outset',\n])\n\nconst widthKeywords = new Set(['thin', 'medium', 'thick'])\n\n// a length: bare number (RN spelling), number+unit, or a width keyword.\n// calc() and var() also measure as widths when nothing else claims them\nconst lengthPattern = /^-?(\\d+\\.?\\d*|\\.\\d+)([a-z%]+)?$/i\n\nfunction classifyBorderComponent(\n component: string,\n property: string,\n colorTokens: ReadonlySet<string>\n): 'width' | 'style' | 'color' | null {\n const lower = component.toLowerCase()\n if (lineStyles.has(lower)) return 'style'\n // outline-style additionally allows auto\n if (lower === 'auto' && property === 'outline') return 'style'\n if (widthKeywords.has(lower) || lengthPattern.test(component)) return 'width'\n if (classifyComponent(component, colorTokens) === 'color') return 'color'\n if (/^(calc|var|min|max|clamp)\\(/i.test(component)) return 'width'\n return null\n}\n\nexport interface BorderFamilyError {\n code: 'unsupported-border-component'\n component: string\n where: 'base' | number\n}\n\ninterface MutableProgram {\n base: string | null\n clauses: ParsedClause[]\n present: boolean\n}\n\nexport function splitBorderValue(\n property: string,\n value: ParsedValue,\n colorTokens: ReadonlySet<string>\n): {\n entries: Array<{ property: string; value: ParsedValue }>\n errors: BorderFamilyError[]\n} {\n const targets = borderFamilyTargets[property]\n const errors: BorderFamilyError[] = []\n const kinds = ['width', 'style', 'color'] as const\n const programs: Record<(typeof kinds)[number], MutableProgram> = {\n width: { base: null, clauses: [], present: false },\n style: { base: null, clauses: [], present: false },\n color: { base: null, clauses: [], present: false },\n }\n\n for (let clauseIndex = -1; clauseIndex < value.clauses.length; clauseIndex++) {\n const isBase = clauseIndex === -1\n const payload = isBase ? value.base : value.clauses[clauseIndex].payload\n if (payload === null) continue\n const where = isBase ? ('base' as const) : clauseIndex\n\n const found: Partial<Record<(typeof kinds)[number], string>> = {}\n for (const component of splitTopLevelComponents(payload)) {\n const kind = classifyBorderComponent(component, property, colorTokens)\n if (kind === null || found[kind] !== undefined) {\n errors.push({ code: 'unsupported-border-component', component, where })\n continue\n }\n found[kind] = component\n }\n\n // Carry the shorthand's effective zero width explicitly. Web receives an\n // equivalent reset, and native does not have to rely on style `none` alone\n // to hide an edge whose width was supplied by another contribution.\n if (found.style === 'none' && found.width === undefined) {\n found.width = '0'\n }\n\n for (const kind of kinds) {\n const component = found[kind]\n if (component === undefined) continue\n const program = programs[kind]\n program.present = true\n if (isBase) {\n program.base = component\n } else {\n program.clauses.push({\n modifiers: value.clauses[clauseIndex].modifiers,\n payload: component,\n })\n }\n }\n }\n\n const entries: Array<{ property: string; value: ParsedValue }> = []\n for (const kind of kinds) {\n const program = programs[kind]\n if (!program.present) continue\n const shared: ParsedValue = { base: program.base, clauses: program.clauses }\n for (const longhand of targets[kind]) {\n // programs are immutable, so every side may alias the same value\n entries.push({ property: longhand, value: shared })\n }\n }\n\n return { entries, errors }\n}\n"
|
|
10
|
+
]
|
|
11
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type GrammarEntry, type TokenCategory } from "./registry";
|
|
2
|
+
type Names = readonly string[] | ReadonlySet<string> | Readonly<Record<string, unknown>>;
|
|
3
|
+
export interface GrammarConfigView {
|
|
4
|
+
shorthands?: Readonly<Record<string, string>>;
|
|
5
|
+
mediaNames?: Names;
|
|
6
|
+
themeNames?: Names;
|
|
7
|
+
platformNames?: Names;
|
|
8
|
+
tokenNames?: Partial<Record<TokenCategory, Names>>;
|
|
9
|
+
/**
|
|
10
|
+
* the media keys that measure a size, so `@key:` is a meaningful container
|
|
11
|
+
* query. derived by createGrammarConfigView when the media input carries
|
|
12
|
+
* query information; absent means UNKNOWN, and the modifier registry then
|
|
13
|
+
* refuses container claims with a diagnostic rather than over-claiming
|
|
14
|
+
*/
|
|
15
|
+
containerSizeNames?: readonly string[];
|
|
16
|
+
}
|
|
17
|
+
export interface ParsedCandidate {
|
|
18
|
+
candidate: string;
|
|
19
|
+
base: string;
|
|
20
|
+
modifiers: readonly string[];
|
|
21
|
+
negative: boolean;
|
|
22
|
+
kind: "utility" | "dynamic";
|
|
23
|
+
valueKind: "token" | "arbitrary" | "enum" | "convenience";
|
|
24
|
+
properties?: Readonly<Record<string, string | number>>;
|
|
25
|
+
entry?: GrammarEntry;
|
|
26
|
+
prefix?: string;
|
|
27
|
+
rawValue?: string;
|
|
28
|
+
arbitrary?: boolean;
|
|
29
|
+
convenience?: string;
|
|
30
|
+
}
|
|
31
|
+
export type CandidateClassification = {
|
|
32
|
+
kind: "tamagui";
|
|
33
|
+
parsed: ParsedCandidate;
|
|
34
|
+
} | {
|
|
35
|
+
kind: "passthrough";
|
|
36
|
+
reason: string;
|
|
37
|
+
};
|
|
38
|
+
export declare function hasTokenName(config: GrammarConfigView, category: TokenCategory, name: string): boolean;
|
|
39
|
+
export declare function parseCandidate(candidate: string, config: GrammarConfigView): ParsedCandidate | null;
|
|
40
|
+
export declare function classifyCandidate(candidate: string, config: GrammarConfigView): CandidateClassification;
|
|
41
|
+
export interface FormatCandidateInput {
|
|
42
|
+
prop: string;
|
|
43
|
+
value: string;
|
|
44
|
+
valueKind: ParsedCandidate["valueKind"];
|
|
45
|
+
modifiers?: readonly string[];
|
|
46
|
+
}
|
|
47
|
+
export declare function formatCandidate({ prop, value, valueKind, modifiers }: FormatCandidateInput, config?: GrammarConfigView): string | null;
|
|
48
|
+
export declare function encodeArbitrary(value: string): string;
|
|
49
|
+
export declare function decodeArbitrary(value: string): string;
|
|
50
|
+
export {};
|
|
51
|
+
|
|
52
|
+
//# sourceMappingURL=candidate.d.ts.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"mappings": "AAAA,cAUO,mBACA,qBACA;KAMF,4BAA4B,sBAAsB,SAAS;AAEhE,iBAAiB,kBAAkB;CACjC,aAAa,SAAS;CACtB,aAAa;CACb,aAAa;CACb,gBAAgB;CAChB,aAAa,QAAQ,OAAO,eAAe;;;;;;;CAO3C;;AAGF,iBAAiB,gBAAgB;CAC/B;CACA;CACA;CACA;CACA,MAAM,YAAY;CAClB,WAAW,UAAU,cAAc,SAAS;CAC5C,aAAa,SAAS;CACtB,QAAQ;CACR;CACA;CACA;CACA;;AAGF,YAAY,0BACR;CAAE,MAAM;CAAW,QAAQ;IAC3B;CAAE,MAAM;CAAe;;AA0H3B,OAAO,iBAAS,aACd,QAAQ,mBACR,UAAU,eACV;AAqPF,OAAO,iBAAS,eACd,mBACA,QAAQ,oBACP;AAmFH,OAAO,iBAAS,kBACd,mBACA,QAAQ,oBACP;AAOH,iBAAiB,qBAAqB;CACpC;CACA;CACA,WAAW,gBAAgB;CAC3B;;AAuBF,OAAO,iBAAS,gBACd,EAAE,MAAM,OAAO,WAAW,aAAkB,sBAC5C,SAAS;AAsHX,OAAO,iBAAS,gBAAgB;AAchC,OAAO,iBAAS,gBAAgB",
|
|
3
|
+
"names": [],
|
|
4
|
+
"sources": [
|
|
5
|
+
"src/candidate.ts"
|
|
6
|
+
],
|
|
7
|
+
"version": 3,
|
|
8
|
+
"sourcesContent": [
|
|
9
|
+
"import {\n grammarEntries,\n fontWeightNames,\n modifierAliases,\n prefixToEntries,\n radiusCornerProps,\n standaloneValueProps,\n textAlignKeywords,\n wholeClassConveniences,\n wholeClassUtilities,\n type GrammarEntry,\n type TokenCategory,\n} from './registry'\nimport { createModifierRegistry } from './modifierRegistry'\nimport { splitColorOpacitySuffix } from './resolvePayload'\nimport { getSafeAreaEdge } from './safeAreaVariables'\nimport type { ModifierKind, ModifierRegistryView } from './valueTypes'\n\ntype Names = readonly string[] | ReadonlySet<string> | Readonly<Record<string, unknown>>\n\nexport interface GrammarConfigView {\n shorthands?: Readonly<Record<string, string>>\n mediaNames?: Names\n themeNames?: Names\n platformNames?: Names\n tokenNames?: Partial<Record<TokenCategory, Names>>\n /**\n * the media keys that measure a size, so `@key:` is a meaningful container\n * query. derived by createGrammarConfigView when the media input carries\n * query information; absent means UNKNOWN, and the modifier registry then\n * refuses container claims with a diagnostic rather than over-claiming\n */\n containerSizeNames?: readonly string[]\n}\n\nexport interface ParsedCandidate {\n candidate: string\n base: string\n modifiers: readonly string[]\n negative: boolean\n kind: 'utility' | 'dynamic'\n valueKind: 'token' | 'arbitrary' | 'enum' | 'convenience'\n properties?: Readonly<Record<string, string | number>>\n entry?: GrammarEntry\n prefix?: string\n rawValue?: string\n arbitrary?: boolean\n convenience?: string\n}\n\nexport type CandidateClassification =\n | { kind: 'tamagui'; parsed: ParsedCandidate }\n | { kind: 'passthrough'; reason: string }\n\nconst sizingConveniences = new Set(['full', 'auto', 'screen', 'min', 'max', 'fit'])\nconst fontGenerics = new Set(['sans', 'serif', 'mono'])\nconst numericPattern = /^\\d+(?:\\.\\d+)?$/\nconst extraPrefixes = [\n 'border-x',\n 'border-y',\n 'rounded-t',\n 'rounded-r',\n 'rounded-b',\n 'rounded-l',\n]\nconst negativeTokenProps = new Set([\n 'margin',\n 'marginTop',\n 'marginRight',\n 'marginBottom',\n 'marginLeft',\n 'marginHorizontal',\n 'marginVertical',\n 'top',\n 'right',\n 'bottom',\n 'left',\n 'inset',\n 'x',\n 'y',\n 'letterSpacing',\n])\nconst borderWidthKeywords = new Set(['thin', 'medium', 'thick'])\nconst ambiguousCssKeywords = new Set([\n 'auto',\n 'dashed',\n 'dotted',\n 'double',\n 'groove',\n 'hidden',\n 'inherit',\n 'initial',\n 'inset',\n 'none',\n 'outset',\n 'revert',\n 'revert-layer',\n 'ridge',\n 'solid',\n 'unset',\n])\nconst cssLengthUnits =\n '(?:px|em|rem|ex|ch|cap|ic|lh|rlh|vw|vh|vmin|vmax|svw|svh|svmin|svmax|lvw|lvh|lvmin|lvmax|dvw|dvh|dvmin|dvmax|cm|mm|q|in|pt|pc)'\nconst cssLengthPattern = new RegExp(`^-?(?:\\\\d+|\\\\d*\\\\.\\\\d+)${cssLengthUnits}$`, 'i')\n\nfunction hasName(names: Names | undefined, name: string): boolean {\n if (!names) return false\n if (Array.isArray(names)) return names.includes(name)\n if (names instanceof Set) return names.has(name)\n return Object.prototype.hasOwnProperty.call(names, name)\n}\n\nfunction splitCandidate(candidate: string): { modifiers: string[]; base: string } | null {\n const parts: string[] = []\n let current = ''\n let bracketDepth = 0\n let escaped = false\n for (const char of candidate) {\n if (escaped) {\n current += char\n escaped = false\n continue\n }\n if (char === '\\\\') {\n current += char\n escaped = true\n continue\n }\n if (char === '[') bracketDepth++\n if (char === ']') bracketDepth--\n if (char === ':' && bracketDepth === 0) {\n parts.push(current)\n current = ''\n } else {\n current += char\n }\n }\n if (!current || bracketDepth !== 0) return null\n return { modifiers: parts, base: current }\n}\n\nconst modifierRegistryCache = new WeakMap<GrammarConfigView, ModifierRegistryView>()\n\nfunction getModifierRegistry(config: GrammarConfigView): ModifierRegistryView {\n let registry = modifierRegistryCache.get(config)\n if (!registry) {\n registry = createModifierRegistry(config).registry\n modifierRegistryCache.set(config, registry)\n }\n return registry\n}\n\nfunction modifierKind(modifier: string, config: GrammarConfigView): ModifierKind | null {\n const canonical = modifierAliases[modifier] || modifier\n return getModifierRegistry(config).get(canonical) || null\n}\n\nfunction modifiersAreKnown(\n modifiers: readonly string[],\n config: GrammarConfigView\n): boolean {\n const seen = new Set<ModifierKind>()\n for (const modifier of modifiers) {\n const kind = modifierKind(modifier, config)\n if (!kind || seen.has(kind)) return false\n seen.add(kind)\n }\n return true\n}\n\nfunction hasTokenDomain(config: GrammarConfigView, category: TokenCategory): boolean {\n return config.tokenNames?.[category] !== undefined\n}\n\nexport function hasTokenName(\n config: GrammarConfigView,\n category: TokenCategory,\n name: string\n): boolean {\n return hasName(config.tokenNames?.[category], name)\n}\n\nfunction resolveEntries(\n prefix: string,\n config: GrammarConfigView\n): readonly GrammarEntry[] {\n const registered = prefixToEntries[prefix]\n if (registered) return registered\n if (prefix === 'border-x' || prefix === 'border-y') {\n const props =\n prefix === 'border-x'\n ? ['borderLeft', 'borderRight']\n : ['borderTop', 'borderBottom']\n return grammarEntries.filter((entry) =>\n props.some((prop) => entry.prop === `${prop}Width` || entry.prop === `${prop}Color`)\n )\n }\n if (prefix.startsWith('rounded-')) {\n const props = radiusCornerProps[prefix.slice('rounded-'.length)]\n return props ? grammarEntries.filter((entry) => props.includes(entry.prop)) : []\n }\n const expanded = config.shorthands?.[prefix]\n if (!expanded) return []\n return grammarEntries.filter((entry) => entry.prop === expanded)\n}\n\nfunction findPrefix(base: string, config: GrammarConfigView): string | null {\n const prefixes = new Set(Object.keys(prefixToEntries))\n for (const prefix of extraPrefixes) prefixes.add(prefix)\n for (const shorthand in config.shorthands) prefixes.add(shorthand)\n let found: string | null = null\n for (const prefix of prefixes) {\n if (base.startsWith(`${prefix}-`) && (!found || prefix.length > found.length)) {\n found = prefix\n }\n }\n return found\n}\n\nfunction arbitraryInner(rawValue: string): string | null {\n if (rawValue.length <= 2 || rawValue[0] !== '[' || rawValue.at(-1) !== ']') {\n return null\n }\n const inner = rawValue.slice(1, -1)\n return arbitraryValueIsBalanced(decodeArbitrary(inner)) ? inner : null\n}\n\nfunction arbitraryValueIsBalanced(value: string): boolean {\n const opening = new Set(['(', '[', '{'])\n const matching: Readonly<Record<string, string>> = { ')': '(', ']': '[', '}': '{' }\n const stack: string[] = []\n let quote = ''\n let escaped = false\n for (const char of value) {\n if (escaped) {\n escaped = false\n continue\n }\n if (char === '\\\\') {\n escaped = true\n continue\n }\n if (quote) {\n if (char === quote) quote = ''\n continue\n }\n if (char === '\"' || char === \"'\") {\n quote = char\n continue\n }\n if (opening.has(char)) {\n stack.push(char)\n continue\n }\n const expected = matching[char]\n if (expected && stack.pop() !== expected) return false\n }\n return !escaped && !quote && stack.length === 0\n}\n\nfunction fractionIsValid(value: string): boolean {\n const fraction = /^(\\d+)\\/(\\d+)$/.exec(value)\n return !!fraction && Number(fraction[2]) !== 0\n}\n\nfunction arbitraryBorderKind(value: string): 'width' | 'color' | null {\n if (/^-?(?:\\d+|\\d*\\.\\d+)$/.test(value)) return 'width'\n if (cssLengthPattern.test(value) || borderWidthKeywords.has(value)) return 'width'\n if (/^(?:calc|min|max|clamp)\\(/.test(value)) return 'width'\n if (ambiguousCssKeywords.has(value) || value.startsWith('var(')) return null\n if (\n value.startsWith('#') ||\n /^(?:rgb|hsl|hwb|lab|lch|oklab|oklch|color|color-mix|light-dark)\\(/.test(value) ||\n /^[a-zA-Z][a-zA-Z0-9-]*$/.test(value)\n ) {\n return 'color'\n }\n return null\n}\n\nfunction tokenLookupName(category: TokenCategory, value: string): string {\n if (category !== 'color') return value\n const suffix = splitColorOpacitySuffix(value)\n return suffix.kind === 'none' ? value : suffix.name\n}\n\nfunction chooseEntry(\n entries: readonly GrammarEntry[],\n prefix: string,\n rawValue: string,\n negative: boolean,\n config: GrammarConfigView\n): {\n entry: GrammarEntry\n valueKind: ParsedCandidate['valueKind']\n convenience?: string\n} | null {\n if (getSafeAreaEdge(rawValue)) {\n const entry = entries.find(\n ({ tokenCategory }) =>\n tokenCategory === 'space' ||\n tokenCategory === 'size' ||\n tokenCategory === 'radius' ||\n tokenCategory === 'fontSize' ||\n tokenCategory === 'lineHeight' ||\n tokenCategory === 'letterSpacing'\n )\n if (entry) {\n return { entry, valueKind: 'convenience', convenience: 'safe-area' }\n }\n }\n\n const arbitrary = arbitraryInner(rawValue)\n if (arbitrary !== null) {\n if (!arbitrary) return null\n if (prefix.startsWith('border')) {\n const width = entries.find((entry) => entry.prop.endsWith('Width'))\n const color = entries.find((entry) => entry.prop.endsWith('Color'))\n const kind = arbitraryBorderKind(decodeArbitrary(arbitrary))\n if (!kind || !width || !color) return null\n return {\n entry: kind === 'width' ? width : color,\n valueKind: 'arbitrary',\n }\n }\n if (prefix === 'text') {\n return {\n entry: entries.find((entry) => entry.prop === 'fontSize')!,\n valueKind: 'arbitrary',\n }\n }\n if (prefix === 'font') {\n return {\n entry: entries.find((entry) => entry.prop === 'fontFamily')!,\n valueKind: 'arbitrary',\n }\n }\n return { entry: entries[0], valueKind: 'arbitrary' }\n }\n\n if (prefix === 'text') {\n const fontSize = entries.find((entry) => entry.prop === 'fontSize')\n if (fontSize && hasTokenName(config, 'fontSize', rawValue)) {\n return { entry: fontSize, valueKind: 'token' }\n }\n if (textAlignKeywords.has(rawValue)) {\n return {\n entry: entries.find((entry) => entry.prop === 'textAlign')!,\n valueKind: 'enum',\n }\n }\n return null\n }\n\n if (prefix === 'font') {\n const fontFamily = entries.find((entry) => entry.prop === 'fontFamily')\n const fontWeight = entries.find((entry) => entry.prop === 'fontWeight')\n if (!fontFamily) return null\n const matchesFamily = hasTokenName(config, 'fontFamily', rawValue)\n const matchesWeight = fontWeight && hasTokenName(config, 'fontWeight', rawValue)\n // a name configured as both a family and a weight is ambiguous, like the\n // border width/color rule above\n if (matchesFamily && matchesWeight) return null\n if (matchesFamily) {\n return { entry: fontFamily, valueKind: 'token' }\n }\n if (matchesWeight) {\n return { entry: fontWeight, valueKind: 'token' }\n }\n return fontGenerics.has(rawValue)\n ? { entry: fontFamily, valueKind: 'convenience', convenience: 'font-generic' }\n : null\n }\n\n if (prefix.startsWith('border')) {\n const width = entries.find((entry) => entry.prop.endsWith('Width'))\n const color = entries.find((entry) => entry.prop.endsWith('Color'))\n const token = negative ? `-${rawValue}` : rawValue\n const matchesWidth = width && hasTokenName(config, 'space', token)\n const colorName = tokenLookupName('color', rawValue)\n const matchesColor = color && hasTokenName(config, 'color', colorName)\n if (matchesWidth && matchesColor) return null\n if (matchesWidth) {\n return { entry: width, valueKind: 'token' }\n }\n if (matchesColor) {\n return { entry: color, valueKind: 'token' }\n }\n return null\n }\n\n for (const entry of entries) {\n if (entry.tokenCategory) {\n const name = negative ? `-${rawValue}` : rawValue\n const tokenName = tokenLookupName(entry.tokenCategory, name)\n if (hasTokenName(config, entry.tokenCategory, tokenName)) {\n return { entry, valueKind: 'token' }\n }\n if (\n entry.tokenCategory === 'size' &&\n (sizingConveniences.has(rawValue) || fractionIsValid(rawValue))\n ) {\n return { entry, valueKind: 'convenience', convenience: 'sizing-keyword' }\n }\n if (entry.tokenCategory === 'zIndex' && numericPattern.test(rawValue)) {\n return { entry, valueKind: 'convenience', convenience: 'integer' }\n }\n continue\n }\n if (prefix === 'opacity' || prefix === 'scale') {\n if (numericPattern.test(rawValue)) {\n return { entry, valueKind: 'convenience', convenience: 'percentage' }\n }\n continue\n }\n if (entry.prop === 'zIndex' && numericPattern.test(rawValue)) {\n return { entry, valueKind: 'convenience', convenience: 'integer' }\n }\n }\n return null\n}\n\nexport function parseCandidate(\n candidate: string,\n config: GrammarConfigView\n): ParsedCandidate | null {\n const split = splitCandidate(candidate)\n if (!split || !modifiersAreKnown(split.modifiers, config)) {\n return null\n }\n const modifiers = split.modifiers.map(\n (modifier) => modifierAliases[modifier] || modifier\n )\n\n const negative = split.base[0] === '-'\n const core = negative ? split.base.slice(1) : split.base\n const prefix = findPrefix(core, config)\n let dynamic:\n | {\n prefix: string\n rawValue: string\n selected: NonNullable<ReturnType<typeof chooseEntry>>\n }\n | undefined\n if (prefix) {\n const rawValue = core.slice(prefix.length + 1)\n if (rawValue) {\n const entries = resolveEntries(prefix, config)\n const selected = chooseEntry(entries, prefix, rawValue, negative, config)\n if (\n selected &&\n (!negative ||\n (selected.valueKind === 'token' && negativeTokenProps.has(selected.entry.prop)))\n ) {\n dynamic = { prefix, rawValue, selected }\n // An exact configured token owns its spelling before a reserved whole utility. Other\n // dynamic forms defer to the whole utility and are used only when no whole form exists.\n if (selected.valueKind === 'token') {\n return {\n candidate,\n base: split.base,\n modifiers,\n negative,\n kind: 'dynamic',\n prefix,\n rawValue,\n arbitrary: arbitraryInner(rawValue) !== null,\n entry: selected.entry,\n valueKind: selected.valueKind,\n convenience: selected.convenience,\n }\n }\n }\n }\n }\n\n const direct = wholeClassUtilities[split.base]\n if (!negative && direct) {\n const convenience = wholeClassConveniences[split.base]\n return {\n candidate,\n base: split.base,\n modifiers,\n kind: 'utility',\n valueKind: convenience ? 'convenience' : 'enum',\n properties: direct,\n convenience,\n negative: false,\n }\n }\n if (dynamic) {\n return {\n candidate,\n base: split.base,\n modifiers,\n negative,\n kind: 'dynamic',\n prefix: dynamic.prefix,\n rawValue: dynamic.rawValue,\n arbitrary: arbitraryInner(dynamic.rawValue) !== null,\n entry: dynamic.selected.entry,\n valueKind: dynamic.selected.valueKind,\n convenience: dynamic.selected.convenience,\n }\n }\n return null\n}\n\nexport function classifyCandidate(\n candidate: string,\n config: GrammarConfigView\n): CandidateClassification {\n const parsed = parseCandidate(candidate, config)\n return parsed\n ? { kind: 'tamagui', parsed }\n : { kind: 'passthrough', reason: 'not in the configured Tamagui grammar' }\n}\n\nexport interface FormatCandidateInput {\n prop: string\n value: string\n valueKind: ParsedCandidate['valueKind']\n modifiers?: readonly string[]\n}\n\nfunction tokenValidationConfig(\n entry: GrammarEntry,\n value: string,\n config: GrammarConfigView | undefined\n): GrammarConfigView {\n const tokenNames: Partial<Record<TokenCategory, Names>> = {\n ...config?.tokenNames,\n }\n const category = entry.tokenCategory!\n if (tokenNames[category] === undefined) {\n tokenNames[category] = [tokenLookupName(category, value)]\n }\n for (const other of prefixToEntries[entry.prefix] || []) {\n if (other.tokenCategory && tokenNames[other.tokenCategory] === undefined) {\n tokenNames[other.tokenCategory] = []\n }\n }\n return { ...config, tokenNames }\n}\n\nexport function formatCandidate(\n { prop, value, valueKind, modifiers = [] }: FormatCandidateInput,\n config?: GrammarConfigView\n): string | null {\n const entry = grammarEntries.find((candidate) => candidate.prop === prop)\n if (!entry) return null\n if (valueKind === 'arbitrary' && value === '') return null\n if (!modifiersAreKnown(modifiers, config || {})) return null\n const normalizedModifiers = modifiers.map(\n (modifier) => modifierAliases[modifier] || modifier\n )\n\n if (valueKind === 'enum') {\n const whole = standaloneValueProps[prop]?.[value]\n if (whole) {\n const candidate = normalizedModifiers.length\n ? `${normalizedModifiers.join(':')}:${whole}`\n : whole\n if (config) {\n const parsed = parseCandidate(candidate, config)\n if (!parsed?.properties || parsed.properties[prop] !== value) return null\n }\n return candidate\n }\n if (prop === 'fontWeight') {\n const name = fontWeightNames[value]\n if (!name) return null\n const core = `font-${name}`\n if (String(wholeClassUtilities[core]?.fontWeight) !== value) return null\n const candidate = normalizedModifiers.length\n ? `${normalizedModifiers.join(':')}:${core}`\n : core\n if (config) {\n const parsed = parseCandidate(candidate, config)\n if (!parsed?.properties || String(parsed.properties.fontWeight) !== value) {\n return null\n }\n }\n return candidate\n }\n }\n if (!entry.prefix) return null\n if (valueKind === 'token') {\n if (!entry.tokenCategory) return null\n const sourceDomainKnown = !!config && hasTokenDomain(config, entry.tokenCategory)\n const lookupName = tokenLookupName(entry.tokenCategory, value)\n if (sourceDomainKnown && !hasTokenName(config!, entry.tokenCategory, lookupName))\n return null\n const colliding = prefixToEntries[entry.prefix].filter(\n (other) => other.prop !== prop && other.tokenCategory\n )\n if (colliding.length) {\n if (!config || !sourceDomainKnown) return null\n for (const other of colliding) {\n if (!hasTokenDomain(config, other.tokenCategory!)) return null\n if (hasTokenName(config, other.tokenCategory!, value)) return null\n }\n }\n }\n const formattedValue = valueKind === 'arbitrary' ? `[${encodeArbitrary(value)}]` : value\n const core =\n formattedValue[0] === '-' && formattedValue[1] !== '['\n ? `-${entry.prefix}-${formattedValue.slice(1)}`\n : formattedValue\n ? `${entry.prefix}-${formattedValue}`\n : entry.prefix\n const candidate = normalizedModifiers.length\n ? `${normalizedModifiers.join(':')}:${core}`\n : core\n const whole = wholeClassUtilities[core]\n if (whole) {\n if (valueKind === 'token') {\n if (!config || !hasTokenDomain(config, entry.tokenCategory!)) return null\n } else if (config) {\n const parsed = parseCandidate(candidate, config)\n if (parsed?.kind === 'utility') {\n return Object.prototype.hasOwnProperty.call(parsed.properties, prop) &&\n String(parsed.properties?.[prop]) === value\n ? candidate\n : null\n }\n return parsed?.entry?.prop === prop && parsed.valueKind === valueKind\n ? candidate\n : null\n } else if (\n Object.prototype.hasOwnProperty.call(whole, prop) &&\n String(whole[prop]) === value\n ) {\n return candidate\n } else {\n return null\n }\n }\n if (valueKind === 'token') {\n if (\n !hasTokenDomain(config || {}, entry.tokenCategory!) &&\n parseCandidate(candidate, config || {})\n ) {\n return null\n }\n const parsed = parseCandidate(candidate, tokenValidationConfig(entry, value, config))\n const parsedValue = parsed?.rawValue\n ? `${parsed.negative ? '-' : ''}${parsed.rawValue}`\n : null\n if (\n parsed?.kind !== 'dynamic' ||\n parsed.entry?.prop !== prop ||\n parsed.valueKind !== 'token' ||\n parsedValue !== value\n ) {\n return null\n }\n return candidate\n }\n const parsed = parseCandidate(candidate, config || {})\n if (!parsed || parsed.entry?.prop !== prop || parsed.valueKind !== valueKind)\n return null\n return candidate\n}\n\nexport function encodeArbitrary(value: string): string {\n let encoded = ''\n for (const char of value) {\n if (char === '\\\\' || char === '_' || char === '[' || char === ']') {\n encoded += `\\\\${char}`\n } else if (/\\s/.test(char)) {\n encoded += '_'\n } else {\n encoded += char\n }\n }\n return encoded\n}\n\nexport function decodeArbitrary(value: string): string {\n let decoded = ''\n for (let index = 0; index < value.length; index++) {\n const char = value[index]\n if (char === '\\\\' && index + 1 < value.length) {\n decoded += value[++index]\n } else {\n decoded += char === '_' ? ' ' : char\n }\n }\n return decoded\n}\n"
|
|
10
|
+
]
|
|
11
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { TokenCategory } from "./registry";
|
|
2
|
+
export interface CandidateContribution {
|
|
3
|
+
property: string;
|
|
4
|
+
/** the token category this contribution binds through, when config-derived */
|
|
5
|
+
tokenCategory?: TokenCategory;
|
|
6
|
+
}
|
|
7
|
+
export interface CandidatePropertyMismatch {
|
|
8
|
+
code: "candidate-property-mismatch";
|
|
9
|
+
candidate: string;
|
|
10
|
+
property: string;
|
|
11
|
+
contributedProperties: readonly string[];
|
|
12
|
+
message: string;
|
|
13
|
+
}
|
|
14
|
+
export type CandidateTargetResult<Contribution extends CandidateContribution> = {
|
|
15
|
+
ok: true;
|
|
16
|
+
contribution: Contribution;
|
|
17
|
+
} | {
|
|
18
|
+
ok: false;
|
|
19
|
+
diagnostic: CandidatePropertyMismatch;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* selects the contribution addressed by an overloaded family prop.
|
|
23
|
+
*
|
|
24
|
+
* candidate resolution happens before this function, so both the compiler and
|
|
25
|
+
* runtime pass the same family contributions here. A candidate is valid only
|
|
26
|
+
* when it contributes to the authored property itself. This keeps shared
|
|
27
|
+
* families such as `text-*` from accepting a color candidate for `fontSize`,
|
|
28
|
+
* or a font-size candidate for `color`.
|
|
29
|
+
*/
|
|
30
|
+
export declare function resolveCandidateTarget<Contribution extends CandidateContribution>(property: string, candidate: string, contributions: readonly Contribution[]): CandidateTargetResult<Contribution>;
|
|
31
|
+
|
|
32
|
+
//# sourceMappingURL=candidateTarget.d.ts.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"mappings": "AAAA,cAAc,qBAAqB;AAEnC,iBAAiB,sBAAsB;CACrC;;CAEA,gBAAgB;;AAGlB,iBAAiB,0BAA0B;CACzC,MAAM;CACN;CACA;CACA;CACA;;AAGF,YAAY,sBAAsB,qBAAqB,yBACnD;CACE,IAAI;CACJ,cAAc;IAEhB;CACE,IAAI;CACJ,YAAY;;;;;;;;;;;AAYlB,OAAO,iBAAS,uBAAuB,qBAAqB,uBAC1D,kBACA,mBACA,wBAAwB,iBACvB,sBAAsB",
|
|
3
|
+
"names": [],
|
|
4
|
+
"sources": [
|
|
5
|
+
"src/candidateTarget.ts"
|
|
6
|
+
],
|
|
7
|
+
"version": 3,
|
|
8
|
+
"sourcesContent": [
|
|
9
|
+
"import type { TokenCategory } from './registry'\n\nexport interface CandidateContribution {\n property: string\n /** the token category this contribution binds through, when config-derived */\n tokenCategory?: TokenCategory\n}\n\nexport interface CandidatePropertyMismatch {\n code: 'candidate-property-mismatch'\n candidate: string\n property: string\n contributedProperties: readonly string[]\n message: string\n}\n\nexport type CandidateTargetResult<Contribution extends CandidateContribution> =\n | {\n ok: true\n contribution: Contribution\n }\n | {\n ok: false\n diagnostic: CandidatePropertyMismatch\n }\n\n/**\n * selects the contribution addressed by an overloaded family prop.\n *\n * candidate resolution happens before this function, so both the compiler and\n * runtime pass the same family contributions here. A candidate is valid only\n * when it contributes to the authored property itself. This keeps shared\n * families such as `text-*` from accepting a color candidate for `fontSize`,\n * or a font-size candidate for `color`.\n */\nexport function resolveCandidateTarget<Contribution extends CandidateContribution>(\n property: string,\n candidate: string,\n contributions: readonly Contribution[]\n): CandidateTargetResult<Contribution> {\n for (const contribution of contributions) {\n if (contribution.property === property) {\n return { ok: true, contribution }\n }\n }\n\n const contributedProperties: string[] = []\n for (const contribution of contributions) {\n if (!contributedProperties.includes(contribution.property)) {\n contributedProperties.push(contribution.property)\n }\n }\n\n return {\n ok: false,\n diagnostic: {\n code: 'candidate-property-mismatch',\n candidate,\n property,\n contributedProperties,\n message:\n contributedProperties.length === 0\n ? `\"${candidate}\" does not contribute to \"${property}\"`\n : `\"${candidate}\" contributes to ${contributedProperties.map((name) => `\"${name}\"`).join(', ')}, not \"${property}\"`,\n },\n }\n}\n"
|
|
10
|
+
]
|
|
11
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { ModifierKind, ModifierRegistryView } from "./valueTypes";
|
|
2
|
+
export interface ClauseCapability {
|
|
3
|
+
/** the clause can lower to web CSS */
|
|
4
|
+
web: boolean;
|
|
5
|
+
/** the clause can evaluate on native */
|
|
6
|
+
native: boolean;
|
|
7
|
+
/** why a side is unsupported, for diagnostics */
|
|
8
|
+
note?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Per-modifier capability. `kind` comes from the same registry the value was
|
|
12
|
+
* parsed against; an unregistered modifier is a parse error upstream and
|
|
13
|
+
* reports unsupported on both targets here.
|
|
14
|
+
*/
|
|
15
|
+
export declare function clauseCapability(modifier: string, kind: ModifierKind | undefined): ClauseCapability;
|
|
16
|
+
/** file-extension intent: shared files must support BOTH targets */
|
|
17
|
+
export type ConversionTargets = "shared" | "web" | "native";
|
|
18
|
+
/**
|
|
19
|
+
* Host validity as the consumer already holds it — a projection of the
|
|
20
|
+
* component's staticConfig, never a table of this package's own.
|
|
21
|
+
*/
|
|
22
|
+
export interface HostView {
|
|
23
|
+
/** does this host accept `prop` as a style (validStyles + accept) */
|
|
24
|
+
accepts(prop: string): boolean;
|
|
25
|
+
/** for diagnostics: the component label the author sees */
|
|
26
|
+
displayName?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface ConversionReason {
|
|
29
|
+
dimension: "property" | "clause" | "host";
|
|
30
|
+
modifier?: string;
|
|
31
|
+
message: string;
|
|
32
|
+
/** the action the author takes, stated concretely */
|
|
33
|
+
remedy: string;
|
|
34
|
+
}
|
|
35
|
+
export interface ConversionAssessment {
|
|
36
|
+
/**
|
|
37
|
+
* clean: convertible AND evaluable where authored, with ALL THREE
|
|
38
|
+
* dimensions verified — clean is a promise, so an unchecked dimension can
|
|
39
|
+
* never produce it. needs-relocation: the conversion is syntactically
|
|
40
|
+
* right but this site was DETERMINED unable to evaluate it — the remedy
|
|
41
|
+
* names where it goes. unknown-host: property and clauses verified but the
|
|
42
|
+
* component identity could not be established, so host validity is
|
|
43
|
+
* UNVERIFIED rather than fine — the site needs review, not relocation.
|
|
44
|
+
* ineligible: this property cannot carry the clause spelling at all.
|
|
45
|
+
*/
|
|
46
|
+
verdict: "clean" | "needs-relocation" | "unknown-host" | "ineligible";
|
|
47
|
+
reasons: readonly ConversionReason[];
|
|
48
|
+
}
|
|
49
|
+
export interface ConversionInput {
|
|
50
|
+
property: string;
|
|
51
|
+
/** modifiers used across the value's clauses, deduplicated by the caller or not */
|
|
52
|
+
modifiers?: readonly string[];
|
|
53
|
+
targets: ConversionTargets;
|
|
54
|
+
/**
|
|
55
|
+
* absent means the consumer could NOT establish the component's identity
|
|
56
|
+
* (structural provenance proves only direct View/Text and traceable styled
|
|
57
|
+
* bases today) — the verdict is then at best 'unknown-host', never 'clean'
|
|
58
|
+
*/
|
|
59
|
+
host?: HostView;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The one call a converter, lint rule, or report makes before claiming a
|
|
63
|
+
* flat-value suggestion is safe. Composes property eligibility, per-target
|
|
64
|
+
* clause capability, and host validity; consumers never combine axes
|
|
65
|
+
* themselves.
|
|
66
|
+
*/
|
|
67
|
+
export declare function assessFlatConversion(input: ConversionInput, registry: ModifierRegistryView): ConversionAssessment;
|
|
68
|
+
|
|
69
|
+
//# sourceMappingURL=clauseCapability.d.ts.map
|