ast-grep --pattern 'var code = $PAT' --rewrite 'let code = $PAT' --lang js
https://ast-grep.github.io/guide/introduction.html469 karma · joined November 28, 2023
ast-grep --pattern 'var code = $PAT' --rewrite 'let code = $PAT' --lang js
https://ast-grep.github.io/guide/introduction.htmlThe issue you linked to is referring to the case in which you import a namespace object and then re-export it. Bundlers like webpack and rollup (which vite uses in production) can tree shake this pattern as well, but esbuild struggles with it.
If you're using esbuild instead of this:
import * as someLibrary from "some-library"
someLibrary.someFunction()
export { someLibrary }
You can still do this: import * as someLibrary from "some-library"
someLibrary.someFunction()
export * from "some-library"
export { default as someLibraryDefault } from "some-library"
Tree shaking works as expected for downstream packages using esbuild in the second case, which someone else in the linked issue pointed out: https://github.com/evanw/esbuild/issues/1420#issuecomment-96... import * as someLibrary from "some-library"
someLibrary.someFunction()
Which works pretty well with IDE autocomplete in my experience.[1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
This of course requires your build tool to actually understand the TS type system, which is why it's not supported in tools like esbuild and tsx (which uses esbuild under the hood).
Yes, the JSON modules proposal is finished.
https://github.com/tc39/proposal-json-modules
https://caniuse.com/mdn-javascript_statements_import_import_...
const supportsRequestStreams = (() => {
let duplexAccessed = false;
const hasContentType = new Request('http://localhost', {
body: new ReadableStream(),
method: 'POST',
get duplex() {
duplexAccessed = true;
return 'half';
},
}).headers.has('Content-Type');
return duplexAccessed && !hasContentType;
})();
Safari doesn't appear to support the duplex option (the duplex getter is never triggered), and Firefox can't even handle a stream being used as the body of a Request object, and ends up converting the body to a string, and then setting the content type header to 'text/plain'.Not being able to import TypeScript files without including the ts extension is definitely annoying. The rewriteRelativeImportExtensions tsconfig option added in TS 5.7 made it much more bearable though. When you enable that option not only does the TS compiler stop complaining when you specify the '.ts' extension in import statements (just like the allowImportingTsExtensions option has always allowed), but it also rewrites the paths if you compile the files, so that the build artifacts have the correct js extension: https://www.typescriptlang.org/docs/handbook/release-notes/t...
No, you can allow access to specific domains, IP addresses, filesystem paths, environment variables, etc, while denying everything else by default. You can for instance allow access to only a specific IP (e.g. `deno run --allow-net='127.0.0.1' main.ts`), while implicitly blocking every other IP.
What the commenter is complaining about is the fact that Deno doesn't check which IP address a domain name actually resolves to using DNS resolution. So if you explicitly deny '1.1.1.1', and the script you're running fetches from a domain with an A record pointing to '1.1.1.1', Deno will allow it.
In practice, I usually use allow lists rather than deny lists, because I very rarely have an exhaustive list on hand of every IP address or domain I'm expecting a rogue script to attempt to access.
I won't speak for the author of the proposal, but given that Lit itself has special control flow mechanisms for conditionals and loops (the cache and repeat directives respectively [2][3]), I can't imagine the proposal being too opposed to these mechanisms.
[1] https://github.com/preactjs/signals/blob/eae850a9f3aa62e505a...
[2] https://lit.dev/docs/templates/conditionals/#caching-templat...
[3] https://lit.dev/docs/templates/lists/#the-repeat-directive
Haven't these tagged template libraries coalesced around the html`<${SomeComponent}><//>` syntax for custom components (including control flow components like Solid's `For` component)? The readme for Ryan Carniato's Lit DOM Expressions library includes this example for instance [1]:
const view = html`
<table class="table table-hover table-striped test-data">
<tbody>
<${For} each=${() => state.data}
>${row => html`
<tr>
<td class="col-md-1" textContent=${row.id} />
<td class="col-md-4">
<a onClick=${[select, row.id]}>${() => row.label}</a>
</td>
<td class="col-md-1">
<a onClick=${[remove, row.id]}
><span class="glyphicon glyphicon-remove"
/></a>
</td>
<td class="col-md-6" />
</tr>
`}<//
>
</tbody>
</table>
`;
The author of the article mentions this very briefly, where he writes "For JSX-style references you would need to use binding syntax like <${MyComponent}>". The Preact author's htm tagged template library uses this convention as well [2].[1] https://github.com/ryansolid/dom-expressions/tree/7fd9f86f1b...
The example I was responding to was using the Declarative Shadow DOM. My comment was intended to point out the simple fact that the imperative component definition the author was complaining about is superfluous, meaning you can safely remove that entire script from the example.
Exactly, I'm not sure why you've included the JS. The whole point of the Declarative Shadow DOM is to create shadow roots declaratively, rather than imperatively. To quote web.dev "This gives us the benefits of Shadow DOM's encapsulation and slot projection in static HTML. No JavaScript is needed to produce the entire tree, including the Shadow Root." [1]
[1] https://web.dev/articles/declarative-shadow-dom#how_to_build....
<my-component>
<template shadowrootmode="open">
<style>
::slotted(*) {
font-weight: bold;
font-family: sans-serif;
}
</style>
<slot></slot>
</template>
<p>content</p>
</my-component>The mode does not toggle the shadow DOM on and off. It just specifies whether or not the element's shadowRoot object is a public property. An open mode makes this possible: `document.querySelector('my-component').shadowRoot`.
You have to explicitly opt-in to the shadow DOM either imperatively by calling `attachShadow`, or declaratively in HTML by giving the element a template child element with a `shadowrootmode` attribute.
> Also there's arguably design. Should a 'glob' library actually read the file system and give you filenames or should it just tell you if a string matches a glob and leave the reset to you?
There's a function in Node's stdlib that does this as well (albeit it's marked as experimental): https://nodejs.org/docs/latest-v24.x/api/path.html#pathmatch...
My argument does not hinge upon the definition of the word inevitable. You originally said "I mean, I can't think of a time a high profile project written in a lower level representation got ported to a higher level language."
I gave a relatively thorough accounting of why you've observed this, and why it doesn't indicate what you believe it to indicate here: https://news.ycombinator.com/item?id=43339297
Instead of addressing the substance of the argument you focused on this introductory sentence: "I'd like to address your larger point which seems to be that all greenfield projects are necessarily best suited to low level languages."
Regardless of how narrowly or widely you want me to interpret your stance, my point is that the data you're using to form your opinion (rewrites from higher to lower level languages) does not support any variation of your argument. You "can't think of a time a high profile project written in a lower level representation got ported to a higher level language" because developers tend to be more hesitant about reaching for lower level languages (due to the higher barrier to entry), and therefore are less likely to misuse them in the wrong problem domain.
> If I said "If you drink and drive you will inevitably get into an accident" - would you argue against that statement?
If we were having a discussion about automobile safety and you wrote several hundred words about why a specific type of accident isn't indicative of a larger trend, I wouldn't respond by cherry picking the first sentence of your comment, and quoting Google definitions about a phone ringing.
The first comment I wrote in this thread was a response to the following quote: "Yet projects inevitably get to the stage where a more native representation wins out." Inevitable means impossible to evade. That's about as close to a black and white statement as possible. You're also completely ignoring the substance of my argument and focusing on the wording. My point is that language rewrites (like the TS rewrite that sparked this discussion) are a faulty indicator of scripting language quality.
> I almost think you aren't reading my post at this point and are just arguing with a strawman you invented in your head. But I am assuming good faith on your part here, so once again I'll just repeat myself again and again: LLMs have already changed the barrier to entry for low-level languages and they will continue to do so.
And I've already said that I disagree with this assertion. I'll just quote myself in case you haven't read through all my comments: "I'm not an AI pessimist, but I'm also not an AI maximalist who is convinced that AI will completely eliminate the need for human code authoring and review, and as long as humans are required to write and review code, then those benefits [of scripting languages] still apply." I was under the impression that I didn't have to keep restating my position.
I don't believe that AI has eroded the barriers of entry to the point where the average Ruby or PHP developer will enjoy passing around memory allocators in Zig while writing API endpoints. Neither of us can be 100% certain about what the future holds for AI, but as someone else pointed out, making technical decisions in the present based on AI speculation is a gamble.
First of all, I would argue that software rewrites are a bad proxy metric for language quality in general. Language rewrites don't measure languages purely on a qualitative scale, but rather on a scale of how likely they are to be misused in the wrong problem domain.
Low level languages tend to have a higher barrier to entry, which as a result means they're less likely to be chosen on a whim during the first iteration of a project. This phenomenon is exhibited not just at the macroscopic level of language choice, but often times when determining which data structures and techniques to use within a specific language. I've very seldomly found myself accidentally reaching for a Uint8Array or a WeakRef in JS when a normal array or reference would suffice, and then having to rewrite my code, not because those solutions are superior, but because they're so much less ergonomic that I'm only likely to use them when I'm relatively certain they're required.
This results in obvious selection bias. If you were to survey JS developers and ask how often they've rewritten a normal reference in favor of a WeakRef vs the opposite migration, the results would be skewed because the cost of dereferencing WeakRefs is high enough that you're unlikely to use them hastily. The same is true to a certain extent in regards to language choice. Developers are less likely to spend time appeasing Rust's borrow checker when PHP/Ruby/JS would suffice, so if a scripting language is the best choice for the problem at hand, they're less likely to get it wrong during the first iteration and have to suffer through a massive rewrite (and then post about it on HN). I've seen plenty of examples of competent software developers saying they'd choose a scripting language in lieu of Go/Rust/Zig. Here's the founder of Hashicorp (who built his company on Go, and who's currently building a terminal in Zig), saying he'd choose PHP or Rails for a web server in 2025: https://www.youtube.com/watch?v=YQnz7L6x068&t=1821s
WASM is used to generate the query plan, but query execution now happens entirely within TypeScript, whereas under the previous architecture both steps were handled by Rust. So in a very literal sense some of the Rust code is being rewritten in TypeScript.
> Basically, if the majority of your application is already in JavaScript and expects primarily to interact with other code written in JavaScript, it usually doesn't make sense to serialize your data, pass it to another runtime for some processing, then pass the result back.
My point was simply to refute the assertion that once software is written in a low level language, it will never be converted to a higher level language, as if low level languages are necessarily the terminal state for all software, which is what your original comment seemed to be suggesting. This feels like a bit of a "No true Scotsman" argument: https://en.wikipedia.org/wiki/No_true_Scotsman
> As for the "compilers are special" reasoning, I don't ascribe to it.
Compilers (and more specifically lexers and parsers) are special in the sense that they're incredibly well suited for languages with shared memory multithreading. Not every workload fits that profile.
> The old cases I would choose the scripting language (familiarity, speed of adding new features, ability to hire a team quickly) seem to be eroding in the face of LLMs.
I'm not an AI pessimist, but I'm also not an AI maximalist who is convinced that AI will completely eliminate the need for human code authoring and review, and as long as humans are required to write and review code, then those benefits still apply. In fact, one of the stated reasons for the Prisma rewrite was "skillset barriers". "Contributing to the query engine requires a combination of Rust and TypeScript proficiency, reducing the opportunity for community involvement." [1]
[1] https://www.prisma.io/blog/from-rust-to-typescript-a-new-cha...
Prisma is currently being rewritten from Rust to TypeScript: https://www.prisma.io/blog/rust-to-typescript-update-boostin...
> Yet projects inevitably get to the stage where a more native representation wins out.
I would be careful about extrapolating the performance gains achieved by the Go TypeScript port to non-compiler use cases. A compiler is perhaps the worst use case for a language like JS, because it is both (as Anders Hejlsberg refers to it) an "embarassingly parallel task" (because each source file can be parsed independently), but also requires the results of the parsing step to be aggregated and shared across multiple threads (which requires shared memory multithreading of AST objects). Over half of the performance gains can be attributed to being able to spin up a separate goroutine to parse each source file. Anders explains it perfectly here: https://www.youtube.com/watch?v=ZlGza4oIleY&t=2027s
We might eventually get shared memory multithreading (beyond Array Buffers) in JS via the Structs proposal [1], but that remains to be seen.
[1] https://github.com/tc39/proposal-structs?tab=readme-ov-file
It would essentially help you produce TypeScript that's compatible with the --experimental-strip-types flag (and ts-blank-space), rather than the --experimental-transform-types flag, which is nice because (as someone else in this thread pointed out), Node 23 enables the --experimental-strip-types flag by default: https://nodejs.org/en/blog/release/v23.6.0#unflagging---expe...
Apparently they're planning on adding a tsconfig option to disallow these Node-incompatible features as well [1].
Using this limited subset of TS also allows your code to compile with Bloomberg's ts-blank-space, which literally just replaces type declarations with whitespace [2].