bun icon indicating copy to clipboard operation
bun copied to clipboard

Add a flag to preserve comments during transpilation

Open kakserpom opened this issue 2 years ago • 2 comments

What is the problem this feature would solve?

Comments like this one are currently stripped out: image

I need them at runtime to parse from func.toString()

What is the feature you are proposing to solve the problem?

A flag to preserve comments during transpilation

What alternatives have you considered?

No response

kakserpom avatar Mar 20 '23 14:03 kakserpom

I'd also like to use comments for annotations for subsequent build steps, such as https://github.com/terser/terser#annotations.

maxmilton avatar Dec 04 '23 01:12 maxmilton

there probably should be an option to persist vite-ignore comments, too. Unsure how to avoid them otherwise

chrisbbreuer avatar Jul 18 '24 11:07 chrisbbreuer

The opposite should be the default. Comments should be preserved by default, and removed only when instructed to be removed. Comments are there for a reason.

This is how other JavaScript runtimes handle comments, from here originally https://github.com/oven-sh/bun/issues/8727#issuecomment-2440431738

@paperdave Consider what this says:

$ bun build --help | grep no-bundle
      --no-bundle                     Transpile file only, do not bundle

That "Transpile file only" language is important. There's no expectation that anything other than transpilations will occur.

bun build --no-bundle also removes shebang lines.

Just for completeness, Node.js' amaro when node --experimental-strip-types is used, to only transpile TypeScript source code to JavaScript, doesn't remove comments.

bun install amaro
bun build node_modules/amaro/dist/index.js --target=node --outfile=nodejs-amaro-bundle.js 

strip-types.js

import * as amaro from "./nodejs-amaro-bundle.js";
import { readFileSync, writeFileSync } from "node:fs";
import * as process from "node:process";
const { load, transformSync } = amaro.default;
const url = new URL(process.argv.at(-1), import.meta.url);
let { code } = transformSync(readFileSync(url.pathname));
// TODO Remove space characters where types were @guest271314
// code = code.replace(/\s+(?=[,=()])/g, "").replace(/\s+(?=[{])/g, " ");
console.log(code);
writeFileSync(url.pathname.split("/").at(-1) + ".js", code);
bun run strip-types.js ../path/to/file.ts

or

node strip-types.js ../path/to/file.ts

though does leave multiple space characters where the types were defined

const runtime         = navigator.userAgent;
const buffer              = new ArrayBuffer(0, { maxByteLength: 1024 ** 2 });
const view           = new DataView(buffer);
const encoder              = new TextEncoder();

let readable                                                            ,
  writable                            ,
  exit             = () => {};

// ...

