-
-
Notifications
You must be signed in to change notification settings - Fork 36k
Addons: Add PURE annotations for tree-shaking. #26912
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
Conversation
Just a comment - this may be the kind of thing that's best done in stages so it's easier for project members to review and approve. Ie a PR for just adding |
Making a PR that just adds the |
There seems to be three recurring patterns used in this PR:
I'm familiar with the first one since we are already using this approach in the core. Do you mind explain 2 and 3? It's important that all collaborators (including myself^^) fully understand these patterns to ensure maintenance. Especially the IIFE thing is something that could easily be overlooked in new code that is going to be added to the library. |
The use of // Assumes tree-shaking is at module level
class Foo {}
/* @__PURE__ */ Object.assign( Foo, { staticProperty: true } ); // Assumes tree-shaking is at module level by value
const Foo = /* @__PURE__ */ ( () => {
class Foo {}
Foo.staticProperty = true
return Foo;
} )();
|
Related (in some way) issue: #25526 |
See latter case in #26912 (comment).
Looks like the latter case in #26912 (comment) is true. This adds a lot of whitespace nonsense, but otherwise the files are unmodified. Still, this would be good to split up since the rest of this PR very cleanly adds annotations. Edit: I've since removed the whitespace changes, but they will need to be formatted with a linter. Actual code changes should just be the scope for the IIFE. |
const CstParser = chevrotain.CstParser; | ||
const { CstParser } = chevrotain; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps a better example of #26912 (comment). I refactored this from a static method back to a class in b9d5666, figuring that destructuring from chevrotain
rather than member access would tree-shake and that seems to be the case. Why they're not equivalent for tools, I'm not sure. Perhaps it's accounting for possible getters? This case is regarded in https://esbuild.github.io/api/#tree-shaking-and-side-effects.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In any event, I like this solution more than the previous refactoring 😇 .
I've also recapped everything found in review in the PR description for future reference with some notes on future quality control via linting. For this PR and future reference, I've written a CLI tool to E2E test with bundlers and diagnostics which I describe further there. |
return lottie; | ||
|
||
} ); | ||
|
||
export default lottie; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
from my tests, this optimization and most of the optimizations inside examples/jsm/libs
are not needed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After further review I have to affirm that they are necessary and precisely for the issues I outlined in the PR description. Rather than individually resolve these like I did in #26935, I thought it better to wrap their scopes in an IIFE for the sake of review.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure how you're testing, but I've made a repro branch of what I mean:
https://github.com/marcofugaro/treeshake-test/tree/addons-test
As you can see LottieLoader
and lottie_canvas.module.js
are tree-shaken already.
I also tested fflate.module.js
, opentype.module.js
, mmdparser.module.js
but I believe most of jsm/libs
are already tree-shakeable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a matter of fact even other addons are tree-shaken already, I think having the proxy Addons.js
is enough to make all addons tree-shakeable.
I tested with this
// Addons.js
export * from './loaders/LottieLoader.js';
export * from './loaders/TTFLoader.js';
export * from './loaders/TiltLoader.js';
export * from './loaders/USDZLoader.js';
export * from './loaders/VOXLoader.js';
export * from './loaders/VRMLLoader.js';
export * from './loaders/VTKLoader.js';
export * from './loaders/XYZLoader.js';
export * from './loaders/lwo/IFFParser.js';
export * from './loaders/lwo/LWO2Parser.js';
export * from './loaders/lwo/LWO3Parser.js';
// index.js
import { LottieLoader } from 'three/addons'
console.log(LottieLoader)
And they all were already tree-shaken except LottieLoader (I am not testing this PR, but default three). But maybe I'm wrong, could you test this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can provide repro of this if needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As per my last message, I explained the aforementioned issues and testing strategy in the PR description. I'm not testing any optimizers like Uglify or Terser (Vite optionally uses the latter), nor am I treating three as external in the event that package.sideEffects
is interactive. Please understand that I'm regarding the lowest common denominator between minification strategies for creating a baseline for testing and the sake of observability, so even though tools like ESBuild and Rollup have access to package information for smarter traversal, others only work at the module level and can't afford such optimizations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I did read the description. My point is that in the real world, those issues don't prevent the tree-shaking of #26910.
Unlike the main bundle, we're not importing from a single file here. Since the package.sideEffects
flag is present, the bundlers are able to completely discard the unused files, even if they have some side-effects in it.
I did a test with the full Addons.js from #26910 and as you can see Esbuild, Rollup and Webpack can successfully tree-shake the Addons.js file, even without minification (only three.module.js and FXAAShader.js are present in the final bundle):
https://github.com/marcofugaro/treeshake-test/blob/addons-test-full/dist/index-esbuild.js
My point is, the changes in jsm/libs
and the IIFE wraps make maintaning the repo more difficult, while not providing any real improvement to tree-shaking.
Closing since we can rely on |
Related issue: #24595
Description
Backports tree-shaking fixes for addons from
three-stdlib
. This enablesthree/addons
in #26910.I haven't touched WebGPU or nodes code since they are WIP and won't be included in #26910, but I have a branch handy where I've already investigated tree-shaking prior which is uniquely effectful (related: #25526).
Progress:
three/addons/animation
three/addons/animation/AnimationClipCreator.js
three/addons/animation/CCDIKSolver.js
three/addons/animation/MMDAnimationHelper.js
three/addons/animation/MMDPhysics.js
three/addons/cameras
three/addons/cameras/CinematicCamera.js
three/addons/capabilities
three/addons/capabilities/WebGL.js
three/addons/controls
three/addons/controls/ArcballControls.js
three/addons/controls/DragControls.js
three/addons/controls/FirstPersonControls.js
three/addons/controls/FlyControls.js
three/addons/controls/MapControls.js
three/addons/controls/OrbitControls.js
three/addons/controls/PointerLockControls.js
three/addons/controls/TrackballControls.js
three/addons/controls/TransformControls.js
three/addons/csm
three/addons/csm/CSM.js
three/addons/csm/CSMFrustum.js
three/addons/csm/CSMHelper.js
three/addons/csm/CSMShader.js
three/addons/curves
three/addons/curves/CurveExtras.js
three/addons/curves/NURBSCurve.js
three/addons/curves/NURBSSurface.js
three/addons/curves/NURBSUtils.js
three/addons/effects
three/addons/effects/AnaglyphEffect.js
three/addons/effects/AsciiEffect.js
three/addons/effects/OutlineEffect.js
three/addons/effects/ParallaxBarrierEffect.js
three/addons/effects/PeppersGhostEffect.js
three/addons/effects/StereoEffect.js
three/addons/environments
three/addons/environments/DebugEnvironment.js
three/addons/environments/RoomEnvironment.js
three/addons/exporters
three/addons/exporters/DRACOExporter.js
three/addons/exporters/EXRExporter.js
three/addons/exporters/GLTFExporter.js
three/addons/exporters/KTX2Exporter.js
three/addons/exporters/MMDExporter.js
three/addons/exporters/OBJExporter.js
three/addons/exporters/PLYExporter.js
three/addons/exporters/STLExporter.js
three/addons/exporters/USDZExporter.js
three/addons/geometries
three/addons/geometries/BoxLineGeometry.js
three/addons/geometries/ConvexGeometry.js
three/addons/geometries/DecalGeometry.js
three/addons/geometries/ParametricGeometries.js
three/addons/geometries/ParametricGeometry.js
three/addons/geometries/RoundedBoxGeometry.js
three/addons/geometries/TeapotGeometry.js
three/addons/geometries/TextGeometry.js
three/addons/helpers
three/addons/helpers/LightProbeHelper.js
three/addons/helpers/OctreeHelper.js
three/addons/helpers/PositionalAudioHelper.js
three/addons/helpers/RectAreaLightHelper.js
three/addons/helpers/VertexNormalsHelper.js
three/addons/helpers/VertexTangentsHelper.js
three/addons/helpers/ViewHelper.js
three/addons/interactive
three/addons/interactive/HTMLMesh.js
three/addons/interactive/InteractiveGroup.js
three/addons/interactive/SelectionBox.js
three/addons/interactive/SelectionHelper.js
three/addons/lights
three/addons/lights/IESSpotLight.js
three/addons/lights/LightProbeGenerator.js
three/addons/lights/RectAreaLightUniformsLib.js
three/addons/lines
three/addons/lines/Line2.js
three/addons/lines/LineGeometry.js
three/addons/lines/LineMaterial.js
three/addons/lines/LineSegments2.js
three/addons/lines/LineSegmentsGeometry.js
three/addons/lines/Wireframe.js
three/addons/lines/WireframeGeometry2.js
three/addons/loaders
three/addons/loaders/3DMLoader.js
three/addons/loaders/3MFLoader.js
three/addons/loaders/AMFLoader.js
three/addons/loaders/BVHLoader.js
three/addons/loaders/ColladaLoader.js
three/addons/loaders/DDSLoader.js
three/addons/loaders/DRACOLoader.js
three/addons/loaders/EXRLoader.js
three/addons/loaders/FBXLoader.js
three/addons/loaders/FontLoader.js
three/addons/loaders/GCodeLoader.js
three/addons/loaders/GLTFLoader.js
three/addons/loaders/HDRCubeTextureLoader.js
three/addons/loaders/IESLoader.js
three/addons/loaders/KMZLoader.js
three/addons/loaders/KTX2Loader.js
three/addons/loaders/KTXLoader.js
three/addons/loaders/LDrawLoader.js
three/addons/loaders/LUT3dlLoader.js
three/addons/loaders/LUTCubeLoader.js
three/addons/loaders/LWOLoader.js
three/addons/loaders/LogLuvLoader.js
three/addons/loaders/LottieLoader.js
three/addons/loaders/MD2Loader.js
three/addons/loaders/MDDLoader.js
three/addons/loaders/MMDLoader.js
three/addons/loaders/MTLLoader.js
three/addons/loaders/NRRDLoader.js
three/addons/loaders/OBJLoader.js
three/addons/loaders/PCDLoader.js
three/addons/loaders/PDBLoader.js
three/addons/loaders/PLYLoader.js
three/addons/loaders/PVRLoader.js
three/addons/loaders/RGBELoader.js
three/addons/loaders/RGBMLoader.js
three/addons/loaders/STLLoader.js
three/addons/loaders/SVGLoader.js
three/addons/loaders/TDSLoader.js
three/addons/loaders/TGALoader.js
three/addons/loaders/TIFFLoader.js
three/addons/loaders/TTFLoader.js
three/addons/loaders/TiltLoader.js
three/addons/loaders/USDZLoader.js
three/addons/loaders/VOXLoader.js
three/addons/loaders/VRMLLoader.js
three/addons/loaders/VTKLoader.js
three/addons/loaders/XYZLoader.js
three/addons/loaders/lwo/IFFParser.js
three/addons/loaders/lwo/LWO2Parser.js
three/addons/loaders/lwo/LWO3Parser.js
three/addons/materials
three/addons/materials/MeshGouraudMaterial.js
three/addons/math
three/addons/math/Capsule.js
three/addons/math/ColorConverter.js
three/addons/math/ConvexHull.js
three/addons/math/ImprovedNoise.js
three/addons/math/Lut.js
three/addons/math/MeshSurfaceSampler.js
three/addons/math/OBB.js
three/addons/math/Octree.js
three/addons/math/SimplexNoise.js
three/addons/misc
three/addons/misc/ConvexObjectBreaker.js
three/addons/misc/GPUComputationRenderer.js
three/addons/misc/Gyroscope.js
three/addons/misc/MD2Character.js
three/addons/misc/MD2CharacterComplex.js
three/addons/misc/MorphAnimMesh.js
three/addons/misc/MorphBlendMesh.js
three/addons/misc/ProgressiveLightMap.js
three/addons/misc/RollerCoaster.js
three/addons/misc/TubePainter.js
three/addons/misc/Volume.js
three/addons/misc/VolumeSlice.js
three/addons/modifiers
three/addons/modifiers/CurveModifier.js
three/addons/modifiers/EdgeSplitModifier.js
three/addons/modifiers/SimplifyModifier.js
three/addons/modifiers/TessellateModifier.js
three/addons/objects
three/addons/objects/GroundProjectedSkybox.js
three/addons/objects/Lensflare.js
three/addons/objects/MarchingCubes.js
three/addons/objects/Reflector.js
three/addons/objects/ReflectorForSSRPass.js
three/addons/objects/Refractor.js
three/addons/objects/ShadowMesh.js
three/addons/objects/Sky.js
three/addons/objects/Water.js
three/addons/objects/Water2.js
three/addons/physics
three/addons/physics/AmmoPhysics.js
three/addons/physics/RapierPhysics.js
three/addons/postprocessing
three/addons/postprocessing/AfterimagePass.js
three/addons/postprocessing/BloomPass.js
three/addons/postprocessing/BokehPass.js
three/addons/postprocessing/ClearPass.js
three/addons/postprocessing/CubeTexturePass.js
three/addons/postprocessing/DotScreenPass.js
three/addons/postprocessing/EffectComposer.js
three/addons/postprocessing/FilmPass.js
three/addons/postprocessing/GlitchPass.js
three/addons/postprocessing/HalftonePass.js
three/addons/postprocessing/LUTPass.js
three/addons/postprocessing/MaskPass.js
three/addons/postprocessing/OutlinePass.js
three/addons/postprocessing/OutputPass.js
three/addons/postprocessing/Pass.js
three/addons/postprocessing/RenderPass.js
three/addons/postprocessing/RenderPixelatedPass.js
three/addons/postprocessing/SAOPass.js
three/addons/postprocessing/SMAAPass.js
three/addons/postprocessing/SSAARenderPass.js
three/addons/postprocessing/SSAOPass.js
three/addons/postprocessing/SSRPass.js
three/addons/postprocessing/SavePass.js
three/addons/postprocessing/ShaderPass.js
three/addons/postprocessing/TAARenderPass.js
three/addons/postprocessing/TexturePass.js
three/addons/postprocessing/UnrealBloomPass.js
three/addons/renderers
three/addons/renderers/CSS2DRenderer.js
three/addons/renderers/CSS3DRenderer.js
three/addons/renderers/Projector.js
three/addons/renderers/SVGRenderer.js
three/addons/shaders
three/addons/shaders/ACESFilmicToneMappingShader.js
three/addons/shaders/AfterimageShader.js
three/addons/shaders/BasicShader.js
three/addons/shaders/BleachBypassShader.js
three/addons/shaders/BlendShader.js
three/addons/shaders/BokehShader.js
three/addons/shaders/BokehShader2.js
three/addons/shaders/BrightnessContrastShader.js
three/addons/shaders/ColorCorrectionShader.js
three/addons/shaders/ColorifyShader.js
three/addons/shaders/ConvolutionShader.js
three/addons/shaders/CopyShader.js
three/addons/shaders/DOFMipMapShader.js
three/addons/shaders/DepthLimitedBlurShader.js
three/addons/shaders/DigitalGlitch.js
three/addons/shaders/DotScreenShader.js
three/addons/shaders/ExposureShader.js
three/addons/shaders/FXAAShader.js
three/addons/shaders/FilmShader.js
three/addons/shaders/FocusShader.js
three/addons/shaders/FreiChenShader.js
three/addons/shaders/GammaCorrectionShader.js
three/addons/shaders/GodRaysShader.js
three/addons/shaders/HalftoneShader.js
three/addons/shaders/HorizontalBlurShader.js
three/addons/shaders/HorizontalTiltShiftShader.js
three/addons/shaders/HueSaturationShader.js
three/addons/shaders/KaleidoShader.js
three/addons/shaders/LuminosityHighPassShader.js
three/addons/shaders/LuminosityShader.js
three/addons/shaders/MMDToonShader.js
three/addons/shaders/MirrorShader.js
three/addons/shaders/NormalMapShader.js
three/addons/shaders/OutputShader.js
three/addons/shaders/RGBShiftShader.js
three/addons/shaders/SAOShader.js
three/addons/shaders/SMAAShader.js
three/addons/shaders/SSAOShader.js
three/addons/shaders/SSRShader.js
three/addons/shaders/SepiaShader.js
three/addons/shaders/SobelOperatorShader.js
three/addons/shaders/SubsurfaceScatteringShader.js
three/addons/shaders/TechnicolorShader.js
three/addons/shaders/ToonShader.js
three/addons/shaders/TriangleBlurShader.js
three/addons/shaders/UnpackDepthRGBAShader.js
three/addons/shaders/VelocityShader.js
three/addons/shaders/VerticalBlurShader.js
three/addons/shaders/VerticalTiltShiftShader.js
three/addons/shaders/VignetteShader.js
three/addons/shaders/VolumeShader.js
three/addons/shaders/WaterRefractionShader.js
three/addons/textures
three/addons/textures/FlakesTexture.js
three/addons/utils
three/addons/utils/BufferGeometryUtils.js
three/addons/utils/CameraUtils.js
three/addons/utils/GPUStatsPanel.js
three/addons/utils/GeometryCompressionUtils.js
three/addons/utils/GeometryUtils.js
three/addons/utils/LDrawUtils.js
three/addons/utils/PackedPhongMaterial.js
three/addons/utils/SceneUtils.js
three/addons/utils/ShadowMapViewer.js
three/addons/utils/SkeletonUtils.js
three/addons/utils/TextureUtils.js
three/addons/utils/UVsDebug.js
three/addons/utils/WorkerPool.js
three/addons/webxr
three/addons/webxr/ARButton.js
three/addons/webxr/OculusHandModel.js
three/addons/webxr/OculusHandPointerModel.js
three/addons/webxr/Text2D.js
three/addons/webxr/VRButton.js
three/addons/webxr/XRButton.js
three/addons/webxr/XRControllerModelFactory.js
three/addons/webxr/XREstimatedLight.js
three/addons/webxr/XRHandMeshModel.js
three/addons/webxr/XRHandModelFactory.js
three/addons/webxr/XRHandPrimitiveModel.js
three/addons/webxr/XRPlanes.js
Issues
The issues I've come across for tree-shaking are:
/* @__PURE__ */
or/* #__PURE__ */
Object.create
andFileReader
as used here are properly impure, most others aren'tstatic
fields are ES2022, methods ES6)Object.assign
at the module level, but without a value attached to them, they get discardedTesting
For testing, I've written https://github.com/CodyJasonBennett/treeshake-cli which tests against the popular bundlers --
rollup
,webpack
, andesbuild
(vite
usesesbuild
in dev androllup
in prod). Issues such as mentioned above are reported.With
npx treeshake-cli@latest examples/jsm/Addons.js
from #26910:Linting
For linting, so you can get a gentle nudge in CI (as it's likely best effort in core/nodes), I've come across https://github.com/lukastaegert/eslint-plugin-tree-shaking which does not implement support for pure annotations yet. I plan to implement this and possibly use it with the above CLI tool, but will be invaluable here for non-intrusive regression testing.
For files with IIFEs used for static fields, I've collapsed whitespace changes for the sake of review (for why this matters, see #26935 (comment)). In IDE, there will be lint warnings, so they will have to be formatted once greenlighted. See e4fa50b for affected files.