@knighted/module 1.0.0-alpha.9 → 1.0.0-beta.0

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.
Files changed (55) hide show
  1. package/README.md +28 -16
  2. package/dist/assignmentExpression.d.ts +12 -0
  3. package/dist/cjs/assignmentExpression.d.cts +12 -0
  4. package/dist/cjs/expressionStatement.d.cts +2 -3
  5. package/dist/cjs/format.cjs +96 -22
  6. package/dist/cjs/format.d.cts +3 -4
  7. package/dist/cjs/formatters/assignmentExpression.cjs +37 -0
  8. package/dist/cjs/formatters/expressionStatement.cjs +36 -55
  9. package/dist/cjs/formatters/identifier.cjs +31 -31
  10. package/dist/cjs/formatters/memberExpression.cjs +24 -11
  11. package/dist/cjs/formatters/metaProperty.cjs +23 -35
  12. package/dist/cjs/helpers/identifier.cjs +132 -0
  13. package/dist/cjs/helpers/scope.cjs +12 -0
  14. package/dist/cjs/identifier.d.cts +31 -5
  15. package/dist/cjs/memberExpression.d.cts +2 -3
  16. package/dist/cjs/metaProperty.d.cts +2 -3
  17. package/dist/cjs/module.cjs +24 -25
  18. package/dist/cjs/parse.cjs +3 -14
  19. package/dist/cjs/parse.d.cts +1 -1
  20. package/dist/cjs/scope.d.cts +6 -0
  21. package/dist/cjs/types.d.cts +32 -4
  22. package/dist/cjs/utils.cjs +218 -0
  23. package/dist/cjs/utils.d.cts +25 -0
  24. package/dist/cjs/walk.cjs +75 -0
  25. package/dist/cjs/walk.d.cts +20 -0
  26. package/dist/expressionStatement.d.ts +2 -3
  27. package/dist/format.d.ts +3 -4
  28. package/dist/format.js +98 -23
  29. package/dist/formatters/assignmentExpression.d.ts +12 -0
  30. package/dist/formatters/assignmentExpression.js +30 -0
  31. package/dist/formatters/expressionStatement.d.ts +2 -3
  32. package/dist/formatters/expressionStatement.js +36 -55
  33. package/dist/formatters/identifier.d.ts +11 -4
  34. package/dist/formatters/identifier.js +31 -31
  35. package/dist/formatters/memberExpression.d.ts +2 -3
  36. package/dist/formatters/memberExpression.js +24 -11
  37. package/dist/formatters/metaProperty.d.ts +2 -3
  38. package/dist/formatters/metaProperty.js +23 -35
  39. package/dist/helpers/identifier.d.ts +31 -0
  40. package/dist/helpers/identifier.js +127 -0
  41. package/dist/helpers/scope.d.ts +6 -0
  42. package/dist/helpers/scope.js +7 -0
  43. package/dist/identifier.d.ts +31 -5
  44. package/dist/memberExpression.d.ts +2 -3
  45. package/dist/metaProperty.d.ts +2 -3
  46. package/dist/module.js +24 -25
  47. package/dist/parse.d.ts +1 -1
  48. package/dist/parse.js +3 -14
  49. package/dist/scope.d.ts +6 -0
  50. package/dist/types.d.ts +32 -4
  51. package/dist/utils.d.ts +25 -0
  52. package/dist/utils.js +209 -0
  53. package/dist/walk.d.ts +20 -0
  54. package/dist/walk.js +69 -0
  55. package/package.json +43 -25
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.isIdentifierName = exports.identifier = void 0;
7
+ var _periscopic = require("periscopic");
8
+ /**
9
+ * Focus exclusively on IdentifierName type as it has the name property,
10
+ * which is what the identifer utilities are interested in.
11
+ *
12
+ * Explicitly ignore the TSThisParameter type as it is not a valid identifier name.
13
+ */
14
+ const isIdentifierName = node => {
15
+ return node.type === 'Identifier' && typeof node.name === 'string' && node.name !== 'this';
16
+ };
17
+ exports.isIdentifierName = isIdentifierName;
18
+ const scopeCache = new WeakMap();
19
+ const getScopeContext = program => {
20
+ const cached = scopeCache.get(program);
21
+ if (cached) {
22
+ return cached;
23
+ }
24
+ const {
25
+ scope
26
+ } = (0, _periscopic.analyze)(program);
27
+ const context = {
28
+ scope
29
+ };
30
+ scopeCache.set(program, context);
31
+ return context;
32
+ };
33
+
34
+ /**
35
+ * All methods receive the full set of ancestors, which
36
+ * specifically includes the node itself as the last element.
37
+ * The second to last element is the parent node, and so on.
38
+ * The first element is the root node.
39
+ */
40
+ const identifier = exports.identifier = {
41
+ isNamed: node => {
42
+ return isIdentifierName(node);
43
+ },
44
+ isMetaProperty(ancestors) {
45
+ const parent = ancestors[ancestors.length - 2];
46
+ return parent.type === 'MetaProperty' || parent.type === 'MemberExpression' && parent.object.type === 'MetaProperty';
47
+ },
48
+ isModuleScope(ancestors, includeImports = false) {
49
+ const node = ancestors[ancestors.length - 1];
50
+ const parent = ancestors[ancestors.length - 2];
51
+ const program = ancestors[0];
52
+ if (!identifier.isNamed(node) || identifier.isMetaProperty(ancestors) || parent.type === 'LabeledStatement' || parent.type === 'BreakStatement' || parent.type === 'ContinueStatement') {
53
+ return false;
54
+ }
55
+ if (parent.type === 'ImportSpecifier' || parent.type === 'ImportDefaultSpecifier' || parent.type === 'ImportNamespaceSpecifier') {
56
+ return includeImports && parent.local.name === node.name;
57
+ }
58
+ if (parent.type === 'Property' && parent.key === node && !parent.computed) {
59
+ return false;
60
+ }
61
+ if (parent.type === 'MemberExpression' && parent.property === node && !parent.computed) {
62
+ return false;
63
+ }
64
+ const {
65
+ scope: rootScope
66
+ } = getScopeContext(program);
67
+ const owner = rootScope.find_owner(node.name);
68
+ if (!owner) {
69
+ return node.name === 'exports';
70
+ }
71
+ return owner === rootScope;
72
+ },
73
+ isMemberExpressionRoot(ancestors) {
74
+ const node = ancestors[ancestors.length - 1];
75
+ const parent = ancestors[ancestors.length - 2];
76
+ const grandParent = ancestors[ancestors.length - 3];
77
+ return parent.type === 'MemberExpression' && parent.object === node && grandParent.type !== 'MemberExpression';
78
+ },
79
+ isDeclaration(ancestors) {
80
+ const node = ancestors[ancestors.length - 1];
81
+ const parent = ancestors[ancestors.length - 2];
82
+ return (parent.type === 'VariableDeclarator' || parent.type === 'FunctionDeclaration' || parent.type === 'ClassDeclaration') && parent.id === node;
83
+ },
84
+ isClassOrFuncDeclarationId(ancestors) {
85
+ const node = ancestors[ancestors.length - 1];
86
+ const parent = ancestors[ancestors.length - 2];
87
+ return (parent.type === 'ClassDeclaration' || parent.type === 'FunctionDeclaration') && parent.id === node && ancestors.length <= 3;
88
+ },
89
+ isVarDeclarationInGlobalScope(ancestors) {
90
+ const node = ancestors[ancestors.length - 1];
91
+ const parent = ancestors[ancestors.length - 2];
92
+ const grandParent = ancestors[ancestors.length - 3];
93
+ const varBoundScopes = ['ClassDeclaration', 'ClassExpression', 'FunctionDeclaration', 'FunctionExpression', 'ArrowFunctionExpression'];
94
+ return parent.type === 'VariableDeclarator' && parent.id === node && grandParent.type === 'VariableDeclaration' && grandParent.kind === 'var' && ancestors.every(ancestor => {
95
+ return !varBoundScopes.includes(ancestor.type);
96
+ });
97
+ },
98
+ isIife(ancestors) {
99
+ const parent = ancestors[ancestors.length - 2];
100
+ return parent.type === 'FunctionExpression' && ancestors.some(ancestor => ancestor.type === 'ParenthesizedExpression');
101
+ },
102
+ isFunctionExpressionId(ancestors) {
103
+ const node = ancestors[ancestors.length - 1];
104
+ const parent = ancestors[ancestors.length - 2];
105
+ return parent.type === 'FunctionExpression' && parent.id === node;
106
+ },
107
+ isExportSpecifierAlias(ancestors) {
108
+ const node = ancestors[ancestors.length - 1];
109
+ const parent = ancestors[ancestors.length - 2];
110
+ return parent.type === 'ExportSpecifier' && parent.exported === node;
111
+ },
112
+ isClassPropertyKey(ancestors) {
113
+ const node = ancestors[ancestors.length - 1];
114
+ const parent = ancestors[ancestors.length - 2];
115
+ return parent.type === 'PropertyDefinition' && parent.key === node;
116
+ },
117
+ isMethodDefinitionKey(ancestors) {
118
+ const node = ancestors[ancestors.length - 1];
119
+ const parent = ancestors[ancestors.length - 2];
120
+ return parent.type === 'MethodDefinition' && parent.key === node;
121
+ },
122
+ isMemberKey(ancestors) {
123
+ const node = ancestors[ancestors.length - 1];
124
+ const parent = ancestors[ancestors.length - 2];
125
+ return parent.type === 'MemberExpression' && parent.property === node;
126
+ },
127
+ isPropertyKey(ancestors) {
128
+ const node = ancestors[ancestors.length - 1];
129
+ const parent = ancestors[ancestors.length - 2];
130
+ return parent.type === 'Property' && parent.key === node;
131
+ }
132
+ };
@@ -0,0 +1,12 @@
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,5 +1,31 @@
1
- import MagicString from 'magic-string';
2
- import type { NodePath } from '@babel/traverse';
3
- import type { Identifier } from '@babel/types';
4
- import type { FormatterOptions } from '../types.cjs';
5
- export declare const identifier: (nodePath: NodePath<Identifier>, src: MagicString, options: FormatterOptions) => void;
1
+ import type { Node, IdentifierName } from 'oxc-parser';
2
+ /**
3
+ * Focus exclusively on IdentifierName type as it has the name property,
4
+ * which is what the identifer utilities are interested in.
5
+ *
6
+ * Explicitly ignore the TSThisParameter type as it is not a valid identifier name.
7
+ */
8
+ declare const isIdentifierName: (node: Node) => node is IdentifierName;
9
+ /**
10
+ * All methods receive the full set of ancestors, which
11
+ * specifically includes the node itself as the last element.
12
+ * The second to last element is the parent node, and so on.
13
+ * The first element is the root node.
14
+ */
15
+ declare const identifier: {
16
+ isNamed: (node: Node) => node is IdentifierName;
17
+ isMetaProperty(ancestors: Node[]): boolean;
18
+ isModuleScope(ancestors: Node[], includeImports?: boolean): boolean;
19
+ isMemberExpressionRoot(ancestors: Node[]): boolean;
20
+ isDeclaration(ancestors: Node[]): boolean;
21
+ isClassOrFuncDeclarationId(ancestors: Node[]): boolean;
22
+ isVarDeclarationInGlobalScope(ancestors: Node[]): boolean;
23
+ isIife(ancestors: Node[]): boolean;
24
+ isFunctionExpressionId(ancestors: Node[]): boolean;
25
+ isExportSpecifierAlias(ancestors: Node[]): boolean;
26
+ isClassPropertyKey(ancestors: Node[]): boolean;
27
+ isMethodDefinitionKey(ancestors: Node[]): boolean;
28
+ isMemberKey(ancestors: Node[]): boolean;
29
+ isPropertyKey(ancestors: Node[]): boolean;
30
+ };
31
+ export { identifier, isIdentifierName };
@@ -1,5 +1,4 @@
1
1
  import MagicString from 'magic-string';
