Skip to content

Commit 8a8f3a5

Browse files
authored
feat: allow @nativeclass transformations within solid userland code (#13)
1 parent a3e2c76 commit 8a8f3a5

File tree

3 files changed

+79
-0
lines changed

3 files changed

+79
-0
lines changed

babel-plugin-native-class.js

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
const ts = require('typescript');
2+
const parser = require('@babel/parser');
3+
4+
module.exports = function () {
5+
return {
6+
name: 'babel-plugin-native-class',
7+
visitor: {
8+
Program(path) {
9+
path.traverse({
10+
ClassDeclaration(classPath) {
11+
const { node } = classPath;
12+
13+
if (hasNativeClassDecorator(node)) {
14+
const tsSource = classPath.hub.file.code;
15+
const transpiledCode = transpileClassToES5(node, tsSource);
16+
17+
if (transpiledCode) {
18+
const babelAst = parser.parse(transpiledCode, {
19+
sourceType: 'module',
20+
plugins: ['typescript', 'decorators-legacy'],
21+
}).program.body;
22+
23+
classPath.replaceWithMultiple(babelAst);
24+
}
25+
}
26+
},
27+
});
28+
},
29+
},
30+
};
31+
};
32+
33+
function hasNativeClassDecorator(node) {
34+
return (
35+
node.decorators &&
36+
node.decorators.some(decorator => {
37+
const expression = decorator.expression;
38+
return expression.name === 'NativeClass' || (expression.callee && expression.callee.name === 'NativeClass');
39+
})
40+
);
41+
}
42+
43+
function removeNativeClassDecorator(code, className) {
44+
const decoratorRegex = new RegExp(`@NativeClass(\\((.|\\n)*?\\))?\\s*class\\s+${className}`, 'gm');
45+
return code.replace(decoratorRegex, `class ${className}`);
46+
}
47+
48+
function transpileClassToES5(node, sourceCode) {
49+
const className = node.id.name;
50+
const classStart = node.start;
51+
const classEnd = node.end;
52+
53+
const classCode = sourceCode.slice(classStart, classEnd);
54+
const cleanedCode = removeNativeClassDecorator(classCode, className);
55+
56+
const transpiled = ts.transpileModule(cleanedCode, {
57+
compilerOptions: {
58+
noEmitHelpers: true,
59+
module: ts.ModuleKind.ESNext,
60+
target: ts.ScriptTarget.ES5,
61+
experimentalDecorators: true,
62+
emitDecoratorMetadata: true,
63+
},
64+
});
65+
66+
return transpiled.outputText.replace(
67+
/(Object\.defineProperty\(.*?{.*?)(enumerable:\s*false)(.*?}\))/gs,
68+
'$1enumerable: true$3'
69+
);
70+
}

nativescript.webpack.js

+5
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ const solid = (config, env) => {
5757
],
5858
"@babel/typescript"
5959
],
60+
plugins: [
61+
path.resolve(__dirname, 'babel-plugin-native-class.js'),
62+
['@babel/plugin-proposal-decorators', { legacy: true }],
63+
['@babel/plugin-proposal-class-properties', { loose: true }]
64+
],
6065
env: {
6166
development: {
6267
plugins: [['solid-refresh/babel', { bundler: 'webpack5' }]],

package.json

+4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
"repository": "nativescript-community/solid-js",
2020
"bugs": "https://github.com/nativescript-community/solid-js/issues",
2121
"homepage": "https://github.com/nativescript-community/solid-js",
22+
"dependencies": {
23+
"@babel/plugin-proposal-class-properties": "^7.18.6",
24+
"@babel/plugin-proposal-decorators": "^7.25.9"
25+
},
2226
"peerDependencies": {
2327
"@babel/preset-typescript": "7.23.3",
2428
"babel-preset-solid": "^1.8.9",

0 commit comments

Comments
 (0)