From 434ef521f4c42add11b47e59af81b23ede3caa54 Mon Sep 17 00:00:00 2001 From: Wout Mertens Date: Thu, 17 Oct 2024 22:58:14 +0200 Subject: [PATCH] wip trying to get qwik processed by the optimizer so bind:value is done via a qrl perhaps instead use a well-known name for it and export it from qwik? This will load the framework, which is ok since it needs to write to a signal --- packages/docs/src/repl/worker/repl-plugins.ts | 6 +- packages/qwik/src/core/ssr/ssr-render-jsx.ts | 44 ++++++++-- .../qwik_core__test__example_input_bind.snap | 29 ++----- .../qwik/src/optimizer/core/src/transform.rs | 86 ------------------- packages/qwik/tsconfig.json | 1 + scripts/submodule-build.ts | 1 + scripts/submodule-core.ts | 7 +- scripts/submodule-optimizer.ts | 1 + scripts/submodule-server.ts | 1 + scripts/submodule-testing.ts | 2 +- 10 files changed, 54 insertions(+), 124 deletions(-) diff --git a/packages/docs/src/repl/worker/repl-plugins.ts b/packages/docs/src/repl/worker/repl-plugins.ts index d57a1d499a7..e8361fd3c9c 100644 --- a/packages/docs/src/repl/worker/repl-plugins.ts +++ b/packages/docs/src/repl/worker/repl-plugins.ts @@ -24,7 +24,7 @@ export const replResolver = (options: ReplInputOptions, buildMode: 'client' | 's id === '@builder.io/qwik/jsx-runtime' || id === '@builder.io/qwik/jsx-dev-runtime' ) { - return '\0qwikCore'; + return '/core.qwik.mjs'; } if (id === '@builder.io/qwik/server') { return '\0qwikServer'; @@ -48,14 +48,14 @@ export const replResolver = (options: ReplInputOptions, buildMode: 'client' | 's return input.code; } if (buildMode === 'ssr') { - if (id === '\0qwikCore') { + if (id === '/core.qwik.mjs') { return getRuntimeBundle('qwikCore'); } if (id === '\0qwikServer') { return getRuntimeBundle('qwikServer'); } } - if (id === '\0qwikCore') { + if (id === '/core.qwik.mjs') { if (options.buildMode === 'production') { const rsp = await depResponse('@builder.io/qwik', '/core.min.mjs'); if (rsp) { diff --git a/packages/qwik/src/core/ssr/ssr-render-jsx.ts b/packages/qwik/src/core/ssr/ssr-render-jsx.ts index 8cfd491bb9b..787a789d1af 100644 --- a/packages/qwik/src/core/ssr/ssr-render-jsx.ts +++ b/packages/qwik/src/core/ssr/ssr-render-jsx.ts @@ -7,8 +7,17 @@ import { Slot } from '../shared/jsx/slot.public'; import type { JSXNode, JSXOutput } from '../shared/jsx/types/jsx-node'; import type { JSXChildren } from '../shared/jsx/types/jsx-qwik-attributes'; import { SSRComment, SSRRaw, SSRStream, type SSRStreamChildren } from '../shared/jsx/utils.public'; -import { trackSignal } from '../use/use-core'; +import { isQrl } from '../shared/qrl/qrl-class'; +import type { QRL } from '../shared/qrl/qrl.public'; +import { qrlToString, type SerializationContext } from '../shared/shared-serialization'; +import { DEBUG_TYPE, VirtualType } from '../shared/types'; import { isAsyncGenerator } from '../shared/utils/async-generator'; +import { + convertEventNameFromJsxPropToHtmlAttr, + getEventNameFromJsxProp, + isJsxPropertyAnEventName, + isPreventDefault, +} from '../shared/utils/event-names'; import { EMPTY_ARRAY } from '../shared/utils/flyweight'; import { throwErrorAndStop } from '../shared/utils/log'; import { @@ -32,8 +41,9 @@ import { DEBUG_TYPE, VirtualType } from '../shared/types'; import { WrappedSignal, EffectProperty, isSignal } from '../signal/signal'; import { applyInlineComponent, applyQwikComponentBody } from './ssr-render-component'; import type { ISsrComponentFrame, ISsrNode, SSRContainer, SsrAttrs } from './ssr-types'; -import { qInspector } from '../shared/utils/qdev'; -import { serializeAttribute } from '../shared/utils/styles'; +// Allow the optimizer to process this $ +// @ts-ignore -- this gets renamed to qwik during build +import { $ } from '@builder.io/qwik-external'; class ParentComponentData { constructor( @@ -367,8 +377,7 @@ export function toSsrAttrs( return null; } const ssrAttrs: SsrAttrs = []; - for (const key in record) { - let value = record[key]; + const handleProp = (key: string, value: unknown) => { if (isJsxPropertyAnEventName(key)) { if (anotherRecord) { /** @@ -397,7 +406,7 @@ export function toSsrAttrs( // merge values from the const props with the var props value = getMergedEventPropValues(value, anotherValue); } else { - continue; + return; } } } @@ -405,7 +414,7 @@ export function toSsrAttrs( if (eventValue) { ssrAttrs.push(convertEventNameFromJsxPropToHtmlAttr(key), eventValue); } - continue; + return; } if (isSignal(value)) { @@ -416,7 +425,7 @@ export function toSsrAttrs( } else { ssrAttrs.push(key, value); } - continue; + return; } if (isPreventDefault(key)) { @@ -426,6 +435,25 @@ export function toSsrAttrs( value = serializeAttribute(key, value, styleScopedId); ssrAttrs.push(key, value as string); + }; + for (const key in record) { + const value = record[key]; + if (key.startsWith('bind:')) { + const propName = key.slice(5); + // emit signal value as the value of the input + handleProp(propName, value); + // emit handler to update the signal value + const handler = propName === 'value' || propName === 'checked' ? 'onInput$' : 'onChange$'; + // todo verify if static listeners flag needs to be set + handleProp( + handler, + $((_: any, element: any) => { + (value as Signal).value = element[propName]; + }) + ); + } else { + handleProp(key, value); + } } if (key != null) { ssrAttrs.push(ELEMENT_KEY, key); diff --git a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_input_bind.snap b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_input_bind.snap index 0effcb79d14..9e7879685fb 100644 --- a/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_input_bind.snap +++ b/packages/qwik/src/optimizer/core/src/snapshots/qwik_core__test__example_input_bind.snap @@ -27,10 +27,9 @@ export const Greeter = component$(() => { ============================= test.js == import { componentQrl } from "@builder.io/qwik"; -import { useLexicalScope } from "@builder.io/qwik"; -import { inlinedQrl } from "@builder.io/qwik"; import { _jsxSorted } from "@builder.io/qwik"; import { _wrapProp } from "@builder.io/qwik"; +import { inlinedQrl } from "@builder.io/qwik"; import { Fragment as _Fragment } from "@builder.io/qwik/jsx-runtime"; export const Greeter = /*#__PURE__*/ componentQrl(/*#__PURE__*/ inlinedQrl(()=>{ const value = useSignal(0); @@ -38,31 +37,13 @@ export const Greeter = /*#__PURE__*/ componentQrl(/*#__PURE__*/ inlinedQrl(()=>{ const stuff = useSignal(); return /*#__PURE__*/ _jsxSorted(_Fragment, null, null, [ /*#__PURE__*/ _jsxSorted("input", null, { - "value": value, - "onInput$": /*#__PURE__*/ inlinedQrl((_, elm)=>{ - const [value] = useLexicalScope(); - return value.value = elm.value; - }, "s_6IZeYpXCNXA", [ - value - ]) + "bind:value": value }, null, 3, null), /*#__PURE__*/ _jsxSorted("input", null, { - "checked": checked, - "onInput$": /*#__PURE__*/ inlinedQrl((_, elm)=>{ - const [checked] = useLexicalScope(); - return checked.value = elm.checked; - }, "s_JPI3bLCVnso", [ - checked - ]) + "bind:checked": checked }, null, 3, null), /*#__PURE__*/ _jsxSorted("input", null, { - "stuff": stuff, - "onChange$": /*#__PURE__*/ inlinedQrl((_, elm)=>{ - const [stuff] = useLexicalScope(); - return stuff.value = elm.stuff; - }, "s_eyREJ0lZTFw", [ - stuff - ]) + "bind:stuff": stuff }, null, 3, null), /*#__PURE__*/ _jsxSorted("div", null, null, value, 3, null), /*#__PURE__*/ _jsxSorted("div", null, null, _wrapProp(value), 3, null) @@ -70,7 +51,7 @@ export const Greeter = /*#__PURE__*/ componentQrl(/*#__PURE__*/ inlinedQrl(()=>{ }, "s_n7HuG2hhU0Q")); -Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;;;;;AAGA,OAAO,MAAM,wBAAU,sCAAW;IACjC,MAAM,QAAQ,UAAU;IACxB,MAAM,UAAU,UAAU;IAC1B,MAAM,QAAQ;IACd,qBACC;sBACC,WAAC;qBAAkB;;;uBAAA;;;;;sBACnB,WAAC;uBAAoB;;;uBAAA;;;;;sBACrB,WAAC;qBAAkB;;;uBAAA;;;;;sBACnB,WAAC,mBAAK;sBACN,WAAC,6BAAK;;AAIT,qBAAG\"}") +Some("{\"version\":3,\"sources\":[\"/user/qwik/src/test.tsx\"],\"names\":[],\"mappings\":\";;;;;AAGA,OAAO,MAAM,wBAAU,sCAAW;IACjC,MAAM,QAAQ,UAAU;IACxB,MAAM,UAAU,UAAU;IAC1B,MAAM,QAAQ;IACd,qBACC;sBACC,WAAC;YAAD,cAAmB;;sBACnB,WAAC;YAAD,gBAAqB;;sBACrB,WAAC;YAAD,cAAmB;;sBACnB,WAAC,mBAAK;sBACN,WAAC,6BAAK;;AAIT,qBAAG\"}") == DIAGNOSTICS == [] diff --git a/packages/qwik/src/optimizer/core/src/transform.rs b/packages/qwik/src/optimizer/core/src/transform.rs index f9fed6508e4..40b2567afdd 100644 --- a/packages/qwik/src/optimizer/core/src/transform.rs +++ b/packages/qwik/src/optimizer/core/src/transform.rs @@ -1253,92 +1253,6 @@ impl<'a> QwikTransform<'a> { } else { children = Some(transformed_children); } - } else if !is_fn && key_word.starts_with("bind:") { - // TODO: move this into jsx runtime with an inline QRL - // right now we can't do it because qwik doesn't get checked for QRLs - let folded = node.value.clone().fold_with(self); - let prop_name: JsWord = key_word[5..].into(); - maybe_const_props.push(ast::PropOrSpread::Prop(Box::new( - ast::Prop::KeyValue(ast::KeyValueProp { - key: ast::PropName::Str(ast::Str { - span: DUMMY_SP, - value: prop_name.clone(), - raw: None, - }), - value: folded.clone(), - }), - ))); - let elm = private_ident!("elm"); - let arrow_fn = ast::Expr::Arrow(ast::ArrowExpr { - params: vec![ - ast::Pat::Ident(ast::BindingIdent::from( - ast::Ident::new( - "_".into(), - DUMMY_SP, - SyntaxContext::empty(), - ), - )), - ast::Pat::Ident(ast::BindingIdent::from(elm.clone())), - ], - body: Box::new(ast::BlockStmtOrExpr::Expr(Box::new( - ast::Expr::Assign(ast::AssignExpr { - left: ast::AssignTarget::Simple( - ast::SimpleAssignTarget::Member( - ast::MemberExpr { - obj: folded.clone(), - prop: ast::MemberProp::Ident( - ast::IdentName::new( - "value".into(), - DUMMY_SP, - ), - ), - span: DUMMY_SP, - }, - ), - ), - op: ast::AssignOp::Assign, - right: Box::new(ast::Expr::Member( - ast::MemberExpr { - obj: Box::new(ast::Expr::Ident(elm)), - prop: ast::MemberProp::Ident( - ast::IdentName::new( - prop_name, DUMMY_SP, - ), - ), - span: DUMMY_SP, - }, - )), - span: DUMMY_SP, - }), - ))), - ..Default::default() - }); - let event_handler = JsWord::from(match key_word.as_ref() { - "bind:value" => "onInput$", - "bind:checked" => "onInput$", - _ => "onChange$", - }); - let (converted_expr, is_const) = self - ._create_synthetic_qsegment( - arrow_fn, - SegmentKind::EventHandler, - event_handler.clone(), - None, - ); - if !is_const { - static_listeners = false; - } - let converted_prop = ast::PropOrSpread::Prop(Box::new( - ast::Prop::KeyValue(ast::KeyValueProp { - value: Box::new(ast::Expr::Call(converted_expr)), - key: ast::PropName::Str(ast::Str { - span: DUMMY_SP, - value: event_handler, - raw: None, - }), - }), - )); - event_handlers.push(converted_prop); } else if !is_fn && (key_word == *REF || key_word == *QSLOT) { // skip var_props.push(prop.fold_with(self)); diff --git a/packages/qwik/tsconfig.json b/packages/qwik/tsconfig.json index c58f0237398..e9acc552f65 100644 --- a/packages/qwik/tsconfig.json +++ b/packages/qwik/tsconfig.json @@ -3,6 +3,7 @@ "compilerOptions": { "paths": { "@builder.io/qwik": ["packages/qwik/src/core"], + "@builder.io/qwik-external": ["@builder.io/qwik"], "@builder.io/qwik/jsx-runtime": ["packages/qwik/src/jsx-runtime"], "@builder.io/qwik/jsx-dev-runtime": ["packages/qwik/src/jsx-runtime"], "@builder.io/qwik/build": ["packages/qwik/src/build"], diff --git a/scripts/submodule-build.ts b/scripts/submodule-build.ts index 466fa236fd1..c914fe4652b 100644 --- a/scripts/submodule-build.ts +++ b/scripts/submodule-build.ts @@ -42,6 +42,7 @@ export async function bundleIndex(config: BuildConfig, entryName: string) { bundle: true, sourcemap: true, target, + external: ['@builder.io/qwik-external'], }; const esm = build({ diff --git a/scripts/submodule-core.ts b/scripts/submodule-core.ts index a869115b666..61efdd2f14f 100644 --- a/scripts/submodule-core.ts +++ b/scripts/submodule-core.ts @@ -22,7 +22,7 @@ async function submoduleCoreProd(config: BuildConfig) { const input: InputOptions = { input: join(config.tscDir, 'packages', 'qwik', 'src', 'core', 'index.js'), onwarn: rollupOnWarn, - external: ['@builder.io/qwik/build'], + external: ['@builder.io/qwik-external', '@builder.io/qwik/build'], plugins: [ { name: 'setVersion', @@ -34,6 +34,7 @@ async function submoduleCoreProd(config: BuildConfig) { 'globalThis.QWIK_VERSION', JSON.stringify(config.distVersion) ); + b.code = b.code.replaceAll('@builder.io/qwik-external', '@builder.io/qwik'); } } }, @@ -71,6 +72,7 @@ async function submoduleCoreProd(config: BuildConfig) { const inputMin: InputOptions = { input: inputCore, onwarn: rollupOnWarn, + external: ['@builder.io/qwik-external'], plugins: [ { name: 'build', @@ -226,7 +228,7 @@ async function submoduleCoreDev(config: BuildConfig) { const esm = build({ ...opts, - external: ['@builder.io/qwik/build'], + external: ['@builder.io/qwik/build', '@builder.io/qwik-external'], format: 'esm', outExtension: { '.js': '.qwik.mjs' }, }); @@ -234,6 +236,7 @@ async function submoduleCoreDev(config: BuildConfig) { const cjs = build({ ...opts, // we don't externalize qwik build because then the repl service worker sees require() + external: ['@builder.io/qwik-external'], define: { ...opts.define, // Vite's base url diff --git a/scripts/submodule-optimizer.ts b/scripts/submodule-optimizer.ts index ab87134f5b3..ee8ace009d1 100644 --- a/scripts/submodule-optimizer.ts +++ b/scripts/submodule-optimizer.ts @@ -33,6 +33,7 @@ export async function submoduleOptimizer(config: BuildConfig) { external: [ /* no Node.js built-in externals allowed! */ 'espree', + '@builder.io/qwik-external', ], }; diff --git a/scripts/submodule-server.ts b/scripts/submodule-server.ts index 97a8e0721d8..6ab0783ed84 100644 --- a/scripts/submodule-server.ts +++ b/scripts/submodule-server.ts @@ -27,6 +27,7 @@ export async function submoduleServer(config: BuildConfig) { external: [ /* no Node.js built-in externals allowed! */ '@builder.io/qwik-dom', '@builder.io/qwik/build', + '@builder.io/qwik-external', ], }; diff --git a/scripts/submodule-testing.ts b/scripts/submodule-testing.ts index 28e411ef100..34c883b5357 100644 --- a/scripts/submodule-testing.ts +++ b/scripts/submodule-testing.ts @@ -14,7 +14,7 @@ export async function submoduleTesting(config: BuildConfig) { sourcemap: config.dev, bundle: true, target, - external: ['@builder.io/qwik/build', 'prettier', 'vitest'], + external: ['@builder.io/qwik/build', 'prettier', 'vitest', '@builder.io/qwik-external'], platform: 'node', // external: [...nodeBuiltIns], };