@knighted/module 1.0.0-beta.3 → 1.0.0-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -22,7 +22,7 @@ By default `@knighted/module` transforms the one-to-one [differences between ES
22
22
 
23
23
  ## Requirements
24
24
 
25
- - Node >= 20.11.0
25
+ - Node >= 22.21.1
26
26
 
27
27
  ## Install
28
28
 
@@ -133,6 +133,7 @@ Behavior notes (defaults in parentheses)
133
133
  - `requireSource` (`builtin`): whether `require` comes from Node or `createRequire`.
134
134
  - `cjsDefault` (`auto`): bundler-style default interop vs direct `module.exports`.
135
135
  - `out`/`inPlace`: write the transformed code to a file; otherwise the function returns the transformed string only.
136
+ - CommonJS → ESM lowering will throw on `with` statements and unshadowed `eval` calls to avoid unsound rewrites.
136
137
 
137
138
  See [docs/esm-to-cjs.md](docs/esm-to-cjs.md) for deeper notes on live bindings, interop helpers, top-level await behavior, and `import.meta.main` handling. For CommonJS to ESM lowering details, read [docs/cjs-to-esm.md](docs/cjs-to-esm.md).
138
139
 
@@ -141,3 +142,4 @@ See [docs/esm-to-cjs.md](docs/esm-to-cjs.md) for deeper notes on live bindings,
141
142
  - Remove `@knighted/specifier` and avoid double parsing.
142
143
  - Emit source maps and clearer diagnostics for transform choices.
143
144
  - Broaden fixtures covering live-binding and top-level await edge cases across Node versions.
145
+ - Benchmark scope analysis choices: compare `periscopic`, `scope-analyzer`, and `eslint-scope` on fixtures and pick the final adapter.
@@ -382,6 +382,12 @@ const format = async (src, ast, opts) => {
382
382
  }
383
383
  }
384
384
  }
