kyle-johnson / rollup-plugin-optimize-lodash-imports

10 stars 5 forks source link

[esbuild] could not interpret ts syntax #428

Closed ldu1020 closed 1 month ago

ldu1020 commented 4 months ago

There was a problem with using esbuild plugins in ts package among those modules. esbuild was an issue that could not interpret ts syntax,

I would like to write the loader value among the return values of onLoad in consideration of ts.

  build.onLoad({ filter: /.(js|ts|jsx|tsx)$/ }, (args) => {
   ...
            return {
              ...
              loader: 'ts' // or path.parse(args.path).ext.slice(1),
             }
   })
ldu1020 commented 4 months ago

In addition, since the acorn currently used internally does not respond to the typecript, it can be done by extending it using the acorn-typescript package.

const { tsPlugin } = __importStar(require("acorn-typescript"));
const wrappedParse = (code) => acorn.Parser.extend(tsPlugin()).parse(code, { ecmaVersion: "latest", sourceType: "module" });
ldu1020 commented 4 months ago
import fs from "fs";
import { parse } from "path";
import * as acorn from "acorn";
import { Plugin } from "esbuild";
import {
  ParseFunction,
  transform,
  UNCHANGED,
} from "@optimize-lodash/transform";

import { tsPlugin } from "acorn-typescript";

const wrappedParse: ParseFunction = (code) =>
  acorn.Parser.extend(tsPlugin()).parse(code, {
    ecmaVersion: "latest",
    sourceType: "module",
  });

export type PluginOptions = {
  useLodashEs?: true;
  appendDotJs?: boolean;
};

// TODO: filter https://golang.org/pkg/regexp/
export function lodashOptimizeImports({
  useLodashEs,
  appendDotJs = true,
}: PluginOptions = {}): Plugin {
  const cache = new Map<
    string,
    { input: string; output: string | UNCHANGED }
  >();

  return {
    name: "lodash-optimize-imports",
    setup(build) {
      build.onLoad({ filter: /.(js|ts|jsx|tsx)$/ }, async ({ path }) => {
        const input = await fs.promises.readFile(path, "utf8");
        const cached = cache.get(path); // TODO: unit test the cache

        if (cached && input === cached.input && cached.output === UNCHANGED) {
          return;
        }

        const result = transform({
          code: input,
          id: path,
          parse: wrappedParse,
          useLodashEs,
          appendDotJs,
        });
        if (result === UNCHANGED) {
          cache.set(path, { input, output: UNCHANGED });
          return;
        }

        const output = result.code;
        const ext = parse(path).ext.slice(1);

        cache.set(path, { input, output });
        return {
          contents: output,
          loader: ["js", "ts", "jsx", "tsx"].includes(ext) ? ext : "default",
        };
      });
    },
  };
}
kyle-johnson commented 1 month ago

3.0.0 supports this! Thanks so much @ldu1020 for figuring out the hard parts :-)