Skip to content

Commit

Permalink
refactor createPartialCustomElement to createPartialImportNode. f…
Browse files Browse the repository at this point in the history
…ix firefox has problems with `img`s and `loading="lazy"`. see solidjs/solid#1828
  • Loading branch information
titoBouzout committed May 27, 2024
1 parent 1b9dbd5 commit 0ca1176
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 23 deletions.
2 changes: 1 addition & 1 deletion babel-preset/plugin.cjs
Original file line number Diff line number Diff line change
@@ -1 +1 @@
"use strict";var e=require("@babel/helper-plugin-utils"),t=require("@babel/core"),n=require("@babel/plugin-syntax-jsx"),r=require("@babel/helper-module-imports"),s=require("validate-html-nesting");function i(e,t){return e.get(`@babel/plugin-pota-jsx/${t}`)}function a(e,t,n){return e.set(`@babel/plugin-pota-jsx/${t}`,n)}const o=(e,n,s)=>a(n,"id/"+s,function(e,n,s){return()=>{let o=i(n,`imports/${s}`);return o?t.types.cloneNode(o):(o=r.addNamed(e,s,"pota/src/renderer/@main.js",{importedInterop:"uncompiled",importPosition:"after"}),a(n,`imports/${s}`,o),o)}}(e,n,s)),p=(e,n,r)=>t.types.callExpression(i(e,`id/${n}`)(),r),u=(e,n)=>t.types.callExpression(t.types.identifier(e),n);function l(e,t){throw e.buildCodeFrameError(t)}function c(e){const n=e.get("openingElement"),r=m(n.node.name,n.node);let s;return t.types.isIdentifier(r)?s=r.name:t.types.isStringLiteral(r)&&(s=r.value),!!t.types.react.isCompatTag(s)&&s}function m(e,n){return t.types.isJSXIdentifier(e)?"this"===e.name&&t.types.isReferenced(e,n)?t.types.thisExpression():t.types.isValidIdentifier(e.name,!1)?(e.type="Identifier",e):t.types.stringLiteral(e.name):t.types.isJSXMemberExpression(e)?t.types.memberExpression(m(e.object,e),m(e.property,e)):t.types.isJSXNamespacedName(e)?t.types.stringLiteral(`${e.namespace.name}:${e.name.name}`):e}function d(e,n){const r=e.reduce(g,[]);if(n&&n.length>0&&r.push(function(e){let n;if(1===e.length)n=e[0];else{if(!(e.length>1))return;n=t.types.arrayExpression(e)}return t.types.objectProperty(t.types.identifier("children"),n)}(n)),r.length)return t.types.objectExpression(function(e){e&&e.sort(((e,t)=>"children"===e.key?.name?2:e.key?.name?.localeCompare(t.key?.name)));return e}(r))}function g(e,n){if(n="node"in n?n.node:n,t.types.isJSXSpreadAttribute(n)){const r=n.argument;return t.types.isObjectExpression(r)&&!f(r)?e.push(...r.properties):e.push(t.types.spreadElement(r)),e}const r=(s=n.value||t.types.booleanLiteral(!0),t.types.isJSXExpressionContainer(s)?s.expression:s);var s,i;t.types.isStringLiteral(r)&&!t.types.isJSXExpressionContainer(n.value)&&(r.value=r.value.replace(/\n\s+/g," "),null==(i=r.extra)||delete i.raw);return t.types.isJSXNamespacedName(n.name)?n.name=t.types.stringLiteral(n.name.namespace.name+":"+n.name.name.name):t.types.isValidIdentifier(n.name.name,!1)?n.name.type="Identifier":n.name=t.types.stringLiteral(n.name.name),e.push(t.types.inherits(t.types.objectProperty(n.name,r),n)),e}const f=e=>e.properties.some((e=>t.types.isObjectProperty(e,{computed:!1,shorthand:!1})&&(t.types.isIdentifier(e.key,{name:"__proto__"})||t.types.isStringLiteral(e.key,{value:"__proto__"}))));function y(e,t,n){""!==n.trim()?/"|'|=|<|>|`|\s/.test(n)?e.content+=" "+t+"='"+x(n)+"'":e.content+=" "+t+"="+n:e.content+=" "+t}function h(e){return t.types.isStringLiteral(e.value.expression)||t.types.isNumericLiteral(e.value.expression)?String(e.value.expression.value):String(e.value.value)}function v(e,n){return t.types.jSXAttribute(t.types.jSXIdentifier(e),t.types.stringLiteral(n))}const x=(()=>{const e={"'":"&#39;",'"':"&quot;"},t=/['"]/g,n=t=>e[t];return function(e){return e.replace(t,n)}})(),b=new Set(["area","base","basefont","bgsound","br","col","command","embed","frame","hr","image","img","input","keygen","link","menuitem","meta","param","source","track","wbr"]);function S(e){return b.has(e.toLowerCase())}function E(e,t,n){s.isValidHTMLNesting(e,t)||l(n._path,`Invalid HTML: <${e}> cannot be child of <${t}>`)}const L=(()=>{const e={"&":"&amp;","<":"&lt;",">":"&gt;","'":"&#39;",'"':"&quot;"},t=/[&<>'"]/g,n=t=>e[t];return function(e){return e.replace(t,n)}})();function C(e,n){let r=!1;const s=c(e);let i=s.includes("-");const a={tagName:s,content:`<${s} pota`,props:[]},o=[];for(const n of e.get("openingElement").get("attributes")){if(n.isJSXAttribute()&&t.types.isJSXIdentifier(n.node.name)){const e=n.node.name.name;if(i=i||"is"===e,"xmlns"===e&&(r=!0),"xmlns"!==e&&(u=n.node,t.types.isStringLiteral(u.value)||t.types.isNumericLiteral(u.value)||t.types.isStringLiteral(u.value?.expression)||t.types.isNumericLiteral(u.value?.expression))){y(a,e,h(n.node));continue}}o.push(n)}var u;if(!r)switch(s){case"svg":o.push(v("xmlns","http://www.w3.org/2000/svg")),r=!0;break;case"math":o.push(v("xmlns","http://www.w3.org/1998/Math/MathML")),r=!0;break;case"foreignObject":o.push(v("xmlns","http://www.w3.org/1999/xhtml")),r=!0}S(s)?a.content+=" />":a.content+=">";let l=t.types.react.buildChildren(e.node);!function(e,t){for(const n of t)N(n)&&E(e,n.tagName,n)}(a.tagName,l),l=function(e,t){const n=[];for(let r=0;r<e.length;r++){const s=e[r];if(J(s))t.content+=P(s),n.push(s);else{if(!N(s))break;t.content+=X(s),s.arguments[1].elements.length&&t.props.push(...s.arguments[1].elements),n.push(s)}}return e.filter((e=>!n.includes(e)))}(l,a),l=w(l);const m=d(o,l);m?a.props.unshift(m):a.content=a.content.replace(/^<([^\s]+) pota/,"<$1"),S(s)||(a.content+=`</${s}>`);const g=p(n,i?"createPartialCustomElement":"createPartial",[t.types.stringLiteral(a.content),t.types.arrayExpression(a.props)]);return g.isXML=r,g.isCustomElement=i,g.isPartial=!0,g.tagName=s,g._path=e,g}function N(e){return e.isPartial&&!e.isXML&&!e.isCustomElement}function X(e){return e.arguments[0].value}function w(e){const t=[];let n=0,r=e[n],s=e[++n];for(;r&&s;){if(J(r)){if(J(s)){r.value+=P(s),t.push(s),s=e[++n];continue}if(N(s)){s.arguments[0].value=P(r)+X(s),t.push(r),r=s,s=e[++n];continue}}if(N(r)){if(J(s)){r.arguments[0].value+=P(s),t.push(s),s=e[++n];continue}if(N(s)){r.arguments[0].value+=X(s),s.arguments[1].elements.length&&r.arguments[1].elements.push(...s.arguments[1].elements),t.push(s),s=e[++n];continue}}r=s,s=e[++n]}return e.filter((e=>!t.includes(e)))}function j(e){return w(t.types.react.buildChildren(e.node))}function J(e){return t.types.isStringLiteral(e)||t.types.isNumericLiteral(e)||t.types.isStringLiteral(e.value?.expression)||t.types.isNumericLiteral(e.value?.expression)}function P(e){return t.types.isStringLiteral(e.value?.expression)||t.types.isNumericLiteral(e.value?.expression)?L(e.value?.expression.value):L(e.value)}function I(e,n){const r=d(e.get("openingElement").get("attributes"),j(e)),s=function(e){const t=e.get("openingElement");return m(t.node.name,t.node)}(e),i=function(e){const n=e.get("openingElement"),r=m(n.node.name,n.node);if(t.types.isIdentifier(r))return r.name;if(t.types.isStringLiteral(r))return r.value;if(t.types.isMemberExpression(r))return r.object.name+"."+r.property.name;throw console.log(r),e.buildCodeFrameError("Cannot figure out `tagName` for JSX function.")}(e),a=e.scope;a.pota=a.pota||{partials:{},components:{},files:{}};const o=a.pota;return o.components[i]||(o.components[i]=a.generateUidIdentifier("_"+i),a.push({id:o.components[i],init:p(n,"createComponent",[s])})),u(o.components[i].name,r?[r]:[])}var k=function({name:r}){return e.declare(((e,s)=>({name:r,inherits:n.default,visitor:{JSXNamespacedName(e){},JSXSpreadChild(e){l(e,"Spread children are not supported.")},Program:{enter(e,t){t.pota={partials:{},components:{},files:{}},o(e,t,"createPartial"),o(e,t,"createPartialCustomElement"),o(e,t,"createComponent")},exit(e,n){e.traverse({CallExpression(e,n){if(e.node.isPartial){const r=function(e,n){0===e.node.arguments[1].elements.length&&function(e,t){const n=e.indexOf(t);-1!==n&&e.splice(n,1)}(e.node.arguments,e.node.arguments[1]);const r=X(e.node),s=e.scope.getProgramParent();s.pota=s.pota||{partials:{},components:{},files:{}};const i=s.pota;if(!i.partials[r]){i.partials[r]=s.generateUidIdentifier("_partial");const a=[e.node.arguments[0]],o=e.node.arguments[1]?e.node.arguments[1].elements[0].properties.find((e=>"xmlns"===e?.key?.name))?.value?.value:void 0;o&&a.push(t.types.stringLiteral(o)),s.push({id:i.partials[r],init:p(n,e.node.isCustomElement?"createPartialCustomElement":"createPartial",a)})}return u(i.partials[r].name,e.node.arguments[1]?[e.node.arguments[1]]:[])}(e,n);e.replaceWith(t.types.inherits(r,e.node))}}},n)}},JSXFragment:{exit(e,n){const r=function(e,n){const r=j(e);return 1===r.length?r[0]:r.length>1?t.types.arrayExpression(r):void 0}(e);e.replaceWith(t.types.inherits(r,e.node))}},JSXElement:{exit(e,n){const r=c(e)?C(e,n):I(e,n);e.replaceWith(t.types.inherits(r,e.node))}},JSXAttribute(e){t.types.isJSXElement(e.node.value)&&(e.node.value=t.types.jsxExpressionContainer(e.node.value))}}})))}({name:"transform-pota-jsx"});module.exports=k;
"use strict";var e=require("@babel/helper-plugin-utils"),t=require("@babel/core"),n=require("@babel/plugin-syntax-jsx"),r=require("@babel/helper-module-imports"),s=require("validate-html-nesting");function i(e,t){return e.get(`@babel/plugin-pota-jsx/${t}`)}function a(e,t,n){return e.set(`@babel/plugin-pota-jsx/${t}`,n)}const o=(e,n,s)=>a(n,"id/"+s,function(e,n,s){return()=>{let o=i(n,`imports/${s}`);return o?t.types.cloneNode(o):(o=r.addNamed(e,s,"pota/src/renderer/@main.js",{importedInterop:"uncompiled",importPosition:"after"}),a(n,`imports/${s}`,o),o)}}(e,n,s)),p=(e,n,r)=>t.types.callExpression(i(e,`id/${n}`)(),r),u=(e,n)=>t.types.callExpression(t.types.identifier(e),n);function l(e,t){throw e.buildCodeFrameError(t)}function c(e){const n=e.get("openingElement"),r=m(n.node.name,n.node);let s;return t.types.isIdentifier(r)?s=r.name:t.types.isStringLiteral(r)&&(s=r.value),!!t.types.react.isCompatTag(s)&&s}function m(e,n){return t.types.isJSXIdentifier(e)?"this"===e.name&&t.types.isReferenced(e,n)?t.types.thisExpression():t.types.isValidIdentifier(e.name,!1)?(e.type="Identifier",e):t.types.stringLiteral(e.name):t.types.isJSXMemberExpression(e)?t.types.memberExpression(m(e.object,e),m(e.property,e)):t.types.isJSXNamespacedName(e)?t.types.stringLiteral(`${e.namespace.name}:${e.name.name}`):e}function d(e,n){const r=e.reduce(g,[]);if(n&&n.length>0&&r.push(function(e){let n;if(1===e.length)n=e[0];else{if(!(e.length>1))return;n=t.types.arrayExpression(e)}return t.types.objectProperty(t.types.identifier("children"),n)}(n)),r.length)return t.types.objectExpression(function(e){e&&e.sort(((e,t)=>"children"===e.key?.name?2:e.key?.name?.localeCompare(t.key?.name)));return e}(r))}function g(e,n){if(n="node"in n?n.node:n,t.types.isJSXSpreadAttribute(n)){const r=n.argument;return t.types.isObjectExpression(r)&&!f(r)?e.push(...r.properties):e.push(t.types.spreadElement(r)),e}const r=(s=n.value||t.types.booleanLiteral(!0),t.types.isJSXExpressionContainer(s)?s.expression:s);var s,i;t.types.isStringLiteral(r)&&!t.types.isJSXExpressionContainer(n.value)&&(r.value=r.value.replace(/\n\s+/g," "),null==(i=r.extra)||delete i.raw);return t.types.isJSXNamespacedName(n.name)?n.name=t.types.stringLiteral(n.name.namespace.name+":"+n.name.name.name):t.types.isValidIdentifier(n.name.name,!1)?n.name.type="Identifier":n.name=t.types.stringLiteral(n.name.name),e.push(t.types.inherits(t.types.objectProperty(n.name,r),n)),e}const f=e=>e.properties.some((e=>t.types.isObjectProperty(e,{computed:!1,shorthand:!1})&&(t.types.isIdentifier(e.key,{name:"__proto__"})||t.types.isStringLiteral(e.key,{value:"__proto__"}))));function y(e,t,n){""!==n.trim()?/"|'|=|<|>|`|\s/.test(n)?e.content+=" "+t+"='"+x(n)+"'":e.content+=" "+t+"="+n:e.content+=" "+t}function h(e){return t.types.isStringLiteral(e.value.expression)||t.types.isNumericLiteral(e.value.expression)?String(e.value.expression.value):String(e.value.value)}function v(e,n){return t.types.jSXAttribute(t.types.jSXIdentifier(e),t.types.stringLiteral(n))}const x=(()=>{const e={"'":"&#39;",'"':"&quot;"},t=/['"]/g,n=t=>e[t];return function(e){return e.replace(t,n)}})(),b=new Set(["area","base","basefont","bgsound","br","col","command","embed","frame","hr","image","img","input","keygen","link","menuitem","meta","param","source","track","wbr"]);function S(e){return b.has(e.toLowerCase())}function L(e,t,n){s.isValidHTMLNesting(e,t)||l(n._path,`Invalid HTML: <${e}> cannot be child of <${t}>`)}const N=(()=>{const e={"&":"&amp;","<":"&lt;",">":"&gt;","'":"&#39;",'"':"&quot;"},t=/[&<>'"]/g,n=t=>e[t];return function(e){return e.replace(t,n)}})();function E(e,n){let r=!1;const s=c(e);let i=s.includes("-");const a={tagName:s,content:`<${s} pota`,props:[]},o=[];for(const n of e.get("openingElement").get("attributes")){if(n.isJSXAttribute()&&t.types.isJSXIdentifier(n.node.name)){const e=n.node.name.name;if(i=i||"is"===e||"img"===s&&"loading"===e,"xmlns"===e&&(r=!0),"xmlns"!==e&&(u=n.node,t.types.isStringLiteral(u.value)||t.types.isNumericLiteral(u.value)||t.types.isStringLiteral(u.value?.expression)||t.types.isNumericLiteral(u.value?.expression))){y(a,e,h(n.node));continue}}o.push(n)}var u;if(!r)switch(s){case"svg":o.push(v("xmlns","http://www.w3.org/2000/svg")),r=!0;break;case"math":o.push(v("xmlns","http://www.w3.org/1998/Math/MathML")),r=!0;break;case"foreignObject":o.push(v("xmlns","http://www.w3.org/1999/xhtml")),r=!0}S(s)?a.content+=" />":a.content+=">";let l=t.types.react.buildChildren(e.node);!function(e,t){for(const n of t)I(n)&&L(e,n.tagName,n)}(a.tagName,l),l=function(e,t){const n=[];for(let r=0;r<e.length;r++){const s=e[r];if(J(s))t.content+=P(s),n.push(s);else{if(!I(s))break;t.content+=X(s),s.arguments[1].elements.length&&t.props.push(...s.arguments[1].elements),n.push(s)}}return e.filter((e=>!n.includes(e)))}(l,a),l=w(l);const m=d(o,l);m?a.props.unshift(m):a.content=a.content.replace(/^<([^\s]+) pota/,"<$1"),S(s)||(a.content+=`</${s}>`);const g=p(n,i?"createPartialImportNode":"createPartial",[t.types.stringLiteral(a.content),t.types.arrayExpression(a.props)]);return g.isXML=r,g.isImportNode=i,g.isPartial=!0,g.tagName=s,g._path=e,g}function I(e){return e.isPartial&&!e.isXML&&!e.isImportNode}function X(e){return e.arguments[0].value}function w(e){const t=[];let n=0,r=e[n],s=e[++n];for(;r&&s;){if(J(r)){if(J(s)){r.value+=P(s),t.push(s),s=e[++n];continue}if(I(s)){s.arguments[0].value=P(r)+X(s),t.push(r),r=s,s=e[++n];continue}}if(I(r)){if(J(s)){r.arguments[0].value+=P(s),t.push(s),s=e[++n];continue}if(I(s)){r.arguments[0].value+=X(s),s.arguments[1].elements.length&&r.arguments[1].elements.push(...s.arguments[1].elements),t.push(s),s=e[++n];continue}}r=s,s=e[++n]}return e.filter((e=>!t.includes(e)))}function j(e){return w(t.types.react.buildChildren(e.node))}function J(e){return t.types.isStringLiteral(e)||t.types.isNumericLiteral(e)||t.types.isStringLiteral(e.value?.expression)||t.types.isNumericLiteral(e.value?.expression)}function P(e){return t.types.isStringLiteral(e.value?.expression)||t.types.isNumericLiteral(e.value?.expression)?N(e.value?.expression.value):N(e.value)}function C(e,n){const r=d(e.get("openingElement").get("attributes"),j(e)),s=function(e){const t=e.get("openingElement");return m(t.node.name,t.node)}(e),i=function(e){const n=e.get("openingElement"),r=m(n.node.name,n.node);if(t.types.isIdentifier(r))return r.name;if(t.types.isStringLiteral(r))return r.value;if(t.types.isMemberExpression(r))return r.object.name+"."+r.property.name;throw console.log(r),e.buildCodeFrameError("Cannot figure out `tagName` for JSX function.")}(e),a=e.scope;a.pota=a.pota||{partials:{},components:{},files:{}};const o=a.pota;return o.components[i]||(o.components[i]=a.generateUidIdentifier("_"+i),a.push({id:o.components[i],init:p(n,"createComponent",[s])})),u(o.components[i].name,r?[r]:[])}var k=function({name:r}){return e.declare(((e,s)=>({name:r,inherits:n.default,visitor:{JSXNamespacedName(e){},JSXSpreadChild(e){l(e,"Spread children are not supported.")},Program:{enter(e,t){t.pota={partials:{},components:{},files:{}},o(e,t,"createPartial"),o(e,t,"createPartialImportNode"),o(e,t,"createComponent")},exit(e,n){e.traverse({CallExpression(e,n){if(e.node.isPartial){const r=function(e,n){0===e.node.arguments[1].elements.length&&function(e,t){const n=e.indexOf(t);-1!==n&&e.splice(n,1)}(e.node.arguments,e.node.arguments[1]);const r=X(e.node),s=e.scope.getProgramParent();s.pota=s.pota||{partials:{},components:{},files:{}};const i=s.pota;if(!i.partials[r]){i.partials[r]=s.generateUidIdentifier("_partial");const a=[e.node.arguments[0]],o=e.node.arguments[1]?e.node.arguments[1].elements[0].properties.find((e=>"xmlns"===e?.key?.name))?.value?.value:void 0;o&&a.push(t.types.stringLiteral(o)),s.push({id:i.partials[r],init:p(n,e.node.isImportNode?"createPartialImportNode":"createPartial",a)})}return u(i.partials[r].name,e.node.arguments[1]?[e.node.arguments[1]]:[])}(e,n);e.replaceWith(t.types.inherits(r,e.node))}}},n)}},JSXFragment:{exit(e,n){const r=function(e,n){const r=j(e);return 1===r.length?r[0]:r.length>1?t.types.arrayExpression(r):void 0}(e);e.replaceWith(t.types.inherits(r,e.node))}},JSXElement:{exit(e,n){const r=c(e)?E(e,n):C(e,n);e.replaceWith(t.types.inherits(r,e.node))}},JSXAttribute(e){t.types.isJSXElement(e.node.value)&&(e.node.value=t.types.jsxExpressionContainer(e.node.value))}}})))}({name:"transform-pota-jsx"});module.exports=k;
2 changes: 1 addition & 1 deletion babel-preset/transform/@main.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export default function createPlugin({ name }) {
state.pota = { partials: {}, components: {}, files: {} }

createImport(path, state, 'createPartial')
createImport(path, state, 'createPartialCustomElement')
createImport(path, state, 'createPartialImportNode')
createImport(path, state, 'createComponent')

if (false && options?.development) {
Expand Down
21 changes: 13 additions & 8 deletions babel-preset/transform/partial.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ export function buildPartial(path, state) {

const tagName = getTagName(path)

// custom element
// custom elements

let isCustomElement = tagName.includes('-')
let isImportNode = tagName.includes('-')

// open opening tag

Expand All @@ -40,7 +40,12 @@ export function buildPartial(path, state) {
if (attr.isJSXAttribute() && t.isJSXIdentifier(attr.node.name)) {
const name = attr.node.name.name

isCustomElement = isCustomElement || name === 'is'
isImportNode =
isImportNode ||
// custom elements need `importNode`
name === 'is' ||
// Firefox needs `importNode` for images with loading="lazy"
(tagName === 'img' && name === 'loading')

if (name === 'xmlns') {
isXML = true
Expand Down Expand Up @@ -137,11 +142,11 @@ export function buildPartial(path, state) {

const partial = callFunctionImport(
state,
isCustomElement ? 'createPartialCustomElement' : 'createPartial',
isImportNode ? 'createPartialImportNode' : 'createPartial',
[t.stringLiteral(tag.content), t.arrayExpression(tag.props)],
)
partial.isXML = isXML
partial.isCustomElement = isCustomElement
partial.isImportNode = isImportNode
partial.isPartial = true
partial.tagName = tagName
/**
Expand Down Expand Up @@ -203,8 +208,8 @@ export function partialMerge(path, state) {
id: pota.partials[partial],
init: callFunctionImport(
state,
path.node.isCustomElement
? 'createPartialCustomElement'
path.node.isImportNode
? 'createPartialImportNode'
: 'createPartial',
args,
),
Expand All @@ -222,7 +227,7 @@ export function partialMerge(path, state) {
* `custom element`
*/
export function isPartialHTML(node) {
return node.isPartial && !node.isXML && !node.isCustomElement
return node.isPartial && !node.isXML && !node.isImportNode
}

/** Returns `true` when `node` is partial */
Expand Down
Loading

0 comments on commit 0ca1176

Please sign in to comment.