@gtkx/cli 2.0.0-beta.1 → 2.0.0-beta.2

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 (165) hide show
  1. package/dist/builder.d.ts +1 -0
  2. package/dist/builder.d.ts.map +1 -1
  3. package/dist/builder.js +17 -5
  4. package/dist/builder.js.map +1 -1
  5. package/dist/cli.js +2 -0
  6. package/dist/cli.js.map +1 -1
  7. package/dist/codegen/run-codegen.d.ts +4 -2
  8. package/dist/codegen/run-codegen.d.ts.map +1 -1
  9. package/dist/codegen/run-codegen.js +13 -8
  10. package/dist/codegen/run-codegen.js.map +1 -1
  11. package/dist/codegen/store-resolver.d.ts +1 -1
  12. package/dist/codegen/store-resolver.d.ts.map +1 -1
  13. package/dist/codegen/store-resolver.js +2 -2
  14. package/dist/codegen/store-resolver.js.map +1 -1
  15. package/dist/commands/build.d.ts +5 -0
  16. package/dist/commands/build.d.ts.map +1 -1
  17. package/dist/commands/build.js +83 -10
  18. package/dist/commands/build.js.map +1 -1
  19. package/dist/commands/codegen.d.ts +1 -0
  20. package/dist/commands/codegen.d.ts.map +1 -1
  21. package/dist/commands/codegen.js +6 -5
  22. package/dist/commands/codegen.js.map +1 -1
  23. package/dist/commands/deploy.d.ts +1 -0
  24. package/dist/commands/deploy.d.ts.map +1 -1
  25. package/dist/commands/deploy.js +4 -2
  26. package/dist/commands/deploy.js.map +1 -1
  27. package/dist/commands/dev.d.ts +8 -0
  28. package/dist/commands/dev.d.ts.map +1 -1
  29. package/dist/commands/dev.js +19 -1
  30. package/dist/commands/dev.js.map +1 -1
  31. package/dist/deploy/freedesktop/metainfo.d.ts.map +1 -1
  32. package/dist/deploy/freedesktop/metainfo.js +41 -2
  33. package/dist/deploy/freedesktop/metainfo.js.map +1 -1
  34. package/dist/deploy/nfpm/config.d.ts.map +1 -1
  35. package/dist/deploy/nfpm/config.js +5 -2
  36. package/dist/deploy/nfpm/config.js.map +1 -1
  37. package/dist/deploy/nfpm/contents.d.ts.map +1 -1
  38. package/dist/deploy/nfpm/contents.js +8 -0
  39. package/dist/deploy/nfpm/contents.js.map +1 -1
  40. package/dist/deploy/node-runtime/elf.d.ts +2 -1
  41. package/dist/deploy/node-runtime/elf.d.ts.map +1 -1
  42. package/dist/deploy/node-runtime/elf.js +32 -5
  43. package/dist/deploy/node-runtime/elf.js.map +1 -1
  44. package/dist/deploy/node-runtime/index.d.ts +2 -1
  45. package/dist/deploy/node-runtime/index.d.ts.map +1 -1
  46. package/dist/deploy/node-runtime/index.js +73 -4
  47. package/dist/deploy/node-runtime/index.js.map +1 -1
  48. package/dist/deploy/notices/node-runtime.js +2 -2
  49. package/dist/deploy/notices/node-runtime.js.map +1 -1
  50. package/dist/deploy/payload/launcher.d.ts.map +1 -1
  51. package/dist/deploy/payload/launcher.js +14 -1
  52. package/dist/deploy/payload/launcher.js.map +1 -1
  53. package/dist/deploy/payload/stage.js +1 -1
  54. package/dist/deploy/payload/stage.js.map +1 -1
  55. package/dist/deploy/run-deploy.d.ts +1 -0
  56. package/dist/deploy/run-deploy.d.ts.map +1 -1
  57. package/dist/deploy/run-deploy.js +100 -14
  58. package/dist/deploy/run-deploy.js.map +1 -1
  59. package/dist/deploy/settings/build-manifest.d.ts +6 -1
  60. package/dist/deploy/settings/build-manifest.d.ts.map +1 -1
  61. package/dist/deploy/settings/build-manifest.js +15 -4
  62. package/dist/deploy/settings/build-manifest.js.map +1 -1
  63. package/dist/deploy/settings/extra-files.d.ts +1 -1
  64. package/dist/deploy/settings/extra-files.d.ts.map +1 -1
  65. package/dist/deploy/settings/extra-files.js +22 -1
  66. package/dist/deploy/settings/extra-files.js.map +1 -1
  67. package/dist/deploy/settings/index.d.ts +1 -0
  68. package/dist/deploy/settings/index.d.ts.map +1 -1
  69. package/dist/deploy/settings/index.js +2 -1
  70. package/dist/deploy/settings/index.js.map +1 -1
  71. package/dist/deploy/settings/paths.d.ts +2 -1
  72. package/dist/deploy/settings/paths.d.ts.map +1 -1
  73. package/dist/deploy/settings/paths.js +113 -5
  74. package/dist/deploy/settings/paths.js.map +1 -1
  75. package/dist/deploy/targets/flatpak-source.d.ts.map +1 -1
  76. package/dist/deploy/targets/flatpak-source.js +11 -2
  77. package/dist/deploy/targets/flatpak-source.js.map +1 -1
  78. package/dist/deploy/types.d.ts +1 -0
  79. package/dist/deploy/types.d.ts.map +1 -1
  80. package/dist/deploy/types.js.map +1 -1
  81. package/dist/dev/headless.d.ts +3 -0
  82. package/dist/dev/headless.d.ts.map +1 -0
  83. package/dist/dev/headless.js +35 -0
  84. package/dist/dev/headless.js.map +1 -0
  85. package/dist/dev/runner-deps.js +1 -1
  86. package/dist/dev/runner-deps.js.map +1 -1
  87. package/dist/i18n/source-messages.d.ts.map +1 -1
  88. package/dist/i18n/source-messages.js +835 -1
  89. package/dist/i18n/source-messages.js.map +1 -1
  90. package/dist/internal/build-manifest.d.ts +5 -2
  91. package/dist/internal/build-manifest.d.ts.map +1 -1
  92. package/dist/internal/build-manifest.js +15 -2
  93. package/dist/internal/build-manifest.js.map +1 -1
  94. package/dist/internal/build-output.d.ts +8 -0
  95. package/dist/internal/build-output.d.ts.map +1 -0
  96. package/dist/internal/build-output.js +69 -0
  97. package/dist/internal/build-output.js.map +1 -0
  98. package/dist/internal/entry-arg.d.ts +4 -1
  99. package/dist/internal/entry-arg.d.ts.map +1 -1
  100. package/dist/internal/entry-arg.js +7 -1
  101. package/dist/internal/entry-arg.js.map +1 -1
  102. package/dist/internal/output-directory.d.ts +15 -0
  103. package/dist/internal/output-directory.d.ts.map +1 -0
  104. package/dist/internal/output-directory.js +490 -0
  105. package/dist/internal/output-directory.js.map +1 -0
  106. package/dist/internal/prepare-project.d.ts +2 -0
  107. package/dist/internal/prepare-project.d.ts.map +1 -1
  108. package/dist/internal/prepare-project.js +3 -3
  109. package/dist/internal/prepare-project.js.map +1 -1
  110. package/dist/internal/source-imports.js +1 -1
  111. package/dist/internal/source-imports.js.map +1 -1
  112. package/dist/mcp/client.js +2 -2
  113. package/dist/mcp/client.js.map +1 -1
  114. package/dist/vite-plugins/build-manifest.d.ts +5 -1
  115. package/dist/vite-plugins/build-manifest.d.ts.map +1 -1
  116. package/dist/vite-plugins/build-manifest.js +2 -1
  117. package/dist/vite-plugins/build-manifest.js.map +1 -1
  118. package/dist/vite-plugins/i18n.d.ts.map +1 -1
  119. package/dist/vite-plugins/i18n.js +58 -6
  120. package/dist/vite-plugins/i18n.js.map +1 -1
  121. package/dist/vite-plugins/index.d.ts +8 -1
  122. package/dist/vite-plugins/index.d.ts.map +1 -1
  123. package/dist/vite-plugins/index.js +6 -2
  124. package/dist/vite-plugins/index.js.map +1 -1
  125. package/dist/vite-plugins/self-contained.d.ts.map +1 -1
  126. package/dist/vite-plugins/self-contained.js +159 -158
  127. package/dist/vite-plugins/self-contained.js.map +1 -1
  128. package/package.json +9 -9
  129. package/src/builder.ts +18 -5
  130. package/src/cli.ts +3 -0
  131. package/src/codegen/run-codegen.ts +15 -7
  132. package/src/codegen/store-resolver.ts +2 -2
  133. package/src/commands/build.ts +13 -2
  134. package/src/commands/codegen.ts +6 -5
  135. package/src/commands/deploy.ts +4 -2
  136. package/src/commands/dev.ts +20 -1
  137. package/src/deploy/freedesktop/metainfo.ts +73 -21
  138. package/src/deploy/nfpm/config.ts +5 -2
  139. package/src/deploy/nfpm/contents.ts +8 -0
  140. package/src/deploy/node-runtime/elf.ts +43 -5
  141. package/src/deploy/node-runtime/index.ts +105 -5
  142. package/src/deploy/notices/node-runtime.ts +2 -2
  143. package/src/deploy/payload/launcher.ts +20 -1
  144. package/src/deploy/payload/stage.ts +1 -1
  145. package/src/deploy/run-deploy.ts +56 -16
  146. package/src/deploy/settings/build-manifest.ts +28 -3
  147. package/src/deploy/settings/extra-files.ts +26 -2
  148. package/src/deploy/settings/index.ts +3 -1
  149. package/src/deploy/settings/paths.ts +70 -5
  150. package/src/deploy/targets/flatpak-source.ts +13 -2
  151. package/src/deploy/types.ts +1 -0
  152. package/src/dev/headless.ts +40 -0
  153. package/src/dev/runner-deps.ts +1 -1
  154. package/src/i18n/source-messages.ts +1220 -2
  155. package/src/internal/build-manifest.ts +25 -1
  156. package/src/internal/build-output.ts +99 -0
  157. package/src/internal/entry-arg.ts +8 -1
  158. package/src/internal/output-directory.ts +699 -0
  159. package/src/internal/prepare-project.ts +5 -5
  160. package/src/internal/source-imports.ts +1 -1
  161. package/src/mcp/client.ts +2 -2
  162. package/src/vite-plugins/build-manifest.ts +7 -1
  163. package/src/vite-plugins/i18n.ts +86 -5
  164. package/src/vite-plugins/index.ts +14 -7
  165. package/src/vite-plugins/self-contained.ts +184 -185
