char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
61e3521
main
CommonJS support
avril executes CommonJS via synchronous require at the core V8 layer, with
ESM wrappers for import of CommonJS. There is no global registry: every
isolate keeps its own require cache shared by require and ESM.
deps: ['npm:cjslib@1']
// main.cjs const lib = require ( 'npm:cjslib@1' ); const local = require ( './a.cjs' );
What works
- Local
.cjs/.ctsfiles:require('./a.cjs'),require.resolve, JSONrequire('./data.json'), directory lookup (package.jsonmainthenindex.js/.json/.cjs), extension lookup (.js/.json/.noderejecting.node). Local.jsstays ESM: requiring it fails withrequire() of ESM … use import() instead. - npm CommonJS/JSON via
require:.cjs,.cts(transpiled preserving CJS), and.jsoutside"type": "module"(nearesttypeboundary, including nestedpackage.jsonscopes), unless that file is advertised as the package'smoduleentry. That entry is ESM, without changing sibling files; explicit.cjs/.ctsextensions still take precedence..jsonparses viarequire, never viaimport..mjs/.mtsstay ESM. - ESM
importof CommonJS (local and npm):import def, { named } from './a.cjs'wheredefaultis the exactmodule.exportsvalue (objects share identity withrequirecallers) and named exports snapshot its properties. Primitives exposedefaultonly (namedisundefined). - Wrapper semantics:
function (exports, require, module, __filename, __dirname)called withthisas the initialexports, non-strict script semantics (top-levelreturnlegal), BOM/shebang stripped, absolute__filename/__dirname,module.id/filename/loadedplusmodule.require. The module is cached BEFORE execution so cycles observe partial exports and replacements; failures are removed for retry. require.resolvereturns the canonical path without executing.require(ESM)fails explicitly (use import() instead);node:builtins and native.nodeaddons fail explicitly.avril:builtins viarequireare explicitly unsupported (useimport()); dynamicimport()from CJS uses the existing pipeline with the CJS filename as referrer.- Conditional
exports/importschooserequire(avril,require,default) independently fromimport(avril,import,module,default). Legacy npm entries prefermainforrequire(nevermodule). Export targets stay exact in both modes. - Lazy/conditional/
try-catchand computedrequires never become eager imports: only static ESMimports are prepared up front. Local config npm bindings are acquired during async preparation of the local CJS root, never on syncrequire; unpreparednpm:/remote acquisition via syncrequirefails clearly without network I/O or leaking project import maps into npm internals.
Limits
- Named export analysis is static:
exports.foo,module.exports.foo(identifier or valid-identifier string keys),module.exports = { … }object literals, and single-propertyObject.defineProperty(exports, "foo", …). Top-levelthis.fooexecutes (this === exports) but is not statically listed (usedefault). String keys that are not valid identifiers (for exampleexports["foo-bar"]) stay available viadefaultonly.exports = { … }does not affectmodule.exportsand is ignored.module.exports = require(…)re-exports are not followed (onlydefault). Computed keys beyond string literals are skipped. - Synchronous remote (
http:/https:)requireis unavailable (useimport());requireof a remote URL is rejected without fetching.require.cache/require.mainare not provided.require(ESM)is rejected (use import()); JSON loads only viarequire. - Capabilities: every
require(including cached returns andrequire.resolve) checks referrer and target; sources are rechecked around reads and after user code, with transitive CommonJS closures rechecked so cached exports cannot bypass a later pledge. npm data flows through verified manifests and virtual-registryjs-execauthority; local project grants never authorize npm cache files. Code loads require onlyjs-exec, neverread.
1# CommonJS support 2 3avril executes CommonJS via synchronous `require` at the core V8 layer, with 4ESM wrappers for `import` of CommonJS. There is no global registry: every 5isolate keeps its own `require` cache shared by `require` and ESM. 6 7``` text 8deps: ['npm:cjslib@1'] 9``` 10``` js 11// main.cjs 12const lib = require('npm:cjslib@1'); 13const local = require('./a.cjs'); 14``` 15 16## What works 17 18- Local `.cjs`/`.cts` files: `require('./a.cjs')`, `require.resolve`, JSON 19`require('./data.json')`, directory lookup (`package.json` `main` then 20`index.js`/`.json`/`.cjs`), extension lookup (`.js`/`.json`/`.node` 21rejecting `.node`). Local `.js` stays ESM: requiring it fails with 22`require() of ESM … use import() instead`. 23- npm CommonJS/JSON via `require`: `.cjs`, `.cts` (transpiled preserving CJS), 24and `.js` outside `"type": "module"` (nearest `type` boundary, including 25nested `package.json` scopes), unless that file is advertised as the 26package's `module` entry. That entry is ESM, without changing sibling files; 27explicit `.cjs`/`.cts` extensions still take precedence. `.json` parses via 28`require`, never via `import`. `.mjs`/`.mts` stay ESM. 29- ESM `import` of CommonJS (local and npm): `import def, { named } from 30'./a.cjs'` where `default` is the exact `module.exports` value (objects 31share identity with `require` callers) and named exports snapshot its 32properties. Primitives expose `default` only (`named` is `undefined`). 33- Wrapper semantics: `function (exports, require, module, __filename, 34__dirname)` called with `this` as the initial `exports`, non-strict script 35semantics (top-level `return` legal), BOM/shebang stripped, absolute 36`__filename`/`__dirname`, `module.id`/`filename`/`loaded` plus 37`module.require`. The module is cached BEFORE execution so cycles observe 38partial exports and replacements; failures are removed for retry. 39- `require.resolve` returns the canonical path without executing. 40`require(ESM)` fails explicitly (`use import() instead`); `node:` builtins 41and native `.node` addons fail explicitly. `avril:` builtins via `require` 42are explicitly unsupported (use `import()`); dynamic `import()` from CJS 43uses the existing pipeline with the CJS filename as referrer. 44- Conditional `exports`/`imports` choose `require` (`avril,require,default`) 45independently from `import` (`avril,import,module,default`). Legacy npm 46entries prefer `main` for `require` (never `module`). Export targets stay 47exact in both modes. 48- Lazy/conditional/`try-catch` and computed `require`s never become eager 49imports: only static ESM `import`s are prepared up front. Local config npm 50bindings are acquired during async preparation of the local CJS root, never 51on sync `require`; unprepared `npm:`/remote acquisition via sync `require` 52fails clearly without network I/O or leaking project import maps into npm 53internals. 54 55## Limits 56 57- Named export analysis is static: 58`exports.foo`, `module.exports.foo` (identifier or valid-identifier string 59keys), `module.exports = { … }` object literals, and single-property 60`Object.defineProperty(exports, "foo", …)`. Top-level `this.foo` executes 61(`this === exports`) but is not statically listed (use `default`). String keys 62that are not valid identifiers (for example 63`exports["foo-bar"]`) stay available via `default` only. `exports = { … }` 64does not affect `module.exports` and is ignored. 65`module.exports = require(…)` re-exports are not followed (only `default`). 66Computed keys beyond string literals are skipped. 67- Synchronous remote (`http:`/`https:`) `require` is unavailable (use `import()`); `require` of a remote URL is rejected without fetching. `require.cache`/`require.main` are not provided. `require(ESM)` is rejected (`use import()`); JSON loads only via `require`. 68- Capabilities: every `require` (including cached returns and 69`require.resolve`) checks referrer and target; sources are rechecked around 70reads and after user code, with transitive CommonJS closures rechecked so 71cached exports cannot bypass a later pledge. npm data flows through verified 72manifests and virtual-registry `js-exec` authority; local project grants 73never authorize npm cache files. Code loads require only `js-exec`, never 74`read`.