react-live-unplugin
Version:
Turn React components into live code blocks with ease.
75 lines (74 loc) • 2.49 kB
JavaScript
import { parse } from "@babel/parser";
import defu from "defu";
import { createUnplugin } from "unplugin";
import { interopDefault } from "./utils.js";
const randomSeed = Math.random().toString(36).slice(2);
const defaults = {
id: /\.live\.(j|t)sx$/,
exclude: /node_modules/,
reactLiveModulePath: "react-live-unplugin/default/ReactLive",
reactLiveExportName: "ReactLive"
};
const reactLiveUnpluginFactory = (options) => {
const mergedOptions = defu(options, defaults);
return {
name: "unplugin-react-live",
enforce: mergedOptions.enforce,
transform: {
filter: {
id: mergedOptions.id,
exclude: mergedOptions.exclude
},
async handler(code) {
const ast = parse(code, {
// parse in strict mode and allow module declarations
sourceType: "module",
plugins: ["jsx", "typescript"]
});
const imports = [];
const importsCode = [];
const traverse = await interopDefault(
interopDefault(import("@babel/traverse"))
);
traverse(ast, {
ImportDeclaration(path) {
importsCode.push(
code.substring(
path.node.start ?? 0,
path.node.end ?? code.length
)
);
path.node.specifiers.forEach((specifier) => {
imports.push(specifier.local.name);
});
}
});
const codeVarName = `code_${randomSeed}`;
const scopeVarName = `scope_${randomSeed}`;
const componentVarName = `Component_${randomSeed}`;
const codeCode = `const ${codeVarName} = ${JSON.stringify(code)};`;
const scopeCode = `const ${scopeVarName} = ${imports.length === 0 ? "{}" : `{ ${imports.join(", ")} }`};`;
const exportDemo = `export default function Demo() {
return (
<${componentVarName} scope={${scopeVarName}} code={${codeVarName}} />
);
}`;
const compiled = `import { ${mergedOptions.reactLiveExportName} as ${componentVarName} } from "${mergedOptions.reactLiveModulePath.replace(
/"/g,
'\\"'
)}";
${importsCode.join("\n")}
${codeCode}
${scopeCode}
${exportDemo}
`;
return compiled;
}
}
};
};
const reactLiveUnpluginInternal = createUnplugin(reactLiveUnpluginFactory);
const reactLiveUnplugin = Object.assign(reactLiveUnpluginInternal, {
defaults
});
export { reactLiveUnplugin };