@@ -1,18 +1,13 @@
1
- import type { ESTree, Plugin, VisitorObject } from "vite";
1
+ import type { Plugin } from "vite";
2
+ import { type NodePath, parseSync, type Scope, traverse } from "@babel/core";
2
3
  import { isBuiltin } from "node:module";
3
4
  import { posix } from "node:path";
4
- import { parseSync, Visitor } from "vite";
5
5
 
6
- type Bindings = {
7
- declarations: Map<string, number>;
8
- factories: Set<string>;
9
- requires: Set<string>;
10
- urls: Set<string>;
11
- };
6
+ type BindingKind = "factory" | "module" | "require" | "url";
7
+
8
+ type ResolvedBinding = NonNullable<ReturnType<Scope["getBinding"]>>;
12
9
 
13
10
  type Scan = {
14
- bindings: Bindings;
15
- calls: ESTree.CallExpression[];
16
11
  sources: string[];
17
12
  };
18
13
 
@@ -21,199 +16,208 @@ type Chunk = {
21
16
  code: string;
22
17
  };
23
18
 
24
- type PatternItem =
25
- | ESTree.BindingPattern |
26
- ESTree.BindingProperty |
27
- ESTree.BindingRestElement |
28
- ESTree.FormalParameterRest |
29
- ESTree.TSParameterProperty |
30
- null;
31
-
32
19
  const REQUIRE_FACTORY = "createRequire";
