glsl-modules
Advanced tools
| import { EntityDependency } from "./entity-dependency.js"; | ||
| import { ModuleEntity } from "./types.js"; | ||
| declare const entityTypes: readonly ["function", "variable", "struct", "define"]; | ||
| export type EntityType = typeof entityTypes[number]; | ||
| export declare abstract class BaseEntity { | ||
| readonly type: EntityType; | ||
| key: string; | ||
| id: string; | ||
| name: string; | ||
| path: string; | ||
| definition: string; | ||
| index: number; | ||
| dependencies: EntityDependency[]; | ||
| constructor(name: string, path: string, index: number, definition: string); | ||
| getResolvedDefinition(idNameMap: Record<string, string>): string; | ||
| isDependentOn(candidate: ModuleEntity): boolean; | ||
| addDependencies(...dependencies: EntityDependency[]): void; | ||
| removeDependency(id: string): void; | ||
| protected abstract getDependencyTestString(): string; | ||
| determineDependencies(candidateDependencies: EntityDependency[]): void; | ||
| } | ||
| export {}; |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.BaseEntity = void 0; | ||
| const entityTypes = ["function", "variable", "struct", "define"]; | ||
| class BaseEntity { | ||
| constructor(name, path, index, definition) { | ||
| this.dependencies = []; | ||
| this.id = `${path}/${name}`; | ||
| this.key = this.id; | ||
| this.name = name; | ||
| this.path = path; | ||
| this.index = index; | ||
| this.definition = definition.trim(); | ||
| } | ||
| getResolvedDefinition(idNameMap) { | ||
| let { definition, name, id, dependencies } = this; | ||
| const newName = idNameMap[id]; | ||
| if (newName) | ||
| definition = definition.replace(new RegExp(`\\b${name}\\b`), newName); | ||
| for (const dependency of dependencies) { | ||
| const newDependencyName = idNameMap[dependency.id]; | ||
| if (newDependencyName) { | ||
| definition = definition.replaceAll(new RegExp(`\\b${dependency.localName}\\b`, "g"), newDependencyName); | ||
| } | ||
| else if (dependency.alias) { | ||
| definition = definition.replaceAll(new RegExp(`\\b${dependency.alias}\\b`, "g"), dependency.name); | ||
| } | ||
| } | ||
| return `/* ${id} */\n` + definition; | ||
| } | ||
| isDependentOn(candidate) { | ||
| return this.dependencies.some((dependency) => dependency.id === candidate.id); | ||
| } | ||
| addDependencies(...dependencies) { | ||
| for (const dependency of dependencies) { | ||
| if (this.dependencies.find((d) => d.id === dependency.id)) | ||
| return; | ||
| this.dependencies.push(dependency); | ||
| } | ||
| } | ||
| removeDependency(id) { | ||
| const index = this.dependencies.findIndex((d) => d.id === id); | ||
| if (index === -1) | ||
| return; | ||
| this.dependencies.splice(index, 1); | ||
| } | ||
| determineDependencies(candidateDependencies) { | ||
| const dependencyTestString = this.getDependencyTestString(); | ||
| const dependencies = candidateDependencies | ||
| .filter((dependency) => dependency.isDependencyOf(dependencyTestString)); | ||
| this.addDependencies(...dependencies); | ||
| } | ||
| } | ||
| exports.BaseEntity = BaseEntity; |
| import { Define } from "./define.js"; | ||
| import { Func } from "./func.js"; | ||
| import { Struct } from "./struct.js"; | ||
| import { Variable } from "./variable.js"; | ||
| export type ModuleEntity = Func | Define | Struct | Variable; |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); |
| import { DependencyGraph } from "../utils/topological-sort.js"; | ||
| import { GLSLModule } from "./glsl-module.js"; | ||
| import { ModuleEntity } from "./module-content/types.js"; | ||
| export declare class Resolver { | ||
| module: GLSLModule; | ||
| allEntities: ModuleEntity[]; | ||
| idToEntityMap: Record<string, ModuleEntity[]>; | ||
| constructor(module: GLSLModule); | ||
| createIdToEntityMap(): Record<string, ModuleEntity[]>; | ||
| resolve(): string; | ||
| createDependencyGraph(): DependencyGraph; | ||
| sortEntitiesByDependencyOrder(): void; | ||
| createNameCollisionMap(entities: ModuleEntity[]): Record<string, string>; | ||
| } |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.Resolver = void 0; | ||
| const resolve_dependencies_js_1 = require("../utils/resolve-dependencies.js"); | ||
| const topological_sort_js_1 = require("../utils/topological-sort.js"); | ||
| class Resolver { | ||
| constructor(module) { | ||
| this.module = module; | ||
| const { entities, moduleContext } = module; | ||
| // Create array of entities in module and all of the dependencies | ||
| this.allEntities = entities | ||
| .concat((0, resolve_dependencies_js_1.resolveDependencies)(entities, moduleContext.libraries)); | ||
| this.idToEntityMap = this.createIdToEntityMap(); | ||
| } | ||
| createIdToEntityMap() { | ||
| return this.allEntities.reduce((map, entity) => { | ||
| var _a; | ||
| var _b; | ||
| ((_a = map[_b = entity.id]) !== null && _a !== void 0 ? _a : (map[_b] = [])).push(entity); | ||
| return map; | ||
| }, {}); | ||
| } | ||
| resolve() { | ||
| const { allEntities } = this; | ||
| const idToNewNameMap = this.createNameCollisionMap(allEntities); | ||
| this.sortEntitiesByDependencyOrder(); | ||
| let resolvedCode = [this.module.unparsedCode.trim()] | ||
| .concat(allEntities.map((entity) => entity.getResolvedDefinition(idToNewNameMap))) | ||
| .join("\n\n") | ||
| // Make code slightly more readable by removing excessive spacing | ||
| .replace(/\n+\s*\n+/g, "\n\n"); | ||
| return resolvedCode; | ||
| } | ||
| // Creates graph that describes dependency structure of entities. | ||
| // The graph is represented as an object mapping each id to a set containing its dependencies | ||
| createDependencyGraph() { | ||
| var _a; | ||
| var _b; | ||
| const { allEntities, idToEntityMap } = this; | ||
| const dependencyGraph = {}; | ||
| for (const entity of allEntities) { | ||
| const dependencySet = (_a = dependencyGraph[_b = entity.key]) !== null && _a !== void 0 ? _a : (dependencyGraph[_b] = new Set()); | ||
| for (const dependency of entity.dependencies) { | ||
| // Get all entities that are potential dependencies | ||
| const candidateEntities = idToEntityMap[dependency.id]; | ||
| if (!candidateEntities) | ||
| debugger; | ||
| for (const candidate of candidateEntities) { | ||
| if (candidate.key === entity.key) | ||
| continue; | ||
| // If the candidate has entity as dependency, there is a | ||
| // dependency cycle -> only include candidates that come | ||
| // before it in module | ||
| const isCyclicDependency = candidate.dependencies.some((dep) => dep.id === entity.id); | ||
| // Dependency cycle between different modules not allowed | ||
| if (isCyclicDependency && candidate.path !== entity.path) | ||
| throw new Error(`Cyclic dependency between modules not allowed: ${candidate.id} and ${entity.id}`); | ||
| if (isCyclicDependency && candidate.index > entity.index) | ||
| continue; | ||
| dependencySet.add(candidate.key); | ||
| } | ||
| } | ||
| } | ||
| return dependencyGraph; | ||
| } | ||
| // Sorts the array of entities in the correct order, ensuring | ||
| // dependencies come before dependents | ||
| sortEntitiesByDependencyOrder() { | ||
| const dependencyGraph = this.createDependencyGraph(); | ||
| const entityOrder = (0, topological_sort_js_1.topologicalSort)(dependencyGraph); | ||
| // Maps from id of entity to its position in the script | ||
| const keyToPositionMap = {}; | ||
| for (let i = 0; i < entityOrder.length; i++) { | ||
| keyToPositionMap[entityOrder[i]] = i; | ||
| } | ||
| this.allEntities.sort((a, b) => keyToPositionMap[a.key] - keyToPositionMap[b.key]); | ||
| } | ||
| createNameCollisionMap(entities) { | ||
| var _a; | ||
| var _b; | ||
| const nameEntityIdsMap = {}; | ||
| for (const entity of entities) { | ||
| ((_a = nameEntityIdsMap[_b = entity.name]) !== null && _a !== void 0 ? _a : (nameEntityIdsMap[_b] = new Set())).add(entity.id); | ||
| } | ||
| const idToNewNameMap = {}; | ||
| for (const [name, idSet] of Object.entries(nameEntityIdsMap)) { | ||
| if (idSet.size < 2) | ||
| continue; // Unique name | ||
| const separator = name.endsWith("_") ? "" : "_"; | ||
| const ids = Array.from(idSet); | ||
| for (let i = 0; i < ids.length; i++) { | ||
| idToNewNameMap[ids[i]] = `${name}${separator}${i}`; | ||
| } | ||
| } | ||
| return idToNewNameMap; | ||
| } | ||
| } | ||
| exports.Resolver = Resolver; |
| import { GLSLModule } from "./glsl-module.js"; | ||
| import { GLSLPlugin } from "./glsl-plugin.js"; | ||
| import { ModuleEntity } from "./module-content/module-entity.js"; | ||
| import { ModuleEntity } from "./module-content/types.js"; | ||
| type LibraryStructure = { | ||
@@ -27,5 +27,4 @@ [key: string]: LibraryStructure | GLSLModule | string; | ||
| getEntity(path: string, name: string, type: "import" | "ambient", originId: string): ModuleEntity[]; | ||
| resolveDependencies(entity: ModuleEntity, seenIds?: Set<string>): ModuleEntity[]; | ||
| addDependency(library: GLSLLibrary): this; | ||
| } | ||
| export {}; |
@@ -50,7 +50,6 @@ "use strict"; | ||
| const module = this.modules[path]; | ||
| if (!module) | ||
| throw new Error(`No module "${path}" in library ${this.name}`); | ||
| return module.getEntity({ name, type, originId }); | ||
| } | ||
| resolveDependencies(entity, seenIds) { | ||
| return resolveDependenciesHelper(entity, this, seenIds !== null && seenIds !== void 0 ? seenIds : new Set()); | ||
| } | ||
| addDependency(library) { | ||
@@ -77,18 +76,1 @@ this.dependencies[library.name] = library; | ||
| } | ||
| function resolveDependenciesHelper(entity, library, seenIds) { | ||
| const entries = []; | ||
| for (const { id, path, name, type } of entity.dependencies) { | ||
| if (seenIds.has(id)) | ||
| continue; | ||
| seenIds.add(id); | ||
| const libraryName = GLSLLibrary.extractLibraryNameFromPath(path); | ||
| const dependencyLibrary = libraryName === library.name ? library : library.dependencies[libraryName]; | ||
| const exportedEntities = dependencyLibrary.getEntity(path, name, type, entity.id); | ||
| entries.push(...exportedEntities); | ||
| for (const entry of exportedEntities) { | ||
| const dependencies = resolveDependenciesHelper(entry, dependencyLibrary, seenIds); | ||
| entries.push(...dependencies); | ||
| } | ||
| } | ||
| return entries; | ||
| } |
@@ -20,6 +20,6 @@ import { Export, Import, ModuleEntity } from "./module-content/index.js"; | ||
| unparsedCode: string; | ||
| isOutputModule: boolean; | ||
| isShaderModule: boolean; | ||
| moduleContext: ModuleContext; | ||
| pluginContext: PluginContext; | ||
| constructor(path: string, code: string, context: ModuleContext, isOutputModule?: boolean); | ||
| constructor(path: string, code: string, context: ModuleContext); | ||
| parse(code: string): GLSLParser; | ||
@@ -26,0 +26,0 @@ applyPlugins(): void; |
@@ -8,12 +8,12 @@ "use strict"; | ||
| const resolve_dependencies_js_1 = require("../utils/resolve-dependencies.js"); | ||
| const index_js_1 = require("../utils/index.js"); | ||
| const remove_comments_js_1 = require("../utils/remove-comments.js"); | ||
| const resolver_js_1 = require("./resolver.js"); | ||
| class GLSLModule { | ||
| constructor(path, code, context, isOutputModule = false) { | ||
| constructor(path, code, context) { | ||
| this.entities = []; | ||
| this.imports = []; | ||
| this.exports = []; | ||
| this.isShaderModule = false; | ||
| this.path = path; | ||
| this.moduleContext = context; | ||
| this.isOutputModule = isOutputModule; | ||
| const parser = this.parse(code); | ||
@@ -27,3 +27,3 @@ this.unparsedCode = parser.code; | ||
| continue; | ||
| code = plugin.preprocess(code, this.isOutputModule); | ||
| code = plugin.preprocess(code, this.isShaderModule); | ||
| } | ||
@@ -40,7 +40,5 @@ const parser = new glsl_parser_js_1.GLSLParser(code, this.path).parseAll(); | ||
| var _a; | ||
| const { path, isOutputModule, } = this; | ||
| const { plugins, libraries: dependencies } = this.moduleContext; | ||
| let importedEntities = (0, resolve_dependencies_js_1.resolveDependencies)(this.entities, dependencies); | ||
| // TODO Again, temporary solution | ||
| importedEntities = [...new Set(importedEntities)]; | ||
| const { path, isShaderModule: isOutputModule, } = this; | ||
| const { plugins, libraries } = this.moduleContext; | ||
| let importedEntities = (0, resolve_dependencies_js_1.resolveDependencies)(this.entities, libraries); | ||
| const context = this.pluginContext = new glsl_plugin_js_1.PluginContext(path, this.entities, this.imports, importedEntities); | ||
@@ -73,14 +71,3 @@ for (const plugin of plugins) { | ||
| resolve() { | ||
| let entities = this.entities | ||
| .concat((0, resolve_dependencies_js_1.resolveDependencies)(this.entities, this.moduleContext.libraries)); | ||
| // TODO this is a temporary solution, resolveDependencies should not have duplicate entities to begin with | ||
| entities = [...new Set(entities)]; | ||
| const idToNewNameMap = createNameCollisionMap(entities); | ||
| sortEntitiesByDependencyOrder(entities); | ||
| let resolvedCode = [this.unparsedCode.trim()] | ||
| .concat(entities.map((entity) => entity.getResolvedDefinition(idToNewNameMap))) | ||
| .join("\n\n"); | ||
| // Make code slightly more readable by removing excessive spacing | ||
| resolvedCode = resolvedCode.replace(/\n+\s*\n+/g, "\n\n"); | ||
| return resolvedCode; | ||
| return new resolver_js_1.Resolver(this).resolve(); | ||
| } | ||
@@ -103,69 +90,1 @@ validateExport(request) { | ||
| exports.GLSLModule = GLSLModule; | ||
| // Creates graph that describes dependency structure of entities. | ||
| // The graph is represented as an object mapping each id to a set containing its | ||
| // dependencies | ||
| function createDependencyGraph(entities) { | ||
| var _a; | ||
| var _b; | ||
| const dependencyGraph = {}; | ||
| const idToEntityMap = entities.reduce((map, entity) => { | ||
| var _a; | ||
| var _b; | ||
| ((_a = map[_b = entity.id]) !== null && _a !== void 0 ? _a : (map[_b] = [])).push(entity); | ||
| return map; | ||
| }, {}); | ||
| for (const entity of entities) { | ||
| const dependencySet = (_a = dependencyGraph[_b = entity.key]) !== null && _a !== void 0 ? _a : (dependencyGraph[_b] = new Set()); | ||
| for (const dependency of entity.dependencies) { | ||
| // Get all entities that are potential dependencies | ||
| const candidateEntities = idToEntityMap[dependency.id]; | ||
| for (const candidate of candidateEntities) { | ||
| if (candidate.key === entity.key) | ||
| continue; | ||
| // If the candidate has entity as dependency, there is a | ||
| // dependency cycle -> only include candidates that come | ||
| // before it in module | ||
| const isCyclicDependency = candidate.dependencies.some((dep) => dep.id === entity.id); | ||
| // Dependency cycle between different modules not allowed | ||
| (0, index_js_1.assert)(!(isCyclicDependency && candidate.path !== entity.path), `Cyclic dependency between modules not allowed: ${candidate.id} and ${entity.id}`); | ||
| if (isCyclicDependency && candidate.index > entity.index) | ||
| continue; | ||
| dependencySet.add(candidate.key); | ||
| } | ||
| } | ||
| } | ||
| return dependencyGraph; | ||
| } | ||
| // Takes an array of entities and sorts it in the correct order, ensuring | ||
| // dependencies come before dependents | ||
| function sortEntitiesByDependencyOrder(entities) { | ||
| const dependencyGraph = createDependencyGraph(entities); | ||
| const entityOrder = (0, index_js_1.topologicalSort)(dependencyGraph); | ||
| // Maps from id of entity to its position in the script | ||
| const keyToPositionMap = {}; | ||
| for (let i = 0; i < entityOrder.length; i++) { | ||
| keyToPositionMap[entityOrder[i]] = i; | ||
| } | ||
| entities.sort((a, b) => keyToPositionMap[a.key] - keyToPositionMap[b.key]); | ||
| } | ||
| // Creates a map from the ids of entities with name collisions to new names | ||
| function createNameCollisionMap(entities) { | ||
| var _a; | ||
| var _b; | ||
| const nameEntityIdsMap = {}; | ||
| for (const entity of entities) { | ||
| const set = (_a = nameEntityIdsMap[_b = entity.name]) !== null && _a !== void 0 ? _a : (nameEntityIdsMap[_b] = new Set()); | ||
| set.add(entity.id); | ||
| } | ||
| const idToNewNameMap = {}; | ||
| for (const [name, idSet] of Object.entries(nameEntityIdsMap)) { | ||
| if (idSet.size < 2) | ||
| continue; // Unique name | ||
| const separator = name.endsWith("_") ? "" : "_"; | ||
| const ids = [...idSet]; | ||
| for (let i = 0; i < ids.length; i++) { | ||
| idToNewNameMap[ids[i]] = `${name}${separator}${i}`; | ||
| } | ||
| } | ||
| return idToNewNameMap; | ||
| } |
@@ -34,2 +34,5 @@ "use strict"; | ||
| const [signature, returnType, name, argsString] = match.groups; | ||
| // Special case layout(location = 0), which may be incorrectly captured | ||
| if (name === "layout") | ||
| continue; | ||
| const definition = this.code.slice(startIndex, endIndex); | ||
@@ -117,9 +120,10 @@ const args = index_js_2.Argument.parseArgumentsString(argsString); | ||
| throw new Error(`Invalid export entry: ${entry}`); | ||
| const [qualifier, name, alias] = extractMatch.groups; | ||
| let [qualifier, name, alias] = extractMatch.groups; | ||
| if (alias) { | ||
| if (this.code.match(new RegExp(`\\b${alias}\\b`)) !== null) | ||
| throw new Error(`Export alias "${alias}" exists in same module`); | ||
| this.code = this.code.replaceAll(new RegExp(`\\b${name}\\b`, "g"), alias); | ||
| this.code = this.code.replace(new RegExp(`\\b${name}\\b`, "g"), alias); | ||
| name = alias; | ||
| } | ||
| if (qualifier && !index_js_2.exportQualifiers.some((q) => q === qualifier)) | ||
| if (qualifier && !index_js_2.exportQualifiers.includes(qualifier)) | ||
| throw new Error(`Invalid export qualifier "${qualifier}"`); | ||
@@ -126,0 +130,0 @@ return new index_js_2.Export(name, qualifier); |
@@ -24,3 +24,4 @@ import { Import, ModuleEntity } from "./module-content/index.js"; | ||
| }; | ||
| export type GLSLPluginFunction<A extends any[] = any[]> = (...args: A) => GLSLPlugin; | ||
| type GLSLPluginFunction<A extends any[] = any[]> = (...args: A) => GLSLPlugin; | ||
| export declare function definePlugin<A extends any[]>(pluginFunction: GLSLPluginFunction<A>): GLSLPluginFunction<A>; | ||
| export {}; |
@@ -24,3 +24,4 @@ "use strict"; | ||
| const { libraries, plugins } = this; | ||
| const module = new glsl_module_js_1.GLSLModule("@output", code, { libraries, plugins }, true); | ||
| const module = new glsl_module_js_1.GLSLModule("@shader", code, { libraries, plugins }); | ||
| module.isShaderModule = true; | ||
| module.applyPlugins(); | ||
@@ -27,0 +28,0 @@ return module.resolve(); |
@@ -1,5 +0,6 @@ | ||
| import { ModuleEntity } from "./module-entity.js"; | ||
| import { BaseEntity } from "./base-entity.js"; | ||
| export declare const defineRegex: RegExp; | ||
| export declare class Define extends ModuleEntity { | ||
| export declare class Define extends BaseEntity { | ||
| readonly type = "define"; | ||
| protected getDependencyTestString(): string; | ||
| } |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.Define = exports.defineRegex = void 0; | ||
| const module_entity_js_1 = require("./module-entity.js"); | ||
| const base_entity_js_1 = require("./base-entity.js"); | ||
| exports.defineRegex = /^ *(#define +(\w+(?:\([^\)]*\))?) +(.*))$/m; | ||
| const extractDefineRegex = /#define *([^ ]+)+ *([^ ]+)+/; | ||
| class Define extends module_entity_js_1.ModuleEntity { | ||
| class Define extends base_entity_js_1.BaseEntity { | ||
| constructor() { | ||
| super(...arguments); | ||
| this.type = "define"; | ||
| } | ||
| getDependencyTestString() { | ||
@@ -9,0 +13,0 @@ const [, , value] = this.definition.match(extractDefineRegex); |
@@ -1,2 +0,2 @@ | ||
| import { ModuleEntity } from "./module-entity.js"; | ||
| import { BaseEntity } from "./base-entity.js"; | ||
| export declare const functionSignatureRegex: RegExp; | ||
@@ -11,3 +11,4 @@ export declare class Argument { | ||
| } | ||
| export declare class Func extends ModuleEntity { | ||
| export declare class Func extends BaseEntity { | ||
| readonly type = "function"; | ||
| arguments: Argument[]; | ||
@@ -14,0 +15,0 @@ constructor(name: string, path: string, index: number, definition: string, args: Argument[]); |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.Func = exports.Argument = exports.functionSignatureRegex = void 0; | ||
| const module_entity_js_1 = require("./module-entity.js"); | ||
| const base_entity_js_1 = require("./base-entity.js"); | ||
| const regexes_js_1 = require("../../utils/regexes.js"); | ||
| exports.functionSignatureRegex = /((\w+)\s+(\w+)\s*\(([^\)]*)\))(?![^{]*\})/; | ||
| exports.functionSignatureRegex = /((\w+)\s+(\w+)\s*\(([^\)]*)\))(?:\s*\{)/; | ||
| const plainArgumentExtractRegex = /(\w+)\s+(\w+)/; | ||
@@ -31,5 +31,6 @@ class Argument { | ||
| exports.Argument = Argument; | ||
| class Func extends module_entity_js_1.ModuleEntity { | ||
| class Func extends base_entity_js_1.BaseEntity { | ||
| constructor(name, path, index, definition, args) { | ||
| super(name, path, index, definition); | ||
| this.type = "function"; | ||
| this.arguments = args; | ||
@@ -36,0 +37,0 @@ this.key = `${this.id}:${args.map(({ type }) => type).join(",")}`; |
@@ -0,1 +1,2 @@ | ||
| export * from "./base-entity.js"; | ||
| export * from "./define.js"; | ||
@@ -5,5 +6,5 @@ export * from "./export.js"; | ||
| export * from "./import.js"; | ||
| export * from "./module-entity.js"; | ||
| export * from "./struct.js"; | ||
| export * from "./variable.js"; | ||
| export * from "./entity-dependency.js"; | ||
| export * from "./types.js"; |
@@ -17,2 +17,3 @@ "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| __exportStar(require("./base-entity.js"), exports); | ||
| __exportStar(require("./define.js"), exports); | ||
@@ -22,5 +23,5 @@ __exportStar(require("./export.js"), exports); | ||
| __exportStar(require("./import.js"), exports); | ||
| __exportStar(require("./module-entity.js"), exports); | ||
| __exportStar(require("./struct.js"), exports); | ||
| __exportStar(require("./variable.js"), exports); | ||
| __exportStar(require("./entity-dependency.js"), exports); | ||
| __exportStar(require("./types.js"), exports); |
| import { Argument } from "./func.js"; | ||
| import { ModuleEntity } from "./module-entity.js"; | ||
| import { BaseEntity } from "./base-entity.js"; | ||
| export declare const structRegex: RegExp; | ||
| export declare class Struct extends ModuleEntity { | ||
| export declare class Struct extends BaseEntity { | ||
| readonly type = "struct"; | ||
| arguments: Argument[]; | ||
@@ -6,0 +7,0 @@ constructor(name: string, path: string, index: number, definition: string, args: Argument[]); |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.Struct = exports.structRegex = void 0; | ||
| const module_entity_js_1 = require("./module-entity.js"); | ||
| const base_entity_js_1 = require("./base-entity.js"); | ||
| exports.structRegex = /^ *(struct\s+(\w+)\s+{([^}]+)}\s*;)/m; | ||
| const extractStructBodyRegex = /^[^{]*{([\s\S]*)}\s*;$/; | ||
| class Struct extends module_entity_js_1.ModuleEntity { | ||
| class Struct extends base_entity_js_1.BaseEntity { | ||
| constructor(name, path, index, definition, args) { | ||
| super(name, path, index, definition); | ||
| this.type = "struct"; | ||
| this.arguments = args; | ||
@@ -11,0 +12,0 @@ } |
@@ -1,5 +0,6 @@ | ||
| import { ModuleEntity } from "./module-entity.js"; | ||
| import { BaseEntity } from "./base-entity.js"; | ||
| export declare const variableRegex: RegExp; | ||
| export declare class Variable extends ModuleEntity { | ||
| export declare class Variable extends BaseEntity { | ||
| readonly type = "variable"; | ||
| protected getDependencyTestString(): string; | ||
| } |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.Variable = exports.variableRegex = void 0; | ||
| const module_entity_js_1 = require("./module-entity.js"); | ||
| const base_entity_js_1 = require("./base-entity.js"); | ||
| exports.variableRegex = /\s*((?:const\s+)?(\w+)\s+(\w+)\s+=\s+(\S+)\s*;)(?![^{]*})/; | ||
| class Variable extends module_entity_js_1.ModuleEntity { | ||
| class Variable extends base_entity_js_1.BaseEntity { | ||
| constructor() { | ||
| super(...arguments); | ||
| this.type = "variable"; | ||
| } | ||
| getDependencyTestString() { | ||
@@ -8,0 +12,0 @@ const [, , type, , value] = this.definition.match(exports.variableRegex); |
+1
-1
@@ -1,1 +0,1 @@ | ||
| export { GLSLRegistry, GLSLLibrary, GLSLPlugin as GLSLPlugin, definePlugin as createPlugin, resolveGLSL } from "./core-system/index.js"; | ||
| export { GLSLRegistry, GLSLLibrary, GLSLPlugin, definePlugin, resolveGLSL } from "./core-system/index.js"; |
+2
-2
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.resolveGLSL = exports.createPlugin = exports.GLSLLibrary = exports.GLSLRegistry = void 0; | ||
| exports.resolveGLSL = exports.definePlugin = exports.GLSLLibrary = exports.GLSLRegistry = void 0; | ||
| var index_js_1 = require("./core-system/index.js"); | ||
| Object.defineProperty(exports, "GLSLRegistry", { enumerable: true, get: function () { return index_js_1.GLSLRegistry; } }); | ||
| Object.defineProperty(exports, "GLSLLibrary", { enumerable: true, get: function () { return index_js_1.GLSLLibrary; } }); | ||
| Object.defineProperty(exports, "createPlugin", { enumerable: true, get: function () { return index_js_1.definePlugin; } }); | ||
| Object.defineProperty(exports, "definePlugin", { enumerable: true, get: function () { return index_js_1.definePlugin; } }); | ||
| Object.defineProperty(exports, "resolveGLSL", { enumerable: true, get: function () { return index_js_1.resolveGLSL; } }); |
@@ -91,3 +91,3 @@ "use strict"; | ||
| var _a, _b; | ||
| const unprocessedFunctionBlocks = [...functionBlocks]; | ||
| const unprocessedFunctionBlocks = Array.from(functionBlocks); | ||
| const processedFunctionStrings = []; | ||
@@ -94,0 +94,0 @@ while (unprocessedFunctionBlocks.length > 0) { |
@@ -9,3 +9,2 @@ "use strict"; | ||
| exports.cssColorsPlugin = cssColorsPlugin; | ||
| const assert_js_1 = require("../utils/assert.js"); | ||
| const find_closing_index_js_1 = require("../utils/find-closing-index.js"); | ||
@@ -17,3 +16,3 @@ const match_iterator_js_1 = require("../utils/match-iterator.js"); | ||
| "named": /\bcss-([a-z]+)\b/i, | ||
| "hex": /#(?:[0-9a-f]){3,8}/i, | ||
| "hex": /#(?:(?:[0-9a-z]{3,4})|(?:[0-9a-z]{2}){3,4})\b/i, | ||
| "rgb": /\brgba?\(/i, | ||
@@ -28,3 +27,2 @@ "hsl": /\bhsla?\(/i, | ||
| }; | ||
| const validHexLengths = [4, 5, 7, 9]; | ||
| function cssColorsPlugin(options = {}) { | ||
@@ -49,6 +47,5 @@ var _a; | ||
| const { selection, startIndex, endIndex } = match; | ||
| if (selection === "#def") | ||
| if (selection === "#define") | ||
| continue; | ||
| const { length } = selection; | ||
| (0, assert_js_1.assert)(validHexLengths.some((l) => l === length), `Invalid hex triplet ${match}`); | ||
| parsedColors.push({ selection, cssString: selection, hasAlpha: length === 5 || length === 9, startIndex, endIndex }); | ||
@@ -55,0 +52,0 @@ } |
@@ -1,3 +0,2 @@ | ||
| import { GLSLPlugin } from "../core-system/glsl-plugin.js"; | ||
| export declare function destructuringPlugin(): GLSLPlugin; | ||
| export {}; | ||
| /** | ||
@@ -4,0 +3,0 @@ * Plugin that destructures struct fields into variables |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.destructuringPlugin = destructuringPlugin; | ||
| const func_js_1 = require("../core-system/module-content/func.js"); | ||
| const match_iterator_js_1 = require("../utils/match-iterator.js"); | ||
| // Work in progress! | ||
| const pluginId = "destructuring"; | ||
@@ -52,4 +52,4 @@ const destructureRegex = /\w+\s*{\s*([^}]+)\s*}\s*=\s*/; | ||
| var _a, _b, _c; | ||
| const destructures = [...(0, match_iterator_js_1.matchIterator)(code, destructureRegex)]; | ||
| const forLoops = [...(0, match_iterator_js_1.matchIterator)(code, /\bfor\s*\(/)]; | ||
| const destructures = Array.from((0, match_iterator_js_1.matchIterator)(code, destructureRegex)); | ||
| const forLoops = Array.from((0, match_iterator_js_1.matchIterator)(code, /\bfor\s*\(/)); | ||
| let destructureIndex = 0; | ||
@@ -60,3 +60,3 @@ let forIndex = 0; | ||
| let rootBlock = newBlock(rootStart); | ||
| let blockQueue = []; | ||
| let blockStack = []; | ||
| let parentBlock = null; | ||
@@ -78,3 +78,3 @@ let currentBlock = rootBlock; | ||
| if (parentBlock) | ||
| blockQueue.push(parentBlock); | ||
| blockStack.push(parentBlock); | ||
| parentBlock = currentBlock; | ||
@@ -92,3 +92,3 @@ currentBlock = newBlock(newBlockIndex); | ||
| currentBlock = parentBlock; | ||
| parentBlock = (_c = blockQueue.pop()) !== null && _c !== void 0 ? _c : null; | ||
| parentBlock = (_c = blockStack.pop()) !== null && _c !== void 0 ? _c : null; | ||
| braceLevel--; | ||
@@ -95,0 +95,0 @@ } |
@@ -76,3 +76,3 @@ "use strict"; | ||
| let entityIndex = Math.max(...moduleEntities.map((entity) => entity.index + entity.definition.length)) + 2; | ||
| let candidateEntities = [...moduleEntities]; | ||
| let candidateEntities = Array.from(moduleEntities); | ||
| while (candidateEntities.length > 0) { | ||
@@ -87,3 +87,3 @@ const candidateEntity = candidateEntities.pop(); | ||
| const { definition } = candidateEntity; | ||
| for (const { id, localName } of [...candidateEntity.dependencies]) { | ||
| for (const { id, localName } of Array.from(candidateEntity.dependencies)) { | ||
| const higherOrderFunctions = (_a = memoizedHOFs[id]) !== null && _a !== void 0 ? _a : (memoizedHOFs[id] = findHigherOrderFunctions(context.getEntitiesById(id))); | ||
@@ -90,0 +90,0 @@ for (const func of higherOrderFunctions) { |
@@ -9,2 +9,1 @@ export * from "./find-closing-index.js"; | ||
| export * from "./split-at-commas-outside-braces.js"; | ||
| export * from "./assert.js"; |
@@ -25,2 +25,1 @@ "use strict"; | ||
| __exportStar(require("./split-at-commas-outside-braces.js"), exports); | ||
| __exportStar(require("./assert.js"), exports); |
@@ -5,20 +5,23 @@ "use strict"; | ||
| const glsl_library_js_1 = require("../core-system/glsl-library.js"); | ||
| // TODO make more efficient, now it resolves the same dependencies multiple times | ||
| function resolveDependencies(entities, libraries) { | ||
| let resolvedEntities = []; | ||
| const seenIds = new Set(entities.map(({ id }) => id)); | ||
| const seenIds = new Set(); | ||
| const stack = []; | ||
| for (const entity of entities) { | ||
| for (const { path, id, name, type } of entity.dependencies) { | ||
| if (seenIds.has(id)) | ||
| continue; | ||
| const libraryName = glsl_library_js_1.GLSLLibrary.extractLibraryNameFromPath(path); | ||
| const library = libraries[libraryName]; | ||
| if (!library) | ||
| throw new Error(`Library ${libraryName} missing`); | ||
| const importedEntities = library.getEntity(path, name, type, entity.id); | ||
| resolvedEntities.push(...importedEntities); | ||
| for (const entity of importedEntities) { | ||
| const dependencies = library.resolveDependencies(entity, seenIds); | ||
| resolvedEntities.push(...dependencies); | ||
| } | ||
| seenIds.add(entity.id); | ||
| for (const dependency of entity.dependencies) | ||
| stack.push({ dependency, dependentId: entity.id, libraries }); | ||
| } | ||
| while (stack.length > 0) { | ||
| const { dependency, dependentId, libraries } = stack.pop(); | ||
| const { path, id, name, type } = dependency; | ||
| if (seenIds.has(id)) | ||
| continue; | ||
| seenIds.add(id); | ||
| const activeLibrary = libraries[glsl_library_js_1.GLSLLibrary.extractLibraryNameFromPath(path)]; | ||
| const exportedEntities = activeLibrary.getEntity(path, name, type, dependentId); | ||
| for (const entity of exportedEntities) { | ||
| for (const dependency of entity.dependencies) | ||
| stack.push({ dependency, dependentId: entity.id, libraries: activeLibrary.dependencies }); | ||
| resolvedEntities.push(entity); | ||
| } | ||
@@ -25,0 +28,0 @@ } |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.splitAtCommasOutsideBraces = splitAtCommasOutsideBraces; | ||
| // TODO try to find regex solution | ||
| function splitAtCommasOutsideBraces(input) { | ||
@@ -6,0 +5,0 @@ const components = []; |
+32
-16
| { | ||
| "name": "glsl-modules", | ||
| "version": "0.3.0", | ||
| "version": "0.4.0", | ||
| "exports": { | ||
| ".": { | ||
| "types": "./dist/index.d.ts", | ||
| "import": "./dist/index.js", | ||
| "require": "./dist/index.js" | ||
| ".": { | ||
| "types": "./dist/index.d.ts", | ||
| "import": "./dist/index.js", | ||
| "require": "./dist/index.js" | ||
| }, | ||
| "./plugins": { | ||
| "types": "./dist/plugins/index.d.ts", | ||
| "import": "./dist/plugins/index.js", | ||
| "require": "./dist/plugins/index.js" | ||
| } | ||
| }, | ||
| "./plugins": { | ||
| "types": "./dist/plugins/index.d.ts", | ||
| "import": "./dist/plugins/index.js", | ||
| "require": "./dist/plugins/index.js" | ||
| } | ||
| }, | ||
| "files": ["./dist"], | ||
| "files": [ | ||
| "./dist" | ||
| ], | ||
| "scripts": { | ||
| "build": "tsc", | ||
| "prepublishOnly": "npm run build" | ||
| "build": "tsc -p tsconfig.build.json", | ||
| "prepublishOnly": "npm run build", | ||
| "test": "vitest", | ||
| "test:watch": "vitest --watch", | ||
| "test:coverage": "vitest run --coverage" | ||
| }, | ||
| "keywords": ["glsl", "shader", "preprocessor", "webgl", "modules", "plugin"], | ||
| "keywords": [ | ||
| "glsl", | ||
| "shader", | ||
| "preprocessor", | ||
| "webgl", | ||
| "modules", | ||
| "plugin" | ||
| ], | ||
| "author": "Mathias Isaksen <mathias@st4yho.me> (https://st4yho.me)", | ||
@@ -32,4 +44,8 @@ "license": "MIT", | ||
| "devDependencies": { | ||
| "typescript": "^5.7.3" | ||
| "@types/node": "^24.2.1", | ||
| "@vitest/ui": "^3.2.4", | ||
| "playwright": "^1.55.0", | ||
| "typescript": "^5.7.3", | ||
| "vitest": "^3.2.4" | ||
| } | ||
| } |
+19
-2
@@ -14,3 +14,3 @@ <div align="center"> | ||
| Interested in seeing it in action and trying it out for yourself? | ||
| Check out the glsl-modules playground: https://glsl-modules.vercel.app | ||
| Check out the playground: https://glsl-modules.vercel.app | ||
@@ -32,2 +32,3 @@ The library is still a work in progress, and the API is subject to change. | ||
| * [css-colors](#css-colors) | ||
| * [Current known limitations](#current-known-limitations) | ||
@@ -41,3 +42,2 @@ ## Installation | ||
| ## Examples | ||
@@ -370,1 +370,18 @@ **Note**: The GLSL code snippets below are simplified, missing version directives and so on. | ||
| ``` | ||
| ## Current known limitations | ||
| ### Global #if, #else and #endif directives | ||
| `glsl-modules` currently does not handle global `#if`-`#else`-`#endif` blocks (that is, not inside a function definition). | ||
| This, for example, will not work: | ||
| ```glsl | ||
| #if SOME_VALUE | ||
| float foo() { return 1.0; } | ||
| #else | ||
| float foo() { return 2.0; } | ||
| #endif | ||
| ``` | ||
| ### Library name collisions | ||
| It is currently not possible to add multiple libraries with the same name to a registry, or as dependencies to a library. | ||
| This may be eventually solved by supporting aliases for library dependencies. |
| import { EntityDependency } from "./entity-dependency.js"; | ||
| export declare abstract class ModuleEntity { | ||
| key: string; | ||
| id: string; | ||
| name: string; | ||
| path: string; | ||
| definition: string; | ||
| index: number; | ||
| dependencies: EntityDependency[]; | ||
| pluginData: any[]; | ||
| constructor(name: string, path: string, index: number, definition: string); | ||
| getKey(): string; | ||
| getResolvedDefinition(idNameMap: Record<string, string>): string; | ||
| isDependentOn(candidate: ModuleEntity): boolean; | ||
| addDependencies(...dependencies: EntityDependency[]): void; | ||
| removeDependency(id: string): void; | ||
| protected abstract getDependencyTestString(): string; | ||
| determineDependencies(candidateDependencies: EntityDependency[]): void; | ||
| } |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.ModuleEntity = void 0; | ||
| class ModuleEntity { | ||
| constructor(name, path, index, definition) { | ||
| this.dependencies = []; | ||
| this.pluginData = []; | ||
| this.id = `${path}/${name}`; | ||
| this.key = this.id; | ||
| this.name = name; | ||
| this.path = path; | ||
| this.index = index; | ||
| this.definition = definition.trim(); | ||
| } | ||
| getKey() { return this.id; } | ||
| getResolvedDefinition(idNameMap) { | ||
| let { definition, name, id, dependencies } = this; | ||
| const newName = idNameMap[id]; | ||
| if (newName) | ||
| definition = definition.replace(new RegExp(`\\b${name}\\b`), newName); | ||
| for (const dependency of dependencies) { | ||
| const newDependencyName = idNameMap[dependency.id]; | ||
| if (newDependencyName) { | ||
| definition = definition.replaceAll(new RegExp(`\\b${dependency.localName}\\b`, "g"), newDependencyName); | ||
| } | ||
| else if (dependency.alias) { | ||
| definition = definition.replaceAll(new RegExp(`\\b${dependency.alias}\\b`, "g"), dependency.name); | ||
| } | ||
| } | ||
| return `/* ${id} */\n` + definition; | ||
| } | ||
| isDependentOn(candidate) { | ||
| return this.dependencies.some((dependency) => dependency.id === candidate.id); | ||
| } | ||
| addDependencies(...dependencies) { | ||
| for (const dependency of dependencies) { | ||
| if (this.dependencies.find((d) => d.id === dependency.id)) | ||
| return; | ||
| this.dependencies.push(dependency); | ||
| } | ||
| } | ||
| removeDependency(id) { | ||
| const index = this.dependencies.findIndex((d) => d.id === id); | ||
| if (index === -1) | ||
| return; | ||
| this.dependencies.splice(index, 1); | ||
| } | ||
| determineDependencies(candidateDependencies) { | ||
| const dependencyTestString = this.getDependencyTestString(); | ||
| const dependencies = candidateDependencies | ||
| .filter((dependency) => dependency.isDependencyOf(dependencyTestString)); | ||
| this.addDependencies(...dependencies); | ||
| } | ||
| } | ||
| exports.ModuleEntity = ModuleEntity; |
| export declare function assert(test: any, errorMessage: string): void; |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.assert = assert; | ||
| function assert(test, errorMessage) { | ||
| if (!test) | ||
| throw new Error("Assertion failed:\n" + errorMessage); | ||
| } |
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
100511
2.03%77
2.67%1968
1.6%383
4.36%5
400%