diff --git a/TODO.txt b/TODO.txt index d4b9f6d..19679d7 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,4 +1,19 @@ TODO: + Make sure component works right + bundled, of course + webpack? + Minimum react requirement? + Move off of regl + Unify implementations? + Responsive changes + Move start time to rain object + Matrix component should record, then overwrite it + Reshape all passes to react to config changes, ie. "configure" + main.js "formulate" --> "configure" + simple deltas only require updating the uniforms + return boolean of whether all deltas are simple + Resource changes are simple if they're cached and loaded, false otherwise + remake the pipeline if anything returns false Create multiple distributions core One embedded MSDF, combined from the two main glyph sets and their configs @@ -7,7 +22,6 @@ TODO: and then one with built-in MSDF generation (TTF + glyphString) --> MSDF Is MSDF strictly necessary? - Move off of regl Expanded configurability Modify regl pass async build(config, inputs) diff --git a/js/Matrix.js b/js/Matrix.js index 6a8a2a9..13f6453 100644 --- a/js/Matrix.js +++ b/js/Matrix.js @@ -112,7 +112,9 @@ export const Matrix = memo((props) => { const [rRenderer, setRenderer] = useState(null); const [rRain, setRain] = useState(null); - const configProps = Object.fromEntries(Object.entries(rawConfigProps).filter(([_, value]) => value != null)); + const configProps = Object.fromEntries( + Object.entries(rawConfigProps).filter(([_, value]) => value != null), + ); const supportsWebGPU = () => { return ( @@ -155,7 +157,12 @@ export const Matrix = memo((props) => { setCanvas(canvas); const loadRain = async () => { - const renderer = await import(`./${useWebGPU ? "webgpu" : "regl"}/main.js`); + let renderer; + if (useWebGPU) { + renderer = await import("./webgpu/main.js"); + } else { + renderer = await import("./regl/main.js"); + } setRenderer(renderer); const rain = await renderer.init(canvas); setRain(rain); diff --git a/js/bundle-contents.js b/js/bundle-contents.js index ef1adf5..93c2943 100644 --- a/js/bundle-contents.js +++ b/js/bundle-contents.js @@ -2,7 +2,4 @@ import { Matrix } from "./Matrix"; import inclusions from "./inclusions"; import * as reglRenderer from "./regl/main"; import * as webgpuRenderer from "./webgpu/main"; -globalThis.inclusions = inclusions; -globalThis.reglRenderer = reglRenderer; -globalThis.webgpuRenderer = webgpuRenderer; -globalThis.Matrix = Matrix; +export { inclusions, reglRenderer, webgpuRenderer, Matrix }; diff --git a/js/fetchLibraries.js b/js/fetchLibraries.js index c68bd01..2620684 100644 --- a/js/fetchLibraries.js +++ b/js/fetchLibraries.js @@ -1,9 +1,10 @@ export default async () => { - let glMatrix, createREGL; + let glMatrix, createREGL, inclusions; try { glMatrix = await import("gl-matrix"); createREGL = (await import("regl")).default; + inclusions = (await import("./inclusions.js")).default; } catch { const loadJS = (src) => new Promise((resolve, reject) => { @@ -14,7 +15,8 @@ export default async () => { await Promise.all([loadJS("lib/regl.min.js"), loadJS("lib/gl-matrix.js")]); glMatrix = globalThis.glMatrix; createREGL = globalThis.createREGL; + inclusions = []; } - return { glMatrix, createREGL }; + return { glMatrix, createREGL, inclusions }; }; diff --git a/js/inclusions.js b/js/inclusions.js index 318855c..1e14e2d 100644 --- a/js/inclusions.js +++ b/js/inclusions.js @@ -1,75 +1,74 @@ -import highPassFrag from "../shaders/glsl/bloomPass.highPass.frag.glsl"; -import blurFrag from "../shaders/glsl/bloomPass.blur.frag.glsl"; -import combineFrag from "../shaders/glsl/bloomPass.combine.frag.glsl"; -import imagePassFrag from "../shaders/glsl/imagePass.frag.glsl"; -import mirrorPassFrag from "../shaders/glsl/mirrorPass.frag.glsl"; -import palettePassFrag from "../shaders/glsl/palettePass.frag.glsl"; -import rainPassIntro from "../shaders/glsl/rainPass.intro.frag.glsl"; -import rainPassRaindrop from "../shaders/glsl/rainPass.raindrop.frag.glsl"; -import rainPassSymbol from "../shaders/glsl/rainPass.symbol.frag.glsl"; -import rainPassEffect from "../shaders/glsl/rainPass.effect.frag.glsl"; -import rainPassVert from "../shaders/glsl/rainPass.vert.glsl"; -import rainPassFrag from "../shaders/glsl/rainPass.frag.glsl"; -import stripePassFrag from "../shaders/glsl/stripePass.frag.glsl"; -import msdfCoptic from "../assets/coptic_msdf.png"; -import msdfGothic from "../assets/gothic_msdf.png"; -import msdfMatrixCode from "../assets/matrixcode_msdf.png"; -import msdfRes from "../assets/resurrections_msdf.png"; -// import megacity from "../assets/megacity_msdf.png"; -import msdfResGlint from "../assets/resurrections_glint_msdf.png"; -// import msdfHuberfishA from "../assets/huberfish_a_msdf.png"; -// import msdfHuberfishD from "../assets/huberfish_d_msdf.png"; -// import msdfGtargTenretni from "../assets/gtarg_tenretniolleh_msdf.png"; -// import msdfGtargAlienText from "../assets/gtarg_alientext_msdf.png"; -// import msdfNeoMatrixology from "../assets/neomatrixology_msdf.png"; -// import texSand from "../assets/sand.png"; -// import texPixels from "../assets/pixel_grid.png"; -import texMesh from "../assets/mesh.png"; -import texMetal from "../assets/metal.png"; -import bloomBlurShader from "../shaders/wgsl/bloomBlur.wgsl"; -import bloomCombineShader from "../shaders/wgsl/bloomCombine.wgsl"; -import endPassShader from "../shaders/wgsl/endPass.wgsl"; -import imagePassShader from "../shaders/wgsl/imagePass.wgsl"; -import mirrorPassShader from "../shaders/wgsl/mirrorPass.wgsl"; -import palettePassShader from "../shaders/wgsl/palettePass.wgsl"; -import rainPassShader from "../shaders/wgsl/rainPass.wgsl"; -import stripePassShader from "../shaders/wgsl/stripePass.wgsl"; - export default [ - highPassFrag, - blurFrag, - combineFrag, - imagePassFrag, - mirrorPassFrag, - palettePassFrag, - rainPassIntro, - rainPassRaindrop, - rainPassSymbol, - rainPassEffect, - rainPassVert, - rainPassFrag, - stripePassFrag, - msdfCoptic, - msdfGothic, - msdfMatrixCode, - msdfRes, - // megacity, - msdfResGlint, - // msdfHuberfishA, - // msdfHuberfishD, - // msdfGtargTenretni, - // msdfGtargAlienText, - // msdfNeoMatrixology, - // texSand, - // texPixels, - texMesh, - texMetal, - bloomBlurShader, - bloomCombineShader, - endPassShader, - imagePassShader, - mirrorPassShader, - palettePassShader, - rainPassShader, - stripePassShader, + [ + "import::shaders/glsl/bloomPass.highPass.frag.glsl", + () => import("../shaders/glsl/bloomPass.highPass.frag.glsl"), + ], + [ + "import::shaders/glsl/bloomPass.blur.frag.glsl", + () => import("../shaders/glsl/bloomPass.blur.frag.glsl"), + ], + [ + "import::shaders/glsl/bloomPass.combine.frag.glsl", + () => import("../shaders/glsl/bloomPass.combine.frag.glsl"), + ], + ["import::shaders/glsl/imagePass.frag.glsl", () => import("../shaders/glsl/imagePass.frag.glsl")], + [ + "import::shaders/glsl/mirrorPass.frag.glsl", + () => import("../shaders/glsl/mirrorPass.frag.glsl"), + ], + [ + "import::shaders/glsl/palettePass.frag.glsl", + () => import("../shaders/glsl/palettePass.frag.glsl"), + ], + [ + "import::shaders/glsl/rainPass.intro.frag.glsl", + () => import("../shaders/glsl/rainPass.intro.frag.glsl"), + ], + [ + "import::shaders/glsl/rainPass.raindrop.frag.glsl", + () => import("../shaders/glsl/rainPass.raindrop.frag.glsl"), + ], + [ + "import::shaders/glsl/rainPass.symbol.frag.glsl", + () => import("../shaders/glsl/rainPass.symbol.frag.glsl"), + ], + [ + "import::shaders/glsl/rainPass.effect.frag.glsl", + () => import("../shaders/glsl/rainPass.effect.frag.glsl"), + ], + ["import::shaders/glsl/rainPass.vert.glsl", () => import("../shaders/glsl/rainPass.vert.glsl")], + ["import::shaders/glsl/rainPass.frag.glsl", () => import("../shaders/glsl/rainPass.frag.glsl")], + [ + "import::shaders/glsl/stripePass.frag.glsl", + () => import("../shaders/glsl/stripePass.frag.glsl"), + ], + ["import::assets/coptic_msdf.png", () => import("../assets/coptic_msdf.png")], + ["import::assets/gothic_msdf.png", () => import("../assets/gothic_msdf.png")], + ["import::assets/matrixcode_msdf.png", () => import("../assets/matrixcode_msdf.png")], + ["import::assets/resurrections_msdf.png", () => import("../assets/resurrections_msdf.png")], + ["import::assets/megacity_msdf.png", () => import("../assets/megacity_msdf.png")], + [ + "import::assets/resurrections_glint_msdf.png", + () => import("../assets/resurrections_glint_msdf.png"), + ], + ["import::assets/huberfish_a_msdf.png", () => import("../assets/huberfish_a_msdf.png")], + ["import::assets/huberfish_d_msdf.png", () => import("../assets/huberfish_d_msdf.png")], + [ + "import::assets/gtarg_tenretniolleh_msdf.png", + () => import("../assets/gtarg_tenretniolleh_msdf.png"), + ], + ["import::assets/gtarg_alientext_msdf.png", () => import("../assets/gtarg_alientext_msdf.png")], + ["import::assets/neomatrixology_msdf.png", () => import("../assets/neomatrixology_msdf.png")], + ["import::assets/sand.png", () => import("../assets/sand.png")], + ["import::assets/pixel_grid.png", () => import("../assets/pixel_grid.png")], + ["import::assets/mesh.png", () => import("../assets/mesh.png")], + ["import::assets/metal.png", () => import("../assets/metal.png")], + ["import::shaders/wgsl/bloomBlur.wgsl", () => import("../shaders/wgsl/bloomBlur.wgsl")], + ["import::shaders/wgsl/bloomCombine.wgsl", () => import("../shaders/wgsl/bloomCombine.wgsl")], + ["import::shaders/wgsl/endPass.wgsl", () => import("../shaders/wgsl/endPass.wgsl")], + ["import::shaders/wgsl/imagePass.wgsl", () => import("../shaders/wgsl/imagePass.wgsl")], + ["import::shaders/wgsl/mirrorPass.wgsl", () => import("../shaders/wgsl/mirrorPass.wgsl")], + ["import::shaders/wgsl/palettePass.wgsl", () => import("../shaders/wgsl/palettePass.wgsl")], + ["import::shaders/wgsl/rainPass.wgsl", () => import("../shaders/wgsl/rainPass.wgsl")], + ["import::shaders/wgsl/stripePass.wgsl", () => import("../shaders/wgsl/stripePass.wgsl")], ]; diff --git a/js/index.js b/js/index.js index 700bb7d..1ac6d09 100644 --- a/js/index.js +++ b/js/index.js @@ -4,7 +4,6 @@ import { createRoot } from "react-dom/client"; import { Matrix } from "./Matrix"; const root = createRoot(document.getElementById("root")); -let idx = 1; const versions = [ "classic", "3d", @@ -17,24 +16,42 @@ const versions = [ "bugs", "morpheus", ]; +const effects = ["none", "plain", "palette", "stripes", "pride", "trans", "image", "mirror"]; const App = () => { const [version, setVersion] = useState(versions[0]); + const [effect, setEffect] = useState("plain"); const [numColumns, setNumColumns] = useState(80); const [cursorColor, setCursorColor] = useState(null); const [backgroundColor, setBackgroundColor] = useState("0,0,0"); const [rendererType, setRendererType] = useState(null); const [density, setDensity] = useState(2); const [destroyed, setDestroyed] = useState(false); - const onButtonClick = () => { + const onVersionButtonClick = () => { setVersion((s) => { - const newVersion = versions[idx]; - idx = (idx + 1) % versions.length; - console.log(newVersion); + let index = versions.indexOf(version) + 1; + if (index === versions.length) { + index = 0; + } + const newVersion = versions[index]; + console.log("version:", newVersion); return newVersion; }); setCursorColor(null); setBackgroundColor(null); }; + const onEffectButtonClick = () => { + setEffect((s) => { + let index = effects.indexOf(effect) + 1; + if (index === effects.length) { + index = 0; + } + const newEffect = effects[index]; + console.log("effect:", newEffect); + return newEffect; + }); + setCursorColor(null); + setBackgroundColor(null); + }; const onRendererButtonClick = () => { setRendererType(() => (rendererType === "webgpu" ? "regl" : "webgpu")); }; @@ -45,7 +62,8 @@ const App = () => { return (