33
20
  const REQUIRE_BINDING = "require";
34
21
  const RESOLVE_PROPERTY = "resolve";
35
- const MODULE_BUILTIN = "node:module";
22
+ const URL_PROPERTY = "url";
23
+ const MODULE_BUILTINS = new Set(["module", "node:module"]);
36
24
  const OUTPUT_DIRECTORY = ".";
37
25
  const RELATIVE_SPECIFIER = /^\.\.?(?:\/|$)/;
38
26
 
39
- const exportName = (name: ESTree.ModuleExportName): string | null => (name.type === "Identifier" ? name.name : null);
40
- const referenceName = (node: ESTree.Argument): string | null => (node.type === "Identifier" ? node.name : null);
41
-
42
- const countName = (declarations: Map<string, number>, name: string | null): void => {
43
- if (name !== null) {
44
- declarations.set(name, (declarations.get(name) ?? 0) + 1);
27
+ const importedName = (path: NodePath): string | null => {
28
+ if (!path.isImportSpecifier()) {
29
+ return null;
45
30
  }
31
+
32
+ const { imported } = path.node;
33
+
34
+ return imported.type === "Identifier" ? imported.name : imported.value;
46
35
  };
47
36
 
48
- const boundPattern = (item: PatternItem): ESTree.BindingPattern | null => {
49
- if (item === null) {
37
+ const importKind = (path: NodePath): BindingKind | null => {
38
+ const declaration = path.parentPath;
39
+
40
+ if (!declaration.isImportDeclaration() || !MODULE_BUILTINS.has(declaration.node.source.value)) {
50
41
  return null;
51
42
  }
52
43
 
53
- if (item.type === "Property") {
54
- return item.value;
44
+ if (importedName(path) === REQUIRE_FACTORY) {
45
+ return "factory";
55
46
  }
56
47
 
57
- if (item.type === "RestElement") {
58
- return item.argument;
48
+ return path.isImportNamespaceSpecifier() || path.isImportDefaultSpecifier() ? "module" : null;
49
+ };
50
+
51
+ const memberName = (path: NodePath): string | null => {
52
+ if (!path.isMemberExpression()) {
53
+ return null;
59
54
  }
60
55
 
61
- return item.type === "TSParameterProperty" ? item.parameter : item;
62
- };
56
+ const property = path.get("property");
63
57
 
64
- const countPatterns = (declarations: Map<string, number>, items: PatternItem[]): void => {
65
- for (const item of items) {
66
- countPattern(declarations, boundPattern(item));
58
+ if (path.node.computed) {
59
+ return property.isStringLiteral() ? property.node.value : null;
67
60
  }
61
+
62
+ return property.isIdentifier() ? property.node.name : null;
68
63
  };
69
64
 
70
- const countPattern = (declarations: Map<string, number>, pattern: ESTree.BindingPattern | null): void => {
71
- if (pattern === null) {
72
- return;
65
+ const isImportMeta = (path: NodePath): boolean =>
66
+ path.isMetaProperty() && path.node.meta.name === "import" && path.node.property.name === "meta";
67
+
68
+ const isDirectMetaUrl = (path: NodePath): boolean => {
69
+ if (!path.isMemberExpression() || memberName(path) !== URL_PROPERTY) {
70
+ return false;
73
71
  }
74
72
 
75
- if (pattern.type === "Identifier") {
76
- countName(declarations, pattern.name);
73
+ const object = path.get("object");
77
74
 
78
- return;
75
+ return isImportMeta(object);
76
+ };
77
+
78
+ const bindingKind = (binding: ResolvedBinding, seen: Set<ResolvedBinding>): BindingKind | null => {
79
+ if (seen.has(binding)) {
80
+ return null;
79
81
  }
80
82
 
81
- if (pattern.type === "AssignmentPattern") {
82
- countPattern(declarations, pattern.left);
83
+ const next = new Set(seen).add(binding);
84
+ const imported = importKind(binding.path);
83
85
 
84
- return;
86
+ if (imported !== null) {
87
+ return imported;
88
+ }
89
+
90
+ if (!binding.path.isVariableDeclarator()) {
91
+ return null;
85
92
  }
86
93
 
87
- countPatterns(declarations, pattern.type === "ObjectPattern" ? pattern.properties : pattern.elements);
94
+ const initializer = binding.path.get("init");
95
+
96
+ return initializer.node === null ? null : expressionKind(initializer, next);
88
97
  };
89
98
 
90
- const countFunction = (
91
- declarations: Map<string, number>,
92
- node: ESTree.ArrowFunctionExpression | ESTree.Function,
93
- ): void => {
94
- countPattern(declarations, node.id);
95
- countPatterns(declarations, node.params);
99
+ const nameKind = (scope: Scope, name: string, seen: Set<ResolvedBinding>): BindingKind | null => {
100
+ const binding = scope.getBinding(name);
101
+
102
+ if (binding !== undefined) {
103
+ return bindingKind(binding, seen);
104
+ }
105
+
106
+ if (name === REQUIRE_FACTORY) {
107
+ return "factory";
108
+ }
109
+
110
+ return name === REQUIRE_BINDING ? "require" : null;
96
111
  };
97
112
 
98
- const declarationVisitor = (declarations: Map<string, number>): VisitorObject => ({
99
- ArrowFunctionExpression: (node) => {
100
- countFunction(declarations, node);
101
- },
102
- CatchClause: (node) => {
103
- countPattern(declarations, node.param);
104
- },
105
- ClassDeclaration: (node) => {
106
- countPattern(declarations, node.id);
107
- },
108
- ClassExpression: (node) => {
109
- countPattern(declarations, node.id);
110
- },
111
- ExportSpecifier: (node) => {
112
- countName(declarations, exportName(node.local));
113
- },
114
- FunctionDeclaration: (node) => {
115
- countFunction(declarations, node);
116
- },
117
- FunctionExpression: (node) => {
118
- countFunction(declarations, node);
119
- },
120
- ImportDefaultSpecifier: (node) => {
121
- countPattern(declarations, node.local);
122
- },
123
- ImportNamespaceSpecifier: (node) => {
124
- countPattern(declarations, node.local);
125
- },
126
- ImportSpecifier: (node) => {
127
- countPattern(declarations, node.local);
128
- },
129
- VariableDeclarator: (node) => {
130
- countPattern(declarations, node.id);
131
- },
132
- });
133
-
134
- const isUnshadowed = (name: string, bindings: Bindings): boolean => (bindings.declarations.get(name) ?? 0) <= 1;
135
-
136
- const templateString = (node: ESTree.TemplateLiteral): string | null => {
137
- const [quasi] = node.quasis;
138
-
139
- return quasi === undefined || node.expressions.length > 0 ? null : quasi.value.cooked;
113
+ const isModuleExpression = (path: NodePath, seen: Set<ResolvedBinding>): boolean => {
114
+ if (path.isIdentifier()) {
115
+ return nameKind(path.scope, path.node.name, seen) === "module";
116
+ }
117
+
118
+ if (!path.isCallExpression()) {
119
+ return false;
120
+ }
121
+
122
+ const [source] = path.get("arguments");
123
+ const callee = path.get("callee");
124
+ const sourceName = source === undefined ? null : literalString(source);
125
+
126
+ return (
127
+ sourceName !== null &&
128
+ MODULE_BUILTINS.has(sourceName) &&
129
+ isRequireExpression(callee, seen)
130
+ );
140
131
  };
141
132
 
142
- const literalString = (node: ESTree.Argument | null): string | null => {
143
- if (node?.type === "TemplateLiteral") {
144
- return templateString(node);
133
+ const isFactoryExpression = (path: NodePath, seen: Set<ResolvedBinding>): boolean => {
134
+ if (path.isIdentifier()) {
135
+ return nameKind(path.scope, path.node.name, seen) === "factory";
145
136
  }
146
137
 
147
- return node?.type === "Literal" && typeof node.value === "string" ? node.value : null;
138
+ if (!path.isMemberExpression() || memberName(path) !== REQUIRE_FACTORY) {
139
+ return false;
140
+ }
141
+
142
+ return isModuleExpression(path.get("object"), seen);
148
143
  };
149
144
 
150
- const isMetaUrl = (node: ESTree.Argument, bindings: Bindings): boolean => {
151
- if (node.type === "MemberExpression") {
152
- return node.object.type === "MetaProperty";
145
+ const isRequireFactoryCall = (path: NodePath, seen: Set<ResolvedBinding>): boolean => {
146
+ if (!path.isCallExpression()) {
147
+ return false;
153
148
  }
154
149
 
155
- const name = referenceName(node);
150
+ const callee = path.get("callee");
156
151
 
157
- return name !== null && bindings.urls.has(name) && isUnshadowed(name, bindings);
152
+ return isFactoryExpression(callee, seen);
158
153
  };
159
154
 
160
- const hasMetaUrl = (node: ESTree.CallExpression, bindings: Bindings): boolean =>
161
- node.arguments.some((argument) => isMetaUrl(argument, bindings));
155
+ const expressionKind = (path: NodePath, seen: Set<ResolvedBinding>): BindingKind | null => {
156
+ if (isDirectMetaUrl(path)) {
157
+ return "url";
158
+ }
162
159
 
163
- const isRequireName = (name: string | null, bindings: Bindings): boolean =>
164
- name !== null && (name === REQUIRE_BINDING || bindings.requires.has(name)) && isUnshadowed(name, bindings);
160
+ if (path.isIdentifier()) {
161
+ return nameKind(path.scope, path.node.name, seen);
162
+ }
165
163
 
166
- const isFactoryName = (name: string | null, bindings: Bindings): boolean =>
167
- name !== null && (name === REQUIRE_FACTORY || bindings.factories.has(name)) && isUnshadowed(name, bindings);
164
+ if (isFactoryExpression(path, seen)) {
165
+ return "factory";
166
+ }
168
167
 
169
- const isRequireFactory = (node: ESTree.Argument, bindings: Bindings): boolean =>
170
- node.type === "CallExpression" &&
171
- (isFactoryName(referenceName(node.callee), bindings) || hasMetaUrl(node, bindings));
168
+ if (isModuleExpression(path, seen)) {
169
+ return "module";
170
+ }
172
171
 
173
- const isResolveTarget = (object: ESTree.Expression, bindings: Bindings): boolean =>
174
- object.type === "MetaProperty" ||
175
- isRequireName(referenceName(object), bindings) ||
176
- isRequireFactory(object, bindings);
172
+ return isRequireFactoryCall(path, seen) ? "require" : null;
173
+ };
177
174
 
178
- const isResolveMember = (callee: ESTree.Expression, bindings: Bindings): boolean =>
179
- callee.type === "MemberExpression" &&
180
- !callee.computed &&
181
- callee.property.type === "Identifier" &&
182
- callee.property.name === RESOLVE_PROPERTY &&
183
- isResolveTarget(callee.object, bindings);
175
+ const isMetaUrl = (path: NodePath): boolean =>
176
+ isDirectMetaUrl(path) ||
177
+ (path.isIdentifier() && nameKind(path.scope, path.node.name, new Set()) === "url");
184
178
 
185
- const isResolvingCall = (call: ESTree.CallExpression, bindings: Bindings): boolean =>
186
- hasMetaUrl(call, bindings) ||
187
- isRequireName(referenceName(call.callee), bindings) ||
188
- isRequireFactory(call.callee, bindings) ||
189
- isResolveMember(call.callee, bindings);
179
+ const isRequireExpression = (path: NodePath, seen: Set<ResolvedBinding> = new Set()): boolean =>
180
+ isRequireFactoryCall(path, seen) ||
181
+ (path.isIdentifier() && nameKind(path.scope, path.node.name, seen) === "require");
190
182
 
191
- const collectFactories = (node: ESTree.ImportDeclaration, bindings: Bindings): void => {
192
- if (node.source.value !== MODULE_BUILTIN) {
193
- return;
183
+ const hasMetaUrlArgument = (path: NodePath): boolean => {
184
+ if (!path.isCallExpression()) {
185
+ return false;
194
186
  }
195
187
 
196
- for (const specifier of node.specifiers) {
197
- if (specifier.type === "ImportSpecifier" && exportName(specifier.imported) === REQUIRE_FACTORY) {
198
- bindings.factories.add(specifier.local.name);
199
- }
200
- }
188
+ return path.get("arguments").some((argument) => isMetaUrl(argument));
201
189
  };
202
190
 
203
- const collectBinding = (node: ESTree.VariableDeclarator, bindings: Bindings): void => {
204
- const { id, init } = node;
191
+ const isResolveMember = (path: NodePath): boolean => {
192
+ if (!path.isMemberExpression() || memberName(path) !== RESOLVE_PROPERTY) {
193
+ return false;
194
+ }
205
195
 
206
- if (init === null || id.type !== "Identifier") {
207
- return;
196
+ const object = path.get("object");
197
+
198
+ return isImportMeta(object) || isRequireExpression(object);
199
+ };
200
+
201
+ const isResolvingCall = (path: NodePath): boolean => {
202
+ if (!path.isCallExpression()) {
203
+ return false;
208
204
  }
209
205
 
210
- if (isMetaUrl(init, bindings)) {
211
- bindings.urls.add(id.name);
206
+ const callee = path.get("callee");
207
+
208
+ return hasMetaUrlArgument(path) || isRequireExpression(callee) || isResolveMember(callee);
209
+ };
210
+
211
+ const literalString = (path: NodePath): string | null => {
212
+ if (path.isStringLiteral()) {
213
+ return path.node.value;
212
214
  }
213
215
 
214
- if (isRequireFactory(init, bindings)) {
215
- bindings.requires.add(id.name);
216
+ if (!path.isTemplateLiteral() || path.node.expressions.length > 0) {
217
+ return null;
216
218
  }
219
+
220
+ return path.node.quasis[0]?.value.cooked ?? null;
217
221
  };
218
222
 
219
223
  const collectSource = (scan: Scan, source: string | null): void => {
@@ -222,55 +226,51 @@ const collectSource = (scan: Scan, source: string | null): void => {
222
226
  }
223
227
  };
224
228
 
225
- const collectCall = (scan: Scan, node: ESTree.CallExpression): void => {
226
- if (node.arguments.some((argument) => literalString(argument) !== null)) {
227
- scan.calls.push(node);
229
+ const collectCallSources = (scan: Scan, path: NodePath): void => {
230
+ if (!isResolvingCall(path) || !path.isCallExpression()) {
231
+ return;
228
232
  }
229
- };
230
233
 
231
- const scanVisitor = (scan: Scan): VisitorObject => ({
232
- CallExpression: (node) => {
233
- collectCall(scan, node);
234
- },
235
- ExportAllDeclaration: (node) => {
236
- collectSource(scan, node.source.value);
237
- },
238
- ExportNamedDeclaration: (node) => {
239
- collectSource(scan, node.source?.value ?? null);
240
- },
241
- ImportDeclaration: (node) => {
242
- collectFactories(node, scan.bindings);
243
- collectSource(scan, node.source.value);
244
- },
245
- ImportExpression: (node) => {
246
- collectSource(scan, literalString(node.source));
247
- },
248
- VariableDeclarator: (node) => {
249
- collectBinding(node, scan.bindings);
250
- },
251
- });
234
+ for (const argument of path.get("arguments")) {
235
+ collectSource(scan, literalString(argument));
236
+ }
237
+ };
252
238
 
253
239
  const scanChunk = (chunk: Chunk): Scan => {
254
- const scan: Scan = {
255
- bindings: { declarations: new Map(), factories: new Set(), requires: new Set(), urls: new Set() },
256
- calls: [],
257
- sources: [],
258
- };
240
+ const scan: Scan = { sources: [] };
241
+ const ast = parseSync(chunk.code, {
242
+ ast: true,
243
+ babelrc: false,
244
+ configFile: false,
245
+ filename: chunk.fileName,
246
+ sourceType: "module",
247
+ });
248
+
249
+ if (ast === null) {
250
+ throw new Error(`Cannot inspect ${chunk.fileName}`);
251
+ }
259
252
 
260
- const { program } = parseSync(chunk.fileName, chunk.code);
261
- new Visitor(declarationVisitor(scan.bindings.declarations)).visit(program);
262
- new Visitor(scanVisitor(scan)).visit(program);
253
+ traverse(ast, {
254
+ CallExpression: (path) => {
255
+ collectCallSources(scan, path);
256
+ },
257
+ ExportAllDeclaration: (path) => {
258
+ collectSource(scan, path.node.source.value);
259
+ },
260
+ ExportNamedDeclaration: (path) => {
261
+ collectSource(scan, path.node.source?.value ?? null);
262
+ },
263
+ ImportDeclaration: (path) => {
264
+ collectSource(scan, path.node.source.value);
265
+ },
266
+ ImportExpression: (path) => {
267
+ collectSource(scan, literalString(path.get("source")));
268
+ },
269
+ });
263
270
 
264
271
  return scan;
265
272
  };
266
273
 
267
- const callSpecifiers = (scan: Scan): string[] =>
268
- scan.calls
269
- .filter((call) => isResolvingCall(call, scan.bindings))
270
- .flatMap((call) => call.arguments)
271
- .map((argument) => literalString(argument))
272
- .filter((specifier) => specifier !== null);
273
-
274
274
  const isReachable = (specifier: string, directory: string, emitted: Set<string>): boolean => {
275
275
  if (isBuiltin(specifier)) {
276
276
  return true;
@@ -286,10 +286,9 @@ const isReachable = (specifier: string, directory: string, emitted: Set<string>)
286
286
  };
287
287
 
288
288
  const unreachableSpecifiers = (chunk: Chunk, emitted: Set<string>): string[] => {
289
- const scan = scanChunk(chunk);
290
289
  const directory = posix.dirname(chunk.fileName);
291
290
 
292
- return new Set([...scan.sources, ...callSpecifiers(scan)])
291
+ return new Set(scanChunk(chunk).sources)
293
292
  .values()
294
293
  .filter((specifier) => !isReachable(specifier, directory, emitted))
295
294
  .toArray()