@knighted/module 1.0.0-alpha.1 → 1.0.0-alpha.3
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 +9 -4
- package/dist/cjs/expressionStatement.d.cts +5 -0
- package/dist/cjs/format.cjs +14 -69
- package/dist/cjs/format.d.cts +6 -4
- package/dist/cjs/formatters/expressionStatement.cjs +68 -0
- package/dist/cjs/formatters/memberExpression.cjs +40 -0
- package/dist/cjs/formatters/metaProperty.cjs +50 -0
- package/dist/cjs/memberExpression.d.cts +5 -0
- package/dist/cjs/metaProperty.d.cts +5 -0
- package/dist/cjs/module.cjs +27 -10
- package/dist/cjs/module.d.cts +0 -6
- package/dist/cjs/types.d.cts +3 -2
- package/dist/expressionStatement.d.ts +5 -0
- package/dist/format.d.ts +6 -4
- package/dist/format.js +14 -69
- package/dist/formatters/expressionStatement.d.ts +5 -0
- package/dist/formatters/expressionStatement.js +61 -0
- package/dist/formatters/memberExpression.d.ts +5 -0
- package/dist/formatters/memberExpression.js +33 -0
- package/dist/formatters/metaProperty.d.ts +5 -0
- package/dist/formatters/metaProperty.js +43 -0
- package/dist/memberExpression.d.ts +5 -0
- package/dist/metaProperty.d.ts +5 -0
- package/dist/module.d.ts +0 -6
- package/dist/module.js +28 -11
- package/dist/types.d.ts +3 -2
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -55,7 +55,7 @@ await transform('./file.js', {
|
|
|
55
55
|
|
|
56
56
|
Which produces
|
|
57
57
|
|
|
58
|
-
**file.
|
|
58
|
+
**file.cjs**
|
|
59
59
|
|
|
60
60
|
```js
|
|
61
61
|
const { argv } = require('node:process')
|
|
@@ -87,10 +87,15 @@ type ModuleOptions = {
|
|
|
87
87
|
/* What module system to convert to. */
|
|
88
88
|
type?: 'module' | 'commonjs'
|
|
89
89
|
/* Whether import/export and require/exports should be transformed. */
|
|
90
|
-
|
|
90
|
+
modules?: boolean
|
|
91
91
|
/* Whether to change specifier extensions to the assigned value. If omitted they are left alone. */
|
|
92
|
-
|
|
93
|
-
/* What filepath to write the transformed source to.
|
|
92
|
+
specifier?: '.js' | '.mjs' | '.cjs' | '.ts' | '.mts' | '.cts'
|
|
93
|
+
/* What filepath to write the transformed source to. */
|
|
94
94
|
out?: string
|
|
95
95
|
}
|
|
96
96
|
```
|
|
97
|
+
|
|
98
|
+
## Roadmap
|
|
99
|
+
|
|
100
|
+
- Support option `modules`.
|
|
101
|
+
- Remove `@knighted/specifier` and avoid double parsing.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import MagicString from 'magic-string';
|
|
2
|
+
import type { NodePath } from '@babel/traverse';
|
|
3
|
+
import type { ExpressionStatement } from '@babel/types';
|
|
4
|
+
import type { FormatterOptions } from '../types.cjs';
|
|
5
|
+
export declare const expressionStatement: (nodePath: NodePath<ExpressionStatement>, src: MagicString, options: FormatterOptions) => void;
|
package/dist/cjs/format.cjs
CHANGED
|
@@ -6,82 +6,27 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.format = void 0;
|
|
7
7
|
var _magicString = _interopRequireDefault(require("magic-string"));
|
|
8
8
|
var _traverse2 = _interopRequireDefault(require("@babel/traverse"));
|
|
9
|
+
var _metaProperty = require("./formatters/metaProperty.cjs");
|
|
10
|
+
var _memberExpression = require("./formatters/memberExpression.cjs");
|
|
11
|
+
var _expressionStatement = require("./formatters/expressionStatement.cjs");
|
|
9
12
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
13
|
const traverse = _traverse2.default.default;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Note, there is no specific conversion for `import.meta.main` as it does not exist.
|
|
17
|
+
* @see https://github.com/nodejs/node/issues/49440
|
|
18
|
+
*/
|
|
11
19
|
const format = (code, ast, options) => {
|
|
12
20
|
const src = new _magicString.default(code);
|
|
13
|
-
const {
|
|
14
|
-
type = 'commonjs'
|
|
15
|
-
} = options;
|
|
16
21
|
traverse(ast, {
|
|
17
|
-
MetaProperty(
|
|
18
|
-
|
|
19
|
-
const path = metaPropertyPath.findParent(path => path.isMemberExpression());
|
|
20
|
-
if (path) {
|
|
21
|
-
const {
|
|
22
|
-
node
|
|
23
|
-
} = path;
|
|
24
|
-
const {
|
|
25
|
-
start,
|
|
26
|
-
end
|
|
27
|
-
} = node;
|
|
28
|
-
if (node.type === 'MemberExpression' && node.property.type === 'Identifier' && typeof start == 'number' && typeof end === 'number') {
|
|
29
|
-
const name = node.property.name;
|
|
30
|
-
switch (name) {
|
|
31
|
-
case 'url':
|
|
32
|
-
src.update(start, end, 'require("node:url").pathToFileURL(__filename).toString()');
|
|
33
|
-
break;
|
|
34
|
-
case 'filename':
|
|
35
|
-
src.update(start, end, '__filename');
|
|
36
|
-
break;
|
|
37
|
-
case 'dirname':
|
|
38
|
-
src.update(start, end, '__dirname');
|
|
39
|
-
break;
|
|
40
|
-
case 'resolve':
|
|
41
|
-
src.update(start, end, 'require.resolve');
|
|
42
|
-
break;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
22
|
+
MetaProperty(path) {
|
|
23
|
+
(0, _metaProperty.metaProperty)(path, src, options);
|
|
47
24
|
},
|
|
48
|
-
ExpressionStatement(
|
|
49
|
-
|
|
50
|
-
const {
|
|
51
|
-
node
|
|
52
|
-
} = expressionStatementPath;
|
|
53
|
-
const {
|
|
54
|
-
start,
|
|
55
|
-
end
|
|
56
|
-
} = node;
|
|
57
|
-
if (node.expression.type === 'Identifier' && typeof start === 'number' && typeof end === 'number') {
|
|
58
|
-
const name = node.expression.name;
|
|
59
|
-
switch (name) {
|
|
60
|
-
case '__filename':
|
|
61
|
-
src.update(start, end, 'import.meta.filename');
|
|
62
|
-
break;
|
|
63
|
-
case '__dirname':
|
|
64
|
-
src.update(start, end, 'import.meta.dirname');
|
|
65
|
-
break;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
25
|
+
ExpressionStatement(path) {
|
|
26
|
+
(0, _expressionStatement.expressionStatement)(path, src, options);
|
|
69
27
|
},
|
|
70
|
-
MemberExpression(
|
|
71
|
-
|
|
72
|
-
const {
|
|
73
|
-
node
|
|
74
|
-
} = memberExpressionPath;
|
|
75
|
-
const {
|
|
76
|
-
start,
|
|
77
|
-
end
|
|
78
|
-
} = node;
|
|
79
|
-
|
|
80
|
-
// Update require.resolve to import.meta.resolve
|
|
81
|
-
if (node.object.type === 'Identifier' && node.object.name === 'require' && node.property.type === 'Identifier' && node.property.name == 'resolve' && typeof start === 'number' && typeof end === 'number') {
|
|
82
|
-
src.update(start, end, 'import.meta.resolve');
|
|
83
|
-
}
|
|
84
|
-
}
|
|
28
|
+
MemberExpression(path) {
|
|
29
|
+
(0, _memberExpression.memberExpression)(path, src, options);
|
|
85
30
|
}
|
|
86
31
|
});
|
|
87
32
|
return src;
|
package/dist/cjs/format.d.cts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import MagicString from 'magic-string';
|
|
2
2
|
import type { ParseResult } from '@babel/parser';
|
|
3
3
|
import type { File } from '@babel/types';
|
|
4
|
-
import type {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
import type { FormatterOptions } from './types.cjs';
|
|
5
|
+
/**
|
|
6
|
+
* Note, there is no specific conversion for `import.meta.main` as it does not exist.
|
|
7
|
+
* @see https://github.com/nodejs/node/issues/49440
|
|
8
|
+
*/
|
|
9
|
+
export declare const format: (code: string, ast: ParseResult<File>, options: FormatterOptions) => MagicString;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.expressionStatement = void 0;
|
|
7
|
+
const expressionStatement = (nodePath, src, options) => {
|
|
8
|
+
if (options.type === 'module') {
|
|
9
|
+
const {
|
|
10
|
+
node
|
|
11
|
+
} = nodePath;
|
|
12
|
+
const {
|
|
13
|
+
start,
|
|
14
|
+
end
|
|
15
|
+
} = node;
|
|
16
|
+
if (typeof start === 'number' && typeof end === 'number') {
|
|
17
|
+
const isMemberExpressionModuleExports = expression => {
|
|
18
|
+
return expression.object.type === 'Identifier' && expression.object.name === 'module' && expression.property.type === 'Identifier' && expression.property.name === 'exports';
|
|
19
|
+
};
|
|
20
|
+
if (node.expression.type === 'Identifier') {
|
|
21
|
+
const name = node.expression.name;
|
|
22
|
+
|
|
23
|
+
// CommonJS globals
|
|
24
|
+
switch (name) {
|
|
25
|
+
case 'module':
|
|
26
|
+
src.update(start, end, 'import.meta');
|
|
27
|
+
break;
|
|
28
|
+
case 'exports':
|
|
29
|
+
src.update(start, end, '{}');
|
|
30
|
+
break;
|
|
31
|
+
case '__filename':
|
|
32
|
+
src.update(start, end, 'import.meta.filename');
|
|
33
|
+
break;
|
|
34
|
+
case '__dirname':
|
|
35
|
+
src.update(start, end, 'import.meta.dirname');
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (node.expression.type === 'MemberExpression') {
|
|
40
|
+
const {
|
|
41
|
+
expression
|
|
42
|
+
} = node;
|
|
43
|
+
|
|
44
|
+
// Check for `module.exports` without an assignment
|
|
45
|
+
if (isMemberExpressionModuleExports(expression)) {
|
|
46
|
+
/**
|
|
47
|
+
* @TODO: Should this depend on `options.modules` being enabled?
|
|
48
|
+
* Probably not for the same reason `exports` is converted to an empty object (ReferenceError in ESM).
|
|
49
|
+
* This is a standalone reference to `module.exports` without being part of an AssignmentExpression.
|
|
50
|
+
*/
|
|
51
|
+
src.update(start, end, '{}');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/*
|
|
56
|
+
if (
|
|
57
|
+
options.modules &&
|
|
58
|
+
node.expression.type === 'AssignmentExpression' &&
|
|
59
|
+
node.expression.left.type === 'MemberExpression' &&
|
|
60
|
+
isMemberExpressionModuleExports(node.expression.left)
|
|
61
|
+
) {
|
|
62
|
+
// @TODO support `modules` option.
|
|
63
|
+
}
|
|
64
|
+
*/
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
exports.expressionStatement = expressionStatement;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.memberExpression = void 0;
|
|
7
|
+
const memberExpression = (nodePath, src, options) => {
|
|
8
|
+
if (options.type === 'module') {
|
|
9
|
+
const {
|
|
10
|
+
node
|
|
11
|
+
} = nodePath;
|
|
12
|
+
const {
|
|
13
|
+
start,
|
|
14
|
+
end
|
|
15
|
+
} = node;
|
|
16
|
+
if (typeof start === 'number' && typeof end === 'number' && node.object.type === 'Identifier' && node.object.name === 'require' && node.property.type === 'Identifier') {
|
|
17
|
+
const {
|
|
18
|
+
name
|
|
19
|
+
} = node.property;
|
|
20
|
+
|
|
21
|
+
// CommonJS properties of `require`
|
|
22
|
+
switch (name) {
|
|
23
|
+
case 'main':
|
|
24
|
+
src.update(start, end, 'import.meta');
|
|
25
|
+
break;
|
|
26
|
+
case 'resolve':
|
|
27
|
+
src.update(start, end, 'import.meta.resolve');
|
|
28
|
+
break;
|
|
29
|
+
case 'cache':
|
|
30
|
+
/**
|
|
31
|
+
* Can of worms here. ¯\_(ツ)_/¯
|
|
32
|
+
* @see https://github.com/nodejs/help/issues/2806
|
|
33
|
+
*/
|
|
34
|
+
src.update(start, end, '{}');
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
exports.memberExpression = memberExpression;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.metaProperty = void 0;
|
|
7
|
+
const metaProperty = (nodePath, src, options) => {
|
|
8
|
+
if (options.type === 'commonjs') {
|
|
9
|
+
const path = nodePath.findParent(path => path.isMemberExpression());
|
|
10
|
+
if (path) {
|
|
11
|
+
const {
|
|
12
|
+
node
|
|
13
|
+
} = path;
|
|
14
|
+
const {
|
|
15
|
+
start,
|
|
16
|
+
end
|
|
17
|
+
} = node;
|
|
18
|
+
if (node.type === 'MemberExpression' && node.property.type === 'Identifier' && typeof start == 'number' && typeof end === 'number') {
|
|
19
|
+
const name = node.property.name;
|
|
20
|
+
switch (name) {
|
|
21
|
+
case 'url':
|
|
22
|
+
src.update(start, end, 'require("node:url").pathToFileURL(__filename).toString()');
|
|
23
|
+
break;
|
|
24
|
+
case 'filename':
|
|
25
|
+
src.update(start, end, '__filename');
|
|
26
|
+
break;
|
|
27
|
+
case 'dirname':
|
|
28
|
+
src.update(start, end, '__dirname');
|
|
29
|
+
break;
|
|
30
|
+
case 'resolve':
|
|
31
|
+
src.update(start, end, 'require.resolve');
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
} else {
|
|
36
|
+
const {
|
|
37
|
+
node
|
|
38
|
+
} = nodePath;
|
|
39
|
+
const {
|
|
40
|
+
start,
|
|
41
|
+
end
|
|
42
|
+
} = node;
|
|
43
|
+
if (node.property.type === 'Identifier' && node.property.name === 'meta' && typeof start === 'number' && typeof end === 'number') {
|
|
44
|
+
// This is an `import.meta` expression
|
|
45
|
+
src.update(start, end, 'require.main');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
exports.metaProperty = metaProperty;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import MagicString from 'magic-string';
|
|
2
|
+
import type { NodePath } from '@babel/traverse';
|
|
3
|
+
import type { MemberExpression } from '@babel/types';
|
|
4
|
+
import type { FormatterOptions } from '../types.cjs';
|
|
5
|
+
export declare const memberExpression: (nodePath: NodePath<MemberExpression>, src: MagicString, options: FormatterOptions) => void;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import MagicString from 'magic-string';
|
|
2
|
+
import type { NodePath } from '@babel/traverse';
|
|
3
|
+
import type { MetaProperty } from '@babel/types';
|
|
4
|
+
import type { FormatterOptions } from '../types.cjs';
|
|
5
|
+
export declare const metaProperty: (nodePath: NodePath<MetaProperty>, src: MagicString, options: FormatterOptions) => void;
|
package/dist/cjs/module.cjs
CHANGED
|
@@ -6,21 +6,15 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.transform = void 0;
|
|
7
7
|
var _nodePath = require("node:path");
|
|
8
8
|
var _promises = require("node:fs/promises");
|
|
9
|
+
var _specifier = require("@knighted/specifier");
|
|
9
10
|
var _parse = require("./parse.cjs");
|
|
10
11
|
var _format = require("./format.cjs");
|
|
11
12
|
const defaultOptions = {
|
|
12
13
|
type: 'commonjs',
|
|
13
14
|
out: undefined,
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
modules: false,
|
|
16
|
+
specifier: undefined
|
|
16
17
|
};
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Transforms a file from one Node.js module system to another based on options.
|
|
20
|
-
* Module globals, for example import.meta, __dirname, __filename, etc. are always transformed.
|
|
21
|
-
* However, the CommonJS `module` object is not transformed, for instance `module.path`, `module.children`, etc.,
|
|
22
|
-
* with the exception of `module.exports` which is converted when `loading` is set to `true`.
|
|
23
|
-
*/
|
|
24
18
|
const transform = async (filename, options = defaultOptions) => {
|
|
25
19
|
const opts = {
|
|
26
20
|
...defaultOptions,
|
|
@@ -29,6 +23,29 @@ const transform = async (filename, options = defaultOptions) => {
|
|
|
29
23
|
const file = (0, _nodePath.resolve)(filename);
|
|
30
24
|
const code = (await (0, _promises.readFile)(file)).toString();
|
|
31
25
|
const ast = (0, _parse.parse)(code);
|
|
32
|
-
|
|
26
|
+
let source = (0, _format.format)(code, ast, opts).toString();
|
|
27
|
+
if (options.specifier) {
|
|
28
|
+
const {
|
|
29
|
+
code,
|
|
30
|
+
error
|
|
31
|
+
} = await _specifier.specifier.updateSrc(source, ({
|
|
32
|
+
value
|
|
33
|
+
}) => {
|
|
34
|
+
// Collapse any BinaryExpression or NewExpression to test for a relative specifier
|
|
35
|
+
const collapsed = value.replace(/['"`+)\s]|new String\(/g, '');
|
|
36
|
+
const relative = /^(?:\.|\.\.)\//;
|
|
37
|
+
if (relative.test(collapsed)) {
|
|
38
|
+
// $2 is for any closing quotation/parens around BE or NE
|
|
39
|
+
return value.replace(/(.+)\.(?:m|c)?(?:j|t)s([)'"`]*)?$/, `$1${options.specifier}$2`);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
if (code && !error) {
|
|
43
|
+
source = code;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (opts.out) {
|
|
47
|
+
await (0, _promises.writeFile)((0, _nodePath.resolve)(opts.out), source);
|
|
48
|
+
}
|
|
49
|
+
return source;
|
|
33
50
|
};
|
|
34
51
|
exports.transform = transform;
|
package/dist/cjs/module.d.cts
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
1
|
import type { ModuleOptions } from './types.cjs';
|
|
2
|
-
/**
|
|
3
|
-
* Transforms a file from one Node.js module system to another based on options.
|
|
4
|
-
* Module globals, for example import.meta, __dirname, __filename, etc. are always transformed.
|
|
5
|
-
* However, the CommonJS `module` object is not transformed, for instance `module.path`, `module.children`, etc.,
|
|
6
|
-
* with the exception of `module.exports` which is converted when `loading` is set to `true`.
|
|
7
|
-
*/
|
|
8
2
|
declare const transform: (filename: string, options?: ModuleOptions) => Promise<string>;
|
|
9
3
|
export { transform };
|
package/dist/cjs/types.d.cts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export type ModuleOptions = {
|
|
2
2
|
type?: 'module' | 'commonjs';
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
modules?: boolean;
|
|
4
|
+
specifier?: '.js' | '.mjs' | '.cjs' | '.ts' | '.mts' | '.cts';
|
|
5
5
|
out?: string;
|
|
6
6
|
};
|
|
7
|
+
export type FormatterOptions = Omit<ModuleOptions, 'out'> & Required<Pick<ModuleOptions, 'type'>>;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import MagicString from 'magic-string';
|
|
2
|
+
import type { NodePath } from '@babel/traverse';
|
|
3
|
+
import type { ExpressionStatement } from '@babel/types';
|
|
4
|
+
import type { FormatterOptions } from '../types.js';
|
|
5
|
+
export declare const expressionStatement: (nodePath: NodePath<ExpressionStatement>, src: MagicString, options: FormatterOptions) => void;
|
package/dist/format.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import MagicString from 'magic-string';
|
|
2
2
|
import type { ParseResult } from '@babel/parser';
|
|
3
3
|
import type { File } from '@babel/types';
|
|
4
|
-
import type {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
import type { FormatterOptions } from './types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Note, there is no specific conversion for `import.meta.main` as it does not exist.
|
|
7
|
+
* @see https://github.com/nodejs/node/issues/49440
|
|
8
|
+
*/
|
|
9
|
+
export declare const format: (code: string, ast: ParseResult<File>, options: FormatterOptions) => MagicString;
|
package/dist/format.js
CHANGED
|
@@ -1,80 +1,25 @@
|
|
|
1
1
|
import MagicString from 'magic-string';
|
|
2
2
|
import _traverse from '@babel/traverse';
|
|
3
|
+
import { metaProperty } from './formatters/metaProperty.js';
|
|
4
|
+
import { memberExpression } from './formatters/memberExpression.js';
|
|
5
|
+
import { expressionStatement } from './formatters/expressionStatement.js';
|
|
3
6
|
const traverse = _traverse.default;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Note, there is no specific conversion for `import.meta.main` as it does not exist.
|
|
10
|
+
* @see https://github.com/nodejs/node/issues/49440
|
|
11
|
+
*/
|
|
4
12
|
export const format = (code, ast, options) => {
|
|
5
13
|
const src = new MagicString(code);
|
|
6
|
-
const {
|
|
7
|
-
type = 'commonjs'
|
|
8
|
-
} = options;
|
|
9
14
|
traverse(ast, {
|
|
10
|
-
MetaProperty(
|
|
11
|
-
|
|
12
|
-
const path = metaPropertyPath.findParent(path => path.isMemberExpression());
|
|
13
|
-
if (path) {
|
|
14
|
-
const {
|
|
15
|
-
node
|
|
16
|
-
} = path;
|
|
17
|
-
const {
|
|
18
|
-
start,
|
|
19
|
-
end
|
|
20
|
-
} = node;
|
|
21
|
-
if (node.type === 'MemberExpression' && node.property.type === 'Identifier' && typeof start == 'number' && typeof end === 'number') {
|
|
22
|
-
const name = node.property.name;
|
|
23
|
-
switch (name) {
|
|
24
|
-
case 'url':
|
|
25
|
-
src.update(start, end, 'require("node:url").pathToFileURL(__filename).toString()');
|
|
26
|
-
break;
|
|
27
|
-
case 'filename':
|
|
28
|
-
src.update(start, end, '__filename');
|
|
29
|
-
break;
|
|
30
|
-
case 'dirname':
|
|
31
|
-
src.update(start, end, '__dirname');
|
|
32
|
-
break;
|
|
33
|
-
case 'resolve':
|
|
34
|
-
src.update(start, end, 'require.resolve');
|
|
35
|
-
break;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
15
|
+
MetaProperty(path) {
|
|
16
|
+
metaProperty(path, src, options);
|
|
40
17
|
},
|
|
41
|
-
ExpressionStatement(
|
|
42
|
-
|
|
43
|
-
const {
|
|
44
|
-
node
|
|
45
|
-
} = expressionStatementPath;
|
|
46
|
-
const {
|
|
47
|
-
start,
|
|
48
|
-
end
|
|
49
|
-
} = node;
|
|
50
|
-
if (node.expression.type === 'Identifier' && typeof start === 'number' && typeof end === 'number') {
|
|
51
|
-
const name = node.expression.name;
|
|
52
|
-
switch (name) {
|
|
53
|
-
case '__filename':
|
|
54
|
-
src.update(start, end, 'import.meta.filename');
|
|
55
|
-
break;
|
|
56
|
-
case '__dirname':
|
|
57
|
-
src.update(start, end, 'import.meta.dirname');
|
|
58
|
-
break;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
18
|
+
ExpressionStatement(path) {
|
|
19
|
+
expressionStatement(path, src, options);
|
|
62
20
|
},
|
|
63
|
-
MemberExpression(
|
|
64
|
-
|
|
65
|
-
const {
|
|
66
|
-
node
|
|
67
|
-
} = memberExpressionPath;
|
|
68
|
-
const {
|
|
69
|
-
start,
|
|
70
|
-
end
|
|
71
|
-
} = node;
|
|
72
|
-
|
|
73
|
-
// Update require.resolve to import.meta.resolve
|
|
74
|
-
if (node.object.type === 'Identifier' && node.object.name === 'require' && node.property.type === 'Identifier' && node.property.name == 'resolve' && typeof start === 'number' && typeof end === 'number') {
|
|
75
|
-
src.update(start, end, 'import.meta.resolve');
|
|
76
|
-
}
|
|
77
|
-
}
|
|
21
|
+
MemberExpression(path) {
|
|
22
|
+
memberExpression(path, src, options);
|
|
78
23
|
}
|
|
79
24
|
});
|
|
80
25
|
return src;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import MagicString from 'magic-string';
|
|
2
|
+
import type { NodePath } from '@babel/traverse';
|
|
3
|
+
import type { ExpressionStatement } from '@babel/types';
|
|
4
|
+
import type { FormatterOptions } from '../types.js';
|
|
5
|
+
export declare const expressionStatement: (nodePath: NodePath<ExpressionStatement>, src: MagicString, options: FormatterOptions) => void;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export const expressionStatement = (nodePath, src, options) => {
|
|
2
|
+
if (options.type === 'module') {
|
|
3
|
+
const {
|
|
4
|
+
node
|
|
5
|
+
} = nodePath;
|
|
6
|
+
const {
|
|
7
|
+
start,
|
|
8
|
+
end
|
|
9
|
+
} = node;
|
|
10
|
+
if (typeof start === 'number' && typeof end === 'number') {
|
|
11
|
+
const isMemberExpressionModuleExports = expression => {
|
|
12
|
+
return expression.object.type === 'Identifier' && expression.object.name === 'module' && expression.property.type === 'Identifier' && expression.property.name === 'exports';
|
|
13
|
+
};
|
|
14
|
+
if (node.expression.type === 'Identifier') {
|
|
15
|
+
const name = node.expression.name;
|
|
16
|
+
|
|
17
|
+
// CommonJS globals
|
|
18
|
+
switch (name) {
|
|
19
|
+
case 'module':
|
|
20
|
+
src.update(start, end, 'import.meta');
|
|
21
|
+
break;
|
|
22
|
+
case 'exports':
|
|
23
|
+
src.update(start, end, '{}');
|
|
24
|
+
break;
|
|
25
|
+
case '__filename':
|
|
26
|
+
src.update(start, end, 'import.meta.filename');
|
|
27
|
+
break;
|
|
28
|
+
case '__dirname':
|
|
29
|
+
src.update(start, end, 'import.meta.dirname');
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (node.expression.type === 'MemberExpression') {
|
|
34
|
+
const {
|
|
35
|
+
expression
|
|
36
|
+
} = node;
|
|
37
|
+
|
|
38
|
+
// Check for `module.exports` without an assignment
|
|
39
|
+
if (isMemberExpressionModuleExports(expression)) {
|
|
40
|
+
/**
|
|
41
|
+
* @TODO: Should this depend on `options.modules` being enabled?
|
|
42
|
+
* Probably not for the same reason `exports` is converted to an empty object (ReferenceError in ESM).
|
|
43
|
+
* This is a standalone reference to `module.exports` without being part of an AssignmentExpression.
|
|
44
|
+
*/
|
|
45
|
+
src.update(start, end, '{}');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/*
|
|
50
|
+
if (
|
|
51
|
+
options.modules &&
|
|
52
|
+
node.expression.type === 'AssignmentExpression' &&
|
|
53
|
+
node.expression.left.type === 'MemberExpression' &&
|
|
54
|
+
isMemberExpressionModuleExports(node.expression.left)
|
|
55
|
+
) {
|
|
56
|
+
// @TODO support `modules` option.
|
|
57
|
+
}
|
|
58
|
+
*/
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import MagicString from 'magic-string';
|
|
2
|
+
import type { NodePath } from '@babel/traverse';
|
|
3
|
+
import type { MemberExpression } from '@babel/types';
|
|
4
|
+
import type { FormatterOptions } from '../types.js';
|
|
5
|
+
export declare const memberExpression: (nodePath: NodePath<MemberExpression>, src: MagicString, options: FormatterOptions) => void;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export const memberExpression = (nodePath, src, options) => {
|
|
2
|
+
if (options.type === 'module') {
|
|
3
|
+
const {
|
|
4
|
+
node
|
|
5
|
+
} = nodePath;
|
|
6
|
+
const {
|
|
7
|
+
start,
|
|
8
|
+
end
|
|
9
|
+
} = node;
|
|
10
|
+
if (typeof start === 'number' && typeof end === 'number' && node.object.type === 'Identifier' && node.object.name === 'require' && node.property.type === 'Identifier') {
|
|
11
|
+
const {
|
|
12
|
+
name
|
|
13
|
+
} = node.property;
|
|
14
|
+
|
|
15
|
+
// CommonJS properties of `require`
|
|
16
|
+
switch (name) {
|
|
17
|
+
case 'main':
|
|
18
|
+
src.update(start, end, 'import.meta');
|
|
19
|
+
break;
|
|
20
|
+
case 'resolve':
|
|
21
|
+
src.update(start, end, 'import.meta.resolve');
|
|
22
|
+
break;
|
|
23
|
+
case 'cache':
|
|
24
|
+
/**
|
|
25
|
+
* Can of worms here. ¯\_(ツ)_/¯
|
|
26
|
+
* @see https://github.com/nodejs/help/issues/2806
|
|
27
|
+
*/
|
|
28
|
+
src.update(start, end, '{}');
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import MagicString from 'magic-string';
|
|
2
|
+
import type { NodePath } from '@babel/traverse';
|
|
3
|
+
import type { MetaProperty } from '@babel/types';
|
|
4
|
+
import type { FormatterOptions } from '../types.js';
|
|
5
|
+
export declare const metaProperty: (nodePath: NodePath<MetaProperty>, src: MagicString, options: FormatterOptions) => void;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export const metaProperty = (nodePath, src, options) => {
|
|
2
|
+
if (options.type === 'commonjs') {
|
|
3
|
+
const path = nodePath.findParent(path => path.isMemberExpression());
|
|
4
|
+
if (path) {
|
|
5
|
+
const {
|
|
6
|
+
node
|
|
7
|
+
} = path;
|
|
8
|
+
const {
|
|
9
|
+
start,
|
|
10
|
+
end
|
|
11
|
+
} = node;
|
|
12
|
+
if (node.type === 'MemberExpression' && node.property.type === 'Identifier' && typeof start == 'number' && typeof end === 'number') {
|
|
13
|
+
const name = node.property.name;
|
|
14
|
+
switch (name) {
|
|
15
|
+
case 'url':
|
|
16
|
+
src.update(start, end, 'require("node:url").pathToFileURL(__filename).toString()');
|
|
17
|
+
break;
|
|
18
|
+
case 'filename':
|
|
19
|
+
src.update(start, end, '__filename');
|
|
20
|
+
break;
|
|
21
|
+
case 'dirname':
|
|
22
|
+
src.update(start, end, '__dirname');
|
|
23
|
+
break;
|
|
24
|
+
case 'resolve':
|
|
25
|
+
src.update(start, end, 'require.resolve');
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
} else {
|
|
30
|
+
const {
|
|
31
|
+
node
|
|
32
|
+
} = nodePath;
|
|
33
|
+
const {
|
|
34
|
+
start,
|
|
35
|
+
end
|
|
36
|
+
} = node;
|
|
37
|
+
if (node.property.type === 'Identifier' && node.property.name === 'meta' && typeof start === 'number' && typeof end === 'number') {
|
|
38
|
+
// This is an `import.meta` expression
|
|
39
|
+
src.update(start, end, 'require.main');
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import MagicString from 'magic-string';
|
|
2
|
+
import type { NodePath } from '@babel/traverse';
|
|
3
|
+
import type { MemberExpression } from '@babel/types';
|
|
4
|
+
import type { FormatterOptions } from '../types.js';
|
|
5
|
+
export declare const memberExpression: (nodePath: NodePath<MemberExpression>, src: MagicString, options: FormatterOptions) => void;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import MagicString from 'magic-string';
|
|
2
|
+
import type { NodePath } from '@babel/traverse';
|
|
3
|
+
import type { MetaProperty } from '@babel/types';
|
|
4
|
+
import type { FormatterOptions } from '../types.js';
|
|
5
|
+
export declare const metaProperty: (nodePath: NodePath<MetaProperty>, src: MagicString, options: FormatterOptions) => void;
|
package/dist/module.d.ts
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
1
|
import type { ModuleOptions } from './types.js';
|
|
2
|
-
/**
|
|
3
|
-
* Transforms a file from one Node.js module system to another based on options.
|
|
4
|
-
* Module globals, for example import.meta, __dirname, __filename, etc. are always transformed.
|
|
5
|
-
* However, the CommonJS `module` object is not transformed, for instance `module.path`, `module.children`, etc.,
|
|
6
|
-
* with the exception of `module.exports` which is converted when `loading` is set to `true`.
|
|
7
|
-
*/
|
|
8
2
|
declare const transform: (filename: string, options?: ModuleOptions) => Promise<string>;
|
|
9
3
|
export { transform };
|
package/dist/module.js
CHANGED
|
@@ -1,20 +1,14 @@
|
|
|
1
1
|
import { resolve } from 'node:path';
|
|
2
|
-
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { specifier } from '@knighted/specifier';
|
|
3
4
|
import { parse } from './parse.js';
|
|
4
5
|
import { format } from './format.js';
|
|
5
6
|
const defaultOptions = {
|
|
6
7
|
type: 'commonjs',
|
|
7
8
|
out: undefined,
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
modules: false,
|
|
10
|
+
specifier: undefined
|
|
10
11
|
};
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Transforms a file from one Node.js module system to another based on options.
|
|
14
|
-
* Module globals, for example import.meta, __dirname, __filename, etc. are always transformed.
|
|
15
|
-
* However, the CommonJS `module` object is not transformed, for instance `module.path`, `module.children`, etc.,
|
|
16
|
-
* with the exception of `module.exports` which is converted when `loading` is set to `true`.
|
|
17
|
-
*/
|
|
18
12
|
const transform = async (filename, options = defaultOptions) => {
|
|
19
13
|
const opts = {
|
|
20
14
|
...defaultOptions,
|
|
@@ -23,6 +17,29 @@ const transform = async (filename, options = defaultOptions) => {
|
|
|
23
17
|
const file = resolve(filename);
|
|
24
18
|
const code = (await readFile(file)).toString();
|
|
25
19
|
const ast = parse(code);
|
|
26
|
-
|
|
20
|
+
let source = format(code, ast, opts).toString();
|
|
21
|
+
if (options.specifier) {
|
|
22
|
+
const {
|
|
23
|
+
code,
|
|
24
|
+
error
|
|
25
|
+
} = await specifier.updateSrc(source, ({
|
|
26
|
+
value
|
|
27
|
+
}) => {
|
|
28
|
+
// Collapse any BinaryExpression or NewExpression to test for a relative specifier
|
|
29
|
+
const collapsed = value.replace(/['"`+)\s]|new String\(/g, '');
|
|
30
|
+
const relative = /^(?:\.|\.\.)\//;
|
|
31
|
+
if (relative.test(collapsed)) {
|
|
32
|
+
// $2 is for any closing quotation/parens around BE or NE
|
|
33
|
+
return value.replace(/(.+)\.(?:m|c)?(?:j|t)s([)'"`]*)?$/, `$1${options.specifier}$2`);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
if (code && !error) {
|
|
37
|
+
source = code;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (opts.out) {
|
|
41
|
+
await writeFile(resolve(opts.out), source);
|
|
42
|
+
}
|
|
43
|
+
return source;
|
|
27
44
|
};
|
|
28
45
|
export { transform };
|
package/dist/types.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export type ModuleOptions = {
|
|
2
2
|
type?: 'module' | 'commonjs';
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
modules?: boolean;
|
|
4
|
+
specifier?: '.js' | '.mjs' | '.cjs' | '.ts' | '.mts' | '.cts';
|
|
5
5
|
out?: string;
|
|
6
6
|
};
|
|
7
|
+
export type FormatterOptions = Omit<ModuleOptions, 'out'> & Required<Pick<ModuleOptions, 'type'>>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@knighted/module",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.3",
|
|
4
4
|
"description": "Converts module differences in source files between ES and CommonJS.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/module.js",
|
|
@@ -70,11 +70,12 @@
|
|
|
70
70
|
"prettier": "^3.2.5",
|
|
71
71
|
"tsx": "^4.11.0",
|
|
72
72
|
"typescript": "^5.4.5",
|
|
73
|
-
"typescript-eslint": "^8.0.0-alpha.
|
|
73
|
+
"typescript-eslint": "^8.0.0-alpha.18"
|
|
74
74
|
},
|
|
75
75
|
"dependencies": {
|
|
76
76
|
"@babel/parser": "^7.24.6",
|
|
77
77
|
"@babel/traverse": "^7.24.6",
|
|
78
|
+
"@knighted/specifier": "^2.0.0-rc.1",
|
|
78
79
|
"magic-string": "^0.30.10"
|
|
79
80
|
},
|
|
80
81
|
"prettier": {
|