2
- import type { NodePath } from '@babel/traverse';
3
- import type { MemberExpression } from '@babel/types';
2
+ import type { MemberExpression, Node } from 'oxc-parser';
4
3
  import type { FormatterOptions } from '../types.cjs';
5
- export declare const memberExpression: (nodePath: NodePath<MemberExpression>, src: MagicString, options: FormatterOptions) => void;
4
+ export declare const memberExpression: (node: MemberExpression, parent: Node | null, src: MagicString, options: FormatterOptions) => void;
@@ -1,5 +1,4 @@
1
1
  import MagicString from 'magic-string';
2
- import type { NodePath } from '@babel/traverse';
3
- import type { MetaProperty } from '@babel/types';
2
+ import type { Node, MetaProperty } from 'oxc-parser';
4
3
  import type { FormatterOptions } from '../types.cjs';
5
- export declare const metaProperty: (nodePath: NodePath<MetaProperty>, src: MagicString, options: FormatterOptions) => void;
4
+ export declare const metaProperty: (node: MetaProperty, parent: Node | null, src: MagicString, options: FormatterOptions) => void;
@@ -9,26 +9,20 @@ var _promises = require("node:fs/promises");
9
9
  var _specifier = require("@knighted/specifier");
10
10
  var _parse = require("./parse.cjs");
