import | compile an es module | compile a dylib from source cell | Search

The code imports modules, defines constants, and constructs paths to DLL files, before implementing two functions: buildDLL, which compiles DLLs from code, and makeDLL, which imports and interprets code, then logs extracted namespace information to the console.

Run example

npm run import -- "compile a csharp file into a DLL"

compile a csharp file into a DLL


const fs = require('fs')
const path = require('path')
const { Module } = require('module')

const BUILD_DIRECTORY = path.join(__dirname, '../.build')

function buildDLL(code, pathToCode, ctx) {
  if (!fs.existsSync(BUILD_DIRECTORY)) {
    fs.mkdirSync(BUILD_DIRECTORY)
  }

  const {
    importNotebook, interpret
  } = require('../Core')
  const codeCell = interpret(pathToCode)
  // TODO: compare file times for dylib module recompile
  const { safeurl } = importNotebook("domain cache tools")
  let libName = safeurl(codeCell.questions[0])
  let libPath = path.join(BUILD_DIRECTORY, libName + '.cs')
  if (!fs.existsSync(libPath)
    || fs.statSync(codeCell.filename).mtime > fs.statSync(libPath).mtime
  ) {
    fs.writeFileSync(libPath, codeCell.source.join(''))
  }


  // TODO: recompile dylib
  let objPath = path.join(BUILD_DIRECTORY, libName + '.dll')
  if (!fs.existsSync(objPath)
    || fs.statSync(libPath).mtime > fs.statSync(objPath).mtime
  ) {
    const { spawnSync } = require('child_process')
    let args = ['-target:library', '-out:' + objPath, libPath]
    spawnSync('mcs', args, { stdio: [0, 1, 2] })
  }
}

async function makeDLL(code, pathToCode, ctx) {

  if (Module._cache[pathToCode]) {
    return Module._cache[pathToCode].exports
  }
  // TODO: await module loaded
  buildDLL(code, pathToCode, ctx)

  const {
    importNotebook, interpret
  } = require('../Core')
  const codeCell = interpret(pathToCode)
  // TODO: compare file times for dylib module recompile
  const { safeurl } = importNotebook("domain cache tools")
  let libName = safeurl(codeCell.questions[0])

  const selectCode = await importNotebook("select antlr tree")
  const rootNode = (await selectCode(['//*'], code.toString(), 'csharp'))[0]

  const getClassParams = await importNotebook("list csharp params")
  const getClassNames = await importNotebook("list csharp classes")
  const getNamespacesBySize = await importNotebook("list csharp namespaces")
  const namespace = (await getNamespacesBySize(rootNode))[0]
  console.log(namespace)
  const dotnet = require('node-api-dotnet')
  dotnet.load(path.join(BUILD_DIRECTORY, libName + '.dll'))
  const classes = await getClassNames(rootNode, namespace)
  console.log(classes)
  const params = await getClassParams(rootNode, classes[0])
  console.log(params)

  // TODO: loop through classes
  dotnet[namespace][classes[0]].functions = params.map(p => p[0])
  for(let i = 0; i < params.length; i++) {
    if(dotnet[namespace][classes[0]][params[i][0]]) {
      dotnet[namespace][classes[0]][params[i][0]].params = params
    }
  }

  return dotnet[namespace][classes[0]]
}

module.exports.makeDLL = makeDLL

What the code could have been:

const fs = require('fs');
const path = require('path');
const { EOL } = require('os');
const { Module } = require('module');
const dotnet = require('node-api-dotnet');
const { spawnSync } = require('child_process');

const BUILD_DIRECTORY = path.join(__dirname, '../.build');

class DLLBuilder {
  async buildDLL(code, pathToCode, ctx) {
    if (!fs.existsSync(BUILD_DIRECTORY)) {
      fs.mkdirSync(BUILD_DIRECTORY);
    }

    const {
      importNotebook,
      interpret
    } = require('../Core');

    const codeCell = await interpret(pathToCode);
    const { safeurl } = importNotebook('domain cache tools');
    const libName = safeurl(codeCell.questions[0]);
    const libPath = path.join(BUILD_DIRECTORY, libName + '.cs');
    const lastModified = (await fs.promises.stat(libPath)).mtime;
    const file = await fs.promises.stat(codeCell.filename);
    if (!fs.existsSync(libPath) || file.mtime > lastModified) {
      await fs.promises.writeFile(libPath, codeCell.source.join(EOL));
    }

    const objPath = path.join(BUILD_DIRECTORY, libName + '.dll');
    if (!fs.existsSync(objPath) || lastModified > (await fs.promises.stat(objPath)).mtime) {
      const args = ['-target:library', '-out:' + objPath, libPath];
      spawnSync('mcs', args, { stdio: [0, 1, 2] });
    }
  }
}

async function makeDLL(code, pathToCode, ctx) {
  if (Module._cache[pathToCode]) {
    return Module._cache[pathToCode].exports;
  }

  const dllBuilder = new DLLBuilder();
  await dllBuilder.buildDLL(code, pathToCode, ctx);

  const {
    importNotebook,
    interpret
  } = require('../Core');
  const codeCell = await interpret(pathToCode);

  const { safeurl } = importNotebook('domain cache tools');
  const libName = safeurl(codeCell.questions[0]);

  const selectCode = await importNotebook('select antlr tree');
  const rootNode = (await selectCode(['//*'], code.toString(), 'csharp'))[0];

  const getClassParams = await importNotebook('list csharp params');
  const getClassNames = await importNotebook('list csharp classes');
  const getNamespacesBySize = await importNotebook('list csharp namespaces');
  const namespace = (await getNamespacesBySize(rootNode))[0];
  console.log(namespace);
  dotnet.load(path.join(BUILD_DIRECTORY, libName + '.dll'));
  const classes = await getClassNames(rootNode, namespace);
  console.log(classes);
  const params = await getClassParams(rootNode, classes[0]);
  console.log(params);

  dotnet[namespace][classes[0]].functions = params.map(p => p[0]);
  for (let i = 0; i < params.length; i++) {
    if (dotnet[namespace][classes[0]][params[i][0]]) {
      dotnet[namespace][classes[0]][params[i][0]].params = params;
    }
  }

  return dotnet[namespace][classes[0]];
}

module.exports.makeDLL = makeDLL;

Breakdown of the Code

Requires and Imports

The code starts by importing the following modules:

Constants and File Pathing

buildDLL Function

The buildDLL function checks if the BUILD_DIRECTORY exists and creates it if not. It then:

  1. Requires the importNotebook and interpret functions from ../Core.
  2. Interprets the code at the specified pathToCode using interpret.
  3. Checks if the DLL file exists or if the source file has been modified since the last compilation. If either condition is true, it recompiles the DLL.
  4. Uses the spawnSync function from the child_process module to spawn a new process that compiles the DLL using a C# compiler (mcs).
  5. Writes the compiled DLL to disk.

makeDLL Function

The makeDLL function:

  1. Checks if a module is already cached for the specified pathToCode. If so, it returns the cached exports.
  2. Calls buildDLL to compile the DLL.
  3. Requires the importNotebook and interpret functions from ../Core.
  4. Interprets the code at the specified pathToCode using interpret.
  5. Uses importNotebook to import three notebooks and extract the following information:
  6. Logs the extracted namespace to the console.

Note: This breakdown only includes the code that is presented in the snippet. The actual code may contain additional functionality and dependencies not shown here.