Rain

- + + @@ -86,6 +104,7 @@ const App = () => { { const libraries = await fetchLibraries(); createREGL = libraries.createREGL; glMatrix = libraries.glMatrix; + inclusions = libraries.inclusions; const resize = () => { const devicePixelRatio = window.devicePixelRatio ?? 1; diff --git a/js/utils/config.js b/js/utils/config.js index 75d6918..1a03ebd 100644 --- a/js/utils/config.js +++ b/js/utils/config.js @@ -17,14 +17,12 @@ const fonts = { glyphSequenceLength: 57, glyphTextureGridSize: [8, 8], }, - /* megacity: { // The glyphs seen in the film trilogy glyphMSDFURL: "assets/megacity_msdf.png", glyphSequenceLength: 64, glyphTextureGridSize: [8, 8], }, - */ resurrections: { // The glyphs seen in the film trilogy glyphMSDFURL: "assets/resurrections_msdf.png", @@ -32,7 +30,6 @@ const fonts = { glyphSequenceLength: 135, glyphTextureGridSize: [13, 12], }, - /* huberfishA: { glyphMSDFURL: "assets/huberfish_a_msdf.png", glyphSequenceLength: 34, @@ -58,12 +55,11 @@ const fonts = { glyphSequenceLength: 12, glyphTextureGridSize: [4, 4], }, - */ }; const textureURLs = { - // sand: "assets/sand.png", - // pixels: "assets/pixel_grid.png", + sand: "assets/sand.png", + pixels: "assets/pixel_grid.png", mesh: "assets/mesh.png", metal: "assets/metal.png", }; @@ -136,7 +132,6 @@ const defaults = { const versions = { classic: {}, - /* megacity: { font: "megacity", animationSpeed: 0.5, @@ -155,7 +150,6 @@ const versions = { cursorColor: hsl(0.167, 1, 0.75), cursorIntensity: 2, }, - */ operator: { cursorColor: hsl(0.375, 1, 0.66), cursorIntensity: 3, @@ -268,7 +262,6 @@ const versions = { raindropLength: 0.3, density: 0.75, }, - /* morpheus: { font: "resurrections", glintTexture: "mesh", @@ -358,7 +351,6 @@ const versions = { // { color: hsl(0.1, 1.0, 0.9), at: 1.0 }, ], }, - */ ["3d"]: { volumetric: true, fallSpeed: 0.5, diff --git a/js/webgpu/bloomPass.js b/js/webgpu/bloomPass.js index a0767fb..d6cdf16 100644 --- a/js/webgpu/bloomPass.js +++ b/js/webgpu/bloomPass.js @@ -39,7 +39,7 @@ const makePyramidViews = (pyramid) => pyramid.map((tex) => tex.createView()); // The bloom pass is basically an added blur of the rain pass's high-pass output. // The blur approximation is the sum of a pyramid of downscaled, blurred textures. -export default ({ config, device }) => { +export default ({ config, device, cache }) => { const pyramidHeight = 4; const bloomSize = config.bloomSize; const bloomStrength = config.bloomStrength; @@ -54,8 +54,8 @@ export default ({ config, device }) => { } const assets = [ - loadShader(device, "shaders/wgsl/bloomBlur.wgsl"), - loadShader(device, "shaders/wgsl/bloomCombine.wgsl"), + loadShader(device, cache, "shaders/wgsl/bloomBlur.wgsl"), + loadShader(device, cache, "shaders/wgsl/bloomCombine.wgsl"), ]; const linearSampler = device.createSampler({ diff --git a/js/webgpu/endPass.js b/js/webgpu/endPass.js index 0f25241..141ec7b 100644 --- a/js/webgpu/endPass.js +++ b/js/webgpu/endPass.js @@ -5,7 +5,7 @@ import { loadShader, makeBindGroup, makePass } from "./utils.js"; const numVerticesPerQuad = 2 * 3; -export default ({ device, canvasFormat, canvasContext }) => { +export default ({ device, cache, canvasFormat, canvasContext }) => { const nearestSampler = device.createSampler(); const renderPassConfig = { @@ -21,7 +21,7 @@ export default ({ device, canvasFormat, canvasContext }) => { let renderPipeline; let renderBindGroup; - const assets = [loadShader(device, "shaders/wgsl/endPass.wgsl")]; + const assets = [loadShader(device, cache, "shaders/wgsl/endPass.wgsl")]; const loaded = (async () => { const [imageShader] = await Promise.all(assets); diff --git a/js/webgpu/imagePass.js b/js/webgpu/imagePass.js index f75de8d..fc4dbf1 100644 --- a/js/webgpu/imagePass.js +++ b/js/webgpu/imagePass.js @@ -17,7 +17,7 @@ export default ({ config, cache, device }) => { const bgURL = "bgURL" in config ? config.bgURL : defaultBGURL; const assets = [ loadTexture(device, cache, bgURL), - loadShader(device, "shaders/wgsl/imagePass.wgsl"), + loadShader(device, cache, "shaders/wgsl/imagePass.wgsl"), ]; const linearSampler = device.createSampler({ diff --git a/js/webgpu/main.js b/js/webgpu/main.js index 6030d2e..1fe7fd3 100644 --- a/js/webgpu/main.js +++ b/js/webgpu/main.js @@ -24,11 +24,12 @@ const effects = { mirror: makeMirrorPass, }; -let glMatrix; +let glMatrix, inclusions; export const init = async (canvas) => { const libraries = await fetchLibraries(); glMatrix = libraries.glMatrix; + inclusions = libraries.inclusions; const resize = () => { const devicePixelRatio = window.devicePixelRatio ?? 1; diff --git a/js/webgpu/mirrorPass.js b/js/webgpu/mirrorPass.js index e5ec465..3dc929a 100644 --- a/js/webgpu/mirrorPass.js +++ b/js/webgpu/mirrorPass.js @@ -24,8 +24,8 @@ window.onclick = (e) => { touchesChanged = true; }; -export default ({ config, device, cameraTex, cameraAspectRatio, timeBuffer }) => { - const assets = [loadShader(device, "shaders/wgsl/mirrorPass.wgsl")]; +export default ({ config, device, cache, cameraTex, cameraAspectRatio, timeBuffer }) => { + const assets = [loadShader(device, cache, "shaders/wgsl/mirrorPass.wgsl")]; const linearSampler = device.createSampler({ magFilter: "linear", diff --git a/js/webgpu/palettePass.js b/js/webgpu/palettePass.js index 3aca409..0872da9 100644 --- a/js/webgpu/palettePass.js +++ b/js/webgpu/palettePass.js @@ -73,7 +73,7 @@ const makePalette = (device, paletteUniforms, entries) => { // won't persist across subsequent frames. This is a safe trick // in screen space. -export default ({ config, device, timeBuffer }) => { +export default ({ config, device, cache, timeBuffer }) => { const linearSampler = device.createSampler({ magFilter: "linear", minFilter: "linear", @@ -86,7 +86,7 @@ export default ({ config, device, timeBuffer }) => { let output; let screenSize; - const assets = [loadShader(device, "shaders/wgsl/palettePass.wgsl")]; + const assets = [loadShader(device, cache, "shaders/wgsl/palettePass.wgsl")]; const loaded = (async () => { const [paletteShader] = await Promise.all(assets); diff --git a/js/webgpu/rainPass.js b/js/webgpu/rainPass.js index 497bd90..e9e2a5f 100644 --- a/js/webgpu/rainPass.js +++ b/js/webgpu/rainPass.js @@ -39,7 +39,7 @@ export default ({ config, glMatrix, cache, device, timeBuffer }) => { loadTexture(device, cache, config.glintMSDFURL), loadTexture(device, cache, config.baseTextureURL, false, true), loadTexture(device, cache, config.glintTextureURL, false, true), - loadShader(device, "shaders/wgsl/rainPass.wgsl"), + loadShader(device, cache, "shaders/wgsl/rainPass.wgsl"), ]; // The volumetric mode multiplies the number of columns diff --git a/js/webgpu/stripePass.js b/js/webgpu/stripePass.js index 79ab6f3..289a02a 100644 --- a/js/webgpu/stripePass.js +++ b/js/webgpu/stripePass.js @@ -43,7 +43,7 @@ const numVerticesPerQuad = 2 * 3; // won't persist across subsequent frames. This is a safe trick // in screen space. -export default ({ config, device, timeBuffer }) => { +export default ({ config, device, cache, timeBuffer }) => { // Expand and convert stripe colors into 1D texture data const stripeColors = "stripeColors" in config @@ -68,7 +68,7 @@ export default ({ config, device, timeBuffer }) => { let output; let screenSize; - const assets = [loadShader(device, "shaders/wgsl/stripePass.wgsl")]; + const assets = [loadShader(device, cache, "shaders/wgsl/stripePass.wgsl")]; const loaded = (async () => { const [stripeShader] = await Promise.all(assets); diff --git a/js/webgpu/utils.js b/js/webgpu/utils.js index 2e286f2..4f7efe9 100644 --- a/js/webgpu/utils.js +++ b/js/webgpu/utils.js @@ -62,7 +62,11 @@ const makeComputeTarget = (device, size, mipLevelCount = 1) => GPUTextureUsage.STORAGE_BINDING, }); -const loadShader = async (device, url) => { +const loadShader = async (device, cache, url) => { + const key = url; + if (cache.has(key)) { + return cache.get(key); + } const response = await fetch(url); const code = await response.text(); return { diff --git a/package.json b/package.json index debad07..1bbee14 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,21 @@ { - "name": "react-matrix-rain", - "version": "1.0.0", - "description": "web-based green code rain, made with love, for react", - "main": "dist/index.cjs.js", + "name": "digital-rain", + "version": "0.1.0", + "description": "web-based green code rain, made with love", + "type": "module", + "main": "./dist/digital-rain.cjs", + "module": "./dist/digital-rain.module.js", + "exports": { + ".": { + "import": "./dist/digital-rain.module.js", + "require": "./dist/digital-rain.cjs" + } + }, "files": [ - "/dist" + "/dist", + "LICENSE", + "package.json", + "README.md" ], "scripts": { "test": "echo \"Error: no test specified\" && exit 1", @@ -12,12 +23,35 @@ "start": "npm run format ; webpack serve --config ./webpack.config.js", "build": "npm run format ; rollup -c" }, - "keywords": [], - "author": "", - "license": "ISC", + "keywords": [ + "rain", + "matrix", + "javascript", + "webgl", + "webgl-computer-graphics", + "matrix-rain", + "matrix-digital-rain" + ], + "author": { + "name": "Rezmason", + "url": "https://rezmason.net" + }, + "contributors": [ + { + "name": "nohren" + } + ], + "homepage": "https://github.com/Rezmason/matrix", + "repository": { + "type": "git", + "url": "https://github.com/Rezmason/matrix" + }, + "bugs": { + "url": "https://github.com/Rezmason/matrix/issues" + }, + "license": "MIT", "dependencies": { "gl-matrix": "^3.4.3", - "holoplay-core": "^0.0.9", "regl": "^2.1.0" }, "devDependencies": { @@ -46,5 +80,13 @@ "peerDependencies": { "react": "^18.2.0", "react-dom": "^18.2.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } } } diff --git a/rollup.config.mjs b/rollup.config.mjs index cc77b43..e5a12b2 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -8,40 +8,65 @@ import terser from "@rollup/plugin-terser"; import { string } from "rollup-plugin-string"; import image from "@rollup/plugin-image"; -export default { - input: "js/bundle-contents.js", - external: ["react", "react-dom"], // keep them out of your bundle - plugins: [ - peerDepsExternal(), // auto-exclude peerDeps - nodeResolve(), // so Rollup can find deps in node_modules - string({ include: ["**/*.glsl"] }), - string({ include: ["**/*.wgsl"] }), - image({ include: ["**/*.png"], limit: 0 }), - babel({ - exclude: "node_modules/**", // transpile JSX - babelHelpers: "bundled", - presets: ["@babel/preset-react", "@babel/preset-env"], - }), - commonjs(), // turn CJS deps into ES - terser({ - sourceMap: false, // <- suppress .map generation - format: { comments: false }, - }), - visualizer({ - filename: "dist/stats.html", - gzipSize: true, - brotliSize: true, - includeAssets: true, - }), // bundle-size treemap - ], - output: [ - { +export default [ + { + input: "js/bundle-contents.js", + external: ["react", "react-dom"], // keep them out of your bundle + plugins: [ + peerDepsExternal(), // auto-exclude peerDeps + nodeResolve(), // so Rollup can find deps in node_modules + string({ include: ["**/*.glsl"] }), + string({ include: ["**/*.wgsl"] }), + image({ include: ["**/*.png"], limit: 0 }), + babel({ + exclude: "node_modules/**", // transpile JSX + babelHelpers: "bundled", + presets: ["@babel/preset-react", "@babel/preset-env"], + }), + commonjs(), // turn CJS deps into ES + terser({ + sourceMap: false, // <- suppress .map generation + format: { comments: false }, + }), + visualizer({ + filename: "dist/stats.html", + gzipSize: true, + brotliSize: true, + includeAssets: true, + }), // bundle-size treemap + ], + output: { inlineDynamicImports: true, - file: "dist/index.cjs.js", + file: "dist/digital-rain.cjs.js", format: "cjs", exports: "named", sourcemap: false, }, - // { file: 'dist/index.esm.js', format: 'es' } // optional ESM build - ], -}; + }, + { + input: "js/bundle-contents.js", + external: ["react", "react-dom"], // keep them out of your bundle + plugins: [ + peerDepsExternal(), // auto-exclude peerDeps + nodeResolve(), // so Rollup can find deps in node_modules + string({ include: ["**/*.glsl"] }), + string({ include: ["**/*.wgsl"] }), + image({ include: ["**/*.png"], limit: 0 }), + babel({ + exclude: "node_modules/**", // transpile JSX + babelHelpers: "bundled", + presets: ["@babel/preset-react", "@babel/preset-env"], + }), + commonjs(), // turn CJS deps into ES + ], + output: [ + { + inlineDynamicImports: true, + file: "dist/digital-rain.module.js", + format: "es", + exports: "named", + sourcemap: true, + }, + ], + }, +]; diff --git a/webpack.config.js b/webpack.config.js index 7887383..8684ed6 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,9 +1,14 @@ -const webpack = require("webpack"); -const path = require("path"); -const HtmlWebpackPlugin = require("html-webpack-plugin"); -const CopyPlugin = require("copy-webpack-plugin"); +import webpack from "webpack"; +import path from "path"; +import HtmlWebpackPlugin from "html-webpack-plugin"; +import CopyPlugin from "copy-webpack-plugin"; -module.exports = { +import { dirname } from "node:path"; +import { fileURLToPath } from "node:url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +export default { mode: "development", entry: path.resolve(__dirname, "./js/index.js"), module: {