Deno Is Webby
blog.jim-nielsen.com
blog.jim-nielsen.com
import 'https://cdn.jsdelivr.net/npm/marked@3.0.7/marked.min.js';
// now I can use window.marked()EDIT: I was wrong. See @spoiler's reply.
It's not part of a package manager, because there isn't one, though.
*scare-quotes because the reader might feel strongly opposed to the term, not because I have an agenda
Oh wow, I had no idea. That rules!
All I can think of is "using query string as getenv() for random debug hacks" or "using history as a hack to synchronously reach the structured clone algorithm", neither of which a library should do (but both easily emulatable).
That said, there are other browser APIs that I would love to see available elsewhere:
- window.crypto as an interface over libcrypto
- window.localStorage and window.sessionStorage as an abstraction over temporary files and an in-memory key/value store, respectively
- window.indexedDb for an in-runtime DB à la Erlang Term Storage or MUMPS
- window.opener, window.parent, window.open(), window.postMessage() for inter-process communication
There's a lot to work with here if you get creative.
The first attempt at this was to port the node standard library to the browser via browserify and the like. This tended to create gigantic JS build artifacts and frequently led to runtime errors when developers used an abstraction that just had no equivalent in the browser (like saving something to disk or opening a separate process). The browser is definitely the lowest common denominator of JS runtimes, even if there are a few browser APIs that just don't make sense in a server environment (like window.history)
node 17 is bringing the fetch api to core which to me makes perfect sense. theres definitely no point in node creating their own version of fetch. and fetch is useful on both the browser and server, given that fetch is an easy way to facilitate a client-server connection over http/s. perhaps the same applies to window.crypto.
i think you start to lose the benefit of standardization when: 1. half the api gets repurposed to try to fit a (in some cases, completely subjective) counterpart 2. functionality isn't 1:1
Doesn't this mean your code won't run if the website https://cdn.jsdelivr.net goes down?
In Node.js you download modules you need, to a local folder under you dev-folder. So you can run and develop and test your code whether you have internet connection or not.
I wonder if this could be a security issue. It's hard to know who has control over all those nested repositories, and who keeps a look on them to ensure they are not maliciously modified? Is anybody checking on cryptographic signatures of them?
Only asking because I don't know much about Demo.
Ah. The enievitable "just".
And how exactly do you set the import map to point to the private server for the transitive dependencies?
Deno's own docs don't bother with such trivialities and show a very toy example, of course, https://deno.land/manual/linking_to_external_code/import_map...
https://github.com/WICG/import-maps https://deno.land/manual@v1.20.1/linking_to_external_code/im...
If that's still not enough, you can also just download the JS file and import from a local path or use the vendoring feature.
Deno’s vendoring approach isn’t like a stopgap in case third party servers go down. It’s the ’right’ way in Deno, and is another brilliant fix to Node’s approach.
Node always makes you go through a formal step to install a dependency - and then you’re still reliant on npm’s CDN being online during your builds anyway. The only way to break that build time dependency is to use a clunky, nonstandard hack to check in your node_modules, like yarn 2.0 or pnpm, which then takes you right off Node’s “golden path” (such as it is) and creates loads of incompatibilities.
Deno’s approach is the best of both worlds.
1. With Deno you can import direct from a URL with no formal install step, which is great for prototyping. The caching engine makes it fast without a need for every little toy project to have to have a giant node_modules folder.
2. Vendoring your scripts provides a dead easy, golden-path way to make your project completely non-dependent on third party during build, ie gives you same advantage as using Yarn 2 or pnpm in Node, but as a first class citizen of the runtime, so it’s efficient and standardised and will therefore not have tons of ecosystem incompatibilities.
Why? I'm building my program out of components in my node_modules -folder. Why do I need internet?
Porn companies have decided basically every format war (including pioneering streaming).
https://www.youtube.com/watch?v=FyKRubB5N60&list=PLU_9ndwi4C...
> console.log("%cHello World", "color: red");
TIL, neat! If anyone else did too - https://developer.mozilla.org/en-US/docs/Web/API/console#sty... looks great
Using alert/confirm/prompt for CLI tools is nice too, makes a lot of sense to build that in and use the web APIs for it.
And the right thing to resist doing either. It's to think about what "services" your program actually needs to be able to function and then to isolate those parts from the rest of your application by putting it behind a well-defined interface. In other words, the best way to fix the incompatibility problem caused by API mismatches is to never make the mistake of coding directly against the host platform's APIs to begin with. Trying to do it with compatibility shims to make one platform look like the other is a fool's errand. You end up running around trying to achieve parity with a mammoth API surface area (which might never have been especially well-designed to begin with...).
Let's say you're implementing a program similar in scope to UNIX's file(1). For our example, though, suppose you're really only concerned with text files and specifically whether a given text file is using DOS-style CRLF line separators or UNIX-style LF terminators. You really only need two capabilities: a `read` operation to get the contents of a file and a `print` operation to show the output to the user. Does your program care whether that `read` is happening with a Web standards-backed FileReader or NodeJS's proprietary `fs` module? There's no reason it should. Design the best "system" layer that makes the most sense for your application's needs.
You can see this implementation strategy in the way the TypeScript team wrote the code for the TypeScript compiler itself when it was made public. Even in the early days, it could run on multiple platforms—including NodeJS, JScript/Windows Script Host, and various browsers (old or new)—because it didn't overly concern itself with anything except its real job of lexing, parsing, and type-checking its inputs—wherever they came from—and then writing the output in a platform-agnostic way.
It seems like you're making a good point, but I'm confused by your comment. Do you believe we should avoid coding directly against the host platform's APIs, or do believe we should avoid compatibility shims? At some point, you need compatibility shims in order to avoid coding directly against the host platform's APIs. The most that your runtime strives to match existing runtimes, the less you need to worry about writing shims to improve compatibility.
> Do you believe we should avoid coding directly against the host platform's APIs, or do believe we should avoid compatibility shims?
Avoid compatibility shims that lead to you writing your application's logic directly against platform APIs.
You recognize that you need to read a file, so you write your application logic against the simplest possible interface you can think of that would let you do that: `read`.* You don't try to emulate either platform's API. This sounds like a bad deal, because neither platform provides native support for this interface, and the other approach is tempting, because you already get one implementation of the API for "free" (on whatever platform where that API is native), and you'd "only" have to write the compatibility shim for the other one (or re-use somebody else's shim). This is a mistake. For one thing, people end up misjudging the cost/benefits of each, and for another, the compatibility shim doesn't make the program easier to understand. You end up with quirks of that API infecting increasingly more parts of the program.
* What you don't do is go off and design the best, elaborate, pluggable, most extensible abstraction layer that you can think of. You implement `read`.
I regularly read this advice and I think it's bad in general. If you only ever work against an abstraction, you lose time and might get a worse end result than the one without the abstraction. And in the end, you might throw it away, if it doesn't prove successful. So move the cost for working with an abstraction into the future as much as possible, when you know the alternatives you have to abstract over.
There’s a distinct set of skills and instincts involved in recognizing a generalization of a problem, and a narrower one in recognizing matching solutions which are actually suited to the specific problem. It’s not a skill/instinct everyone has, but it’s not one that should be immediately dismissed either. And one’s recognition of both can grow and be refined over time.
I also think this kind of balanced flexibility should be something we demand as a baseline assumption of the craft:
- We’re going to have instincts no matter what, and we should be encouraged to explore them
- Some of those instincts will prove right, some wrong; often “wrong” will be grey and murky and temporally separated from trying
- When we’ve recognized the error, we should allocate time to correct it with what we’ve learned
- As this process recurses, we should assess whether our predictive senses are getting closer to reality, and adjust our risk impulse accordingly
I really hope we will have something like that. It would both give us the option of never using magical globals and make the distinction clear whether the module comes from the language or the runtime. Although I can see a case where it could get annoying if you are writing for multiple runtimes and need to get e.g. fetch (import fetch from "runtime:fetch").
There seems to be a big gap in knowledge when it comes to boundaries between the language specification, runtimes, and innate abilities.
People consistently confused about why they can't use JSX without a build-step, not understanding that JSX isn't "real", why doesn't fetch() work in Node, why does code in my Next.js app break randomly (server vs client render context).
Lot of pain in trying to explain to folks that a browser and Node are two different universes that happen to speak the same general language.
There's too much magic in this ecosystem and not enough emphasis placed on taking the time to understand the tools you work with, IMO.
That's exactly why it's so good that Deno is trying to close the gap. More consistency = fewer surprises
The things that make JS unique aren't even mentioned in a lot of degree programs.
Larry Wall famously said the three virtues of a programmer are impatience, laziness, and hubris. Not learning JS is at the exact intersection where people do all of these at the same time and meet with epic failure. People seem to think to themselves, "I learned Java and this looks like Java, so it must act like java" and so they run down the completely wrong path.
They wouldn't dream of this with C, Java or even python (even though these probably work exactly like other languages they know -- unlike JS), but for some reason perfectly smart developers make this crazy decision all the time.
It's because they can. You can cobble something together and it works or seems to work. It may fail later down the chain but browsers accept all sorts of hackery, because they have been historically doing this for a long time. Before there was Javascript you could write broken HTML, nest elements wrong and browsers still figured out a way to deal with it. The expectations of half-assed solutions has always been there on the web.
Actually it does now! Or it will soon.
https://fusebit.io/blog/node-fetch/?utm_source=www.google.co...
(They want to do that because alert() "stops the world" by blocking the main thread event loop, and because they make it easy for a site to post a message that appears to come from Chrome itself, or from another website.)
https://groups.google.com/a/chromium.org/g/blink-dev/c/hTOXi...
> We’re on a long, slow path to deprecate and remove window.alert/confirm/prompt and beforeunload handlers due to their role in user-hostile event loop pausing, as well as phishing and other abuse mechanisms. We’ve been successfully chipping away at them in various cases, e.g. background tabs, subframes with no user interaction, and now cross-origin subframes. Each step is hard-fought progress toward the eventual goal, and we should consider carefully whether we want to regress, even in an opt-in manner.
Rich Harris has a good blog post about this. https://dev.to/richharris/stay-alert-d
But on the serious side, this is useful for many antiquated apps (e.g. government stuff) that break tons of stuff if you attempt to re-submit a form, use the back button, etc, so I see that causing a ton of problems.
I still haven’t found a way to consistently send a request while the tab/browser is closing.
Why would you need it to be consistent? Depending on this for anything other than analytics is bad design. It’s like catching sigterm on desktop.
Ok, alert is ugly, but for a lot of situations (= industrial software) it's perfectly acceptable.
alert('whatever');
This cannot: const answer = confirm('A querstion?');
const value = doSomethingWithUserInput(answer);
return doSomethingWithValue(value);
The API depends on it blocking the main thread, and removing that expectation is effectively just as disruptive as removing the feature entirely: non-abusive usage would have no reason to call this function if not to block for user input. Introducing some magical suspension of the stack like async/await or generators would break assumptions about the entire concurrent execution model of the language (the inverse problem of “colored functions”, infectious async/await etc).The best thing they can do without removing the API is provide an escape hatch, which most browsers already do when alert &co are called multiple times in quick succession.
Why should I as a user have to endure instagram and twitter blocking content with a delayed login prompt that cannot be canceled? Because they can. Browser APIs or not malicious developers will be malicious.
> We can't normalise the attitude that collateral damage is the price of progress, even if we accept the premise — which I don't — that removing APIs like alert represents progress. For all its flaws, the web is generally agreed to be a stable platform, where investments made today will stand the test of time. A world in which websites are treated as inherently transient objects, where APIs we commonly rely on today could be cast aside as unwanted baggage by tomorrow's spec wranglers, is a world in which the web has already lost.
This is just normal. Is it confusing for a new programmer? Maybe. Is it something they should learn to distinguish? Yes!
I don’t think the answer to this is boolean. For instance:
> I can run C# in Unity. The number of functions I can call is vastly different than running it outside Unity.
If one is learning both in tandem, coming from a background with a language with different scoping rules, knowing the distinction even exists and should be a consideration might not even show up in reading materials. Sure, it’s an important skill to learn, but it’s not especially transferable. I know how it works in some environments and still find it baffling in others where I even know how to read the code. (Example: Java treats methods as locally scoped functions; I still have no idea how to distinguish them without running the code in a debugger. But I don’t write Java! I have a limited understanding of what’s in scope reading it.)
What things aren't like this?
I get that in serverless environments like in Deno deploy each instance gets one core etc but if you're running it on a VPS or just on a normal server I would want something like the cluster module like in node.
You can also do the same by deploying a worker through the dashboard UI (including writing the code). Nothing to link though because you need an account. The playground is limited in what it can do because it’s not deployed. You can also use Pages to point at a repo which lets you build a website and server side code (through Pages Functions which actually runs your code in a worker).
[1] https://developers.cloudflare.com/workers/learning/playgroun...
Since Workers started before Rust was a thing, Deno have a mild advantage on development velocity of the runtime (C++ is more annoying in some ways). They’ve also compounded that velocity by writing most of the runtime in Typescript whereas our runtime is in C++.
However, our runtime is far more battle tested at scale which means trickier distributed systems problems solved and more efficiencies of scale (+ c++ has less of a memory hit than TS so it will be interesting to see if they can hit the memory scale needed to support many customers simultaneously). Personally I wish them the best as I think competition is always critical to force you to focus on making a better product.
Can you explain more? Deno doesn't use typescript in the runtime. It is rust and pure js (for globals and wrapping bindings). I don't expect their current architecture to be any less efficient from what I know but it may have changed.
Doesn’t matter though because it’s the same (the only way to run TS is to transpile to JS)
https://github.com/denoland/deno/blob/main/runtime/js/README...
A good chunk of the runtime is in JS, snapshotted and loaded into each isolate. This is a greater memory overhead (and probably slower startup time) than having it written directly in Rust because the snapshot needs to be copied into each isolate. I assume Deno Deploy follows a similar model as Workers where you have many many isolates running concurrently within a process where this overhead may start to matter.
If Deno Deploy gets some scale, I’m curious how they’ll tackle. Less interesting is if they start inlining a good chunk of that functionality into Rust. It’s much more interesting if they figure out some way to improve v8 to reduce this cost. There’s some ways potentially if you could create a COW clonable isolate in v8 efficiently and it could be interesting to collaborate with them on it. But that’s hard. Maybe they have alternate plans.
The standards committees have partly advanced that and Deno is following those leads, but I've got my fingers crossed that Deno will also fill in gaps (and those will make their way back into standards committee considerations).
(My other answers have been more ... controversial, like: make [] false-y, add macros, and bring back `with`!)
“Make [] false-y”
Why? “Add macros”
Why? How? “Bring back `with`”
Why?If you want objects to behave like dictionaries, an empty object should also behave the same, by the way.
I'm not sure about macros.
I kind of want to see `with` back also. The problem with it was ambiguity. The syntax could be adjusted to avoid the ambiguity. .e.g `.prop = val;` could be legal inside the block. But "why though" you ask. A `with` block makes it visually obvious that a block of code is specifically relating to getters/setters on a particular object instance.
If I have a cup that's empty, there's still a cup there. It's presumptuous to assume I care about the contents.
Checking for presence of a number with a truthy check is a common source of errors, if 0 is a valid number.
I just don't agree at all that Python's interpretation of truthiness (or equality, for that matter) is inherently the "right" one. If I clone somebody are they equal because they have the same genetic makeup? No. Though a pretty contrived example that requires common understanding of equality.
At least from a programming perspective, I usually care more about instance equality rather than structural equality. Of course Python supports that too via the `is` keyword
But it's also not consistent with the ways that other empty but typed cups are treated ("" for the empty string, 0 for empty count / quantity, both of which are false-y). Different presumptions for different types of cups carries its own implicit hazards from context sensitivity.
Are they? All of them? 0:1 <≠> F:T? Boolean and binary are and should be distinct, and never the twain shall meet? A bold thesis in the world of computing.
Context-sensitive meaning is something people navigate all the time. You probably do it in some software contexts w/o even realizing it (and we all do it with human language and relationships w/o thinking consciously about it).
It's most likely to be confusing if you're unfamiliar with the context, or if the context-specific rules turn out to be convoluted.
0:1 maps on to F:T or even nothing:something or empty:non-empty pretty effortlessly. Even rigorously, with the right level of attention.
Implicit can mean subtle. Whether that means confusing (or whether there's some other cognitive load imposed by some forms of subtlety) is a separate question. And probably subjective.
Explicit can be a virtue. Concision can also be a virtue, and sometimes context-sensitivity promotes that.
Definitely it's biggest warts are around core data types - mainly the implicit-casting rules - but those are also impossible to change at this point. "Don't break the web" and all that.
And yeah, making [] false-y would probably do bad compatibility-breaking things. That's from the department of wishes more than good practical going-forward choices.
(I know BigInt is a thing and Temporal is coming.)
This also is or can be the case in C, C++, Clojure, Python, and many other languages [1].
It's also possible to make length 1 typed arrays of integers and operate on them if you really need. Then, as you mention there's BigInt numbers which are supported on the last few versions of every major browser except IE11.
import * as esbuild from "https://deno.land/x/esbuild@v0.14.25/mod.js";
If you want to bundle and minify SCSS, you can use dart-sass: import { useDartSass } from "https://raw.githubusercontent.com/MarkTiedemann/deno-dart-sass/0.1.0/mod.ts";How did people write semi elegant Ruby or python all these years, why wasn’t there such a massive push for types in those languages? Most likely because backend people chose the backend language of their choice, but on the frontend they detested JavaScript (you have no choice, you must JavaScript you anti authoritarian shit heads) so much they had to drown it with some kind of ketchup to make it edible (Typescript).
> why wasn’t there such a massive push for types in those languages
Are you just choosing to ignore all of the history of type checking Python and Ruby?
It’s not hard for me to imagine the reversal of this trend inevitably where everyone goes ‘the fuck are we writing all these verbose types for this dumb web app for?’.
> [why] are we writing all these verbose types for this dumb web app for?
You can take anything to the extreme, but types are a zero risk, low effort investment that has a quick return. If someone over-types something it's hardly a problem compared to an over engineered OOP codebase.
There are a bunch of people claiming this and that are OOP. To me OOP is encapsulating a mutable state inside a dynamic namespace (an object, an instance of class), with functions that can access the state and the ability to inherit / extend namespaces.
And I absolutely don't need it, I don't agree with the view some things are better done with OOP. Even gaming or GUI programming, domains typically considered to be the best for OOP, turned to ECS (which is very functional) and Elm style APIs.
Going back at OOP: I consider mutable state to be a necessary evil to be limited as much as possible; inheritance makes it hard to track what code is being run.
The best practice for writing OOP revolves around limiting mutable state and inheritance, so why even bother with OOP in the first place?
I can have encapsulation with namespaces / modules in functional languages as well. I don't need much else and I can live happily without `this` and using composition instead of inheritance.
OOP was the first marketing wave focused at developers and it's gone.
This leaves me skeptical if you have extensive experience with Typescript. It's way more than "oh this is a number not a string." It's "you forgot this property on an object's return type that you built from a response value" or "your Redux reducer doesn't handle all of the possible action types so it will crash at run time"
And what are all these dr. Strange multiverse possibilities you speak of? Any semi seasoned JS dev has spidey sense for watching out for null and undefined, and generally you should know the type of what you are returning. Is there that much variability in what you are dealing with? If it’s an array of objects, that’s not hard to hold in your brain, just watch out for empty arrays or null/undefined items.
Yes.
I don't mean any disrespect but again it sounds like you really haven't tried Typescript.
Spidey sense can, often and will fail.
That comment reeks of the same biases that C/C++ developers make when they admit that a majority of C/C++ bugs are related to hard-to-detect pointer/memory safety issues, but claim that they're a good C/C++ developer because their own code doesn't have such bugs, despite the bugs by their very nature being hard-to-detect.
That's from Deno's home page. What I'm talking about isn't whether TypeScript is useful for some projects, as that has been proven beyond question. What I'm saying is that as a tool for "quickly scripting", as claimed by Deno, it is unnecessary and counter productive to JavaScript as a whole.
My point is that the Deno project is a high profile JavaScript engine which, in my opinion, is misguided in their end-user focus on TypeScript and I think it's a shame.
Devs are always searching for the "right" way to do things, and can be easily convinced to do things like misuse "const" because some pedantic fool convinced them it was sorta like type safety, and therefore not using it was "baaaaaaad". And the sheep followed and now const is used everywhere, despite the clear and unambiguous intended functionality of the feature's designers, who added it as a way of tracking, incredibly, constants and nothing else.
It would be nice if we saved a generation of devs another debacle like const, NoSQL databases and UML.
Our solution is the right way to do things... for ourselves. Sorry that it upsets you.
It's important to choose the right tool for the job and SQL/ relational DBs fit many usecases but are not always the right choice. Especially when handling massive amounts of data, that can fit into structures such as wide-column stores, databases like scylla or columnar storage with clickhouse, can help massively.
Otherwise, it just ends up being a big flat in-memory table with a badly implemented version of SQL welded on top, written by developers who never bothered to learn SQL or database management in the first place. Then a bunch of fad followers jump on the bandwagon, and before you know it, we have a decade of idiots blathering on about "web scale" technologies.
That's the debacle I'm talking about. Millions of man hours wasted.
Also, having to do type definitions for modules which don't have types is a massive pain. Overall, I appreciate the type safety, but I don't think TS delivers on that promise and I'd rather use a real typed language.
What does is mean to "respect the devs right to choose which ever language they want"?
If it's like every other JavaScript project I've been on since 2016, it's going to actually require thousands of JavaScript packages and doing everything with it is going to be slow as molasses even on high-end developer workstations.
V8 was originally created as the javascript engine for Chrome, but it is used in many products now, including Node.js and Deno: https://v8.dev/
In PHP reinventing the wheel was one of the biggest issues.
Kinda fun to imagine a packaging system where you have to pay a very small crypto fee to publish. I'd bet less packages but more useful stuff on the whole.
(And its team will try to push the standard towards being as close to the current Typescript as possible)
Edit: I should perhaps listen before I speak -- the proposal linked above talks about JSDoc and why it does not fulfill their needs, which may be the case. I have not run in to these limitations myself.
Think about the old node library request vs fetch, require vs import/import(). I hope deno has Buffers and I won't have to use atob / btoa.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
Also using it where I'm not tied to Node by a specific dependency. There's some shimming surface towards getting Deno able to run node packages and vice-versa as well as ES-Module packaging of node repositories for use directly in the browser or deno, to more or less success.
But there's a proper sqlite driver now using the new ffi api support in deno. This didn't exist last time I tried googling db support in deno.
It's solving a problem no one has.
Standard library improvements are always welcome, though.