function encodeMessage(message        )             {

// ...


async function* getMessage()                             {

// ...

Deno transpiles and preserves comments when deno install --entrypoint ../path/to/file.ts

// Transpile TypeScript to JavaScript using Deno built-ins
// Usage: deno -A deno-ts-js-cache.js path/to/file.ts
// Write Deno's generated cache .ts.js file to stdout and current directory
// ts: Path to .ts script
const [ts] = Deno.args;
const url = new URL(import.meta.resolve(ts));
const { pathname } = url;
const filename = pathname.split("/").at(-1);
const decoder = new TextDecoder();
// Path to generated cache .ts.js script
const jsCache = `file${pathname}.js`;
// info: Get Deno cache and TypeScript cache subdirectories
const info = new Deno.Command(Deno.execPath(), {
  args: [
    "info",
    "--json",
  ],
});
// Deno cache directory and generated TypeScript subdirectory
const { denoDir, typescriptCache } = JSON.parse(
  decoder.decode((await info.output()).stdout),
);
// Cache
// https://docs.deno.com/runtime/fundamentals/modules/#vendoring-remote-modules
const command = await new Deno.Command(Deno.execPath(), {
  args: ["install", "--entrypoint", "-r", ts],
}).output();
// Generated cache .ts.js file
const js = (await Deno.readTextFile(`${typescriptCache}/${jsCache}`))
  .replace(/\/\/#\ssourceMappingURL=.+\n\/\/.+$/img, "");
console.log(js);
await Deno.writeTextFile(`${filename}.js`, js);
Deno.exit(0);
deno -A deno-ts-js-cache.js ../path/to/file.ts 
#!/usr/bin/env -S /home/user/bin/bun run

// ...

// Resizable ArrayBuffer supported by tsc Version 5.7.0-dev.20241019
import process from "node:process";
const runtime = navigator.userAgent;
const buffer = new ArrayBuffer(0, {
  maxByteLength: 1024 ** 2
});
const view = new DataView(buffer);
const encoder = new TextEncoder();
let readable, writable, exit = ()=>{};
if (runtime.startsWith("Deno")) {
  // @ts-ignore Deno
  ({ readable } = Deno.stdin);
  // @ts-ignore Deno
  ({ writable } = Deno.stdout);
  // @ts-ignore Deno
  ({ exit } = Deno);
}
if (runtime.startsWith("Node")) {
  readable = process.stdin;
  writable = new WritableStream({
    write (value) {
      process.stdout.write(value);
    }
  }, new CountQueuingStrategy({
    highWaterMark: Infinity
  }));
  ({ exit } = process);
}
if (runtime.startsWith("Bun")) {
  // @ts-ignore Bun
  readable = Bun.file("/dev/stdin").stream();
  writable = new WritableStream({
    async write (value) {
      // @ts-ignore Bun
      await Bun.write(Bun.stdout, value);
    }
  }, new CountQueuingStrategy({
    highWaterMark: Infinity
  }));
  ({ exit } = process);
}

bun build --no-bundle does not preserve comments, including removing the shebang line

bun build ~/path/to/file.ts --no-bundle --outfile=filets.js
import process from "node:process";
const runtime = navigator.userAgent;
const buffer = new ArrayBuffer(0, { maxByteLength: 1024 ** 2 });
const view = new DataView(buffer);
const encoder = new TextEncoder;
let readable, writable, exit = () => {
};
if (runtime.startsWith("Deno")) {
  ({ readable } = Deno.stdin);
  ({ writable } = Deno.stdout);
  ({ exit } = Deno);
}
if (runtime.startsWith("Node")) {
  readable = process.stdin;
  writable = new WritableStream({
    write(value) {
      process.stdout.write(value);
    }
  }, new CountQueuingStrategy({ highWaterMark: 1 / 0 }));
  ({ exit } = process);
}
if (runtime.startsWith("Bun")) {
  readable = Bun.file("/dev/stdin").stream();
  writable = new WritableStream({
    async write(value) {
      await Bun.write(Bun.stdout, value);
    }
  }, new CountQueuingStrategy({ highWaterMark: 1 / 0 }));
  ({ exit } = process);
}

guest271314 avatar Oct 28 '24 04:10 guest271314

The opposite should be the default. Comments should be preserved by default, and removed only when instructed to be removed.

I agree with this, we are on the same page. The code that tracks comments doesn't exist. When it does, I think it should be active when --minify-whitespace is disabled.

I have had times where I had to flip between source code and bundled code to read comments, when this is not the case with any other bundler.

paperclover avatar Oct 28 '24 07:10 paperclover

Looks like Node.js' amaro doesn't need to be since last week or so. transform does remove comments, strip doesn't, and doesn't format space characters where types were

node node-strip-types.js ../path/to/file.ts strip
node node-strip-types.js ../path/to/file.ts transform
// https://github.com/nodejs/node/commit/53b1050e6f692ee0330e1076e045b58aada0032d#diff-4e8f3cce79719e4a337f58575b20c998b093eb64164b847ca0eb9ba884d8a801R338
import { stripTypeScriptTypes } from "node:module";
import { readFileSync, writeFileSync } from "node:fs";
import { stdout } from "node:process";
const [, , sourceTs, mode = "transform", sourceUrl = sourceTs, sourceMap = false] = process.argv;
const code = readFileSync(sourceTs, "utf8");
const strippedCode = stripTypeScriptTypes(code, { mode, sourceUrl, sourceMap });
stdout.write(strippedCode);

guest271314 avatar Nov 01 '24 03:11 guest271314