From f56a1c278e4241bbbdab280402ac8d4350b9e584 Mon Sep 17 00:00:00 2001 From: Ib Green Date: Mon, 3 Aug 2026 11:10:30 -0400 Subject: [PATCH] Prune inlined WebGL constant imports --- .../index.ts | 83 ++++++++++++++++++- ...s-transform-inline-webgl-constants.spec.ts | 56 +++++++++++++ 2 files changed, 138 insertions(+), 1 deletion(-) diff --git a/modules/ts-plugins/src/ts-transform-inline-webgl-constants/index.ts b/modules/ts-plugins/src/ts-transform-inline-webgl-constants/index.ts index 4aa4b6a..69f9576 100644 --- a/modules/ts-plugins/src/ts-transform-inline-webgl-constants/index.ts +++ b/modules/ts-plugins/src/ts-transform-inline-webgl-constants/index.ts @@ -14,6 +14,12 @@ import type {Program, TransformationContext, SourceFile, Node} from 'typescript' import type {TransformerExtras, PluginConfig} from 'ts-patch'; import {GL} from '@luma.gl/constants'; +const WEBGL_CONSTANT_MODULES = new Set([ + '@luma.gl/constants', + '@luma.gl/webgl/constants', + 'luma.gl/constants' +]); + export default function (program: Program, pluginConfig: PluginConfig, {ts}: TransformerExtras) { return (ctx: TransformationContext) => { const {factory} = ctx; @@ -25,6 +31,9 @@ export default function (program: Program, pluginConfig: PluginConfig, {ts}: Tra return (sourceFile: SourceFile) => { function visit(node: Node): Node { + if (ts.isImportDeclaration(node)) { + return node; + } if (ts.isPropertyAccessExpression(node) && filterLeftIdentifier(node)) { const key = node.getChildAt(2); if (ts.isIdentifier(key) && key.text in GL) { @@ -39,7 +48,79 @@ export default function (program: Program, pluginConfig: PluginConfig, {ts}: Tra } return ts.visitEachChild(node, visit, ctx); } - return ts.visitNode(sourceFile, visit); + + const transformedSourceFile = ts.visitEachChild(sourceFile, visit, ctx); + const runtimeIdentifiers = getRuntimeIdentifiers(transformedSourceFile); + + const statements = transformedSourceFile.statements.flatMap((statement) => { + if (!ts.isImportDeclaration(statement) || !isWebGLConstantsImport(statement)) { + return [statement]; + } + + const importClause = statement.importClause; + const namedBindings = importClause?.namedBindings; + if (!importClause || !namedBindings || !ts.isNamedImports(namedBindings)) { + return [statement]; + } + + const elements = namedBindings.elements.filter((element) => + runtimeIdentifiers.has(element.name.text) + ); + if (elements.length === namedBindings.elements.length) { + return [statement]; + } + if (!elements.length && !importClause.name) { + return []; + } + + const updatedNamedBindings = elements.length + ? factory.updateNamedImports(namedBindings, elements) + : undefined; + const updatedImportClause = factory.updateImportClause( + importClause, + importClause.isTypeOnly, + importClause.name, + updatedNamedBindings + ); + return [ + factory.updateImportDeclaration( + statement, + statement.modifiers, + updatedImportClause, + statement.moduleSpecifier, + statement.assertClause + ) + ]; + }); + + return factory.updateSourceFile(transformedSourceFile, statements); + + function getRuntimeIdentifiers(node: Node): Set { + const identifiers = new Set(); + + collectRuntimeIdentifiers(node); + return identifiers; + + function collectRuntimeIdentifiers(child: Node): void { + if (ts.isImportDeclaration(child) || ts.isTypeNode(child)) { + return; + } + if (ts.isIdentifier(child)) { + identifiers.add(child.text); + } + ts.forEachChild(child, collectRuntimeIdentifiers); + } + } + + function isWebGLConstantsImport(node: Node): boolean { + if (ts.isImportDeclaration(node)) { + return ( + ts.isStringLiteral(node.moduleSpecifier) && + WEBGL_CONSTANT_MODULES.has(node.moduleSpecifier.text) + ); + } + return false; + } }; }; } diff --git a/modules/ts-plugins/test/ts-transform-inline-webgl-constants.spec.ts b/modules/ts-plugins/test/ts-transform-inline-webgl-constants.spec.ts index aa9ee5a..04e4b49 100644 --- a/modules/ts-plugins/test/ts-transform-inline-webgl-constants.spec.ts +++ b/modules/ts-plugins/test/ts-transform-inline-webgl-constants.spec.ts @@ -7,6 +7,8 @@ const testCases = [ { title: 'drop GL import', input: `\ +import {GL} from '@luma.gl/webgl/constants'; + device.setParametersWebGL({ blendFunc: [GL.ONE, GL.ONE_MINUS_DST_COLOR, GL.SRC_ALPHA, GL.DST_ALPHA] }); @@ -15,6 +17,60 @@ device.setParametersWebGL({ device.setParametersWebGL({ blendFunc: [1, 775, 770, 772] }); +export {}; +` + }, + { + title: 'retain other named imports', + input: `\ +import {GL, OTHER_CONSTANT} from '@luma.gl/webgl/constants'; + +console.log(GL.TRIANGLES, OTHER_CONSTANT); +`, + output: `\ +import { OTHER_CONSTANT } from '@luma.gl/webgl/constants'; +console.log(4, OTHER_CONSTANT); +` + }, + { + title: 'retain GL import for dynamic access', + input: `\ +import {GL} from '@luma.gl/webgl/constants'; + +const name = 'TRIANGLES'; +console.log(GL[name]); +`, + output: `\ +import { GL } from '@luma.gl/webgl/constants'; +const name = 'TRIANGLES'; +console.log(GL[name]); +` + }, + { + title: 'retain GL import for direct access', + input: `\ +import {GL} from '@luma.gl/webgl/constants'; + +console.log(GL); +`, + output: `\ +import { GL } from '@luma.gl/webgl/constants'; +console.log(GL); +` + }, + { + title: 'drop GL import when remaining references are types', + input: `\ +import {GL, GLPrimitiveTopology, type GLTextureTarget} from '@luma.gl/webgl/constants'; + +export function getTriangleMode(topology: GLPrimitiveTopology): GLTextureTarget | GL.TRIANGLES { + return GL.TRIANGLES; +} +`, + output: `\ +export function getTriangleMode(topology) { + return 4; +} ` }, {