Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
.idea
lib

# Logs
logs
*.log
Expand Down
67 changes: 60 additions & 7 deletions __tests__/usage.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,64 @@
const swc = require('@swc/core')
const path = require('path')
const ConsoleStripper = require(path.join(__dirname, '../lib/index.js')).default;
const JSXConditionTransformPlugin = require(path.join(__dirname, '../lib/index.js')).default;

it('should strip console call', () => {
const output = swc.transformSync(`console.log('Foo')`, {
plugin: (m) => (new ConsoleStripper()).visitModule(m),
});
it('should convert jsx x-if condition', () => {

expect(output.code.replace(/\n/g, '')).toBe('void 0;')
})
let input = `import { createElement } from 'react';
function Foo(props) {
return (
<View {...props} x-if={true} className="container">
<View x-if={condition}>First</View>
</View>
)
}`;

const transformedOutput = swc.transformSync(input, {
jsc: {
parser: {
jsx: true
},
},
plugin: JSXConditionTransformPlugin
});

const output = `function _extends() {
_extends = Object.assign || function(target) {
for(var i = 1; i < arguments.length; i++){
var source = arguments[i];
for(var key in source){
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
import { createCondition as __create_condition__ } from "babel-runtime-jsx-plus";
import { createElement } from "react";
function Foo(props) {
return __create_condition__([
function() {
return true;
},
function() {
return React.createElement(View, _extends({}, props, {
className: "container"
}), __create_condition__([
function() {
return condition;
},
function() {
return React.createElement(View, null, "First");
}
]));
}
]);
}
`;

expect(transformedOutput.code).toBe(output);
});
5 changes: 0 additions & 5 deletions lib/index.d.ts

This file was deleted.

29 changes: 0 additions & 29 deletions lib/index.js

This file was deleted.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
{
"name": "swc-plugin-transform-jsx-condition",
"version": "0.1.0",
"version": "0.1.0-beta.1",
"description": "Support of transform jsx condition directive based on SWC",
"main": "lib/index.js",
"scripts": {
"build": "tsc -d",
"build:watch": "tsc -d --watch",
"test": "jest"
},
"types": "./lib/index.d.ts",
Expand Down
162 changes: 145 additions & 17 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,155 @@
import {CallExpression, Expression} from '@swc/core';
import Visitor from '@swc/core/Visitor'
import {
Expression,
JSXElement, Program, Statement,
} from '@swc/core';
import Visitor from "@swc/core/Visitor";
import {
ExprOrSpread
} from "@swc/core/types";
import {
buildArrayExpression,
buildArrowFunctionExpression, buildCallExpression, buildIdentifier, buildImportDeclaration,
buildJSXElement,
buildJSXExpressionContainer, buildNamedImportSpecifier, buildNullLiteral, buildStringLiteral
} from "./utils";

export default class ConsoleStripper extends Visitor {
visitCallExpression(e: CallExpression): Expression {
if (e.callee.type !== 'MemberExpression') {
return e;
enum JSXConditionType {
if = 'x-if',
else = 'x-else',
elseif = 'x-elseif'
}

function isJSXCondition(n: JSXElement) {
let opening = n.opening;
let openingAttributes = opening.attributes;

if (openingAttributes) {
for (let attribute of openingAttributes) {
if (attribute.type === 'JSXAttribute' && attribute.name.type === 'Identifier') {
switch (attribute.name.value) {
case JSXConditionType.if:
case JSXConditionType.else:
case JSXConditionType.elseif: {
return true;
}
}
}
}
}
return false;
}

type JSXCondition = {
type: 'x-if' | 'x-elseif' | 'x-else',
expression?: Expression;
}

if (e.callee.object.type === 'Identifier' && e.callee.object.value === 'console') {
if (e.callee.property.type === 'Identifier') {
return {
type: "UnaryExpression",
span: e.span,
operator: 'void',
argument: {
type: 'NumericLiteral',
span: e.span,
value: 0
function getJSXCondition(n: JSXElement): JSXCondition | undefined {
let opening = n.opening;
let openingAttributes = opening.attributes;
if (!openingAttributes) return undefined;

for (let attribute of openingAttributes) {
if (attribute.type === 'JSXAttribute' && attribute.name.type === 'Identifier') {
switch (attribute.name.value) {
case JSXConditionType.if:
case JSXConditionType.else:
case JSXConditionType.elseif: {
if (attribute.value?.type === 'JSXExpressionContainer') {
return {
type: attribute.name.value,
expression: attribute.value.expression
};
}
if (attribute.value === null) {
return {
type: attribute.name.value,
expression: buildNullLiteral()
}
}
}
}
}
}

return undefined;
}

return e
function JSXConditionToStandard(n: JSXElement) {
let openingAttributes = n.opening.attributes;

if (openingAttributes) {
openingAttributes = openingAttributes.filter((attribute) => {
if (attribute.type === 'JSXAttribute' && attribute.name.type === 'Identifier') {
switch (attribute.name.value) {
case JSXConditionType.if:
case JSXConditionType.else:
case JSXConditionType.elseif: {
return false;
}
}
}
return true;
});
}
return buildJSXElement({
...n.opening,
attributes: openingAttributes
}, n.children, n.closing)
}


function transformJSXCondition(n: JSXElement, isChild: boolean): JSXElement {
n.children = n.children.map((n) => {
if (n.type === 'JSXElement') {
return transformJSXCondition(n, true);
}
return n;
});

if (!isJSXCondition(n)) {
return n;
}

let condition = getJSXCondition(n)!;

let elements: ExprOrSpread[] = [
{
expression: buildArrowFunctionExpression([], getJSXCondition(n)!.expression!)
},
{
expression: buildArrowFunctionExpression([], JSXConditionToStandard(n))
}
];

let body = buildCallExpression(buildIdentifier('__create_condition__', false), [
{
expression: buildArrayExpression(elements)
}
]) as any;

return isChild ? buildJSXExpressionContainer(body) : body;
}

class JSXConditionTransformer extends Visitor {
visitJSXElement(n: JSXElement): JSXElement {
if (isJSXCondition(n)) {
return transformJSXCondition(n, false);
}

return n;
}
}

export default function JSXConditionTransformPlugin(m: Program): Program {
let result = new JSXConditionTransformer().visitProgram(m);
let babelImport = buildImportDeclaration([
buildNamedImportSpecifier(
buildIdentifier('__create_condition__', false),
buildIdentifier('createCondition', false)
)
], buildStringLiteral('babel-runtime-jsx-plus'));
result.body.unshift(babelImport as any);

return result;
}
Loading