Giter Site home page Giter Site logo

Comments (8)

tipiirai avatar tipiirai commented on May 24, 2024 1

I'm not sure how eval can mimic sentences like import { hello } from './hello.js´. Worth a try. Getting the hello variable might be tricky.

from nue.

hichem-dahi avatar hichem-dahi commented on May 24, 2024

Can't we use eval function (although it may not be secure) or it's alternatives?

from nue.

tipiirai avatar tipiirai commented on May 24, 2024

I don't think you can import modules with eval.

from nue.

tipiirai avatar tipiirai commented on May 24, 2024

ie. I'm open to any option that works. Let's tinker with the possible security issues / other concerns later.

from nue.

hichem-dahi avatar hichem-dahi commented on May 24, 2024

In the example you've shared, we have

...
    const text = await (await fetch(url)).text();
    const dataURL = `data:text/javascript;base64,${btoa(text)}`;
    const { Buffer } = await import(dataURL);
...

Can't we replace it with

...
    const text = await (await fetch(url)).text();
    eval(test);
...

from nue.

hichem-dahi avatar hichem-dahi commented on May 24, 2024

Why would it be tricky? Wouldn't eval parse it normally?

from nue.

kon-pas avatar kon-pas commented on May 24, 2024

@tipiirai
Did you work this out?

I came up with a fairly simple, but very primitive solution to transpile the source by hand. We could look up all import statements and transform them to dynamic imports, which are allowed in non-module environments, like eval and Function.

Here is my sample implementation. It uses a regex for the pattern lookup, so it's very error-prone. It works, but I'm not convinced anyone should use this:

const BunTranspiler = new Bun.Transpiler({
  loader: 'js',
  // minifyWhitespace: true, // TEMP: Commented out for debugging reasons
})

function transpileImports(source) {
  const imports = BunTranspiler
    .scanImports(source)
    .filter(imp => imp.kind == 'import-statement')
    .map(imp => imp.path)

  // Slightly modified version of a regex made by Antón Kryukov Chinaev @antonkc
  // https://github.com/antonkc/MOR/blob/main/matchJsImports.md
  const regex = /(?<=(?:[\s\n;])|^)(?:import[\s\n]*)((?:(?:[_\$\w][_\$\w0-9]*)(?:[\s\n]+(?:as[\s\n]+(?:[_\$\w][_\$\w0-9]*)))?(?=(?:[\n\s]*,[\n\s]*[\{\*])|(?:[\n\s]+from)))?)[\s\n,]*((?:\*[\n\s]*(?:as[\s\n]+(?:[_\$\w][_\$\w0-9]*))(?=[\n\s]+from))?)[\s\n,]*((?:\{[n\s]*(?:(?:[_\$\w][_\$\w0-9]*)(?:[\s\n]+(?:as[\s\n]+(?:[_\$\w][_\$\w0-9]*)))?[\s\n]*,?[\s\n]*)*\}(?=[\n\s]*from))?)(?:[\s\n]*((?:from)?))[\s\n]*(?:["']([^"']*)(["']))[\s\n]*?;?/gm

  return BunTranspiler
    .transformSync(source)  // Strips comments, minifies whitespaces, etc.
    .replaceAll(regex, (input, defaultImport, wildcardImport, namedImports, from, moduleName, quote) => {

      // Just to be more confident, that the import statement is indeed an import statement,
      // though it's incorrect in some rare cases, but more on that later on
      if (!imports.includes(moduleName)) return input

      // TODO: Resolve the path to run the script in the right context
      const path = quote + moduleName + quote

      if (wildcardImport)
        if (defaultImport)
          return `const ${wildcardImport.split(' ').at(-1)} = await import(${path}), ${defaultImport} = ${wildcardImport.split(' ').at(-1)}.default;`
        else return `const ${wildcardImport.split(' ').at(-1)} = await import(${path});`
      else if (namedImports)
        if (defaultImport)
          return `const ${namedImports.replaceAll(' as ', ': ').slice(0, -1)}, default: ${defaultImport} } = await import(${path});`
        else return `const ${namedImports.replaceAll(' as ', ': ')} = await import(${path});`
      else if (defaultImport) return `const { default: ${defaultImport} } = await import(${path});`
      return `await import (${path});`
    })
}

