@velarscript/web 0.31.0 → 0.32.0
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/README.md +1 -1
- package/dist/analysis/calls/intrinsics/config.d.ts +10 -0
- package/dist/analysis/calls/intrinsics/config.d.ts.map +1 -0
- package/dist/analysis/calls/intrinsics/config.js +19 -0
- package/dist/analysis/calls/intrinsics/config.js.map +1 -0
- package/dist/analysis/calls/intrinsics/forms.d.ts +10 -0
- package/dist/analysis/calls/intrinsics/forms.d.ts.map +1 -0
- package/dist/analysis/calls/intrinsics/forms.js +38 -0
- package/dist/analysis/calls/intrinsics/forms.js.map +1 -0
- package/dist/analysis/calls/intrinsics/http.d.ts +10 -0
- package/dist/analysis/calls/intrinsics/http.d.ts.map +1 -0
- package/dist/analysis/calls/intrinsics/http.js +37 -0
- package/dist/analysis/calls/intrinsics/http.js.map +1 -0
- package/dist/analysis/calls/intrinsics/reactive.d.ts +10 -0
- package/dist/analysis/calls/intrinsics/reactive.d.ts.map +1 -0
- package/dist/analysis/calls/intrinsics/reactive.js +30 -0
- package/dist/analysis/calls/intrinsics/reactive.js.map +1 -0
- package/dist/analysis/calls/intrinsics/storage.d.ts +12 -0
- package/dist/analysis/calls/intrinsics/storage.d.ts.map +1 -0
- package/dist/analysis/calls/intrinsics/storage.js +86 -0
- package/dist/analysis/calls/intrinsics/storage.js.map +1 -0
- package/dist/analysis/calls/intrinsics/web.d.ts +10 -0
- package/dist/analysis/calls/intrinsics/web.d.ts.map +1 -0
- package/dist/analysis/calls/intrinsics/web.js +100 -0
- package/dist/analysis/calls/intrinsics/web.js.map +1 -0
- package/dist/analysis/calls/intrinsics.d.ts +13 -0
- package/dist/analysis/calls/intrinsics.d.ts.map +1 -0
- package/dist/analysis/calls/intrinsics.js +31 -0
- package/dist/analysis/calls/intrinsics.js.map +1 -0
- package/dist/analysis/components/contracts.d.ts +19 -0
- package/dist/analysis/components/contracts.d.ts.map +1 -0
- package/dist/analysis/components/contracts.js +83 -0
- package/dist/analysis/components/contracts.js.map +1 -0
- package/dist/analysis/components/declaration.d.ts +4 -0
- package/dist/analysis/components/declaration.d.ts.map +1 -0
- package/dist/analysis/components/declaration.js +163 -0
- package/dist/analysis/components/declaration.js.map +1 -0
- package/dist/analysis/components/host.d.ts +61 -0
- package/dist/analysis/components/host.d.ts.map +1 -0
- package/dist/analysis/components/host.js +22 -0
- package/dist/analysis/components/host.js.map +1 -0
- package/dist/analysis/components/lifecycle.d.ts +22 -0
- package/dist/analysis/components/lifecycle.d.ts.map +1 -0
- package/dist/analysis/components/lifecycle.js +33 -0
- package/dist/analysis/components/lifecycle.js.map +1 -0
- package/dist/analysis/components/ownership.d.ts +74 -0
- package/dist/analysis/components/ownership.d.ts.map +1 -0
- package/dist/analysis/components/ownership.js +106 -0
- package/dist/analysis/components/ownership.js.map +1 -0
- package/dist/analysis/declarations.d.ts +99 -0
- package/dist/analysis/declarations.d.ts.map +1 -0
- package/dist/analysis/declarations.js +176 -0
- package/dist/analysis/declarations.js.map +1 -0
- package/dist/analysis/inference.d.ts +67 -0
- package/dist/analysis/inference.d.ts.map +1 -0
- package/dist/analysis/inference.js +236 -0
- package/dist/analysis/inference.js.map +1 -0
- package/dist/analysis/jsx/attributes.d.ts +103 -0
- package/dist/analysis/jsx/attributes.d.ts.map +1 -0
- package/dist/analysis/jsx/attributes.js +601 -0
- package/dist/analysis/jsx/attributes.js.map +1 -0
- package/dist/analysis/jsx/elements.d.ts +17 -0
- package/dist/analysis/jsx/elements.d.ts.map +1 -0
- package/dist/analysis/jsx/elements.js +213 -0
- package/dist/analysis/jsx/elements.js.map +1 -0
- package/dist/analysis/jsx/host.d.ts +61 -0
- package/dist/analysis/jsx/host.d.ts.map +1 -0
- package/dist/analysis/jsx/host.js +15 -0
- package/dist/analysis/jsx/host.js.map +1 -0
- package/dist/analysis/jsx/keys.d.ts +15 -0
- package/dist/analysis/jsx/keys.d.ts.map +1 -0
- package/dist/analysis/jsx/keys.js +67 -0
- package/dist/analysis/jsx/keys.js.map +1 -0
- package/dist/analysis/jsx/security.d.ts +29 -0
- package/dist/analysis/jsx/security.d.ts.map +1 -0
- package/dist/analysis/jsx/security.js +61 -0
- package/dist/analysis/jsx/security.js.map +1 -0
- package/dist/analysis/jsx-detection.d.ts +49 -0
- package/dist/analysis/jsx-detection.d.ts.map +1 -0
- package/dist/analysis/jsx-detection.js +196 -0
- package/dist/analysis/jsx-detection.js.map +1 -0
- package/dist/analysis/keyed-rebuild.d.ts +148 -0
- package/dist/analysis/keyed-rebuild.d.ts.map +1 -0
- package/dist/analysis/keyed-rebuild.js +299 -0
- package/dist/analysis/keyed-rebuild.js.map +1 -0
- package/dist/analysis/keyframes-analysis.d.ts +6 -0
- package/dist/analysis/keyframes-analysis.d.ts.map +1 -0
- package/dist/analysis/keyframes-analysis.js +79 -0
- package/dist/analysis/keyframes-analysis.js.map +1 -0
- package/dist/analysis/look/builders.d.ts +29 -0
- package/dist/analysis/look/builders.d.ts.map +1 -0
- package/dist/analysis/look/builders.js +168 -0
- package/dist/analysis/look/builders.js.map +1 -0
- package/dist/analysis/look/conditions.d.ts +13 -0
- package/dist/analysis/look/conditions.d.ts.map +1 -0
- package/dist/analysis/look/conditions.js +84 -0
- package/dist/analysis/look/conditions.js.map +1 -0
- package/dist/analysis/look/edits.d.ts +25 -0
- package/dist/analysis/look/edits.d.ts.map +1 -0
- package/dist/analysis/look/edits.js +57 -0
- package/dist/analysis/look/edits.js.map +1 -0
- package/dist/analysis/look/entries.d.ts +56 -0
- package/dist/analysis/look/entries.d.ts.map +1 -0
- package/dist/analysis/look/entries.js +223 -0
- package/dist/analysis/look/entries.js.map +1 -0
- package/dist/analysis/look/host.d.ts +47 -0
- package/dist/analysis/look/host.d.ts.map +1 -0
- package/dist/analysis/look/host.js +16 -0
- package/dist/analysis/look/host.js.map +1 -0
- package/dist/analysis/look/tokens.d.ts +75 -0
- package/dist/analysis/look/tokens.d.ts.map +1 -0
- package/dist/analysis/look/tokens.js +151 -0
- package/dist/analysis/look/tokens.js.map +1 -0
- package/dist/analysis/look/values.d.ts +45 -0
- package/dist/analysis/look/values.d.ts.map +1 -0
- package/dist/analysis/look/values.js +148 -0
- package/dist/analysis/look/values.js.map +1 -0
- package/dist/analysis/look-conditions.d.ts +47 -0
- package/dist/analysis/look-conditions.d.ts.map +1 -0
- package/dist/analysis/look-conditions.js +157 -0
- package/dist/analysis/look-conditions.js.map +1 -0
- package/dist/analysis/look-sites.d.ts +54 -0
- package/dist/analysis/look-sites.d.ts.map +1 -0
- package/dist/analysis/look-sites.js +104 -0
- package/dist/analysis/look-sites.js.map +1 -0
- package/dist/analysis/look-vocabulary-guidance.d.ts +76 -0
- package/dist/analysis/look-vocabulary-guidance.d.ts.map +1 -0
- package/dist/analysis/look-vocabulary-guidance.js +338 -0
- package/dist/analysis/look-vocabulary-guidance.js.map +1 -0
- package/dist/analysis/media-conditions.d.ts +33 -0
- package/dist/analysis/media-conditions.d.ts.map +1 -0
- package/dist/analysis/media-conditions.js +92 -0
- package/dist/analysis/media-conditions.js.map +1 -0
- package/dist/analysis/program-passes.d.ts +106 -0
- package/dist/analysis/program-passes.d.ts.map +1 -0
- package/dist/analysis/program-passes.js +149 -0
- package/dist/analysis/program-passes.js.map +1 -0
- package/dist/analysis/reactivity/bindings.d.ts +37 -0
- package/dist/analysis/reactivity/bindings.d.ts.map +1 -0
- package/dist/analysis/reactivity/bindings.js +88 -0
- package/dist/analysis/reactivity/bindings.js.map +1 -0
- package/dist/analysis/reactivity/derived.d.ts +39 -0
- package/dist/analysis/reactivity/derived.d.ts.map +1 -0
- package/dist/analysis/reactivity/derived.js +80 -0
- package/dist/analysis/reactivity/derived.js.map +1 -0
- package/dist/analysis/reactivity/host.d.ts +38 -0
- package/dist/analysis/reactivity/host.d.ts.map +1 -0
- package/dist/analysis/reactivity/host.js +14 -0
- package/dist/analysis/reactivity/host.js.map +1 -0
- package/dist/analysis/reactivity/retired-accessors.d.ts +82 -0
- package/dist/analysis/reactivity/retired-accessors.d.ts.map +1 -0
- package/dist/analysis/reactivity/retired-accessors.js +118 -0
- package/dist/analysis/reactivity/retired-accessors.js.map +1 -0
- package/dist/analysis/renderable.d.ts +47 -0
- package/dist/analysis/renderable.d.ts.map +1 -0
- package/dist/analysis/renderable.js +105 -0
- package/dist/analysis/renderable.js.map +1 -0
- package/dist/analysis/routes.d.ts +64 -0
- package/dist/analysis/routes.d.ts.map +1 -0
- package/dist/analysis/routes.js +182 -0
- package/dist/analysis/routes.js.map +1 -0
- package/dist/analysis/url-attributes.d.ts +12 -0
- package/dist/analysis/url-attributes.d.ts.map +1 -0
- package/dist/analysis/url-attributes.js +48 -0
- package/dist/analysis/url-attributes.js.map +1 -0
- package/dist/analysis/watch-cycles.d.ts +102 -0
- package/dist/analysis/watch-cycles.d.ts.map +1 -1
- package/dist/analysis/watch-cycles.js +240 -1
- package/dist/analysis/watch-cycles.js.map +1 -1
- package/dist/analysis/watch-subject.d.ts +45 -0
- package/dist/analysis/watch-subject.d.ts.map +1 -0
- package/dist/analysis/watch-subject.js +272 -0
- package/dist/analysis/watch-subject.js.map +1 -0
- package/dist/analysis/web-types.d.ts +27 -0
- package/dist/analysis/web-types.d.ts.map +1 -0
- package/dist/analysis/web-types.js +170 -0
- package/dist/analysis/web-types.js.map +1 -0
- package/dist/analyzer.d.ts +18 -617
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +208 -4853
- package/dist/analyzer.js.map +1 -1
- package/dist/compiler.d.ts +20 -0
- package/dist/compiler.d.ts.map +1 -1
- package/dist/compiler.js +29 -864
- package/dist/compiler.js.map +1 -1
- package/dist/documentation.d.ts +28 -0
- package/dist/documentation.d.ts.map +1 -0
- package/dist/documentation.js +67 -0
- package/dist/documentation.js.map +1 -0
- package/dist/editor/completions.d.ts +12 -0
- package/dist/editor/completions.d.ts.map +1 -0
- package/dist/editor/completions.js +6 -0
- package/dist/editor/completions.js.map +1 -0
- package/dist/editor/jsx-completion.d.ts +12 -0
- package/dist/editor/jsx-completion.d.ts.map +1 -0
- package/dist/editor/jsx-completion.js +137 -0
- package/dist/editor/jsx-completion.js.map +1 -0
- package/dist/editor/look-completion.d.ts +29 -0
- package/dist/editor/look-completion.d.ts.map +1 -0
- package/dist/editor/look-completion.js +159 -0
- package/dist/editor/look-completion.js.map +1 -0
- package/dist/editor.d.ts.map +1 -1
- package/dist/editor.js +12 -288
- package/dist/editor.js.map +1 -1
- package/dist/emit/components.d.ts +33 -0
- package/dist/emit/components.d.ts.map +1 -0
- package/dist/emit/components.js +135 -0
- package/dist/emit/components.js.map +1 -0
- package/dist/emit/jsx.d.ts +57 -0
- package/dist/emit/jsx.d.ts.map +1 -0
- package/dist/emit/jsx.js +384 -0
- package/dist/emit/jsx.js.map +1 -0
- package/dist/emit/look-css.d.ts +44 -0
- package/dist/emit/look-css.d.ts.map +1 -0
- package/dist/emit/look-css.js +177 -0
- package/dist/emit/look-css.js.map +1 -0
- package/dist/emit/look.d.ts +39 -0
- package/dist/emit/look.d.ts.map +1 -0
- package/dist/emit/look.js +218 -0
- package/dist/emit/look.js.map +1 -0
- package/dist/emit/runtime-imports.d.ts +40 -0
- package/dist/emit/runtime-imports.d.ts.map +1 -0
- package/dist/emit/runtime-imports.js +184 -0
- package/dist/emit/runtime-imports.js.map +1 -0
- package/dist/emitter.d.ts +15 -68
- package/dist/emitter.d.ts.map +1 -1
- package/dist/emitter.js +79 -1008
- package/dist/emitter.js.map +1 -1
- package/dist/look/animation.d.ts +7 -0
- package/dist/look/animation.d.ts.map +1 -0
- package/dist/look/animation.js +13 -0
- package/dist/look/animation.js.map +1 -0
- package/dist/look/builders.d.ts +35 -0
- package/dist/look/builders.d.ts.map +1 -0
- package/dist/look/builders.js +74 -0
- package/dist/look/builders.js.map +1 -0
- package/dist/look/css-functions.d.ts +28 -0
- package/dist/look/css-functions.d.ts.map +1 -0
- package/dist/look/css-functions.js +54 -0
- package/dist/look/css-functions.js.map +1 -0
- package/dist/look/keyword-sets.d.ts +41 -0
- package/dist/look/keyword-sets.d.ts.map +1 -0
- package/dist/look/keyword-sets.js +117 -0
- package/dist/look/keyword-sets.js.map +1 -0
- package/dist/look/keywords.d.ts +55 -0
- package/dist/look/keywords.d.ts.map +1 -0
- package/dist/look/keywords.js +387 -0
- package/dist/look/keywords.js.map +1 -0
- package/dist/look/media.d.ts +19 -0
- package/dist/look/media.d.ts.map +1 -0
- package/dist/look/media.js +27 -0
- package/dist/look/media.js.map +1 -0
- package/dist/look/naming.d.ts +9 -0
- package/dist/look/naming.d.ts.map +1 -0
- package/dist/look/naming.js +45 -0
- package/dist/look/naming.js.map +1 -0
- package/dist/look/properties.d.ts +16 -0
- package/dist/look/properties.d.ts.map +1 -0
- package/dist/look/properties.js +52 -0
- package/dist/look/properties.js.map +1 -0
- package/dist/look/shorthands.d.ts +25 -0
- package/dist/look/shorthands.d.ts.map +1 -0
- package/dist/look/shorthands.js +130 -0
- package/dist/look/shorthands.js.map +1 -0
- package/dist/look/tokens.d.ts +38 -0
- package/dist/look/tokens.d.ts.map +1 -0
- package/dist/look/tokens.js +59 -0
- package/dist/look/tokens.js.map +1 -0
- package/dist/look/units.d.ts +24 -0
- package/dist/look/units.d.ts.map +1 -0
- package/dist/look/units.js +33 -0
- package/dist/look/units.js.map +1 -0
- package/dist/look.d.ts +32 -227
- package/dist/look.d.ts.map +1 -1
- package/dist/look.js +28 -933
- package/dist/look.js.map +1 -1
- package/dist/module-roster.d.ts +16 -0
- package/dist/module-roster.d.ts.map +1 -0
- package/dist/module-roster.js +28 -0
- package/dist/module-roster.js.map +1 -0
- package/dist/modules/app.d.ts +9 -0
- package/dist/modules/app.d.ts.map +1 -0
- package/dist/modules/app.js +20 -0
- package/dist/modules/app.js.map +1 -0
- package/dist/modules/browser-test.d.ts +9 -0
- package/dist/modules/browser-test.d.ts.map +1 -0
- package/dist/modules/browser-test.js +67 -0
- package/dist/modules/browser-test.js.map +1 -0
- package/dist/modules/browser.d.ts +10 -0
- package/dist/modules/browser.d.ts.map +1 -0
- package/dist/modules/browser.js +89 -0
- package/dist/modules/browser.js.map +1 -0
- package/dist/modules/config.d.ts +9 -0
- package/dist/modules/config.d.ts.map +1 -0
- package/dist/modules/config.js +14 -0
- package/dist/modules/config.js.map +1 -0
- package/dist/modules/files.d.ts +9 -0
- package/dist/modules/files.d.ts.map +1 -0
- package/dist/modules/files.js +16 -0
- package/dist/modules/files.js.map +1 -0
- package/dist/modules/forms.d.ts +9 -0
- package/dist/modules/forms.d.ts.map +1 -0
- package/dist/modules/forms.js +26 -0
- package/dist/modules/forms.js.map +1 -0
- package/dist/modules/http.d.ts +9 -0
- package/dist/modules/http.d.ts.map +1 -0
- package/dist/modules/http.js +127 -0
- package/dist/modules/http.js.map +1 -0
- package/dist/modules/look.d.ts +9 -0
- package/dist/modules/look.d.ts.map +1 -0
- package/dist/modules/look.js +58 -0
- package/dist/modules/look.js.map +1 -0
- package/dist/modules/realtime.d.ts +9 -0
- package/dist/modules/realtime.d.ts.map +1 -0
- package/dist/modules/realtime.js +163 -0
- package/dist/modules/realtime.js.map +1 -0
- package/dist/modules/storage.d.ts +9 -0
- package/dist/modules/storage.d.ts.map +1 -0
- package/dist/modules/storage.js +59 -0
- package/dist/modules/storage.js.map +1 -0
- package/dist/modules/types.d.ts +78 -0
- package/dist/modules/types.d.ts.map +1 -0
- package/dist/modules/types.js +154 -0
- package/dist/modules/types.js.map +1 -0
- package/dist/modules/web.d.ts +9 -0
- package/dist/modules/web.d.ts.map +1 -0
- package/dist/modules/web.js +63 -0
- package/dist/modules/web.js.map +1 -0
- package/dist/modules/websocket.d.ts +11 -0
- package/dist/modules/websocket.d.ts.map +1 -0
- package/dist/modules/websocket.js +75 -0
- package/dist/modules/websocket.js.map +1 -0
- package/dist/parser/expressions/jsx.d.ts +17 -0
- package/dist/parser/expressions/jsx.d.ts.map +1 -0
- package/dist/parser/expressions/jsx.js +43 -0
- package/dist/parser/expressions/jsx.js.map +1 -0
- package/dist/parser/expressions/keyframes.d.ts +15 -0
- package/dist/parser/expressions/keyframes.d.ts.map +1 -0
- package/dist/parser/expressions/keyframes.js +33 -0
- package/dist/parser/expressions/keyframes.js.map +1 -0
- package/dist/parser/expressions/look.d.ts +16 -0
- package/dist/parser/expressions/look.d.ts.map +1 -0
- package/dist/parser/expressions/look.js +37 -0
- package/dist/parser/expressions/look.js.map +1 -0
- package/dist/parser/expressions/visual-block.d.ts +17 -0
- package/dist/parser/expressions/visual-block.d.ts.map +1 -0
- package/dist/parser/expressions/visual-block.js +21 -0
- package/dist/parser/expressions/visual-block.js.map +1 -0
- package/dist/parser/keyframes-source.d.ts +24 -0
- package/dist/parser/keyframes-source.d.ts.map +1 -0
- package/dist/parser/keyframes-source.js +133 -0
- package/dist/parser/keyframes-source.js.map +1 -0
- package/dist/parser/look-source.d.ts +25 -0
- package/dist/parser/look-source.d.ts.map +1 -0
- package/dist/parser/look-source.js +189 -0
- package/dist/parser/look-source.js.map +1 -0
- package/dist/parser/spans.d.ts +24 -0
- package/dist/parser/spans.d.ts.map +1 -0
- package/dist/parser/spans.js +113 -0
- package/dist/parser/spans.js.map +1 -0
- package/dist/parser/statements/components.d.ts +37 -0
- package/dist/parser/statements/components.d.ts.map +1 -0
- package/dist/parser/statements/components.js +185 -0
- package/dist/parser/statements/components.js.map +1 -0
- package/dist/parser/statements/heads.d.ts +38 -0
- package/dist/parser/statements/heads.d.ts.map +1 -0
- package/dist/parser/statements/heads.js +55 -0
- package/dist/parser/statements/heads.js.map +1 -0
- package/dist/parser/statements/imports.d.ts +9 -0
- package/dist/parser/statements/imports.d.ts.map +1 -0
- package/dist/parser/statements/imports.js +31 -0
- package/dist/parser/statements/imports.js.map +1 -0
- package/dist/parser/statements/reactive.d.ts +31 -0
- package/dist/parser/statements/reactive.d.ts.map +1 -0
- package/dist/parser/statements/reactive.js +68 -0
- package/dist/parser/statements/reactive.js.map +1 -0
- package/dist/parser/statements/unsafe-css.d.ts +21 -0
- package/dist/parser/statements/unsafe-css.d.ts.map +1 -0
- package/dist/parser/statements/unsafe-css.js +42 -0
- package/dist/parser/statements/unsafe-css.js.map +1 -0
- package/dist/parser.d.ts +37 -50
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +98 -882
- package/dist/parser.js.map +1 -1
- package/package.json +2 -2
package/dist/emitter.js
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { JavaScriptEmitter, spanIdentity, VELAR_ERROR_NORMALIZATION_MODULE, VELAR_RUNTIME_REGISTRY_KEY } from "@velarscript/compiler/extension";
|
|
6
|
-
import { CSS_STRING_RUNTIME_BODY, LOOK_ARITHMETIC_RUNTIME, WEB_FILE_TYPE_RUNTIME, WEB_LOCAL_REACTIVE_BRIDGE_RUNTIME, WEB_LOCAL_REACTIVE_COLLECTION_BRIDGE_RUNTIME, WEB_RUNTIME_BODY, WEB_RUNTIME_FOUNDATION, WEB_RUNTIME_FOUNDATION_SHARED_ERROR, } from "./runtime-sources.generated.js";
|
|
1
|
+
import { JavaScriptEmitter, spanIdentity } from "@velarscript/compiler/extension";
|
|
2
|
+
import { LOOK_ARITHMETIC_HINT } from "./look.js";
|
|
3
|
+
import { isLookStaticValue } from "./look-static.js";
|
|
4
|
+
import { keyframesCanonical, keyframesName } from "./keyframes.js";
|
|
7
5
|
import { isWebExpression, isWebJsx, isWebKeyframes, isWebLook, isWebStatement, isWebUnit, webExpressionContainsDirectAwait, webStatementContainsDirectAwait, webWatchSubjectLabel, } from "./ast.js";
|
|
6
|
+
import { emitComponent, emitReactiveAssignment } from "./emit/components.js";
|
|
7
|
+
import { emitJsx } from "./emit/jsx.js";
|
|
8
|
+
import { emitLook, emitLookArithmetic, prepareLooks } from "./emit/look.js";
|
|
9
|
+
import { additionalHelpers, containsUnitLiteral, containsWebSyntax, detachedTaskHelpers, emitIsCheck, emitTypeCheck, includesErrorNormalizationRuntime, reactiveBridgeHelpers, visitLookExpressions, } from "./emit/runtime-imports.js";
|
|
10
|
+
// D115 P4 R3d: the JSX recognizers and the scalar-text hint are the emitter's
|
|
11
|
+
// vocabulary, shared with the analyzer that diagnoses what emission demotes.
|
|
12
|
+
// They live with the lowering that defines them and reach their existing
|
|
13
|
+
// importers from here, so no import path moves.
|
|
14
|
+
export { dynamicChildLeaves, jsxKeyedList, JSX_SCALAR_TEXT_HINT, } from "./emit/jsx.js";
|
|
8
15
|
/**
|
|
9
16
|
* Statements whose body is not module evaluation. A `def`, a class method, and
|
|
10
17
|
* a `test` run when something calls them, and reach that caller's failure
|
|
@@ -47,22 +54,29 @@ export class WebJavaScriptEmitter extends JavaScriptEmitter {
|
|
|
47
54
|
lookKeywordProperties = new Set();
|
|
48
55
|
jsxId = 0;
|
|
49
56
|
keyframeNames = new Map();
|
|
57
|
+
/**
|
|
58
|
+
* D115 P4 R3d: the four emission families this emitter owns as collaborators
|
|
59
|
+
* rather than as more of itself, and the one host object they reach it
|
|
60
|
+
* through. Every `protected` seam is still declared on this class, forwarding
|
|
61
|
+
* to the family that owns its body, so the base emitter reaches the same seam
|
|
62
|
+
* it always did.
|
|
63
|
+
*/
|
|
64
|
+
host;
|
|
50
65
|
constructor(hints, forcedFunctionExports = new Set(), resourceContents = new Map(), extensionImports = new Map(), options = {}) {
|
|
51
66
|
super(hints, forcedFunctionExports, options);
|
|
52
67
|
this.resourceContents = resourceContents;
|
|
53
68
|
this.importedLookStaticValues = new Map([...(extensionImports.get("@velarscript/web") ?? [])]
|
|
54
69
|
.filter((entry) => isLookStaticValue(entry[1])));
|
|
70
|
+
this.host = this.emitHost();
|
|
55
71
|
}
|
|
56
72
|
emit(program) {
|
|
57
|
-
this.
|
|
58
|
-
this.prepareLooks(program);
|
|
73
|
+
prepareLooks(this.host, program);
|
|
59
74
|
this.webOutput = containsWebSyntax(program);
|
|
60
75
|
this.needsFileTypeHelper = false;
|
|
61
76
|
this.needsLookArithmeticRuntime = [...this.hints.extensionCalls.values()].includes(LOOK_ARITHMETIC_HINT);
|
|
62
77
|
this.moduleEvaluation = true;
|
|
63
78
|
return super.emit(program);
|
|
64
79
|
}
|
|
65
|
-
/** Emits `render` with module evaluation switched off, for a body that runs under an owner of its own. */
|
|
66
80
|
outsideModuleEvaluation(render) {
|
|
67
81
|
const previous = this.moduleEvaluation;
|
|
68
82
|
this.moduleEvaluation = false;
|
|
@@ -83,132 +97,22 @@ export class WebJavaScriptEmitter extends JavaScriptEmitter {
|
|
|
83
97
|
return this.webOutput;
|
|
84
98
|
}
|
|
85
99
|
emitTypeCheck(type, value, state = "undefined") {
|
|
86
|
-
|
|
87
|
-
if (type.name === "Event" || type.name === "KeyboardEvent" || type.name === "PointerEvent" || type.name === "InputEvent" || type.name === "CompositionEvent" || type.name === "ClipboardEvent") {
|
|
88
|
-
return `(typeof ${type.name} !== "undefined" && ${value} instanceof ${type.name})`;
|
|
89
|
-
}
|
|
90
|
-
if (type.name === "Element")
|
|
91
|
-
return `(typeof Element !== "undefined" && ${value} instanceof Element)`;
|
|
92
|
-
if (type.name === "CanvasElement")
|
|
93
|
-
return `(typeof HTMLCanvasElement !== "undefined" && ${value} instanceof HTMLCanvasElement)`;
|
|
94
|
-
if (type.name === "DialogElement")
|
|
95
|
-
return `(typeof HTMLDialogElement !== "undefined" && ${value} instanceof HTMLDialogElement)`;
|
|
96
|
-
if (type.name === "InputElement") {
|
|
97
|
-
return `((typeof HTMLInputElement !== "undefined" && ${value} instanceof HTMLInputElement) || (typeof HTMLSelectElement !== "undefined" && ${value} instanceof HTMLSelectElement) || (typeof HTMLTextAreaElement !== "undefined" && ${value} instanceof HTMLTextAreaElement))`;
|
|
98
|
-
}
|
|
99
|
-
if (type.name === "TextAreaElement")
|
|
100
|
-
return `(typeof HTMLTextAreaElement !== "undefined" && ${value} instanceof HTMLTextAreaElement)`;
|
|
101
|
-
if (type.name === "Blob")
|
|
102
|
-
return `(typeof Blob !== "undefined" && ${value} instanceof Blob)`;
|
|
103
|
-
if (type.name === "File") {
|
|
104
|
-
this.needsFileTypeHelper = true;
|
|
105
|
-
return `__velarFileTypeIs(${value})`;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
return super.emitTypeCheck(type, value, state);
|
|
100
|
+
return emitTypeCheck(this.host, type, value, state);
|
|
109
101
|
}
|
|
110
102
|
emitIsCheck(type, value) {
|
|
111
|
-
|
|
112
|
-
|| type.name === "KeyboardEvent"
|
|
113
|
-
|| type.name === "PointerEvent"
|
|
114
|
-
|| type.name === "InputEvent"
|
|
115
|
-
|| type.name === "CompositionEvent"
|
|
116
|
-
|| type.name === "ClipboardEvent"
|
|
117
|
-
|| type.name === "Element"
|
|
118
|
-
|| type.name === "CanvasElement"
|
|
119
|
-
|| type.name === "DialogElement"
|
|
120
|
-
|| type.name === "InputElement"
|
|
121
|
-
|| type.name === "TextAreaElement"
|
|
122
|
-
|| type.name === "Blob"
|
|
123
|
-
|| type.name === "File"))
|
|
124
|
-
return this.emitTypeCheck(type, value);
|
|
125
|
-
return super.emitIsCheck(type, value);
|
|
103
|
+
return emitIsCheck(this.host, type, value);
|
|
126
104
|
}
|
|
127
105
|
additionalHelpers(_program) {
|
|
128
|
-
return
|
|
129
|
-
...(this.needsLookArithmeticRuntime ? [LOOK_ARITHMETIC_RUNTIME] : []),
|
|
130
|
-
...(this.webOutput ? this.webRuntimeHelpers() : []),
|
|
131
|
-
...(this.needsFileTypeHelper ? [WEB_FILE_TYPE_RUNTIME] : []),
|
|
132
|
-
];
|
|
133
|
-
}
|
|
134
|
-
webRuntimeHelpers() {
|
|
135
|
-
if (!this.usesSharedRuntimeModules())
|
|
136
|
-
return [webRuntime(WEB_RUNTIME_FOUNDATION, this.lookKeywordTable())];
|
|
137
|
-
this.requireRuntimeModule(VELAR_ERROR_NORMALIZATION_MODULE);
|
|
138
|
-
return [
|
|
139
|
-
`import { errorApply as __velarErrorApply, errorCode as __velarErrorCode, isError as __velarIsError, normalizeError as __velarNormalizeError } from ${JSON.stringify(VELAR_ERROR_NORMALIZATION_MODULE)};`,
|
|
140
|
-
webRuntime(WEB_RUNTIME_FOUNDATION_SHARED_ERROR, this.lookKeywordTable()),
|
|
141
|
-
];
|
|
142
|
-
}
|
|
143
|
-
/**
|
|
144
|
-
* The closed keyword sets of the properties this module styles, so a value
|
|
145
|
-
* the compiler could not read is still checked before it reaches the DOM.
|
|
146
|
-
* Only the properties written here ship: the whole table is 17 KiB, and a
|
|
147
|
-
* module pays for the properties it uses.
|
|
148
|
-
*/
|
|
149
|
-
lookKeywordTable() {
|
|
150
|
-
const entries = [...this.lookKeywordProperties].sort()
|
|
151
|
-
.map((name) => ` ${JSON.stringify(cssPropertyName(name))}: ${JSON.stringify([...LOOK_PROPERTY_KEYWORDS.get(name) ?? []])},`);
|
|
152
|
-
return entries.length === 0
|
|
153
|
-
? "const __velarLookKeywords = { __proto__: null };"
|
|
154
|
-
: `const __velarLookKeywords = {\n __proto__: null,\n${entries.join("\n")}\n};`;
|
|
106
|
+
return additionalHelpers(this.host);
|
|
155
107
|
}
|
|
156
108
|
reactiveBridgeHelpers(needsJavaScriptCallBoundary, needsCollections, usedIdentifiers = new Set()) {
|
|
157
|
-
|
|
158
|
-
return super.reactiveBridgeHelpers(needsJavaScriptCallBoundary, needsCollections, usedIdentifiers);
|
|
159
|
-
if (!this.webOutput)
|
|
160
|
-
return super.reactiveBridgeHelpers(needsJavaScriptCallBoundary, needsCollections, usedIdentifiers);
|
|
161
|
-
if (!needsJavaScriptCallBoundary && !needsCollections)
|
|
162
|
-
return [];
|
|
163
|
-
return [WEB_LOCAL_REACTIVE_BRIDGE_RUNTIME, ...(needsCollections ? [WEB_LOCAL_REACTIVE_COLLECTION_BRIDGE_RUNTIME] : [])];
|
|
109
|
+
return reactiveBridgeHelpers(this.host, needsJavaScriptCallBoundary, needsCollections, usedIdentifiers);
|
|
164
110
|
}
|
|
165
111
|
includesErrorNormalizationRuntime() {
|
|
166
|
-
return this.
|
|
112
|
+
return includesErrorNormalizationRuntime(this.host);
|
|
167
113
|
}
|
|
168
|
-
// Web detached tasks report through the velar/app error chain with the
|
|
169
|
-
// distinct "detached" phase. The runtime registry is looked up at report
|
|
170
|
-
// time (module-level tasks can finish before or after the application
|
|
171
|
-
// runtime installs); the captured microtask throw keeps a failure loud when
|
|
172
|
-
// no runtime exists yet, and 'unhandled: true' keeps it loud when no
|
|
173
|
-
// onError handler is installed. Host operations are captured at module
|
|
174
|
-
// initialization, matching the owned-callback discipline.
|
|
175
114
|
detachedTaskHelpers() {
|
|
176
|
-
|
|
177
|
-
// host reporting: the browser path would route a detached failure through a
|
|
178
|
-
// runtime registry that never gets installed and end in a microtask throw,
|
|
179
|
-
// which under `velar test` kills the whole Node test process.
|
|
180
|
-
if (!this.webOutput)
|
|
181
|
-
return super.detachedTaskHelpers();
|
|
182
|
-
return [[
|
|
183
|
-
`const __velarDetachedRegistryKey = Symbol.for(${JSON.stringify(VELAR_RUNTIME_REGISTRY_KEY)});`,
|
|
184
|
-
"const __velarDetachedPromiseThen = globalThis.Promise.prototype.then;",
|
|
185
|
-
"const __velarDetachedApply = Reflect.apply;",
|
|
186
|
-
"const __velarDetachedEnqueue = queueMicrotask;",
|
|
187
|
-
"function __velarDetachedReport(failure) {",
|
|
188
|
-
" const error = __velarNormalizeError(failure);",
|
|
189
|
-
" const runtime = globalThis[__velarDetachedRegistryKey];",
|
|
190
|
-
" if (runtime && typeof runtime.report === \"function\") {",
|
|
191
|
-
" // An action reports its own failure once, in the action phase with",
|
|
192
|
-
" // the action's name as detail. The detached observer of that same",
|
|
193
|
-
" // rejection must not report it a second time.",
|
|
194
|
-
" try {",
|
|
195
|
-
" if (__velarIsError(failure) && __velarGraphWeakSetRemove(runtime.actionFailures, failure)) return;",
|
|
196
|
-
" } catch {}",
|
|
197
|
-
" runtime.report(error, { phase: \"detached\", detail: \"\", unhandled: true });",
|
|
198
|
-
" return;",
|
|
199
|
-
" }",
|
|
200
|
-
" __velarDetachedApply(__velarDetachedEnqueue, globalThis, [() => { throw error; }]);",
|
|
201
|
-
"}",
|
|
202
|
-
"function __velarDetachedTask(task) {",
|
|
203
|
-
// D114 W2: the one place a detached Promise enters the Web runtime, and
|
|
204
|
-
// so the one place the reactive window can be told that an observer run
|
|
205
|
-
// started asynchronous work. The runtime foundation decides whether a run
|
|
206
|
-
// is in progress; this site only reports the fact.
|
|
207
|
-
" __velarNoteAsyncWork();",
|
|
208
|
-
" __velarDetachedApply(__velarDetachedPromiseThen, task, [null, __velarDetachedReport]);",
|
|
209
|
-
" return null;",
|
|
210
|
-
"}",
|
|
211
|
-
].join("\n")];
|
|
115
|
+
return detachedTaskHelpers(this.host);
|
|
212
116
|
}
|
|
213
117
|
visitExtensionRuntimeExpression(expression, visitExpression) {
|
|
214
118
|
if (isWebUnit(expression))
|
|
@@ -302,7 +206,7 @@ export class WebJavaScriptEmitter extends JavaScriptEmitter {
|
|
|
302
206
|
if (statement.kind === "ExtensionStatement:web:unsafe-css")
|
|
303
207
|
return "";
|
|
304
208
|
if (statement.kind === "ExtensionStatement:web:component")
|
|
305
|
-
return this.
|
|
209
|
+
return emitComponent(this.host, statement, depth);
|
|
306
210
|
if (statement.kind === "ExtensionStatement:web:state") {
|
|
307
211
|
const indentation = " ".repeat(depth);
|
|
308
212
|
return `${indentation}${statement.exported ? "export " : ""}const ${statement.name} = __velarState(${this.emitMappedExpression(statement.initializer)}, ${JSON.stringify(statement.name)});`;
|
|
@@ -338,7 +242,7 @@ export class WebJavaScriptEmitter extends JavaScriptEmitter {
|
|
|
338
242
|
}
|
|
339
243
|
}
|
|
340
244
|
if (statement.kind === "AssignmentStatement") {
|
|
341
|
-
const reactive = this.
|
|
245
|
+
const reactive = emitReactiveAssignment(this.host, statement, depth);
|
|
342
246
|
if (reactive)
|
|
343
247
|
return reactive;
|
|
344
248
|
}
|
|
@@ -369,12 +273,12 @@ export class WebJavaScriptEmitter extends JavaScriptEmitter {
|
|
|
369
273
|
if (expression.kind === "BinaryExpression" && ["+", "-", "*", "/"].includes(expression.operator)
|
|
370
274
|
&& (containsUnitLiteral(expression)
|
|
371
275
|
|| this.hints.extensionCalls.get(spanIdentity(expression.span)) === LOOK_ARITHMETIC_HINT)) {
|
|
372
|
-
return this.
|
|
276
|
+
return emitLookArithmetic(this.host, expression);
|
|
373
277
|
}
|
|
374
278
|
if (isWebExpression(expression) && expression.kind === "ExtensionExpression:web:look-hook")
|
|
375
279
|
return "false";
|
|
376
280
|
if (isWebLook(expression))
|
|
377
|
-
return this.
|
|
281
|
+
return emitLook(this.host, expression);
|
|
378
282
|
if (expression.kind === "CallExpression") {
|
|
379
283
|
// D103 rule 2: a checked design token reference is compile-time text. The
|
|
380
284
|
// analyzer proved the name is a literal custom property identifier and
|
|
@@ -398,7 +302,7 @@ export class WebJavaScriptEmitter extends JavaScriptEmitter {
|
|
|
398
302
|
return JSON.stringify(controlled);
|
|
399
303
|
}
|
|
400
304
|
if (isWebJsx(expression)) {
|
|
401
|
-
return this.
|
|
305
|
+
return emitJsx(this.host, expression, this.currentScope ?? "__velarGlobalScope", this.currentScope !== null, this.currentJsxNamespace, false);
|
|
402
306
|
}
|
|
403
307
|
if (expression.kind === "CallExpression" && expression.callee.kind === "IdentifierExpression"
|
|
404
308
|
&& expression.callee.name === "mount" && expression.arguments.length === 2) {
|
|
@@ -429,887 +333,54 @@ export class WebJavaScriptEmitter extends JavaScriptEmitter {
|
|
|
429
333
|
return emitted;
|
|
430
334
|
return emitted.replaceAll("__velarListPop(", "__velarWebListPop(");
|
|
431
335
|
}
|
|
432
|
-
emitLook(expression) {
|
|
433
|
-
return `__velarLook([${this.emitLookEntries(expression.entries, [EMPTY_LOOK_TERM], "").join(", ")}])`;
|
|
434
|
-
}
|
|
435
|
-
emitLookEntries(entries, contexts, target) {
|
|
436
|
-
const parts = [];
|
|
437
|
-
for (const entry of entries) {
|
|
438
|
-
if (entry.kind === "LookSpread") {
|
|
439
|
-
parts.push(this.emitMappedExpression(entry.value));
|
|
440
|
-
continue;
|
|
441
|
-
}
|
|
442
|
-
if (entry.kind === "LookIf") {
|
|
443
|
-
const thenContexts = combineLookTerms(contexts, lookConditionTerms(entry.condition, false, this.lookStaticValues));
|
|
444
|
-
const elseContexts = combineLookTerms(contexts, lookConditionTerms(entry.condition, true, this.lookStaticValues));
|
|
445
|
-
parts.push(...this.emitLookEntries(entry.thenEntries, thenContexts, target));
|
|
446
|
-
parts.push(...this.emitLookEntries(entry.elseEntries, elseContexts, target));
|
|
447
|
-
continue;
|
|
448
|
-
}
|
|
449
|
-
if (entry.kind === "LookTarget") {
|
|
450
|
-
parts.push(...this.emitLookEntries(entry.entries, contexts, entry.name));
|
|
451
|
-
continue;
|
|
452
|
-
}
|
|
453
|
-
const property = cssPropertyName(entry.name);
|
|
454
|
-
for (const context of contexts) {
|
|
455
|
-
const token = lookToken(context.staticAtoms, target, property);
|
|
456
|
-
const rule = `{ rules: { ${JSON.stringify(token)}: ${this.emitLookValue(entry.value)} } }`;
|
|
457
|
-
const runtime = context.runtimeAtoms.map((atom) => {
|
|
458
|
-
const value = this.emitCondition(atom.expression);
|
|
459
|
-
return atom.negated ? `!(${value})` : `(${value})`;
|
|
460
|
-
}).join(" && ");
|
|
461
|
-
parts.push(runtime ? `(${runtime} ? ${rule} : null)` : rule);
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
return parts;
|
|
465
|
-
}
|
|
466
|
-
emitLookValue(expression) {
|
|
467
|
-
return this.emitMappedExpression(expression);
|
|
468
|
-
}
|
|
469
|
-
emitLookArithmetic(expression) {
|
|
470
|
-
return `__velarLookMath(${JSON.stringify(expression.operator)}, ${this.emitMappedExpression(expression.left)}, ${this.emitMappedExpression(expression.right)})`;
|
|
471
|
-
}
|
|
472
|
-
emitComponent(statement, depth) {
|
|
473
|
-
const indentation = " ".repeat(depth);
|
|
474
|
-
const outerIndent = " ".repeat(depth + 1);
|
|
475
|
-
const bodyIndent = " ".repeat(depth + 2);
|
|
476
|
-
const previousScope = this.currentScope;
|
|
477
|
-
const previousJsxNamespace = this.currentJsxNamespace;
|
|
478
|
-
this.currentScope = "__velarComponentScope";
|
|
479
|
-
this.currentJsxNamespace = "__velarNamespace";
|
|
480
|
-
// Props are live reactive inputs: every parameter becomes a read-only
|
|
481
|
-
// handle over the per-instance props store, so prop reads lower through
|
|
482
|
-
// .get() exactly like state reads do.
|
|
483
|
-
const lines = [];
|
|
484
|
-
for (const parameter of statement.parameters) {
|
|
485
|
-
if (parameter.defaultValue) {
|
|
486
|
-
lines.push(`${bodyIndent}const ${parameter.name} = __velarProp(__velarProps, ${JSON.stringify(parameter.name)}, () => (${this.emitMappedExpression(parameter.defaultValue)}));`);
|
|
487
|
-
}
|
|
488
|
-
else {
|
|
489
|
-
lines.push(`${bodyIndent}const ${parameter.name} = __velarRequiredProp(__velarProps, ${JSON.stringify(parameter.name)}, ${JSON.stringify(statement.name)});`);
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
let render = null;
|
|
493
|
-
let expose = null;
|
|
494
|
-
let mountedBody = [];
|
|
495
|
-
let cleanupBody = [];
|
|
496
|
-
for (const item of statement.body) {
|
|
497
|
-
if (item.kind === "ExtensionStatement:web:state") {
|
|
498
|
-
lines.push(`${bodyIndent}const ${item.name} = __velarState(${this.emitMappedExpression(item.initializer)}, ${JSON.stringify(item.name)});`);
|
|
499
|
-
}
|
|
500
|
-
else if (item.kind === "ExtensionStatement:web:computed") {
|
|
501
|
-
lines.push(`${bodyIndent}const ${item.name} = __velarComputed(() => (${this.emitMappedExpression(item.initializer)}));`);
|
|
502
|
-
}
|
|
503
|
-
else if (item.kind === "ExtensionStatement:web:resource") {
|
|
504
|
-
lines.push(`${bodyIndent}const ${item.name} = __velarResource(() => ${this.emitMappedExpression(item.initializer)}, __velarComponentScope, ${JSON.stringify(item.name)});`);
|
|
505
|
-
}
|
|
506
|
-
else if (item.kind === "ExtensionStatement:web:action") {
|
|
507
|
-
const parameters = item.parameters.map((parameter) => this.emitParameter(parameter.name, parameter.defaultValue, parameter.rest)).join(", ");
|
|
508
|
-
const actionLines = [...this.emitStatementLines(item.body, depth + 3)];
|
|
509
|
-
if (!this.blockAlwaysReturns(item.body))
|
|
510
|
-
actionLines.push(`${" ".repeat(depth + 3)}return null;`);
|
|
511
|
-
const actionBody = actionLines.join("\n");
|
|
512
|
-
lines.push(`${bodyIndent}const ${item.name} = __velarAction(async (${parameters}) => {${actionBody ? `\n${actionBody}\n${bodyIndent}` : ""}}, __velarComponentScope, ${JSON.stringify(item.name)});`);
|
|
513
|
-
}
|
|
514
|
-
else if (item.kind === "ExtensionStatement:web:watch") {
|
|
515
|
-
const parameters = [item.currentName, item.previousName].filter((name) => name !== null).join(", ");
|
|
516
|
-
const watchLines = this.emitStatementLines(item.body, depth + 3).join("\n");
|
|
517
|
-
lines.push(`${bodyIndent}__velarWatch(() => ${this.emitMappedExpression(item.expression)}, (${parameters}) => {${watchLines ? `\n${watchLines}\n${bodyIndent}` : ""}}, __velarComponentScope, ${JSON.stringify(webWatchSubjectLabel(item.expression))});`);
|
|
518
|
-
}
|
|
519
|
-
else if (item.kind === "ExtensionStatement:web:expose") {
|
|
520
|
-
expose ??= item.value;
|
|
521
|
-
}
|
|
522
|
-
else if (item.kind === "ExtensionStatement:web:mounted") {
|
|
523
|
-
mountedBody = item.body;
|
|
524
|
-
}
|
|
525
|
-
else if (item.kind === "ExtensionStatement:web:cleanup") {
|
|
526
|
-
cleanupBody = item.body;
|
|
527
|
-
}
|
|
528
|
-
else if (item.kind === "ReturnStatement") {
|
|
529
|
-
render = item.value;
|
|
530
|
-
}
|
|
531
|
-
else {
|
|
532
|
-
lines.push(this.emitMappedStatement(item, depth + 2));
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
// A direct JSX root owns its own attribute/child observers and keeps a
|
|
536
|
-
// stable host. Every other WebNode expression is a live root position:
|
|
537
|
-
// evaluate it inside a dedicated child scope so conditions and helper
|
|
538
|
-
// calls can replace the root without rerunning component setup.
|
|
539
|
-
let renderedRoot = "__velarDomCreateComment(\"missing render\")";
|
|
540
|
-
if (render) {
|
|
541
|
-
if (isWebJsx(render))
|
|
542
|
-
renderedRoot = this.emitMappedExpression(render);
|
|
543
|
-
else {
|
|
544
|
-
const rootScope = this.currentScope;
|
|
545
|
-
this.currentScope = "__velarDynamicScope";
|
|
546
|
-
try {
|
|
547
|
-
renderedRoot = `__velarDynamicComponent((__velarDynamicScope) => ${this.emitMappedExpression(render)}, __velarComponentScope)`;
|
|
548
|
-
}
|
|
549
|
-
finally {
|
|
550
|
-
this.currentScope = rootScope;
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
lines.push(`${bodyIndent}const __velarRoot = ${renderedRoot};`);
|
|
555
|
-
lines.push(`${bodyIndent}const __velarHandle = ${expose ? `__velarComponentHandle(${this.emitMappedExpression(expose)}, ${JSON.stringify(statement.name)})` : "null"};`);
|
|
556
|
-
lines.push(`${bodyIndent}if (__velarProps.class !== undefined) __velarClassBindRoot(__velarRoot, () => __velarProps.class, __velarComponentScope);`);
|
|
557
|
-
lines.push(`${bodyIndent}if (__velarProps.look !== undefined) __velarLookBindRoot(__velarRoot, () => __velarProps.look, __velarComponentScope);`);
|
|
558
|
-
// 'class' and 'look' are fields a component may declare and read, so they
|
|
559
|
-
// arrive as props. The 'style:' slot is not: it is bound to the instance
|
|
560
|
-
// root by whoever wrote it, at the instantiation site, for every component
|
|
561
|
-
// alike -- see __velarInstantiate.
|
|
562
|
-
const mounted = this.emitStatementLines(mountedBody, depth + 3).join("\n");
|
|
563
|
-
const cleanup = cleanupBody.map((child) => {
|
|
564
|
-
if (["VariableDeclaration", "FunctionDeclaration", "ClassDeclaration", "TypeDeclaration", "EnumDeclaration"].includes(child.kind)) {
|
|
565
|
-
return this.emitMappedStatement(child, depth + 3);
|
|
566
|
-
}
|
|
567
|
-
const inner = this.emitMappedStatement(child, depth + 4);
|
|
568
|
-
if (!inner)
|
|
569
|
-
return "";
|
|
570
|
-
const cleanupIndent = " ".repeat(depth + 3);
|
|
571
|
-
return `${cleanupIndent}__velarCleanupStep(() => {\n${inner}\n${cleanupIndent}}, __velarComponentScope);`;
|
|
572
|
-
}).filter(Boolean).join("\n");
|
|
573
|
-
const cleanupBodyText = `() => {${cleanup ? `\n${cleanup}\n${bodyIndent}` : ""}}`;
|
|
574
|
-
const functionLines = [
|
|
575
|
-
`${outerIndent}const __velarComponentScope = __velarSetupBegin(__velarScope(${JSON.stringify(statement.name)}));`,
|
|
576
|
-
`${outerIndent}let __velarConstructionCleanup = () => {};`,
|
|
577
|
-
`${outerIndent}try {`,
|
|
578
|
-
`${bodyIndent}__velarConstructionCleanup = ${cleanupBodyText};`,
|
|
579
|
-
...lines,
|
|
580
|
-
`${bodyIndent}return __velarSetupEnd(__velarComponent(__velarRoot, __velarComponentScope, async () => {${mounted ? `\n${mounted}\n${bodyIndent}` : ""}}, __velarConstructionCleanup, __velarHandle));`,
|
|
581
|
-
`${outerIndent}} catch (__velarConstructionError) {`,
|
|
582
|
-
`${bodyIndent}__velarSetupEnd(null);`,
|
|
583
|
-
`${bodyIndent}try { __velarConstructionCleanup(); } catch (__velarCleanupError) { __velarReport(__velarCleanupError, "cleanup", __velarComponentScope); }`,
|
|
584
|
-
`${bodyIndent}__velarDestroyScope(__velarComponentScope);`,
|
|
585
|
-
`${bodyIndent}throw __velarConstructionError;`,
|
|
586
|
-
`${outerIndent}}`,
|
|
587
|
-
];
|
|
588
|
-
this.currentScope = previousScope;
|
|
589
|
-
this.currentJsxNamespace = previousJsxNamespace;
|
|
590
|
-
return `${indentation}${statement.exported ? "export " : ""}function ${statement.name}(__velarProps = {}, __velarNamespace = "html") {\n${functionLines.filter(Boolean).join("\n")}\n${indentation}}`;
|
|
591
|
-
}
|
|
592
|
-
emitReactiveAssignment(statement, depth) {
|
|
593
|
-
const indentation = " ".repeat(depth);
|
|
594
|
-
if (statement.target.kind === "IdentifierExpression"
|
|
595
|
-
&& this.hints.reactiveReferences.get(spanIdentity(statement.target.span)) === "state") {
|
|
596
|
-
const state = statement.target.name;
|
|
597
|
-
const value = this.emitMappedExpression(statement.value);
|
|
598
|
-
if (statement.operator === "=")
|
|
599
|
-
return `${indentation}${state}.set(${value});`;
|
|
600
|
-
return `${indentation}${state}.set(${state}.get() ${statement.operator.slice(0, -1)} ${value});`;
|
|
601
|
-
}
|
|
602
|
-
return null;
|
|
603
|
-
}
|
|
604
|
-
emitJsx(expression, scope, asChild, namespace, mapped = true) {
|
|
605
|
-
const render = () => this.emitJsxCode(expression, scope, asChild, namespace);
|
|
606
|
-
return mapped ? this.emitMappedJavaScript(expression.span, render) : render();
|
|
607
|
-
}
|
|
608
|
-
emitJsxCode(expression, scope, asChild, namespace) {
|
|
609
|
-
if (/^[A-Z]/u.test(expression.tag)) {
|
|
610
|
-
const reactiveComponent = this.hints.reactiveReferences.has(spanIdentity(expression.tagSpan));
|
|
611
|
-
const componentScope = reactiveComponent ? "__velarDynamicScope" : scope;
|
|
612
|
-
const previousScope = this.currentScope;
|
|
613
|
-
if (reactiveComponent)
|
|
614
|
-
this.currentScope = componentScope;
|
|
615
|
-
try {
|
|
616
|
-
// Static component identity keeps the existing stable child fast path.
|
|
617
|
-
// A reactive Component value owns a dynamic region that remounts only
|
|
618
|
-
// when the constructor identity itself changes; ordinary prop updates
|
|
619
|
-
// continue through the child's live prop cells.
|
|
620
|
-
const properties = expression.attributes
|
|
621
|
-
.filter((attribute) => attribute.name !== "key" && attribute.name !== "ref" && attribute.name !== "look" && !attribute.name.startsWith("look:")
|
|
622
|
-
&& attribute.name !== "style" && !attribute.name.startsWith("style:"))
|
|
623
|
-
.map((attribute) => this.emitMappedJavaScript(attribute.span, () => `${this.emitObjectKey(attribute.name)}: () => (${this.emitJsxAttributeValue(attribute)})`));
|
|
624
|
-
const lookValue = this.emitJsxLookValue(expression);
|
|
625
|
-
const lookAttribute = expression.attributes.find((attribute) => attribute.name === "look" || attribute.name.startsWith("look:"));
|
|
626
|
-
if (lookValue && lookAttribute) {
|
|
627
|
-
properties.push(this.emitMappedJavaScript(lookAttribute.span, () => `look: () => (${lookValue})`));
|
|
628
|
-
}
|
|
629
|
-
const styleValue = this.emitJsxStyleValue(expression);
|
|
630
|
-
const styleAttribute = expression.attributes.find((attribute) => attribute.name.startsWith("style:"));
|
|
631
|
-
if (styleValue && styleAttribute) {
|
|
632
|
-
properties.push(this.emitMappedJavaScript(styleAttribute.span, () => `__velarStyle: () => (${styleValue})`));
|
|
633
|
-
}
|
|
634
|
-
// Children stay a thunk so the charter's evaluation order holds at
|
|
635
|
-
// the runtime boundary: props left to right, then children, then the
|
|
636
|
-
// component function. The thunk takes the scope to build into, so the
|
|
637
|
-
// position that shows the slot owns what it built and destroys it when
|
|
638
|
-
// it stops showing it.
|
|
639
|
-
const children = hasMeaningfulChildren(expression.children)
|
|
640
|
-
? `(__velarChildrenScope = ${componentScope}) => (${this.emitJsxChildrenCode(expression.children, namespace)})`
|
|
641
|
-
: "undefined";
|
|
642
|
-
const ref = expression.attributes.find((attribute) => attribute.name === "ref")?.value;
|
|
643
|
-
const refSetter = ref && typeof ref !== "string" && ref.kind === "IdentifierExpression"
|
|
644
|
-
? `(next, previous) => { if (previous === undefined || ${ref.name} === previous) ${ref.name} = next; }`
|
|
645
|
-
: null;
|
|
646
|
-
const component = reactiveComponent ? `${expression.tag}.get()` : expression.tag;
|
|
647
|
-
const arguments_ = `${component}, { ${properties.join(", ")} }, ${children}, ${componentScope}, ${namespace}${refSetter ? `, ${refSetter}` : ""}`;
|
|
648
|
-
if (reactiveComponent) {
|
|
649
|
-
return `__velarDynamicComponent((__velarDynamicScope) => __velarChild(${arguments_}), ${scope})`;
|
|
650
|
-
}
|
|
651
|
-
if (asChild)
|
|
652
|
-
return `__velarChild(${arguments_})`;
|
|
653
|
-
// D90 R4-b's designed site: `const root = <App />` builds its instance
|
|
654
|
-
// while the module evaluates, which is outside every transaction the
|
|
655
|
-
// runtime owns. The site stays legal and eager; only its failure moves,
|
|
656
|
-
// from an uncaught module-evaluation throw to the same no-blank-page
|
|
657
|
-
// machinery `mount` has always run.
|
|
658
|
-
return `${this.moduleEvaluation ? "__velarModuleInstantiate" : "__velarInstantiate"}(${arguments_})`;
|
|
659
|
-
}
|
|
660
|
-
finally {
|
|
661
|
-
this.currentScope = previousScope;
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
const id = ++this.jsxId;
|
|
665
|
-
const element = `__velarElement${id}`;
|
|
666
|
-
const elementNamespace = expression.tag === "svg" ? '"svg"' : namespace;
|
|
667
|
-
const childNamespace = expression.tag === "foreignObject" ? '"html"' : elementNamespace;
|
|
668
|
-
const lines = [expression.tag
|
|
669
|
-
? `const ${element} = __velarCreateElement(${JSON.stringify(expression.tag)}, ${elementNamespace});`
|
|
670
|
-
: `const ${element} = __velarDomCreateFragment();`];
|
|
671
|
-
let emittedLook = false;
|
|
672
|
-
let emittedStyle = false;
|
|
673
|
-
for (const attribute of expression.attributes) {
|
|
674
|
-
if (attribute.name === "key")
|
|
675
|
-
continue;
|
|
676
|
-
if (attribute.name === "look" || attribute.name.startsWith("look:")) {
|
|
677
|
-
if (!emittedLook) {
|
|
678
|
-
emittedLook = true;
|
|
679
|
-
const lookValue = this.emitJsxLookValue(expression);
|
|
680
|
-
if (lookValue)
|
|
681
|
-
lines.push(this.emitMappedJavaScript(attribute.span, () => `__velarLookBind(${element}, () => ${lookValue}, ${scope});`));
|
|
682
|
-
}
|
|
683
|
-
continue;
|
|
684
|
-
}
|
|
685
|
-
if (attribute.name.startsWith("style:")) {
|
|
686
|
-
if (!emittedStyle) {
|
|
687
|
-
emittedStyle = true;
|
|
688
|
-
const styleValue = this.emitJsxStyleValue(expression);
|
|
689
|
-
if (styleValue)
|
|
690
|
-
lines.push(this.emitMappedJavaScript(attribute.span, () => `__velarStyleBind(${element}, () => (${styleValue}), ${scope});`));
|
|
691
|
-
}
|
|
692
|
-
continue;
|
|
693
|
-
}
|
|
694
|
-
if (attribute.name === "style")
|
|
695
|
-
continue;
|
|
696
|
-
lines.push(this.emitMappedJavaScript(attribute.span, () => {
|
|
697
|
-
const value = attribute.value;
|
|
698
|
-
if (attribute.name === "ref" && value && typeof value !== "string" && value.kind === "IdentifierExpression") {
|
|
699
|
-
return `${value.name} = ${element}; __velarAppendOwned(${scope}.cleanups, () => { if (${value.name} === ${element}) ${value.name} = null; });`;
|
|
700
|
-
}
|
|
701
|
-
if (attribute.name.startsWith("on:") && value && typeof value !== "string") {
|
|
702
|
-
const [event, ...modifiers] = attribute.name.slice(3).split(".");
|
|
703
|
-
return `__velarOn(${element}, ${JSON.stringify(event)}, () => (${this.emitMappedExpression(value)}), ${scope}, ${JSON.stringify(modifiers)});`;
|
|
704
|
-
}
|
|
705
|
-
if (attribute.name === "bind:value" && value && typeof value !== "string") {
|
|
706
|
-
const numeric = expression.tag === "input" && expression.attributes.some((item) => item.name === "type" && item.value === "number");
|
|
707
|
-
const enumName = this.hints.enumValueBindings.get(attribute.span.start);
|
|
708
|
-
return `__velarBindValue(${element}, ${this.emitBindTarget(value)}, ${scope}, ${numeric}${enumName ? `, ${enumName}.parse` : ""});`;
|
|
709
|
-
}
|
|
710
|
-
if (attribute.name === "bind:checked" && value && typeof value !== "string") {
|
|
711
|
-
return `__velarBindChecked(${element}, ${this.emitBindTarget(value)}, ${scope});`;
|
|
712
|
-
}
|
|
713
|
-
if (attribute.name === "bind:group" && value && typeof value !== "string") {
|
|
714
|
-
const multiple = expression.attributes.some((item) => item.name === "type" && item.value === "checkbox");
|
|
715
|
-
const enumName = this.hints.enumValueBindings.get(attribute.span.start);
|
|
716
|
-
return `__velarBindGroup(${element}, ${this.emitBindTarget(value)}, ${scope}, ${multiple}${enumName ? `, ${enumName}.parse` : ""});`;
|
|
717
|
-
}
|
|
718
|
-
if (attribute.name.startsWith("class:") && value && typeof value !== "string") {
|
|
719
|
-
return `__velarClass(${element}, ${JSON.stringify(attribute.name.slice(6))}, () => ${this.emitMappedExpression(value)}, ${scope});`;
|
|
720
|
-
}
|
|
721
|
-
if (attribute.name === "host")
|
|
722
|
-
return `${element}.__velarHost = true;`;
|
|
723
|
-
if (attribute.name === "class" && value && typeof value !== "string") {
|
|
724
|
-
return `__velarClassBind(${element}, () => ${this.emitMappedExpression(value)}, ${scope});`;
|
|
725
|
-
}
|
|
726
|
-
if (attribute.name === "unsafe:html") {
|
|
727
|
-
const html = typeof value === "string" ? JSON.stringify(value) : value === null ? '""' : this.emitMappedExpression(value);
|
|
728
|
-
return `__velarHtml(${element}, () => ${html}, ${scope});`;
|
|
729
|
-
}
|
|
730
|
-
if (typeof value === "string" || value === null) {
|
|
731
|
-
return `__velarStaticAttr(${element}, ${JSON.stringify(attribute.name)}, ${value === null ? "true" : JSON.stringify(value)});`;
|
|
732
|
-
}
|
|
733
|
-
return `__velarAttr(${element}, ${JSON.stringify(attribute.name)}, () => ${this.emitMappedExpression(value)}, ${scope});`;
|
|
734
|
-
}));
|
|
735
|
-
}
|
|
736
|
-
for (const child of expression.children) {
|
|
737
|
-
if (child.kind === "JSXText") {
|
|
738
|
-
const text = normalizeJsxText(child.value);
|
|
739
|
-
if (text)
|
|
740
|
-
lines.push(this.emitMappedJavaScript(child.span, () => `__velarDomAppend(${element}, __velarDomCreateTextNode(${JSON.stringify(text)}));`));
|
|
741
|
-
}
|
|
742
|
-
else if (child.kind === "ExtensionExpression:web:jsx") {
|
|
743
|
-
lines.push(this.emitMappedJavaScript(child.span, () => `__velarAppend(${element}, ${this.emitJsx(child, scope, true, childNamespace)});`));
|
|
744
|
-
}
|
|
745
|
-
else {
|
|
746
|
-
lines.push(this.emitMappedJavaScript(child.expression.span, () => this.emitDynamicChild(element, child.expression, scope, childNamespace)));
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
lines.push(`return ${element};`);
|
|
750
|
-
return `(() => { ${lines.join(" ")} })()`;
|
|
751
|
-
}
|
|
752
|
-
emitJsxChildren(children, scope, namespace) {
|
|
753
|
-
const fragmentSpan = children[0]?.span ?? { start: 0, end: 0 };
|
|
754
|
-
const fragment = { kind: "ExtensionExpression:web:jsx", tag: "", tagSpan: { start: fragmentSpan.start, end: fragmentSpan.start }, attributes: [], children, span: fragmentSpan };
|
|
755
|
-
return this.emitJsx(fragment, scope, true, namespace);
|
|
756
|
-
}
|
|
757
|
-
// The slot body builds into whichever scope the consuming position hands it,
|
|
758
|
-
// so every observer, ref and cleanup it registers dies with that position
|
|
759
|
-
// rather than accumulating on the caller for the component's whole lifetime.
|
|
760
|
-
emitJsxChildrenCode(children, namespace) {
|
|
761
|
-
const previousScope = this.currentScope;
|
|
762
|
-
this.currentScope = "__velarChildrenScope";
|
|
763
|
-
try {
|
|
764
|
-
return this.emitJsxChildren(children, "__velarChildrenScope", namespace);
|
|
765
|
-
}
|
|
766
|
-
finally {
|
|
767
|
-
this.currentScope = previousScope;
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
emitDynamicChild(parent, expression, scope, namespace) {
|
|
771
|
-
const leaves = dynamicChildLeaves(expression);
|
|
772
|
-
const previousScope = this.currentScope;
|
|
773
|
-
const previousJsxNamespace = this.currentJsxNamespace;
|
|
774
|
-
this.currentScope = "__velarChildScope";
|
|
775
|
-
this.currentJsxNamespace = namespace;
|
|
776
|
-
// A conditional splits into one region per branch leaf only when a keyed
|
|
777
|
-
// list is somewhere among them; each region gates itself on the shared
|
|
778
|
-
// branch conditions, so at most one region renders content at a time and
|
|
779
|
-
// the keyed list keeps identity-preserving children across the branch flip.
|
|
780
|
-
// Without a keyed leaf the interpolation stays one dynamic region -- unless
|
|
781
|
-
// its checked type can only ever be one text node, which is the whole tree
|
|
782
|
-
// of a conversation surface and is answered by one text node instead (F1).
|
|
783
|
-
const keyed = leaves.some((leaf) => leaf.list?.key);
|
|
784
|
-
const scalarText = !keyed && this.isScalarTextChild(expression);
|
|
785
|
-
// A scalar region owns no child scope, so nothing inside it may address
|
|
786
|
-
// one; the guard above proves nothing does.
|
|
787
|
-
if (scalarText)
|
|
788
|
-
this.currentScope = scope;
|
|
789
|
-
const statements = keyed
|
|
790
|
-
? leaves.map((leaf) => this.emitDynamicChildLeaf(parent, leaf, scope, namespace))
|
|
791
|
-
: scalarText
|
|
792
|
-
? [`__velarText(${parent}, () => ${this.emitMappedExpression(expression)}, ${scope});`]
|
|
793
|
-
: [`__velarDynamic(${parent}, (__velarChildScope) => ${this.emitMappedExpression(expression)}, ${scope});`];
|
|
794
|
-
this.currentScope = previousScope;
|
|
795
|
-
this.currentJsxNamespace = previousJsxNamespace;
|
|
796
|
-
return statements.join(" ");
|
|
797
|
-
}
|
|
798
336
|
/**
|
|
799
|
-
* The
|
|
800
|
-
*
|
|
801
|
-
*
|
|
802
|
-
*
|
|
803
|
-
*
|
|
337
|
+
* The one object every family is handed. Its properties are live reads and
|
|
338
|
+
* writes of this emitter: a family that records "this module needs the
|
|
339
|
+
* file-type helper" or assigns the stylesheet must set the emitter's own
|
|
340
|
+
* field, because `emit()`, `css()` and `styleSegments()` read them after
|
|
341
|
+
* every family has run.
|
|
804
342
|
*/
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
const read = this.emitMappedExpression(value);
|
|
846
|
-
const assignment = { kind: "AssignmentStatement", target: value, value: next, operator: "=", span: value.span };
|
|
847
|
-
const write = this.emitStatement(assignment, 0).trim();
|
|
848
|
-
return `{ get: () => (${read}), set: (__velarBindNext) => { ${write} } }`;
|
|
849
|
-
}
|
|
850
|
-
emitJsxAttributeValue(attribute) {
|
|
851
|
-
if (attribute.value === null)
|
|
852
|
-
return "true";
|
|
853
|
-
if (typeof attribute.value === "string")
|
|
854
|
-
return JSON.stringify(attribute.value);
|
|
855
|
-
return this.emitMappedExpression(attribute.value);
|
|
856
|
-
}
|
|
857
|
-
emitJsxLookValue(expression) {
|
|
858
|
-
const base = expression.attributes.find((attribute) => attribute.name === "look");
|
|
859
|
-
const inline = expression.attributes.filter((attribute) => attribute.name.startsWith("look:"));
|
|
860
|
-
const baseValue = base?.value && typeof base.value !== "string" ? this.emitMappedExpression(base.value) : null;
|
|
861
|
-
if (inline.length === 0)
|
|
862
|
-
return baseValue;
|
|
863
|
-
const rules = inline.map((attribute) => {
|
|
864
|
-
const property = cssPropertyName(attribute.name.slice("look:".length));
|
|
865
|
-
const token = lookToken([], "", property);
|
|
866
|
-
const value = attribute.value === null ? "null"
|
|
867
|
-
: typeof attribute.value === "string" ? JSON.stringify(attribute.value)
|
|
868
|
-
: this.emitMappedExpression(attribute.value);
|
|
869
|
-
return `${JSON.stringify(token)}: ${value}`;
|
|
870
|
-
});
|
|
871
|
-
const anonymous = `{ rules: { ${rules.join(", ")} } }`;
|
|
872
|
-
return `__velarLook([${[baseValue, anonymous].filter(Boolean).join(", ")}])`;
|
|
873
|
-
}
|
|
874
|
-
emitJsxStyleValue(expression) {
|
|
875
|
-
const inline = expression.attributes.filter((attribute) => attribute.name.startsWith("style:"));
|
|
876
|
-
if (inline.length === 0)
|
|
877
|
-
return null;
|
|
878
|
-
const properties = inline.map((attribute) => {
|
|
879
|
-
const property = cssPropertyName(attribute.name.slice("style:".length));
|
|
880
|
-
const value = attribute.value === null ? "null"
|
|
881
|
-
: typeof attribute.value === "string" ? JSON.stringify(attribute.value)
|
|
882
|
-
: this.emitMappedExpression(attribute.value);
|
|
883
|
-
return `${JSON.stringify(property)}: ${value}`;
|
|
884
|
-
});
|
|
885
|
-
return `{ ${properties.join(", ")} }`;
|
|
886
|
-
}
|
|
887
|
-
prepareLooks(program) {
|
|
888
|
-
const rules = new Map();
|
|
889
|
-
const keyframeRules = new Map();
|
|
890
|
-
const keyframeCanonicals = new Map();
|
|
891
|
-
this.keyframeNames.clear();
|
|
892
|
-
this.lookKeywordProperties.clear();
|
|
893
|
-
// A token's stylesheet position used to be wherever it first appeared
|
|
894
|
-
// anywhere in the module, because a Map keeps first-insertion order. The
|
|
895
|
-
// sequence records that first appearance explicitly so emission can sort by
|
|
896
|
-
// condition rank first and fall back to declaration order, rather than
|
|
897
|
-
// letting an unrelated earlier look decide a later one's winner (LOK-U8).
|
|
898
|
-
let sequence = 0;
|
|
899
|
-
const addRule = (token, property, target, staticAtoms) => {
|
|
900
|
-
if (rules.has(token))
|
|
901
|
-
return;
|
|
902
|
-
rules.set(token, { token, property, target, staticAtoms, sequence: sequence += 1 });
|
|
903
|
-
};
|
|
904
|
-
const noteKeywordProperty = (name) => {
|
|
905
|
-
if (LOOK_PROPERTY_VALUE_KINDS.get(name) === "keyword")
|
|
906
|
-
this.lookKeywordProperties.add(name);
|
|
907
|
-
};
|
|
908
|
-
const visit = (value) => {
|
|
909
|
-
if (!value || typeof value !== "object")
|
|
910
|
-
return;
|
|
911
|
-
const record = value;
|
|
912
|
-
if (record.kind === "ExtensionExpression:web:jsx") {
|
|
913
|
-
const element = record;
|
|
914
|
-
for (const attribute of element.attributes) {
|
|
915
|
-
if (!attribute.name.startsWith("look:") && !attribute.name.startsWith("style:"))
|
|
916
|
-
continue;
|
|
917
|
-
const name = attribute.name.slice(attribute.name.indexOf(":") + 1);
|
|
918
|
-
if (!LOOK_PROPERTIES.has(name))
|
|
919
|
-
continue;
|
|
920
|
-
noteKeywordProperty(name);
|
|
921
|
-
if (!attribute.name.startsWith("look:"))
|
|
922
|
-
continue;
|
|
923
|
-
const property = cssPropertyName(name);
|
|
924
|
-
addRule(lookToken([], "", property), property, "", []);
|
|
925
|
-
}
|
|
926
|
-
}
|
|
927
|
-
if (record.kind === "ExtensionExpression:web:look") {
|
|
928
|
-
const collect = (entries, contexts = [EMPTY_LOOK_TERM], target = "") => {
|
|
929
|
-
for (const entry of entries) {
|
|
930
|
-
if (entry.kind === "LookProperty") {
|
|
931
|
-
const property = cssPropertyName(entry.name);
|
|
932
|
-
noteKeywordProperty(entry.name);
|
|
933
|
-
for (const context of contexts) {
|
|
934
|
-
addRule(lookToken(context.staticAtoms, target, property), property, target, context.staticAtoms);
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
else if (entry.kind === "LookIf") {
|
|
938
|
-
collect(entry.thenEntries, combineLookTerms(contexts, lookConditionTerms(entry.condition, false, this.lookStaticValues)), target);
|
|
939
|
-
collect(entry.elseEntries, combineLookTerms(contexts, lookConditionTerms(entry.condition, true, this.lookStaticValues)), target);
|
|
940
|
-
}
|
|
941
|
-
else if (entry.kind === "LookTarget") {
|
|
942
|
-
collect(entry.entries, contexts, entry.name);
|
|
943
|
-
}
|
|
944
|
-
}
|
|
945
|
-
};
|
|
946
|
-
collect(record.entries);
|
|
947
|
-
}
|
|
948
|
-
if (record.kind === "ExtensionExpression:web:keyframes") {
|
|
949
|
-
const expression = record;
|
|
950
|
-
const canonical = keyframesCanonical(expression, this.lookStaticValues);
|
|
951
|
-
const name = keyframesName(canonical);
|
|
952
|
-
this.keyframeNames.set(spanIdentity(expression.span), name);
|
|
953
|
-
const reused = keyframeCanonicals.get(name);
|
|
954
|
-
// The name is a promise that equal structures share one rule. Reusing
|
|
955
|
-
// it for a structure that is not equal would make one animation play
|
|
956
|
-
// another's motion, so the reuse path proves the identity rather than
|
|
957
|
-
// trusting the digest (LOK-U11).
|
|
958
|
-
if (reused !== undefined && reused !== canonical) {
|
|
959
|
-
throw new Error(`Generated keyframes name ${name} collides between two different keyframe structures`);
|
|
960
|
-
}
|
|
961
|
-
if (reused === undefined) {
|
|
962
|
-
keyframeCanonicals.set(name, canonical);
|
|
963
|
-
const stops = [...expression.stops]
|
|
964
|
-
.sort((left, right) => Math.min(...left.offsets) - Math.min(...right.offsets))
|
|
965
|
-
.map((stop) => {
|
|
966
|
-
const selectors = [...stop.offsets].sort((left, right) => left - right)
|
|
967
|
-
.map((offset) => offset === 0 ? "from" : offset === 100 ? "to" : `${offset}%`)
|
|
968
|
-
.join(",");
|
|
969
|
-
const declarations = stop.entries.map((entry) => {
|
|
970
|
-
const css = keyframeCssValue(entry.value, this.lookStaticValues);
|
|
971
|
-
// A value the lowering could not prove is one balanced
|
|
972
|
-
// declaration never reaches the concatenation: `}` in a stop
|
|
973
|
-
// closed the at-rule and turned the rest into author-owned CSS
|
|
974
|
-
// in the compiler-owned segment (LOK-U9). The analyzer already
|
|
975
|
-
// reports the same null as a diagnostic, so emission only has
|
|
976
|
-
// to stay structurally incapable of writing the escape.
|
|
977
|
-
return css === null || !isCssDeclarationValue(css) ? "" : `${cssPropertyName(entry.name)}:${css}`;
|
|
978
|
-
}).filter(Boolean).join(";");
|
|
979
|
-
return `${selectors}{${declarations}}`;
|
|
980
|
-
}).join("");
|
|
981
|
-
keyframeRules.set(name, `@keyframes ${name}{${stops}}`);
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
for (const child of Object.values(record)) {
|
|
985
|
-
if (Array.isArray(child))
|
|
986
|
-
child.forEach(visit);
|
|
987
|
-
else
|
|
988
|
-
visit(child);
|
|
989
|
-
}
|
|
990
|
-
};
|
|
991
|
-
visit(program);
|
|
992
|
-
const lookCss = [...rules.values()]
|
|
993
|
-
.map((rule) => ({ rule, depth: lookConditionDepth(rule.staticAtoms) }))
|
|
994
|
-
// Rank decides, and declaration order only separates rules that share a
|
|
995
|
-
// rank. Emission used to follow the Map, so the sheet's byte order — and
|
|
996
|
-
// through it the per-module concatenation order the CLI sorts by
|
|
997
|
-
// filename — could pick the winner of a tie (LOK-U8, LOK-U10).
|
|
998
|
-
.sort((left, right) => left.depth - right.depth || left.rule.sequence - right.rule.sequence)
|
|
999
|
-
.map(({ rule, depth }) => {
|
|
1000
|
-
const hookAtoms = rule.staticAtoms.filter((atom) => atom.kind === "hook");
|
|
1001
|
-
const mediaAtoms = rule.staticAtoms.filter((atom) => atom.kind === "media" || atom.kind === "scheme" || atom.kind === "motion");
|
|
1002
|
-
const base = `[data-velar-look~=${JSON.stringify(rule.token)}]${"[data-velar-look]".repeat(depth)}`;
|
|
1003
|
-
const selectors = lookSelectors(base, hookAtoms, rule.target);
|
|
1004
|
-
const css = `${selectors.join(",")}{${lookDeclaration(rule.token, rule.property)}}`;
|
|
1005
|
-
const query = mediaAtoms.map(lookMediaQuery).join(" and ");
|
|
1006
|
-
return query ? `@media ${query}{${css}}` : css;
|
|
1007
|
-
});
|
|
1008
|
-
const before = [];
|
|
1009
|
-
const after = [];
|
|
1010
|
-
for (const statement of program.body) {
|
|
1011
|
-
if (!isWebStatement(statement) || statement.kind !== "ExtensionStatement:web:unsafe-css")
|
|
1012
|
-
continue;
|
|
1013
|
-
const source = statement.source.kind === "inline"
|
|
1014
|
-
? statement.source.css
|
|
1015
|
-
: this.resourceContents.get(statement.source.path) ?? "";
|
|
1016
|
-
(statement.placement === "after" ? after : before).push(source.trim());
|
|
1017
|
-
}
|
|
1018
|
-
this.cssSegments = {
|
|
1019
|
-
before: before.filter(Boolean).join("\n\n"),
|
|
1020
|
-
controlled: [...keyframeRules.values(), ...lookCss].filter(Boolean).join("\n\n"),
|
|
1021
|
-
after: after.filter(Boolean).join("\n\n"),
|
|
343
|
+
emitHost() {
|
|
344
|
+
const emitter = this;
|
|
345
|
+
return {
|
|
346
|
+
get currentJsxNamespace() { return emitter.currentJsxNamespace; },
|
|
347
|
+
set currentJsxNamespace(value) { emitter.currentJsxNamespace = value; },
|
|
348
|
+
get currentScope() { return emitter.currentScope; },
|
|
349
|
+
set currentScope(value) { emitter.currentScope = value; },
|
|
350
|
+
get cssOutput() { return emitter.cssOutput; },
|
|
351
|
+
set cssOutput(value) { emitter.cssOutput = value; },
|
|
352
|
+
get cssSegments() { return emitter.cssSegments; },
|
|
353
|
+
set cssSegments(value) { emitter.cssSegments = value; },
|
|
354
|
+
get hints() { return emitter.hints; },
|
|
355
|
+
get importedLookStaticValues() { return emitter.importedLookStaticValues; },
|
|
356
|
+
get jsxId() { return emitter.jsxId; },
|
|
357
|
+
set jsxId(value) { emitter.jsxId = value; },
|
|
358
|
+
get keyframeNames() { return emitter.keyframeNames; },
|
|
359
|
+
get lookKeywordProperties() { return emitter.lookKeywordProperties; },
|
|
360
|
+
get lookStaticValues() { return emitter.lookStaticValues; },
|
|
361
|
+
set lookStaticValues(value) { emitter.lookStaticValues = value; },
|
|
362
|
+
get moduleEvaluation() { return emitter.moduleEvaluation; },
|
|
363
|
+
get needsFileTypeHelper() { return emitter.needsFileTypeHelper; },
|
|
364
|
+
set needsFileTypeHelper(value) { emitter.needsFileTypeHelper = value; },
|
|
365
|
+
get needsLookArithmeticRuntime() { return emitter.needsLookArithmeticRuntime; },
|
|
366
|
+
get resourceContents() { return emitter.resourceContents; },
|
|
367
|
+
get webOutput() { return emitter.webOutput; },
|
|
368
|
+
baseDetachedTaskHelpers: () => super.detachedTaskHelpers(),
|
|
369
|
+
baseIsCheck: (type, value) => super.emitIsCheck(type, value),
|
|
370
|
+
baseReactiveBridgeHelpers: (needsJavaScriptCallBoundary, needsCollections, usedIdentifiers) => super.reactiveBridgeHelpers(needsJavaScriptCallBoundary, needsCollections, usedIdentifiers),
|
|
371
|
+
baseTypeCheck: (type, value, state) => super.emitTypeCheck(type, value, state),
|
|
372
|
+
blockAlwaysReturns: (statements) => emitter.blockAlwaysReturns(statements),
|
|
373
|
+
emitCondition: (expression) => emitter.emitCondition(expression),
|
|
374
|
+
emitMappedExpression: (expression) => emitter.emitMappedExpression(expression),
|
|
375
|
+
emitMappedJavaScript: (sourceSpan, render) => emitter.emitMappedJavaScript(sourceSpan, render),
|
|
376
|
+
emitMappedStatement: (statement, depth) => emitter.emitMappedStatement(statement, depth),
|
|
377
|
+
emitObjectKey: (name) => emitter.emitObjectKey(name),
|
|
378
|
+
emitParameter: (name, defaultValue, rest) => emitter.emitParameter(name, defaultValue, rest),
|
|
379
|
+
emitStatement: (statement, depth) => emitter.emitStatement(statement, depth),
|
|
380
|
+
emitStatementLines: (statements, depth) => emitter.emitStatementLines(statements, depth),
|
|
381
|
+
requireRuntimeModule: (source) => emitter.requireRuntimeModule(source),
|
|
382
|
+
usesSharedRuntimeModules: () => emitter.usesSharedRuntimeModules(),
|
|
1022
383
|
};
|
|
1023
|
-
this.cssOutput = [this.cssSegments.before, this.cssSegments.controlled, this.cssSegments.after].filter(Boolean).join("\n\n");
|
|
1024
|
-
if (this.cssOutput)
|
|
1025
|
-
this.cssOutput += "\n";
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1028
|
-
function normalizeJsxText(value) {
|
|
1029
|
-
const normalized = value.replace(/\s+/gu, " ");
|
|
1030
|
-
if (!value.includes("\n"))
|
|
1031
|
-
return normalized;
|
|
1032
|
-
return (/^\s*\n/u.test(value) ? normalized.trimStart() : normalized).replace(/\n\s*$/u.test(value) ? /\s+$/u : /$^/u, "");
|
|
1033
|
-
}
|
|
1034
|
-
function hasMeaningfulChildren(children) {
|
|
1035
|
-
return children.some((child) => child.kind !== "JSXText" || child.value.trim().length > 0);
|
|
1036
|
-
}
|
|
1037
|
-
// The keyed-children fast path is syntactic: an interpolation leaf must be
|
|
1038
|
-
// exactly `source.map(single-parameter arrow returning JSX)`, keyed when the
|
|
1039
|
-
// arrow's root element carries a `key` attribute. The analyzer mirrors this
|
|
1040
|
-
// recognizer through dynamicChildLeaves, so anything the emitter demotes to a
|
|
1041
|
-
// rebuild-all dynamic region is diagnosed rather than silently forfeited.
|
|
1042
|
-
export function jsxKeyedList(expression) {
|
|
1043
|
-
if (expression.kind !== "CallExpression" || expression.callee.kind !== "MemberExpression" || expression.callee.property !== "map")
|
|
1044
|
-
return null;
|
|
1045
|
-
const callback = expression.arguments[0];
|
|
1046
|
-
if (!callback || callback.kind !== "ArrowFunctionExpression" || callback.asynchronous || callback.parameters.length !== 1 || callback.body.kind !== "ExtensionExpression:web:jsx")
|
|
1047
|
-
return null;
|
|
1048
|
-
const arrow = callback;
|
|
1049
|
-
const key = arrow.body.attributes.find((attribute) => attribute.name === "key") ?? null;
|
|
1050
|
-
return { source: expression.callee.object, arrow, key };
|
|
1051
|
-
}
|
|
1052
|
-
/**
|
|
1053
|
-
* F1: the analyzer's verdict that an interpolation's checked type renders as
|
|
1054
|
-
* exactly one text node. The emitter cannot re-derive it — the checked type is
|
|
1055
|
-
* gone by lowering time — so the analyzer stamps the child expression's span
|
|
1056
|
-
* and the emitter reads it back through the extension-hint channel that
|
|
1057
|
-
* `LOOK_ARITHMETIC_HINT` already uses.
|
|
1058
|
-
*/
|
|
1059
|
-
export const JSX_SCALAR_TEXT_HINT = "@velarscript/web:jsx-scalar-text";
|
|
1060
|
-
/**
|
|
1061
|
-
* The one thing a scalar interpolation must not contain. `__velarText` owns no
|
|
1062
|
-
* child scope, so a nested element inside the expression — legal where a
|
|
1063
|
-
* function takes a `WebNode` and answers text — would have nothing to register
|
|
1064
|
-
* its observers and cleanups against. The type says one text node; this says
|
|
1065
|
-
* nothing was built to make it.
|
|
1066
|
-
*/
|
|
1067
|
-
function containsJsxExpression(value) {
|
|
1068
|
-
if (!value || typeof value !== "object")
|
|
1069
|
-
return false;
|
|
1070
|
-
const record = value;
|
|
1071
|
-
if (record.kind === "ExtensionExpression:web:jsx")
|
|
1072
|
-
return true;
|
|
1073
|
-
return Object.values(record).some((child) => Array.isArray(child) ? child.some(containsJsxExpression) : containsJsxExpression(child));
|
|
1074
|
-
}
|
|
1075
|
-
// Flattens an interpolation into render leaves. A conditional contributes its
|
|
1076
|
-
// branch leaves, each remembering the chain of branch conditions that keeps it
|
|
1077
|
-
// active, so an empty-state ternary around a keyed list still reaches the
|
|
1078
|
-
// keyed fast path instead of demoting every child to rebuild-all updates.
|
|
1079
|
-
export function dynamicChildLeaves(expression, guards = []) {
|
|
1080
|
-
const list = jsxKeyedList(expression);
|
|
1081
|
-
if (list)
|
|
1082
|
-
return [{ expression, list, guards }];
|
|
1083
|
-
if (expression.kind === "ConditionalExpression") {
|
|
1084
|
-
return [
|
|
1085
|
-
...dynamicChildLeaves(expression.thenValue, [...guards, { condition: expression.condition, thenBranch: true }]),
|
|
1086
|
-
...dynamicChildLeaves(expression.elseValue, [...guards, { condition: expression.condition, thenBranch: false }]),
|
|
1087
|
-
];
|
|
1088
|
-
}
|
|
1089
|
-
return [{ expression, list: null, guards }];
|
|
1090
|
-
}
|
|
1091
|
-
const EMPTY_LOOK_TERM = Object.freeze({ staticAtoms: [], runtimeAtoms: [] });
|
|
1092
|
-
const LOOK_CONDITION_TERM_LIMIT = 32;
|
|
1093
|
-
function lookConditionTerms(expression, negated = false, staticValues = new Map()) {
|
|
1094
|
-
if (expression.kind === "UnaryExpression" && expression.operator === "not")
|
|
1095
|
-
return lookConditionTerms(expression.operand, !negated, staticValues);
|
|
1096
|
-
if (expression.kind === "BinaryExpression" && (expression.operator === "and" || expression.operator === "or")) {
|
|
1097
|
-
const conjunction = (expression.operator === "and") !== negated;
|
|
1098
|
-
const left = lookConditionTerms(expression.left, negated, staticValues);
|
|
1099
|
-
const right = lookConditionTerms(expression.right, negated, staticValues);
|
|
1100
|
-
return conjunction ? combineLookTerms(left, right) : [...left, ...right].slice(0, LOOK_CONDITION_TERM_LIMIT);
|
|
1101
|
-
}
|
|
1102
|
-
if (isWebExpression(expression) && expression.kind === "ExtensionExpression:web:look-hook") {
|
|
1103
|
-
return [{ staticAtoms: [{ kind: "hook", name: expression.name, negated }], runtimeAtoms: [] }];
|
|
1104
|
-
}
|
|
1105
|
-
const media = viewportAtom(expression, negated, staticValues) ?? schemeAtom(expression, negated);
|
|
1106
|
-
if (media)
|
|
1107
|
-
return [{ staticAtoms: [media], runtimeAtoms: [] }];
|
|
1108
|
-
return [{ staticAtoms: [], runtimeAtoms: [{ expression, negated }] }];
|
|
1109
|
-
}
|
|
1110
|
-
// A breakpoint is complementary the way the schemes and the motion preference
|
|
1111
|
-
// are: `not (width <= X)` is `width > X`, one condition with one media query.
|
|
1112
|
-
// The atom therefore folds the negation into its operator at construction, so
|
|
1113
|
-
// the two spellings reach `lookToken` as the same token instead of as two
|
|
1114
|
-
// rules that tie on specificity and are separated by source order.
|
|
1115
|
-
const LOOK_NEGATED_MEDIA_OPERATORS = new Map([
|
|
1116
|
-
["<", ">="], ["<=", ">"], [">", "<="], [">=", "<"],
|
|
1117
|
-
]);
|
|
1118
|
-
function viewportAtom(expression, negated, staticValues) {
|
|
1119
|
-
if (expression.kind !== "BinaryExpression" || !["<", "<=", ">", ">="].includes(expression.operator))
|
|
1120
|
-
return null;
|
|
1121
|
-
if (expression.left.kind !== "MemberExpression" || expression.left.object.kind !== "IdentifierExpression" || expression.left.object.name !== "viewport")
|
|
1122
|
-
return null;
|
|
1123
|
-
if (expression.left.property !== "width" && expression.left.property !== "height")
|
|
1124
|
-
return null;
|
|
1125
|
-
const threshold = evaluateLookStaticExpression(expression.right, staticValues);
|
|
1126
|
-
if (threshold?.kind !== "unit" || !LOOK_MEDIA_LENGTH_UNITS.has(threshold.unit))
|
|
1127
|
-
return null;
|
|
1128
|
-
const written = expression.operator;
|
|
1129
|
-
return {
|
|
1130
|
-
kind: "media",
|
|
1131
|
-
name: expression.left.property,
|
|
1132
|
-
operator: negated ? LOOK_NEGATED_MEDIA_OPERATORS.get(written) : written,
|
|
1133
|
-
value: lookStaticCss(threshold),
|
|
1134
|
-
negated: false,
|
|
1135
|
-
};
|
|
1136
|
-
}
|
|
1137
|
-
// 'scheme.dark' / 'scheme.light' lower to prefers-color-scheme media atoms.
|
|
1138
|
-
// The two subjects are complementary, so negation flips to the other scheme
|
|
1139
|
-
// and the atom itself stays canonical.
|
|
1140
|
-
function schemeAtom(expression, negated) {
|
|
1141
|
-
if (expression.kind !== "MemberExpression" || expression.object.kind !== "IdentifierExpression")
|
|
1142
|
-
return null;
|
|
1143
|
-
// LOK-U3: 'motion.reduced' joins the media subjects. prefers-reduced-motion is
|
|
1144
|
-
// complementary in the same way the schemes are, so negation names the other
|
|
1145
|
-
// side of the query rather than wrapping it.
|
|
1146
|
-
if (expression.object.name === "motion") {
|
|
1147
|
-
return expression.property === "reduced" ? { kind: "motion", name: negated ? "no-preference" : "reduce", negated: false } : null;
|
|
1148
|
-
}
|
|
1149
|
-
if (expression.object.name !== "scheme")
|
|
1150
|
-
return null;
|
|
1151
|
-
if (expression.property !== "dark" && expression.property !== "light")
|
|
1152
|
-
return null;
|
|
1153
|
-
const scheme = negated ? (expression.property === "dark" ? "light" : "dark") : expression.property;
|
|
1154
|
-
return { kind: "scheme", name: scheme, negated: false };
|
|
1155
|
-
}
|
|
1156
|
-
function combineLookTerms(left, right) {
|
|
1157
|
-
const combined = [];
|
|
1158
|
-
for (const first of left) {
|
|
1159
|
-
for (const second of right) {
|
|
1160
|
-
combined.push({
|
|
1161
|
-
staticAtoms: [...first.staticAtoms, ...second.staticAtoms],
|
|
1162
|
-
runtimeAtoms: [...first.runtimeAtoms, ...second.runtimeAtoms],
|
|
1163
|
-
});
|
|
1164
|
-
if (combined.length >= LOOK_CONDITION_TERM_LIMIT)
|
|
1165
|
-
return combined;
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
|
-
return combined;
|
|
1169
|
-
}
|
|
1170
|
-
function lookToken(atoms, target, property) {
|
|
1171
|
-
const conditions = atoms.map((atom) => {
|
|
1172
|
-
if (atom.kind === "hook")
|
|
1173
|
-
return `${atom.negated ? "not-" : ""}${kebab(atom.name)}`;
|
|
1174
|
-
if (atom.kind === "scheme")
|
|
1175
|
-
return `scheme-${atom.name}`;
|
|
1176
|
-
if (atom.kind === "motion")
|
|
1177
|
-
return `motion-${atom.name}`;
|
|
1178
|
-
return `viewport-${atom.name}-${lookOperatorName(atom.operator)}-${atom.value}`;
|
|
1179
|
-
}).sort();
|
|
1180
|
-
const prefix = [target ? kebab(target) : "", conditions.length > 0 ? conditions.join("+") : "base"].filter(Boolean).join(":");
|
|
1181
|
-
return `${prefix}:${property}`;
|
|
1182
|
-
}
|
|
1183
|
-
/**
|
|
1184
|
-
* How many extra `[data-velar-look]` selectors a rule carries, so that the
|
|
1185
|
-
* winner between two Look rules is decided by the conditions they name rather
|
|
1186
|
-
* than by their position in the sheet.
|
|
1187
|
-
*
|
|
1188
|
-
* The bump used to be a single flat `+1` for any non-empty condition set, which
|
|
1189
|
-
* made every conditional rule specificity `(0,2,0)`: a state rule tied with a
|
|
1190
|
-
* media rule, and a two-condition refinement tied with the one-condition
|
|
1191
|
-
* fallback it refines. Ties then fell through to source order, and source order
|
|
1192
|
-
* is per-module concatenation order, which the CLI sorts by filename — so the
|
|
1193
|
-
* rendered colour could change when a file was renamed (LOK-U8, LOK-U10,
|
|
1194
|
-
* LOK-U12).
|
|
1195
|
-
*
|
|
1196
|
-
* The rank is base < media < state < media+state, and within a rank a rule that
|
|
1197
|
-
* names more conditions outranks one that names fewer. The per-rank span is
|
|
1198
|
-
* bounded so a pathological condition count cannot cross a rank boundary; rules
|
|
1199
|
-
* that saturate it fall back to declaration order, which is stable.
|
|
1200
|
-
*/
|
|
1201
|
-
const LOOK_RANK_SPAN = 3;
|
|
1202
|
-
function lookConditionDepth(atoms) {
|
|
1203
|
-
if (atoms.length === 0)
|
|
1204
|
-
return 0;
|
|
1205
|
-
const hooks = atoms.filter((atom) => atom.kind === "hook").length;
|
|
1206
|
-
const media = atoms.length - hooks;
|
|
1207
|
-
const rank = hooks > 0 ? (media > 0 ? 2 : 1) : 0;
|
|
1208
|
-
return rank * LOOK_RANK_SPAN + Math.min(atoms.length, LOOK_RANK_SPAN);
|
|
1209
|
-
}
|
|
1210
|
-
function lookVariable(token) {
|
|
1211
|
-
return `--velar-look-${token.replace(/[^A-Za-z0-9_-]+/gu, "-")}`;
|
|
1212
|
-
}
|
|
1213
|
-
function lookDeclaration(token, property) {
|
|
1214
|
-
const value = `var(${lookVariable(token)})`;
|
|
1215
|
-
return `${property}:${value}`;
|
|
1216
|
-
}
|
|
1217
|
-
function lookOperatorName(operator) {
|
|
1218
|
-
return operator === "<" ? "lt" : operator === "<=" ? "lte" : operator === ">" ? "gt" : "gte";
|
|
1219
|
-
}
|
|
1220
|
-
function lookMediaQuery(atom) {
|
|
1221
|
-
if (atom.kind === "scheme")
|
|
1222
|
-
return `(prefers-color-scheme: ${atom.name})`;
|
|
1223
|
-
if (atom.kind === "motion")
|
|
1224
|
-
return `(prefers-reduced-motion: ${atom.name})`;
|
|
1225
|
-
return `(${atom.name} ${atom.operator} ${atom.value})`;
|
|
1226
|
-
}
|
|
1227
|
-
function lookSelectors(base, atoms, target) {
|
|
1228
|
-
let selectors = [base];
|
|
1229
|
-
for (const atom of atoms) {
|
|
1230
|
-
const states = lookHookSelectors(atom.name);
|
|
1231
|
-
if (atom.negated) {
|
|
1232
|
-
const condition = states.map((state) => `:not(${state})`).join("");
|
|
1233
|
-
selectors = selectors.map((selector) => `${selector}:where(${condition})`);
|
|
1234
|
-
}
|
|
1235
|
-
else {
|
|
1236
|
-
selectors = selectors.flatMap((selector) => states.map((state) => `${selector}:where(${state})`));
|
|
1237
|
-
}
|
|
1238
384
|
}
|
|
1239
|
-
const suffix = target ? LOOK_TARGET_SELECTORS.get(target) ?? `::${kebab(target)}` : "";
|
|
1240
|
-
return selectors.map((selector) => `${selector}${suffix}`);
|
|
1241
|
-
}
|
|
1242
|
-
function lookHookSelectors(name) {
|
|
1243
|
-
if (name === "focusVisible")
|
|
1244
|
-
return [":focus-visible"];
|
|
1245
|
-
if (name === "current")
|
|
1246
|
-
return ["[aria-current=\"page\"]"];
|
|
1247
|
-
if (name === "disabled")
|
|
1248
|
-
return [":disabled", "[aria-disabled=\"true\"]"];
|
|
1249
|
-
if (name === "checked")
|
|
1250
|
-
return [":checked", "[aria-checked=\"true\"]"];
|
|
1251
|
-
if (name === "invalid")
|
|
1252
|
-
return [":invalid", "[aria-invalid=\"true\"]"];
|
|
1253
|
-
if (name === "open")
|
|
1254
|
-
return [":open", "[open]", "[aria-expanded=\"true\"]"];
|
|
1255
|
-
return [`:${kebab(name)}`];
|
|
1256
|
-
}
|
|
1257
|
-
const LOOK_TARGET_SELECTORS = new Map([
|
|
1258
|
-
["before", "::before"], ["after", "::after"], ["backdrop", "::backdrop"], ["placeholder", "::placeholder"], ["selection", "::selection"],
|
|
1259
|
-
["marker", "::marker"], ["fileSelectorButton", "::file-selector-button"],
|
|
1260
|
-
]);
|
|
1261
|
-
function kebab(value) {
|
|
1262
|
-
return value.replace(/[A-Z]/gu, (character) => `-${character.toLowerCase()}`);
|
|
1263
|
-
}
|
|
1264
|
-
function visitLookExpressions(entries, visit) {
|
|
1265
|
-
for (const entry of entries) {
|
|
1266
|
-
if (entry.kind === "LookProperty" || entry.kind === "LookSpread")
|
|
1267
|
-
visit(entry.value);
|
|
1268
|
-
else if (entry.kind === "LookIf") {
|
|
1269
|
-
visit(entry.condition);
|
|
1270
|
-
visitLookExpressions(entry.thenEntries, visit);
|
|
1271
|
-
visitLookExpressions(entry.elseEntries, visit);
|
|
1272
|
-
}
|
|
1273
|
-
else
|
|
1274
|
-
visitLookExpressions(entry.entries, visit);
|
|
1275
|
-
}
|
|
1276
|
-
}
|
|
1277
|
-
function containsUnitLiteral(value) {
|
|
1278
|
-
if (!value || typeof value !== "object")
|
|
1279
|
-
return false;
|
|
1280
|
-
const record = value;
|
|
1281
|
-
if (record.kind === "ExtensionExpression:web:unit")
|
|
1282
|
-
return true;
|
|
1283
|
-
for (const child of Object.values(record)) {
|
|
1284
|
-
if (Array.isArray(child)) {
|
|
1285
|
-
if (child.some(containsUnitLiteral))
|
|
1286
|
-
return true;
|
|
1287
|
-
}
|
|
1288
|
-
else if (containsUnitLiteral(child))
|
|
1289
|
-
return true;
|
|
1290
|
-
}
|
|
1291
|
-
return false;
|
|
1292
|
-
}
|
|
1293
|
-
function containsWebSyntax(value) {
|
|
1294
|
-
if (!value || typeof value !== "object")
|
|
1295
|
-
return false;
|
|
1296
|
-
const record = value;
|
|
1297
|
-
if (record.kind === "ExtensionStatement:web:component" || record.kind === "ExtensionStatement:web:expose" || record.kind === "ExtensionStatement:web:unsafe-css" || record.kind === "ExtensionExpression:web:look" || record.kind === "ExtensionExpression:web:keyframes" || record.kind === "ExtensionExpression:web:jsx"
|
|
1298
|
-
|| record.kind === "ExtensionStatement:web:state" || record.kind === "ExtensionStatement:web:computed" || record.kind === "ExtensionStatement:web:resource" || record.kind === "ExtensionStatement:web:action" || record.kind === "ExtensionStatement:web:watch")
|
|
1299
|
-
return true;
|
|
1300
|
-
if (record.kind === "IdentifierExpression" && (record.name === "mount" || record.name === "tick"))
|
|
1301
|
-
return true;
|
|
1302
|
-
return Object.values(record).some((child) => Array.isArray(child) ? child.some(containsWebSyntax) : containsWebSyntax(child));
|
|
1303
|
-
}
|
|
1304
|
-
/**
|
|
1305
|
-
* The runtime an emitted Web program carries inside itself: the foundation it
|
|
1306
|
-
* installs, the CSS string serializer, the closed keyword sets of the Look
|
|
1307
|
-
* properties *this* module styles, and the runtime body. Only the keyword table
|
|
1308
|
-
* differs per compilation, which is why it is the one hole
|
|
1309
|
-
* `runtime/manifest.json` records as an assembly (LOK-U13 keeps the serializer
|
|
1310
|
-
* the one css-string.ts publishes rather than a second spelling of it).
|
|
1311
|
-
*/
|
|
1312
|
-
function webRuntime(foundation, lookKeywords) {
|
|
1313
|
-
return `${foundation}\n${CSS_STRING_RUNTIME_BODY}${lookKeywords}\n${WEB_RUNTIME_BODY}`;
|
|
1314
385
|
}
|
|
1315
386
|
//# sourceMappingURL=emitter.js.map
|