385
+ if (shouldRaiseEsm && node.type === 'WithStatement') {
386
+ throw new Error('Cannot transform to ESM: with statements are not supported.');
387
+ }
388
+ if (shouldRaiseEsm && node.type === 'CallExpression' && node.callee.type === 'Identifier' && node.callee.name === 'eval' && !shadowedBindings.has('eval')) {
389
+ throw new Error('Cannot transform to ESM: eval is not supported.');
390
+ }
385
391
  if (shouldRaiseEsm && node.type === 'CallExpression' && isRequireCall(node, shadowedBindings)) {
386
392
  const isStatic = isStaticRequire(node, shadowedBindings);
387
393
  const parent = ancestors[ancestors.length - 2] ?? null;
@@ -0,0 +1,2 @@
1
+ declare const scopeNodes: string[];
2
+ export { scopeNodes };
@@ -5,8 +5,8 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.collectScopeIdentifiers = exports.collectModuleIdentifiers = void 0;
7
7
  var _walk = require("#walk");
8
- var _scope = require("#helpers/scope.js");
9
8
  var _identifier = require("#helpers/identifier.js");
9
+ var _scopeNodes = require("./scopeNodes.cjs");
10
10
  const collectScopeIdentifiers = (node, scopes) => {
11
11
  const {
12
12
  type
@@ -152,11 +152,14 @@ const collectModuleIdentifiers = async (ast, hoisting = true) => {
152
152
  const isModuleScope = _identifier.identifier.isModuleScope(ancestors);
153
153
  const isClassOrFuncDeclaration = _identifier.identifier.isClassOrFuncDeclarationId(ancestors);
154
154
  const isVarDeclarationInGlobalScope = _identifier.identifier.isVarDeclarationInGlobalScope(ancestors);
155
+ const parent = ancestors[ancestors.length - 2];
156
+ const grandParent = ancestors[ancestors.length - 3];
157
+ const hoistSafe = parent.type === 'FunctionDeclaration' || parent.type === 'VariableDeclarator' && grandParent?.type === 'VariableDeclaration' && grandParent.kind === 'var';
155
158
  if (isModuleScope || isClassOrFuncDeclaration || isVarDeclarationInGlobalScope) {
156
159
  meta.declare.push(node);
157
160
 
158
161
  // Check for hoisted reads
159
- if (hoisting && globalReads.has(name)) {
162
+ if (hoisting && hoistSafe && globalReads.has(name)) {
160
163
  const reads = globalReads.get(name);
161
164
  if (reads) {
162
165
  reads.forEach(read => {
@@ -180,7 +183,7 @@ const collectModuleIdentifiers = async (ast, hoisting = true) => {
180
183
  const {
181
184
  type
182
185
  } = node;
183
- if (_scope.scopes.includes(type)) {
186
+ if (_scopeNodes.scopeNodes.includes(type)) {
184
187
  scopes.pop();
185
188
  }
186
189
  }
@@ -8,11 +8,11 @@ var _nodePath = require("node:path");
8
8
  // Determine language from filename extension for specifier rewrite.
9
9
 
10
10
  const getLangFromExt = filename => {
11
- const ext = (0, _nodePath.extname)(filename);
12
- if (ext.endsWith('.js')) {
11
+ const ext = (0, _nodePath.extname)(filename).toLowerCase();
12
+ if (ext === '.js' || ext === '.mjs' || ext === '.cjs') {
13
13
  return 'js';
14
14
  }
15
- if (ext.endsWith('.ts')) {
15
+ if (ext === '.ts' || ext === '.mts' || ext === '.cts') {
16
16
  return 'ts';
17
17
  }
18
18
  if (ext === '.tsx') {
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.scopeNodes = void 0;
7
+ const scopeNodes = exports.scopeNodes = ['BlockStatement', 'FunctionDeclaration', 'FunctionExpression', 'ArrowFunctionExpression', 'ClassDeclaration', 'ClassExpression', 'ClassBody', 'StaticBlock'];
@@ -6,14 +6,14 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.requireMainRgx = exports.isValidUrl = exports.getLangFromExt = exports.exportsRename = exports.collectScopeIdentifiers = exports.collectModuleIdentifiers = exports.collectCjsExports = void 0;
7
7
  var _nodePath = require("node:path");
8
8
  var _walk = require("./walk.cjs");
9
- var _scope = require("./helpers/scope.cjs");
10
9
  var _identifier = require("./helpers/identifier.cjs");
10
+ var _scopeNodes = require("./utils/scopeNodes.cjs");
11
11
  const getLangFromExt = filename => {
12
- const ext = (0, _nodePath.extname)(filename);
13
- if (ext.endsWith('.js')) {
12
+ const ext = (0, _nodePath.extname)(filename).toLowerCase();
13
+ if (ext === '.js' || ext === '.mjs' || ext === '.cjs') {
14
14
  return 'js';
15
15
  }
16
- if (ext.endsWith('.ts')) {
16
+ if (ext === '.ts' || ext === '.mts' || ext === '.cts') {
17
17
  return 'ts';
18
18
  }
19
19
  if (ext === '.tsx') {
@@ -250,11 +250,14 @@ const collectModuleIdentifiers = async (ast, hoisting = true) => {
250
250
  const isModuleScope = _identifier.identifier.isModuleScope(ancestors);
251
251
  const isClassOrFuncDeclaration = _identifier.identifier.isClassOrFuncDeclarationId(ancestors);
252
252
  const isVarDeclarationInGlobalScope = _identifier.identifier.isVarDeclarationInGlobalScope(ancestors);
253
+ const parent = ancestors[ancestors.length - 2];
254
+ const grandParent = ancestors[ancestors.length - 3];
255
+ const hoistSafe = parent.type === 'FunctionDeclaration' || parent.type === 'VariableDeclarator' && grandParent?.type === 'VariableDeclaration' && grandParent.kind === 'var';
253
256
  if (isModuleScope || isClassOrFuncDeclaration || isVarDeclarationInGlobalScope) {
254
257
  meta.declare.push(node);
255
258
 
256
259
  // Check for hoisted reads
257
- if (hoisting && globalReads.has(name)) {
260
+ if (hoisting && hoistSafe && globalReads.has(name)) {
258
261
  const reads = globalReads.get(name);
259
262
  if (reads) {
260
263
  reads.forEach(read => {
@@ -278,7 +281,7 @@ const collectModuleIdentifiers = async (ast, hoisting = true) => {
278
281
  const {
279
282
  type
280
283
  } = node;
281
- if (_scope.scopes.includes(type)) {
284
+ if (_scopeNodes.scopeNodes.includes(type)) {
282
285
  scopes.pop();
283
286
  }
284
287
  }
package/dist/format.js CHANGED
@@ -375,6 +375,12 @@ const format = async (src, ast, opts) => {
375
375
  }
376
376
  }
377
377
  }
378
+ if (shouldRaiseEsm && node.type === 'WithStatement') {
379
+ throw new Error('Cannot transform to ESM: with statements are not supported.');
380
+ }
381
+ if (shouldRaiseEsm && node.type === 'CallExpression' && node.callee.type === 'Identifier' && node.callee.name === 'eval' && !shadowedBindings.has('eval')) {
382
+ throw new Error('Cannot transform to ESM: eval is not supported.');
383
+ }
378
384
  if (shouldRaiseEsm && node.type === 'CallExpression' && isRequireCall(node, shadowedBindings)) {
379
385
  const isStatic = isStaticRequire(node, shadowedBindings);
380
386
  const parent = ancestors[ancestors.length - 2] ?? null;
@@ -0,0 +1,2 @@
1
+ declare const scopeNodes: string[];
2
+ export { scopeNodes };
@@ -0,0 +1,2 @@
1
+ declare const scopeNodes: string[];
2
+ export { scopeNodes };
@@ -1,6 +1,6 @@
1
1
  import { ancestorWalk } from '#walk';
2
- import { scopes as scopeNodes } from '#helpers/scope.js';
3
2
  import { identifier } from '#helpers/identifier.js';
3
+ import { scopeNodes } from './scopeNodes.js';
4
4
  const collectScopeIdentifiers = (node, scopes) => {
5
5
  const {
6
6
  type
@@ -145,11 +145,14 @@ const collectModuleIdentifiers = async (ast, hoisting = true) => {
145
145
  const isModuleScope = identifier.isModuleScope(ancestors);
146
146
  const isClassOrFuncDeclaration = identifier.isClassOrFuncDeclarationId(ancestors);
147
147
  const isVarDeclarationInGlobalScope = identifier.isVarDeclarationInGlobalScope(ancestors);
148
+ const parent = ancestors[ancestors.length - 2];
149
+ const grandParent = ancestors[ancestors.length - 3];
150
+ const hoistSafe = parent.type === 'FunctionDeclaration' || parent.type === 'VariableDeclarator' && grandParent?.type === 'VariableDeclaration' && grandParent.kind === 'var';
148
151
  if (isModuleScope || isClassOrFuncDeclaration || isVarDeclarationInGlobalScope) {
149
152
  meta.declare.push(node);
150
153
 
151
154
  // Check for hoisted reads
152
- if (hoisting && globalReads.has(name)) {
155
+ if (hoisting && hoistSafe && globalReads.has(name)) {
153
156
  const reads = globalReads.get(name);
154
157
  if (reads) {
155
158
  reads.forEach(read => {
@@ -3,11 +3,11 @@ import { extname } from 'node:path';
3
3
  // Determine language from filename extension for specifier rewrite.
4
4
 
5
5
  const getLangFromExt = filename => {
6
- const ext = extname(filename);
7
- if (ext.endsWith('.js')) {
6
+ const ext = extname(filename).toLowerCase();
7
+ if (ext === '.js' || ext === '.mjs' || ext === '.cjs') {
8
8
  return 'js';
9
9
  }
10
- if (ext.endsWith('.ts')) {
10
+ if (ext === '.ts' || ext === '.mts' || ext === '.cts') {
11
11
  return 'ts';
12
12
  }
13
13
  if (ext === '.tsx') {
@@ -0,0 +1,2 @@
1
+ const scopeNodes = ['BlockStatement', 'FunctionDeclaration', 'FunctionExpression', 'ArrowFunctionExpression', 'ClassDeclaration', 'ClassExpression', 'ClassBody', 'StaticBlock'];
2
+ export { scopeNodes };
package/dist/utils.js CHANGED
@@ -1,13 +1,13 @@
1
1
  import { extname } from 'node:path';
2
2
  import { ancestorWalk } from './walk.js';
3
- import { scopes as scopeNodes } from './helpers/scope.js';
4
3
  import { identifier } from './helpers/identifier.js';
4
+ import { scopeNodes } from './utils/scopeNodes.js';
5
5
  const getLangFromExt = filename => {
6
- const ext = extname(filename);
7
- if (ext.endsWith('.js')) {
6
+ const ext = extname(filename).toLowerCase();
7
+ if (ext === '.js' || ext === '.mjs' || ext === '.cjs') {
8
8
  return 'js';
9
9
  }
10
- if (ext.endsWith('.ts')) {
10
+ if (ext === '.ts' || ext === '.mts' || ext === '.cts') {
11
11
  return 'ts';
12
12
  }
13
13
  if (ext === '.tsx') {
@@ -240,11 +240,14 @@ const collectModuleIdentifiers = async (ast, hoisting = true) => {
240
240
  const isModuleScope = identifier.isModuleScope(ancestors);
241
241
  const isClassOrFuncDeclaration = identifier.isClassOrFuncDeclarationId(ancestors);
242
242
  const isVarDeclarationInGlobalScope = identifier.isVarDeclarationInGlobalScope(ancestors);
243
+ const parent = ancestors[ancestors.length - 2];
244
+ const grandParent = ancestors[ancestors.length - 3];
245
+ const hoistSafe = parent.type === 'FunctionDeclaration' || parent.type === 'VariableDeclarator' && grandParent?.type === 'VariableDeclaration' && grandParent.kind === 'var';
243
246
  if (isModuleScope || isClassOrFuncDeclaration || isVarDeclarationInGlobalScope) {
244
247
  meta.declare.push(node);
245
248
 
246
249
  // Check for hoisted reads
247
- if (hoisting && globalReads.has(name)) {
250
+ if (hoisting && hoistSafe && globalReads.has(name)) {
248
251
  const reads = globalReads.get(name);
249
252
  if (reads) {
250
253
  reads.forEach(read => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knighted/module",
3
- "version": "1.0.0-beta.3",
3
+ "version": "1.0.0-beta.5",
4
4
  "description": "Transforms differences between ES modules and CommonJS.",
5
5
  "type": "module",
6
6
  "main": "dist/module.js",
@@ -27,7 +27,7 @@
27
27
  "#formatters/*.js": "./src/formatters/*.js"
28
28
  },
29
29
  "engines": {
30
- "node": ">=20.11.0"
30
+ "node": ">=22.21.1"
31
31
  },
32
32
  "engineStrict": true,
33
33
  "scripts": {
@@ -1,12 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.scopes = exports.scope = void 0;
7
- const scopes = exports.scopes = ['BlockStatement', 'FunctionDeclaration', 'FunctionExpression', 'ArrowFunctionExpression', 'ClassDeclaration', 'ClassExpression', 'ClassBody', 'StaticBlock'];
8
- const scope = exports.scope = {
9
- isScope(node) {
10
- return scopes.includes(node.type);
11
- }
12
- };
@@ -1,6 +0,0 @@
1
- import type { Node } from 'oxc-parser';
2
- declare const scopes: string[];
3
- declare const scope: {
4
- isScope(node: Node): boolean;
5
- };
6
- export { scopes, scope };
@@ -1,7 +0,0 @@
1
- const scopes = ['BlockStatement', 'FunctionDeclaration', 'FunctionExpression', 'ArrowFunctionExpression', 'ClassDeclaration', 'ClassExpression', 'ClassBody', 'StaticBlock'];
2
- const scope = {
3
- isScope(node) {
4
- return scopes.includes(node.type);
5
- }
6
- };
7
- export { scopes, scope };
package/dist/scope.d.ts DELETED
@@ -1,6 +0,0 @@
1
- import type { Node } from 'oxc-parser';
2
- declare const scopes: string[];
3
- declare const scope: {
4
- isScope(node: Node): boolean;
5
- };
6
- export { scopes, scope };
@@ -1,6 +0,0 @@
1
- import type { Node } from 'oxc-parser';
2
- declare const scopes: string[];
3
- declare const scope: {
4
- isScope(node: Node): boolean;
5
- };
6
- export { scopes, scope };