unplugin-jsx-string
Advanced tools
Comparing version 0.0.0 to 0.1.0
@@ -25,5 +25,10 @@ var __create = Object.create; | ||
__export(src_exports, { | ||
convert: () => convert | ||
convert: () => convert, | ||
default: () => src_default | ||
}); | ||
module.exports = __toCommonJS(src_exports); | ||
var import_unplugin = require("unplugin"); | ||
var import_pluginutils = require("@rollup/pluginutils"); | ||
// src/core/convert.ts | ||
var import_parser = require("@babel/parser"); | ||
@@ -33,11 +38,18 @@ var import_traverse = __toESM(require("@babel/traverse")); | ||
var import_types = require("@babel/types"); | ||
var import_magic_string = __toESM(require("magic-string")); | ||
// src/utils.ts | ||
// src/core/utils.ts | ||
var import_json5 = __toESM(require("json5")); | ||
var import_lodash = require("lodash"); | ||
var styleToString = (styles) => Object.entries(styles).map(([key, value]) => `${(0, import_lodash.kebabCase)(key)}:${value}`).join(";"); | ||
var normalizeObjectString = (text) => JSON.stringify(import_json5.default.parse(text)); | ||
var normalizeObjectString = (text) => { | ||
try { | ||
return JSON.stringify(import_json5.default.parse(text)); | ||
} catch (err) { | ||
throw new SyntaxError(`Invalid attribute value: ${text}, error: ${err.message}`); | ||
} | ||
}; | ||
// src/index.ts | ||
var convert = (code) => { | ||
// src/core/convert.ts | ||
var convert = (code, debug) => { | ||
const ast = (0, import_parser.parse)(code, { | ||
@@ -50,10 +62,29 @@ sourceType: "module", | ||
CallExpression(path) { | ||
if (path.node.callee.type === "Identifier" && path.node.callee.name === "toString" && (0, import_types.isJSX)(path.node.arguments[0])) | ||
nodes.push(path.node.arguments[0]); | ||
if (path.node.callee.type === "Identifier" && path.node.callee.name === "jsxToString" && (0, import_types.isJSX)(path.node.arguments[0])) | ||
nodes.push([path.node.arguments[0], path.node]); | ||
} | ||
}); | ||
const result = nodes.map((node) => jsxToString(node)); | ||
for (const r of result) { | ||
console.log("\n", r); | ||
const s = new import_magic_string.default(code); | ||
for (const [node, expr] of nodes) { | ||
let str; | ||
if (!debug) { | ||
str = escapeString(jsxToString(node)); | ||
} else { | ||
try { | ||
str = escapeString(jsxToString(node)); | ||
} catch (err) { | ||
str = `(() => { throw new Error(${escapeString(err.toString())}) })()`; | ||
} | ||
} | ||
s.overwrite(expr.start, expr.end, str); | ||
} | ||
return { | ||
code: s.toString(), | ||
get map() { | ||
return s.generateMap(); | ||
} | ||
}; | ||
function escapeString(str) { | ||
return `\`${str.replaceAll("`", "\\`").replaceAll("$", "\\$")}\``; | ||
} | ||
function jsxToString(node) { | ||
@@ -67,3 +98,3 @@ var _a; | ||
case "JSXText": | ||
return node.value; | ||
return jsxTextToString(node); | ||
case "JSXEmptyExpression": | ||
@@ -74,5 +105,13 @@ return ((_a = node.innerComments) == null ? void 0 : _a.map((comment) => `<!--${comment.value}-->`).join("")) ?? ""; | ||
default: | ||
return ""; | ||
return notSupported(node); | ||
} | ||
} | ||
function jsxTextToString(node) { | ||
const texts = node.value.split("\n"); | ||
return texts.map((text, idx) => idx > 0 ? text.trim() : text).filter((line) => { | ||
if (line.trim().length === 0) | ||
return false; | ||
return true; | ||
}).join(" "); | ||
} | ||
function jsxElementToString(node) { | ||
@@ -83,3 +122,3 @@ if (node.openingElement.selfClosing) { | ||
const children = jsxChildrenToString(node.children); | ||
return `${jsxOpeningElementToString(node.openingElement)}${children}</${nameToString(node.closingElement.name)}>`; | ||
return `${jsxOpeningElementToString(node.openingElement)}${children}</${jsxNameToString(node.closingElement.name)}>`; | ||
} | ||
@@ -91,8 +130,12 @@ } | ||
function jsxOpeningElementToString(node) { | ||
let str = `<${nameToString(node.name)}`; | ||
for (const attr of node.attributes) { | ||
let str = `<${jsxNameToString(node.name)}`; | ||
const props = node.attributes.map((attr) => { | ||
if (attr.type === "JSXAttribute") { | ||
str += ` ${jsxAttributeToString(attr)}`; | ||
return jsxAttributeToString(attr); | ||
} else { | ||
return notSupported(node); | ||
} | ||
}).filter((x) => x !== void 0); | ||
if (props.length > 0) { | ||
str += ` ${props.join(" ")}`; | ||
} | ||
@@ -103,37 +146,45 @@ str += node.selfClosing ? "/>" : ">"; | ||
function jsxAttributeToString(node) { | ||
let str = nameToString(node.name); | ||
if (str === "className") | ||
str = "class"; | ||
else if (str.startsWith("on")) | ||
str = str.toLowerCase(); | ||
let name = jsxNameToString(node.name); | ||
if (name === "className") | ||
name = "class"; | ||
else if (name.startsWith("on")) | ||
name = name.toLowerCase(); | ||
let value; | ||
if (node.value) { | ||
let value = valueToString(node.value); | ||
if (str === "style") { | ||
value = styleToString(JSON.parse(value)); | ||
} else if (str === "class") { | ||
const classes = JSON.parse(value); | ||
if (Array.isArray(classes)) | ||
value = classes.join(" "); | ||
const rawValue = jsxAttrValueToString(node.value, name); | ||
if (rawValue === null) { | ||
return void 0; | ||
} | ||
str += `="${(0, import_entities.encode)(value)}"`; | ||
value = rawValue; | ||
} | ||
return str; | ||
return `${name}${value !== void 0 ? `="${(0, import_entities.encode)(value)}"` : ""}`; | ||
} | ||
function nameToString(node) { | ||
function jsxNameToString(node) { | ||
if (node.type === "JSXIdentifier") { | ||
return node.name; | ||
} else { | ||
return ""; | ||
return notSupported(node); | ||
} | ||
} | ||
function valueToString(node) { | ||
switch (node.type) { | ||
case "StringLiteral": | ||
return node.value; | ||
case "JSXExpressionContainer": | ||
return expressionToString(node.expression); | ||
default: | ||
console.log(node); | ||
return ""; | ||
function jsxAttrValueToString(node, key) { | ||
let value; | ||
if ((0, import_types.isJSXExpressionContainer)(node) && (0, import_types.isBooleanLiteral)(node.expression)) { | ||
value = node.expression.value ? void 0 : null; | ||
} else if ((0, import_types.isJSXExpressionContainer)(node) && (0, import_types.isFunction)(node.expression)) { | ||
value = getSource(node.expression.body); | ||
} else if ((0, import_types.isJSX)(node)) { | ||
value = jsxToString(node); | ||
if ((0, import_types.isJSXExpressionContainer)(node) && ((0, import_types.isObjectExpression)(node.expression) || (0, import_types.isArrayExpression)(node.expression))) { | ||
if (key === "style") { | ||
value = styleToString(JSON.parse(value)); | ||
} else if (key === "class") { | ||
const classes = JSON.parse(value); | ||
if (Array.isArray(classes)) | ||
value = classes.join(" "); | ||
} | ||
} | ||
} else if ((0, import_types.isStringLiteral)(node)) { | ||
value = node.value; | ||
} | ||
return value; | ||
} | ||
@@ -152,3 +203,3 @@ function expressionToString(node) { | ||
default: | ||
return getSource(node); | ||
return notSupported(node); | ||
} | ||
@@ -169,3 +220,3 @@ } | ||
if (node.expressions.length > 0) | ||
notSupported(); | ||
notSupported(node); | ||
return node.quasis.map((quasi) => quasi.value.cooked).join(""); | ||
@@ -176,6 +227,34 @@ } | ||
} | ||
function notSupported(node) { | ||
throw new Error(`not supported ${node.type}: ${getSource(node)}`); | ||
} | ||
}; | ||
function notSupported() { | ||
throw new Error("not supported"); | ||
// src/index.ts | ||
function resolveOption(options) { | ||
return { | ||
include: options.include || [/\.[jt]sx$/], | ||
exclude: options.exclude || void 0, | ||
debug: options.debug ?? false | ||
}; | ||
} | ||
var src_default = (0, import_unplugin.createUnplugin)((options = {}) => { | ||
const opt = resolveOption(options); | ||
const filter = (0, import_pluginutils.createFilter)(opt.include, opt.exclude); | ||
const name = "unplugin-jsx-string"; | ||
return { | ||
name, | ||
enforce: "pre", | ||
transformInclude(id) { | ||
return filter(id); | ||
}, | ||
transform(code) { | ||
try { | ||
return convert(code, opt.debug); | ||
} catch (err) { | ||
this.error(`${name} ${err}`); | ||
} | ||
} | ||
}; | ||
}); | ||
// Annotate the CommonJS export names for ESM import in node: | ||
@@ -182,0 +261,0 @@ 0 && (module.exports = { |
{ | ||
"name": "unplugin-jsx-string", | ||
"version": "0.0.0", | ||
"version": "0.1.0", | ||
"packageManager": "pnpm@7.1.9", | ||
@@ -20,10 +20,2 @@ "description": "My awesome typescript library", | ||
}, | ||
"scripts": { | ||
"lint": "eslint . --ext .vue,.js,.ts,.jsx,.tsx,.json,.md", | ||
"lint:fix": "pnpm run lint -- --fix", | ||
"build": "tsup && tsx scripts/postbuild.mts", | ||
"test": "vitest", | ||
"release": "bumpp --commit --push --tag && pnpm publish", | ||
"prepublishOnly": "pnpm run build" | ||
}, | ||
"dependencies": { | ||
@@ -33,5 +25,8 @@ "@babel/parser": "^7.18.4", | ||
"@babel/types": "^7.18.4", | ||
"@rollup/pluginutils": "^4.2.1", | ||
"entities": "^4.3.0", | ||
"json5": "^2.2.1", | ||
"lodash": "^4.17.21" | ||
"lodash": "^4.17.21", | ||
"magic-string": "^0.26.2", | ||
"unplugin": "^0.7.0" | ||
}, | ||
@@ -44,2 +39,3 @@ "devDependencies": { | ||
"@types/node": "*", | ||
"@types/react": "^18.0.12", | ||
"bumpp": "^7.1.1", | ||
@@ -57,3 +53,10 @@ "eslint": "^8.17.0", | ||
"node": ">=14.19.0" | ||
}, | ||
"scripts": { | ||
"lint": "eslint . --ext .vue,.js,.ts,.jsx,.tsx,.json,.md", | ||
"lint:fix": "pnpm run lint -- --fix", | ||
"build": "tsup && tsx scripts/postbuild.mts", | ||
"test": "vitest", | ||
"release": "bumpp --commit --push --tag && pnpm publish" | ||
} | ||
} | ||
} |
106
README.md
@@ -5,3 +5,3 @@ # unplugin-jsx-string [](https://npmjs.com/package/unplugin-jsx-string) | ||
## Install | ||
## Installation | ||
@@ -12,2 +12,106 @@ ```bash | ||
<details> | ||
<summary>Vite</summary><br> | ||
```ts | ||
// vite.config.ts | ||
import DefineOptions from 'unplugin-jsx-string/vite' | ||
import Vue from '@vitejs/plugin-vue' | ||
export default defineConfig({ | ||
plugins: [Vue(), DefineOptions()], | ||
}) | ||
``` | ||
<br></details> | ||
<details> | ||
<summary>Rollup</summary><br> | ||
```ts | ||
// rollup.config.js | ||
import DefineOptions from 'unplugin-jsx-string/rollup' | ||
export default { | ||
plugins: [DefineOptions()], // Must be before Vue plugin! | ||
} | ||
``` | ||
<br></details> | ||
<details> | ||
<summary>esbuild</summary><br> | ||
```ts | ||
// esbuild.config.js | ||
import { build } from 'esbuild' | ||
build({ | ||
plugins: [ | ||
require('unplugin-jsx-string/esbuild')(), // Must be before Vue plugin! | ||
], | ||
}) | ||
``` | ||
<br></details> | ||
<details> | ||
<summary>Webpack</summary><br> | ||
```ts | ||
// webpack.config.js | ||
module.exports = { | ||
/* ... */ | ||
plugins: [require('unplugin-jsx-string/webpack')()], | ||
} | ||
``` | ||
<br></details> | ||
<details> | ||
<summary>Vue CLI</summary><br> | ||
```ts | ||
// vue.config.js | ||
module.exports = { | ||
configureWebpack: { | ||
plugins: [require('unplugin-jsx-string/webpack')()], | ||
}, | ||
} | ||
``` | ||
<br></details> | ||
## Usage | ||
```tsx | ||
// basic usage | ||
jsxToString(<div>Hello</div>) | ||
// "<div>Hello</div>" | ||
// class list | ||
jsxToString(<div className={['bar', 'foo']} />) | ||
// `<div class="bar foo"/>` | ||
// styles | ||
jsxToString(<div style={{ color: 'red', textAlign: 'center' }} />) | ||
// `<div style="color:red;text-align:center"/>` | ||
// events | ||
jsxToString(<button onClick={() => 'clicked'}></button>) | ||
// "<button onclick="'clicked'"></button>" | ||
// children | ||
jsxToString( | ||
<div> | ||
<p>foo</p> | ||
<p>bar</p> | ||
<br /> | ||
<div /> | ||
123 | ||
</div> | ||
) | ||
// "<div><p>foo</p><p>bar</p><br/><div/>123</div>" | ||
``` | ||
## Sponsors | ||
@@ -14,0 +118,0 @@ |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
67542
27
1852
126
9
15
1
+ Added@rollup/pluginutils@^4.2.1
+ Addedmagic-string@^0.26.2
+ Addedunplugin@^0.7.0
+ Added@rollup/pluginutils@4.2.1(transitive)
+ Addedacorn@8.14.0(transitive)
+ Addedanymatch@3.1.3(transitive)
+ Addedbinary-extensions@2.3.0(transitive)
+ Addedbraces@3.0.3(transitive)
+ Addedchokidar@3.6.0(transitive)
+ Addedestree-walker@2.0.2(transitive)
+ Addedfill-range@7.1.1(transitive)
+ Addedfsevents@2.3.3(transitive)
+ Addedglob-parent@5.1.2(transitive)
+ Addedis-binary-path@2.1.0(transitive)
+ Addedis-extglob@2.1.1(transitive)
+ Addedis-glob@4.0.3(transitive)
+ Addedis-number@7.0.0(transitive)
+ Addedmagic-string@0.26.7(transitive)
+ Addednormalize-path@3.0.0(transitive)
+ Addedpicomatch@2.3.1(transitive)
+ Addedreaddirp@3.6.0(transitive)
+ Addedsourcemap-codec@1.4.8(transitive)
+ Addedto-regex-range@5.0.1(transitive)
+ Addedunplugin@0.7.2(transitive)
+ Addedwebpack-sources@3.2.3(transitive)
+ Addedwebpack-virtual-modules@0.4.6(transitive)