11
11
  var _format = require("./format.cjs");
12
+ var _utils = require("./utils.cjs");
12
13
  const defaultOptions = {
13
- type: 'commonjs',
14
+ target: 'commonjs',
15
+ sourceType: 'auto',
16
+ transformSyntax: true,
17
+ liveBindings: 'strict',
18
+ rewriteSpecifier: undefined,
19
+ dirFilename: 'inject',
20
+ importMeta: 'shim',
21
+ requireSource: 'builtin',
22
+ cjsDefault: 'auto',
23
+ topLevelAwait: 'error',
14
24
  out: undefined,
15
- modules: false,
16
- specifier: undefined
17
- };
18
- const getLangFromExt = filename => {
19
- const ext = (0, _nodePath.extname)(filename);
20
- if (/\.js$/.test(ext)) {
21
- return 'js';
22
- }
23
- if (/\.ts$/.test(ext)) {
24
- return 'ts';
25
- }
26
- if (ext === '.tsx') {
27
- return 'tsx';
28
- }
29
- if (ext === '.jsx') {
30
- return 'jsx';
31
- }
25
+ inPlace: false
32
26
  };
33
27
  const transform = async (filename, options = defaultOptions) => {
34
28
  const opts = {
@@ -37,24 +31,29 @@ const transform = async (filename, options = defaultOptions) => {
37
31
  };
38
32
  const file = (0, _nodePath.resolve)(filename);
39
33
  const code = (await (0, _promises.readFile)(file)).toString();
40
- const ast = (0, _parse.parse)(code);
41
- let source = (0, _format.format)(code, ast, opts).toString();
42
- if (options.specifier) {
43
- const code = await _specifier.specifier.updateSrc(source, getLangFromExt(filename), ({
34
+ const ast = (0, _parse.parse)(filename, code);
35
+ let source = await (0, _format.format)(code, ast, opts);
36
+ if (opts.rewriteSpecifier) {
37
+ const code = await _specifier.specifier.updateSrc(source, (0, _utils.getLangFromExt)(filename), ({
44
38
  value
45
39
  }) => {
40
+ if (typeof opts.rewriteSpecifier === 'function') {
41
+ return opts.rewriteSpecifier(value) ?? undefined;
42
+ }
43
+
46
44
  // Collapse any BinaryExpression or NewExpression to test for a relative specifier
47
45
  const collapsed = value.replace(/['"`+)\s]|new String\(/g, '');
48
46
  const relative = /^(?:\.|\.\.)\//;
49
47
  if (relative.test(collapsed)) {
50
48
  // $2 is for any closing quotation/parens around BE or NE
51
- return value.replace(/(.+)\.(?:m|c)?(?:j|t)s([)'"`]*)?$/, `$1${options.specifier}$2`);
49
+ return value.replace(/(.+)\.(?:m|c)?(?:j|t)s([)'"]*)?$/, `$1${opts.rewriteSpecifier}$2`);
52
50
  }
53
51
  });
54
52
  source = code;
55
53
  }
56
- if (opts.out) {
57
- await (0, _promises.writeFile)((0, _nodePath.resolve)(opts.out), source);
54
+ const outputPath = opts.inPlace ? file : opts.out ? (0, _nodePath.resolve)(opts.out) : undefined;
55
+ if (outputPath) {
56
+ await (0, _promises.writeFile)(outputPath, source);
58
57
  }
59
58
  return source;
60
59
  };
@@ -4,19 +4,8 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.parse = void 0;
7
- var _parser = require("@babel/parser");
8
- const parse = (source, dts = false) => {
9
- const ast = (0, _parser.parse)(source, {
10
- sourceType: 'module',
11
- allowAwaitOutsideFunction: true,
12
- allowReturnOutsideFunction: true,
13
- allowImportExportEverywhere: true,
14
- plugins: ['jsx', ['importAttributes', {
15
- deprecatedAssertSyntax: true
16
- }], ['typescript', {
17
- dts
18
- }]]
19
- });
20
- return ast;
7
+ var _oxcParser = require("oxc-parser");
8
+ const parse = (filename, code) => {
9
+ return (0, _oxcParser.parseSync)(filename, code);
21
10
  };
22
11
  exports.parse = parse;
@@ -1,2 +1,2 @@
1
- declare const parse: (source: string, dts?: boolean) => import("@babel/parser").ParseResult<import("@babel/types").File>;
1
+ declare const parse: (filename: string, code: string) => import("oxc-parser").ParseResult;
2
2
  export { parse };
@@ -0,0 +1,6 @@
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 +1,35 @@
1
+ import type { Node, Span, IdentifierName, IdentifierReference, BindingIdentifier, LabelIdentifier, TSIndexSignatureName } from 'oxc-parser';
2
+ export type RewriteSpecifier = '.js' | '.mjs' | '.cjs' | '.ts' | '.mts' | '.cts' | ((value: string) => string | null | undefined);
1
3
  export type ModuleOptions = {
2
- type?: 'module' | 'commonjs';
3
- modules?: boolean;
4
- specifier?: '.js' | '.mjs' | '.cjs' | '.ts' | '.mts' | '.cts';
4
+ target: 'module' | 'commonjs';
5
+ sourceType?: 'auto' | 'module' | 'commonjs';
6
+ transformSyntax?: boolean;
7
+ liveBindings?: 'strict' | 'loose' | 'off';
8
+ rewriteSpecifier?: RewriteSpecifier;
9
+ dirFilename?: 'inject' | 'preserve' | 'error';
10
+ importMeta?: 'preserve' | 'shim' | 'error';
11
+ requireSource?: 'builtin' | 'create-require';
12
+ cjsDefault?: 'module-exports' | 'auto' | 'none';
13
+ topLevelAwait?: 'error' | 'wrap' | 'preserve';
5
14
  out?: string;
15
+ inPlace?: boolean;
6
16
  };
7
- export type FormatterOptions = Omit<ModuleOptions, 'out'> & Required<Pick<ModuleOptions, 'type'>>;
17
+ export type SpannedNode = Node & Span;
18
+ export type ExportsMeta = {
19
+ hasExportsBeenReassigned: boolean;
20
+ hasDefaultExportBeenReassigned: boolean;
21
+ hasDefaultExportBeenAssigned: boolean;
22
+ defaultExportValue: unknown;
23
+ };
24
+ export type IdentMeta = {
25
+ declare: SpannedNode[];
26
+ read: SpannedNode[];
27
+ };
28
+ export type Scope = {
29
+ type: string;
30
+ name: string;
31
+ node: Node;
32
+ idents: Set<string>;
33
+ };
34
+ export type FormatterOptions = Omit<ModuleOptions, 'out' | 'inPlace'>;
35
+ export type Identifier = IdentifierName | IdentifierReference | BindingIdentifier | LabelIdentifier | TSIndexSignatureName;
@@ -0,0 +1,218 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.requireMainRgx = exports.isValidUrl = exports.getLangFromExt = exports.exportsRename = exports.collectScopeIdentifiers = exports.collectModuleIdentifiers = void 0;
7
+ var _nodePath = require("node:path");
8
+ var _walk = require("./walk.cjs");
9
+ var _scope = require("./helpers/scope.cjs");
10
+ var _identifier = require("./helpers/identifier.cjs");
11
+ const getLangFromExt = filename => {
12
+ const ext = (0, _nodePath.extname)(filename);
13
+ if (ext.endsWith('.js')) {
14
+ return 'js';
15
+ }
16
+ if (ext.endsWith('.ts')) {
17
+ return 'ts';
18
+ }
19
+ if (ext === '.tsx') {
20
+ return 'tsx';
21
+ }
22
+ if (ext === '.jsx') {
23
+ return 'jsx';
24
+ }
25
+ };
26
+ exports.getLangFromExt = getLangFromExt;
27
+ const isValidUrl = url => {
28
+ try {
29
+ new URL(url);
30
+ return true;
31
+ } catch {
32
+ return false;
33
+ }
34
+ };
35
+ exports.isValidUrl = isValidUrl;
36
+ const exportsRename = exports.exportsRename = '__exports';
37
+ const requireMainRgx = exports.requireMainRgx = /(require\.main\s*===\s*module|module\s*===\s*require\.main)/g;
38
+ const collectScopeIdentifiers = (node, scopes) => {
39
+ const {
40
+ type
41
+ } = node;
42
+ switch (type) {
43
+ case 'BlockStatement':
44
+ case 'ClassBody':
45
+ scopes.push({
46
+ node,
47
+ type: 'Block',
48
+ name: type,
49
+ idents: new Set()
50
+ });
51
+ break;
52
+ case 'FunctionDeclaration':
53
+ case 'FunctionExpression':
54
+ case 'ArrowFunctionExpression':
55
+ {
56
+ const name = node.id ? node.id.name : 'anonymous';
57
+ const scope = {
58
+ node,
59
+ name,
60
+ type: 'Function',
61
+ idents: new Set()
62
+ };
63
+ node.params.map(param => {
64
+ if (param.type === 'TSParameterProperty') {
65
+ return param.parameter;
66
+ }
67
+ if (param.type === 'RestElement') {
68
+ return param.argument;
69
+ }
70
+ if (param.type === 'AssignmentPattern') {
71
+ return param.left;
72
+ }
73
+ return param;
74
+ }).filter(_identifier.identifier.isNamed).forEach(param => {
75
+ scope.idents.add(param.name);
76
+ });
77
+
78
+ /**
79
+ * If a FunctionExpression has an id, it is a named function expression.
80
+ * The function expression name shadows the module scope identifier, so
81
+ * we don't want to count reads of module identifers that have the same name.
82
+ * They also do not cause a SyntaxError if the function expression name is
83
+ * the same as a module scope identifier.
84
+ *
85
+ * TODO: Is this necessary for FunctionDeclaration?
86
+ */
87
+ if (node.type === 'FunctionExpression' && node.id) {
88
+ scope.idents.add(node.id.name);
89
+ }
90
+
91
+ // First add the function to any previous scopes
92
+ if (scopes.length > 0) {
93
+ scopes[scopes.length - 1].idents.add(name);
94
+ }
95
+
96
+ // Then add the function scope to the scopes stack
97
+ scopes.push(scope);
98
+ }
99
+ break;
100
+ case 'ClassDeclaration':
101
+ {
102
+ const className = node.id ? node.id.name : 'anonymous';
103
+
104
+ // First add the class to any previous scopes
105
+ if (scopes.length > 0) {
106
+ scopes[scopes.length - 1].idents.add(className);
107
+ }
108
+
109
+ // Then add the class to the scopes stack
110
+ scopes.push({
111
+ node,
112
+ name: className,
113
+ type: 'Class',
114
+ idents: new Set()
115
+ });
116
+ }
117
+ break;
118
+ case 'ClassExpression':
119
+ {}
120
+ break;
121
+ case 'VariableDeclaration':
122
+ if (scopes.length > 0) {
123
+ const scope = scopes[scopes.length - 1];
124
+ node.declarations.forEach(decl => {
125
+ if (decl.type === 'VariableDeclarator' && decl.id.type === 'Identifier') {
126
+ scope.idents.add(decl.id.name);
127
+ }
128
+ });
129
+ }
130
+ break;
131
+ }
132
+ };
133
+
134
+ /**
135
+ * Collects all module scope identifiers in the AST.
136
+ *
137
+ * Ignores identifiers that are in functions or classes.
138
+ * Ignores new scopes for StaticBlock nodes (can only reference static class members).
139
+ *
140
+ * Special case handling for these which create their own scopes,
141
+ * but are also valid module scope identifiers:
142
+ * - ClassDeclaration
143
+ * - FunctionDeclaration
144
+ *
145
+ * Special case handling for var inside BlockStatement
146
+ * which are also valid module scope identifiers.
147
+ */
148
+ exports.collectScopeIdentifiers = collectScopeIdentifiers;
149
+ const collectModuleIdentifiers = async (ast, hoisting = true) => {
150
+ const identifiers = new Map();
151
+ const globalReads = new Map();
152
+ const scopes = [];
153
+ await (0, _walk.ancestorWalk)(ast, {
154
+ enter(node, ancestors) {
155
+ const {
156
+ type
157
+ } = node;
158
+ collectScopeIdentifiers(node, scopes);
159
+
160
+ // Add module scope identifiers to the registry map
161
+
162
+ if (type === 'Identifier') {
163
+ const {
164
+ name
165
+ } = node;
166
+ const meta = identifiers.get(name) ?? {
167
+ declare: [],
168
+ read: []
169
+ };
170
+ const isDeclaration = _identifier.identifier.isDeclaration(ancestors);
171
+ const inScope = scopes.some(scope => scope.idents.has(name) || scope.name === name);
172
+ if (hoisting && !_identifier.identifier.isDeclaration(ancestors) && !_identifier.identifier.isFunctionExpressionId(ancestors) && !_identifier.identifier.isExportSpecifierAlias(ancestors) && !_identifier.identifier.isClassPropertyKey(ancestors) && !_identifier.identifier.isMethodDefinitionKey(ancestors) && !_identifier.identifier.isMemberKey(ancestors) && !_identifier.identifier.isPropertyKey(ancestors) && !_identifier.identifier.isIife(ancestors) && !inScope) {
173
+ if (globalReads.has(name)) {
174
+ globalReads.get(name)?.push(node);
175
+ } else {
176
+ globalReads.set(name, [node]);
177
+ }
178
+ }
179
+ if (isDeclaration) {
180
+ const isModuleScope = _identifier.identifier.isModuleScope(ancestors);
181
+ const isClassOrFuncDeclaration = _identifier.identifier.isClassOrFuncDeclarationId(ancestors);
182
+ const isVarDeclarationInGlobalScope = _identifier.identifier.isVarDeclarationInGlobalScope(ancestors);
183
+ if (isModuleScope || isClassOrFuncDeclaration || isVarDeclarationInGlobalScope) {
184
+ meta.declare.push(node);
185
+
186
+ // Check for hoisted reads
187
+ if (hoisting && globalReads.has(name)) {
188
+ const reads = globalReads.get(name);
189
+ if (reads) {
190
+ reads.forEach(read => {
191
+ if (!meta.read.includes(read)) {
192
+ meta.read.push(read);
193
+ }
194
+ });
195
+ }
196
+ }
197
+ identifiers.set(name, meta);
198
+ }
199
+ } else {
200
+ if (identifiers.has(name) && !inScope && !_identifier.identifier.isIife(ancestors) && !_identifier.identifier.isFunctionExpressionId(ancestors) && !_identifier.identifier.isExportSpecifierAlias(ancestors) && !_identifier.identifier.isClassPropertyKey(ancestors) && !_identifier.identifier.isMethodDefinitionKey(ancestors) && !_identifier.identifier.isMemberKey(ancestors) && !_identifier.identifier.isPropertyKey(ancestors)) {
201
+ // Closure is referencing module scope identifier
202
+ meta.read.push(node);
203
+ }
204
+ }
205
+ }
206
+ },
207
+ leave(node) {
208
+ const {
209
+ type
210
+ } = node;
211
+ if (_scope.scopes.includes(type)) {
212
+ scopes.pop();
213
+ }
214
+ }
215
+ });
216
+ return identifiers;
217
+ };
218
+ exports.collectModuleIdentifiers = collectModuleIdentifiers;
@@ -0,0 +1,25 @@
1
+ import type { Node } from 'oxc-parser';
2
+ import type { Specifier } from '@knighted/specifier';
3
+ import type { IdentMeta, Scope } from './types.cjs';
4
+ type UpdateSrcLang = Parameters<Specifier['updateSrc']>[1];
5
+ declare const getLangFromExt: (filename: string) => UpdateSrcLang;
6
+ declare const isValidUrl: (url: string) => boolean;
7
+ declare const exportsRename = "__exports";
8
+ declare const requireMainRgx: RegExp;
9
+ declare const collectScopeIdentifiers: (node: Node, scopes: Scope[]) => void;
10
+ /**
11
+ * Collects all module scope identifiers in the AST.
12
+ *
13
+ * Ignores identifiers that are in functions or classes.
14
+ * Ignores new scopes for StaticBlock nodes (can only reference static class members).
15
+ *
16
+ * Special case handling for these which create their own scopes,
17
+ * but are also valid module scope identifiers:
18
+ * - ClassDeclaration
19
+ * - FunctionDeclaration
20
+ *
21
+ * Special case handling for var inside BlockStatement
22
+ * which are also valid module scope identifiers.
23
+ */
24
+ declare const collectModuleIdentifiers: (ast: Node, hoisting?: boolean) => Promise<Map<string, IdentMeta>>;
25
+ export { getLangFromExt, isValidUrl, collectScopeIdentifiers, collectModuleIdentifiers, exportsRename, requireMainRgx, };
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.walk = exports.ancestorWalk = void 0;
7
+ var _oxcParser = require("oxc-parser");
8
+ /**
9
+ * Using visitorKeys instead of oxc Visitor to keep
10
+ * an ancestor-aware enter/leave API with this.skip()
11
+ * without per-node method boilerplate.
12
+ */
13
+
14
+ let skipDepth = -1;
15
+ const isNodeLike = value => {
16
+ return Boolean(value && typeof value === 'object' && 'type' in value);
17
+ };
18
+ const traverse = async (node, visitors, ancestors, depth) => {
19
+ if (!node) return;
20
+ const keys = _oxcParser.visitorKeys[node.type] ?? [];
21
+ const ctx = {
22
+ skip: () => {
23
+ skipDepth = depth;
24
+ }
25
+ };
26
+ ancestors.push(node);
27
+ if (visitors.enter) {
28
+ await visitors.enter.call(ctx, node, ancestors);
29
+ }
30
+ if (skipDepth === -1 || depth < skipDepth) {
31
+ const fields = node;
32
+ for (const key of keys) {
33
+ const child = fields[key];
34
+ if (Array.isArray(child)) {
35
+ for (const nested of child) {
36
+ if (isNodeLike(nested)) {
37
+ await traverse(nested, visitors, ancestors, depth + 1);
38
+ }
39
+ }
40
+ } else if (isNodeLike(child)) {
41
+ await traverse(child, visitors, ancestors, depth + 1);
42
+ }
43
+ }
44
+ }
45
+ if (visitors.leave) {
46
+ await visitors.leave.call(ctx, node, ancestors);
47
+ }
48
+ ancestors.pop();
49
+ if (skipDepth === depth) {
50
+ skipDepth = -1;
51
+ }
52
+ };
53
+ const ancestorWalk = async (node, visitors) => {
54
+ skipDepth = -1;
55
+ await traverse(node, visitors, [], 0);
56
+ };
57
+ exports.ancestorWalk = ancestorWalk;
58
+ const walk = async (node, visitors) => {
59
+ const wrap = {
60
+ enter(current, ancestors) {
61
+ const parent = ancestors[ancestors.length - 2] ?? null;
62
+ if (visitors.enter) {
63
+ return visitors.enter.call(this, current, parent);
64
+ }
65
+ },
66
+ leave(current, ancestors) {
67
+ const parent = ancestors[ancestors.length - 2] ?? null;
68
+ if (visitors.leave) {
69
+ return visitors.leave.call(this, current, parent);
70
+ }
71
+ }
72
+ };
73
+ await ancestorWalk(node, wrap);
74
+ };
75
+ exports.walk = walk;