Repository: vshymanskyy/node-inline-cpp
Branch: master
Commit: 8e5d8da853a9
Files: 8
Total size: 11.7 KB
Directory structure:
gitextract_b4qtfggj/
├── .npmignore
├── LICENSE
├── README.md
├── examples/
│ ├── 01_simple.js
│ ├── 02_customCompilerOptions.js
│ └── 03_customModuleInit.js
├── index.js
└── package.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .npmignore
================================================
.git
package-lock.json
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2018 Volodymyr Shymanskyy
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
[](https://www.npmjs.com/package/inline-cpp)
[](https://www.npmjs.com/package/inline-cpp)
[](https://github.com/vshymanskyy/node-inline-cpp/issues)
[](https://github.com/vshymanskyy/node-inline-cpp)
# inline-cpp
Inline C++ with Node.js
**Works on:**
<img src="https://cdn.rawgit.com/simple-icons/simple-icons/develop/icons/linux.svg" width="18" height="18" /> Linux,
<img src="https://cdn.rawgit.com/simple-icons/simple-icons/develop/icons/windows.svg" width="18" height="18" /> Windows,
<img src="https://cdn.rawgit.com/simple-icons/simple-icons/develop/icons/apple.svg" width="18" height="18" /> MacOS
**Purpose:**
- Simplify native module prototyping. Enable native code in Node.js REPL.
- Allow JS scripts to generate C++ code and run it dynamically.
- Popularise NAPI usage and `node-addon-api`.
- This is **NOT** intended to be used as native module replacement!
If you want to publish a native module, please package it as required by `node-gyp`.
## Installation
```sh
npm install --save inline-cpp
```
or install it globally (it works with Node.js REPL):
```sh
npm install -g inline-cpp
```
## Usage
```js
// test.js
const compile = require('inline-cpp');
const hello = compile `
String func(const CallbackInfo& info) {
return String::New(info.Env(), "Hello world from C++!");
}
`
console.log(hello())
```
Now run it:
```sh
➜ node test.js
Hello world from C++!
```
The first time you run the script, it takes longer to execute. For each inline block of code, a native module will be generated, compiled with `node-gyp` and loaded dynamically. If the module `Init` function is not defined, it is generated as well.
The next time you run the script, it will reuse previously generated module, so it will run instantly (unless you change the inline C++ code).
For more C++ code examples, see [node-addon-api](https://github.com/nodejs/node-addon-api#examples)
For more `inline-cpp` API examples, see [examples on github](https://github.com/vshymanskyy/node-inline-cpp/tree/master/examples)
## API
`inline-cpp` supports several invocation methods.
Pass some code as string to build it with default options.
```js
const InlineCPP = require('inline-cpp');
InlineCPP('code')
```
You can also pass code using [tagged template syntax](https://developers.google.com/web/updates/2015/01/ES6-Template-Strings#tagged_templates).
```js
InlineCPP `code`
```
Pass an object to create a new compiler with custom options.
Options will get passed to `node-gyp` target.
```js
const customCompiler = InlineCPP({ ... })
```
If the code block only contains a single function, the compiler returns the function.
If it contains multiple functions or custom `Init`, the module itself is returned.
## Disclaimer
This is just a prototype. I created this to check the general concept.
You're welcome to contribute! Here are some ideas:
- [x] Parse/Find all functions in the block of code, add them to exports
- [ ] Use node-gyp directly, instead of invoking `node node-gyp.js`
- [ ] Improve error handling/reporting
- [ ] Create advanced usage examples
- [ ] Cleanup unused modules from cache periodically
- [ ] ...
## Debugging
You can enable debug output by setting env. variable: `DEBUG=inline-cpp`
================================================
FILE: examples/01_simple.js
================================================
const InlineCPP = require('../');
// Tagged template with default options
const hello = InlineCPP `
String func(const CallbackInfo& info) {
return String::New(info.Env(), "Hello world!");
}
`
console.log(hello()); // Hello world!
================================================
FILE: examples/02_customCompilerOptions.js
================================================
const InlineCPP = require('../');
// Create a compiler with some custom options to node-gyp
const compile = InlineCPP({ "defines": [`SOME_MESSAGE="Bazinga!"`] });
const func = compile(`
String func(const CallbackInfo& info) {
return String::New(info.Env(), SOME_MESSAGE);
}
`)
console.log(func()); // Bazinga!
================================================
FILE: examples/03_customModuleInit.js
================================================
const InlineCPP = require('../');
// Note: This is just an example.
// You can actually remove the Init function here,
// as the auto-generated one is the same.
const mod = InlineCPP(`
String alice(const CallbackInfo& info) {
return String::New(info.Env(), "Alice");
}
String bob(const CallbackInfo& info) {
return String::New(info.Env(), "Bob");
}
Object Init(Env env, Object exports) {
exports.Set("alice", Function::New(env, alice));
exports.Set("bob", Function::New(env, bob));
return exports;
}
`);
console.log(mod.alice(), 'and', mod.bob()); // Alice and Bob
================================================
FILE: index.js
================================================
const os = require('os');
const fs = require('fs-extra');
const path = require('path');
const crypto = require('crypto');
const { execSync } = require('child_process');
const paths = require('env-paths')('nodejs-inline-cpp', {suffix: ''});
const findParentDir = require('find-parent-dir');
const debug = require('debug')('inline-cpp');
const _ = require('lodash');
let nodeAddon, nodeGyp;
function findBuildDeps() {
if (nodeAddon && nodeGyp) return;
nodeAddon = require.resolve('node-addon-api');
nodeAddon = findParentDir.sync(nodeAddon, 'package.json');
nodeGyp = require.resolve('node-gyp');
nodeGyp = findParentDir.sync(nodeGyp, 'package.json');
nodeGyp = path.join(nodeGyp, 'bin', 'node-gyp.js');
debug('Using node-gyp:', nodeGyp);
debug('Using node-addon-api:', nodeAddon);
// For some reason, windows needs path to be escaped
if (os.platform() === 'win32') {
nodeAddon = nodeAddon.replace(/[\\$'"]/g, "\\$&")
}
}
function optsMerge(objValue, srcValue) {
if (_.isArray(objValue)) {
return objValue.concat(srcValue);
}
}
function generateModule(code, opts) {
opts = opts || {};
code = code.trim();
// The stripped code is only used for some auto-detection, it is not actually compiled!
const strippedCode = ' ' + code
.replace(/,/g, ' , ')
.replace(/\(/g, ' ( ')
.replace(/\)/g, ' ) ')
.replace(/{/g, ' { ')
.replace(/}/g, ' } ')
.replace(/\s\s+/g, ' ') + ' ';
// Find all function declarations
let funcsRe = /(([a-zA-Z_][\w:]*)\s+([a-zA-Z_]\w*)\s*\(\s*((?:[a-zA-Z0-9_:&\*,\s])*)\s*\))\s*{/gm;
let m;
let funcs = [];
let funcSingle, funcInit;
while ((m = funcsRe.exec(strippedCode)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (m.index === funcsRe.lastIndex) {
funcsRe.lastIndex++;
}
const func = {
signature: m[1],
name: m[3],
returns: m[2],
arguments: m[4]
}
debug('Function:', func.signature);
if (func.name === 'Init') {
funcInit = func;
} else {
funcs.push(func);
}
}
if (funcs.length === 1) funcSingle = funcs[0];
let init = '';
// If init function is not provided, generate it
if (!funcInit) {
init = 'Object Init(Env env, Object exports) {\n';
for (let f of funcs) {
init += ` exports.Set("${f.name}", Function::New(env, ${f.name}));\n`;
}
init += ' return exports;\n'
init += '}\n';
}
let body =
`
#include <napi.h>
using namespace Napi;
${code}
${init}
NODE_API_MODULE(addon, Init)
`;
// Generate a hash using actual code and build options
const modName = 'm_' + crypto.createHash('sha1').update(JSON.stringify(opts)).update(body).digest("hex");
const modPath = path.join(paths.cache, modName);
const modNode = path.join(modPath, modName+'.node');
// If the same hash exists, try loading it
if (fs.existsSync(modNode)) {
debug('Loading cached', modPath);
try {
if (funcSingle && !funcInit) {
return require(modNode)[funcSingle.name];
} else {
return require(modNode);
}
} catch(e) {}
}
// Ok no luck, let's build it...
findBuildDeps();
let gypTarget = {
"target_name": modName,
"sources": [
"module.cpp"
],
"include_dirs": [
`<!@(node -p "require('${nodeAddon}').include")`
],
"dependencies": [
`<!(node -p "require('${nodeAddon}').gyp")`
],
"cflags!": ["-fno-exceptions"],
"cflags_cc!": ["-fno-exceptions"],
"xcode_settings": {
"GCC_ENABLE_CPP_EXCEPTIONS": "YES",
"CLANG_CXX_LIBRARY": "libc++",
"MACOSX_DEPLOYMENT_TARGET": "10.7",
},
"msvs_settings": {
"VCCLCompilerTool": { "ExceptionHandling": 1 },
},
"defines": ["NAPI_CPP_EXCEPTIONS"],
}
gypTarget = _.mergeWith(gypTarget, opts, optsMerge)
let binding = {
"targets": [ gypTarget ]
};
debug('Building', modPath);
fs.ensureDirSync(modPath);
fs.writeFileSync(path.join(modPath, 'module.cpp'), body);
fs.writeJsonSync(path.join(modPath, 'binding.gyp'), binding, { spaces: 2 });
let execOpts = {
stdio: (debug.enabled) ? [0,1,2] : [null,null,null]
};
execSync(`node "${nodeGyp}" configure --directory="${modPath}"`, execOpts)
try {
execSync(`node "${nodeGyp}" build --directory="${modPath}"`, execOpts)
fs.renameSync(path.join(modPath, 'build', 'Release', modName+'.node'), modNode)
fs.removeSync(path.join(modPath, 'build'))
if (funcSingle && !funcInit) {
return require(modNode)[funcSingle.name];
} else {
return require(modNode);
}
} catch (e) {
throw new Error('C++ build failed')
}
}
function compiler(opts) {
return function(obj) {
let compileString;
// Handle tagged template invocation
if (Array.isArray(obj) && Array.isArray(obj.raw)) {
let interpVals = [].concat(Array.prototype.slice.call(arguments)).slice(1);
compileString = obj[0];
for (let i = 0, l = interpVals.length; i < l; i++) {
compileString += '' + interpVals[i] + obj[i + 1];
}
} else if (typeof obj === 'string' || obj instanceof String) {
compileString = obj;
}
if (compileString) {
return generateModule(compileString, opts);
}
throw new Error('Wrong arguments for inline-cpp')
}
}
module.exports = function(obj) {
if (typeof obj === 'object' &&
!Array.isArray(obj)
) {
return compiler(obj)
}
return compiler()(obj)
}
================================================
FILE: package.json
================================================
{
"name": "inline-cpp",
"version": "0.1.8",
"description": "Use inline C++ in your JS",
"author": "Volodymyr Shymanskyy",
"license": "MIT",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"debug": "^3.1.0",
"env-paths": "^1.0.0",
"find-parent-dir": "^0.3.0",
"fs-extra": "^7.0.0",
"lodash": "^4.17.10",
"node-addon-api": "^1.4.0",
"node-gyp": "^3.7.0"
},
"keywords": [
"inline",
"C++",
"cpp",
"native",
"NAPI"
],
"repository": {
"type": "git",
"url": "https://github.com/vshymanskyy/node-inline-cpp"
},
"bugs": {
"url": "https://github.com/vshymanskyy/node-inline-cpp/issues"
}
}
gitextract_b4qtfggj/ ├── .npmignore ├── LICENSE ├── README.md ├── examples/ │ ├── 01_simple.js │ ├── 02_customCompilerOptions.js │ └── 03_customModuleInit.js ├── index.js └── package.json
SYMBOL INDEX (4 symbols across 1 files)
FILE: index.js
function findBuildDeps (line 13) | function findBuildDeps() {
function optsMerge (line 32) | function optsMerge(objValue, srcValue) {
function generateModule (line 38) | function generateModule(code, opts) {
function compiler (line 187) | function compiler(opts) {
Condensed preview — 8 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (13K chars).
[
{
"path": ".npmignore",
"chars": 23,
"preview": ".git\npackage-lock.json\n"
},
{
"path": "LICENSE",
"chars": 1077,
"preview": "MIT License\n\nCopyright (c) 2018 Volodymyr Shymanskyy\n\nPermission is hereby granted, free of charge, to any person obtain"
},
{
"path": "README.md",
"chars": 3502,
"preview": "[](https://www.npmjs.com/package/inline-cpp)\n[![NPM download]"
},
{
"path": "examples/01_simple.js",
"chars": 240,
"preview": "const InlineCPP = require('../');\n\n// Tagged template with default options\nconst hello = InlineCPP `\n String func(const"
},
{
"path": "examples/02_customCompilerOptions.js",
"chars": 321,
"preview": "const InlineCPP = require('../');\n\n// Create a compiler with some custom options to node-gyp\nconst compile = InlineCPP({"
},
{
"path": "examples/03_customModuleInit.js",
"chars": 603,
"preview": "const InlineCPP = require('../');\n\n// Note: This is just an example.\n// You can actually remove the Init function here,\n"
},
{
"path": "index.js",
"chars": 5495,
"preview": "const os = require('os');\nconst fs = require('fs-extra');\nconst path = require('path');\nconst crypto = require('crypto')"
},
{
"path": "package.json",
"chars": 736,
"preview": "{\n \"name\": \"inline-cpp\",\n \"version\": \"0.1.8\",\n \"description\": \"Use inline C++ in your JS\",\n \"author\": \"Volodymyr Shy"
}
]
About this extraction
This page contains the full source code of the vshymanskyy/node-inline-cpp GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 8 files (11.7 KB), approximately 3.5k tokens, and a symbol index with 4 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.