diff --git a/packages/core/src/__tests__/__snapshots__/alpine.test.ts.snap b/packages/core/src/__tests__/__snapshots__/alpine.test.ts.snap index 9cc6978299..9427523ab6 100644 --- a/packages/core/src/__tests__/__snapshots__/alpine.test.ts.snap +++ b/packages/core/src/__tests__/__snapshots__/alpine.test.ts.snap @@ -535,9 +535,7 @@ exports[`Alpine.js > jsx > Javascript Test > CustomCode 1`] = ` scriptsRun: [], findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (this.$refs.elem && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = this.$refs.elem.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -599,9 +597,7 @@ exports[`Alpine.js > jsx > Javascript Test > Embed 1`] = ` scriptsRun: [], findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (this.$refs.elem && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = this.$refs.elem.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -733,8 +729,7 @@ exports[`Alpine.js > jsx > Javascript Test > Form 1`] = ` const el = event.currentTarget; const headers = props.customHeaders || {}; let body; - const formData = new FormData(el); // TODO: maybe support null - + const formData = new FormData(el); const formPairs = Array.from( event.currentTarget.querySelectorAll(\\"input,select,textarea\\") ) @@ -761,7 +756,6 @@ exports[`Alpine.js > jsx > Javascript Test > Form 1`] = ` value = num; } } else if (el.type === \\"file\\") { - // TODO: one vs multiple files value = el.files; } else { value = el.value; @@ -789,14 +783,11 @@ exports[`Alpine.js > jsx > Javascript Test > Form 1`] = ` ) { contentType = \\"multipart/form-data\\"; } - }); // TODO: send as urlEncoded or multipart by default - // because of ease of use and reliability in browser API - // for encoding the form? + }); if (contentType !== \\"application/json\\") { body = formData; } else { - // Json const json = {}; Array.from(formPairs).forEach(({ value, key }) => { set(json, key, value); @@ -805,10 +796,7 @@ exports[`Alpine.js > jsx > Javascript Test > Form 1`] = ` } if (contentType && contentType !== \\"multipart/form-data\\") { - if ( - /* Zapier doesn't allow content-type header to be sent from browsers */ - !(sendWithJs && props.action?.includes(\\"zapier.com\\")) - ) { + if (!(sendWithJs && props.action?.includes(\\"zapier.com\\"))) { headers[\\"content-type\\"] = contentType; } } @@ -835,15 +823,11 @@ exports[`Alpine.js > jsx > Javascript Test > Form 1`] = ` }/api/v1/form-submit?apiKey=\${builder.apiKey}&to=\${btoa( props.sendSubmissionsToEmail || \\"\\" )}&name=\${encodeURIComponent(props.name || \\"\\")}\`; - fetch( - props.sendSubmissionsTo === \\"email\\" ? formUrl : props.action, - /* TODO: throw error if no action URL */ - { - body, - headers, - method: props.method || \\"post\\", - } - ).then( + fetch(props.sendSubmissionsTo === \\"email\\" ? formUrl : props.action, { + body, + headers, + method: props.method || \\"post\\", + }).then( async (res) => { let body; const contentType = res.headers.get(\\"content-type\\"); @@ -858,13 +842,10 @@ exports[`Alpine.js > jsx > Javascript Test > Form 1`] = ` } if (!res.ok && props.errorMessagePath) { - /* TODO: allow supplying an error formatter function */ let message = get(body, props.errorMessagePath); if (message) { if (typeof message !== \\"string\\") { - /* TODO: ideally convert json to yaml so it woul dbe like - error: - email has been taken */ message = JSON.stringify(message); } @@ -889,13 +870,11 @@ exports[`Alpine.js > jsx > Javascript Test > Form 1`] = ` if (submitSuccessEvent.defaultPrevented) { return; } - /* TODO: option to turn this on/off? */ if (props.resetFormOnSubmit !== false) { this.$refs.formRef.reset(); } } - /* TODO: client side route event first that can be preventDefaulted */ if (props.successUrl) { if (this.$refs.formRef) { @@ -981,7 +960,6 @@ exports[`Alpine.js > jsx > Javascript Test > Image 1`] = ` }, useLazyLoading() { - // TODO: Add more checks here, like testing for real web browsers return !!props.lazy && this.isBrowser(); }, @@ -1632,6 +1610,41 @@ exports[`Alpine.js > jsx > Javascript Test > classState 1`] = ` " `; +exports[`Alpine.js > jsx > Javascript Test > commentsWithQuotes 1`] = ` +" +
+ + + Hello + + + ! I can run in React, Vue, Solid, or Liquid! +
+ +" +`; + exports[`Alpine.js > jsx > Javascript Test > componentWithContext 1`] = ` "
@@ -2181,7 +2194,6 @@ exports[`Alpine.js > jsx > Javascript Test > renderBlock 1`] = ` const ref = props.context.registeredComponents[componentName]; if (!ref) { - // TODO: Public doc page with more info about this message console.warn(\` Could not find a registered component named \\"\${componentName}\\". If you registered it, is the file that registered it imported by the file that needs to render it?\`); @@ -2240,11 +2252,6 @@ exports[`Alpine.js > jsx > Javascript Test > renderBlock 1`] = ` componentRef: this.component?.component, componentOptions: { ...getBlockComponentOptions(this.useBlock), - - /** - * These attributes are passed to the wrapper element when there is one. If \`noWrap\` is set to true, then - * they are provided to the component itself directly. - */ ...(this.shouldWrap ? {} : { @@ -2257,30 +2264,16 @@ exports[`Alpine.js > jsx > Javascript Test > renderBlock 1`] = ` }, get useChildren() { - // TO-DO: When should \`canHaveChildren\` dictate rendering? - // This is currently commented out because some Builder components (e.g. Box) do not have \`canHaveChildren: true\`, - // but still receive and need to render children. - // return this.componentInfo?.canHaveChildren ? this.useBlock.children : []; return this.useBlock.children ?? []; }, get childrenWithoutParentComponent() { - /** - * When there is no \`componentRef\`, there might still be children that need to be rendered. In this case, - * we render them outside of \`componentRef\`. - * NOTE: We make sure not to render this if \`repeatItemData\` is non-null, because that means we are rendering an array of - * blocks, and the children will be repeated within those blocks. - */ const shouldRenderChildrenOutsideRef = !this.component?.component && !this.repeatItemData; return shouldRenderChildrenOutsideRef ? this.useChildren : []; }, get repeatItemData() { - /** - * we don't use \`this.useBlock\` here because the processing done within its logic includes evaluating the block's bindings, - * which will not work if there is a repeat. - */ const { repeat, ...blockWithoutRepeat } = props.block; if (!repeat?.collection) { @@ -2345,7 +2338,6 @@ exports[`Alpine.js > jsx > Javascript Test > renderBlock 1`] = ` if (TARGET === \\"reactNative\\") { return RenderComponentWithContext; } else if (TARGET === \\"vue3\\") { - // vue3 expects a string for the component tag return \\"RenderComponent\\"; } else { return RenderComponent; @@ -3232,9 +3224,7 @@ exports[`Alpine.js > jsx > Typescript Test > CustomCode 1`] = ` scriptsRun: [], findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (this.$refs.elem && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = this.$refs.elem.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -3296,9 +3286,7 @@ exports[`Alpine.js > jsx > Typescript Test > Embed 1`] = ` scriptsRun: [], findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (this.$refs.elem && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = this.$refs.elem.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -3431,8 +3419,7 @@ exports[`Alpine.js > jsx > Typescript Test > Form 1`] = ` const el = event.currentTarget; const headers = props.customHeaders || {}; let body: any; - const formData = new FormData(el); // TODO: maybe support null - + const formData = new FormData(el); const formPairs: { key: string; value: File | boolean | number | string | FileList; @@ -3458,7 +3445,6 @@ exports[`Alpine.js > jsx > Typescript Test > Form 1`] = ` value = num; } } else if (el.type === 'file') { - // TODO: one vs multiple files value = el.files; } else { value = el.value; @@ -3484,14 +3470,11 @@ exports[`Alpine.js > jsx > Typescript Test > Form 1`] = ` if (value instanceof File || Array.isArray(value) && value[0] instanceof File || value instanceof FileList) { contentType = 'multipart/form-data'; } - }); // TODO: send as urlEncoded or multipart by default - // because of ease of use and reliability in browser API - // for encoding the form? + }); if (contentType !== 'application/json') { body = formData; } else { - // Json const json = {}; Array.from(formPairs).forEach(({ value, @@ -3503,9 +3486,7 @@ exports[`Alpine.js > jsx > Typescript Test > Form 1`] = ` } if (contentType && contentType !== 'multipart/form-data') { - if ( - /* Zapier doesn't allow content-type header to be sent from browsers */ - !(sendWithJs && props.action?.includes('zapier.com'))) { + if (!(sendWithJs && props.action?.includes('zapier.com'))) { headers['content-type'] = contentType; } } @@ -3526,9 +3507,7 @@ exports[`Alpine.js > jsx > Typescript Test > Form 1`] = ` this.formState = 'sending'; const formUrl = \`\${builder.env === 'dev' ? 'http://localhost:5000' : 'https://builder.io'}/api/v1/form-submit?apiKey=\${builder.apiKey}&to=\${btoa(props.sendSubmissionsToEmail || '')}&name=\${encodeURIComponent(props.name || '')}\`; - fetch(props.sendSubmissionsTo === 'email' ? formUrl : props.action! - /* TODO: throw error if no action URL */ - , { + fetch(props.sendSubmissionsTo === 'email' ? formUrl : props.action!, { body, headers, method: props.method || 'post' @@ -3543,13 +3522,10 @@ exports[`Alpine.js > jsx > Typescript Test > Form 1`] = ` } if (!res.ok && props.errorMessagePath) { - /* TODO: allow supplying an error formatter function */ let message = get(body, props.errorMessagePath); if (message) { if (typeof message !== 'string') { - /* TODO: ideally convert json to yaml so it woul dbe like - error: - email has been taken */ message = JSON.stringify(message); } @@ -3574,15 +3550,11 @@ exports[`Alpine.js > jsx > Typescript Test > Form 1`] = ` if (submitSuccessEvent.defaultPrevented) { return; } - /* TODO: option to turn this on/off? */ - if (props.resetFormOnSubmit !== false) { this.$refs.formRef.reset(); } } - /* TODO: client side route event first that can be preventDefaulted */ - if (props.successUrl) { if (this.$refs.formRef) { @@ -3667,7 +3639,6 @@ exports[`Alpine.js > jsx > Typescript Test > Image 1`] = ` }, useLazyLoading() { - // TODO: Add more checks here, like testing for real web browsers return !!props.lazy && this.isBrowser(); }, @@ -4318,6 +4289,41 @@ exports[`Alpine.js > jsx > Typescript Test > classState 1`] = ` " `; +exports[`Alpine.js > jsx > Typescript Test > commentsWithQuotes 1`] = ` +" +
+ + + Hello + + + ! I can run in React, Vue, Solid, or Liquid! +
+ +" +`; + exports[`Alpine.js > jsx > Typescript Test > componentWithContext 1`] = ` "
@@ -4867,7 +4873,6 @@ exports[`Alpine.js > jsx > Typescript Test > renderBlock 1`] = ` const ref = props.context.registeredComponents[componentName]; if (!ref) { - // TODO: Public doc page with more info about this message console.warn(\` Could not find a registered component named \\"\${componentName}\\". If you registered it, is the file that registered it imported by the file that needs to render it?\`); @@ -4926,11 +4931,6 @@ exports[`Alpine.js > jsx > Typescript Test > renderBlock 1`] = ` componentRef: this.component?.component, componentOptions: { ...getBlockComponentOptions(this.useBlock), - - /** - * These attributes are passed to the wrapper element when there is one. If \`noWrap\` is set to true, then - * they are provided to the component itself directly. - */ ...(this.shouldWrap ? {} : { @@ -4943,30 +4943,16 @@ exports[`Alpine.js > jsx > Typescript Test > renderBlock 1`] = ` }, get useChildren() { - // TO-DO: When should \`canHaveChildren\` dictate rendering? - // This is currently commented out because some Builder components (e.g. Box) do not have \`canHaveChildren: true\`, - // but still receive and need to render children. - // return this.componentInfo?.canHaveChildren ? this.useBlock.children : []; return this.useBlock.children ?? []; }, get childrenWithoutParentComponent() { - /** - * When there is no \`componentRef\`, there might still be children that need to be rendered. In this case, - * we render them outside of \`componentRef\`. - * NOTE: We make sure not to render this if \`repeatItemData\` is non-null, because that means we are rendering an array of - * blocks, and the children will be repeated within those blocks. - */ const shouldRenderChildrenOutsideRef = !this.component?.component && !this.repeatItemData; return shouldRenderChildrenOutsideRef ? this.useChildren : []; }, get repeatItemData() { - /** - * we don't use \`this.useBlock\` here because the processing done within its logic includes evaluating the block's bindings, - * which will not work if there is a repeat. - */ const { repeat, ...blockWithoutRepeat } = props.block; if (!repeat?.collection) { @@ -5034,7 +5020,6 @@ exports[`Alpine.js > jsx > Typescript Test > renderBlock 1`] = ` if (TARGET === \\"reactNative\\") { return RenderComponentWithContext; } else if (TARGET === \\"vue3\\") { - // vue3 expects a string for the component tag return \\"RenderComponent\\"; } else { return RenderComponent; diff --git a/packages/core/src/__tests__/__snapshots__/angular.import.test.ts.snap b/packages/core/src/__tests__/__snapshots__/angular.import.test.ts.snap index e0a9d3bb58..051936e84a 100644 --- a/packages/core/src/__tests__/__snapshots__/angular.import.test.ts.snap +++ b/packages/core/src/__tests__/__snapshots__/angular.import.test.ts.snap @@ -843,9 +843,7 @@ export class CustomCode { scriptsInserted = []; scriptsRun = []; findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (this.elem.nativeElement && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = this.elem.nativeElement.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -923,9 +921,7 @@ export class CustomCode { scriptsInserted = []; scriptsRun = []; findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (this.elem.nativeElement && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = this.elem.nativeElement.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -1101,8 +1097,7 @@ export class FormComponent { const el = event.currentTarget; const headers = this.customHeaders || {}; let body; - const formData = new FormData(el); // TODO: maybe support null - + const formData = new FormData(el); const formPairs = Array.from( event.currentTarget.querySelectorAll(\\"input,select,textarea\\") ) @@ -1129,7 +1124,6 @@ export class FormComponent { value = num; } } else if (el.type === \\"file\\") { - // TODO: one vs multiple files value = el.files; } else { value = el.value; @@ -1157,14 +1151,11 @@ export class FormComponent { ) { contentType = \\"multipart/form-data\\"; } - }); // TODO: send as urlEncoded or multipart by default - // because of ease of use and reliability in browser API - // for encoding the form? + }); if (contentType !== \\"application/json\\") { body = formData; } else { - // Json const json = {}; Array.from(formPairs).forEach(({ value, key }) => { set(json, key, value); @@ -1173,10 +1164,7 @@ export class FormComponent { } if (contentType && contentType !== \\"multipart/form-data\\") { - if ( - /* Zapier doesn't allow content-type header to be sent from browsers */ - !(sendWithJs && this.action?.includes(\\"zapier.com\\")) - ) { + if (!(sendWithJs && this.action?.includes(\\"zapier.com\\"))) { headers[\\"content-type\\"] = contentType; } } @@ -1201,15 +1189,11 @@ export class FormComponent { }/api/v1/form-submit?apiKey=\${builder.apiKey}&to=\${btoa( this.sendSubmissionsToEmail || \\"\\" )}&name=\${encodeURIComponent(this.name || \\"\\")}\`; - fetch( - this.sendSubmissionsTo === \\"email\\" ? formUrl : this.action, - /* TODO: throw error if no action URL */ - { - body, - headers, - method: this.method || \\"post\\", - } - ).then( + fetch(this.sendSubmissionsTo === \\"email\\" ? formUrl : this.action, { + body, + headers, + method: this.method || \\"post\\", + }).then( async (res) => { let body; const contentType = res.headers.get(\\"content-type\\"); @@ -1221,13 +1205,10 @@ export class FormComponent { } if (!res.ok && this.errorMessagePath) { - /* TODO: allow supplying an error formatter function */ let message = get(body, this.errorMessagePath); if (message) { if (typeof message !== \\"string\\") { - /* TODO: ideally convert json to yaml so it woul dbe like - error: - email has been taken */ message = JSON.stringify(message); } @@ -1252,13 +1233,11 @@ export class FormComponent { if (submitSuccessEvent.defaultPrevented) { return; } - /* TODO: option to turn this on/off? */ if (this.resetFormOnSubmit !== false) { this.formRef.nativeElement.reset(); } } - /* TODO: client side route event first that can be preventDefaulted */ if (this.successUrl) { if (this.formRef.nativeElement) { @@ -1366,7 +1345,6 @@ export class Image { this.imageLoaded = true; } useLazyLoading() { - // TODO: Add more checks here, like testing for real web browsers return !!this.lazy && this.isBrowser(); } isBrowser = function isBrowser() { @@ -2433,6 +2411,8 @@ export class MyBasicComponentModule {} " `; +exports[`Angular with Preserve Imports and File Extensions > jsx > Javascript Test > commentsWithQuotes 1`] = `"JSON5: invalid character '&' at 2:10"`; + exports[`Angular with Preserve Imports and File Extensions > jsx > Javascript Test > componentWithContext 1`] = ` "import { NgModule } from \\"@angular/core\\"; import { CommonModule } from \\"@angular/common\\"; @@ -3453,7 +3433,6 @@ export class RenderBlock { const ref = this.context.registeredComponents[componentName]; if (!ref) { - // TODO: Public doc page with more info about this message console.warn(\` Could not find a registered this.component named \\"\${componentName}\\". If you registered it, is the file that registered it imported by the file that needs to render it?\`); @@ -3506,11 +3485,6 @@ export class RenderBlock { componentRef: this.component?.component, componentOptions: { ...getBlockComponentOptions(this.useBlock), - - /** - * These this.attributes are passed to the wrapper element when there is one. If \`noWrap\` is set to true, then - * they are provided to the this.component itself directly. - */ ...(this.shouldWrap ? {} : { @@ -3522,28 +3496,14 @@ export class RenderBlock { }; } get useChildren() { - // TO-DO: When should \`canHaveChildren\` dictate rendering? - // This is currently commented out because some Builder components (e.g. Box) do not have \`canHaveChildren: true\`, - // but still receive and need to render children. - // return state.componentInfo?.canHaveChildren ? state.useBlock.children : []; return this.useBlock.children ?? []; } get childrenWithoutParentComponent() { - /** - * When there is no \`componentRef\`, there might still be children that need to be rendered. In this case, - * we render them outside of \`componentRef\`. - * NOTE: We make sure not to render this if \`repeatItemData\` is non-null, because that means we are rendering an array of - * blocks, and the children will be repeated within those blocks. - */ const shouldRenderChildrenOutsideRef = !this.component?.component && !this.repeatItemData; return shouldRenderChildrenOutsideRef ? this.useChildren : []; } get repeatItemData() { - /** - * we don't use \`state.useBlock\` here because the processing done within its logic includes evaluating the block's bindings, - * which will not work if there is a repeat. - */ const { repeat, ...blockWithoutRepeat } = this.block; if (!repeat?.collection) { @@ -3604,7 +3564,6 @@ export class RenderBlock { if (TARGET === \\"reactNative\\") { return RenderComponentWithContext; } else if (TARGET === \\"vue3\\") { - // vue3 expects a string for the this.component tag return \\"RenderComponent\\"; } else { return RenderComponent; @@ -5177,9 +5136,7 @@ export class CustomCode { scriptsInserted = []; scriptsRun = []; findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (this.elem.nativeElement && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = this.elem.nativeElement.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -5262,9 +5219,7 @@ export class CustomCode { scriptsInserted = []; scriptsRun = []; findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (this.elem.nativeElement && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = this.elem.nativeElement.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -5468,8 +5423,7 @@ export class FormComponent { const el = event.currentTarget; const headers = this.customHeaders || {}; let body: any; - const formData = new FormData(el); // TODO: maybe support null - + const formData = new FormData(el); const formPairs: { key: string; value: File | boolean | number | string | FileList; @@ -5499,7 +5453,6 @@ export class FormComponent { value = num; } } else if (el.type === \\"file\\") { - // TODO: one vs multiple files value = el.files; } else { value = el.value; @@ -5527,14 +5480,11 @@ export class FormComponent { ) { contentType = \\"multipart/form-data\\"; } - }); // TODO: send as urlEncoded or multipart by default - // because of ease of use and reliability in browser API - // for encoding the form? + }); if (contentType !== \\"application/json\\") { body = formData; } else { - // Json const json = {}; Array.from(formPairs).forEach(({ value, key }) => { set(json, key, value); @@ -5543,10 +5493,7 @@ export class FormComponent { } if (contentType && contentType !== \\"multipart/form-data\\") { - if ( - /* Zapier doesn't allow content-type header to be sent from browsers */ - !(sendWithJs && this.action?.includes(\\"zapier.com\\")) - ) { + if (!(sendWithJs && this.action?.includes(\\"zapier.com\\"))) { headers[\\"content-type\\"] = contentType; } } @@ -5571,15 +5518,11 @@ export class FormComponent { }/api/v1/form-submit?apiKey=\${builder.apiKey}&to=\${btoa( this.sendSubmissionsToEmail || \\"\\" )}&name=\${encodeURIComponent(this.name || \\"\\")}\`; - fetch( - this.sendSubmissionsTo === \\"email\\" ? formUrl : this.action!, - /* TODO: throw error if no action URL */ - { - body, - headers, - method: this.method || \\"post\\", - } - ).then( + fetch(this.sendSubmissionsTo === \\"email\\" ? formUrl : this.action!, { + body, + headers, + method: this.method || \\"post\\", + }).then( async (res) => { let body; const contentType = res.headers.get(\\"content-type\\"); @@ -5591,13 +5534,10 @@ export class FormComponent { } if (!res.ok && this.errorMessagePath) { - /* TODO: allow supplying an error formatter function */ let message = get(body, this.errorMessagePath); if (message) { if (typeof message !== \\"string\\") { - /* TODO: ideally convert json to yaml so it woul dbe like - error: - email has been taken */ message = JSON.stringify(message); } @@ -5622,13 +5562,11 @@ export class FormComponent { if (submitSuccessEvent.defaultPrevented) { return; } - /* TODO: option to turn this on/off? */ if (this.resetFormOnSubmit !== false) { this.formRef.nativeElement.reset(); } } - /* TODO: client side route event first that can be preventDefaulted */ if (this.successUrl) { if (this.formRef.nativeElement) { @@ -5755,7 +5693,6 @@ export class Image { this.imageLoaded = true; } useLazyLoading() { - // TODO: Add more checks here, like testing for real web browsers return !!this.lazy && this.isBrowser(); } isBrowser = function isBrowser() { @@ -6963,6 +6900,8 @@ export class MyBasicComponentModule {} " `; +exports[`Angular with Preserve Imports and File Extensions > jsx > Typescript Test > commentsWithQuotes 1`] = `"JSON5: invalid character '&' at 2:10"`; + exports[`Angular with Preserve Imports and File Extensions > jsx > Typescript Test > componentWithContext 1`] = ` "import { NgModule } from \\"@angular/core\\"; import { CommonModule } from \\"@angular/common\\"; @@ -8084,7 +8023,6 @@ export class RenderBlock { const ref = this.context.registeredComponents[componentName]; if (!ref) { - // TODO: Public doc page with more info about this message console.warn(\` Could not find a registered this.component named \\"\${componentName}\\". If you registered it, is the file that registered it imported by the file that needs to render it?\`); @@ -8137,11 +8075,6 @@ export class RenderBlock { componentRef: this.component?.component, componentOptions: { ...getBlockComponentOptions(this.useBlock), - - /** - * These this.attributes are passed to the wrapper element when there is one. If \`noWrap\` is set to true, then - * they are provided to the this.component itself directly. - */ ...(this.shouldWrap ? {} : { @@ -8153,28 +8086,14 @@ export class RenderBlock { }; } get useChildren() { - // TO-DO: When should \`canHaveChildren\` dictate rendering? - // This is currently commented out because some Builder components (e.g. Box) do not have \`canHaveChildren: true\`, - // but still receive and need to render children. - // return state.componentInfo?.canHaveChildren ? state.useBlock.children : []; return this.useBlock.children ?? []; } get childrenWithoutParentComponent() { - /** - * When there is no \`componentRef\`, there might still be children that need to be rendered. In this case, - * we render them outside of \`componentRef\`. - * NOTE: We make sure not to render this if \`repeatItemData\` is non-null, because that means we are rendering an array of - * blocks, and the children will be repeated within those blocks. - */ const shouldRenderChildrenOutsideRef = !this.component?.component && !this.repeatItemData; return shouldRenderChildrenOutsideRef ? this.useChildren : []; } get repeatItemData() { - /** - * we don't use \`state.useBlock\` here because the processing done within its logic includes evaluating the block's bindings, - * which will not work if there is a repeat. - */ const { repeat, ...blockWithoutRepeat } = this.block; if (!repeat?.collection) { @@ -8235,7 +8154,6 @@ export class RenderBlock { if (TARGET === \\"reactNative\\") { return RenderComponentWithContext; } else if (TARGET === \\"vue3\\") { - // vue3 expects a string for the this.component tag return \\"RenderComponent\\"; } else { return RenderComponent; diff --git a/packages/core/src/__tests__/__snapshots__/angular.mapper.test.ts.snap b/packages/core/src/__tests__/__snapshots__/angular.mapper.test.ts.snap index 9b45fd388a..ab6b7f976d 100644 --- a/packages/core/src/__tests__/__snapshots__/angular.mapper.test.ts.snap +++ b/packages/core/src/__tests__/__snapshots__/angular.mapper.test.ts.snap @@ -861,9 +861,7 @@ export class CustomCode { scriptsInserted = []; scriptsRun = []; findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (this.elem.nativeElement && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = this.elem.nativeElement.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -942,9 +940,7 @@ export class CustomCode { scriptsInserted = []; scriptsRun = []; findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (this.elem.nativeElement && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = this.elem.nativeElement.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -1121,8 +1117,7 @@ export class FormComponent { const el = event.currentTarget; const headers = this.customHeaders || {}; let body; - const formData = new FormData(el); // TODO: maybe support null - + const formData = new FormData(el); const formPairs = Array.from( event.currentTarget.querySelectorAll(\\"input,select,textarea\\") ) @@ -1149,7 +1144,6 @@ export class FormComponent { value = num; } } else if (el.type === \\"file\\") { - // TODO: one vs multiple files value = el.files; } else { value = el.value; @@ -1177,14 +1171,11 @@ export class FormComponent { ) { contentType = \\"multipart/form-data\\"; } - }); // TODO: send as urlEncoded or multipart by default - // because of ease of use and reliability in browser API - // for encoding the form? + }); if (contentType !== \\"application/json\\") { body = formData; } else { - // Json const json = {}; Array.from(formPairs).forEach(({ value, key }) => { set(json, key, value); @@ -1193,10 +1184,7 @@ export class FormComponent { } if (contentType && contentType !== \\"multipart/form-data\\") { - if ( - /* Zapier doesn't allow content-type header to be sent from browsers */ - !(sendWithJs && this.action?.includes(\\"zapier.com\\")) - ) { + if (!(sendWithJs && this.action?.includes(\\"zapier.com\\"))) { headers[\\"content-type\\"] = contentType; } } @@ -1221,15 +1209,11 @@ export class FormComponent { }/api/v1/form-submit?apiKey=\${builder.apiKey}&to=\${btoa( this.sendSubmissionsToEmail || \\"\\" )}&name=\${encodeURIComponent(this.name || \\"\\")}\`; - fetch( - this.sendSubmissionsTo === \\"email\\" ? formUrl : this.action, - /* TODO: throw error if no action URL */ - { - body, - headers, - method: this.method || \\"post\\", - } - ).then( + fetch(this.sendSubmissionsTo === \\"email\\" ? formUrl : this.action, { + body, + headers, + method: this.method || \\"post\\", + }).then( async (res) => { let body; const contentType = res.headers.get(\\"content-type\\"); @@ -1241,13 +1225,10 @@ export class FormComponent { } if (!res.ok && this.errorMessagePath) { - /* TODO: allow supplying an error formatter function */ let message = get(body, this.errorMessagePath); if (message) { if (typeof message !== \\"string\\") { - /* TODO: ideally convert json to yaml so it woul dbe like - error: - email has been taken */ message = JSON.stringify(message); } @@ -1272,13 +1253,11 @@ export class FormComponent { if (submitSuccessEvent.defaultPrevented) { return; } - /* TODO: option to turn this on/off? */ if (this.resetFormOnSubmit !== false) { this.formRef.nativeElement.reset(); } } - /* TODO: client side route event first that can be preventDefaulted */ if (this.successUrl) { if (this.formRef.nativeElement) { @@ -1387,7 +1366,6 @@ export class Image { this.imageLoaded = true; } useLazyLoading() { - // TODO: Add more checks here, like testing for real web browsers return !!this.lazy && this.isBrowser(); } isBrowser = function isBrowser() { @@ -2482,6 +2460,8 @@ export class MyBasicComponentModule {} " `; +exports[`Angular with Import Mapper Tests > jsx > Javascript Test > commentsWithQuotes 1`] = `"JSON5: invalid character '&' at 2:10"`; + exports[`Angular with Import Mapper Tests > jsx > Javascript Test > componentWithContext 1`] = ` "import { NgModule } from \\"@angular/core\\"; import { CommonModule } from \\"@angular/common\\"; @@ -3530,7 +3510,6 @@ export class RenderBlock { const ref = this.context.registeredComponents[componentName]; if (!ref) { - // TODO: Public doc page with more info about this message console.warn(\` Could not find a registered this.component named \\"\${componentName}\\". If you registered it, is the file that registered it imported by the file that needs to render it?\`); @@ -3583,11 +3562,6 @@ export class RenderBlock { componentRef: this.component?.component, componentOptions: { ...getBlockComponentOptions(this.useBlock), - - /** - * These this.attributes are passed to the wrapper element when there is one. If \`noWrap\` is set to true, then - * they are provided to the this.component itself directly. - */ ...(this.shouldWrap ? {} : { @@ -3599,28 +3573,14 @@ export class RenderBlock { }; } get useChildren() { - // TO-DO: When should \`canHaveChildren\` dictate rendering? - // This is currently commented out because some Builder components (e.g. Box) do not have \`canHaveChildren: true\`, - // but still receive and need to render children. - // return state.componentInfo?.canHaveChildren ? state.useBlock.children : []; return this.useBlock.children ?? []; } get childrenWithoutParentComponent() { - /** - * When there is no \`componentRef\`, there might still be children that need to be rendered. In this case, - * we render them outside of \`componentRef\`. - * NOTE: We make sure not to render this if \`repeatItemData\` is non-null, because that means we are rendering an array of - * blocks, and the children will be repeated within those blocks. - */ const shouldRenderChildrenOutsideRef = !this.component?.component && !this.repeatItemData; return shouldRenderChildrenOutsideRef ? this.useChildren : []; } get repeatItemData() { - /** - * we don't use \`state.useBlock\` here because the processing done within its logic includes evaluating the block's bindings, - * which will not work if there is a repeat. - */ const { repeat, ...blockWithoutRepeat } = this.block; if (!repeat?.collection) { @@ -3681,7 +3641,6 @@ export class RenderBlock { if (TARGET === \\"reactNative\\") { return RenderComponentWithContext; } else if (TARGET === \\"vue3\\") { - // vue3 expects a string for the this.component tag return \\"RenderComponent\\"; } else { return RenderComponent; @@ -5293,9 +5252,7 @@ export class CustomCode { scriptsInserted = []; scriptsRun = []; findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (this.elem.nativeElement && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = this.elem.nativeElement.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -5379,9 +5336,7 @@ export class CustomCode { scriptsInserted = []; scriptsRun = []; findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (this.elem.nativeElement && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = this.elem.nativeElement.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -5586,8 +5541,7 @@ export class FormComponent { const el = event.currentTarget; const headers = this.customHeaders || {}; let body: any; - const formData = new FormData(el); // TODO: maybe support null - + const formData = new FormData(el); const formPairs: { key: string; value: File | boolean | number | string | FileList; @@ -5617,7 +5571,6 @@ export class FormComponent { value = num; } } else if (el.type === \\"file\\") { - // TODO: one vs multiple files value = el.files; } else { value = el.value; @@ -5645,14 +5598,11 @@ export class FormComponent { ) { contentType = \\"multipart/form-data\\"; } - }); // TODO: send as urlEncoded or multipart by default - // because of ease of use and reliability in browser API - // for encoding the form? + }); if (contentType !== \\"application/json\\") { body = formData; } else { - // Json const json = {}; Array.from(formPairs).forEach(({ value, key }) => { set(json, key, value); @@ -5661,10 +5611,7 @@ export class FormComponent { } if (contentType && contentType !== \\"multipart/form-data\\") { - if ( - /* Zapier doesn't allow content-type header to be sent from browsers */ - !(sendWithJs && this.action?.includes(\\"zapier.com\\")) - ) { + if (!(sendWithJs && this.action?.includes(\\"zapier.com\\"))) { headers[\\"content-type\\"] = contentType; } } @@ -5689,15 +5636,11 @@ export class FormComponent { }/api/v1/form-submit?apiKey=\${builder.apiKey}&to=\${btoa( this.sendSubmissionsToEmail || \\"\\" )}&name=\${encodeURIComponent(this.name || \\"\\")}\`; - fetch( - this.sendSubmissionsTo === \\"email\\" ? formUrl : this.action!, - /* TODO: throw error if no action URL */ - { - body, - headers, - method: this.method || \\"post\\", - } - ).then( + fetch(this.sendSubmissionsTo === \\"email\\" ? formUrl : this.action!, { + body, + headers, + method: this.method || \\"post\\", + }).then( async (res) => { let body; const contentType = res.headers.get(\\"content-type\\"); @@ -5709,13 +5652,10 @@ export class FormComponent { } if (!res.ok && this.errorMessagePath) { - /* TODO: allow supplying an error formatter function */ let message = get(body, this.errorMessagePath); if (message) { if (typeof message !== \\"string\\") { - /* TODO: ideally convert json to yaml so it woul dbe like - error: - email has been taken */ message = JSON.stringify(message); } @@ -5740,13 +5680,11 @@ export class FormComponent { if (submitSuccessEvent.defaultPrevented) { return; } - /* TODO: option to turn this on/off? */ if (this.resetFormOnSubmit !== false) { this.formRef.nativeElement.reset(); } } - /* TODO: client side route event first that can be preventDefaulted */ if (this.successUrl) { if (this.formRef.nativeElement) { @@ -5874,7 +5812,6 @@ export class Image { this.imageLoaded = true; } useLazyLoading() { - // TODO: Add more checks here, like testing for real web browsers return !!this.lazy && this.isBrowser(); } isBrowser = function isBrowser() { @@ -7110,6 +7047,8 @@ export class MyBasicComponentModule {} " `; +exports[`Angular with Import Mapper Tests > jsx > Typescript Test > commentsWithQuotes 1`] = `"JSON5: invalid character '&' at 2:10"`; + exports[`Angular with Import Mapper Tests > jsx > Typescript Test > componentWithContext 1`] = ` "import { NgModule } from \\"@angular/core\\"; import { CommonModule } from \\"@angular/common\\"; @@ -8261,7 +8200,6 @@ export class RenderBlock { const ref = this.context.registeredComponents[componentName]; if (!ref) { - // TODO: Public doc page with more info about this message console.warn(\` Could not find a registered this.component named \\"\${componentName}\\". If you registered it, is the file that registered it imported by the file that needs to render it?\`); @@ -8314,11 +8252,6 @@ export class RenderBlock { componentRef: this.component?.component, componentOptions: { ...getBlockComponentOptions(this.useBlock), - - /** - * These this.attributes are passed to the wrapper element when there is one. If \`noWrap\` is set to true, then - * they are provided to the this.component itself directly. - */ ...(this.shouldWrap ? {} : { @@ -8330,28 +8263,14 @@ export class RenderBlock { }; } get useChildren() { - // TO-DO: When should \`canHaveChildren\` dictate rendering? - // This is currently commented out because some Builder components (e.g. Box) do not have \`canHaveChildren: true\`, - // but still receive and need to render children. - // return state.componentInfo?.canHaveChildren ? state.useBlock.children : []; return this.useBlock.children ?? []; } get childrenWithoutParentComponent() { - /** - * When there is no \`componentRef\`, there might still be children that need to be rendered. In this case, - * we render them outside of \`componentRef\`. - * NOTE: We make sure not to render this if \`repeatItemData\` is non-null, because that means we are rendering an array of - * blocks, and the children will be repeated within those blocks. - */ const shouldRenderChildrenOutsideRef = !this.component?.component && !this.repeatItemData; return shouldRenderChildrenOutsideRef ? this.useChildren : []; } get repeatItemData() { - /** - * we don't use \`state.useBlock\` here because the processing done within its logic includes evaluating the block's bindings, - * which will not work if there is a repeat. - */ const { repeat, ...blockWithoutRepeat } = this.block; if (!repeat?.collection) { @@ -8412,7 +8331,6 @@ export class RenderBlock { if (TARGET === \\"reactNative\\") { return RenderComponentWithContext; } else if (TARGET === \\"vue3\\") { - // vue3 expects a string for the this.component tag return \\"RenderComponent\\"; } else { return RenderComponent; diff --git a/packages/core/src/__tests__/__snapshots__/angular.test.ts.snap b/packages/core/src/__tests__/__snapshots__/angular.test.ts.snap index 94d0878829..05b5788c6d 100644 --- a/packages/core/src/__tests__/__snapshots__/angular.test.ts.snap +++ b/packages/core/src/__tests__/__snapshots__/angular.test.ts.snap @@ -1531,9 +1531,7 @@ export class CustomCode { scriptsInserted = []; scriptsRun = []; findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (this.elem.nativeElement && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = this.elem.nativeElement.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -1611,9 +1609,7 @@ export class CustomCode { scriptsInserted = []; scriptsRun = []; findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (this.elem.nativeElement && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = this.elem.nativeElement.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -1684,9 +1680,7 @@ export class CustomCode { scriptsInserted = []; scriptsRun = []; findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (this.elem.nativeElement && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = this.elem.nativeElement.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -1764,9 +1758,7 @@ export class CustomCode { scriptsInserted = []; scriptsRun = []; findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (this.elem.nativeElement && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = this.elem.nativeElement.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -1935,8 +1927,7 @@ export class FormComponent { const el = event.currentTarget; const headers = this.customHeaders || {}; let body; - const formData = new FormData(el); // TODO: maybe support null - + const formData = new FormData(el); const formPairs = Array.from( event.currentTarget.querySelectorAll(\\"input,select,textarea\\") ) @@ -1963,7 +1954,6 @@ export class FormComponent { value = num; } } else if (el.type === \\"file\\") { - // TODO: one vs multiple files value = el.files; } else { value = el.value; @@ -1991,14 +1981,11 @@ export class FormComponent { ) { contentType = \\"multipart/form-data\\"; } - }); // TODO: send as urlEncoded or multipart by default - // because of ease of use and reliability in browser API - // for encoding the form? + }); if (contentType !== \\"application/json\\") { body = formData; } else { - // Json const json = {}; Array.from(formPairs).forEach(({ value, key }) => { set(json, key, value); @@ -2007,10 +1994,7 @@ export class FormComponent { } if (contentType && contentType !== \\"multipart/form-data\\") { - if ( - /* Zapier doesn't allow content-type header to be sent from browsers */ - !(sendWithJs && this.action?.includes(\\"zapier.com\\")) - ) { + if (!(sendWithJs && this.action?.includes(\\"zapier.com\\"))) { headers[\\"content-type\\"] = contentType; } } @@ -2035,15 +2019,11 @@ export class FormComponent { }/api/v1/form-submit?apiKey=\${builder.apiKey}&to=\${btoa( this.sendSubmissionsToEmail || \\"\\" )}&name=\${encodeURIComponent(this.name || \\"\\")}\`; - fetch( - this.sendSubmissionsTo === \\"email\\" ? formUrl : this.action, - /* TODO: throw error if no action URL */ - { - body, - headers, - method: this.method || \\"post\\", - } - ).then( + fetch(this.sendSubmissionsTo === \\"email\\" ? formUrl : this.action, { + body, + headers, + method: this.method || \\"post\\", + }).then( async (res) => { let body; const contentType = res.headers.get(\\"content-type\\"); @@ -2055,13 +2035,10 @@ export class FormComponent { } if (!res.ok && this.errorMessagePath) { - /* TODO: allow supplying an error formatter function */ let message = get(body, this.errorMessagePath); if (message) { if (typeof message !== \\"string\\") { - /* TODO: ideally convert json to yaml so it woul dbe like - error: - email has been taken */ message = JSON.stringify(message); } @@ -2086,13 +2063,11 @@ export class FormComponent { if (submitSuccessEvent.defaultPrevented) { return; } - /* TODO: option to turn this on/off? */ if (this.resetFormOnSubmit !== false) { this.formRef.nativeElement.reset(); } } - /* TODO: client side route event first that can be preventDefaulted */ if (this.successUrl) { if (this.formRef.nativeElement) { @@ -2267,8 +2242,7 @@ export class FormComponent { const el = event.currentTarget; const headers = this.customHeaders || {}; let body; - const formData = new FormData(el); // TODO: maybe support null - + const formData = new FormData(el); const formPairs = Array.from( event.currentTarget.querySelectorAll(\\"input,select,textarea\\") ) @@ -2295,7 +2269,6 @@ export class FormComponent { value = num; } } else if (el.type === \\"file\\") { - // TODO: one vs multiple files value = el.files; } else { value = el.value; @@ -2323,14 +2296,11 @@ export class FormComponent { ) { contentType = \\"multipart/form-data\\"; } - }); // TODO: send as urlEncoded or multipart by default - // because of ease of use and reliability in browser API - // for encoding the form? + }); if (contentType !== \\"application/json\\") { body = formData; } else { - // Json const json = {}; Array.from(formPairs).forEach(({ value, key }) => { set(json, key, value); @@ -2339,10 +2309,7 @@ export class FormComponent { } if (contentType && contentType !== \\"multipart/form-data\\") { - if ( - /* Zapier doesn't allow content-type header to be sent from browsers */ - !(sendWithJs && this.action?.includes(\\"zapier.com\\")) - ) { + if (!(sendWithJs && this.action?.includes(\\"zapier.com\\"))) { headers[\\"content-type\\"] = contentType; } } @@ -2367,15 +2334,11 @@ export class FormComponent { }/api/v1/form-submit?apiKey=\${builder.apiKey}&to=\${btoa( this.sendSubmissionsToEmail || \\"\\" )}&name=\${encodeURIComponent(this.name || \\"\\")}\`; - fetch( - this.sendSubmissionsTo === \\"email\\" ? formUrl : this.action, - /* TODO: throw error if no action URL */ - { - body, - headers, - method: this.method || \\"post\\", - } - ).then( + fetch(this.sendSubmissionsTo === \\"email\\" ? formUrl : this.action, { + body, + headers, + method: this.method || \\"post\\", + }).then( async (res) => { let body; const contentType = res.headers.get(\\"content-type\\"); @@ -2387,13 +2350,10 @@ export class FormComponent { } if (!res.ok && this.errorMessagePath) { - /* TODO: allow supplying an error formatter function */ let message = get(body, this.errorMessagePath); if (message) { if (typeof message !== \\"string\\") { - /* TODO: ideally convert json to yaml so it woul dbe like - error: - email has been taken */ message = JSON.stringify(message); } @@ -2418,13 +2378,11 @@ export class FormComponent { if (submitSuccessEvent.defaultPrevented) { return; } - /* TODO: option to turn this on/off? */ if (this.resetFormOnSubmit !== false) { this.formRef.nativeElement.reset(); } } - /* TODO: client side route event first that can be preventDefaulted */ if (this.successUrl) { if (this.formRef.nativeElement) { @@ -2525,7 +2483,6 @@ export class Image { this.imageLoaded = true; } useLazyLoading() { - // TODO: Add more checks here, like testing for real web browsers return !!this.lazy && this.isBrowser(); } isBrowser = function isBrowser() { @@ -2632,7 +2589,6 @@ export class Image { this.imageLoaded = true; } useLazyLoading() { - // TODO: Add more checks here, like testing for real web browsers return !!this.lazy && this.isBrowser(); } isBrowser = function isBrowser() { @@ -4519,6 +4475,10 @@ export class MyBasicComponent { " `; +exports[`Angular > jsx > Javascript Test > commentsWithQuotes 1`] = `"JSON5: invalid character '&' at 2:10"`; + +exports[`Angular > jsx > Javascript Test > commentsWithQuotes 2`] = `"JSON5: invalid character '&' at 2:10"`; + exports[`Angular > jsx > Javascript Test > componentWithContext 1`] = ` "import { NgModule } from \\"@angular/core\\"; import { CommonModule } from \\"@angular/common\\"; @@ -6273,7 +6233,6 @@ export class RenderBlock { const ref = this.context.registeredComponents[componentName]; if (!ref) { - // TODO: Public doc page with more info about this message console.warn(\` Could not find a registered this.component named \\"\${componentName}\\". If you registered it, is the file that registered it imported by the file that needs to render it?\`); @@ -6326,11 +6285,6 @@ export class RenderBlock { componentRef: this.component?.component, componentOptions: { ...getBlockComponentOptions(this.useBlock), - - /** - * These this.attributes are passed to the wrapper element when there is one. If \`noWrap\` is set to true, then - * they are provided to the this.component itself directly. - */ ...(this.shouldWrap ? {} : { @@ -6342,28 +6296,14 @@ export class RenderBlock { }; } get useChildren() { - // TO-DO: When should \`canHaveChildren\` dictate rendering? - // This is currently commented out because some Builder components (e.g. Box) do not have \`canHaveChildren: true\`, - // but still receive and need to render children. - // return state.componentInfo?.canHaveChildren ? state.useBlock.children : []; return this.useBlock.children ?? []; } get childrenWithoutParentComponent() { - /** - * When there is no \`componentRef\`, there might still be children that need to be rendered. In this case, - * we render them outside of \`componentRef\`. - * NOTE: We make sure not to render this if \`repeatItemData\` is non-null, because that means we are rendering an array of - * blocks, and the children will be repeated within those blocks. - */ const shouldRenderChildrenOutsideRef = !this.component?.component && !this.repeatItemData; return shouldRenderChildrenOutsideRef ? this.useChildren : []; } get repeatItemData() { - /** - * we don't use \`state.useBlock\` here because the processing done within its logic includes evaluating the block's bindings, - * which will not work if there is a repeat. - */ const { repeat, ...blockWithoutRepeat } = this.block; if (!repeat?.collection) { @@ -6424,7 +6364,6 @@ export class RenderBlock { if (TARGET === \\"reactNative\\") { return RenderComponentWithContext; } else if (TARGET === \\"vue3\\") { - // vue3 expects a string for the this.component tag return \\"RenderComponent\\"; } else { return RenderComponent; @@ -6530,7 +6469,6 @@ export class RenderBlock { const ref = this.context.registeredComponents[componentName]; if (!ref) { - // TODO: Public doc page with more info about this message console.warn(\` Could not find a registered this.component named \\"\${componentName}\\". If you registered it, is the file that registered it imported by the file that needs to render it?\`); @@ -6583,11 +6521,6 @@ export class RenderBlock { componentRef: this.component?.component, componentOptions: { ...getBlockComponentOptions(this.useBlock), - - /** - * These this.attributes are passed to the wrapper element when there is one. If \`noWrap\` is set to true, then - * they are provided to the this.component itself directly. - */ ...(this.shouldWrap ? {} : { @@ -6599,28 +6532,14 @@ export class RenderBlock { }; } get useChildren() { - // TO-DO: When should \`canHaveChildren\` dictate rendering? - // This is currently commented out because some Builder components (e.g. Box) do not have \`canHaveChildren: true\`, - // but still receive and need to render children. - // return state.componentInfo?.canHaveChildren ? state.useBlock.children : []; return this.useBlock.children ?? []; } get childrenWithoutParentComponent() { - /** - * When there is no \`componentRef\`, there might still be children that need to be rendered. In this case, - * we render them outside of \`componentRef\`. - * NOTE: We make sure not to render this if \`repeatItemData\` is non-null, because that means we are rendering an array of - * blocks, and the children will be repeated within those blocks. - */ const shouldRenderChildrenOutsideRef = !this.component?.component && !this.repeatItemData; return shouldRenderChildrenOutsideRef ? this.useChildren : []; } get repeatItemData() { - /** - * we don't use \`state.useBlock\` here because the processing done within its logic includes evaluating the block's bindings, - * which will not work if there is a repeat. - */ const { repeat, ...blockWithoutRepeat } = this.block; if (!repeat?.collection) { @@ -6681,7 +6600,6 @@ export class RenderBlock { if (TARGET === \\"reactNative\\") { return RenderComponentWithContext; } else if (TARGET === \\"vue3\\") { - // vue3 expects a string for the this.component tag return \\"RenderComponent\\"; } else { return RenderComponent; @@ -9489,9 +9407,7 @@ export class CustomCode { scriptsInserted = []; scriptsRun = []; findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (this.elem.nativeElement && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = this.elem.nativeElement.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -9574,9 +9490,7 @@ export class CustomCode { scriptsInserted = []; scriptsRun = []; findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (this.elem.nativeElement && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = this.elem.nativeElement.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -9652,9 +9566,7 @@ export class CustomCode { scriptsInserted = []; scriptsRun = []; findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (this.elem.nativeElement && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = this.elem.nativeElement.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -9737,9 +9649,7 @@ export class CustomCode { scriptsInserted = []; scriptsRun = []; findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (this.elem.nativeElement && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = this.elem.nativeElement.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -9936,8 +9846,7 @@ export class FormComponent { const el = event.currentTarget; const headers = this.customHeaders || {}; let body: any; - const formData = new FormData(el); // TODO: maybe support null - + const formData = new FormData(el); const formPairs: { key: string; value: File | boolean | number | string | FileList; @@ -9967,7 +9876,6 @@ export class FormComponent { value = num; } } else if (el.type === \\"file\\") { - // TODO: one vs multiple files value = el.files; } else { value = el.value; @@ -9995,14 +9903,11 @@ export class FormComponent { ) { contentType = \\"multipart/form-data\\"; } - }); // TODO: send as urlEncoded or multipart by default - // because of ease of use and reliability in browser API - // for encoding the form? + }); if (contentType !== \\"application/json\\") { body = formData; } else { - // Json const json = {}; Array.from(formPairs).forEach(({ value, key }) => { set(json, key, value); @@ -10011,10 +9916,7 @@ export class FormComponent { } if (contentType && contentType !== \\"multipart/form-data\\") { - if ( - /* Zapier doesn't allow content-type header to be sent from browsers */ - !(sendWithJs && this.action?.includes(\\"zapier.com\\")) - ) { + if (!(sendWithJs && this.action?.includes(\\"zapier.com\\"))) { headers[\\"content-type\\"] = contentType; } } @@ -10039,15 +9941,11 @@ export class FormComponent { }/api/v1/form-submit?apiKey=\${builder.apiKey}&to=\${btoa( this.sendSubmissionsToEmail || \\"\\" )}&name=\${encodeURIComponent(this.name || \\"\\")}\`; - fetch( - this.sendSubmissionsTo === \\"email\\" ? formUrl : this.action!, - /* TODO: throw error if no action URL */ - { - body, - headers, - method: this.method || \\"post\\", - } - ).then( + fetch(this.sendSubmissionsTo === \\"email\\" ? formUrl : this.action!, { + body, + headers, + method: this.method || \\"post\\", + }).then( async (res) => { let body; const contentType = res.headers.get(\\"content-type\\"); @@ -10059,13 +9957,10 @@ export class FormComponent { } if (!res.ok && this.errorMessagePath) { - /* TODO: allow supplying an error formatter function */ let message = get(body, this.errorMessagePath); if (message) { if (typeof message !== \\"string\\") { - /* TODO: ideally convert json to yaml so it woul dbe like - error: - email has been taken */ message = JSON.stringify(message); } @@ -10090,13 +9985,11 @@ export class FormComponent { if (submitSuccessEvent.defaultPrevented) { return; } - /* TODO: option to turn this on/off? */ if (this.resetFormOnSubmit !== false) { this.formRef.nativeElement.reset(); } } - /* TODO: client side route event first that can be preventDefaulted */ if (this.successUrl) { if (this.formRef.nativeElement) { @@ -10299,8 +10192,7 @@ export class FormComponent { const el = event.currentTarget; const headers = this.customHeaders || {}; let body: any; - const formData = new FormData(el); // TODO: maybe support null - + const formData = new FormData(el); const formPairs: { key: string; value: File | boolean | number | string | FileList; @@ -10330,7 +10222,6 @@ export class FormComponent { value = num; } } else if (el.type === \\"file\\") { - // TODO: one vs multiple files value = el.files; } else { value = el.value; @@ -10358,14 +10249,11 @@ export class FormComponent { ) { contentType = \\"multipart/form-data\\"; } - }); // TODO: send as urlEncoded or multipart by default - // because of ease of use and reliability in browser API - // for encoding the form? + }); if (contentType !== \\"application/json\\") { body = formData; } else { - // Json const json = {}; Array.from(formPairs).forEach(({ value, key }) => { set(json, key, value); @@ -10374,10 +10262,7 @@ export class FormComponent { } if (contentType && contentType !== \\"multipart/form-data\\") { - if ( - /* Zapier doesn't allow content-type header to be sent from browsers */ - !(sendWithJs && this.action?.includes(\\"zapier.com\\")) - ) { + if (!(sendWithJs && this.action?.includes(\\"zapier.com\\"))) { headers[\\"content-type\\"] = contentType; } } @@ -10402,15 +10287,11 @@ export class FormComponent { }/api/v1/form-submit?apiKey=\${builder.apiKey}&to=\${btoa( this.sendSubmissionsToEmail || \\"\\" )}&name=\${encodeURIComponent(this.name || \\"\\")}\`; - fetch( - this.sendSubmissionsTo === \\"email\\" ? formUrl : this.action!, - /* TODO: throw error if no action URL */ - { - body, - headers, - method: this.method || \\"post\\", - } - ).then( + fetch(this.sendSubmissionsTo === \\"email\\" ? formUrl : this.action!, { + body, + headers, + method: this.method || \\"post\\", + }).then( async (res) => { let body; const contentType = res.headers.get(\\"content-type\\"); @@ -10422,13 +10303,10 @@ export class FormComponent { } if (!res.ok && this.errorMessagePath) { - /* TODO: allow supplying an error formatter function */ let message = get(body, this.errorMessagePath); if (message) { if (typeof message !== \\"string\\") { - /* TODO: ideally convert json to yaml so it woul dbe like - error: - email has been taken */ message = JSON.stringify(message); } @@ -10453,13 +10331,11 @@ export class FormComponent { if (submitSuccessEvent.defaultPrevented) { return; } - /* TODO: option to turn this on/off? */ if (this.resetFormOnSubmit !== false) { this.formRef.nativeElement.reset(); } } - /* TODO: client side route event first that can be preventDefaulted */ if (this.successUrl) { if (this.formRef.nativeElement) { @@ -10579,7 +10455,6 @@ export class Image { this.imageLoaded = true; } useLazyLoading() { - // TODO: Add more checks here, like testing for real web browsers return !!this.lazy && this.isBrowser(); } isBrowser = function isBrowser() { @@ -10705,7 +10580,6 @@ export class Image { this.imageLoaded = true; } useLazyLoading() { - // TODO: Add more checks here, like testing for real web browsers return !!this.lazy && this.isBrowser(); } isBrowser = function isBrowser() { @@ -12874,6 +12748,10 @@ export class MyBasicComponent { " `; +exports[`Angular > jsx > Typescript Test > commentsWithQuotes 1`] = `"JSON5: invalid character '&' at 2:10"`; + +exports[`Angular > jsx > Typescript Test > commentsWithQuotes 2`] = `"JSON5: invalid character '&' at 2:10"`; + exports[`Angular > jsx > Typescript Test > componentWithContext 1`] = ` "import { NgModule } from \\"@angular/core\\"; import { CommonModule } from \\"@angular/common\\"; @@ -14816,7 +14694,6 @@ export class RenderBlock { const ref = this.context.registeredComponents[componentName]; if (!ref) { - // TODO: Public doc page with more info about this message console.warn(\` Could not find a registered this.component named \\"\${componentName}\\". If you registered it, is the file that registered it imported by the file that needs to render it?\`); @@ -14869,11 +14746,6 @@ export class RenderBlock { componentRef: this.component?.component, componentOptions: { ...getBlockComponentOptions(this.useBlock), - - /** - * These this.attributes are passed to the wrapper element when there is one. If \`noWrap\` is set to true, then - * they are provided to the this.component itself directly. - */ ...(this.shouldWrap ? {} : { @@ -14885,28 +14757,14 @@ export class RenderBlock { }; } get useChildren() { - // TO-DO: When should \`canHaveChildren\` dictate rendering? - // This is currently commented out because some Builder components (e.g. Box) do not have \`canHaveChildren: true\`, - // but still receive and need to render children. - // return state.componentInfo?.canHaveChildren ? state.useBlock.children : []; return this.useBlock.children ?? []; } get childrenWithoutParentComponent() { - /** - * When there is no \`componentRef\`, there might still be children that need to be rendered. In this case, - * we render them outside of \`componentRef\`. - * NOTE: We make sure not to render this if \`repeatItemData\` is non-null, because that means we are rendering an array of - * blocks, and the children will be repeated within those blocks. - */ const shouldRenderChildrenOutsideRef = !this.component?.component && !this.repeatItemData; return shouldRenderChildrenOutsideRef ? this.useChildren : []; } get repeatItemData() { - /** - * we don't use \`state.useBlock\` here because the processing done within its logic includes evaluating the block's bindings, - * which will not work if there is a repeat. - */ const { repeat, ...blockWithoutRepeat } = this.block; if (!repeat?.collection) { @@ -14967,7 +14825,6 @@ export class RenderBlock { if (TARGET === \\"reactNative\\") { return RenderComponentWithContext; } else if (TARGET === \\"vue3\\") { - // vue3 expects a string for the this.component tag return \\"RenderComponent\\"; } else { return RenderComponent; @@ -15086,7 +14943,6 @@ export class RenderBlock { const ref = this.context.registeredComponents[componentName]; if (!ref) { - // TODO: Public doc page with more info about this message console.warn(\` Could not find a registered this.component named \\"\${componentName}\\". If you registered it, is the file that registered it imported by the file that needs to render it?\`); @@ -15139,11 +14995,6 @@ export class RenderBlock { componentRef: this.component?.component, componentOptions: { ...getBlockComponentOptions(this.useBlock), - - /** - * These this.attributes are passed to the wrapper element when there is one. If \`noWrap\` is set to true, then - * they are provided to the this.component itself directly. - */ ...(this.shouldWrap ? {} : { @@ -15155,28 +15006,14 @@ export class RenderBlock { }; } get useChildren() { - // TO-DO: When should \`canHaveChildren\` dictate rendering? - // This is currently commented out because some Builder components (e.g. Box) do not have \`canHaveChildren: true\`, - // but still receive and need to render children. - // return state.componentInfo?.canHaveChildren ? state.useBlock.children : []; return this.useBlock.children ?? []; } get childrenWithoutParentComponent() { - /** - * When there is no \`componentRef\`, there might still be children that need to be rendered. In this case, - * we render them outside of \`componentRef\`. - * NOTE: We make sure not to render this if \`repeatItemData\` is non-null, because that means we are rendering an array of - * blocks, and the children will be repeated within those blocks. - */ const shouldRenderChildrenOutsideRef = !this.component?.component && !this.repeatItemData; return shouldRenderChildrenOutsideRef ? this.useChildren : []; } get repeatItemData() { - /** - * we don't use \`state.useBlock\` here because the processing done within its logic includes evaluating the block's bindings, - * which will not work if there is a repeat. - */ const { repeat, ...blockWithoutRepeat } = this.block; if (!repeat?.collection) { @@ -15237,7 +15074,6 @@ export class RenderBlock { if (TARGET === \\"reactNative\\") { return RenderComponentWithContext; } else if (TARGET === \\"vue3\\") { - // vue3 expects a string for the this.component tag return \\"RenderComponent\\"; } else { return RenderComponent; diff --git a/packages/core/src/__tests__/__snapshots__/builder.test.ts.snap b/packages/core/src/__tests__/__snapshots__/builder.test.ts.snap index ad7bb52abe..2cb7b4c41a 100644 --- a/packages/core/src/__tests__/__snapshots__/builder.test.ts.snap +++ b/packages/core/src/__tests__/__snapshots__/builder.test.ts.snap @@ -218,9 +218,7 @@ Object.assign(state, { scriptsInserted: [], scriptsRun: [], findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -268,9 +266,7 @@ useState({ scriptsInserted: [], scriptsRun: [], findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -326,9 +322,7 @@ export default function MyComponent(props) { scriptsInserted: [], scriptsRun: [], findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem.current && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.current.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -415,9 +409,7 @@ Object.assign(state, { scriptsInserted: [], scriptsRun: [], findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -465,9 +457,7 @@ useState({ scriptsInserted: [], scriptsRun: [], findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -523,9 +513,7 @@ export default function MyComponent(props) { scriptsInserted: [], scriptsRun: [], findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem.current && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.current.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -685,7 +673,6 @@ Object.assign(state, { state.imageLoaded = true; }, useLazyLoading() { - // TODO: Add more checks here, like testing for real web browsers return !!props.lazy && state.isBrowser(); }, isBrowser: function isBrowser() { @@ -728,7 +715,6 @@ useState({ state.imageLoaded = true; }, useLazyLoading() { - // TODO: Add more checks here, like testing for real web browsers return !!props.lazy && state.isBrowser(); }, isBrowser: function isBrowser() { @@ -779,7 +765,6 @@ export default function MyComponent(props) { state.imageLoaded = true; }, useLazyLoading() { - // TODO: Add more checks here, like testing for real web browsers return !!props.lazy && state.isBrowser(); }, isBrowser: function isBrowser() { diff --git a/packages/core/src/__tests__/__snapshots__/context.test.ts.snap b/packages/core/src/__tests__/__snapshots__/context.test.ts.snap index 4172498f5d..0c833a991e 100644 --- a/packages/core/src/__tests__/__snapshots__/context.test.ts.snap +++ b/packages/core/src/__tests__/__snapshots__/context.test.ts.snap @@ -430,7 +430,6 @@ exports[`Context > Use and set context in complex components 1`] = ` const ref = components[state.useBlock.component?.name]; if (componentName && !ref) { - // TODO: Public doc page with more info about this message console.warn(\` Could not find a registered component named \\"\${componentName}\\". If you registered it, is the file that registered it imported by the file that needs to render it?\`); @@ -516,7 +515,6 @@ function RenderBlock(props) { const ref = components[useBlock().component?.name]; if (componentName && !ref) { - // TODO: Public doc page with more info about this message console.warn(\` Could not find a registered component named \\"\${componentName}\\". If you registered it, is the file that registered it imported by the file that needs to render it?\`); diff --git a/packages/core/src/__tests__/__snapshots__/html.test.ts.snap b/packages/core/src/__tests__/__snapshots__/html.test.ts.snap index e9fbbe13bf..80c79eec09 100644 --- a/packages/core/src/__tests__/__snapshots__/html.test.ts.snap +++ b/packages/core/src/__tests__/__snapshots__/html.test.ts.snap @@ -1678,9 +1678,7 @@ exports[`Html > jsx > Javascript Test > CustomCode 1`] = ` scriptsInserted: [], scriptsRun: [], findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -1769,9 +1767,7 @@ exports[`Html > jsx > Javascript Test > Embed 1`] = ` scriptsInserted: [], scriptsRun: [], findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -1882,7 +1878,6 @@ exports[`Html > jsx > Javascript Test > Image 1`] = ` update(); }, useLazyLoading() { - // TODO: Add more checks here, like testing for real web browsers return !!props.lazy && state.isBrowser(); }, isBrowser: function isBrowser() { @@ -3788,6 +3783,99 @@ exports[`Html > jsx > Javascript Test > classState 1`] = ` " `; +exports[`Html > jsx > Javascript Test > commentsWithQuotes 1`] = ` +"
+ + + Hello + + + ! I can run in React, Vue, Solid, or Liquid! +
+ + +" +`; + exports[`Html > jsx > Javascript Test > componentWithContext 1`] = ` " @@ -4881,7 +4969,6 @@ exports[`Html > jsx > Javascript Test > renderBlock 1`] = ` const ref = props.context.registeredComponents[componentName]; if (!ref) { - // TODO: Public doc page with more info about this message console.warn(\` Could not find a registered component named \\"\${componentName}\\". If you registered it, is the file that registered it imported by the file that needs to render it?\`); @@ -4934,11 +5021,6 @@ exports[`Html > jsx > Javascript Test > renderBlock 1`] = ` componentRef: state.component?.component, componentOptions: { ...getBlockComponentOptions(state.useBlock), - - /** - * These attributes are passed to the wrapper element when there is one. If \`noWrap\` is set to true, then - * they are provided to the component itself directly. - */ ...(state.shouldWrap ? {} : { @@ -4951,28 +5033,14 @@ exports[`Html > jsx > Javascript Test > renderBlock 1`] = ` }; }, get useChildren() { - // TO-DO: When should \`canHaveChildren\` dictate rendering? - // This is currently commented out because some Builder components (e.g. Box) do not have \`canHaveChildren: true\`, - // but still receive and need to render children. - // return state.componentInfo?.canHaveChildren ? state.useBlock.children : []; return state.useBlock.children ?? []; }, get childrenWithoutParentComponent() { - /** - * When there is no \`componentRef\`, there might still be children that need to be rendered. In this case, - * we render them outside of \`componentRef\`. - * NOTE: We make sure not to render this if \`repeatItemData\` is non-null, because that means we are rendering an array of - * blocks, and the children will be repeated within those blocks. - */ const shouldRenderChildrenOutsideRef = !state.component?.component && !state.repeatItemData; return shouldRenderChildrenOutsideRef ? state.useChildren : []; }, get repeatItemData() { - /** - * we don't use \`state.useBlock\` here because the processing done within its logic includes evaluating the block's bindings, - * which will not work if there is a repeat. - */ const { repeat, ...blockWithoutRepeat } = props.block; if (!repeat?.collection) { @@ -5034,7 +5102,6 @@ exports[`Html > jsx > Javascript Test > renderBlock 1`] = ` if (TARGET === \\"reactNative\\") { return RenderComponentWithContext; } else if (TARGET === \\"vue3\\") { - // vue3 expects a string for the component tag return \\"RenderComponent\\"; } else { return RenderComponent; @@ -7944,9 +8011,7 @@ exports[`Html > jsx > Typescript Test > CustomCode 1`] = ` scriptsInserted: [], scriptsRun: [], findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -8035,9 +8100,7 @@ exports[`Html > jsx > Typescript Test > Embed 1`] = ` scriptsInserted: [], scriptsRun: [], findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -8148,7 +8211,6 @@ exports[`Html > jsx > Typescript Test > Image 1`] = ` update(); }, useLazyLoading() { - // TODO: Add more checks here, like testing for real web browsers return !!props.lazy && state.isBrowser(); }, isBrowser: function isBrowser() { @@ -10054,6 +10116,99 @@ exports[`Html > jsx > Typescript Test > classState 1`] = ` " `; +exports[`Html > jsx > Typescript Test > commentsWithQuotes 1`] = ` +"
+ + + Hello + + + ! I can run in React, Vue, Solid, or Liquid! +
+ + +" +`; + exports[`Html > jsx > Typescript Test > componentWithContext 1`] = ` " @@ -11147,7 +11302,6 @@ exports[`Html > jsx > Typescript Test > renderBlock 1`] = ` const ref = props.context.registeredComponents[componentName]; if (!ref) { - // TODO: Public doc page with more info about this message console.warn(\` Could not find a registered component named \\"\${componentName}\\". If you registered it, is the file that registered it imported by the file that needs to render it?\`); @@ -11200,11 +11354,6 @@ exports[`Html > jsx > Typescript Test > renderBlock 1`] = ` componentRef: state.component?.component, componentOptions: { ...getBlockComponentOptions(state.useBlock), - - /** - * These attributes are passed to the wrapper element when there is one. If \`noWrap\` is set to true, then - * they are provided to the component itself directly. - */ ...(state.shouldWrap ? {} : { @@ -11217,28 +11366,14 @@ exports[`Html > jsx > Typescript Test > renderBlock 1`] = ` }; }, get useChildren() { - // TO-DO: When should \`canHaveChildren\` dictate rendering? - // This is currently commented out because some Builder components (e.g. Box) do not have \`canHaveChildren: true\`, - // but still receive and need to render children. - // return state.componentInfo?.canHaveChildren ? state.useBlock.children : []; return state.useBlock.children ?? []; }, get childrenWithoutParentComponent() { - /** - * When there is no \`componentRef\`, there might still be children that need to be rendered. In this case, - * we render them outside of \`componentRef\`. - * NOTE: We make sure not to render this if \`repeatItemData\` is non-null, because that means we are rendering an array of - * blocks, and the children will be repeated within those blocks. - */ const shouldRenderChildrenOutsideRef = !state.component?.component && !state.repeatItemData; return shouldRenderChildrenOutsideRef ? state.useChildren : []; }, get repeatItemData() { - /** - * we don't use \`state.useBlock\` here because the processing done within its logic includes evaluating the block's bindings, - * which will not work if there is a repeat. - */ const { repeat, ...blockWithoutRepeat } = props.block; if (!repeat?.collection) { @@ -11303,7 +11438,6 @@ exports[`Html > jsx > Typescript Test > renderBlock 1`] = ` if (TARGET === \\"reactNative\\") { return RenderComponentWithContext; } else if (TARGET === \\"vue3\\") { - // vue3 expects a string for the component tag return \\"RenderComponent\\"; } else { return RenderComponent; diff --git a/packages/core/src/__tests__/__snapshots__/liquid.test.ts.snap b/packages/core/src/__tests__/__snapshots__/liquid.test.ts.snap index a3abd00cb4..9e219485c8 100644 --- a/packages/core/src/__tests__/__snapshots__/liquid.test.ts.snap +++ b/packages/core/src/__tests__/__snapshots__/liquid.test.ts.snap @@ -542,6 +542,20 @@ exports[`Liquid > jsx > Javascript Test > classState 1`] = ` " `; +exports[`Liquid > jsx > Javascript Test > commentsWithQuotes 1`] = ` +"
+ + + Hello {{name}} ! I can run in React, Vue, Solid, or Liquid! +
+ +" +`; + exports[`Liquid > jsx > Javascript Test > componentWithContext 1`] = ` "
{{foo.value}}
@@ -1439,6 +1453,20 @@ exports[`Liquid > jsx > Typescript Test > classState 1`] = ` " `; +exports[`Liquid > jsx > Typescript Test > commentsWithQuotes 1`] = ` +"
+ + + Hello {{name}} ! I can run in React, Vue, Solid, or Liquid! +
+ +" +`; + exports[`Liquid > jsx > Typescript Test > componentWithContext 1`] = ` "
{{foo.value}}
diff --git a/packages/core/src/__tests__/__snapshots__/lit.test.ts.snap b/packages/core/src/__tests__/__snapshots__/lit.test.ts.snap index 3140f0226a..3a6e0b528f 100644 --- a/packages/core/src/__tests__/__snapshots__/lit.test.ts.snap +++ b/packages/core/src/__tests__/__snapshots__/lit.test.ts.snap @@ -803,9 +803,7 @@ export default class CustomCode extends LitElement { @state() scriptsRun = []; findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -878,9 +876,7 @@ export default class CustomCode extends LitElement { @state() scriptsRun = []; findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -1003,8 +999,7 @@ export default class FormComponent extends LitElement { const el = event.currentTarget; const headers = this.customHeaders || {}; let body; - const formData = new FormData(el); // TODO: maybe support null - + const formData = new FormData(el); const formPairs = Array.from( event.currentTarget.querySelectorAll(\\"input,select,textarea\\") ) @@ -1031,7 +1026,6 @@ export default class FormComponent extends LitElement { value = num; } } else if (el.type === \\"file\\") { - // TODO: one vs multiple files value = el.files; } else { value = el.value; @@ -1059,14 +1053,11 @@ export default class FormComponent extends LitElement { ) { contentType = \\"multipart/form-data\\"; } - }); // TODO: send as urlEncoded or multipart by default - // because of ease of use and reliability in browser API - // for encoding the form? + }); if (contentType !== \\"application/json\\") { body = formData; } else { - // Json const json = {}; Array.from(formPairs).forEach(({ value, key }) => { set(json, key, value); @@ -1075,10 +1066,7 @@ export default class FormComponent extends LitElement { } if (contentType && contentType !== \\"multipart/form-data\\") { - if ( - /* Zapier doesn't allow content-type header to be sent from browsers */ - !(sendWithJs && this.action?.includes(\\"zapier.com\\")) - ) { + if (!(sendWithJs && this.action?.includes(\\"zapier.com\\"))) { headers[\\"content-type\\"] = contentType; } } @@ -1103,15 +1091,11 @@ export default class FormComponent extends LitElement { }/api/v1/form-submit?apiKey=\${builder.apiKey}&to=\${btoa( this.sendSubmissionsToEmail || \\"\\" )}&name=\${encodeURIComponent(this.name || \\"\\")}\`; - fetch( - this.sendSubmissionsTo === \\"email\\" ? formUrl : this.action, - /* TODO: throw error if no action URL */ - { - body, - headers, - method: this.method || \\"post\\", - } - ).then( + fetch(this.sendSubmissionsTo === \\"email\\" ? formUrl : this.action, { + body, + headers, + method: this.method || \\"post\\", + }).then( async (res) => { let body; const contentType = res.headers.get(\\"content-type\\"); @@ -1123,13 +1107,10 @@ export default class FormComponent extends LitElement { } if (!res.ok && this.errorMessagePath) { - /* TODO: allow supplying an error formatter function */ let message = get(body, this.errorMessagePath); if (message) { if (typeof message !== \\"string\\") { - /* TODO: ideally convert json to yaml so it woul dbe like - error: - email has been taken */ message = JSON.stringify(message); } @@ -1154,13 +1135,11 @@ export default class FormComponent extends LitElement { if (submitSuccessEvent.defaultPrevented) { return; } - /* TODO: option to turn this on/off? */ if (this.resetFormOnSubmit !== false) { formRef.reset(); } } - /* TODO: client side route event first that can be preventDefaulted */ if (this.successUrl) { if (formRef) { @@ -1292,7 +1271,6 @@ export default class Image extends LitElement { this.imageLoaded = true; } useLazyLoading() { - // TODO: Add more checks here, like testing for real web browsers return !!this.lazy && this.isBrowser(); } isBrowser = function isBrowser() { @@ -2370,6 +2348,44 @@ export default class MyBasicComponent extends LitElement { " `; +exports[`Lit > jsx > Javascript Test > commentsWithQuotes 1`] = ` +"import { LitElement, html, css } from \\"lit\\"; +import { customElement, property, state, query } from \\"lit/decorators.js\\"; + +@customElement(\\"my-component\\") +export default class MyComponent extends LitElement { + createRenderRoot() { + return this; + } + + @state() name = \\"Steve\\"; + + handleFn() {} + doSomething = function doSomething() { + console.log(\\"This is a aa\\"); + }; + + render() { + return html\` + +
+ { + this.name = event.target.value; + }} @click=\${(event) => this.doSomething()} /> Hello \${ + this.name + } ! I can run in + React, Vue, Solid, or Liquid! +
+ + \`; + } +} +" +`; + exports[`Lit > jsx > Javascript Test > componentWithContext 1`] = ` "import Context1 from \\"@dummy/1\\"; import Context2 from \\"@dummy/2\\"; @@ -3282,7 +3298,6 @@ export default class RenderBlock extends LitElement { const ref = this.context.registeredComponents[componentName]; if (!ref) { - // TODO: Public doc page with more info about this message console.warn(\` Could not find a registered component named \\"\${componentName}\\". If you registered it, is the file that registered it imported by the file that needs to render it?\`); @@ -3335,11 +3350,6 @@ export default class RenderBlock extends LitElement { componentRef: this.component?.component, componentOptions: { ...getBlockComponentOptions(this.useBlock), - - /** - * These attributes are passed to the wrapper element when there is one. If \`noWrap\` is set to true, then - * they are provided to the component itself directly. - */ ...(this.shouldWrap ? {} : { @@ -3351,28 +3361,14 @@ export default class RenderBlock extends LitElement { }; } get useChildren() { - // TO-DO: When should \`canHaveChildren\` dictate rendering? - // This is currently commented out because some Builder components (e.g. Box) do not have \`canHaveChildren: true\`, - // but still receive and need to render children. - // return state.componentInfo?.canHaveChildren ? state.useBlock.children : []; return this.useBlock.children ?? []; } get childrenWithoutParentComponent() { - /** - * When there is no \`componentRef\`, there might still be children that need to be rendered. In this case, - * we render them outside of \`componentRef\`. - * NOTE: We make sure not to render this if \`repeatItemData\` is non-null, because that means we are rendering an array of - * blocks, and the children will be repeated within those blocks. - */ const shouldRenderChildrenOutsideRef = !this.component?.component && !this.repeatItemData; return shouldRenderChildrenOutsideRef ? this.useChildren : []; } get repeatItemData() { - /** - * we don't use \`state.useBlock\` here because the processing done within its logic includes evaluating the block's bindings, - * which will not work if there is a repeat. - */ const { repeat, ...blockWithoutRepeat } = this.block; if (!repeat?.collection) { @@ -3433,7 +3429,6 @@ export default class RenderBlock extends LitElement { if (TARGET === \\"reactNative\\") { return RenderComponentWithContext; } else if (TARGET === \\"vue3\\") { - // vue3 expects a string for the component tag return \\"RenderComponent\\"; } else { return RenderComponent; @@ -5009,9 +5004,7 @@ export default class CustomCode extends LitElement { @state() scriptsRun = []; findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -5089,9 +5082,7 @@ export default class CustomCode extends LitElement { @state() scriptsRun = []; findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -5242,8 +5233,7 @@ export default class FormComponent extends LitElement { const el = event.currentTarget; const headers = this.customHeaders || {}; let body: any; - const formData = new FormData(el); // TODO: maybe support null - + const formData = new FormData(el); const formPairs: { key: string; value: File | boolean | number | string | FileList; @@ -5273,7 +5263,6 @@ export default class FormComponent extends LitElement { value = num; } } else if (el.type === \\"file\\") { - // TODO: one vs multiple files value = el.files; } else { value = el.value; @@ -5301,14 +5290,11 @@ export default class FormComponent extends LitElement { ) { contentType = \\"multipart/form-data\\"; } - }); // TODO: send as urlEncoded or multipart by default - // because of ease of use and reliability in browser API - // for encoding the form? + }); if (contentType !== \\"application/json\\") { body = formData; } else { - // Json const json = {}; Array.from(formPairs).forEach(({ value, key }) => { set(json, key, value); @@ -5317,10 +5303,7 @@ export default class FormComponent extends LitElement { } if (contentType && contentType !== \\"multipart/form-data\\") { - if ( - /* Zapier doesn't allow content-type header to be sent from browsers */ - !(sendWithJs && this.action?.includes(\\"zapier.com\\")) - ) { + if (!(sendWithJs && this.action?.includes(\\"zapier.com\\"))) { headers[\\"content-type\\"] = contentType; } } @@ -5345,15 +5328,11 @@ export default class FormComponent extends LitElement { }/api/v1/form-submit?apiKey=\${builder.apiKey}&to=\${btoa( this.sendSubmissionsToEmail || \\"\\" )}&name=\${encodeURIComponent(this.name || \\"\\")}\`; - fetch( - this.sendSubmissionsTo === \\"email\\" ? formUrl : this.action!, - /* TODO: throw error if no action URL */ - { - body, - headers, - method: this.method || \\"post\\", - } - ).then( + fetch(this.sendSubmissionsTo === \\"email\\" ? formUrl : this.action!, { + body, + headers, + method: this.method || \\"post\\", + }).then( async (res) => { let body; const contentType = res.headers.get(\\"content-type\\"); @@ -5365,13 +5344,10 @@ export default class FormComponent extends LitElement { } if (!res.ok && this.errorMessagePath) { - /* TODO: allow supplying an error formatter function */ let message = get(body, this.errorMessagePath); if (message) { if (typeof message !== \\"string\\") { - /* TODO: ideally convert json to yaml so it woul dbe like - error: - email has been taken */ message = JSON.stringify(message); } @@ -5396,13 +5372,11 @@ export default class FormComponent extends LitElement { if (submitSuccessEvent.defaultPrevented) { return; } - /* TODO: option to turn this on/off? */ if (this.resetFormOnSubmit !== false) { formRef.reset(); } } - /* TODO: client side route event first that can be preventDefaulted */ if (this.successUrl) { if (formRef) { @@ -5556,7 +5530,6 @@ export default class Image extends LitElement { this.imageLoaded = true; } useLazyLoading() { - // TODO: Add more checks here, like testing for real web browsers return !!this.lazy && this.isBrowser(); } isBrowser = function isBrowser() { @@ -6775,6 +6748,44 @@ export default class MyBasicComponent extends LitElement { " `; +exports[`Lit > jsx > Typescript Test > commentsWithQuotes 1`] = ` +"import { LitElement, html, css } from \\"lit\\"; +import { customElement, property, state, query } from \\"lit/decorators.js\\"; + +@customElement(\\"my-component\\") +export default class MyComponent extends LitElement { + createRenderRoot() { + return this; + } + + @state() name = \\"Steve\\"; + + handleFn() {} + doSomething = function doSomething() { + console.log(\\"This is a aa\\"); + }; + + render() { + return html\` + +
+ { + this.name = event.target.value; + }} @click=\${(event) => this.doSomething()} /> Hello \${ + this.name + } ! I can run in + React, Vue, Solid, or Liquid! +
+ + \`; + } +} +" +`; + exports[`Lit > jsx > Typescript Test > componentWithContext 1`] = ` "import Context1 from \\"@dummy/1\\"; import Context2 from \\"@dummy/2\\"; @@ -7788,7 +7799,6 @@ export default class RenderBlock extends LitElement { const ref = this.context.registeredComponents[componentName]; if (!ref) { - // TODO: Public doc page with more info about this message console.warn(\` Could not find a registered component named \\"\${componentName}\\". If you registered it, is the file that registered it imported by the file that needs to render it?\`); @@ -7841,11 +7851,6 @@ export default class RenderBlock extends LitElement { componentRef: this.component?.component, componentOptions: { ...getBlockComponentOptions(this.useBlock), - - /** - * These attributes are passed to the wrapper element when there is one. If \`noWrap\` is set to true, then - * they are provided to the component itself directly. - */ ...(this.shouldWrap ? {} : { @@ -7857,28 +7862,14 @@ export default class RenderBlock extends LitElement { }; } get useChildren() { - // TO-DO: When should \`canHaveChildren\` dictate rendering? - // This is currently commented out because some Builder components (e.g. Box) do not have \`canHaveChildren: true\`, - // but still receive and need to render children. - // return state.componentInfo?.canHaveChildren ? state.useBlock.children : []; return this.useBlock.children ?? []; } get childrenWithoutParentComponent() { - /** - * When there is no \`componentRef\`, there might still be children that need to be rendered. In this case, - * we render them outside of \`componentRef\`. - * NOTE: We make sure not to render this if \`repeatItemData\` is non-null, because that means we are rendering an array of - * blocks, and the children will be repeated within those blocks. - */ const shouldRenderChildrenOutsideRef = !this.component?.component && !this.repeatItemData; return shouldRenderChildrenOutsideRef ? this.useChildren : []; } get repeatItemData() { - /** - * we don't use \`state.useBlock\` here because the processing done within its logic includes evaluating the block's bindings, - * which will not work if there is a repeat. - */ const { repeat, ...blockWithoutRepeat } = this.block; if (!repeat?.collection) { @@ -7939,7 +7930,6 @@ export default class RenderBlock extends LitElement { if (TARGET === \\"reactNative\\") { return RenderComponentWithContext; } else if (TARGET === \\"vue3\\") { - // vue3 expects a string for the component tag return \\"RenderComponent\\"; } else { return RenderComponent; diff --git a/packages/core/src/__tests__/__snapshots__/marko.test.ts.snap b/packages/core/src/__tests__/__snapshots__/marko.test.ts.snap index bdf74033d7..90818fade1 100644 --- a/packages/core/src/__tests__/__snapshots__/marko.test.ts.snap +++ b/packages/core/src/__tests__/__snapshots__/marko.test.ts.snap @@ -523,9 +523,7 @@ exports[`Marko > jsx > Javascript Test > ContentSlotJSX 1`] = ` exports[`Marko > jsx > Javascript Test > CustomCode 1`] = ` "class { findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (this.elem && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = this.elem.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -588,9 +586,7 @@ exports[`Marko > jsx > Javascript Test > CustomCode 1`] = ` exports[`Marko > jsx > Javascript Test > Embed 1`] = ` "class { findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (this.elem && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = this.elem.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -681,8 +677,7 @@ class { const el = event.currentTarget; const headers = this.input.customHeaders || {}; let body; - const formData = new FormData(el); // TODO: maybe support null - + const formData = new FormData(el); const formPairs = Array.from( event.currentTarget.querySelectorAll(\\"input,select,textarea\\") ) @@ -709,7 +704,6 @@ class { value = num; } } else if (el.type === \\"file\\") { - // TODO: one vs multiple files value = el.files; } else { value = el.value; @@ -737,14 +731,11 @@ class { ) { contentType = \\"multipart/form-data\\"; } - }); // TODO: send as urlEncoded or multipart by default - // because of ease of use and reliability in browser API - // for encoding the form? + }); if (contentType !== \\"application/json\\") { body = formData; } else { - // Json const json = {}; Array.from(formPairs).forEach(({ value, key }) => { set(json, key, value); @@ -753,10 +744,7 @@ class { } if (contentType && contentType !== \\"multipart/form-data\\") { - if ( - /* Zapier doesn't allow content-type header to be sent from browsers */ - !(sendWithJs && this.input.action?.includes(\\"zapier.com\\")) - ) { + if (!(sendWithJs && this.input.action?.includes(\\"zapier.com\\"))) { headers[\\"content-type\\"] = contentType; } } @@ -783,7 +771,6 @@ class { )}&name=\${encodeURIComponent(this.input.name || \\"\\")}\`; fetch( this.input.sendSubmissionsTo === \\"email\\" ? formUrl : this.input.action, - /* TODO: throw error if no action URL */ { body, headers, @@ -801,13 +788,10 @@ class { } if (!res.ok && this.input.errorMessagePath) { - /* TODO: allow supplying an error formatter function */ let message = get(body, this.input.errorMessagePath); if (message) { if (typeof message !== \\"string\\") { - /* TODO: ideally convert json to yaml so it woul dbe like - error: - email has been taken */ message = JSON.stringify(message); } @@ -832,13 +816,11 @@ class { if (submitSuccessEvent.defaultPrevented) { return; } - /* TODO: option to turn this on/off? */ if (this.input.resetFormOnSubmit !== false) { this.formRef.reset(); } } - /* TODO: client side route event first that can be preventDefaulted */ if (this.input.successUrl) { if (this.formRef) { @@ -894,7 +876,7 @@ class { } style { - .pre-82880ee4 { + .pre-fdd5ed4a { padding: 10px; color: red; text-align: center; @@ -929,7 +911,7 @@ style { > -
+    
           
 \${JSON.stringify(state.responseData, null, 2)}
 
@@ -950,7 +932,6 @@ exports[`Marko > jsx > Javascript Test > Image 1`] = `
     this.state.imageLoaded = true;
   }
   useLazyLoading() {
-    // TODO: Add more checks here, like testing for real web browsers
     return !!this.input.lazy && this.isBrowser();
   }
   isBrowser = function isBrowser() {
@@ -999,7 +980,7 @@ exports[`Marko > jsx > Javascript Test > Image 1`] = `
 }
 
 style { 
-  .img-89debb4e {
+  .img-72fa4045 {
     opacity: 1;
     transition: opacity 0.2s ease-in-out;
     object-fit: cover;
@@ -1012,7 +993,7 @@ style {
       (input.altText) component.setLoaded())
         srcset=(input.srcset)
@@ -1545,6 +1526,37 @@ style {
 
" `; +exports[`Marko > jsx > Javascript Test > commentsWithQuotes 1`] = ` +"class { + handleFn() {} + doSomething = function doSomething() { + console.log(\\"This is a aa\\"); + }; + + onCreate() { + this.state = { name: \\"Steve\\" }; + } +} + +style { + .input-5e4b86eb { + color: red; + } +} +
+ { + state.name = event.target.value; +}) + on-click(event => component.doSomething()) + /> + + Hello \${state.name} ! I can run in React, Vue, Solid, or Liquid! +
" +`; + exports[`Marko > jsx > Javascript Test > componentWithContext 1`] = ` "import Context1 from \\"@dummy/1\\"; import Context2 from \\"@dummy/2\\"; @@ -1962,7 +1974,6 @@ class { const ref = this.input.context.registeredComponents[componentName]; if (!ref) { - // TODO: Public doc page with more info about this message console.warn(\` Could not find a registered component named \\"\${componentName}\\". If you registered it, is the file that registered it imported by the file that needs to render it?\`); @@ -2015,11 +2026,6 @@ class { componentRef: this.component?.component, componentOptions: { ...getBlockComponentOptions(this.useBlock), - - /** - * These attributes are passed to the wrapper element when there is one. If \`noWrap\` is set to true, then - * they are provided to the component itself directly. - */ ...(this.shouldWrap ? {} : { @@ -2031,28 +2037,14 @@ class { }; } get useChildren() { - // TO-DO: When should \`canHaveChildren\` dictate rendering? - // This is currently commented out because some Builder components (e.g. Box) do not have \`canHaveChildren: true\`, - // but still receive and need to render children. - // return state.componentInfo?.canHaveChildren ? state.useBlock.children : []; return this.useBlock.children ?? []; } get childrenWithoutParentComponent() { - /** - * When there is no \`componentRef\`, there might still be children that need to be rendered. In this case, - * we render them outside of \`componentRef\`. - * NOTE: We make sure not to render this if \`repeatItemData\` is non-null, because that means we are rendering an array of - * blocks, and the children will be repeated within those blocks. - */ const shouldRenderChildrenOutsideRef = !this.component?.component && !this.repeatItemData; return shouldRenderChildrenOutsideRef ? this.useChildren : []; } get repeatItemData() { - /** - * we don't use \`state.useBlock\` here because the processing done within its logic includes evaluating the block's bindings, - * which will not work if there is a repeat. - */ const { repeat, ...blockWithoutRepeat } = this.input.block; if (!repeat?.collection) { @@ -2113,7 +2105,6 @@ class { if (TARGET === \\"reactNative\\") { return RenderComponentWithContext; } else if (TARGET === \\"vue3\\") { - // vue3 expects a string for the component tag return \\"RenderComponent\\"; } else { return RenderComponent; @@ -2895,9 +2886,7 @@ class { exports[`Marko > jsx > Typescript Test > CustomCode 1`] = ` "class { findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (this.elem && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = this.elem.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -2960,9 +2949,7 @@ exports[`Marko > jsx > Typescript Test > CustomCode 1`] = ` exports[`Marko > jsx > Typescript Test > Embed 1`] = ` "class { findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (this.elem && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = this.elem.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -3057,8 +3044,7 @@ class { const el = event.currentTarget; const headers = this.input.customHeaders || {}; let body: any; - const formData = new FormData(el); // TODO: maybe support null - + const formData = new FormData(el); const formPairs: { key: string; value: File | boolean | number | string | FileList; @@ -3088,7 +3074,6 @@ class { value = num; } } else if (el.type === \\"file\\") { - // TODO: one vs multiple files value = el.files; } else { value = el.value; @@ -3116,14 +3101,11 @@ class { ) { contentType = \\"multipart/form-data\\"; } - }); // TODO: send as urlEncoded or multipart by default - // because of ease of use and reliability in browser API - // for encoding the form? + }); if (contentType !== \\"application/json\\") { body = formData; } else { - // Json const json = {}; Array.from(formPairs).forEach(({ value, key }) => { set(json, key, value); @@ -3132,10 +3114,7 @@ class { } if (contentType && contentType !== \\"multipart/form-data\\") { - if ( - /* Zapier doesn't allow content-type header to be sent from browsers */ - !(sendWithJs && this.input.action?.includes(\\"zapier.com\\")) - ) { + if (!(sendWithJs && this.input.action?.includes(\\"zapier.com\\"))) { headers[\\"content-type\\"] = contentType; } } @@ -3162,7 +3141,6 @@ class { )}&name=\${encodeURIComponent(this.input.name || \\"\\")}\`; fetch( this.input.sendSubmissionsTo === \\"email\\" ? formUrl : this.input.action!, - /* TODO: throw error if no action URL */ { body, headers, @@ -3180,13 +3158,10 @@ class { } if (!res.ok && this.input.errorMessagePath) { - /* TODO: allow supplying an error formatter function */ let message = get(body, this.input.errorMessagePath); if (message) { if (typeof message !== \\"string\\") { - /* TODO: ideally convert json to yaml so it woul dbe like - error: - email has been taken */ message = JSON.stringify(message); } @@ -3211,13 +3186,11 @@ class { if (submitSuccessEvent.defaultPrevented) { return; } - /* TODO: option to turn this on/off? */ if (this.input.resetFormOnSubmit !== false) { this.formRef.reset(); } } - /* TODO: client side route event first that can be preventDefaulted */ if (this.input.successUrl) { if (this.formRef) { @@ -3273,7 +3246,7 @@ class { } style { - .pre-19a35b98 { + .pre-7945d906 { padding: 10px; color: red; text-align: center; @@ -3308,7 +3281,7 @@ style { > -
+    
           
 \${JSON.stringify(state.responseData, null, 2)}
 
@@ -3329,7 +3302,6 @@ exports[`Marko > jsx > Typescript Test > Image 1`] = `
     this.state.imageLoaded = true;
   }
   useLazyLoading() {
-    // TODO: Add more checks here, like testing for real web browsers
     return !!this.input.lazy && this.isBrowser();
   }
   isBrowser = function isBrowser() {
@@ -3378,7 +3350,7 @@ exports[`Marko > jsx > Typescript Test > Image 1`] = `
 }
 
 style { 
-  .img-3a26582e {
+  .img-cec1ccee {
     opacity: 1;
     transition: opacity 0.2s ease-in-out;
     object-fit: cover;
@@ -3391,7 +3363,7 @@ style {
       (input.altText) component.setLoaded())
         srcset=(input.srcset)
@@ -3926,6 +3898,37 @@ style {
 
" `; +exports[`Marko > jsx > Typescript Test > commentsWithQuotes 1`] = ` +"class { + handleFn() {} + doSomething = function doSomething() { + console.log(\\"This is a aa\\"); + }; + + onCreate() { + this.state = { name: \\"Steve\\" }; + } +} + +style { + .input-5e4b86eb { + color: red; + } +} +
+ { + state.name = event.target.value; +}) + on-click(event => component.doSomething()) + /> + + Hello \${state.name} ! I can run in React, Vue, Solid, or Liquid! +
" +`; + exports[`Marko > jsx > Typescript Test > componentWithContext 1`] = ` "import Context1 from \\"@dummy/1\\"; import Context2 from \\"@dummy/2\\"; @@ -4352,7 +4355,6 @@ class { const ref = this.input.context.registeredComponents[componentName]; if (!ref) { - // TODO: Public doc page with more info about this message console.warn(\` Could not find a registered component named \\"\${componentName}\\". If you registered it, is the file that registered it imported by the file that needs to render it?\`); @@ -4405,11 +4407,6 @@ class { componentRef: this.component?.component, componentOptions: { ...getBlockComponentOptions(this.useBlock), - - /** - * These attributes are passed to the wrapper element when there is one. If \`noWrap\` is set to true, then - * they are provided to the component itself directly. - */ ...(this.shouldWrap ? {} : { @@ -4421,28 +4418,14 @@ class { }; } get useChildren() { - // TO-DO: When should \`canHaveChildren\` dictate rendering? - // This is currently commented out because some Builder components (e.g. Box) do not have \`canHaveChildren: true\`, - // but still receive and need to render children. - // return state.componentInfo?.canHaveChildren ? state.useBlock.children : []; return this.useBlock.children ?? []; } get childrenWithoutParentComponent() { - /** - * When there is no \`componentRef\`, there might still be children that need to be rendered. In this case, - * we render them outside of \`componentRef\`. - * NOTE: We make sure not to render this if \`repeatItemData\` is non-null, because that means we are rendering an array of - * blocks, and the children will be repeated within those blocks. - */ const shouldRenderChildrenOutsideRef = !this.component?.component && !this.repeatItemData; return shouldRenderChildrenOutsideRef ? this.useChildren : []; } get repeatItemData() { - /** - * we don't use \`state.useBlock\` here because the processing done within its logic includes evaluating the block's bindings, - * which will not work if there is a repeat. - */ const { repeat, ...blockWithoutRepeat } = this.input.block; if (!repeat?.collection) { @@ -4503,7 +4486,6 @@ class { if (TARGET === \\"reactNative\\") { return RenderComponentWithContext; } else if (TARGET === \\"vue3\\") { - // vue3 expects a string for the component tag return \\"RenderComponent\\"; } else { return RenderComponent; diff --git a/packages/core/src/__tests__/__snapshots__/parse-jsx.test.ts.snap b/packages/core/src/__tests__/__snapshots__/parse-jsx.test.ts.snap index fa63ed3bbe..28bbf97707 100644 --- a/packages/core/src/__tests__/__snapshots__/parse-jsx.test.ts.snap +++ b/packages/core/src/__tests__/__snapshots__/parse-jsx.test.ts.snap @@ -3237,9 +3237,7 @@ exports[`Parse JSX > Javascript > CustomCode 1`] = ` "state": { "findAndRunScripts": { "code": "findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem && typeof window !== 'undefined') { - /** @type {HTMLScriptElement[]} */ const scripts = elem.getElementsByTagName('script'); for (let i = 0; i < scripts.length; i++) { @@ -3340,9 +3338,7 @@ exports[`Parse JSX > Javascript > Embed 1`] = ` "state": { "findAndRunScripts": { "code": "findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem && typeof window !== 'undefined') { - /** @type {HTMLScriptElement[]} */ const scripts = elem.getElementsByTagName('script'); for (let i = 0; i < scripts.length; i++) { @@ -3920,8 +3916,7 @@ exports[`Parse JSX > Javascript > Form 1`] = ` const el = event.currentTarget; const headers = props.customHeaders || {}; let body; - const formData = new FormData(el); // TODO: maybe support null - + const formData = new FormData(el); const formPairs = Array.from(event.currentTarget.querySelectorAll('input,select,textarea')).filter(el => !!el.name).map(el => { let value; const key = el.name; @@ -3944,7 +3939,6 @@ exports[`Parse JSX > Javascript > Form 1`] = ` value = num; } } else if (el.type === 'file') { - // TODO: one vs multiple files value = el.files; } else { value = el.value; @@ -3970,14 +3964,11 @@ exports[`Parse JSX > Javascript > Form 1`] = ` if (value instanceof File || Array.isArray(value) && value[0] instanceof File || value instanceof FileList) { contentType = 'multipart/form-data'; } - }); // TODO: send as urlEncoded or multipart by default - // because of ease of use and reliability in browser API - // for encoding the form? + }); if (contentType !== 'application/json') { body = formData; } else { - // Json const json = {}; Array.from(formPairs).forEach(({ value, @@ -3989,9 +3980,7 @@ exports[`Parse JSX > Javascript > Form 1`] = ` } if (contentType && contentType !== 'multipart/form-data') { - if ( - /* Zapier doesn't allow content-type header to be sent from browsers */ - !(sendWithJs && props.action?.includes('zapier.com'))) { + if (!(sendWithJs && props.action?.includes('zapier.com'))) { headers['content-type'] = contentType; } } @@ -4012,9 +4001,7 @@ exports[`Parse JSX > Javascript > Form 1`] = ` state.formState = 'sending'; const formUrl = \`\${builder.env === 'dev' ? 'http://localhost:5000' : 'https://builder.io'}/api/v1/form-submit?apiKey=\${builder.apiKey}&to=\${btoa(props.sendSubmissionsToEmail || '')}&name=\${encodeURIComponent(props.name || '')}\`; - fetch(props.sendSubmissionsTo === 'email' ? formUrl : props.action - /* TODO: throw error if no action URL */ - , { + fetch(props.sendSubmissionsTo === 'email' ? formUrl : props.action, { body, headers, method: props.method || 'post' @@ -4029,13 +4016,10 @@ exports[`Parse JSX > Javascript > Form 1`] = ` } if (!res.ok && props.errorMessagePath) { - /* TODO: allow supplying an error formatter function */ let message = get(body, props.errorMessagePath); if (message) { if (typeof message !== 'string') { - /* TODO: ideally convert json to yaml so it woul dbe like - error: - email has been taken */ message = JSON.stringify(message); } @@ -4060,15 +4044,11 @@ exports[`Parse JSX > Javascript > Form 1`] = ` if (submitSuccessEvent.defaultPrevented) { return; } - /* TODO: option to turn this on/off? */ - if (props.resetFormOnSubmit !== false) { formRef.reset(); } } - /* TODO: client side route event first that can be preventDefaulted */ - if (props.successUrl) { if (formRef) { @@ -4420,7 +4400,6 @@ exports[`Parse JSX > Javascript > Image 1`] = ` }, "useLazyLoading": { "code": "useLazyLoading() { - // TODO: Add more checks here, like testing for real web browsers return !!props.lazy && state.isBrowser(); }", "type": "method", @@ -7468,6 +7447,140 @@ exports[`Parse JSX > Javascript > classState 1`] = ` } `; +exports[`Parse JSX > Javascript > commentsWithQuotes 1`] = ` +{ + "@type": "@builder.io/mitosis/component", + "children": [ + { + "@type": "@builder.io/mitosis/node", + "bindings": {}, + "children": [ + { + "@type": "@builder.io/mitosis/node", + "bindings": {}, + "children": [], + "meta": {}, + "name": "div", + "properties": { + "_text": " + ", + }, + "scope": {}, + }, + { + "@type": "@builder.io/mitosis/node", + "bindings": { + "css": { + "code": "{ + color: \\"red\\" +}", + "type": "single", + }, + "onChange": { + "arguments": [ + "event", + ], + "code": "{ + state.name = event.target.value; +}", + "type": "single", + }, + "onClick": { + "code": "state.doSomething()", + "type": "single", + }, + "value": { + "code": "state.name", + "type": "single", + }, + }, + "children": [], + "meta": {}, + "name": "input", + "properties": {}, + "scope": {}, + }, + { + "@type": "@builder.io/mitosis/node", + "bindings": {}, + "children": [], + "meta": {}, + "name": "div", + "properties": { + "_text": " + Hello + ", + }, + "scope": {}, + }, + { + "@type": "@builder.io/mitosis/node", + "bindings": { + "_text": { + "code": "state.name", + "type": "single", + }, + }, + "children": [], + "meta": {}, + "name": "div", + "properties": {}, + "scope": {}, + }, + { + "@type": "@builder.io/mitosis/node", + "bindings": {}, + "children": [], + "meta": {}, + "name": "div", + "properties": { + "_text": "! I can run in React, Vue, Solid, or Liquid! + ", + }, + "scope": {}, + }, + ], + "meta": {}, + "name": "div", + "properties": {}, + "scope": {}, + }, + ], + "context": { + "get": {}, + "set": {}, + }, + "exports": {}, + "hooks": { + "onEvent": [], + "onMount": [], + }, + "imports": [], + "inputs": [], + "meta": {}, + "name": "MyComponent", + "refs": {}, + "state": { + "doSomething": { + "code": "function doSomething() { + console.log(\\"This is a aa\\"); +}", + "type": "function", + }, + "handleFn": { + "code": "handleFn() {}", + "type": "method", + }, + "name": { + "code": "\\"Steve\\"", + "propertyType": "normal", + "type": "property", + }, + }, + "subComponents": [], +} +`; + exports[`Parse JSX > Javascript > componentWithContext 1`] = ` { "@type": "@builder.io/mitosis/component", @@ -10409,12 +10522,6 @@ exports[`Parse JSX > Javascript > renderBlock 1`] = ` }, "childrenWithoutParentComponent": { "code": "get childrenWithoutParentComponent() { - /** - * When there is no \`componentRef\`, there might still be children that need to be rendered. In this case, - * we render them outside of \`componentRef\`. - * NOTE: We make sure not to render this if \`repeatItemData\` is non-null, because that means we are rendering an array of - * blocks, and the children will be repeated within those blocks. - */ const shouldRenderChildrenOutsideRef = !state.component?.component && !state.repeatItemData; return shouldRenderChildrenOutsideRef ? state.useChildren : []; }", @@ -10436,7 +10543,6 @@ exports[`Parse JSX > Javascript > renderBlock 1`] = ` const ref = props.context.registeredComponents[componentName]; if (!ref) { - // TODO: Public doc page with more info about this message console.warn(\` Could not find a registered component named \\"\${componentName}\\". If you registered it, is the file that registered it imported by the file that needs to render it?\`); @@ -10468,11 +10574,6 @@ exports[`Parse JSX > Javascript > renderBlock 1`] = ` blockChildren: state.useChildren, componentRef: state.component?.component, componentOptions: { ...getBlockComponentOptions(state.useBlock), - - /** - * These attributes are passed to the wrapper element when there is one. If \`noWrap\` is set to true, then - * they are provided to the component itself directly. - */ ...(state.shouldWrap ? {} : { attributes: { ...state.attributes, ...state.actions @@ -10490,7 +10591,6 @@ exports[`Parse JSX > Javascript > renderBlock 1`] = ` if (TARGET === 'reactNative') { return RenderComponentWithContext; } else if (TARGET === 'vue3') { - // vue3 expects a string for the component tag return 'RenderComponent'; } else { return RenderComponent; @@ -10500,10 +10600,6 @@ exports[`Parse JSX > Javascript > renderBlock 1`] = ` }, "repeatItemData": { "code": "get repeatItemData() { - /** - * we don't use \`state.useBlock\` here because the processing done within its logic includes evaluating the block's bindings, - * which will not work if there is a repeat. - */ const { repeat, ...blockWithoutRepeat @@ -10565,10 +10661,6 @@ exports[`Parse JSX > Javascript > renderBlock 1`] = ` }, "useChildren": { "code": "get useChildren() { - // TO-DO: When should \`canHaveChildren\` dictate rendering? - // This is currently commented out because some Builder components (e.g. Box) do not have \`canHaveChildren: true\`, - // but still receive and need to render children. - // return state.componentInfo?.canHaveChildren ? state.useBlock.children : []; return state.useBlock.children ?? []; }", "type": "getter", @@ -15920,9 +16012,7 @@ exports[`Parse JSX > Typescript > CustomCode 1`] = ` "state": { "findAndRunScripts": { "code": "findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem && typeof window !== 'undefined') { - /** @type {HTMLScriptElement[]} */ const scripts = elem.getElementsByTagName('script'); for (let i = 0; i < scripts.length; i++) { @@ -16031,9 +16121,7 @@ exports[`Parse JSX > Typescript > Embed 1`] = ` "state": { "findAndRunScripts": { "code": "findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem && typeof window !== 'undefined') { - /** @type {HTMLScriptElement[]} */ const scripts = elem.getElementsByTagName('script'); for (let i = 0; i < scripts.length; i++) { @@ -16622,8 +16710,7 @@ exports[`Parse JSX > Typescript > Form 1`] = ` const el = event.currentTarget; const headers = props.customHeaders || {}; let body: any; - const formData = new FormData(el); // TODO: maybe support null - + const formData = new FormData(el); const formPairs: { key: string; value: File | boolean | number | string | FileList; @@ -16649,7 +16736,6 @@ exports[`Parse JSX > Typescript > Form 1`] = ` value = num; } } else if (el.type === 'file') { - // TODO: one vs multiple files value = el.files; } else { value = el.value; @@ -16675,14 +16761,11 @@ exports[`Parse JSX > Typescript > Form 1`] = ` if (value instanceof File || Array.isArray(value) && value[0] instanceof File || value instanceof FileList) { contentType = 'multipart/form-data'; } - }); // TODO: send as urlEncoded or multipart by default - // because of ease of use and reliability in browser API - // for encoding the form? + }); if (contentType !== 'application/json') { body = formData; } else { - // Json const json = {}; Array.from(formPairs).forEach(({ value, @@ -16694,9 +16777,7 @@ exports[`Parse JSX > Typescript > Form 1`] = ` } if (contentType && contentType !== 'multipart/form-data') { - if ( - /* Zapier doesn't allow content-type header to be sent from browsers */ - !(sendWithJs && props.action?.includes('zapier.com'))) { + if (!(sendWithJs && props.action?.includes('zapier.com'))) { headers['content-type'] = contentType; } } @@ -16717,9 +16798,7 @@ exports[`Parse JSX > Typescript > Form 1`] = ` state.formState = 'sending'; const formUrl = \`\${builder.env === 'dev' ? 'http://localhost:5000' : 'https://builder.io'}/api/v1/form-submit?apiKey=\${builder.apiKey}&to=\${btoa(props.sendSubmissionsToEmail || '')}&name=\${encodeURIComponent(props.name || '')}\`; - fetch(props.sendSubmissionsTo === 'email' ? formUrl : props.action! - /* TODO: throw error if no action URL */ - , { + fetch(props.sendSubmissionsTo === 'email' ? formUrl : props.action!, { body, headers, method: props.method || 'post' @@ -16734,13 +16813,10 @@ exports[`Parse JSX > Typescript > Form 1`] = ` } if (!res.ok && props.errorMessagePath) { - /* TODO: allow supplying an error formatter function */ let message = get(body, props.errorMessagePath); if (message) { if (typeof message !== 'string') { - /* TODO: ideally convert json to yaml so it woul dbe like - error: - email has been taken */ message = JSON.stringify(message); } @@ -16765,15 +16841,11 @@ exports[`Parse JSX > Typescript > Form 1`] = ` if (submitSuccessEvent.defaultPrevented) { return; } - /* TODO: option to turn this on/off? */ - if (props.resetFormOnSubmit !== false) { formRef.reset(); } } - /* TODO: client side route event first that can be preventDefaulted */ - if (props.successUrl) { if (formRef) { @@ -17152,7 +17224,6 @@ exports[`Parse JSX > Typescript > Image 1`] = ` }, "useLazyLoading": { "code": "useLazyLoading() { - // TODO: Add more checks here, like testing for real web browsers return !!props.lazy && state.isBrowser(); }", "type": "method", @@ -20386,6 +20457,140 @@ exports[`Parse JSX > Typescript > classState 1`] = ` } `; +exports[`Parse JSX > Typescript > commentsWithQuotes 1`] = ` +{ + "@type": "@builder.io/mitosis/component", + "children": [ + { + "@type": "@builder.io/mitosis/node", + "bindings": {}, + "children": [ + { + "@type": "@builder.io/mitosis/node", + "bindings": {}, + "children": [], + "meta": {}, + "name": "div", + "properties": { + "_text": " + ", + }, + "scope": {}, + }, + { + "@type": "@builder.io/mitosis/node", + "bindings": { + "css": { + "code": "{ + color: \\"red\\" +}", + "type": "single", + }, + "onChange": { + "arguments": [ + "event", + ], + "code": "{ + state.name = event.target.value; +}", + "type": "single", + }, + "onClick": { + "code": "state.doSomething()", + "type": "single", + }, + "value": { + "code": "state.name", + "type": "single", + }, + }, + "children": [], + "meta": {}, + "name": "input", + "properties": {}, + "scope": {}, + }, + { + "@type": "@builder.io/mitosis/node", + "bindings": {}, + "children": [], + "meta": {}, + "name": "div", + "properties": { + "_text": " + Hello + ", + }, + "scope": {}, + }, + { + "@type": "@builder.io/mitosis/node", + "bindings": { + "_text": { + "code": "state.name", + "type": "single", + }, + }, + "children": [], + "meta": {}, + "name": "div", + "properties": {}, + "scope": {}, + }, + { + "@type": "@builder.io/mitosis/node", + "bindings": {}, + "children": [], + "meta": {}, + "name": "div", + "properties": { + "_text": "! I can run in React, Vue, Solid, or Liquid! + ", + }, + "scope": {}, + }, + ], + "meta": {}, + "name": "div", + "properties": {}, + "scope": {}, + }, + ], + "context": { + "get": {}, + "set": {}, + }, + "exports": {}, + "hooks": { + "onEvent": [], + "onMount": [], + }, + "imports": [], + "inputs": [], + "meta": {}, + "name": "MyComponent", + "refs": {}, + "state": { + "doSomething": { + "code": "function doSomething() { + console.log(\\"This is a aa\\"); +}", + "type": "function", + }, + "handleFn": { + "code": "handleFn() {}", + "type": "method", + }, + "name": { + "code": "\\"Steve\\"", + "propertyType": "normal", + "type": "property", + }, + }, + "subComponents": [], +} +`; + exports[`Parse JSX > Typescript > componentWithContext 1`] = ` { "@type": "@builder.io/mitosis/component", @@ -23490,12 +23695,6 @@ exports[`Parse JSX > Typescript > renderBlock 1`] = ` }, "childrenWithoutParentComponent": { "code": "get childrenWithoutParentComponent() { - /** - * When there is no \`componentRef\`, there might still be children that need to be rendered. In this case, - * we render them outside of \`componentRef\`. - * NOTE: We make sure not to render this if \`repeatItemData\` is non-null, because that means we are rendering an array of - * blocks, and the children will be repeated within those blocks. - */ const shouldRenderChildrenOutsideRef = !state.component?.component && !state.repeatItemData; return shouldRenderChildrenOutsideRef ? state.useChildren : []; }", @@ -23517,7 +23716,6 @@ exports[`Parse JSX > Typescript > renderBlock 1`] = ` const ref = props.context.registeredComponents[componentName]; if (!ref) { - // TODO: Public doc page with more info about this message console.warn(\` Could not find a registered component named \\"\${componentName}\\". If you registered it, is the file that registered it imported by the file that needs to render it?\`); @@ -23549,11 +23747,6 @@ exports[`Parse JSX > Typescript > renderBlock 1`] = ` blockChildren: state.useChildren, componentRef: state.component?.component, componentOptions: { ...getBlockComponentOptions(state.useBlock), - - /** - * These attributes are passed to the wrapper element when there is one. If \`noWrap\` is set to true, then - * they are provided to the component itself directly. - */ ...(state.shouldWrap ? {} : { attributes: { ...state.attributes, ...state.actions @@ -23571,7 +23764,6 @@ exports[`Parse JSX > Typescript > renderBlock 1`] = ` if (TARGET === 'reactNative') { return RenderComponentWithContext; } else if (TARGET === 'vue3') { - // vue3 expects a string for the component tag return 'RenderComponent'; } else { return RenderComponent; @@ -23581,10 +23773,6 @@ exports[`Parse JSX > Typescript > renderBlock 1`] = ` }, "repeatItemData": { "code": "get repeatItemData() { - /** - * we don't use \`state.useBlock\` here because the processing done within its logic includes evaluating the block's bindings, - * which will not work if there is a repeat. - */ const { repeat, ...blockWithoutRepeat @@ -23646,10 +23834,6 @@ exports[`Parse JSX > Typescript > renderBlock 1`] = ` }, "useChildren": { "code": "get useChildren() { - // TO-DO: When should \`canHaveChildren\` dictate rendering? - // This is currently commented out because some Builder components (e.g. Box) do not have \`canHaveChildren: true\`, - // but still receive and need to render children. - // return state.componentInfo?.canHaveChildren ? state.useBlock.children : []; return state.useBlock.children ?? []; }", "type": "getter", @@ -26146,12 +26330,7 @@ exports[`Parse JSX > parseStateObject 1`] = ` { "allRegisteredComponents": { "code": "get allRegisteredComponents() { - const allComponentsArray = [...getDefaultRegisteredComponents(), // While this \`components\` object is deprecated, we must maintain support for it. - // Since users are able to override our default components, we need to make sure that we do not break such - // existing usage. - // This is why we spread \`components\` after the default Builder.io components, but before the \`props.customComponents\`, - // which is the new standard way of providing custom components, and must therefore take precedence. - ...components, ...(props.customComponents || [])]; + const allComponentsArray = [...getDefaultRegisteredComponents(), ...components, ...(props.customComponents || [])]; const allComponents = allComponentsArray.reduce((acc, curr) => ({ ...acc, [curr.name]: curr }), ({} as RegisteredComponents)); @@ -26207,7 +26386,6 @@ exports[`Parse JSX > parseStateObject 1`] = ` }, "evaluateJsCode": { "code": "evaluateJsCode() { - // run any dynamic JS code attached to content const jsCode = state.useContent?.data?.jsCode; if (jsCode) { @@ -26299,7 +26477,6 @@ exports[`Parse JSX > parseStateObject 1`] = ` case 'builder.patchUpdates': { - // TODO break; } } diff --git a/packages/core/src/__tests__/__snapshots__/preact.test.ts.snap b/packages/core/src/__tests__/__snapshots__/preact.test.ts.snap index 5dcf30ff6e..5c659e27bf 100644 --- a/packages/core/src/__tests__/__snapshots__/preact.test.ts.snap +++ b/packages/core/src/__tests__/__snapshots__/preact.test.ts.snap @@ -637,9 +637,7 @@ function CustomCode(props) { const [scriptsRun, setScriptsRun] = useState(() => []); function findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem.current && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.current.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -709,9 +707,7 @@ function CustomCode(props) { const [scriptsRun, setScriptsRun] = useState(() => []); function findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem.current && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.current.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -808,8 +804,7 @@ function FormComponent(props) { const el = event.currentTarget; const headers = props.customHeaders || {}; let body; - const formData = new FormData(el); // TODO: maybe support null - + const formData = new FormData(el); const formPairs = Array.from( event.currentTarget.querySelectorAll(\\"input,select,textarea\\") ) @@ -836,7 +831,6 @@ function FormComponent(props) { value = num; } } else if (el.type === \\"file\\") { - // TODO: one vs multiple files value = el.files; } else { value = el.value; @@ -864,14 +858,11 @@ function FormComponent(props) { ) { contentType = \\"multipart/form-data\\"; } - }); // TODO: send as urlEncoded or multipart by default - // because of ease of use and reliability in browser API - // for encoding the form? + }); if (contentType !== \\"application/json\\") { body = formData; } else { - // Json const json = {}; Array.from(formPairs).forEach(({ value, key }) => { set(json, key, value); @@ -880,77 +871,90 @@ function FormComponent(props) { } if (contentType && contentType !== \\"multipart/form-data\\") { - if ( - /* Zapier doesn't allow content-type header to be sent from browsers */ !( - sendWithJs && props.action?.includes(\\"zapier.com\\") - ) - ) { + if (!(sendWithJs && props.action?.includes(\\"zapier.com\\"))) { headers[\\"content-type\\"] = contentType; } } - const presubmitEvent = new CustomEvent(\\"presubmit\\", { detail: { body } }); + + const presubmitEvent = new CustomEvent(\\"presubmit\\", { + detail: { + body, + }, + }); + if (formRef.current) { formRef.current.dispatchEvent(presubmitEvent); + if (presubmitEvent.defaultPrevented) { return; } } + setFormState(\\"sending\\"); const formUrl = \`\${ builder.env === \\"dev\\" ? \\"http://localhost:5000\\" : \\"https://builder.io\\" }/api/v1/form-submit?apiKey=\${builder.apiKey}&to=\${btoa( props.sendSubmissionsToEmail || \\"\\" )}&name=\${encodeURIComponent(props.name || \\"\\")}\`; - fetch( - props.sendSubmissionsTo === \\"email\\" - ? formUrl - : props.action /* TODO: throw error if no action URL */, - { body, headers, method: props.method || \\"post\\" } - ).then( + fetch(props.sendSubmissionsTo === \\"email\\" ? formUrl : props.action, { + body, + headers, + method: props.method || \\"post\\", + }).then( async (res) => { let body; const contentType = res.headers.get(\\"content-type\\"); + if (contentType && contentType.indexOf(\\"application/json\\") !== -1) { body = await res.json(); } else { body = await res.text(); } + if (!res.ok && props.errorMessagePath) { - /* TODO: allow supplying an error formatter function */ let message = - get(body, props.errorMessagePath); + let message = get(body, props.errorMessagePath); + if (message) { if (typeof message !== \\"string\\") { - /* TODO: ideally convert json to yaml so it woul dbe like error: - email has been taken */ message = - JSON.stringify(message); + message = JSON.stringify(message); } + setFormErrorMessage(message); } } + setResponseData(body); setFormState(res.ok ? \\"success\\" : \\"error\\"); + if (res.ok) { const submitSuccessEvent = new CustomEvent(\\"submit:success\\", { - detail: { res, body }, + detail: { + res, + body, + }, }); + if (formRef.current) { formRef.current.dispatchEvent(submitSuccessEvent); + if (submitSuccessEvent.defaultPrevented) { return; } - /* TODO: option to turn this on/off? */ if ( - props.resetFormOnSubmit !== false - ) { + + if (props.resetFormOnSubmit !== false) { formRef.current.reset(); } } - /* TODO: client side route event first that can be preventDefaulted */ if ( - props.successUrl - ) { + + if (props.successUrl) { if (formRef.current) { const event = new CustomEvent(\\"route\\", { - detail: { url: props.successUrl }, + detail: { + url: props.successUrl, + }, }); formRef.current.dispatchEvent(event); + if (!event.defaultPrevented) { location.href = props.successUrl; } @@ -962,23 +966,28 @@ function FormComponent(props) { }, (err) => { const submitErrorEvent = new CustomEvent(\\"submit:error\\", { - detail: { error: err }, + detail: { + error: err, + }, }); + if (formRef.current) { formRef.current.dispatchEvent(submitErrorEvent); + if (submitErrorEvent.defaultPrevented) { return; } } + setResponseData(err); setFormState(\\"error\\"); } ); } } + return ( - {\\" \\"}
onSubmit(event)} {...props.attributes} > - {\\" \\"} {props.builderBlock && props.builderBlock.children ? ( {props.builderBlock?.children?.map((block, index) => ( @@ -999,7 +1007,8 @@ function FormComponent(props) { /> ))} - ) : null}{\\" \\"} + ) : null} + {submissionState() === \\"error\\" ? ( - ) : null}{\\" \\"} + ) : null} + {submissionState() === \\"sending\\" ? ( - ) : null}{\\" \\"} + ) : null} + {submissionState() === \\"error\\" && responseData ? (
-              {\\" \\"}
-              {JSON.stringify(responseData, null, 2)}{\\" \\"}
+              {JSON.stringify(responseData, null, 2)}
             
- ) : null}{\\" \\"} + ) : null} + {submissionState() === \\"success\\" ? ( - ) : null}{\\" \\"} -
{\\" \\"} + ) : null} + {\\" \\"} + \`}
); } + export default FormComponent; " `; @@ -1063,7 +1075,6 @@ function Image(props) { } function useLazyLoading() { - // TODO: Add more checks here, like testing for real web browsers return !!props.lazy && isBrowser(); } @@ -1888,6 +1899,47 @@ export default MyBasicComponent; " `; +exports[`Preact > jsx > Javascript Test > commentsWithQuotes 1`] = ` +"/** @jsx h */ +import { h, Fragment } from \\"preact\\"; +import { useState } from \\"preact/hooks\\"; + +function MyComponent(props) { + const [name, setName] = useState(() => \\"Steve\\"); + + function handleFn() {} + + function doSomething() { + console.log(\\"This is a aa\\"); + } + + return ( + +
+ { + setName(event.target.value); + }} + onClick={(event) => doSomething()} + /> + Hello + {name}! I can run in React, Vue, Solid, or Liquid! +
+ +
+ ); +} + +export default MyComponent; +" +`; + exports[`Preact > jsx > Javascript Test > componentWithContext 1`] = ` "/** @jsx h */ import { h, Fragment } from \\"preact\\"; @@ -2580,7 +2632,6 @@ export default OnUpdate; exports[`Preact > jsx > Javascript Test > renderBlock 1`] = ` "/** @jsx h */ import { h, Fragment } from \\"preact\\"; -import { useState } from \\"preact/hooks\\"; import { TARGET } from \\"../../constants/target.js\\"; import { evaluate } from \\"../../functions/evaluate.js\\"; import { extractTextStyles } from \\"../../functions/extract-text-styles.js\\"; @@ -2612,7 +2663,6 @@ function RenderBlock(props) { const ref = props.context.registeredComponents[componentName]; if (!ref) { - // TODO: Public doc page with more info about this message console.warn(\` Could not find a registered component named \\"\${componentName}\\". If you registered it, is the file that registered it imported by the file that needs to render it?\`); @@ -2671,11 +2721,6 @@ function RenderBlock(props) { componentRef: component?.()?.component, componentOptions: { ...getBlockComponentOptions(useBlock()), - - /** - * These attributes are passed to the wrapper element when there is one. If \`noWrap\` is set to true, then - * they are provided to the component itself directly. - */ ...(shouldWrap() ? {} : { @@ -2688,30 +2733,16 @@ function RenderBlock(props) { } function useChildren() { - // TO-DO: When should \`canHaveChildren\` dictate rendering? - // This is currently commented out because some Builder components (e.g. Box) do not have \`canHaveChildren: true\`, - // but still receive and need to render children. - // return state.componentInfo?.canHaveChildren ? useBlock().children : []; return useBlock().children ?? []; } function childrenWithoutParentComponent() { - /** - * When there is no \`componentRef\`, there might still be children that need to be rendered. In this case, - * we render them outside of \`componentRef\`. - * NOTE: We make sure not to render this if \`repeatItemData\` is non-null, because that means we are rendering an array of - * blocks, and the children will be repeated within those blocks. - */ const shouldRenderChildrenOutsideRef = !component?.()?.component && !repeatItemData(); return shouldRenderChildrenOutsideRef ? useChildren() : []; } function repeatItemData() { - /** - * we don't use \`useBlock()\` here because the processing done within its logic includes evaluating the block's bindings, - * which will not work if there is a repeat. - */ const { repeat, ...blockWithoutRepeat } = props.block; if (!repeat?.collection) { @@ -2775,15 +2806,12 @@ function RenderBlock(props) { if (TARGET === \\"reactNative\\") { return RenderComponentWithContext; } else if (TARGET === \\"vue3\\") { - // vue3 expects a string for the component tag return \\"RenderComponent\\"; } else { return RenderComponent; } } - const [componentInfo, setComponentInfo] = useState(() => null); - const RenderComponentTagRef = renderComponentTag(); const TagRef = tag(); @@ -4013,9 +4041,7 @@ function CustomCode(props: CustomCodeProps) { const [scriptsRun, setScriptsRun] = useState(() => []); function findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem.current && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.current.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -4090,9 +4116,7 @@ function CustomCode(props: CustomCodeProps) { const [scriptsRun, setScriptsRun] = useState(() => []); function findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem.current && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.current.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -4217,8 +4241,7 @@ function FormComponent(props: FormProps) { const el = event.currentTarget; const headers = props.customHeaders || {}; let body: any; - const formData = new FormData(el); // TODO: maybe support null - + const formData = new FormData(el); const formPairs: { key: string; value: File | boolean | number | string | FileList; @@ -4248,7 +4271,6 @@ function FormComponent(props: FormProps) { value = num; } } else if (el.type === \\"file\\") { - // TODO: one vs multiple files value = el.files; } else { value = el.value; @@ -4276,14 +4298,11 @@ function FormComponent(props: FormProps) { ) { contentType = \\"multipart/form-data\\"; } - }); // TODO: send as urlEncoded or multipart by default - // because of ease of use and reliability in browser API - // for encoding the form? + }); if (contentType !== \\"application/json\\") { body = formData; } else { - // Json const json = {}; Array.from(formPairs).forEach(({ value, key }) => { set(json, key, value); @@ -4292,77 +4311,90 @@ function FormComponent(props: FormProps) { } if (contentType && contentType !== \\"multipart/form-data\\") { - if ( - /* Zapier doesn't allow content-type header to be sent from browsers */ !( - sendWithJs && props.action?.includes(\\"zapier.com\\") - ) - ) { + if (!(sendWithJs && props.action?.includes(\\"zapier.com\\"))) { headers[\\"content-type\\"] = contentType; } } - const presubmitEvent = new CustomEvent(\\"presubmit\\", { detail: { body } }); + + const presubmitEvent = new CustomEvent(\\"presubmit\\", { + detail: { + body, + }, + }); + if (formRef.current) { formRef.current.dispatchEvent(presubmitEvent); + if (presubmitEvent.defaultPrevented) { return; } } + setFormState(\\"sending\\"); const formUrl = \`\${ builder.env === \\"dev\\" ? \\"http://localhost:5000\\" : \\"https://builder.io\\" }/api/v1/form-submit?apiKey=\${builder.apiKey}&to=\${btoa( props.sendSubmissionsToEmail || \\"\\" )}&name=\${encodeURIComponent(props.name || \\"\\")}\`; - fetch( - props.sendSubmissionsTo === \\"email\\" - ? formUrl - : props.action! /* TODO: throw error if no action URL */, - { body, headers, method: props.method || \\"post\\" } - ).then( + fetch(props.sendSubmissionsTo === \\"email\\" ? formUrl : props.action!, { + body, + headers, + method: props.method || \\"post\\", + }).then( async (res) => { let body; const contentType = res.headers.get(\\"content-type\\"); + if (contentType && contentType.indexOf(\\"application/json\\") !== -1) { body = await res.json(); } else { body = await res.text(); } + if (!res.ok && props.errorMessagePath) { - /* TODO: allow supplying an error formatter function */ let message = - get(body, props.errorMessagePath); + let message = get(body, props.errorMessagePath); + if (message) { if (typeof message !== \\"string\\") { - /* TODO: ideally convert json to yaml so it woul dbe like error: - email has been taken */ message = - JSON.stringify(message); + message = JSON.stringify(message); } + setFormErrorMessage(message); } } + setResponseData(body); setFormState(res.ok ? \\"success\\" : \\"error\\"); + if (res.ok) { const submitSuccessEvent = new CustomEvent(\\"submit:success\\", { - detail: { res, body }, + detail: { + res, + body, + }, }); + if (formRef.current) { formRef.current.dispatchEvent(submitSuccessEvent); + if (submitSuccessEvent.defaultPrevented) { return; } - /* TODO: option to turn this on/off? */ if ( - props.resetFormOnSubmit !== false - ) { + + if (props.resetFormOnSubmit !== false) { formRef.current.reset(); } } - /* TODO: client side route event first that can be preventDefaulted */ if ( - props.successUrl - ) { + + if (props.successUrl) { if (formRef.current) { const event = new CustomEvent(\\"route\\", { - detail: { url: props.successUrl }, + detail: { + url: props.successUrl, + }, }); formRef.current.dispatchEvent(event); + if (!event.defaultPrevented) { location.href = props.successUrl; } @@ -4374,23 +4406,28 @@ function FormComponent(props: FormProps) { }, (err) => { const submitErrorEvent = new CustomEvent(\\"submit:error\\", { - detail: { error: err }, + detail: { + error: err, + }, }); + if (formRef.current) { formRef.current.dispatchEvent(submitErrorEvent); + if (submitErrorEvent.defaultPrevented) { return; } } + setResponseData(err); setFormState(\\"error\\"); } ); } } + return ( - {\\" \\"}
onSubmit(event)} {...props.attributes} > - {\\" \\"} {props.builderBlock && props.builderBlock.children ? ( {props.builderBlock?.children?.map((block, index) => ( @@ -4411,7 +4447,8 @@ function FormComponent(props: FormProps) { /> ))} - ) : null}{\\" \\"} + ) : null} + {submissionState() === \\"error\\" ? ( - ) : null}{\\" \\"} + ) : null} + {submissionState() === \\"sending\\" ? ( - ) : null}{\\" \\"} + ) : null} + {submissionState() === \\"error\\" && responseData ? (
-              {\\" \\"}
-              {JSON.stringify(responseData, null, 2)}{\\" \\"}
+              {JSON.stringify(responseData, null, 2)}
             
- ) : null}{\\" \\"} + ) : null} + {submissionState() === \\"success\\" ? ( - ) : null}{\\" \\"} -
{\\" \\"} + ) : null} + {\\" \\"} + \`}
); } + export default FormComponent; " `; @@ -4494,7 +4534,6 @@ function Image(props: ImageProps) { } function useLazyLoading() { - // TODO: Add more checks here, like testing for real web browsers return !!props.lazy && isBrowser(); } @@ -5457,6 +5496,47 @@ export default MyBasicComponent; " `; +exports[`Preact > jsx > Typescript Test > commentsWithQuotes 1`] = ` +"/** @jsx h */ +import { h, Fragment } from \\"preact\\"; +import { useState } from \\"preact/hooks\\"; + +function MyComponent(props: any) { + const [name, setName] = useState(() => \\"Steve\\"); + + function handleFn() {} + + function doSomething() { + console.log(\\"This is a aa\\"); + } + + return ( + +
+ { + setName(event.target.value); + }} + onClick={(event) => doSomething()} + /> + Hello + {name}! I can run in React, Vue, Solid, or Liquid! +
+ +
+ ); +} + +export default MyComponent; +" +`; + exports[`Preact > jsx > Typescript Test > componentWithContext 1`] = ` "/** @jsx h */ import { h, Fragment } from \\"preact\\"; @@ -6239,7 +6319,6 @@ export default OnUpdate; exports[`Preact > jsx > Typescript Test > renderBlock 1`] = ` "/** @jsx h */ import { h, Fragment } from \\"preact\\"; -import { useState } from \\"preact/hooks\\"; export type RenderBlockProps = { block: BuilderBlock; @@ -6284,7 +6363,6 @@ function RenderBlock(props: RenderBlockProps) { const ref = props.context.registeredComponents[componentName]; if (!ref) { - // TODO: Public doc page with more info about this message console.warn(\` Could not find a registered component named \\"\${componentName}\\". If you registered it, is the file that registered it imported by the file that needs to render it?\`); @@ -6343,11 +6421,6 @@ function RenderBlock(props: RenderBlockProps) { componentRef: component?.()?.component, componentOptions: { ...getBlockComponentOptions(useBlock()), - - /** - * These attributes are passed to the wrapper element when there is one. If \`noWrap\` is set to true, then - * they are provided to the component itself directly. - */ ...(shouldWrap() ? {} : { @@ -6360,30 +6433,16 @@ function RenderBlock(props: RenderBlockProps) { } function useChildren() { - // TO-DO: When should \`canHaveChildren\` dictate rendering? - // This is currently commented out because some Builder components (e.g. Box) do not have \`canHaveChildren: true\`, - // but still receive and need to render children. - // return state.componentInfo?.canHaveChildren ? useBlock().children : []; return useBlock().children ?? []; } function childrenWithoutParentComponent() { - /** - * When there is no \`componentRef\`, there might still be children that need to be rendered. In this case, - * we render them outside of \`componentRef\`. - * NOTE: We make sure not to render this if \`repeatItemData\` is non-null, because that means we are rendering an array of - * blocks, and the children will be repeated within those blocks. - */ const shouldRenderChildrenOutsideRef = !component?.()?.component && !repeatItemData(); return shouldRenderChildrenOutsideRef ? useChildren() : []; } function repeatItemData() { - /** - * we don't use \`useBlock()\` here because the processing done within its logic includes evaluating the block's bindings, - * which will not work if there is a repeat. - */ const { repeat, ...blockWithoutRepeat } = props.block; if (!repeat?.collection) { @@ -6447,15 +6506,12 @@ function RenderBlock(props: RenderBlockProps) { if (TARGET === \\"reactNative\\") { return RenderComponentWithContext; } else if (TARGET === \\"vue3\\") { - // vue3 expects a string for the component tag return \\"RenderComponent\\"; } else { return RenderComponent; } } - const [componentInfo, setComponentInfo] = useState(() => null); - const RenderComponentTagRef = renderComponentTag(); const TagRef = tag(); diff --git a/packages/core/src/__tests__/__snapshots__/qwik.test.ts.snap b/packages/core/src/__tests__/__snapshots__/qwik.test.ts.snap index f41400aefa..4dfc7a8366 100644 --- a/packages/core/src/__tests__/__snapshots__/qwik.test.ts.snap +++ b/packages/core/src/__tests__/__snapshots__/qwik.test.ts.snap @@ -2393,9 +2393,7 @@ export const findAndRunScripts = function findAndRunScripts( state, elem ) { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem.value && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.value.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -2470,9 +2468,7 @@ export const findAndRunScripts = function findAndRunScripts( state, elem ) { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem.value && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.value.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -2568,8 +2564,7 @@ export const onSubmit = function onSubmit(props, state, formRef, event) { const el = event.currentTarget; const headers = props.customHeaders || {}; let body; - const formData = new FormData(el); // TODO: maybe support null - + const formData = new FormData(el); const formPairs = Array.from( event.currentTarget.querySelectorAll(\\"input,select,textarea\\") ) @@ -2596,7 +2591,6 @@ export const onSubmit = function onSubmit(props, state, formRef, event) { value = num; } } else if (el.type === \\"file\\") { - // TODO: one vs multiple files value = el.files; } else { value = el.value; @@ -2624,14 +2618,11 @@ export const onSubmit = function onSubmit(props, state, formRef, event) { ) { contentType = \\"multipart/form-data\\"; } - }); // TODO: send as urlEncoded or multipart by default - // because of ease of use and reliability in browser API - // for encoding the form? + }); if (contentType !== \\"application/json\\") { body = formData; } else { - // Json const json = {}; Array.from(formPairs).forEach(({ value, key }) => { set(json, key, value); @@ -2640,10 +2631,7 @@ export const onSubmit = function onSubmit(props, state, formRef, event) { } if (contentType && contentType !== \\"multipart/form-data\\") { - if ( - /* Zapier doesn't allow content-type header to be sent from browsers */ - !(sendWithJs && props.action?.includes(\\"zapier.com\\")) - ) { + if (!(sendWithJs && props.action?.includes(\\"zapier.com\\"))) { headers[\\"content-type\\"] = contentType; } } @@ -2668,15 +2656,11 @@ export const onSubmit = function onSubmit(props, state, formRef, event) { }/api/v1/form-submit?apiKey=\${builder.apiKey}&to=\${btoa( props.sendSubmissionsToEmail || \\"\\" )}&name=\${encodeURIComponent(props.name || \\"\\")}\`; - fetch( - props.sendSubmissionsTo === \\"email\\" ? formUrl : props.action, - /* TODO: throw error if no action URL */ - { - body, - headers, - method: props.method || \\"post\\", - } - ).then( + fetch(props.sendSubmissionsTo === \\"email\\" ? formUrl : props.action, { + body, + headers, + method: props.method || \\"post\\", + }).then( async (res) => { let body; const contentType = res.headers.get(\\"content-type\\"); @@ -2688,13 +2672,10 @@ export const onSubmit = function onSubmit(props, state, formRef, event) { } if (!res.ok && props.errorMessagePath) { - /* TODO: allow supplying an error formatter function */ let message = get(body, props.errorMessagePath); if (message) { if (typeof message !== \\"string\\") { - /* TODO: ideally convert json to yaml so it woul dbe like - error: - email has been taken */ message = JSON.stringify(message); } @@ -2719,13 +2700,11 @@ export const onSubmit = function onSubmit(props, state, formRef, event) { if (submitSuccessEvent.defaultPrevented) { return; } - /* TODO: option to turn this on/off? */ if (props.resetFormOnSubmit !== false) { formRef.value.reset(); } } - /* TODO: client side route event first that can be preventDefaulted */ if (props.successUrl) { if (formRef.value) { @@ -2862,7 +2841,6 @@ export const useLazyLoading = function useLazyLoading( state, pictureRef ) { - // TODO: Add more checks here, like testing for real web browsers return !!props.lazy && state.isBrowser(); }; export const isBrowser = function isBrowser(props, state, pictureRef) { @@ -3693,6 +3671,51 @@ export const STYLES = \` " `; +exports[`qwik > jsx > Javascript Test > commentsWithQuotes 1`] = ` +"import { + $, + Fragment, + component$, + h, + useStore, + useStylesScoped$, +} from \\"@builder.io/qwik\\"; + +export const handleFn = function handleFn(props, state) {}; +export const doSomething = function doSomething(props, state) { + console.log(\\"This is a aa\\"); +}; +export const MyComponent = component$((props) => { + useStylesScoped$(STYLES); + + const state = useStore({ name: \\"Steve\\" }); + + return ( +
+ { + state.name = event.target.value; + })} + onClick$={$((event) => state.doSomething())} + /> + Hello + {state.name}! I can run in React, Vue, Solid, or Liquid! +
+ ); +}); + +export default MyComponent; + +export const STYLES = \` +.input-MyComponent { + color: red; +} +\`; +" +`; + exports[`qwik > jsx > Javascript Test > contentState 1`] = ` "import { Fragment, @@ -4285,7 +4308,6 @@ export const RenderBlock = component$((props) => { const ref = props.context.registeredComponents[componentName]; if (!ref) { - // TODO: Public doc page with more info about this message console.warn(\` Could not find a registered component named \\"\${componentName}\\". If you registered it, is the file that registered it imported by the file that needs to render it?\`); @@ -4338,11 +4360,6 @@ export const RenderBlock = component$((props) => { componentRef: component.value?.component, componentOptions: { ...getBlockComponentOptions(useBlock.value), - - /** - * These attributes are passed to the wrapper element when there is one. If \`noWrap\` is set to true, then - * they are provided to the component itself directly. - */ ...(shouldWrap.value ? {} : { @@ -4354,28 +4371,14 @@ export const RenderBlock = component$((props) => { }; }); const useChildren = useComputed$(() => { - // TO-DO: When should \`canHaveChildren\` dictate rendering? - // This is currently commented out because some Builder components (e.g. Box) do not have \`canHaveChildren: true\`, - // but still receive and need to render children. - // return state.componentInfo?.canHaveChildren ? state.useBlock.children : []; return useBlock.value.children ?? []; }); const childrenWithoutParentComponent = useComputed$(() => { - /** - * When there is no \`componentRef\`, there might still be children that need to be rendered. In this case, - * we render them outside of \`componentRef\`. - * NOTE: We make sure not to render this if \`repeatItemData\` is non-null, because that means we are rendering an array of - * blocks, and the children will be repeated within those blocks. - */ const shouldRenderChildrenOutsideRef = !component.value?.component && !repeatItemData.value; return shouldRenderChildrenOutsideRef ? useChildren.value : []; }); const repeatItemData = useComputed$(() => { - /** - * we don't use \`state.useBlock\` here because the processing done within its logic includes evaluating the block's bindings, - * which will not work if there is a repeat. - */ const { repeat, ...blockWithoutRepeat } = props.block; if (!repeat?.collection) { @@ -4436,7 +4439,6 @@ export const RenderBlock = component$((props) => { if (TARGET === \\"reactNative\\") { return RenderComponentWithContext; } else if (TARGET === \\"vue3\\") { - // vue3 expects a string for the component tag return \\"RenderComponent\\"; } else { return RenderComponent; @@ -5621,9 +5623,7 @@ export const findAndRunScripts = function findAndRunScripts( state, elem ) { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem.value && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.value.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -5702,9 +5702,7 @@ export const findAndRunScripts = function findAndRunScripts( state, elem ) { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem.value && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.value.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -5830,8 +5828,7 @@ export const onSubmit = function onSubmit( const el = event.currentTarget; const headers = props.customHeaders || {}; let body: any; - const formData = new FormData(el); // TODO: maybe support null - + const formData = new FormData(el); const formPairs: { key: string; value: File | boolean | number | string | FileList; @@ -5861,7 +5858,6 @@ export const onSubmit = function onSubmit( value = num; } } else if (el.type === \\"file\\") { - // TODO: one vs multiple files value = el.files; } else { value = el.value; @@ -5889,14 +5885,11 @@ export const onSubmit = function onSubmit( ) { contentType = \\"multipart/form-data\\"; } - }); // TODO: send as urlEncoded or multipart by default - // because of ease of use and reliability in browser API - // for encoding the form? + }); if (contentType !== \\"application/json\\") { body = formData; } else { - // Json const json = {}; Array.from(formPairs).forEach(({ value, key }) => { set(json, key, value); @@ -5905,10 +5898,7 @@ export const onSubmit = function onSubmit( } if (contentType && contentType !== \\"multipart/form-data\\") { - if ( - /* Zapier doesn't allow content-type header to be sent from browsers */ - !(sendWithJs && props.action?.includes(\\"zapier.com\\")) - ) { + if (!(sendWithJs && props.action?.includes(\\"zapier.com\\"))) { headers[\\"content-type\\"] = contentType; } } @@ -5933,15 +5923,11 @@ export const onSubmit = function onSubmit( }/api/v1/form-submit?apiKey=\${builder.apiKey}&to=\${btoa( props.sendSubmissionsToEmail || \\"\\" )}&name=\${encodeURIComponent(props.name || \\"\\")}\`; - fetch( - props.sendSubmissionsTo === \\"email\\" ? formUrl : props.action!, - /* TODO: throw error if no action URL */ - { - body, - headers, - method: props.method || \\"post\\", - } - ).then( + fetch(props.sendSubmissionsTo === \\"email\\" ? formUrl : props.action!, { + body, + headers, + method: props.method || \\"post\\", + }).then( async (res) => { let body; const contentType = res.headers.get(\\"content-type\\"); @@ -5953,13 +5939,10 @@ export const onSubmit = function onSubmit( } if (!res.ok && props.errorMessagePath) { - /* TODO: allow supplying an error formatter function */ let message = get(body, props.errorMessagePath); if (message) { if (typeof message !== \\"string\\") { - /* TODO: ideally convert json to yaml so it woul dbe like - error: - email has been taken */ message = JSON.stringify(message); } @@ -5984,13 +5967,11 @@ export const onSubmit = function onSubmit( if (submitSuccessEvent.defaultPrevented) { return; } - /* TODO: option to turn this on/off? */ if (props.resetFormOnSubmit !== false) { formRef.value.reset(); } } - /* TODO: client side route event first that can be preventDefaulted */ if (props.successUrl) { if (formRef.value) { @@ -6145,7 +6126,6 @@ export const useLazyLoading = function useLazyLoading( state, pictureRef ) { - // TODO: Add more checks here, like testing for real web browsers return !!props.lazy && state.isBrowser(); }; export const isBrowser = function isBrowser(props, state, pictureRef) { @@ -7103,6 +7083,51 @@ export const STYLES = \` " `; +exports[`qwik > jsx > Typescript Test > commentsWithQuotes 1`] = ` +"import { + $, + Fragment, + component$, + h, + useStore, + useStylesScoped$, +} from \\"@builder.io/qwik\\"; + +export const handleFn = function handleFn(props, state) {}; +export const doSomething = function doSomething(props, state) { + console.log(\\"This is a aa\\"); +}; +export const MyComponent = component$((props: any) => { + useStylesScoped$(STYLES); + + const state = useStore({ name: \\"Steve\\" }); + + return ( +
+ { + state.name = event.target.value; + })} + onClick$={$((event) => state.doSomething())} + /> + Hello + {state.name}! I can run in React, Vue, Solid, or Liquid! +
+ ); +}); + +export default MyComponent; + +export const STYLES = \` +.input-MyComponent { + color: red; +} +\`; +" +`; + exports[`qwik > jsx > Typescript Test > contentState 1`] = ` "import { Fragment, @@ -7785,7 +7810,6 @@ export const RenderBlock = component$((props: RenderBlockProps) => { const ref = props.context.registeredComponents[componentName]; if (!ref) { - // TODO: Public doc page with more info about this message console.warn(\` Could not find a registered component named \\"\${componentName}\\". If you registered it, is the file that registered it imported by the file that needs to render it?\`); @@ -7838,11 +7862,6 @@ export const RenderBlock = component$((props: RenderBlockProps) => { componentRef: component.value?.component, componentOptions: { ...getBlockComponentOptions(useBlock.value), - - /** - * These attributes are passed to the wrapper element when there is one. If \`noWrap\` is set to true, then - * they are provided to the component itself directly. - */ ...(shouldWrap.value ? {} : { @@ -7854,28 +7873,14 @@ export const RenderBlock = component$((props: RenderBlockProps) => { }; }); const useChildren = useComputed$(() => { - // TO-DO: When should \`canHaveChildren\` dictate rendering? - // This is currently commented out because some Builder components (e.g. Box) do not have \`canHaveChildren: true\`, - // but still receive and need to render children. - // return state.componentInfo?.canHaveChildren ? state.useBlock.children : []; return useBlock.value.children ?? []; }); const childrenWithoutParentComponent = useComputed$(() => { - /** - * When there is no \`componentRef\`, there might still be children that need to be rendered. In this case, - * we render them outside of \`componentRef\`. - * NOTE: We make sure not to render this if \`repeatItemData\` is non-null, because that means we are rendering an array of - * blocks, and the children will be repeated within those blocks. - */ const shouldRenderChildrenOutsideRef = !component.value?.component && !repeatItemData.value; return shouldRenderChildrenOutsideRef ? useChildren.value : []; }); const repeatItemData = useComputed$(() => { - /** - * we don't use \`state.useBlock\` here because the processing done within its logic includes evaluating the block's bindings, - * which will not work if there is a repeat. - */ const { repeat, ...blockWithoutRepeat } = props.block; if (!repeat?.collection) { @@ -7936,7 +7941,6 @@ export const RenderBlock = component$((props: RenderBlockProps) => { if (TARGET === \\"reactNative\\") { return RenderComponentWithContext; } else if (TARGET === \\"vue3\\") { - // vue3 expects a string for the component tag return \\"RenderComponent\\"; } else { return RenderComponent; diff --git a/packages/core/src/__tests__/__snapshots__/react-native.test.ts.snap b/packages/core/src/__tests__/__snapshots__/react-native.test.ts.snap index 921230750b..469b007cf4 100644 --- a/packages/core/src/__tests__/__snapshots__/react-native.test.ts.snap +++ b/packages/core/src/__tests__/__snapshots__/react-native.test.ts.snap @@ -744,9 +744,7 @@ function CustomCode(props) { const [scriptsRun, setScriptsRun] = useState(() => []); function findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem.current && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.current.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -815,9 +813,7 @@ function CustomCode(props) { const [scriptsRun, setScriptsRun] = useState(() => []); function findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem.current && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.current.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -913,8 +909,7 @@ function FormComponent(props) { const el = event.currentTarget; const headers = props.customHeaders || {}; let body; - const formData = new FormData(el); // TODO: maybe support null - + const formData = new FormData(el); const formPairs = Array.from( event.currentTarget.querySelectorAll(\\"input,select,textarea\\") ) @@ -941,7 +936,6 @@ function FormComponent(props) { value = num; } } else if (el.type === \\"file\\") { - // TODO: one vs multiple files value = el.files; } else { value = el.value; @@ -969,14 +963,11 @@ function FormComponent(props) { ) { contentType = \\"multipart/form-data\\"; } - }); // TODO: send as urlEncoded or multipart by default - // because of ease of use and reliability in browser API - // for encoding the form? + }); if (contentType !== \\"application/json\\") { body = formData; } else { - // Json const json = {}; Array.from(formPairs).forEach(({ value, key }) => { set(json, key, value); @@ -985,77 +976,90 @@ function FormComponent(props) { } if (contentType && contentType !== \\"multipart/form-data\\") { - if ( - /* Zapier doesn't allow content-type header to be sent from browsers */ !( - sendWithJs && props.action?.includes(\\"zapier.com\\") - ) - ) { + if (!(sendWithJs && props.action?.includes(\\"zapier.com\\"))) { headers[\\"content-type\\"] = contentType; } } - const presubmitEvent = new CustomEvent(\\"presubmit\\", { detail: { body } }); + + const presubmitEvent = new CustomEvent(\\"presubmit\\", { + detail: { + body, + }, + }); + if (formRef.current) { formRef.current.dispatchEvent(presubmitEvent); + if (presubmitEvent.defaultPrevented) { return; } } + setFormState(\\"sending\\"); const formUrl = \`\${ builder.env === \\"dev\\" ? \\"http://localhost:5000\\" : \\"https://builder.io\\" }/api/v1/form-submit?apiKey=\${builder.apiKey}&to=\${btoa( props.sendSubmissionsToEmail || \\"\\" )}&name=\${encodeURIComponent(props.name || \\"\\")}\`; - fetch( - props.sendSubmissionsTo === \\"email\\" - ? formUrl - : props.action /* TODO: throw error if no action URL */, - { body, headers, method: props.method || \\"post\\" } - ).then( + fetch(props.sendSubmissionsTo === \\"email\\" ? formUrl : props.action, { + body, + headers, + method: props.method || \\"post\\", + }).then( async (res) => { let body; const contentType = res.headers.get(\\"content-type\\"); + if (contentType && contentType.indexOf(\\"application/json\\") !== -1) { body = await res.json(); } else { body = await res.text(); } + if (!res.ok && props.errorMessagePath) { - /* TODO: allow supplying an error formatter function */ let message = - get(body, props.errorMessagePath); + let message = get(body, props.errorMessagePath); + if (message) { if (typeof message !== \\"string\\") { - /* TODO: ideally convert json to yaml so it woul dbe like error: - email has been taken */ message = - JSON.stringify(message); + message = JSON.stringify(message); } + setFormErrorMessage(message); } } + setResponseData(body); setFormState(res.ok ? \\"success\\" : \\"error\\"); + if (res.ok) { const submitSuccessEvent = new CustomEvent(\\"submit:success\\", { - detail: { res, body }, + detail: { + res, + body, + }, }); + if (formRef.current) { formRef.current.dispatchEvent(submitSuccessEvent); + if (submitSuccessEvent.defaultPrevented) { return; } - /* TODO: option to turn this on/off? */ if ( - props.resetFormOnSubmit !== false - ) { + + if (props.resetFormOnSubmit !== false) { formRef.current.reset(); } } - /* TODO: client side route event first that can be preventDefaulted */ if ( - props.successUrl - ) { + + if (props.successUrl) { if (formRef.current) { const event = new CustomEvent(\\"route\\", { - detail: { url: props.successUrl }, + detail: { + url: props.successUrl, + }, }); formRef.current.dispatchEvent(event); + if (!event.defaultPrevented) { location.href = props.successUrl; } @@ -1067,20 +1071,26 @@ function FormComponent(props) { }, (err) => { const submitErrorEvent = new CustomEvent(\\"submit:error\\", { - detail: { error: err }, + detail: { + error: err, + }, }); + if (formRef.current) { formRef.current.dispatchEvent(submitErrorEvent); + if (submitErrorEvent.defaultPrevented) { return; } } + setResponseData(err); setFormState(\\"error\\"); } ); } } + return ( onSubmit(event)} {...props.attributes} > - {\\" \\"} {props.builderBlock && props.builderBlock.children ? ( <> {props.builderBlock?.children?.map((block, index) => ( ))} - ) : null}{\\" \\"} + ) : null} + {submissionState() === \\"error\\" ? ( <> - ) : null}{\\" \\"} + ) : null} + {submissionState() === \\"sending\\" ? ( <> - ) : null}{\\" \\"} + ) : null} + {submissionState() === \\"error\\" && responseData ? ( <> - {\\" \\"} - {JSON.stringify(responseData, null, 2)}{\\" \\"} + {JSON.stringify(responseData, null, 2)} - ) : null}{\\" \\"} + ) : null} + {submissionState() === \\"success\\" ? ( <> - ) : null}{\\" \\"} + ) : null} ); } + const styles = StyleSheet.create({ view1: { padding: 10, color: \\"red\\", textAlign: \\"center\\" }, }); + export default FormComponent; " `; @@ -1161,7 +1175,6 @@ function Image(props) { } function useLazyLoading() { - // TODO: Add more checks here, like testing for real web browsers return !!props.lazy && isBrowser(); } @@ -2131,6 +2144,50 @@ export default MyBasicComponent; " `; +exports[`React Native > jsx > Javascript Test > commentsWithQuotes 1`] = ` +"import * as React from \\"react\\"; +import { + FlatList, + ScrollView, + View, + StyleSheet, + Image, + Text, +} from \\"react-native\\"; +import { useState } from \\"react\\"; + +function MyComponent(props) { + const [name, setName] = useState(() => \\"Steve\\"); + + function handleFn() {} + + function doSomething() { + console.log(\\"This is a aa\\"); + } + + return ( + + { + setName(event.target.value); + }} + onClick={(event) => doSomething()} + style={styles.view1} + /> + Hello + {name} + ! I can run in React, Vue, Solid, or Liquid! + + ); +} + +const styles = StyleSheet.create({ view1: { color: \\"red\\" } }); + +export default MyComponent; +" +`; + exports[`React Native > jsx > Javascript Test > componentWithContext 1`] = ` "import * as React from \\"react\\"; import { @@ -3016,7 +3073,6 @@ import { Image, Text, } from \\"react-native\\"; -import { useState } from \\"react\\"; import { TARGET } from \\"../../constants/target.js\\"; import { evaluate } from \\"../../functions/evaluate.js\\"; import { extractTextStyles } from \\"../../functions/extract-text-styles.js\\"; @@ -3048,7 +3104,6 @@ function RenderBlock(props) { const ref = props.context.registeredComponents[componentName]; if (!ref) { - // TODO: Public doc page with more info about this message console.warn(\` Could not find a registered component named \\"\${componentName}\\". If you registered it, is the file that registered it imported by the file that needs to render it?\`); @@ -3107,11 +3162,6 @@ function RenderBlock(props) { componentRef: component?.()?.component, componentOptions: { ...getBlockComponentOptions(useBlock()), - - /** - * These attributes are passed to the wrapper element when there is one. If \`noWrap\` is set to true, then - * they are provided to the component itself directly. - */ ...(shouldWrap() ? {} : { @@ -3124,30 +3174,16 @@ function RenderBlock(props) { } function useChildren() { - // TO-DO: When should \`canHaveChildren\` dictate rendering? - // This is currently commented out because some Builder components (e.g. Box) do not have \`canHaveChildren: true\`, - // but still receive and need to render children. - // return state.componentInfo?.canHaveChildren ? useBlock().children : []; return useBlock().children ?? []; } function childrenWithoutParentComponent() { - /** - * When there is no \`componentRef\`, there might still be children that need to be rendered. In this case, - * we render them outside of \`componentRef\`. - * NOTE: We make sure not to render this if \`repeatItemData\` is non-null, because that means we are rendering an array of - * blocks, and the children will be repeated within those blocks. - */ const shouldRenderChildrenOutsideRef = !component?.()?.component && !repeatItemData(); return shouldRenderChildrenOutsideRef ? useChildren() : []; } function repeatItemData() { - /** - * we don't use \`useBlock()\` here because the processing done within its logic includes evaluating the block's bindings, - * which will not work if there is a repeat. - */ const { repeat, ...blockWithoutRepeat } = props.block; if (!repeat?.collection) { @@ -3211,15 +3247,12 @@ function RenderBlock(props) { if (TARGET === \\"reactNative\\") { return RenderComponentWithContext; } else if (TARGET === \\"vue3\\") { - // vue3 expects a string for the component tag return \\"RenderComponent\\"; } else { return RenderComponent; } } - const [componentInfo, setComponentInfo] = useState(() => null); - const RenderComponentTagRef = renderComponentTag(); const TagRef = tag(); @@ -4682,9 +4715,7 @@ function CustomCode(props: CustomCodeProps) { const [scriptsRun, setScriptsRun] = useState(() => []); function findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem.current && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.current.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -4758,9 +4789,7 @@ function CustomCode(props: CustomCodeProps) { const [scriptsRun, setScriptsRun] = useState(() => []); function findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem.current && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.current.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -4884,8 +4913,7 @@ function FormComponent(props: FormProps) { const el = event.currentTarget; const headers = props.customHeaders || {}; let body: any; - const formData = new FormData(el); // TODO: maybe support null - + const formData = new FormData(el); const formPairs: { key: string; value: File | boolean | number | string | FileList; @@ -4915,7 +4943,6 @@ function FormComponent(props: FormProps) { value = num; } } else if (el.type === \\"file\\") { - // TODO: one vs multiple files value = el.files; } else { value = el.value; @@ -4943,14 +4970,11 @@ function FormComponent(props: FormProps) { ) { contentType = \\"multipart/form-data\\"; } - }); // TODO: send as urlEncoded or multipart by default - // because of ease of use and reliability in browser API - // for encoding the form? + }); if (contentType !== \\"application/json\\") { body = formData; } else { - // Json const json = {}; Array.from(formPairs).forEach(({ value, key }) => { set(json, key, value); @@ -4959,77 +4983,90 @@ function FormComponent(props: FormProps) { } if (contentType && contentType !== \\"multipart/form-data\\") { - if ( - /* Zapier doesn't allow content-type header to be sent from browsers */ !( - sendWithJs && props.action?.includes(\\"zapier.com\\") - ) - ) { + if (!(sendWithJs && props.action?.includes(\\"zapier.com\\"))) { headers[\\"content-type\\"] = contentType; } } - const presubmitEvent = new CustomEvent(\\"presubmit\\", { detail: { body } }); + + const presubmitEvent = new CustomEvent(\\"presubmit\\", { + detail: { + body, + }, + }); + if (formRef.current) { formRef.current.dispatchEvent(presubmitEvent); + if (presubmitEvent.defaultPrevented) { return; } } + setFormState(\\"sending\\"); const formUrl = \`\${ builder.env === \\"dev\\" ? \\"http://localhost:5000\\" : \\"https://builder.io\\" }/api/v1/form-submit?apiKey=\${builder.apiKey}&to=\${btoa( props.sendSubmissionsToEmail || \\"\\" )}&name=\${encodeURIComponent(props.name || \\"\\")}\`; - fetch( - props.sendSubmissionsTo === \\"email\\" - ? formUrl - : props.action! /* TODO: throw error if no action URL */, - { body, headers, method: props.method || \\"post\\" } - ).then( + fetch(props.sendSubmissionsTo === \\"email\\" ? formUrl : props.action!, { + body, + headers, + method: props.method || \\"post\\", + }).then( async (res) => { let body; const contentType = res.headers.get(\\"content-type\\"); + if (contentType && contentType.indexOf(\\"application/json\\") !== -1) { body = await res.json(); } else { body = await res.text(); } + if (!res.ok && props.errorMessagePath) { - /* TODO: allow supplying an error formatter function */ let message = - get(body, props.errorMessagePath); + let message = get(body, props.errorMessagePath); + if (message) { if (typeof message !== \\"string\\") { - /* TODO: ideally convert json to yaml so it woul dbe like error: - email has been taken */ message = - JSON.stringify(message); + message = JSON.stringify(message); } + setFormErrorMessage(message); } } + setResponseData(body); setFormState(res.ok ? \\"success\\" : \\"error\\"); + if (res.ok) { const submitSuccessEvent = new CustomEvent(\\"submit:success\\", { - detail: { res, body }, + detail: { + res, + body, + }, }); + if (formRef.current) { formRef.current.dispatchEvent(submitSuccessEvent); + if (submitSuccessEvent.defaultPrevented) { return; } - /* TODO: option to turn this on/off? */ if ( - props.resetFormOnSubmit !== false - ) { + + if (props.resetFormOnSubmit !== false) { formRef.current.reset(); } } - /* TODO: client side route event first that can be preventDefaulted */ if ( - props.successUrl - ) { + + if (props.successUrl) { if (formRef.current) { const event = new CustomEvent(\\"route\\", { - detail: { url: props.successUrl }, + detail: { + url: props.successUrl, + }, }); formRef.current.dispatchEvent(event); + if (!event.defaultPrevented) { location.href = props.successUrl; } @@ -5041,20 +5078,26 @@ function FormComponent(props: FormProps) { }, (err) => { const submitErrorEvent = new CustomEvent(\\"submit:error\\", { - detail: { error: err }, + detail: { + error: err, + }, }); + if (formRef.current) { formRef.current.dispatchEvent(submitErrorEvent); + if (submitErrorEvent.defaultPrevented) { return; } } + setResponseData(err); setFormState(\\"error\\"); } ); } } + return ( onSubmit(event)} {...props.attributes} > - {\\" \\"} {props.builderBlock && props.builderBlock.children ? ( <> {props.builderBlock?.children?.map((block, index) => ( ))} - ) : null}{\\" \\"} + ) : null} + {submissionState() === \\"error\\" ? ( <> - ) : null}{\\" \\"} + ) : null} + {submissionState() === \\"sending\\" ? ( <> - ) : null}{\\" \\"} + ) : null} + {submissionState() === \\"error\\" && responseData ? ( <> - {\\" \\"} - {JSON.stringify(responseData, null, 2)}{\\" \\"} + {JSON.stringify(responseData, null, 2)} - ) : null}{\\" \\"} + ) : null} + {submissionState() === \\"success\\" ? ( <> - ) : null}{\\" \\"} + ) : null} ); } + const styles = StyleSheet.create({ view1: { padding: 10, color: \\"red\\", textAlign: \\"center\\" }, }); + export default FormComponent; " `; @@ -5154,7 +5201,6 @@ function Image(props: ImageProps) { } function useLazyLoading() { - // TODO: Add more checks here, like testing for real web browsers return !!props.lazy && isBrowser(); } @@ -6262,6 +6308,50 @@ export default MyBasicComponent; " `; +exports[`React Native > jsx > Typescript Test > commentsWithQuotes 1`] = ` +"import * as React from \\"react\\"; +import { + FlatList, + ScrollView, + View, + StyleSheet, + Image, + Text, +} from \\"react-native\\"; +import { useState } from \\"react\\"; + +function MyComponent(props: any) { + const [name, setName] = useState(() => \\"Steve\\"); + + function handleFn() {} + + function doSomething() { + console.log(\\"This is a aa\\"); + } + + return ( + + { + setName(event.target.value); + }} + onClick={(event) => doSomething()} + style={styles.view1} + /> + Hello + {name} + ! I can run in React, Vue, Solid, or Liquid! + + ); +} + +const styles = StyleSheet.create({ view1: { color: \\"red\\" } }); + +export default MyComponent; +" +`; + exports[`React Native > jsx > Typescript Test > componentWithContext 1`] = ` "import * as React from \\"react\\"; import { @@ -7237,7 +7327,6 @@ import { Image, Text, } from \\"react-native\\"; -import { useState } from \\"react\\"; export type RenderBlockProps = { block: BuilderBlock; @@ -7282,7 +7371,6 @@ function RenderBlock(props: RenderBlockProps) { const ref = props.context.registeredComponents[componentName]; if (!ref) { - // TODO: Public doc page with more info about this message console.warn(\` Could not find a registered component named \\"\${componentName}\\". If you registered it, is the file that registered it imported by the file that needs to render it?\`); @@ -7341,11 +7429,6 @@ function RenderBlock(props: RenderBlockProps) { componentRef: component?.()?.component, componentOptions: { ...getBlockComponentOptions(useBlock()), - - /** - * These attributes are passed to the wrapper element when there is one. If \`noWrap\` is set to true, then - * they are provided to the component itself directly. - */ ...(shouldWrap() ? {} : { @@ -7358,30 +7441,16 @@ function RenderBlock(props: RenderBlockProps) { } function useChildren() { - // TO-DO: When should \`canHaveChildren\` dictate rendering? - // This is currently commented out because some Builder components (e.g. Box) do not have \`canHaveChildren: true\`, - // but still receive and need to render children. - // return state.componentInfo?.canHaveChildren ? useBlock().children : []; return useBlock().children ?? []; } function childrenWithoutParentComponent() { - /** - * When there is no \`componentRef\`, there might still be children that need to be rendered. In this case, - * we render them outside of \`componentRef\`. - * NOTE: We make sure not to render this if \`repeatItemData\` is non-null, because that means we are rendering an array of - * blocks, and the children will be repeated within those blocks. - */ const shouldRenderChildrenOutsideRef = !component?.()?.component && !repeatItemData(); return shouldRenderChildrenOutsideRef ? useChildren() : []; } function repeatItemData() { - /** - * we don't use \`useBlock()\` here because the processing done within its logic includes evaluating the block's bindings, - * which will not work if there is a repeat. - */ const { repeat, ...blockWithoutRepeat } = props.block; if (!repeat?.collection) { @@ -7445,15 +7514,12 @@ function RenderBlock(props: RenderBlockProps) { if (TARGET === \\"reactNative\\") { return RenderComponentWithContext; } else if (TARGET === \\"vue3\\") { - // vue3 expects a string for the component tag return \\"RenderComponent\\"; } else { return RenderComponent; } } - const [componentInfo, setComponentInfo] = useState(() => null); - const RenderComponentTagRef = renderComponentTag(); const TagRef = tag(); diff --git a/packages/core/src/__tests__/__snapshots__/react.test.ts.snap b/packages/core/src/__tests__/__snapshots__/react.test.ts.snap index b5cb3227e2..abd6ef7ed6 100644 --- a/packages/core/src/__tests__/__snapshots__/react.test.ts.snap +++ b/packages/core/src/__tests__/__snapshots__/react.test.ts.snap @@ -632,9 +632,7 @@ function CustomCode(props) { const [scriptsRun, setScriptsRun] = useState(() => []); function findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem.current && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.current.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -704,9 +702,7 @@ function CustomCode(props) { const [scriptsRun, setScriptsRun] = useState(() => []); function findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem.current && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.current.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -803,8 +799,7 @@ function FormComponent(props) { const el = event.currentTarget; const headers = props.customHeaders || {}; let body; - const formData = new FormData(el); // TODO: maybe support null - + const formData = new FormData(el); const formPairs = Array.from( event.currentTarget.querySelectorAll(\\"input,select,textarea\\") ) @@ -831,7 +826,6 @@ function FormComponent(props) { value = num; } } else if (el.type === \\"file\\") { - // TODO: one vs multiple files value = el.files; } else { value = el.value; @@ -859,14 +853,11 @@ function FormComponent(props) { ) { contentType = \\"multipart/form-data\\"; } - }); // TODO: send as urlEncoded or multipart by default - // because of ease of use and reliability in browser API - // for encoding the form? + }); if (contentType !== \\"application/json\\") { body = formData; } else { - // Json const json = {}; Array.from(formPairs).forEach(({ value, key }) => { set(json, key, value); @@ -875,77 +866,90 @@ function FormComponent(props) { } if (contentType && contentType !== \\"multipart/form-data\\") { - if ( - /* Zapier doesn't allow content-type header to be sent from browsers */ !( - sendWithJs && props.action?.includes(\\"zapier.com\\") - ) - ) { + if (!(sendWithJs && props.action?.includes(\\"zapier.com\\"))) { headers[\\"content-type\\"] = contentType; } } - const presubmitEvent = new CustomEvent(\\"presubmit\\", { detail: { body } }); + + const presubmitEvent = new CustomEvent(\\"presubmit\\", { + detail: { + body, + }, + }); + if (formRef.current) { formRef.current.dispatchEvent(presubmitEvent); + if (presubmitEvent.defaultPrevented) { return; } } + setFormState(\\"sending\\"); const formUrl = \`\${ builder.env === \\"dev\\" ? \\"http://localhost:5000\\" : \\"https://builder.io\\" }/api/v1/form-submit?apiKey=\${builder.apiKey}&to=\${btoa( props.sendSubmissionsToEmail || \\"\\" )}&name=\${encodeURIComponent(props.name || \\"\\")}\`; - fetch( - props.sendSubmissionsTo === \\"email\\" - ? formUrl - : props.action /* TODO: throw error if no action URL */, - { body, headers, method: props.method || \\"post\\" } - ).then( + fetch(props.sendSubmissionsTo === \\"email\\" ? formUrl : props.action, { + body, + headers, + method: props.method || \\"post\\", + }).then( async (res) => { let body; const contentType = res.headers.get(\\"content-type\\"); + if (contentType && contentType.indexOf(\\"application/json\\") !== -1) { body = await res.json(); } else { body = await res.text(); } + if (!res.ok && props.errorMessagePath) { - /* TODO: allow supplying an error formatter function */ let message = - get(body, props.errorMessagePath); + let message = get(body, props.errorMessagePath); + if (message) { if (typeof message !== \\"string\\") { - /* TODO: ideally convert json to yaml so it woul dbe like error: - email has been taken */ message = - JSON.stringify(message); + message = JSON.stringify(message); } + setFormErrorMessage(message); } } + setResponseData(body); setFormState(res.ok ? \\"success\\" : \\"error\\"); + if (res.ok) { const submitSuccessEvent = new CustomEvent(\\"submit:success\\", { - detail: { res, body }, + detail: { + res, + body, + }, }); + if (formRef.current) { formRef.current.dispatchEvent(submitSuccessEvent); + if (submitSuccessEvent.defaultPrevented) { return; } - /* TODO: option to turn this on/off? */ if ( - props.resetFormOnSubmit !== false - ) { + + if (props.resetFormOnSubmit !== false) { formRef.current.reset(); } } - /* TODO: client side route event first that can be preventDefaulted */ if ( - props.successUrl - ) { + + if (props.successUrl) { if (formRef.current) { const event = new CustomEvent(\\"route\\", { - detail: { url: props.successUrl }, + detail: { + url: props.successUrl, + }, }); formRef.current.dispatchEvent(event); + if (!event.defaultPrevented) { location.href = props.successUrl; } @@ -957,23 +961,28 @@ function FormComponent(props) { }, (err) => { const submitErrorEvent = new CustomEvent(\\"submit:error\\", { - detail: { error: err }, + detail: { + error: err, + }, }); + if (formRef.current) { formRef.current.dispatchEvent(submitErrorEvent); + if (submitErrorEvent.defaultPrevented) { return; } } + setResponseData(err); setFormState(\\"error\\"); } ); } } + return ( <> - {\\" \\"}
onSubmit(event)} {...props.attributes} > - {\\" \\"} {props.builderBlock && props.builderBlock.children ? ( <> {props.builderBlock?.children?.map((block, index) => ( @@ -994,7 +1002,8 @@ function FormComponent(props) { /> ))} - ) : null}{\\" \\"} + ) : null} + {submissionState() === \\"error\\" ? ( <> - ) : null}{\\" \\"} + ) : null} + {submissionState() === \\"sending\\" ? ( <> - ) : null}{\\" \\"} + ) : null} + {submissionState() === \\"error\\" && responseData ? ( <>
-              {\\" \\"}
-              {JSON.stringify(responseData, null, 2)}{\\" \\"}
+              {JSON.stringify(responseData, null, 2)}
             
- ) : null}{\\" \\"} + ) : null} + {submissionState() === \\"success\\" ? ( <> - ) : null}{\\" \\"} - {\\" \\"} + ) : null} + {\\" \\"} + \`} ); } + export default FormComponent; " `; @@ -1058,7 +1070,6 @@ function Image(props) { } function useLazyLoading() { - // TODO: Add more checks here, like testing for real web browsers return !!props.lazy && isBrowser(); } @@ -1872,6 +1883,47 @@ export default MyBasicComponent; " `; +exports[`React > jsx > Javascript Test > commentsWithQuotes 1`] = ` +"\\"use client\\"; +import * as React from \\"react\\"; +import { useState } from \\"react\\"; + +function MyComponent(props) { + const [name, setName] = useState(() => \\"Steve\\"); + + function handleFn() {} + + function doSomething() { + console.log(\\"This is a aa\\"); + } + + return ( + <> +
+ { + setName(event.target.value); + }} + onClick={(event) => doSomething()} + /> + Hello + {name}! I can run in React, Vue, Solid, or Liquid! +
+ + + ); +} + +export default MyComponent; +" +`; + exports[`React > jsx > Javascript Test > componentWithContext 1`] = ` "\\"use client\\"; import * as React from \\"react\\"; @@ -2554,9 +2606,7 @@ export default OnUpdate; `; exports[`React > jsx > Javascript Test > renderBlock 1`] = ` -"\\"use client\\"; -import * as React from \\"react\\"; -import { useState } from \\"react\\"; +"import * as React from \\"react\\"; import { TARGET } from \\"../../constants/target.js\\"; import { evaluate } from \\"../../functions/evaluate.js\\"; import { extractTextStyles } from \\"../../functions/extract-text-styles.js\\"; @@ -2588,7 +2638,6 @@ function RenderBlock(props) { const ref = props.context.registeredComponents[componentName]; if (!ref) { - // TODO: Public doc page with more info about this message console.warn(\` Could not find a registered component named \\"\${componentName}\\". If you registered it, is the file that registered it imported by the file that needs to render it?\`); @@ -2647,11 +2696,6 @@ function RenderBlock(props) { componentRef: component?.()?.component, componentOptions: { ...getBlockComponentOptions(useBlock()), - - /** - * These attributes are passed to the wrapper element when there is one. If \`noWrap\` is set to true, then - * they are provided to the component itself directly. - */ ...(shouldWrap() ? {} : { @@ -2664,30 +2708,16 @@ function RenderBlock(props) { } function useChildren() { - // TO-DO: When should \`canHaveChildren\` dictate rendering? - // This is currently commented out because some Builder components (e.g. Box) do not have \`canHaveChildren: true\`, - // but still receive and need to render children. - // return state.componentInfo?.canHaveChildren ? useBlock().children : []; return useBlock().children ?? []; } function childrenWithoutParentComponent() { - /** - * When there is no \`componentRef\`, there might still be children that need to be rendered. In this case, - * we render them outside of \`componentRef\`. - * NOTE: We make sure not to render this if \`repeatItemData\` is non-null, because that means we are rendering an array of - * blocks, and the children will be repeated within those blocks. - */ const shouldRenderChildrenOutsideRef = !component?.()?.component && !repeatItemData(); return shouldRenderChildrenOutsideRef ? useChildren() : []; } function repeatItemData() { - /** - * we don't use \`useBlock()\` here because the processing done within its logic includes evaluating the block's bindings, - * which will not work if there is a repeat. - */ const { repeat, ...blockWithoutRepeat } = props.block; if (!repeat?.collection) { @@ -2751,15 +2781,12 @@ function RenderBlock(props) { if (TARGET === \\"reactNative\\") { return RenderComponentWithContext; } else if (TARGET === \\"vue3\\") { - // vue3 expects a string for the component tag return \\"RenderComponent\\"; } else { return RenderComponent; } } - const [componentInfo, setComponentInfo] = useState(() => null); - const RenderComponentTagRef = renderComponentTag(); const TagRef = tag(); @@ -3962,9 +3989,7 @@ function CustomCode(props: CustomCodeProps) { const [scriptsRun, setScriptsRun] = useState(() => []); function findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem.current && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.current.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -4039,9 +4064,7 @@ function CustomCode(props: CustomCodeProps) { const [scriptsRun, setScriptsRun] = useState(() => []); function findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem.current && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.current.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -4166,8 +4189,7 @@ function FormComponent(props: FormProps) { const el = event.currentTarget; const headers = props.customHeaders || {}; let body: any; - const formData = new FormData(el); // TODO: maybe support null - + const formData = new FormData(el); const formPairs: { key: string; value: File | boolean | number | string | FileList; @@ -4197,7 +4219,6 @@ function FormComponent(props: FormProps) { value = num; } } else if (el.type === \\"file\\") { - // TODO: one vs multiple files value = el.files; } else { value = el.value; @@ -4225,14 +4246,11 @@ function FormComponent(props: FormProps) { ) { contentType = \\"multipart/form-data\\"; } - }); // TODO: send as urlEncoded or multipart by default - // because of ease of use and reliability in browser API - // for encoding the form? + }); if (contentType !== \\"application/json\\") { body = formData; } else { - // Json const json = {}; Array.from(formPairs).forEach(({ value, key }) => { set(json, key, value); @@ -4241,77 +4259,90 @@ function FormComponent(props: FormProps) { } if (contentType && contentType !== \\"multipart/form-data\\") { - if ( - /* Zapier doesn't allow content-type header to be sent from browsers */ !( - sendWithJs && props.action?.includes(\\"zapier.com\\") - ) - ) { + if (!(sendWithJs && props.action?.includes(\\"zapier.com\\"))) { headers[\\"content-type\\"] = contentType; } } - const presubmitEvent = new CustomEvent(\\"presubmit\\", { detail: { body } }); + + const presubmitEvent = new CustomEvent(\\"presubmit\\", { + detail: { + body, + }, + }); + if (formRef.current) { formRef.current.dispatchEvent(presubmitEvent); + if (presubmitEvent.defaultPrevented) { return; } } + setFormState(\\"sending\\"); const formUrl = \`\${ builder.env === \\"dev\\" ? \\"http://localhost:5000\\" : \\"https://builder.io\\" }/api/v1/form-submit?apiKey=\${builder.apiKey}&to=\${btoa( props.sendSubmissionsToEmail || \\"\\" )}&name=\${encodeURIComponent(props.name || \\"\\")}\`; - fetch( - props.sendSubmissionsTo === \\"email\\" - ? formUrl - : props.action! /* TODO: throw error if no action URL */, - { body, headers, method: props.method || \\"post\\" } - ).then( + fetch(props.sendSubmissionsTo === \\"email\\" ? formUrl : props.action!, { + body, + headers, + method: props.method || \\"post\\", + }).then( async (res) => { let body; const contentType = res.headers.get(\\"content-type\\"); + if (contentType && contentType.indexOf(\\"application/json\\") !== -1) { body = await res.json(); } else { body = await res.text(); } + if (!res.ok && props.errorMessagePath) { - /* TODO: allow supplying an error formatter function */ let message = - get(body, props.errorMessagePath); + let message = get(body, props.errorMessagePath); + if (message) { if (typeof message !== \\"string\\") { - /* TODO: ideally convert json to yaml so it woul dbe like error: - email has been taken */ message = - JSON.stringify(message); + message = JSON.stringify(message); } + setFormErrorMessage(message); } } + setResponseData(body); setFormState(res.ok ? \\"success\\" : \\"error\\"); + if (res.ok) { const submitSuccessEvent = new CustomEvent(\\"submit:success\\", { - detail: { res, body }, + detail: { + res, + body, + }, }); + if (formRef.current) { formRef.current.dispatchEvent(submitSuccessEvent); + if (submitSuccessEvent.defaultPrevented) { return; } - /* TODO: option to turn this on/off? */ if ( - props.resetFormOnSubmit !== false - ) { + + if (props.resetFormOnSubmit !== false) { formRef.current.reset(); } } - /* TODO: client side route event first that can be preventDefaulted */ if ( - props.successUrl - ) { + + if (props.successUrl) { if (formRef.current) { const event = new CustomEvent(\\"route\\", { - detail: { url: props.successUrl }, + detail: { + url: props.successUrl, + }, }); formRef.current.dispatchEvent(event); + if (!event.defaultPrevented) { location.href = props.successUrl; } @@ -4323,23 +4354,28 @@ function FormComponent(props: FormProps) { }, (err) => { const submitErrorEvent = new CustomEvent(\\"submit:error\\", { - detail: { error: err }, + detail: { + error: err, + }, }); + if (formRef.current) { formRef.current.dispatchEvent(submitErrorEvent); + if (submitErrorEvent.defaultPrevented) { return; } } + setResponseData(err); setFormState(\\"error\\"); } ); } } + return ( <> - {\\" \\"}
onSubmit(event)} {...props.attributes} > - {\\" \\"} {props.builderBlock && props.builderBlock.children ? ( <> {props.builderBlock?.children?.map((block, index) => ( @@ -4360,7 +4395,8 @@ function FormComponent(props: FormProps) { /> ))} - ) : null}{\\" \\"} + ) : null} + {submissionState() === \\"error\\" ? ( <> - ) : null}{\\" \\"} + ) : null} + {submissionState() === \\"sending\\" ? ( <> - ) : null}{\\" \\"} + ) : null} + {submissionState() === \\"error\\" && responseData ? ( <>
-              {\\" \\"}
-              {JSON.stringify(responseData, null, 2)}{\\" \\"}
+              {JSON.stringify(responseData, null, 2)}
             
- ) : null}{\\" \\"} + ) : null} + {submissionState() === \\"success\\" ? ( <> - ) : null}{\\" \\"} - {\\" \\"} + ) : null} + {\\" \\"} + \`} ); } + export default FormComponent; " `; @@ -4443,7 +4482,6 @@ function Image(props: ImageProps) { } function useLazyLoading() { - // TODO: Add more checks here, like testing for real web browsers return !!props.lazy && isBrowser(); } @@ -5395,6 +5433,47 @@ export default MyBasicComponent; " `; +exports[`React > jsx > Typescript Test > commentsWithQuotes 1`] = ` +"\\"use client\\"; +import * as React from \\"react\\"; +import { useState } from \\"react\\"; + +function MyComponent(props: any) { + const [name, setName] = useState(() => \\"Steve\\"); + + function handleFn() {} + + function doSomething() { + console.log(\\"This is a aa\\"); + } + + return ( + <> +
+ { + setName(event.target.value); + }} + onClick={(event) => doSomething()} + /> + Hello + {name}! I can run in React, Vue, Solid, or Liquid! +
+ + + ); +} + +export default MyComponent; +" +`; + exports[`React > jsx > Typescript Test > componentWithContext 1`] = ` "\\"use client\\"; import * as React from \\"react\\"; @@ -6167,9 +6246,7 @@ export default OnUpdate; `; exports[`React > jsx > Typescript Test > renderBlock 1`] = ` -"\\"use client\\"; -import * as React from \\"react\\"; -import { useState } from \\"react\\"; +"import * as React from \\"react\\"; export type RenderBlockProps = { block: BuilderBlock; @@ -6214,7 +6291,6 @@ function RenderBlock(props: RenderBlockProps) { const ref = props.context.registeredComponents[componentName]; if (!ref) { - // TODO: Public doc page with more info about this message console.warn(\` Could not find a registered component named \\"\${componentName}\\". If you registered it, is the file that registered it imported by the file that needs to render it?\`); @@ -6273,11 +6349,6 @@ function RenderBlock(props: RenderBlockProps) { componentRef: component?.()?.component, componentOptions: { ...getBlockComponentOptions(useBlock()), - - /** - * These attributes are passed to the wrapper element when there is one. If \`noWrap\` is set to true, then - * they are provided to the component itself directly. - */ ...(shouldWrap() ? {} : { @@ -6290,30 +6361,16 @@ function RenderBlock(props: RenderBlockProps) { } function useChildren() { - // TO-DO: When should \`canHaveChildren\` dictate rendering? - // This is currently commented out because some Builder components (e.g. Box) do not have \`canHaveChildren: true\`, - // but still receive and need to render children. - // return state.componentInfo?.canHaveChildren ? useBlock().children : []; return useBlock().children ?? []; } function childrenWithoutParentComponent() { - /** - * When there is no \`componentRef\`, there might still be children that need to be rendered. In this case, - * we render them outside of \`componentRef\`. - * NOTE: We make sure not to render this if \`repeatItemData\` is non-null, because that means we are rendering an array of - * blocks, and the children will be repeated within those blocks. - */ const shouldRenderChildrenOutsideRef = !component?.()?.component && !repeatItemData(); return shouldRenderChildrenOutsideRef ? useChildren() : []; } function repeatItemData() { - /** - * we don't use \`useBlock()\` here because the processing done within its logic includes evaluating the block's bindings, - * which will not work if there is a repeat. - */ const { repeat, ...blockWithoutRepeat } = props.block; if (!repeat?.collection) { @@ -6377,15 +6434,12 @@ function RenderBlock(props: RenderBlockProps) { if (TARGET === \\"reactNative\\") { return RenderComponentWithContext; } else if (TARGET === \\"vue3\\") { - // vue3 expects a string for the component tag return \\"RenderComponent\\"; } else { return RenderComponent; } } - const [componentInfo, setComponentInfo] = useState(() => null); - const RenderComponentTagRef = renderComponentTag(); const TagRef = tag(); diff --git a/packages/core/src/__tests__/__snapshots__/rsc.test.ts.snap b/packages/core/src/__tests__/__snapshots__/rsc.test.ts.snap index 2c95aa1fae..2459844405 100644 --- a/packages/core/src/__tests__/__snapshots__/rsc.test.ts.snap +++ b/packages/core/src/__tests__/__snapshots__/rsc.test.ts.snap @@ -629,9 +629,7 @@ function CustomCode(props) { const [scriptsRun, setScriptsRun] = useState(() => []); function findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem.current && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.current.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -701,9 +699,7 @@ function CustomCode(props) { const [scriptsRun, setScriptsRun] = useState(() => []); function findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem.current && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.current.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -800,8 +796,7 @@ function FormComponent(props) { const el = event.currentTarget; const headers = props.customHeaders || {}; let body; - const formData = new FormData(el); // TODO: maybe support null - + const formData = new FormData(el); const formPairs = Array.from( event.currentTarget.querySelectorAll(\\"input,select,textarea\\") ) @@ -828,7 +823,6 @@ function FormComponent(props) { value = num; } } else if (el.type === \\"file\\") { - // TODO: one vs multiple files value = el.files; } else { value = el.value; @@ -856,14 +850,11 @@ function FormComponent(props) { ) { contentType = \\"multipart/form-data\\"; } - }); // TODO: send as urlEncoded or multipart by default - // because of ease of use and reliability in browser API - // for encoding the form? + }); if (contentType !== \\"application/json\\") { body = formData; } else { - // Json const json = {}; Array.from(formPairs).forEach(({ value, key }) => { set(json, key, value); @@ -872,77 +863,90 @@ function FormComponent(props) { } if (contentType && contentType !== \\"multipart/form-data\\") { - if ( - /* Zapier doesn't allow content-type header to be sent from browsers */ !( - sendWithJs && props.action?.includes(\\"zapier.com\\") - ) - ) { + if (!(sendWithJs && props.action?.includes(\\"zapier.com\\"))) { headers[\\"content-type\\"] = contentType; } } - const presubmitEvent = new CustomEvent(\\"presubmit\\", { detail: { body } }); + + const presubmitEvent = new CustomEvent(\\"presubmit\\", { + detail: { + body, + }, + }); + if (formRef.current) { formRef.current.dispatchEvent(presubmitEvent); + if (presubmitEvent.defaultPrevented) { return; } } + setFormState(\\"sending\\"); const formUrl = \`\${ builder.env === \\"dev\\" ? \\"http://localhost:5000\\" : \\"https://builder.io\\" }/api/v1/form-submit?apiKey=\${builder.apiKey}&to=\${btoa( props.sendSubmissionsToEmail || \\"\\" )}&name=\${encodeURIComponent(props.name || \\"\\")}\`; - fetch( - props.sendSubmissionsTo === \\"email\\" - ? formUrl - : props.action /* TODO: throw error if no action URL */, - { body, headers, method: props.method || \\"post\\" } - ).then( + fetch(props.sendSubmissionsTo === \\"email\\" ? formUrl : props.action, { + body, + headers, + method: props.method || \\"post\\", + }).then( async (res) => { let body; const contentType = res.headers.get(\\"content-type\\"); + if (contentType && contentType.indexOf(\\"application/json\\") !== -1) { body = await res.json(); } else { body = await res.text(); } + if (!res.ok && props.errorMessagePath) { - /* TODO: allow supplying an error formatter function */ let message = - get(body, props.errorMessagePath); + let message = get(body, props.errorMessagePath); + if (message) { if (typeof message !== \\"string\\") { - /* TODO: ideally convert json to yaml so it woul dbe like error: - email has been taken */ message = - JSON.stringify(message); + message = JSON.stringify(message); } + setFormErrorMessage(message); } } + setResponseData(body); setFormState(res.ok ? \\"success\\" : \\"error\\"); + if (res.ok) { const submitSuccessEvent = new CustomEvent(\\"submit:success\\", { - detail: { res, body }, + detail: { + res, + body, + }, }); + if (formRef.current) { formRef.current.dispatchEvent(submitSuccessEvent); + if (submitSuccessEvent.defaultPrevented) { return; } - /* TODO: option to turn this on/off? */ if ( - props.resetFormOnSubmit !== false - ) { + + if (props.resetFormOnSubmit !== false) { formRef.current.reset(); } } - /* TODO: client side route event first that can be preventDefaulted */ if ( - props.successUrl - ) { + + if (props.successUrl) { if (formRef.current) { const event = new CustomEvent(\\"route\\", { - detail: { url: props.successUrl }, + detail: { + url: props.successUrl, + }, }); formRef.current.dispatchEvent(event); + if (!event.defaultPrevented) { location.href = props.successUrl; } @@ -954,23 +958,28 @@ function FormComponent(props) { }, (err) => { const submitErrorEvent = new CustomEvent(\\"submit:error\\", { - detail: { error: err }, + detail: { + error: err, + }, }); + if (formRef.current) { formRef.current.dispatchEvent(submitErrorEvent); + if (submitErrorEvent.defaultPrevented) { return; } } + setResponseData(err); setFormState(\\"error\\"); } ); } } + return ( <> - {\\" \\"}
onSubmit(event)} {...props.attributes} > - {\\" \\"} {props.builderBlock && props.builderBlock.children ? ( <> {props.builderBlock?.children?.map((block, index) => ( @@ -991,7 +999,8 @@ function FormComponent(props) { /> ))} - ) : null}{\\" \\"} + ) : null} + {submissionState() === \\"error\\" ? ( <> - ) : null}{\\" \\"} + ) : null} + {submissionState() === \\"sending\\" ? ( <> - ) : null}{\\" \\"} + ) : null} + {submissionState() === \\"error\\" && responseData ? ( <>
-              {\\" \\"}
-              {JSON.stringify(responseData, null, 2)}{\\" \\"}
+              {JSON.stringify(responseData, null, 2)}
             
- ) : null}{\\" \\"} + ) : null} + {submissionState() === \\"success\\" ? ( <> - ) : null}{\\" \\"} - {\\" \\"} + ) : null} + {\\" \\"} + \`} ); } + export default FormComponent; " `; @@ -1055,7 +1067,6 @@ function Image(props) { } function useLazyLoading() { - // TODO: Add more checks here, like testing for real web browsers return !!props.lazy && isBrowser(); } @@ -1869,6 +1880,47 @@ export default MyBasicComponent; " `; +exports[`RSC > jsx > Javascript Test > commentsWithQuotes 1`] = ` +"\\"use client\\"; +import * as React from \\"react\\"; +import { useState } from \\"react\\"; + +function MyComponent(props) { + const [name, setName] = useState(() => \\"Steve\\"); + + function handleFn() {} + + function doSomething() { + console.log(\\"This is a aa\\"); + } + + return ( + <> +
+ { + setName(event.target.value); + }} + onClick={(event) => doSomething()} + /> + Hello + {name}! I can run in React, Vue, Solid, or Liquid! +
+ + + ); +} + +export default MyComponent; +" +`; + exports[`RSC > jsx > Javascript Test > componentWithContext 1`] = ` "\\"use client\\"; import * as React from \\"react\\"; @@ -2551,14 +2603,14 @@ export default OnUpdate; `; exports[`RSC > jsx > Javascript Test > renderBlock 1`] = ` -"Identifier expected. (212:10) - 210 | {shouldWrap() ? ( - 211 | <>{isEmptyHtmlElement(tag()) ? ( -> 212 | <> - | ^ - 213 | ) : null} - 214 | {!isEmptyHtmlElement(tag()) && repeatItemData() ? ( - 215 | <>{repeatItemData()?.map((data, index) => (" +"Identifier expected. (190:9) + 188 | {shouldWrap() ? ( + 189 | <>{isEmptyHtmlElement(tag()) ? ( +> 190 | <> + | ^ + 191 | ) : null} + 192 | {!isEmptyHtmlElement(tag()) && repeatItemData() ? ( + 193 | <>{repeatItemData()?.map((data, index) => (" `; exports[`RSC > jsx > Javascript Test > renderContentExample 1`] = ` @@ -3697,9 +3749,7 @@ function CustomCode(props: CustomCodeProps) { const [scriptsRun, setScriptsRun] = useState(() => []); function findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem.current && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.current.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -3774,9 +3824,7 @@ function CustomCode(props: CustomCodeProps) { const [scriptsRun, setScriptsRun] = useState(() => []); function findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem.current && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.current.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -3901,8 +3949,7 @@ function FormComponent(props: FormProps) { const el = event.currentTarget; const headers = props.customHeaders || {}; let body: any; - const formData = new FormData(el); // TODO: maybe support null - + const formData = new FormData(el); const formPairs: { key: string; value: File | boolean | number | string | FileList; @@ -3932,7 +3979,6 @@ function FormComponent(props: FormProps) { value = num; } } else if (el.type === \\"file\\") { - // TODO: one vs multiple files value = el.files; } else { value = el.value; @@ -3960,14 +4006,11 @@ function FormComponent(props: FormProps) { ) { contentType = \\"multipart/form-data\\"; } - }); // TODO: send as urlEncoded or multipart by default - // because of ease of use and reliability in browser API - // for encoding the form? + }); if (contentType !== \\"application/json\\") { body = formData; } else { - // Json const json = {}; Array.from(formPairs).forEach(({ value, key }) => { set(json, key, value); @@ -3976,77 +4019,90 @@ function FormComponent(props: FormProps) { } if (contentType && contentType !== \\"multipart/form-data\\") { - if ( - /* Zapier doesn't allow content-type header to be sent from browsers */ !( - sendWithJs && props.action?.includes(\\"zapier.com\\") - ) - ) { + if (!(sendWithJs && props.action?.includes(\\"zapier.com\\"))) { headers[\\"content-type\\"] = contentType; } } - const presubmitEvent = new CustomEvent(\\"presubmit\\", { detail: { body } }); + + const presubmitEvent = new CustomEvent(\\"presubmit\\", { + detail: { + body, + }, + }); + if (formRef.current) { formRef.current.dispatchEvent(presubmitEvent); + if (presubmitEvent.defaultPrevented) { return; } } + setFormState(\\"sending\\"); const formUrl = \`\${ builder.env === \\"dev\\" ? \\"http://localhost:5000\\" : \\"https://builder.io\\" }/api/v1/form-submit?apiKey=\${builder.apiKey}&to=\${btoa( props.sendSubmissionsToEmail || \\"\\" )}&name=\${encodeURIComponent(props.name || \\"\\")}\`; - fetch( - props.sendSubmissionsTo === \\"email\\" - ? formUrl - : props.action! /* TODO: throw error if no action URL */, - { body, headers, method: props.method || \\"post\\" } - ).then( + fetch(props.sendSubmissionsTo === \\"email\\" ? formUrl : props.action!, { + body, + headers, + method: props.method || \\"post\\", + }).then( async (res) => { let body; const contentType = res.headers.get(\\"content-type\\"); + if (contentType && contentType.indexOf(\\"application/json\\") !== -1) { body = await res.json(); } else { body = await res.text(); } + if (!res.ok && props.errorMessagePath) { - /* TODO: allow supplying an error formatter function */ let message = - get(body, props.errorMessagePath); + let message = get(body, props.errorMessagePath); + if (message) { if (typeof message !== \\"string\\") { - /* TODO: ideally convert json to yaml so it woul dbe like error: - email has been taken */ message = - JSON.stringify(message); + message = JSON.stringify(message); } + setFormErrorMessage(message); } } + setResponseData(body); setFormState(res.ok ? \\"success\\" : \\"error\\"); + if (res.ok) { const submitSuccessEvent = new CustomEvent(\\"submit:success\\", { - detail: { res, body }, + detail: { + res, + body, + }, }); + if (formRef.current) { formRef.current.dispatchEvent(submitSuccessEvent); + if (submitSuccessEvent.defaultPrevented) { return; } - /* TODO: option to turn this on/off? */ if ( - props.resetFormOnSubmit !== false - ) { + + if (props.resetFormOnSubmit !== false) { formRef.current.reset(); } } - /* TODO: client side route event first that can be preventDefaulted */ if ( - props.successUrl - ) { + + if (props.successUrl) { if (formRef.current) { const event = new CustomEvent(\\"route\\", { - detail: { url: props.successUrl }, + detail: { + url: props.successUrl, + }, }); formRef.current.dispatchEvent(event); + if (!event.defaultPrevented) { location.href = props.successUrl; } @@ -4058,23 +4114,28 @@ function FormComponent(props: FormProps) { }, (err) => { const submitErrorEvent = new CustomEvent(\\"submit:error\\", { - detail: { error: err }, + detail: { + error: err, + }, }); + if (formRef.current) { formRef.current.dispatchEvent(submitErrorEvent); + if (submitErrorEvent.defaultPrevented) { return; } } + setResponseData(err); setFormState(\\"error\\"); } ); } } + return ( <> - {\\" \\"}
onSubmit(event)} {...props.attributes} > - {\\" \\"} {props.builderBlock && props.builderBlock.children ? ( <> {props.builderBlock?.children?.map((block, index) => ( @@ -4095,7 +4155,8 @@ function FormComponent(props: FormProps) { /> ))} - ) : null}{\\" \\"} + ) : null} + {submissionState() === \\"error\\" ? ( <> - ) : null}{\\" \\"} + ) : null} + {submissionState() === \\"sending\\" ? ( <> - ) : null}{\\" \\"} + ) : null} + {submissionState() === \\"error\\" && responseData ? ( <>
-              {\\" \\"}
-              {JSON.stringify(responseData, null, 2)}{\\" \\"}
+              {JSON.stringify(responseData, null, 2)}
             
- ) : null}{\\" \\"} + ) : null} + {submissionState() === \\"success\\" ? ( <> - ) : null}{\\" \\"} - {\\" \\"} + ) : null} + {\\" \\"} + \`} ); } + export default FormComponent; " `; @@ -4178,7 +4242,6 @@ function Image(props: ImageProps) { } function useLazyLoading() { - // TODO: Add more checks here, like testing for real web browsers return !!props.lazy && isBrowser(); } @@ -5130,6 +5193,47 @@ export default MyBasicComponent; " `; +exports[`RSC > jsx > Typescript Test > commentsWithQuotes 1`] = ` +"\\"use client\\"; +import * as React from \\"react\\"; +import { useState } from \\"react\\"; + +function MyComponent(props: any) { + const [name, setName] = useState(() => \\"Steve\\"); + + function handleFn() {} + + function doSomething() { + console.log(\\"This is a aa\\"); + } + + return ( + <> +
+ { + setName(event.target.value); + }} + onClick={(event) => doSomething()} + /> + Hello + {name}! I can run in React, Vue, Solid, or Liquid! +
+ + + ); +} + +export default MyComponent; +" +`; + exports[`RSC > jsx > Typescript Test > componentWithContext 1`] = ` "\\"use client\\"; import * as React from \\"react\\"; @@ -5902,14 +6006,14 @@ export default OnUpdate; `; exports[`RSC > jsx > Typescript Test > renderBlock 1`] = ` -"Identifier expected. (220:10) - 218 | {shouldWrap() ? ( - 219 | <>{isEmptyHtmlElement(tag()) ? ( -> 220 | <> - | ^ - 221 | ) : null} - 222 | {!isEmptyHtmlElement(tag()) && repeatItemData() ? ( - 223 | <>{repeatItemData()?.map((data, index) => (" +"Identifier expected. (198:9) + 196 | {shouldWrap() ? ( + 197 | <>{isEmptyHtmlElement(tag()) ? ( +> 198 | <> + | ^ + 199 | ) : null} + 200 | {!isEmptyHtmlElement(tag()) && repeatItemData() ? ( + 201 | <>{repeatItemData()?.map((data, index) => (" `; exports[`RSC > jsx > Typescript Test > renderContentExample 1`] = ` diff --git a/packages/core/src/__tests__/__snapshots__/solid.test.ts.snap b/packages/core/src/__tests__/__snapshots__/solid.test.ts.snap index cc09103256..6728028015 100644 --- a/packages/core/src/__tests__/__snapshots__/solid.test.ts.snap +++ b/packages/core/src/__tests__/__snapshots__/solid.test.ts.snap @@ -1289,9 +1289,7 @@ function CustomCode(props) { const [scriptsRun, setScriptsRun] = createSignal([]); function findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -1362,9 +1360,7 @@ function CustomCode(props) { const [scriptsRun, setScriptsRun] = createSignal([]); function findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -1435,9 +1431,7 @@ function CustomCode(props) { const [scriptsRun, setScriptsRun] = createSignal([]); function findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -1508,9 +1502,7 @@ function CustomCode(props) { const [scriptsRun, setScriptsRun] = createSignal([]); function findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -1611,8 +1603,7 @@ function FormComponent(props) { const el = event.currentTarget; const headers = props.customHeaders || {}; let body; - const formData = new FormData(el); // TODO: maybe support null - + const formData = new FormData(el); const formPairs = Array.from( event.currentTarget.querySelectorAll(\\"input,select,textarea\\") ) @@ -1639,7 +1630,6 @@ function FormComponent(props) { value = num; } } else if (el.type === \\"file\\") { - // TODO: one vs multiple files value = el.files; } else { value = el.value; @@ -1667,14 +1657,11 @@ function FormComponent(props) { ) { contentType = \\"multipart/form-data\\"; } - }); // TODO: send as urlEncoded or multipart by default - // because of ease of use and reliability in browser API - // for encoding the form? + }); if (contentType !== \\"application/json\\") { body = formData; } else { - // Json const json = {}; Array.from(formPairs).forEach(({ value, key }) => { set(json, key, value); @@ -1683,10 +1670,7 @@ function FormComponent(props) { } if (contentType && contentType !== \\"multipart/form-data\\") { - if ( - /* Zapier doesn't allow content-type header to be sent from browsers */ - !(sendWithJs && props.action?.includes(\\"zapier.com\\")) - ) { + if (!(sendWithJs && props.action?.includes(\\"zapier.com\\"))) { headers[\\"content-type\\"] = contentType; } } @@ -1711,15 +1695,11 @@ function FormComponent(props) { }/api/v1/form-submit?apiKey=\${builder.apiKey}&to=\${btoa( props.sendSubmissionsToEmail || \\"\\" )}&name=\${encodeURIComponent(props.name || \\"\\")}\`; - fetch( - props.sendSubmissionsTo === \\"email\\" ? formUrl : props.action, - /* TODO: throw error if no action URL */ - { - body, - headers, - method: props.method || \\"post\\", - } - ).then( + fetch(props.sendSubmissionsTo === \\"email\\" ? formUrl : props.action, { + body, + headers, + method: props.method || \\"post\\", + }).then( async (res) => { let body; const contentType = res.headers.get(\\"content-type\\"); @@ -1731,13 +1711,10 @@ function FormComponent(props) { } if (!res.ok && props.errorMessagePath) { - /* TODO: allow supplying an error formatter function */ let message = get(body, props.errorMessagePath); if (message) { if (typeof message !== \\"string\\") { - /* TODO: ideally convert json to yaml so it woul dbe like - error: - email has been taken */ message = JSON.stringify(message); } @@ -1762,13 +1739,11 @@ function FormComponent(props) { if (submitSuccessEvent.defaultPrevented) { return; } - /* TODO: option to turn this on/off? */ if (props.resetFormOnSubmit !== false) { formRef.reset(); } } - /* TODO: client side route event first that can be preventDefaulted */ if (props.successUrl) { if (formRef) { @@ -1915,8 +1890,7 @@ function FormComponent(props) { const el = event.currentTarget; const headers = props.customHeaders || {}; let body; - const formData = new FormData(el); // TODO: maybe support null - + const formData = new FormData(el); const formPairs = Array.from( event.currentTarget.querySelectorAll(\\"input,select,textarea\\") ) @@ -1943,7 +1917,6 @@ function FormComponent(props) { value = num; } } else if (el.type === \\"file\\") { - // TODO: one vs multiple files value = el.files; } else { value = el.value; @@ -1971,14 +1944,11 @@ function FormComponent(props) { ) { contentType = \\"multipart/form-data\\"; } - }); // TODO: send as urlEncoded or multipart by default - // because of ease of use and reliability in browser API - // for encoding the form? + }); if (contentType !== \\"application/json\\") { body = formData; } else { - // Json const json = {}; Array.from(formPairs).forEach(({ value, key }) => { set(json, key, value); @@ -1987,10 +1957,7 @@ function FormComponent(props) { } if (contentType && contentType !== \\"multipart/form-data\\") { - if ( - /* Zapier doesn't allow content-type header to be sent from browsers */ - !(sendWithJs && props.action?.includes(\\"zapier.com\\")) - ) { + if (!(sendWithJs && props.action?.includes(\\"zapier.com\\"))) { headers[\\"content-type\\"] = contentType; } } @@ -2015,15 +1982,11 @@ function FormComponent(props) { }/api/v1/form-submit?apiKey=\${builder.apiKey}&to=\${btoa( props.sendSubmissionsToEmail || \\"\\" )}&name=\${encodeURIComponent(props.name || \\"\\")}\`; - fetch( - props.sendSubmissionsTo === \\"email\\" ? formUrl : props.action, - /* TODO: throw error if no action URL */ - { - body, - headers, - method: props.method || \\"post\\", - } - ).then( + fetch(props.sendSubmissionsTo === \\"email\\" ? formUrl : props.action, { + body, + headers, + method: props.method || \\"post\\", + }).then( async (res) => { let body; const contentType = res.headers.get(\\"content-type\\"); @@ -2035,13 +1998,10 @@ function FormComponent(props) { } if (!res.ok && props.errorMessagePath) { - /* TODO: allow supplying an error formatter function */ let message = get(body, props.errorMessagePath); if (message) { if (typeof message !== \\"string\\") { - /* TODO: ideally convert json to yaml so it woul dbe like - error: - email has been taken */ message = JSON.stringify(message); } @@ -2066,13 +2026,11 @@ function FormComponent(props) { if (submitSuccessEvent.defaultPrevented) { return; } - /* TODO: option to turn this on/off? */ if (props.resetFormOnSubmit !== false) { formRef.reset(); } } - /* TODO: client side route event first that can be preventDefaulted */ if (props.successUrl) { if (formRef) { @@ -2154,7 +2112,7 @@ function FormComponent(props) { >
-
+          
             {JSON.stringify(responseData(), null, 2)}
           
@@ -2166,7 +2124,7 @@ function FormComponent(props) { + + ); +} + +export default MyComponent; +" +`; + exports[`Solid > jsx > Javascript Test > componentWithContext 1`] = ` "import { useContext } from \\"solid-js\\"; @@ -5098,7 +5131,6 @@ function RenderBlock(props) { const ref = props.context.registeredComponents[componentName]; if (!ref) { - // TODO: Public doc page with more info about this message console.warn(\` Could not find a registered component named \\"\${componentName}\\". If you registered it, is the file that registered it imported by the file that needs to render it?\`); @@ -5157,11 +5189,6 @@ function RenderBlock(props) { componentRef: component()?.component, componentOptions: { ...getBlockComponentOptions(useBlock()), - - /** - * These attributes are passed to the wrapper element when there is one. If \`noWrap\` is set to true, then - * they are provided to the component itself directly. - */ ...(shouldWrap() ? {} : { @@ -5174,30 +5201,16 @@ function RenderBlock(props) { } function useChildren() { - // TO-DO: When should \`canHaveChildren\` dictate rendering? - // This is currently commented out because some Builder components (e.g. Box) do not have \`canHaveChildren: true\`, - // but still receive and need to render children. - // return state.componentInfo?.canHaveChildren ? state.useBlock.children : []; return useBlock().children ?? []; } function childrenWithoutParentComponent() { - /** - * When there is no \`componentRef\`, there might still be children that need to be rendered. In this case, - * we render them outside of \`componentRef\`. - * NOTE: We make sure not to render this if \`repeatItemData\` is non-null, because that means we are rendering an array of - * blocks, and the children will be repeated within those blocks. - */ const shouldRenderChildrenOutsideRef = !component()?.component && !repeatItemData(); return shouldRenderChildrenOutsideRef ? useChildren() : []; } function repeatItemData() { - /** - * we don't use \`state.useBlock\` here because the processing done within its logic includes evaluating the block's bindings, - * which will not work if there is a repeat. - */ const { repeat, ...blockWithoutRepeat } = props.block; if (!repeat?.collection) { @@ -5261,7 +5274,6 @@ function RenderBlock(props) { if (TARGET === \\"reactNative\\") { return RenderComponentWithContext; } else if (TARGET === \\"vue3\\") { - // vue3 expects a string for the component tag return \\"RenderComponent\\"; } else { return RenderComponent; @@ -5372,7 +5384,6 @@ function RenderBlock(props) { const ref = props.context.registeredComponents[componentName]; if (!ref) { - // TODO: Public doc page with more info about this message console.warn(\` Could not find a registered component named \\"\${componentName}\\". If you registered it, is the file that registered it imported by the file that needs to render it?\`); @@ -5431,11 +5442,6 @@ function RenderBlock(props) { componentRef: component()?.component, componentOptions: { ...getBlockComponentOptions(useBlock()), - - /** - * These attributes are passed to the wrapper element when there is one. If \`noWrap\` is set to true, then - * they are provided to the component itself directly. - */ ...(shouldWrap() ? {} : { @@ -5448,30 +5454,16 @@ function RenderBlock(props) { } function useChildren() { - // TO-DO: When should \`canHaveChildren\` dictate rendering? - // This is currently commented out because some Builder components (e.g. Box) do not have \`canHaveChildren: true\`, - // but still receive and need to render children. - // return state.componentInfo?.canHaveChildren ? state.useBlock.children : []; return useBlock().children ?? []; } function childrenWithoutParentComponent() { - /** - * When there is no \`componentRef\`, there might still be children that need to be rendered. In this case, - * we render them outside of \`componentRef\`. - * NOTE: We make sure not to render this if \`repeatItemData\` is non-null, because that means we are rendering an array of - * blocks, and the children will be repeated within those blocks. - */ const shouldRenderChildrenOutsideRef = !component()?.component && !repeatItemData(); return shouldRenderChildrenOutsideRef ? useChildren() : []; } function repeatItemData() { - /** - * we don't use \`state.useBlock\` here because the processing done within its logic includes evaluating the block's bindings, - * which will not work if there is a repeat. - */ const { repeat, ...blockWithoutRepeat } = props.block; if (!repeat?.collection) { @@ -5535,7 +5527,6 @@ function RenderBlock(props) { if (TARGET === \\"reactNative\\") { return RenderComponentWithContext; } else if (TARGET === \\"vue3\\") { - // vue3 expects a string for the component tag return \\"RenderComponent\\"; } else { return RenderComponent; @@ -7918,9 +7909,7 @@ function CustomCode(props: CustomCodeProps) { const [scriptsRun, setScriptsRun] = createSignal([]); function findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -7995,9 +7984,7 @@ function CustomCode(props: CustomCodeProps) { const [scriptsRun, setScriptsRun] = createSignal([]); function findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -8073,9 +8060,7 @@ function CustomCode(props: CustomCodeProps) { const [scriptsRun, setScriptsRun] = createSignal([]); function findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -8150,9 +8135,7 @@ function CustomCode(props: CustomCodeProps) { const [scriptsRun, setScriptsRun] = createSignal([]); function findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -8280,8 +8263,7 @@ function FormComponent(props: FormProps) { const el = event.currentTarget; const headers = props.customHeaders || {}; let body: any; - const formData = new FormData(el); // TODO: maybe support null - + const formData = new FormData(el); const formPairs: { key: string; value: File | boolean | number | string | FileList; @@ -8311,7 +8293,6 @@ function FormComponent(props: FormProps) { value = num; } } else if (el.type === \\"file\\") { - // TODO: one vs multiple files value = el.files; } else { value = el.value; @@ -8339,14 +8320,11 @@ function FormComponent(props: FormProps) { ) { contentType = \\"multipart/form-data\\"; } - }); // TODO: send as urlEncoded or multipart by default - // because of ease of use and reliability in browser API - // for encoding the form? + }); if (contentType !== \\"application/json\\") { body = formData; } else { - // Json const json = {}; Array.from(formPairs).forEach(({ value, key }) => { set(json, key, value); @@ -8355,10 +8333,7 @@ function FormComponent(props: FormProps) { } if (contentType && contentType !== \\"multipart/form-data\\") { - if ( - /* Zapier doesn't allow content-type header to be sent from browsers */ - !(sendWithJs && props.action?.includes(\\"zapier.com\\")) - ) { + if (!(sendWithJs && props.action?.includes(\\"zapier.com\\"))) { headers[\\"content-type\\"] = contentType; } } @@ -8383,15 +8358,11 @@ function FormComponent(props: FormProps) { }/api/v1/form-submit?apiKey=\${builder.apiKey}&to=\${btoa( props.sendSubmissionsToEmail || \\"\\" )}&name=\${encodeURIComponent(props.name || \\"\\")}\`; - fetch( - props.sendSubmissionsTo === \\"email\\" ? formUrl : props.action!, - /* TODO: throw error if no action URL */ - { - body, - headers, - method: props.method || \\"post\\", - } - ).then( + fetch(props.sendSubmissionsTo === \\"email\\" ? formUrl : props.action!, { + body, + headers, + method: props.method || \\"post\\", + }).then( async (res) => { let body; const contentType = res.headers.get(\\"content-type\\"); @@ -8403,13 +8374,10 @@ function FormComponent(props: FormProps) { } if (!res.ok && props.errorMessagePath) { - /* TODO: allow supplying an error formatter function */ let message = get(body, props.errorMessagePath); if (message) { if (typeof message !== \\"string\\") { - /* TODO: ideally convert json to yaml so it woul dbe like - error: - email has been taken */ message = JSON.stringify(message); } @@ -8434,13 +8402,11 @@ function FormComponent(props: FormProps) { if (submitSuccessEvent.defaultPrevented) { return; } - /* TODO: option to turn this on/off? */ if (props.resetFormOnSubmit !== false) { formRef.reset(); } } - /* TODO: client side route event first that can be preventDefaulted */ if (props.successUrl) { if (formRef) { @@ -8614,8 +8580,7 @@ function FormComponent(props: FormProps) { const el = event.currentTarget; const headers = props.customHeaders || {}; let body: any; - const formData = new FormData(el); // TODO: maybe support null - + const formData = new FormData(el); const formPairs: { key: string; value: File | boolean | number | string | FileList; @@ -8645,7 +8610,6 @@ function FormComponent(props: FormProps) { value = num; } } else if (el.type === \\"file\\") { - // TODO: one vs multiple files value = el.files; } else { value = el.value; @@ -8673,14 +8637,11 @@ function FormComponent(props: FormProps) { ) { contentType = \\"multipart/form-data\\"; } - }); // TODO: send as urlEncoded or multipart by default - // because of ease of use and reliability in browser API - // for encoding the form? + }); if (contentType !== \\"application/json\\") { body = formData; } else { - // Json const json = {}; Array.from(formPairs).forEach(({ value, key }) => { set(json, key, value); @@ -8689,10 +8650,7 @@ function FormComponent(props: FormProps) { } if (contentType && contentType !== \\"multipart/form-data\\") { - if ( - /* Zapier doesn't allow content-type header to be sent from browsers */ - !(sendWithJs && props.action?.includes(\\"zapier.com\\")) - ) { + if (!(sendWithJs && props.action?.includes(\\"zapier.com\\"))) { headers[\\"content-type\\"] = contentType; } } @@ -8717,15 +8675,11 @@ function FormComponent(props: FormProps) { }/api/v1/form-submit?apiKey=\${builder.apiKey}&to=\${btoa( props.sendSubmissionsToEmail || \\"\\" )}&name=\${encodeURIComponent(props.name || \\"\\")}\`; - fetch( - props.sendSubmissionsTo === \\"email\\" ? formUrl : props.action!, - /* TODO: throw error if no action URL */ - { - body, - headers, - method: props.method || \\"post\\", - } - ).then( + fetch(props.sendSubmissionsTo === \\"email\\" ? formUrl : props.action!, { + body, + headers, + method: props.method || \\"post\\", + }).then( async (res) => { let body; const contentType = res.headers.get(\\"content-type\\"); @@ -8737,13 +8691,10 @@ function FormComponent(props: FormProps) { } if (!res.ok && props.errorMessagePath) { - /* TODO: allow supplying an error formatter function */ let message = get(body, props.errorMessagePath); if (message) { if (typeof message !== \\"string\\") { - /* TODO: ideally convert json to yaml so it woul dbe like - error: - email has been taken */ message = JSON.stringify(message); } @@ -8768,13 +8719,11 @@ function FormComponent(props: FormProps) { if (submitSuccessEvent.defaultPrevented) { return; } - /* TODO: option to turn this on/off? */ if (props.resetFormOnSubmit !== false) { formRef.reset(); } } - /* TODO: client side route event first that can be preventDefaulted */ if (props.successUrl) { if (formRef) { @@ -8856,7 +8805,7 @@ function FormComponent(props: FormProps) { > -
+          
             {JSON.stringify(responseData(), null, 2)}
           
@@ -8868,7 +8817,7 @@ function FormComponent(props: FormProps) { + + ); +} + +export default MyComponent; +" +`; + exports[`Solid > jsx > Typescript Test > componentWithContext 1`] = ` "import { useContext } from \\"solid-js\\"; @@ -12256,7 +12280,6 @@ function RenderBlock(props: RenderBlockProps) { const ref = props.context.registeredComponents[componentName]; if (!ref) { - // TODO: Public doc page with more info about this message console.warn(\` Could not find a registered component named \\"\${componentName}\\". If you registered it, is the file that registered it imported by the file that needs to render it?\`); @@ -12315,11 +12338,6 @@ function RenderBlock(props: RenderBlockProps) { componentRef: component()?.component, componentOptions: { ...getBlockComponentOptions(useBlock()), - - /** - * These attributes are passed to the wrapper element when there is one. If \`noWrap\` is set to true, then - * they are provided to the component itself directly. - */ ...(shouldWrap() ? {} : { @@ -12332,30 +12350,16 @@ function RenderBlock(props: RenderBlockProps) { } function useChildren() { - // TO-DO: When should \`canHaveChildren\` dictate rendering? - // This is currently commented out because some Builder components (e.g. Box) do not have \`canHaveChildren: true\`, - // but still receive and need to render children. - // return state.componentInfo?.canHaveChildren ? state.useBlock.children : []; return useBlock().children ?? []; } function childrenWithoutParentComponent() { - /** - * When there is no \`componentRef\`, there might still be children that need to be rendered. In this case, - * we render them outside of \`componentRef\`. - * NOTE: We make sure not to render this if \`repeatItemData\` is non-null, because that means we are rendering an array of - * blocks, and the children will be repeated within those blocks. - */ const shouldRenderChildrenOutsideRef = !component()?.component && !repeatItemData(); return shouldRenderChildrenOutsideRef ? useChildren() : []; } function repeatItemData() { - /** - * we don't use \`state.useBlock\` here because the processing done within its logic includes evaluating the block's bindings, - * which will not work if there is a repeat. - */ const { repeat, ...blockWithoutRepeat } = props.block; if (!repeat?.collection) { @@ -12419,7 +12423,6 @@ function RenderBlock(props: RenderBlockProps) { if (TARGET === \\"reactNative\\") { return RenderComponentWithContext; } else if (TARGET === \\"vue3\\") { - // vue3 expects a string for the component tag return \\"RenderComponent\\"; } else { return RenderComponent; @@ -12542,7 +12545,6 @@ function RenderBlock(props: RenderBlockProps) { const ref = props.context.registeredComponents[componentName]; if (!ref) { - // TODO: Public doc page with more info about this message console.warn(\` Could not find a registered component named \\"\${componentName}\\". If you registered it, is the file that registered it imported by the file that needs to render it?\`); @@ -12601,11 +12603,6 @@ function RenderBlock(props: RenderBlockProps) { componentRef: component()?.component, componentOptions: { ...getBlockComponentOptions(useBlock()), - - /** - * These attributes are passed to the wrapper element when there is one. If \`noWrap\` is set to true, then - * they are provided to the component itself directly. - */ ...(shouldWrap() ? {} : { @@ -12618,30 +12615,16 @@ function RenderBlock(props: RenderBlockProps) { } function useChildren() { - // TO-DO: When should \`canHaveChildren\` dictate rendering? - // This is currently commented out because some Builder components (e.g. Box) do not have \`canHaveChildren: true\`, - // but still receive and need to render children. - // return state.componentInfo?.canHaveChildren ? state.useBlock.children : []; return useBlock().children ?? []; } function childrenWithoutParentComponent() { - /** - * When there is no \`componentRef\`, there might still be children that need to be rendered. In this case, - * we render them outside of \`componentRef\`. - * NOTE: We make sure not to render this if \`repeatItemData\` is non-null, because that means we are rendering an array of - * blocks, and the children will be repeated within those blocks. - */ const shouldRenderChildrenOutsideRef = !component()?.component && !repeatItemData(); return shouldRenderChildrenOutsideRef ? useChildren() : []; } function repeatItemData() { - /** - * we don't use \`state.useBlock\` here because the processing done within its logic includes evaluating the block's bindings, - * which will not work if there is a repeat. - */ const { repeat, ...blockWithoutRepeat } = props.block; if (!repeat?.collection) { @@ -12705,7 +12688,6 @@ function RenderBlock(props: RenderBlockProps) { if (TARGET === \\"reactNative\\") { return RenderComponentWithContext; } else if (TARGET === \\"vue3\\") { - // vue3 expects a string for the component tag return \\"RenderComponent\\"; } else { return RenderComponent; diff --git a/packages/core/src/__tests__/__snapshots__/stencil.test.ts.snap b/packages/core/src/__tests__/__snapshots__/stencil.test.ts.snap index 2222ba7768..94f8056374 100644 --- a/packages/core/src/__tests__/__snapshots__/stencil.test.ts.snap +++ b/packages/core/src/__tests__/__snapshots__/stencil.test.ts.snap @@ -618,9 +618,7 @@ export default class CustomCode { @State() scriptsRun = []; findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -692,9 +690,7 @@ export default class CustomCode { @State() scriptsRun = []; findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -815,8 +811,7 @@ export default class FormComponent { const el = event.currentTarget; const headers = this.customHeaders || {}; let body; - const formData = new FormData(el); // TODO: maybe support null - + const formData = new FormData(el); const formPairs = Array.from( event.currentTarget.querySelectorAll(\\"input,select,textarea\\") ) @@ -843,7 +838,6 @@ export default class FormComponent { value = num; } } else if (el.type === \\"file\\") { - // TODO: one vs multiple files value = el.files; } else { value = el.value; @@ -871,14 +865,11 @@ export default class FormComponent { ) { contentType = \\"multipart/form-data\\"; } - }); // TODO: send as urlEncoded or multipart by default - // because of ease of use and reliability in browser API - // for encoding the form? + }); if (contentType !== \\"application/json\\") { body = formData; } else { - // Json const json = {}; Array.from(formPairs).forEach(({ value, key }) => { set(json, key, value); @@ -887,10 +878,7 @@ export default class FormComponent { } if (contentType && contentType !== \\"multipart/form-data\\") { - if ( - /* Zapier doesn't allow content-type header to be sent from browsers */ - !(sendWithJs && this.action?.includes(\\"zapier.com\\")) - ) { + if (!(sendWithJs && this.action?.includes(\\"zapier.com\\"))) { headers[\\"content-type\\"] = contentType; } } @@ -915,15 +903,11 @@ export default class FormComponent { }/api/v1/form-submit?apiKey=\${builder.apiKey}&to=\${btoa( this.sendSubmissionsToEmail || \\"\\" )}&name=\${encodeURIComponent(this.name || \\"\\")}\`; - fetch( - this.sendSubmissionsTo === \\"email\\" ? formUrl : this.action, - /* TODO: throw error if no action URL */ - { - body, - headers, - method: this.method || \\"post\\", - } - ).then( + fetch(this.sendSubmissionsTo === \\"email\\" ? formUrl : this.action, { + body, + headers, + method: this.method || \\"post\\", + }).then( async (res) => { let body; const contentType = res.headers.get(\\"content-type\\"); @@ -935,13 +919,10 @@ export default class FormComponent { } if (!res.ok && this.errorMessagePath) { - /* TODO: allow supplying an error formatter function */ let message = get(body, this.errorMessagePath); if (message) { if (typeof message !== \\"string\\") { - /* TODO: ideally convert json to yaml so it woul dbe like - error: - email has been taken */ message = JSON.stringify(message); } @@ -966,13 +947,11 @@ export default class FormComponent { if (submitSuccessEvent.defaultPrevented) { return; } - /* TODO: option to turn this on/off? */ if (this.resetFormOnSubmit !== false) { formRef.reset(); } } - /* TODO: client side route event first that can be preventDefaulted */ if (this.successUrl) { if (formRef) { @@ -1109,7 +1088,6 @@ export default class Image { this.imageLoaded = true; } useLazyLoading() { - // TODO: Add more checks here, like testing for real web browsers return !!this.lazy && this.isBrowser(); } isBrowser = function isBrowser() { @@ -2006,6 +1984,45 @@ export default class MyBasicComponent { " `; +exports[`Stencil > jsx > Javascript Test > commentsWithQuotes 1`] = ` +"import { Component, Prop, h, State, Fragment } from \\"@stencil/core\\"; + +@Component({ + tag: \\"my-component\\", + styles: \` + .input { + color: red; + } +\`, +}) +export default class MyComponent { + @State() name = \\"Steve\\"; + + handleFn() {} + doSomething = function doSomething() { + console.log(\\"This is a aa\\"); + }; + + render() { + return ( +
+ { + this.name = event.target.value; + }} + onClick={(event) => this.doSomething()} + /> + Hello + {this.name}! I can run in React, Vue, Solid, or Liquid! +
+ ); + } +} +" +`; + exports[`Stencil > jsx > Javascript Test > componentWithContext 1`] = ` "import Context1 from \\"@dummy/1\\"; import Context2 from \\"@dummy/2\\"; @@ -2604,14 +2621,14 @@ export default class OnUpdate { `; exports[`Stencil > jsx > Javascript Test > renderBlock 1`] = ` -"':' expected. (205:15) - 203 | return ( - 204 | -> 205 | {this.shouldWrap ? ( +"':' expected. (184:15) + 182 | return ( + 183 | +> 184 | {this.shouldWrap ? ( | ^ - 206 | <>{isEmptyHtmlElement(this.tag) ? ( - 207 | <> - 208 | ) : null}" + 185 | <>{isEmptyHtmlElement(this.tag) ? ( + 186 | <> + 187 | ) : null}" `; exports[`Stencil > jsx > Javascript Test > renderContentExample 1`] = ` @@ -3663,9 +3680,7 @@ export default class CustomCode { @State() scriptsRun = []; findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -3737,9 +3752,7 @@ export default class CustomCode { @State() scriptsRun = []; findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -3864,8 +3877,7 @@ export default class FormComponent { const el = event.currentTarget; const headers = this.customHeaders || {}; let body: any; - const formData = new FormData(el); // TODO: maybe support null - + const formData = new FormData(el); const formPairs: { key: string; value: File | boolean | number | string | FileList; @@ -3895,7 +3907,6 @@ export default class FormComponent { value = num; } } else if (el.type === \\"file\\") { - // TODO: one vs multiple files value = el.files; } else { value = el.value; @@ -3923,14 +3934,11 @@ export default class FormComponent { ) { contentType = \\"multipart/form-data\\"; } - }); // TODO: send as urlEncoded or multipart by default - // because of ease of use and reliability in browser API - // for encoding the form? + }); if (contentType !== \\"application/json\\") { body = formData; } else { - // Json const json = {}; Array.from(formPairs).forEach(({ value, key }) => { set(json, key, value); @@ -3939,10 +3947,7 @@ export default class FormComponent { } if (contentType && contentType !== \\"multipart/form-data\\") { - if ( - /* Zapier doesn't allow content-type header to be sent from browsers */ - !(sendWithJs && this.action?.includes(\\"zapier.com\\")) - ) { + if (!(sendWithJs && this.action?.includes(\\"zapier.com\\"))) { headers[\\"content-type\\"] = contentType; } } @@ -3967,15 +3972,11 @@ export default class FormComponent { }/api/v1/form-submit?apiKey=\${builder.apiKey}&to=\${btoa( this.sendSubmissionsToEmail || \\"\\" )}&name=\${encodeURIComponent(this.name || \\"\\")}\`; - fetch( - this.sendSubmissionsTo === \\"email\\" ? formUrl : this.action!, - /* TODO: throw error if no action URL */ - { - body, - headers, - method: this.method || \\"post\\", - } - ).then( + fetch(this.sendSubmissionsTo === \\"email\\" ? formUrl : this.action!, { + body, + headers, + method: this.method || \\"post\\", + }).then( async (res) => { let body; const contentType = res.headers.get(\\"content-type\\"); @@ -3987,13 +3988,10 @@ export default class FormComponent { } if (!res.ok && this.errorMessagePath) { - /* TODO: allow supplying an error formatter function */ let message = get(body, this.errorMessagePath); if (message) { if (typeof message !== \\"string\\") { - /* TODO: ideally convert json to yaml so it woul dbe like - error: - email has been taken */ message = JSON.stringify(message); } @@ -4018,13 +4016,11 @@ export default class FormComponent { if (submitSuccessEvent.defaultPrevented) { return; } - /* TODO: option to turn this on/off? */ if (this.resetFormOnSubmit !== false) { formRef.reset(); } } - /* TODO: client side route event first that can be preventDefaulted */ if (this.successUrl) { if (formRef) { @@ -4161,7 +4157,6 @@ export default class Image { this.imageLoaded = true; } useLazyLoading() { - // TODO: Add more checks here, like testing for real web browsers return !!this.lazy && this.isBrowser(); } isBrowser = function isBrowser() { @@ -5060,6 +5055,45 @@ export default class MyBasicComponent { " `; +exports[`Stencil > jsx > Typescript Test > commentsWithQuotes 1`] = ` +"import { Component, Prop, h, State, Fragment } from \\"@stencil/core\\"; + +@Component({ + tag: \\"my-component\\", + styles: \` + .input { + color: red; + } +\`, +}) +export default class MyComponent { + @State() name = \\"Steve\\"; + + handleFn() {} + doSomething = function doSomething() { + console.log(\\"This is a aa\\"); + }; + + render() { + return ( +
+ { + this.name = event.target.value; + }} + onClick={(event) => this.doSomething()} + /> + Hello + {this.name}! I can run in React, Vue, Solid, or Liquid! +
+ ); + } +} +" +`; + exports[`Stencil > jsx > Typescript Test > componentWithContext 1`] = ` "import Context1 from \\"@dummy/1\\"; import Context2 from \\"@dummy/2\\"; @@ -5659,14 +5693,14 @@ export default class OnUpdate { `; exports[`Stencil > jsx > Typescript Test > renderBlock 1`] = ` -"':' expected. (210:15) - 208 | return ( - 209 | -> 210 | {this.shouldWrap ? ( +"':' expected. (189:15) + 187 | return ( + 188 | +> 189 | {this.shouldWrap ? ( | ^ - 211 | <>{isEmptyHtmlElement(this.tag) ? ( - 212 | <> - 213 | ) : null}" + 190 | <>{isEmptyHtmlElement(this.tag) ? ( + 191 | <> + 192 | ) : null}" `; exports[`Stencil > jsx > Typescript Test > renderContentExample 1`] = ` diff --git a/packages/core/src/__tests__/__snapshots__/svelte.test.ts.snap b/packages/core/src/__tests__/__snapshots__/svelte.test.ts.snap index 36a6dcc965..789864d338 100644 --- a/packages/core/src/__tests__/__snapshots__/svelte.test.ts.snap +++ b/packages/core/src/__tests__/__snapshots__/svelte.test.ts.snap @@ -507,9 +507,7 @@ exports[`Svelte > jsx > Javascript Test > CustomCode 1`] = ` export let code; function findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -574,9 +572,7 @@ exports[`Svelte > jsx > Javascript Test > Embed 1`] = ` export let code; function findAndRunScripts() { - // TODO: Move this function to standalone one in '@builder.io/utils' if (elem && typeof window !== \\"undefined\\") { - /** @type {HTMLScriptElement[]} */ const scripts = elem.getElementsByTagName(\\"script\\"); for (let i = 0; i < scripts.length; i++) { @@ -677,8 +673,7 @@ exports[`Svelte > jsx > Javascript Test > Form 1`] = ` const el = event.currentTarget; const headers = customHeaders || {}; let body; - const formData = new FormData(el); // TODO: maybe support null - + const formData = new FormData(el); const formPairs = Array.from( event.currentTarget.querySelectorAll(\\"input,select,textarea\\") ) @@ -705,7 +700,6 @@ exports[`Svelte > jsx > Javascript Test > Form 1`] = ` value = num; } } else if (el.type === \\"file\\") { - // TODO: one vs multiple files value = el.files; } else { value = el.value; @@ -733,14 +727,11 @@ exports[`Svelte > jsx > Javascript Test > Form 1`] = ` ) { contentType = \\"multipart/form-data\\"; } - }); // TODO: send as urlEncoded or multipart by default - // because of ease of use and reliability in browser API - // for encoding the form? + }); if (contentType !== \\"application/json\\") { body = formData; } else { - // Json const json = {}; Array.from(formPairs).forEach(({ value, key }) => { set(json, key, value); @@ -749,10 +740,7 @@ exports[`Svelte > jsx > Javascript Test > Form 1`] = ` } if (contentType && contentType !== \\"multipart/form-data\\") { - if ( - /* Zapier doesn't allow content-type header to be sent from browsers */ - !(sendWithJs && action?.includes(\\"zapier.com\\")) - ) { + if (!(sendWithJs && action?.includes(\\"zapier.com\\"))) { headers[\\"content-type\\"] = contentType; } } @@ -777,15 +765,11 @@ exports[`Svelte > jsx > Javascript Test > Form 1`] = ` }/api/v1/form-submit?apiKey=\${builder.apiKey}&to=\${btoa( sendSubmissionsToEmail || \\"\\" )}&name=\${encodeURIComponent(name || \\"\\")}\`; - fetch( - sendSubmissionsTo === \\"email\\" ? formUrl : action, - /* TODO: throw error if no action URL */ - { - body, - headers, - method: method || \\"post\\", - } - ).then( + fetch(sendSubmissionsTo === \\"email\\" ? formUrl : action, { + body, + headers, + method: method || \\"post\\", + }).then( async (res) => { let body; const contentType = res.headers.get(\\"content-type\\"); @@ -797,13 +781,10 @@ exports[`Svelte > jsx > Javascript Test > Form 1`] = ` } if (!res.ok && errorMessagePath) { - /* TODO: allow supplying an error formatter function */ let message = get(body, errorMessagePath); if (message) { if (typeof message !== \\"string\\") { - /* TODO: ideally convert json to yaml so it woul dbe like - error: - email has been taken */ message = JSON.stringify(message); } @@ -828,13 +809,11 @@ exports[`Svelte > jsx > Javascript Test > Form 1`] = ` if (submitSuccessEvent.defaultPrevented) { return; } - /* TODO: option to turn this on/off? */ if (resetFormOnSubmit !== false) { formRef.reset(); } } - /* TODO: client side route event first that can be preventDefaulted */ if (successUrl) { if (formRef) { @@ -947,7 +926,6 @@ exports[`Svelte > jsx > Javascript Test > Image 1`] = ` } function useLazyLoading() { - // TODO: Add more checks here, like testing for real web browsers return !!lazy && isBrowser(); } @@ -1643,6 +1621,36 @@ exports[`Svelte > jsx > Javascript Test > classState 1`] = ` " `; +exports[`Svelte > jsx > Javascript Test > commentsWithQuotes 1`] = ` +" + +
+ { + doSomething(); + }} + bind:value={name} + /> + Hello + {name}! I can run in React, Vue, Solid, or Liquid! +
+ +" +`; + exports[`Svelte > jsx > Javascript Test > componentWithContext 1`] = ` " + +
+ { + doSomething(); + }} + bind:value={name} + /> + Hello + {name}! I can run in React, Vue, Solid, or Liquid! +
+ +" +`; + exports[`Svelte > jsx > Typescript Test > componentWithContext 1`] = ` " + +" +`; + exports[`Vue > jsx > Javascript Test > componentWithContext 1`] = ` "