babel-plugin-rbl 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +24 -0
- package/README.md +109 -0
- package/package.json +50 -0
- package/src/element-map.mjs +34 -0
- package/src/index.d.ts +6 -0
- package/src/index.js +43 -0
- package/src/vite.d.ts +10 -0
- package/src/vite.js +40 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
This is free and unencumbered software released into the public domain.
|
|
2
|
+
|
|
3
|
+
Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
4
|
+
distribute this software, either in source code form or as a compiled
|
|
5
|
+
binary, for any purpose, commercial or non-commercial, and by any
|
|
6
|
+
means.
|
|
7
|
+
|
|
8
|
+
In jurisdictions that recognize copyright laws, the author or authors
|
|
9
|
+
of this software dedicate any and all copyright interest in the
|
|
10
|
+
software to the public domain. We make this dedication for the benefit
|
|
11
|
+
of the public at large and to the detriment of our heirs and
|
|
12
|
+
successors. We intend this dedication to be an overt act of
|
|
13
|
+
relinquishment in perpetuity of all present and future rights to this
|
|
14
|
+
software under copyright law.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
19
|
+
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
20
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
21
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
|
23
|
+
|
|
24
|
+
For more information, please refer to <https://unlicense.org>
|
package/README.md
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# babel-plugin-rbl
|
|
2
|
+
|
|
3
|
+
Lets you write [react-babylon-lite](https://github.com/brianzinn/react-babylon-lite) scenes with
|
|
4
|
+
lowercase JSX intrinsics — `<box/>`, `<pbrMaterial/>`, `<arcRotateCamera/>` — instead of imported
|
|
5
|
+
components, **without giving up tree-shaking**. It rewrites each known lowercase tag into a real
|
|
6
|
+
component import at build time, so the module graph stays statically analyzable and unused feature
|
|
7
|
+
areas still contribute zero bytes.
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
// You write this:
|
|
11
|
+
import 'react-babylon-lite/intrinsics';
|
|
12
|
+
import { Canvas } from 'react-babylon-lite';
|
|
13
|
+
|
|
14
|
+
<Canvas>
|
|
15
|
+
<arcRotateCamera radius={7} />
|
|
16
|
+
<hemisphericLight direction={[0, 1, 0]} />
|
|
17
|
+
<box size={2}>
|
|
18
|
+
<pbrMaterial baseColorFactor="#ff6a00" />
|
|
19
|
+
</box>
|
|
20
|
+
</Canvas>;
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
// The plugin turns the tags into this (conceptually) before your React transform runs:
|
|
25
|
+
import { Box as _Box, PbrMaterial as _PbrMaterial /* … */ } from 'react-babylon-lite/components';
|
|
26
|
+
|
|
27
|
+
<_Box size={2}>
|
|
28
|
+
<_PbrMaterial baseColorFactor="#ff6a00" />
|
|
29
|
+
</_Box>;
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Because the output is ordinary named imports, the bundle is byte-for-byte what you'd get by
|
|
33
|
+
importing the components yourself — proven by the same CI tree-shake gate the library uses. There
|
|
34
|
+
is **no runtime name registry** (unlike an `extend()`-injection approach), so nothing defeats dead-
|
|
35
|
+
code elimination.
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pnpm add -D babel-plugin-rbl
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Setup
|
|
42
|
+
|
|
43
|
+
Pick the entry point that matches your toolchain. Both do the identical rewrite.
|
|
44
|
+
|
|
45
|
+
### Vite (recommended — works with oxc/SWC, `@vitejs/plugin-react` v6+, and Babel)
|
|
46
|
+
|
|
47
|
+
`@vitejs/plugin-react` v6+ is oxc-based and has **no Babel hook**, so use the standalone
|
|
48
|
+
`enforce: 'pre'` transform from the `/vite` subpath. It must run **before** the React plugin:
|
|
49
|
+
|
|
50
|
+
```js
|
|
51
|
+
// vite.config.js
|
|
52
|
+
import react from '@vitejs/plugin-react';
|
|
53
|
+
import reactBabylonLite from 'babel-plugin-rbl/vite';
|
|
54
|
+
import { defineConfig } from 'vite';
|
|
55
|
+
|
|
56
|
+
export default defineConfig({
|
|
57
|
+
plugins: [
|
|
58
|
+
reactBabylonLite(), // <box/> → <_Box/> first…
|
|
59
|
+
react(), // …then the normal JSX/TS transform
|
|
60
|
+
],
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
It only touches `.jsx`/`.tsx` files that actually contain a known tag (cheap regex pre-filter),
|
|
65
|
+
emits sourcemaps, and leaves the rewritten code as JSX/TS for your React plugin to compile as usual.
|
|
66
|
+
|
|
67
|
+
### Babel toolchain (`babel.config.js`, or a Babel-based `@vitejs/plugin-react`)
|
|
68
|
+
|
|
69
|
+
If you already run Babel, add the plugin directly:
|
|
70
|
+
|
|
71
|
+
```js
|
|
72
|
+
// babel.config.js
|
|
73
|
+
export default {
|
|
74
|
+
plugins: ['babel-plugin-rbl'],
|
|
75
|
+
};
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
```js
|
|
79
|
+
// or via a Babel-based @vitejs/plugin-react
|
|
80
|
+
react({ babel: { plugins: ['babel-plugin-rbl'] } });
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## TypeScript
|
|
84
|
+
|
|
85
|
+
Add the types-only import once (anywhere in your app, or in a `.d.ts`) so TypeScript accepts the
|
|
86
|
+
lowercase tags and types their props (`position` → `Vec3Input`, etc.):
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
import 'react-babylon-lite/intrinsics';
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
It has zero runtime cost — it only augments the global JSX namespace. Without it, TypeScript reports
|
|
93
|
+
the lowercase tags as unknown intrinsic elements.
|
|
94
|
+
|
|
95
|
+
## What gets rewritten
|
|
96
|
+
|
|
97
|
+
- Only the **known lowercase tags** are rewritten — the 30 elements generated from the
|
|
98
|
+
react-babylon-lite codegen manifest (the same list `react-babylon-lite/intrinsics` types), e.g.
|
|
99
|
+
`box`, `sphere`, `ground`, `arcRotateCamera`, `hemisphericLight`, `directionalLight`,
|
|
100
|
+
`standardMaterial`, `pbrMaterial`, `gridMaterial`, `pcfDirectionalShadowGenerator`, `transformNode`, …
|
|
101
|
+
- Lowercase tags that **aren't** in the map (DOM elements like `<div/>`) and all capitalized
|
|
102
|
+
components pass through untouched.
|
|
103
|
+
- One import is added per component per file (deduped).
|
|
104
|
+
|
|
105
|
+
The plugin takes no options — imports always resolve from `react-babylon-lite/components`.
|
|
106
|
+
|
|
107
|
+
## License
|
|
108
|
+
|
|
109
|
+
[Unlicense](https://unlicense.org/) (public domain).
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "babel-plugin-rbl",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Rewrites lowercase JSX intrinsics like <box/> into tree-shakeable react-babylon-lite component imports",
|
|
5
|
+
"author": "Brian Zinn <542756+brianzinn@users.noreply.github.com>",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/brianzinn/react-babylon-lite.git",
|
|
9
|
+
"directory": "packages/babel-plugin"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/brianzinn/react-babylon-lite#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/brianzinn/react-babylon-lite/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"babel-plugin",
|
|
17
|
+
"react",
|
|
18
|
+
"babylon-lite",
|
|
19
|
+
"react-babylon-lite"
|
|
20
|
+
],
|
|
21
|
+
"type": "module",
|
|
22
|
+
"main": "./src/index.js",
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./src/index.d.ts",
|
|
26
|
+
"import": "./src/index.js"
|
|
27
|
+
},
|
|
28
|
+
"./vite": {
|
|
29
|
+
"types": "./src/vite.d.ts",
|
|
30
|
+
"import": "./src/vite.js"
|
|
31
|
+
},
|
|
32
|
+
"./package.json": "./package.json"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"src"
|
|
36
|
+
],
|
|
37
|
+
"license": "Unlicense",
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@babel/core": "^7.25.0",
|
|
40
|
+
"@babel/helper-module-imports": "^7.25.9",
|
|
41
|
+
"@babel/plugin-syntax-jsx": "^7.25.0",
|
|
42
|
+
"@babel/plugin-syntax-typescript": "^7.25.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"vitest": "^4.1.10"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"test": "vitest run"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// GENERATED FILE — do not edit. Regenerate with `pnpm codegen` (tools/codegen).
|
|
2
|
+
/** Lowercase JSX element name → named export of react-babylon-lite/components. */
|
|
3
|
+
export const elements = {
|
|
4
|
+
arcRotateCamera: 'ArcRotateCamera',
|
|
5
|
+
box: 'Box',
|
|
6
|
+
capsule: 'Capsule',
|
|
7
|
+
csmDirectionalShadowGenerator: 'CsmDirectionalShadowGenerator',
|
|
8
|
+
cylinder: 'Cylinder',
|
|
9
|
+
directionalLight: 'DirectionalLight',
|
|
10
|
+
disc: 'Disc',
|
|
11
|
+
esmDirectionalShadowGenerator: 'EsmDirectionalShadowGenerator',
|
|
12
|
+
extrudeShape: 'ExtrudeShape',
|
|
13
|
+
freeCamera: 'FreeCamera',
|
|
14
|
+
geospatialCamera: 'GeospatialCamera',
|
|
15
|
+
gridMaterial: 'GridMaterial',
|
|
16
|
+
ground: 'Ground',
|
|
17
|
+
hemisphericLight: 'HemisphericLight',
|
|
18
|
+
meshFromData: 'MeshFromData',
|
|
19
|
+
pbrMaterial: 'PbrMaterial',
|
|
20
|
+
pcfDirectionalShadowGenerator: 'PcfDirectionalShadowGenerator',
|
|
21
|
+
pcfSpotlightShadowGenerator: 'PcfSpotlightShadowGenerator',
|
|
22
|
+
plane: 'Plane',
|
|
23
|
+
pointLight: 'PointLight',
|
|
24
|
+
polyhedron: 'Polyhedron',
|
|
25
|
+
ribbon: 'Ribbon',
|
|
26
|
+
shaderMaterial: 'ShaderMaterial',
|
|
27
|
+
sphere: 'Sphere',
|
|
28
|
+
spotLight: 'SpotLight',
|
|
29
|
+
standardMaterial: 'StandardMaterial',
|
|
30
|
+
torus: 'Torus',
|
|
31
|
+
torusKnot: 'TorusKnot',
|
|
32
|
+
transformNode: 'TransformNode',
|
|
33
|
+
tube: 'Tube',
|
|
34
|
+
};
|
package/src/index.d.ts
ADDED
package/src/index.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { addNamed } from '@babel/helper-module-imports';
|
|
2
|
+
import { elements } from './element-map.mjs';
|
|
3
|
+
|
|
4
|
+
const SOURCE = 'react-babylon-lite/components';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Rewrites known lowercase JSX tags (<box/>) into imports of the matching react-babylon-lite
|
|
8
|
+
* component (<_Box/> + `import { Box as _Box } from 'react-babylon-lite/components'`). The
|
|
9
|
+
* import graph stays statically analyzable, so tree-shaking works exactly as with hand-written
|
|
10
|
+
* component imports — no runtime name registry. Unknown lowercase tags (DOM elements) and
|
|
11
|
+
* capitalized components pass through untouched.
|
|
12
|
+
*
|
|
13
|
+
* Pair with `import 'react-babylon-lite/intrinsics'` (types-only) so TypeScript accepts the tags.
|
|
14
|
+
*/
|
|
15
|
+
export default function reactBabylonLiteBabelPlugin() {
|
|
16
|
+
return {
|
|
17
|
+
name: 'react-babylon-lite',
|
|
18
|
+
visitor: {
|
|
19
|
+
JSXOpeningElement(path, state) {
|
|
20
|
+
const nameNode = path.node.name;
|
|
21
|
+
if (nameNode.type !== 'JSXIdentifier') return;
|
|
22
|
+
const tag = nameNode.name;
|
|
23
|
+
if (!/^[a-z]/.test(tag)) return;
|
|
24
|
+
const exportName = elements[tag];
|
|
25
|
+
if (exportName === undefined) return;
|
|
26
|
+
|
|
27
|
+
// One import per component per file.
|
|
28
|
+
const imports = (state.rblImports ??= new Map());
|
|
29
|
+
let localName = imports.get(exportName);
|
|
30
|
+
if (localName === undefined) {
|
|
31
|
+
localName = addNamed(path, exportName, SOURCE, { nameHint: exportName }).name;
|
|
32
|
+
imports.set(exportName, localName);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
nameNode.name = localName;
|
|
36
|
+
const closing = path.parent.closingElement;
|
|
37
|
+
if (closing !== null && closing !== undefined && closing.name.type === 'JSXIdentifier') {
|
|
38
|
+
closing.name.name = localName;
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
package/src/vite.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standalone Vite plugin (enforce: 'pre') for setups without a babel hook (oxc/SWC-based React
|
|
3
|
+
* plugins). Structurally assignable to Vite's Plugin type.
|
|
4
|
+
*/
|
|
5
|
+
declare function reactBabylonLiteVite(): {
|
|
6
|
+
name: string;
|
|
7
|
+
enforce: 'pre';
|
|
8
|
+
transform(code: string, id: string): Promise<{ code: string; map: unknown } | null>;
|
|
9
|
+
};
|
|
10
|
+
export default reactBabylonLiteVite;
|
package/src/vite.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { transformAsync } from '@babel/core';
|
|
2
|
+
import jsxSyntax from '@babel/plugin-syntax-jsx';
|
|
3
|
+
import typescriptSyntax from '@babel/plugin-syntax-typescript';
|
|
4
|
+
import { elements } from './element-map.mjs';
|
|
5
|
+
import reactBabylonLiteBabelPlugin from './index.js';
|
|
6
|
+
|
|
7
|
+
// Cheap pre-filter: only files that mention a known lowercase tag get the babel pass.
|
|
8
|
+
const TAG_PATTERN = new RegExp(`<(?:${Object.keys(elements).join('|')})[\\s/>]`);
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Standalone Vite plugin (works with rolldown/oxc-based @vitejs/plugin-react v6+, which has no
|
|
12
|
+
* babel hook, and with SWC setups). Runs as an `enforce: 'pre'` transform that ONLY renames
|
|
13
|
+
* known lowercase JSX tags into component imports — output is still JSX/TS, so the regular
|
|
14
|
+
* React plugin compiles it as usual.
|
|
15
|
+
*
|
|
16
|
+
* Usage: `plugins: [reactBabylonLite(), react(), ...]`.
|
|
17
|
+
*/
|
|
18
|
+
export default function reactBabylonLiteVite() {
|
|
19
|
+
return {
|
|
20
|
+
name: 'react-babylon-lite',
|
|
21
|
+
enforce: 'pre',
|
|
22
|
+
async transform(code, id) {
|
|
23
|
+
const filename = id.split('?')[0];
|
|
24
|
+
if (!/\.[jt]sx$/.test(filename)) return null;
|
|
25
|
+
if (!TAG_PATTERN.test(code)) return null;
|
|
26
|
+
|
|
27
|
+
const syntaxPlugins = filename.endsWith('.tsx')
|
|
28
|
+
? [[typescriptSyntax, { isTSX: true }], jsxSyntax]
|
|
29
|
+
: [jsxSyntax];
|
|
30
|
+
const result = await transformAsync(code, {
|
|
31
|
+
configFile: false,
|
|
32
|
+
babelrc: false,
|
|
33
|
+
filename,
|
|
34
|
+
sourceMaps: true,
|
|
35
|
+
plugins: [...syntaxPlugins, reactBabylonLiteBabelPlugin],
|
|
36
|
+
});
|
|
37
|
+
return result?.code != null ? { code: result.code, map: result.map } : null;
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|