fragment-shader
Advanced tools
Comparing version 0.0.3 to 0.1.0
@@ -1,3 +0,2 @@ | ||
import { EditorConfig } from './../types/editor'; | ||
import Shader from './Shader'; | ||
import Shader, { type UniformValue } from './Shader'; | ||
import { EditorView, basicSetup } from 'codemirror'; | ||
@@ -9,10 +8,27 @@ import { EditorViewConfig, ViewUpdate, keymap } from '@codemirror/view'; | ||
import { StreamLanguage } from '@codemirror/language'; | ||
import { createStyleSheet } from '../util/dom'; | ||
import { formatShadertoySource } from '../util/shadertoy'; | ||
import { oneDark } from '@codemirror/theme-one-dark'; | ||
import { BLOCKS, KEYWORDS, MATH, TYPES, RAW_UTILS } from '../constants/glsl'; | ||
import type { Uniform } from '../types/shader'; | ||
import { DEFAULT_FRAGMENT_SHADER, DEFAULT_UNIFORMS } from '../constants/shader'; | ||
import '../util/glslx'; | ||
import styles from '../css/codemirror.css?inline'; | ||
const buildAtoms = (uniforms: Uniform[]) => ({ | ||
...uniforms.reduce((acc: any, key: any) => { | ||
acc[key] = true; | ||
export interface EditorConfig { | ||
target?: HTMLElement; | ||
shader?: string; | ||
uniforms?: UniformValue[]; | ||
debug?: boolean; | ||
onError?: Function; | ||
onSuccess?: Function; | ||
onUpdate?: Function; | ||
width?: number; | ||
height?: number; | ||
dpr?: number; | ||
fillViewport?: boolean; | ||
} | ||
const buildAtoms = (uniforms: UniformValue[]) => ({ | ||
...uniforms.reduce((acc: any, uniform: any) => { | ||
acc[uniform[0]] = true; | ||
return acc; | ||
@@ -40,11 +56,68 @@ }, {}), | ||
uniforms: DEFAULT_UNIFORMS, | ||
debug: true, | ||
onError: () => {}, | ||
onSuccess: () => {}, | ||
onUpdate: () => {}, | ||
width: window.innerWidth, | ||
height: window.innerHeight, | ||
dpr: window.devicePixelRatio, | ||
fillViewport: true, | ||
}; | ||
const errorStylesheet = createStyleSheet(); | ||
let errorStyleTimeout = 0; | ||
function setErrorStyle({ | ||
message, | ||
line, | ||
}: { | ||
message: string; | ||
line: string; | ||
}): void { | ||
clearTimeout(errorStyleTimeout); | ||
errorStyleTimeout = setTimeout(() => { | ||
errorStylesheet.textContent = /*css*/ ` | ||
.cm-line:nth-child(${line}) { | ||
position: relative; | ||
outline: 1px solid var(--red); | ||
} | ||
.cm-gutterElement:nth-child(${line + 1}) { | ||
background: var(--red); | ||
transition: all var(--hover-duration) var(--easing); | ||
outline: 1px solid var(--red); | ||
} | ||
.cm-line:nth-child(${line}):after { | ||
transition: all var(--hover-duration) var(--easing); | ||
content: "${message}"; | ||
position: absolute; | ||
top: 0; | ||
left: 100%; | ||
background: var(--red); | ||
color: var(--white); | ||
z-index: 100; | ||
padding: 0 .5rem; | ||
outline: 1px solid var(--red); | ||
} | ||
`; | ||
}, 500); | ||
} | ||
function hideError() { | ||
clearTimeout(errorStyleTimeout); | ||
errorStylesheet.textContent = ''; | ||
} | ||
export default class Editor { | ||
private editorView: EditorView; | ||
private editorView: EditorView | undefined; | ||
private shader: Shader; | ||
private uniforms: Uniform[]; | ||
private uniforms: UniformValue[]; | ||
private config: EditorConfig; | ||
private _onUpdate: Function | undefined; | ||
private container: HTMLElement; | ||
constructor(config: EditorConfig) { | ||
constructor(config: EditorConfig = DEFAULT_CONFIG) { | ||
this.config = { | ||
@@ -55,3 +128,3 @@ ...DEFAULT_CONFIG, | ||
const { target, shader, uniforms } = this.config; | ||
const { target, shader, uniforms }: any = this.config; | ||
@@ -61,18 +134,81 @@ if (!target || !shader || !uniforms) | ||
this.shader = new Shader({ target, shader, uniforms, debug: true }); | ||
this.uniforms = [...uniforms] as Uniform[]; | ||
this.editorView = this.createEditorView({ | ||
target, | ||
createStyleSheet(styles); | ||
this.container = document.createElement('section'); | ||
this.container.classList.add('editor'); | ||
this.sizeContainer(); | ||
target?.appendChild(this.container); | ||
this.shader = new Shader({ | ||
target: this.container, | ||
shader, | ||
uniforms, | ||
size: { | ||
width: '100vw', | ||
height: '100vh', | ||
}, | ||
debug: this.config.debug, | ||
onError: this.onError.bind(this), | ||
onSuccess: this.onSuccess.bind(this), | ||
width: this.config.width, | ||
height: this.config.height, | ||
dpr: this.config.dpr, | ||
fillViewport: this.config.fillViewport, | ||
}); | ||
this._onUpdate = this.config.onUpdate; | ||
this.uniforms = [...uniforms] as UniformValue[]; | ||
this.createEditorView(); | ||
this.onPaste = this.onPaste.bind(this); | ||
this.sizeContainer = this.sizeContainer.bind(this); | ||
window.addEventListener('paste', this.onPaste); | ||
window.addEventListener('resize', this.sizeContainer); | ||
} | ||
sizeContainer() { | ||
const width = this.config.fillViewport | ||
? window.innerWidth | ||
: this.config.width; | ||
const height = this.config.fillViewport | ||
? window.innerHeight | ||
: this.config.height; | ||
this.container.style.position = 'relative'; | ||
this.container.style.width = width + 'px'; | ||
this.container.style.height = height + 'px'; | ||
this.container.style.overflow = 'hidden'; | ||
} | ||
onError({ line, message }: any) { | ||
this.config?.onError?.({ line, message }); | ||
setErrorStyle({ line, message }); | ||
} | ||
onSuccess() { | ||
this.config?.onSuccess?.(); | ||
hideError(); | ||
} | ||
onPaste({ clipboardData }: ClipboardEvent) { | ||
if (clipboardData === null) return; | ||
const string = clipboardData.getData('text'); | ||
const isShaderToy = string.indexOf('mainImage') !== -1; | ||
if (isShaderToy) { | ||
this.config.shader = formatShadertoySource(string); | ||
this.shader.rebuild({ | ||
shader: this.config.shader, | ||
uniforms: this.uniforms, | ||
}); | ||
this.createEditorView(); | ||
} | ||
} | ||
createState( | ||
shader: string = DEFAULT_FRAGMENT_SHADER, | ||
uniforms: Uniform[any][] = DEFAULT_UNIFORMS | ||
uniforms: UniformValue[any][] = DEFAULT_UNIFORMS | ||
): EditorState { | ||
@@ -91,19 +227,7 @@ return EditorState.create({ | ||
createEditorView({ | ||
target, | ||
shader, | ||
uniforms, | ||
}: { | ||
target: HTMLElement; | ||
shader: string; | ||
uniforms: Uniform[]; | ||
size: { | ||
width: string; | ||
height: string; | ||
}; | ||
}): EditorView { | ||
createEditorView(): void { | ||
this.editorView?.destroy?.(); | ||
return new EditorView({ | ||
parent: target, | ||
state: this.createState(shader, uniforms as any), | ||
this.editorView = new EditorView({ | ||
parent: this.container, | ||
state: this.createState(this.config.shader, this.config.uniforms), | ||
} as EditorViewConfig); | ||
@@ -115,7 +239,12 @@ } | ||
if (!docChanged) return; | ||
this.shader.rebuild({ | ||
shader: state.doc.toString(), | ||
uniforms: this.uniforms, | ||
}); | ||
const shader = state.doc.toString(); | ||
this._onUpdate?.(shader); | ||
hideError(); | ||
this.shader.rebuild({ shader, uniforms: this.uniforms }); | ||
} | ||
destroy() { | ||
window.removeEventListener('paste', this.onPaste); | ||
this.editorView?.destroy(); | ||
} | ||
} |
@@ -6,3 +6,2 @@ import Uniform from './Uniform'; | ||
import { GLSL_UTILS } from '../constants/glsl'; | ||
import { ShaderConfig, ShaderState } from '../types/shader'; | ||
import { HasResolution } from '../types/dimensions'; | ||
@@ -20,2 +19,22 @@ import { | ||
export interface ShaderConfig { | ||
target?: HTMLElement; | ||
shader?: string; | ||
uniforms?: any[]; | ||
width?: number; | ||
height?: number; | ||
fillViewport?: boolean; | ||
dpr?: number; | ||
onSuccess?: Function; | ||
onError?: Function; | ||
animate?: boolean; | ||
debug?: boolean; | ||
} | ||
export interface ShaderState { | ||
active: boolean; | ||
} | ||
export type UniformValue = [string, number, any[]]; | ||
const DEFAULT_CONFIG: ShaderConfig = { | ||
@@ -31,2 +50,4 @@ target: document.body, | ||
debug: false, | ||
onError: () => {}, | ||
onSuccess: () => {}, | ||
}; | ||
@@ -70,2 +91,8 @@ | ||
if (this.config.fillViewport) { | ||
this.canvas.style.position = 'fixed'; | ||
this.canvas.style.top = '0'; | ||
this.canvas.style.left = '0'; | ||
} | ||
sizeCanvas(this.canvas, this.config as HasResolution); | ||
@@ -125,42 +152,27 @@ | ||
get vertexShader(): string { | ||
return ` | ||
${DEFAULT_DEFS} | ||
${DEFAULT_UNIFORM_DECLARATIONS} | ||
${this.uniformDeclarations} | ||
${GLSL_UTILS} | ||
${DEFAULT_VERTEX_SHADER} | ||
`; | ||
return `${DEFAULT_DEFS}\n${DEFAULT_UNIFORM_DECLARATIONS}\n${this.uniformDeclarations}\n${GLSL_UTILS}\n${DEFAULT_VERTEX_SHADER}`; | ||
} | ||
get fragmentShader(): string { | ||
return ` | ||
${DEFAULT_DEFS} | ||
${DEFAULT_UNIFORM_DECLARATIONS} | ||
${this.uniformDeclarations} | ||
${GLSL_UTILS} | ||
${this.config.shader} | ||
`; | ||
return `${DEFAULT_DEFS}\n${DEFAULT_UNIFORM_DECLARATIONS}\n${this.uniformDeclarations}\n${GLSL_UTILS}\n${this.config.shader}`; | ||
} | ||
set uniforms(uniforms: Uniform[]) { | ||
set uniforms(uniforms: UniformValue[]) { | ||
this.config.uniforms = uniforms; | ||
} | ||
set size({ | ||
width, | ||
height, | ||
dpr = window.devicePixelRatio, | ||
}: { | ||
width: number; | ||
height: number; | ||
dpr: number; | ||
}) { | ||
set size(resolution: HasResolution) { | ||
const { | ||
width = window.innerWidth, | ||
height = window.innerHeight, | ||
dpr = window.devicePixelRatio, | ||
} = resolution; | ||
this.config.width = width; | ||
this.config.height = height; | ||
this.config.dpr = dpr; | ||
sizeCanvas(this.canvas, this.config as HasResolution); | ||
this.ctx?.viewport(0, 0, this.canvas.width, this.canvas.height); | ||
} | ||
buildUniforms(): Uniform[] { | ||
buildUniforms(): UniformValue[] { | ||
const uniforms = [...INTERNAL_UNIFORMS, ...(this.config.uniforms || [])]; | ||
@@ -212,6 +224,13 @@ | ||
this.ctx.attachShader(this.program, this.shaders[type]); | ||
this.config?.onSuccess?.(); | ||
if (type === this.ctx.FRAGMENT_SHADER) this.config?.onSuccess?.(); | ||
} else { | ||
const hiddenLines = this.fragmentShader | ||
.split(this.config.shader || '')[0] | ||
.split('\n').length; | ||
const error = this.ctx.getShaderInfoLog(this.shaders[type]); | ||
this.config?.onError?.(error); | ||
const data = error?.split(':').map(v => v.trim()) || []; | ||
const line = parseInt(data[2], 10) + 1 - hiddenLines; | ||
const message = data[4].split('\n')[0]; | ||
this.config?.onError?.({ line, message }); | ||
} | ||
@@ -233,2 +252,3 @@ | ||
sizeCanvas(this.canvas, this.config as HasResolution); | ||
this.ctx?.viewport(0, 0, this.canvas.width, this.canvas.height); | ||
} | ||
@@ -264,31 +284,31 @@ | ||
this.ctx?.deleteShader(this.shaders?.[this.ctx?.FRAGMENT_SHADER]); | ||
this.ctx?.deleteProgram(this.program); | ||
this.program = this.ctx?.createProgram() as any; | ||
this.compileShader(this.ctx?.VERTEX_SHADER, this.vertexShader); | ||
this.compileShader(this.ctx?.FRAGMENT_SHADER, this.fragmentShader); | ||
this.ctx?.linkProgram(this.program as any); | ||
this.ctx?.useProgram(this.program); | ||
this.ctx?.viewport(0, 0, this.canvas.width, this.canvas.height); | ||
this._uniforms = this.buildUniforms(); | ||
this.plane = new Plane(this.ctx); | ||
this.ctx?.enableVertexAttribArray(0); | ||
this.ctx?.vertexAttribPointer( | ||
this.ctx?.getAttribLocation(this.program as any, 'position'), | ||
2, | ||
this.ctx?.FLOAT, | ||
false, | ||
0, | ||
0 | ||
); | ||
} catch (e) { | ||
console.log(e); | ||
// console.log(e); | ||
} | ||
this.program = this.ctx?.createProgram() as any; | ||
this.compileShader(this.ctx?.VERTEX_SHADER, this.vertexShader); | ||
this.compileShader(this.ctx?.FRAGMENT_SHADER, this.fragmentShader); | ||
this.ctx?.linkProgram(this.program as any); | ||
this.ctx?.useProgram(this.program); | ||
this.ctx?.viewport(0, 0, this.canvas.width, this.canvas.height); | ||
this._uniforms = this.buildUniforms(); | ||
this.plane = new Plane(this.ctx); | ||
this.ctx?.enableVertexAttribArray(0); | ||
this.ctx?.vertexAttribPointer( | ||
this.ctx?.getAttribLocation(this.program as any, 'position'), | ||
2, | ||
this.ctx?.FLOAT, | ||
false, | ||
0, | ||
0 | ||
); | ||
} | ||
tick(now: DOMHighResTimeStamp): void { | ||
const time = now; | ||
const time = now / 1000; | ||
this._uniforms?.resolution?.set([this.canvas.width, this.canvas.height]); | ||
this._uniforms?.time?.set(time / 1000); | ||
this._uniforms?.stream?.set(this.stream ?? time / 1000); | ||
this._uniforms?.time?.set(time); | ||
this._uniforms?.stream?.set(time); | ||
this._uniforms?.volume?.set(this.volume); | ||
@@ -295,0 +315,0 @@ this.config?.uniforms?.forEach(uniform => { |
@@ -1,2 +0,2 @@ | ||
export const hue = /*glsl*/ ` | ||
export const k_hue = /*glsl*/ ` | ||
vec4 k_hue(vec4 color, float shift) { | ||
@@ -18,3 +18,3 @@ float I = dot(color, vec4(0.596, -0.275, -0.321, 0.0)); | ||
export const kale = /*glsl*/ ` | ||
export const k_kale = /*glsl*/ ` | ||
vec2 k_kale(vec2 uv, vec2 offset, float sides) { | ||
@@ -32,3 +32,3 @@ float angle = atan(uv.y, uv.x); | ||
export const orb = /*glsl*/ ` | ||
export const k_orb = /*glsl*/ ` | ||
vec4 k_orb(vec2 uv, float size, vec2 position, vec3 color, float contrast) { | ||
@@ -39,3 +39,3 @@ return pow(vec4(size / length(uv + position) * color, 1.), vec4(contrast)); | ||
export const rainbow = /*glsl*/ ` | ||
export const k_rainbow = /*glsl*/ ` | ||
vec3 k_rainbow(float progress, float stretch, float offset) { | ||
@@ -46,3 +46,3 @@ return vec3(cos(vec3(-2, 0, -1) * 3.14159265359 * 2. / 3. + (2. * 3.14159265359) * (progress * stretch) + offset) * 0.5 + 0.5); | ||
export const rotate2d = /* glsl */ ` | ||
export const k_rotate2d = /* glsl */ ` | ||
mat2 k_rotate2d(float angle) { | ||
@@ -53,3 +53,3 @@ return mat2(cos(angle), -sin(angle), sin(angle), cos(angle)); | ||
export const swap = /* glsl */ ` | ||
export const k_swap = /* glsl */ ` | ||
vec2 k_swap(vec2 uv, vec2 uv2, bool val, bool valTween, float valTweenProgress) { | ||
@@ -63,8 +63,8 @@ return valTween | ||
export const RAW_UTILS = { | ||
hue, | ||
kale, | ||
orb, | ||
rainbow, | ||
rotate2d, | ||
swap, | ||
k_hue, | ||
k_kale, | ||
k_orb, | ||
k_rainbow, | ||
k_rotate2d, | ||
k_swap, | ||
} as any; | ||
@@ -71,0 +71,0 @@ |
@@ -11,3 +11,10 @@ export const DEFAULT_VERTEX_SHADER = /*glsl*/ ` | ||
void main () { | ||
gl_FragColor = vec4(pink, 1.); | ||
vec2 uv = -1. + 2. * gl_FragCoord.xy / resolution.xy; | ||
uv.x *= resolution.x / resolution.y; | ||
float r = sin(uv.x + time); | ||
float g = cos(uv.y + time); | ||
float b = sin(uv.y - time); | ||
gl_FragColor = vec4(r, g, b, 1.); | ||
} | ||
@@ -14,0 +21,0 @@ `; |
@@ -1,2 +0,3 @@ | ||
export { default as Shader } from './classes/Shader'; | ||
export { default as Editor } from './classes/Editor'; | ||
export { default as Shader, type ShaderConfig } from './classes/Shader'; | ||
export { default as Editor, type EditorConfig } from './classes/Editor'; | ||
export * from './types/dimensions'; |
{ | ||
"name": "fragment-shader", | ||
"version": "0.0.3", | ||
"description": "A lightweight, performant WebGL fragment shader renderer.", | ||
"version": "0.1.0", | ||
"description": "A lightweight, performant WebGL fragment shader renderer + editor. ", | ||
"main": "index.js", | ||
@@ -15,2 +15,3 @@ "scripts": { | ||
"WebGL", | ||
"fragment", | ||
"shader", | ||
@@ -32,3 +33,4 @@ "renderer" | ||
"@codemirror/view": "^6.12.0", | ||
"codemirror": "^6.0.1" | ||
"codemirror": "^6.0.1", | ||
"glslx": "^0.3.0" | ||
}, | ||
@@ -35,0 +37,0 @@ "devDependencies": { |
# **fragment-shader** | ||
> A lightweight & highly performant WebGL fragment shader renderer written in TypeScript. | ||
> • `/classes/Shader.ts` – A lightweight & highly performant WebGL fragment shader renderer written in TypeScript. | ||
- Smaller than `3kb`. | ||
- Appx. `3kb` (gzipped). | ||
- Phenomal performance characteristics, both in rendering speed and in memory consumption. | ||
- Extremely minimal taxing of the garbage collector. | ||
- Certified jank-free – if your experience differs, please let me know! | ||
- Zero-configuration instantiation (see `Hello World` section for details on all default behaviors. | ||
- Zero-configuration instantiation (see `Shader.ts` section for details on all default behaviors). | ||
> • `/classes/Editor.ts` – A live, in-browser GLSL code editor implemented with Codemirror, synced with an instance of `Shader.ts`. | ||
- Appx. `165kb` (gzipped). | ||
- Live rendering! Shader re-renders on every keystroke. | ||
- Smart syntax highlighting and bracket matching. | ||
- Realtime autocomplete surfaces GLSL keywords, uniform names & more as you write your shader. | ||
- Shader compilation errors surface in the editor on relevant lines. | ||
- `ShaderToy` support! Paste your favorite shaders into the editor (work in progress). | ||
## **Installation** _( NPM )_ | ||
@@ -19,8 +28,12 @@ | ||
## **Hello World** _( Default / Bare Bones Implementation )_ | ||
To begin let's look at the core renderer, found in `/classes/Shader.ts`. | ||
## **Shader.ts** | ||
--- | ||
> **Note:** there are several plugins in modern IDEs (VSCode, etc.) that enable GLSL (shader language) syntax highlighting within template literals by prefacing them with `/*glsl*/` – doesn't seem to work on GitHub though. | ||
> **Note** there are several plugins in modern IDEs (VSCode, etc.) that enable GLSL (shader language) syntax highlighting within template literals by prefacing them with `/*glsl*/` – doesn't seem to work on GitHub though. | ||
### **Bare Bones Implementation** | ||
```javascript | ||
@@ -48,10 +61,10 @@ import { Shader } from 'fragment-shader'; | ||
## **Configured Implementation** | ||
### **Configured Implementation** | ||
--- | ||
If you wish for the renderer to behave differently than its default configuration, you can do so by passing the constructor a configuration object. | ||
If you wish for the renderer to behave differently than its default configuration, you can do so by passing the constructor a configuration object. The object's shape (and its default values) look like this: | ||
```typescript | ||
import { Shader } from 'fragment-shader'; | ||
import { Shader, type ShaderConfig } from 'fragment-shader'; | ||
@@ -66,9 +79,9 @@ const config: ShaderConfig = { | ||
uniforms: [], | ||
width: 800, | ||
height: 600, | ||
width: window.innerWidth, | ||
height: window.innerHeight, | ||
dpr: window.devicePixelRatio, | ||
fillViewport: false, | ||
fillViewport: true, | ||
onSuccess: () => {}, | ||
onError: () => {}, | ||
animate: false, | ||
animate: true, | ||
debug: false, | ||
@@ -83,3 +96,3 @@ }; | ||
```javascript | ||
import { Shader } from 'fragment-shader'; | ||
import { Shader, type ShaderConfig } from 'fragment-shader'; | ||
@@ -98,13 +111,15 @@ const config: ShaderConfig = { ... } | ||
```javascript | ||
import { Shader } from 'fragment-shader'; | ||
import { Shader, type ShaderConfig } from 'fragment-shader'; | ||
const shader = new Shader( | ||
/*glsl*/ ` | ||
void main () { | ||
gl_FragColor = vec4(.8, .2, .7, 1.); | ||
} | ||
`, | ||
{ animate: false } | ||
); | ||
const config: ShaderConfig = { | ||
shader: /*glsl*/ ` | ||
void main () { | ||
gl_FragColor = vec4(.8, .2, .7, 1.); | ||
} | ||
`, | ||
animate: false, | ||
}; | ||
const shader = new Shader(config); | ||
const tick = (now: DOMHighResTimeStamp) => { | ||
@@ -117,1 +132,23 @@ requestAnimationFrame(tick); | ||
``` | ||
Now that we can easily render shaders in the browser, let's experiment with authoring them too! | ||
--- | ||
## **Editor.ts** | ||
Instantiating an `Editor` should feel familiar after working with the `Shader` class: | ||
```javascript | ||
import { Editor } from 'fragment-shader'; | ||
const glsl = /*glsl*/ ` | ||
void main () { | ||
gl_FragColor = vec4(.8, .2, .7, 1.); | ||
} | ||
`; | ||
const editor = new Editor(glsl); | ||
``` | ||
> ( More documentation coming soon! ) |
@@ -1,2 +0,2 @@ | ||
import type { Uniform } from './shader'; | ||
import type { UniformValue } from '../classes/Shader'; | ||
@@ -6,3 +6,3 @@ export interface EditorConfig { | ||
shader?: string; | ||
uniforms?: Uniform[]; | ||
uniforms?: UniformValue[]; | ||
} |
@@ -24,1 +24,18 @@ import { HasResolution } from './../types/dimensions'; | ||
} | ||
export function createStyleSheet(src: string = ''): HTMLStyleElement { | ||
const style: HTMLStyleElement = document.createElement('style'); | ||
style.textContent = src; | ||
document.head.appendChild(style); | ||
return style; | ||
} | ||
export function loadExternalScript(uri: string) { | ||
return new Promise((resolve, reject) => { | ||
const script = document.createElement('script'); | ||
script.src = uri; | ||
script.addEventListener('load', resolve); | ||
script.addEventListener('error', reject); | ||
document.body.appendChild(script); | ||
}); | ||
} |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
297611
21
7782
149
8
4
3
1
+ Addedglslx@^0.3.0
+ Addedglslx@0.3.0(transitive)