Given the source:

import defaultImport1 from "./module";
import * as wildcardImport1 from "./module";
import { namedImport1 } from "./module";
import { namedImport1 as alias1 } from "./module";
import { default as alias2 } from "./module";
import { namedImport2, namedImport3 } from "./module";
import defaultImport2, * as wildcardImport2 from "./module";
import { namedImport4, namedImport1 as alias3, /* ... */ } from "./module";
import defaultImport3, { namedImport5, /* ... */ } from "./module";
import "./module";

It transpiles to:

const { default: defaultImport1 } = await import("./module");
const wildcardImport1 = await import("./module");
const {namedImport1} = await import("./module");
const {namedImport1: alias1} = await import("./module");
const {default: alias2} = await import("./module");
const {namedImport2, namedImport3} = await import("./module");
const wildcardImport2 = await import("./module"), defaultImport2 = wildcardImport2.default;
const {namedImport4, namedImport1: alias3} = await import("./module");
const {namedImport5, default: defaultImport3 } = await import("./module");
await import ("./module");

And in action:

// module.js
export default 42
export const namedImport1 = 42
export const namedImport2 = 42
export const namedImport3 = 42
export const namedImport4 = 42
export const namedImport5 = 42
const source = `
import defaultImport1 from "./module";
import * as wildcardImport1 from "./module";
import { namedImport1 } from "./module";
import { namedImport1 as alias1 } from "./module";
import { default as alias2 } from "./module";
import { namedImport2, namedImport3 } from "./module";
import defaultImport2, * as wildcardImport2 from "./module";
import { namedImport4, namedImport1 as alias3, /* ... */ } from "./module";
import defaultImport3, { namedImport5, /* ... */ } from "./module";
import "./module";

console.log({
  alias1,
  alias2,
  alias3,
  namedImport1,
  namedImport2,
  namedImport3,
  namedImport4,
  namedImport5,
  defaultImport1,
  defaultImport2,
  defaultImport3,
  wildcardImport1,
  wildcardImport2,
})
`

const transpiled = transpileImports(source)
const AsyncFunction = async function () {}.constructor
AsyncFunction(transpiled)() /* =>
{
  alias1: 42,
  alias2: 42,
  alias3: 42,
  namedImport1: 42,
  namedImport2: 42,
  namedImport3: 42,
  namedImport4: 42,
  namedImport5: 42,
  defaultImport1: 42,
  defaultImport2: 42,
  defaultImport3: 42,
  wildcardImport1: Module {
    default: 42,
    namedImport1: 42,
    namedImport2: 42,
    namedImport3: 42,
    namedImport4: 42,
    namedImport5: 42,
  },
  wildcardImport2: Module {
    default: 42,
    namedImport1: 42,
    namedImport2: 42,
    namedImport3: 42,
    namedImport4: 42,
    namedImport5: 42,
  },
}
*/

Issues that I've already found:

  • Regex doesn't match type imports

  • Regex doesn't match string literal named imports: import { "string literal" as alias } from "module"

  • Regex matches all import statement syntax instances despite its context, for example in strings. We verify that with Bun's scanImports(), but if the source code contains both a real import statement, and a fake one, they both get transpiled:

    "import 'module'";
    import 'module';

    Transpiles to:

    "await import ('module')";
    await import ('module');

from nue.

Hi-Alan avatar Hi-Alan commented on May 24, 2024

This is a highly desirable feature, and I'm looking forward to it. I think there are 3-level component life time:

  • compile-time
  • server side at runtime
  • client side at runtime

from nue.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.