diff --git a/packages/endomoat/package.json b/packages/endomoat/package.json index b4246da29e..1b986c6f49 100644 --- a/packages/endomoat/package.json +++ b/packages/endomoat/package.json @@ -37,6 +37,7 @@ "scripts": { "lint:deps": "depcheck", "test": "npm run test:run", + "test:prep": "cd test/fixture/native && npm install", "test:run": "ava" }, "dependencies": { diff --git a/packages/endomoat/src/constants.js b/packages/endomoat/src/constants.js index 22bf598152..9f09dca017 100644 --- a/packages/endomoat/src/constants.js +++ b/packages/endomoat/src/constants.js @@ -29,3 +29,7 @@ export const RSRC_POLICY_GLOBALS = 'globals' export const LMR_TYPE_BUILTIN = 'builtin' export const LMR_TYPE_SOURCE = 'js' + +export const LMR_TYPE_NATIVE = 'native' + +export const ENDO_PARSER_BYTES = 'bytes' diff --git a/packages/endomoat/src/policy-gen/policy-generator-context.js b/packages/endomoat/src/policy-gen/policy-generator-context.js index 392881e8e7..cd00e31220 100644 --- a/packages/endomoat/src/policy-gen/policy-generator-context.js +++ b/packages/endomoat/src/policy-gen/policy-generator-context.js @@ -8,8 +8,10 @@ import { isBuiltin as nodeIsBuiltin } from 'node:module' import { fileURLToPath } from 'node:url' import path from 'path' import { + ENDO_PARSER_BYTES, LAVAMOAT_PKG_POLICY_ROOT, LMR_TYPE_BUILTIN, + LMR_TYPE_NATIVE, LMR_TYPE_SOURCE, } from '../constants.js' import { defaultReadPower } from '../power.js' @@ -271,7 +273,10 @@ export class PolicyGeneratorContext { * @returns {Promise} * @internal */ - async buildModuleRecordsForSource(specifier, { record, sourceLocation }) { + async buildModuleRecordsForSource( + specifier, + { parser, record, sourceLocation } + ) { if (!sourceLocation) { // XXX: why would we not have a sourceLocation? throw new TypeError( @@ -299,17 +304,24 @@ export class PolicyGeneratorContext { * The `ModuleSource.content` prop is already pre-processed by Endo, and we * do not want that, since it befouls our AST crawling. * + * This will not be run if the `parser` is `bytes`. + * * @remarks * Doing this first since it may be more likely to fail than the other - * operations below - * @type {NonNullable< - * import('lavamoat-core').LavamoatModuleRecord['content'] - * >} + * operations below. + * @type {string | undefined} * @todo Modify Endo to surface the original source + * + * @todo Add more exceptions to the parsers? */ - const content = await this.readPower(sourceLocation).then((buffer) => - PolicyGeneratorContext.#decoder.decode(buffer) - ) + let content + + await Promise.resolve() + if (parser !== ENDO_PARSER_BYTES) { + content = await this.readPower(sourceLocation).then((buffer) => + PolicyGeneratorContext.#decoder.decode(buffer) + ) + } /** * The {@link LavamoatModuleRecord.file} prop @@ -338,7 +350,7 @@ export class PolicyGeneratorContext { packageName: this.packageName, importMap, content, - type: LMR_TYPE_SOURCE, + type: parser === ENDO_PARSER_BYTES ? LMR_TYPE_NATIVE : LMR_TYPE_SOURCE, }), ] diff --git a/packages/endomoat/test/fixture/native/.npmrc b/packages/endomoat/test/fixture/native/.npmrc new file mode 100644 index 0000000000..41658e38c2 --- /dev/null +++ b/packages/endomoat/test/fixture/native/.npmrc @@ -0,0 +1,2 @@ +# needed to build native module +ignore-scripts=false diff --git a/packages/endomoat/test/fixture/native/app.js b/packages/endomoat/test/fixture/native/app.js new file mode 100644 index 0000000000..013e288909 --- /dev/null +++ b/packages/endomoat/test/fixture/native/app.js @@ -0,0 +1,2 @@ +import addon from 'napi' +export const hello = addon.hello() diff --git a/packages/endomoat/test/fixture/native/node-modules/napi/README.md b/packages/endomoat/test/fixture/native/node-modules/napi/README.md new file mode 100644 index 0000000000..85a18f18a3 --- /dev/null +++ b/packages/endomoat/test/fixture/native/node-modules/napi/README.md @@ -0,0 +1,5 @@ +This is an example of a module using Node-API (a.k.a. N-API) to provide a native module. + +It was modified from the [nodejs/node-addon-examples](https://github.com/nodejs/node-addon-examples/tree/f8561c0233c3a75a99fab5ec20e729ae1fd0a1f6/src/1-getting-started/1_hello_world/napi) repo. + +The original uses the [bindings](https://npm.im/bindings) package, but it was removed here since that package uses dynamic requires. diff --git a/packages/endomoat/test/fixture/native/node-modules/napi/binding.gyp b/packages/endomoat/test/fixture/native/node-modules/napi/binding.gyp new file mode 100644 index 0000000000..4dc6017f79 --- /dev/null +++ b/packages/endomoat/test/fixture/native/node-modules/napi/binding.gyp @@ -0,0 +1,8 @@ +{ + "targets": [ + { + "target_name": "hello", + "sources": [ "hello.c" ] + } + ] +} diff --git a/packages/endomoat/test/fixture/native/node-modules/napi/hello.c b/packages/endomoat/test/fixture/native/node-modules/napi/hello.c new file mode 100644 index 0000000000..19207c8291 --- /dev/null +++ b/packages/endomoat/test/fixture/native/node-modules/napi/hello.c @@ -0,0 +1,23 @@ +#include +#include + +static napi_value Method(napi_env env, napi_callback_info info) { + napi_status status; + napi_value world; + status = napi_create_string_utf8(env, "world", 5, &world); + assert(status == napi_ok); + return world; +} + +#define DECLARE_NAPI_METHOD(name, func) \ + { name, 0, func, 0, 0, 0, napi_default, 0 } + +static napi_value Init(napi_env env, napi_value exports) { + napi_status status; + napi_property_descriptor desc = DECLARE_NAPI_METHOD("hello", Method); + status = napi_define_properties(env, exports, 1, &desc); + assert(status == napi_ok); + return exports; +} + +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/packages/endomoat/test/fixture/native/node-modules/napi/hello.js b/packages/endomoat/test/fixture/native/node-modules/napi/hello.js new file mode 100644 index 0000000000..4f6ee86d00 --- /dev/null +++ b/packages/endomoat/test/fixture/native/node-modules/napi/hello.js @@ -0,0 +1,5 @@ +var addon = require('./build/Release/hello') + +console.log(addon.hello()) // 'world' + +module.exports = addon diff --git a/packages/endomoat/test/fixture/native/node-modules/napi/package.json b/packages/endomoat/test/fixture/native/node-modules/napi/package.json new file mode 100644 index 0000000000..35067451a7 --- /dev/null +++ b/packages/endomoat/test/fixture/native/node-modules/napi/package.json @@ -0,0 +1,14 @@ +{ + "name": "napi", + "version": "0.0.0", + "description": "Node.js Addons Example #1", + "private": true, + "main": "hello.js", + "scripts": { + "test": "node hello.js" + }, + "gypfile": true, + "parsers": { + "node": "bytes" + } +} diff --git a/packages/endomoat/test/fixture/native/package-lock.json b/packages/endomoat/test/fixture/native/package-lock.json new file mode 100644 index 0000000000..86afd95a03 --- /dev/null +++ b/packages/endomoat/test/fixture/native/package-lock.json @@ -0,0 +1,24 @@ +{ + "name": "no-deps", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "no-deps", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "napi": "file://./node-modules/napi" + } + }, + "node_modules/napi": { + "resolved": "node-modules/napi", + "link": true + }, + "node-modules/napi": { + "version": "0.0.0", + "hasInstallScript": true + } + } +} diff --git a/packages/endomoat/test/fixture/native/package.json b/packages/endomoat/test/fixture/native/package.json new file mode 100644 index 0000000000..555fca929b --- /dev/null +++ b/packages/endomoat/test/fixture/native/package.json @@ -0,0 +1,11 @@ +{ + "name": "no-deps", + "version": "1.0.0", + "type": "module", + "license": "ISC", + "private": true, + "main": "app.js", + "dependencies": { + "napi": "file://./node-modules/napi" + } +}