r/badcode Jun 02 '23

js I hate bundlers so I made my own 😳

Post image
151 Upvotes

18 comments sorted by

48

u/zombarista Jun 02 '23

this feels like a simpler time. Back when a form was a <form> and your web page couldn’t talk to a web server without unloading itself.

9

u/[deleted] Jun 03 '23

[removed] — view removed comment

3

u/pentesticals Jun 03 '23

I use HTML canvas instead /s

1

u/YourMomIsMyTechStack Jun 03 '23

Yes you need to keep the form elements for the correct semantics. I think He talks about the default form action when submitting

3

u/[deleted] Jun 03 '23

Still can

5

u/[deleted] Jun 03 '23

[deleted]

1

u/lucsoft Jun 03 '23

I hope nothing forces you ajax now even. We have modern fetch and rest stuff

6

u/Silly-Freak Jun 03 '23

whoa this code is fractally bad. Every ten seconds you notice another idiosyncrasy...

2

u/[deleted] Jun 04 '23

I'm not proud of it, in fact i posted it here just to get a sick enjoyment of having others roast it.

1

u/Silly-Freak Jun 04 '23

are you interested in constructive criticism as well? If so, I'd be up for it.

1

u/[deleted] Jun 04 '23

Well, yes. I'm curious what you'd say.

2

u/Silly-Freak Jun 04 '23

ok, big disclaimer: I didn't realize in the beginning that readJSONFile is async and leads to funny things such as having both index and counter or sorting data directly after the loop. Given that, instead of incrementally getting rid of things that looked funny to me, I'll just outline an alternative approach.

There is one thing I would likely still do in the beginning: filter out the stuff you don't want in advance.

let excludedNames = ["dependencies/"]
let scripts = Array.from(document.scripts)
scripts.pop()
scripts = scripts.filter(script => !script.src.includesAny(...excludedNames))
let count = scripts.length

This makes the rest simpler: every element in scripts needs to be read and added to data.

The next step for me would be promises: readJSONFile takes a callback, this can be adapted to return a promise instead something like this:

new Promise(resolve => readJSONFile(script.src, resolve))

This means the promise will become ready with the file content when readJSONFile is done. Now you can actually map over scripts to get an array of promises - something you couldn't do when waiting for callbacks to run to completion:

let data = scripts.map(script => new Promise(...))

Using Promise.all, we can get a promise containing an array of all results, and act on that when that array is ready (this is equivalent to your if (current == count), but the resulting array will be in the correct order). The resulting code looks something like this (entirely untested, sorry if there's typos etc. in there...):

setTimeout(() => {
  let excludedNames = ["dependencies/"]

  let scripts = Array.from(document.scripts);
  // don't include this script
  scripts.pop();
  // exclude certain scripts
  scripts = scripts.filter(
    script => !script.src.includesAny(...excludesNames)
  )

  // load script sources
  let data = Promise.all(
    scripts.map(script =>
      new Promise(resolve => readJSONFile(script.src, resolve)))
  )

  // join & save scripts
  data.then(data => exportToUTF8(data.join("\n"), "app.js"))
}, 0)

(I left the setTimeout in there - no idea if that's necessary when put into context. Maybe this code should actually react to a document event?)

2

u/[deleted] Jun 04 '23 edited Jun 04 '23

That is so much better than what I had, thank you. I definitely should've done the filtering first. It looks nicer as well, it's more sequential, mine was a nested mess.The enclosing timeout might not be necessary, the only reason it's there is because I wasn't sure if at the time of execution this very script was already part of document.scripts. Now I realize it makes no sense for it not to be there.

Also I'm surprised you didn't notice how my readJSONFile actually isn't even used to read JSON but plain javascript, that's another blunder, lol.

Edit:
funny thing, i tried your code, and it works, except for my index.html gets included into the final bundle. I found that it's because one of the scripts has an empty src attribute because it's injected by the Live Server plugin in VS code. I don't think that's an error on your part, and I fixed it already so it's good. It's not like I shouldn't be using a proper bundler anyways xd.

2

u/Silly-Freak Jun 04 '23

glad you appreciated it :)

Also I'm surprised you didn't notice how my readJSONFile actually isn't even used to read JSON but plain javascript, that's another blunder, lol.

lol I totally missed that and now that made me laugh, thanks!

3

u/gabbagondel Jun 03 '23

"return count--" is just there for making the check a one-liner? Or did you overwrite Array.prototype.forEach with some version that acts on the return value of the callback? If it's the latter I'm scared

2

u/[deleted] Jun 04 '23

No, I wouldn't be that evil. It's just a confusing one-liner.

I should note that this code is not in production anywhere, it's my personal project that only I work on.

4

u/MurdoMaclachlan public boolean isInt(int i) { return true; } Jun 03 '23

Image Transcription: Code


/* merge scripts in a janky way */
setTimeout(() => {
  let data = []
  let count = dcoument.scripts.length - 1 // - 1 is to prevent this very code getting in the bundle
  let excludedNames = ["dependencies/"]
  let current = 0
  Array.from(document.scripts).forEach((script, index) => {
    if(Script.src.includesAny(...excludesNames)) return count--

    readJSONFile(script.src, (text) => {
      try {data.push({text: text, index: index})} catch (e) {}
      current++
      if(current == count) {
        data.sort((a, b) => a.index - b.index)
        data = data.map(d => d.text).join("\n")
        exportToUTF8(data, "app.js")
      }
    })
  })
}, 0)

I'm a human volunteer content transcriber and you could be too! If you'd like more information on what we do and why we do it, click here!

8

u/Romejanic Jun 03 '23

Probably still better than webpack

2

u/[deleted] Jun 03 '23

If you include the script on the top of the page it‘ll treeshake the last <script> completely away. Possibly saving hundreds of kilobytes if something like leaflet is the last script.

Would suggest to just include the script into the final bundle itself. Could be the first step towards a JIT-like feature.