-
Notifications
You must be signed in to change notification settings - Fork 138
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Preserve jsx #559
Comments
Sorry for the delay in getting to this. Unfortunately that's not configurable right now, though you can hack it yourself by calling into Sucrase internals. Here's an example that copies and tweaks the import {HelperManager} from "sucrase/dist/HelperManager";
import identifyShadowedGlobals from "sucrase/dist/identifyShadowedGlobals";
import NameManager from "sucrase/dist/NameManager";
import {parse} from "sucrase/dist/parser";
import TokenProcessor from "sucrase/dist/TokenProcessor";
import RootTransformer from "sucrase/dist/transformers/RootTransformer";
import getTSImportedNames from "sucrase/dist/util/getTSImportedNames";
function transformTSOnly(code: string): string {
const {tokens, scopes} = parse(
code,
true /* isJSXEnabled */,
true /* isTypeScriptEnabled */,
false /* isFlowEnabled */,
);
const nameManager = new NameManager(code, tokens);
const helperManager = new HelperManager(nameManager);
const tokenProcessor = new TokenProcessor(code, tokens, false /* isFlowEnabled */, helperManager);
identifyShadowedGlobals(tokenProcessor, scopes, getTSImportedNames(tokenProcessor));
const sucraseContext = {
tokenProcessor,
scopes,
nameManager,
importProcessor: null,
helperManager,
};
const transformer = new RootTransformer(sucraseContext, ["typescript"], false, {
transforms: ["typescript"],
});
return transformer.transform();
}
console.log(
transformTSOnly(`
import React from 'react';
interface LabelProps {
name: string;
}
function Label({name}: LabelProps): JSX.Element {
return (
<div className="foo">
Hello {name}
</div>
);
}
`),
); Of course, any upgrade to Sucrase should be considered a potentially-breaking change for code like this. What's your use case? And did I understand your goal correctly? Generally I think of Sucrase as targeting browsers and node, where AFAIU there's never native support for JSX. I could see two ways to add built-in support here:
|
if I were to speculate here, I think the use case would be preserving the JSX/TSX so it can be handed off to another transpiler, there are many babel plugins alone that take advantage of the JSX syntax tree and do transformations, but pre-transform the JSX into JS, you lose access to all that. I reckon this is the number1 motivator for a change like this. |
yepp, in my case: solidjs. react has no monopoly on jsx ; ) example: convert tsx to jsx files in solid-blocks
fixed version/*
tsx2jsx.mjs
convert typescript to vanillajs (actual javascript)
npm i -D sucrase tiny-glob
node tsx2jsx.mjs
https://github.com/alangpierce/sucrase/issues/559
https://github.com/milahu/random/blob/master/javascript/tsx2jsx.mjs
*/
import fs from 'fs';
import {spawnSync} from 'child_process';
import glob from 'tiny-glob';
import {HelperManager} from "sucrase/dist/HelperManager.js";
import identifyShadowedGlobalsModule from "sucrase/dist/identifyShadowedGlobals.js";
import NameManagerModule from "sucrase/dist/NameManager.js";
import {parse} from "sucrase/dist/parser/index.js";
import TokenProcessorModule from "sucrase/dist/TokenProcessor.js";
import RootTransformerModule from "sucrase/dist/transformers/RootTransformer.js";
import getTSImportedNamesModule from "sucrase/dist/util/getTSImportedNames.js";
// workaround ...
// TypeError: NameManager is not a constructor
const NameManager = NameManagerModule.default;
const TokenProcessor = TokenProcessorModule.default;
const getTSImportedNames = getTSImportedNamesModule.default;
const identifyShadowedGlobals = identifyShadowedGlobalsModule.default;
const RootTransformer = RootTransformerModule.default;
function transformTSOnly(code) {
const {tokens, scopes} = parse(
code,
true /* isJSXEnabled */,
true /* isTypeScriptEnabled */,
false /* isFlowEnabled */,
);
const nameManager = new NameManager(code, tokens);
const helperManager = new HelperManager(nameManager);
const tokenProcessor = new TokenProcessor(code, tokens, false /* isFlowEnabled */, helperManager);
identifyShadowedGlobals(tokenProcessor, scopes, getTSImportedNames(tokenProcessor));
const sucraseContext = {
tokenProcessor,
scopes,
nameManager,
importProcessor: null,
helperManager,
};
// https://github.com/alangpierce/sucrase#transforms
const sucraseOptions = {
transforms: ["typescript"],
disableESTransforms: true, // keep modern javascript: Optional chaining, Nullish coalescing, ...
};
const transformer = new RootTransformer(sucraseContext, ["typescript"], false, sucraseOptions);
return transformer.transform();
}
const gitDirty = spawnSync('git', ['diff-index', 'HEAD', '--'], { encoding: 'utf8' }).stdout;
if (gitDirty != '') {
console.log(`error: git is dirty:\n\n${gitDirty}`);
process.exit(1);
}
const todoTransform = [];
//for (const fi of await glob('./src/**/*.tsx')) { // convert all files in src/ folder
for (const fi of await glob('./**/*.tsx')) { // convert all files in workdir
const fo = fi.slice(0, -4) + '.jsx';
console.log(`rename: ${fi} -> ${fo}`);
spawnSync('git', ['mv', '-v', fi, fo]); // rename
todoTransform.push([fi, fo]);
}
spawnSync('git', ['commit', '-m', 'tsx2jsx: rename']); // commit
for (const [fi, fo] of todoTransform) {
console.log(`transform: ${fi} -> ${fo}`);
const i = fs.readFileSync(fo, 'utf8');
const o = transformTSOnly(i);
// always do your backups :P
fs.writeFileSync(fo, o, 'utf8'); // replace
spawnSync('git', ['add', fo]); // add
}
spawnSync('git', ['commit', '-m', 'tsx2jsx: transform']); // commit
console.log(`
next steps:
git diff HEAD^ # inspect transform
git reset --hard HEAD~2 # undo transform + rename
`); edit: i was looking for alternative: tsconfig.json{
"include": [ "./src" ],
"compilerOptions": {
"outDir": "./tsc-out",
"target": "es2020",
"module": "es2020",
"strict": false,
"esModuleInterop": true,
"jsx": "preserve",
"jsxImportSource": "solid-js",
"moduleResolution": "node"
}
} |
To strip TypeScript:
You get ES6 without TypeScript, without needing the TypeScript compiler or installing all the project dependencies 🥳 |
Fixes #780 Fixes #559 This PR adds support for preserving JSX as-is. Since the parser need to know if JSX is enabled or not, preserving JSX is accomplished by keeping `"jsx"` as a transform, but setting the `jsxRuntime` mode to `"preserve"`. Most of the details just work: the imports and typescript transform are mostly able to remove type syntax and transform imported names properly. The one missing case was handling situations like `<div>`, which is not actually an identifier access and thus should not be transformed by the imports transform. This required adding a special case to the parser to remove the identifier role in that case. For now, the ts-node integration does not recognize this option because Node wouldn't be able to recognize JSX anyway.
I just released support for this in 3.31.0. To preserve JSX, include |
Is there a way for sucrase to parse jsx but not transform it? Thanks
The text was updated successfully, but these errors were encountered: