@sshkeda/task 0.0.0-stage → 1.0.1
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/LICENSE +21 -0
- package/README.md +131 -2
- package/package.json +41 -4
- package/src/index.ts +2 -0
- package/src/lib/errors/index.ts +15 -0
- package/src/lib/errors/internal/errors.ts +147 -0
- package/src/lib/kill-leftover-processes/index.ts +17 -0
- package/src/lib/kill-leftover-processes/internal/command-words.ts +16 -0
- package/src/lib/kill-leftover-processes/internal/find-process-ids.ts +36 -0
- package/src/lib/kill-leftover-processes/internal/kill-process-groups.ts +270 -0
- package/src/lib/kill-leftover-processes/internal/list-linux-processes-from-proc.ts +191 -0
- package/src/lib/kill-leftover-processes/internal/list-macos-processes-with-sysctl.ts +335 -0
- package/src/lib/kill-leftover-processes/internal/list-processes.ts +58 -0
- package/src/lib/kill-leftover-processes/internal/mark-run-processes.ts +367 -0
- package/src/lib/kill-leftover-processes/internal/process-cleanup-errors.ts +17 -0
- package/src/lib/kill-leftover-processes/internal/send-signal.ts +32 -0
- package/src/lib/kill-leftover-processes/internal/signal-run-processes.ts +48 -0
- package/src/lib/kill-leftover-processes/internal/started-process-groups.ts +107 -0
- package/src/lib/main-thread/index.ts +3 -0
- package/src/lib/main-thread/internal/listen-for-stop-requests.ts +102 -0
- package/src/lib/main-thread/internal/run-main-thread.ts +81 -0
- package/src/lib/main-thread/internal/run-root-task-fiber.ts +191 -0
- package/src/lib/main-thread/internal/stops-at-first-failure.ts +20 -0
- package/src/lib/main-thread/internal/trace-the-run.ts +107 -0
- package/src/lib/main-thread/lib/find-code-each-task-depends-on/index.ts +7 -0
- package/src/lib/main-thread/lib/find-code-each-task-depends-on/internal/follow-imports-from-entry-files.ts +306 -0
- package/src/lib/main-thread/lib/find-code-each-task-depends-on/internal/load-all-reporting-first-failure.ts +11 -0
- package/src/lib/main-thread/lib/find-code-each-task-depends-on/internal/make-module-graph.ts +48 -0
- package/src/lib/main-thread/lib/find-code-each-task-depends-on/internal/read-configured-loaders.ts +64 -0
- package/src/lib/main-thread/lib/find-code-each-task-depends-on/internal/resolve-module-specifiers.ts +60 -0
- package/src/lib/main-thread/lib/find-code-each-task-depends-on/internal/rewrite-worker-imports.ts +118 -0
- package/src/lib/main-thread/lib/find-code-each-task-depends-on/internal/worker-import-errors.ts +19 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/index.ts +7 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/internal/handle-worker-events.ts +348 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/internal/make-task-runs.ts +207 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/lib/run-requested-task-once/index.ts +1 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/lib/run-requested-task-once/internal/await-within-counted-limit.ts +29 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/lib/run-requested-task-once/internal/execute-task.ts +272 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/lib/run-requested-task-once/internal/make-task-setup-clock.ts +129 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/lib/run-requested-task-once/internal/run-requested-tasks.ts +135 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/shared/define-task-run-state.ts +288 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/shared/make-stopwatch.ts +39 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/shared/share-standard-input-with-root-task.ts +36 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/shared/stop-task.ts +166 -0
- package/src/lib/main-thread/lib/scan-module-source/index.ts +1 -0
- package/src/lib/main-thread/lib/scan-module-source/internal/find-lexical-bindings.ts +154 -0
- package/src/lib/main-thread/lib/scan-module-source/internal/find-task-declarations.ts +205 -0
- package/src/lib/main-thread/lib/scan-module-source/internal/parse-module-text.ts +34 -0
- package/src/lib/main-thread/lib/scan-module-source/internal/read-literal-string.ts +15 -0
- package/src/lib/main-thread/lib/scan-module-source/internal/read-module-loads.ts +316 -0
- package/src/lib/main-thread/lib/scan-module-source/internal/scan-module-text.ts +70 -0
- package/src/lib/main-thread/lib/scan-module-source/internal/unwrap-type-assertions.ts +10 -0
- package/src/lib/main-thread/lib/scan-module-source/internal/visit-non-reference-identifiers.ts +21 -0
- package/src/lib/main-thread/shared/fail-run.ts +36 -0
- package/src/lib/main-thread/shared/find-failure-reason.ts +43 -0
- package/src/lib/main-thread/shared/find-package-directories.ts +34 -0
- package/src/lib/main-thread/shared/find-tasks-reachable-from-root-task-file.ts +246 -0
- package/src/lib/main-thread/shared/list-directory-paths-up-to-root.ts +13 -0
- package/src/lib/main-thread/shared/main-thread-errors.ts +373 -0
- package/src/lib/main-thread/shared/package-json-errors.ts +93 -0
- package/src/lib/main-thread/shared/provide-discovered-tasks.ts +26 -0
- package/src/lib/main-thread/shared/provide-process-groups.ts +63 -0
- package/src/lib/main-thread/shared/provide-run-root-task-config.ts +66 -0
- package/src/lib/main-thread/shared/provide-stop-state.ts +150 -0
- package/src/lib/main-thread/shared/provide-worker-pool.ts +607 -0
- package/src/lib/main-thread/shared/read-bare-package-name.ts +3 -0
- package/src/lib/main-thread/shared/read-package-json.ts +149 -0
- package/src/lib/main-thread/shared/report-task-failure.ts +31 -0
- package/src/lib/preload-rewritten-imports-into-each-worker/index.ts +5 -0
- package/src/lib/preload-rewritten-imports-into-each-worker/internal/load-rewritten-imports.ts +34 -0
- package/src/lib/supervise-the-run/index.ts +15 -0
- package/src/lib/supervise-the-run/internal/adopt-orphans-on-linux.ts +73 -0
- package/src/lib/supervise-the-run/internal/check-root-task-file.ts +8 -0
- package/src/lib/supervise-the-run/internal/report-ready.ts +12 -0
- package/src/lib/supervise-the-run/internal/share-process-groups.ts +25 -0
- package/src/lib/supervise-the-run/internal/start-watching-the-supervisor.ts +6 -0
- package/src/lib/supervise-the-run/internal/supervise-run.ts +155 -0
- package/src/lib/supervise-the-run/internal/talk-to-the-supervisor.ts +53 -0
- package/src/lib/supervise-the-run/internal/watch-the-supervisor.ts +61 -0
- package/src/lib/task/index.ts +5 -0
- package/src/lib/task/internal/make-tasks.ts +82 -0
- package/src/lib/task/internal/run-command.ts +194 -0
- package/src/lib/task/internal/run-root-task.ts +263 -0
- package/src/lib/task/internal/run-task-lists.ts +51 -0
- package/src/lib/task/internal/trust-output-mode-names.ts +18 -0
- package/src/lib/task/internal/trust-task-results.ts +35 -0
- package/src/lib/task/internal/use-task-services.ts +4 -0
- package/src/lib/worker-thread/index.ts +3 -0
- package/src/lib/worker-thread/internal/answer-main-thread-messages.ts +141 -0
- package/src/lib/worker-thread/internal/describe-task-body-result.ts +91 -0
- package/src/lib/worker-thread/internal/name-config-key-not-set.ts +57 -0
- package/src/lib/worker-thread/internal/run-task-body.ts +117 -0
- package/src/lib/worker-thread/internal/serve-on-the-parent-port.ts +63 -0
- package/src/lib/worker-thread/internal/start-worker-thread.ts +41 -0
- package/src/lib/worker-thread/lib/provide-task-services/index.ts +1 -0
- package/src/lib/worker-thread/lib/provide-task-services/internal/make-task-logger.ts +34 -0
- package/src/lib/worker-thread/lib/provide-task-services/internal/provide-task-services.ts +86 -0
- package/src/lib/worker-thread/lib/provide-task-services/internal/read-standard-input-from-main-thread.ts +58 -0
- package/src/lib/worker-thread/lib/provide-task-services/internal/resolve-paths-from-package-directory.ts +61 -0
- package/src/lib/worker-thread/lib/provide-task-services/internal/run-commands-in-package-directory.ts +24 -0
- package/src/lib/worker-thread/lib/provide-task-services/internal/spawn-commands-with-bun.ts +545 -0
- package/src/lib/worker-thread/lib/provide-task-services/internal/spawn-reported-process.ts +151 -0
- package/src/lib/worker-thread/shared/await-main-thread-answers.ts +76 -0
- package/src/lib/worker-thread/shared/decode-task-result-json.ts +114 -0
- package/src/lib/worker-thread/shared/define-task-body-record.ts +11 -0
- package/src/lib/worker-thread/shared/describe-issue-input.ts +52 -0
- package/src/lib/worker-thread/shared/plain-object.ts +8 -0
- package/src/lib/worker-thread/shared/provide-worker-state.ts +160 -0
- package/src/lib/worker-thread/shared/worker-thread-errors.ts +41 -0
- package/src/shared/attribute-reported-failures.ts +25 -0
- package/src/shared/claim-processes-to-report.ts +22 -0
- package/src/shared/command-errors.ts +44 -0
- package/src/shared/complete-discarded-calls-once-sent.ts +12 -0
- package/src/shared/cut-to-graphemes.ts +12 -0
- package/src/shared/environment-config-errors.ts +74 -0
- package/src/shared/error-fields.ts +13 -0
- package/src/shared/find-bun-loader-for-script.ts +32 -0
- package/src/shared/find-node-modules-package-directory.ts +35 -0
- package/src/shared/find-user-locations-in-stack-traces.ts +64 -0
- package/src/shared/format-error-lines.ts +53 -0
- package/src/shared/format-task-lines.ts +21 -0
- package/src/shared/hold-task-logs.ts +24 -0
- package/src/shared/invariant-errors.ts +28 -0
- package/src/shared/mark-task-worker-thread-contexts.ts +29 -0
- package/src/shared/module-graph-errors.ts +49 -0
- package/src/shared/name-config-key.ts +42 -0
- package/src/shared/names-from-package-json.ts +7 -0
- package/src/shared/print-to-standard-error.ts +48 -0
- package/src/shared/process-errors.ts +191 -0
- package/src/shared/read-environment-config.ts +239 -0
- package/src/shared/record-tasks-made.ts +10 -0
- package/src/shared/report-cause.ts +140 -0
- package/src/shared/report-errors.ts +121 -0
- package/src/shared/resolve-task-options.ts +20 -0
- package/src/shared/rpc.ts +161 -0
- package/src/shared/run-in-child-fiber-and-join.ts +16 -0
- package/src/shared/run-marker.ts +7 -0
- package/src/shared/save-standard-stream-flags.ts +57 -0
- package/src/shared/select-cause-reasons.ts +14 -0
- package/src/shared/signal-run-called-twice.ts +11 -0
- package/src/shared/standard-input-streams.ts +41 -0
- package/src/shared/stop-signals.ts +41 -0
- package/src/shared/task-errors.ts +224 -0
- package/src/shared/task-package.ts +5 -0
- package/src/shared/task-results.ts +55 -0
- package/src/shared/task-services.ts +30 -0
- package/src/shared/write-trace-file.ts +112 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { Visitor } from "oxc-parser";
|
|
2
|
+
import type { parseSync, CallExpression, Expression, Program } from "oxc-parser";
|
|
3
|
+
|
|
4
|
+
import { TASK_PACKAGE } from "../../../../../shared/task-package.ts";
|
|
5
|
+
import {
|
|
6
|
+
TaskCalledBelowTopLevel,
|
|
7
|
+
TaskNameEmpty,
|
|
8
|
+
TaskNameComputed,
|
|
9
|
+
TaskNameMissing,
|
|
10
|
+
TaskNameNotString,
|
|
11
|
+
TaskPackageImportedWithoutTask,
|
|
12
|
+
} from "../../../shared/main-thread-errors.ts";
|
|
13
|
+
import type { ModuleScanRefusal } from "../../../shared/main-thread-errors.ts";
|
|
14
|
+
import { readBarePackageName } from "../../../shared/read-bare-package-name.ts";
|
|
15
|
+
import { findLexicalBindings } from "./find-lexical-bindings.ts";
|
|
16
|
+
import { readLiteralString } from "./read-literal-string.ts";
|
|
17
|
+
import { listValueImports, readRequireFunctionName } from "./read-module-loads.ts";
|
|
18
|
+
import { unwrapTypeAssertions } from "./unwrap-type-assertions.ts";
|
|
19
|
+
|
|
20
|
+
const unwrapPipeCalls = (
|
|
21
|
+
expression: Expression | null | undefined,
|
|
22
|
+
): Expression | null | undefined => {
|
|
23
|
+
const isMissing = expression === null || expression === undefined;
|
|
24
|
+
if (isMissing) {
|
|
25
|
+
return expression;
|
|
26
|
+
}
|
|
27
|
+
const unwrapped = unwrapTypeAssertions(expression);
|
|
28
|
+
if (unwrapped.type !== "CallExpression") {
|
|
29
|
+
return unwrapped;
|
|
30
|
+
}
|
|
31
|
+
const callee = unwrapped.callee;
|
|
32
|
+
const callsPipeMethod =
|
|
33
|
+
callee.type === "MemberExpression" && !callee.computed && callee.property.name === "pipe";
|
|
34
|
+
return callsPipeMethod ? unwrapPipeCalls(callee.object) : unwrapped;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const listInitializers = (
|
|
38
|
+
statement: Program["body"][number],
|
|
39
|
+
): readonly (Expression | null | undefined)[] => {
|
|
40
|
+
const declared = statement.type === "ExportNamedDeclaration" ? statement.declaration : statement;
|
|
41
|
+
if (declared?.type === "VariableDeclaration") {
|
|
42
|
+
return declared.declarations.map((declarator) => declarator.init);
|
|
43
|
+
}
|
|
44
|
+
if (declared?.type === "ExpressionStatement") {
|
|
45
|
+
return [declared.expression];
|
|
46
|
+
}
|
|
47
|
+
if (declared?.type !== "ExportDefaultDeclaration") {
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
const declaration = declared.declaration;
|
|
51
|
+
const isDeclaration =
|
|
52
|
+
declaration.type === "FunctionDeclaration" ||
|
|
53
|
+
declaration.type === "ClassDeclaration" ||
|
|
54
|
+
declaration.type === "TSInterfaceDeclaration";
|
|
55
|
+
return isDeclaration ? [] : [declaration];
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const readRequiredModule = (initializer: Expression | null) => {
|
|
59
|
+
if (initializer === null) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
const required = unwrapTypeAssertions(initializer);
|
|
63
|
+
const isRequireCall =
|
|
64
|
+
required.type === "CallExpression" && readRequireFunctionName(required) !== null;
|
|
65
|
+
return isRequireCall ? readLiteralString(required.arguments[0]) : null;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const listTaskPackageRequires = (program: Program) =>
|
|
69
|
+
program.body.flatMap((statement) =>
|
|
70
|
+
statement.type === "VariableDeclaration"
|
|
71
|
+
? statement.declarations.flatMap(({ id, init: initializer }) => {
|
|
72
|
+
const requiredModule = readRequiredModule(initializer);
|
|
73
|
+
const requiresTaskPackage =
|
|
74
|
+
requiredModule !== null && readBarePackageName(requiredModule) === TASK_PACKAGE.name;
|
|
75
|
+
return requiresTaskPackage ? [id] : [];
|
|
76
|
+
})
|
|
77
|
+
: [],
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
export const findTaskDeclarations = (
|
|
81
|
+
parsed: ReturnType<typeof parseSync>,
|
|
82
|
+
text: string,
|
|
83
|
+
lineAt: (offset: number) => number,
|
|
84
|
+
) => {
|
|
85
|
+
const importViolations: (typeof ModuleScanRefusal.Type)[] = [];
|
|
86
|
+
const nameViolations: (typeof ModuleScanRefusal.Type)[] = [];
|
|
87
|
+
const bindingFor = findLexicalBindings(parsed.program);
|
|
88
|
+
const taskNamespaceBindings = new Set<number>();
|
|
89
|
+
const moduleNamespaceBindings = new Set<number>();
|
|
90
|
+
for (const { declaration, valueEntries } of listValueImports(parsed)) {
|
|
91
|
+
const importedModule = declaration.moduleRequest.value;
|
|
92
|
+
if (readBarePackageName(importedModule) !== TASK_PACKAGE.name) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
for (const importEntry of valueEntries) {
|
|
97
|
+
if (importEntry.importName.name === "Task") {
|
|
98
|
+
taskNamespaceBindings.add(importEntry.localName.start);
|
|
99
|
+
} else if (importEntry.importName.kind === "NamespaceObject") {
|
|
100
|
+
moduleNamespaceBindings.add(importEntry.localName.start);
|
|
101
|
+
} else if (importEntry.importName.name === null) {
|
|
102
|
+
importViolations.push(
|
|
103
|
+
new TaskPackageImportedWithoutTask({ line: lineAt(declaration.start) }),
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
for (const binding of listTaskPackageRequires(parsed.program)) {
|
|
110
|
+
if (binding.type === "Identifier") {
|
|
111
|
+
moduleNamespaceBindings.add(binding.start);
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (binding.type !== "ObjectPattern") {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
for (const property of binding.properties) {
|
|
118
|
+
const bindsTaskNamespace =
|
|
119
|
+
property.type === "Property" &&
|
|
120
|
+
!property.computed &&
|
|
121
|
+
property.key.type === "Identifier" &&
|
|
122
|
+
property.key.name === "Task" &&
|
|
123
|
+
property.value.type === "Identifier";
|
|
124
|
+
if (bindsTaskNamespace) {
|
|
125
|
+
taskNamespaceBindings.add(property.value.start);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const isTaskNamespace = (expression: Expression): boolean =>
|
|
131
|
+
expression.type === "Identifier"
|
|
132
|
+
? taskNamespaceBindings.has(bindingFor(expression) ?? -1)
|
|
133
|
+
: expression.type === "MemberExpression" &&
|
|
134
|
+
!expression.computed &&
|
|
135
|
+
expression.object.type === "Identifier" &&
|
|
136
|
+
moduleNamespaceBindings.has(bindingFor(expression.object) ?? -1) &&
|
|
137
|
+
expression.property.name === "Task";
|
|
138
|
+
|
|
139
|
+
const callsTask = (call: CallExpression) => {
|
|
140
|
+
const callee = call.callee;
|
|
141
|
+
const isNamedMemberCall = callee.type === "MemberExpression" && !callee.computed;
|
|
142
|
+
return isNamedMemberCall && callee.property.name === "make" && isTaskNamespace(callee.object);
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const madeTasks: { readonly name: string; readonly line: number }[] = [];
|
|
146
|
+
const topLevelTaskCalls = new Set<number>();
|
|
147
|
+
for (const statement of parsed.program.body) {
|
|
148
|
+
for (const pipedInitializer of listInitializers(statement)) {
|
|
149
|
+
const initializer = unwrapPipeCalls(pipedInitializer);
|
|
150
|
+
const isTaskCall = initializer?.type === "CallExpression" && callsTask(initializer);
|
|
151
|
+
if (!isTaskCall) {
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
topLevelTaskCalls.add(initializer.start);
|
|
156
|
+
const [nameArgument] = initializer.arguments;
|
|
157
|
+
const taskName = readLiteralString(nameArgument);
|
|
158
|
+
const isNonStringLiteralName = taskName === null && nameArgument?.type === "Literal";
|
|
159
|
+
if (nameArgument === undefined) {
|
|
160
|
+
nameViolations.push(new TaskNameMissing({ line: lineAt(initializer.start) }));
|
|
161
|
+
} else if (isNonStringLiteralName) {
|
|
162
|
+
nameViolations.push(
|
|
163
|
+
new TaskNameNotString({
|
|
164
|
+
line: lineAt(initializer.start),
|
|
165
|
+
nameText: text.slice(nameArgument.start, nameArgument.end),
|
|
166
|
+
}),
|
|
167
|
+
);
|
|
168
|
+
} else if (taskName === null) {
|
|
169
|
+
nameViolations.push(
|
|
170
|
+
new TaskNameComputed({
|
|
171
|
+
line: lineAt(initializer.start),
|
|
172
|
+
nameText: text.slice(nameArgument.start, nameArgument.end),
|
|
173
|
+
}),
|
|
174
|
+
);
|
|
175
|
+
} else if (taskName === "") {
|
|
176
|
+
nameViolations.push(new TaskNameEmpty({ line: lineAt(initializer.start) }));
|
|
177
|
+
} else {
|
|
178
|
+
madeTasks.push({ name: taskName, line: lineAt(initializer.start) });
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return { madeTasks, importViolations, nameViolations, callsTask, topLevelTaskCalls };
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
export const findTaskCallsBelowTopLevel = (
|
|
187
|
+
program: Program,
|
|
188
|
+
taskDeclarations: Pick<
|
|
189
|
+
ReturnType<typeof findTaskDeclarations>,
|
|
190
|
+
"callsTask" | "topLevelTaskCalls"
|
|
191
|
+
>,
|
|
192
|
+
lineAt: (offset: number) => number,
|
|
193
|
+
) => {
|
|
194
|
+
const violations: (typeof ModuleScanRefusal.Type)[] = [];
|
|
195
|
+
new Visitor({
|
|
196
|
+
CallExpression: (call) => {
|
|
197
|
+
const isTaskCallBelowTopLevel =
|
|
198
|
+
taskDeclarations.callsTask(call) && !taskDeclarations.topLevelTaskCalls.has(call.start);
|
|
199
|
+
if (isTaskCallBelowTopLevel) {
|
|
200
|
+
violations.push(new TaskCalledBelowTopLevel({ line: lineAt(call.start) }));
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
}).visit(program);
|
|
204
|
+
return violations;
|
|
205
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { parseSync } from "oxc-parser";
|
|
2
|
+
|
|
3
|
+
import type { SCRIPT_LOADERS } from "../../../../../shared/find-bun-loader-for-script.ts";
|
|
4
|
+
|
|
5
|
+
const PARSER_LANGUAGE_OF_LOADER = {
|
|
6
|
+
js: "jsx",
|
|
7
|
+
jsx: "jsx",
|
|
8
|
+
ts: "ts",
|
|
9
|
+
tsx: "tsx",
|
|
10
|
+
} as const satisfies Record<(typeof SCRIPT_LOADERS)[number], string>;
|
|
11
|
+
|
|
12
|
+
const findParserLanguage = (
|
|
13
|
+
moduleFilePath: string,
|
|
14
|
+
configuredLoader: (typeof SCRIPT_LOADERS)[number] | null,
|
|
15
|
+
) => {
|
|
16
|
+
if (configuredLoader !== null) {
|
|
17
|
+
return { lang: PARSER_LANGUAGE_OF_LOADER[configuredLoader] };
|
|
18
|
+
}
|
|
19
|
+
return moduleFilePath.endsWith(".js") ? { lang: "jsx" as const } : {};
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const parseModuleText = (
|
|
23
|
+
moduleFilePath: string,
|
|
24
|
+
text: string,
|
|
25
|
+
options: {
|
|
26
|
+
readonly configuredLoader: (typeof SCRIPT_LOADERS)[number] | null;
|
|
27
|
+
readonly sourceType?: "commonjs";
|
|
28
|
+
},
|
|
29
|
+
) =>
|
|
30
|
+
parseSync(moduleFilePath, text, {
|
|
31
|
+
preserveParens: false,
|
|
32
|
+
...findParserLanguage(moduleFilePath, options.configuredLoader),
|
|
33
|
+
...(options.sourceType === undefined ? {} : { sourceType: options.sourceType }),
|
|
34
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Argument } from "oxc-parser";
|
|
2
|
+
|
|
3
|
+
export const readLiteralString = (argument: Argument | undefined) => {
|
|
4
|
+
const isStringLiteral = argument?.type === "Literal" && typeof argument.value === "string";
|
|
5
|
+
if (isStringLiteral) {
|
|
6
|
+
return argument.value;
|
|
7
|
+
}
|
|
8
|
+
const isPlainTemplateLiteral =
|
|
9
|
+
argument?.type === "TemplateLiteral" && argument.expressions.length === 0;
|
|
10
|
+
if (isPlainTemplateLiteral) {
|
|
11
|
+
const [onlyQuasi] = argument.quasis;
|
|
12
|
+
return onlyQuasi === undefined ? null : onlyQuasi.value.cooked;
|
|
13
|
+
}
|
|
14
|
+
return null;
|
|
15
|
+
};
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import { Visitor } from "oxc-parser";
|
|
2
|
+
import type {
|
|
3
|
+
Argument,
|
|
4
|
+
CallExpression,
|
|
5
|
+
Expression,
|
|
6
|
+
ImportAttribute,
|
|
7
|
+
parseSync,
|
|
8
|
+
Program,
|
|
9
|
+
TSImportEqualsDeclaration,
|
|
10
|
+
} from "oxc-parser";
|
|
11
|
+
|
|
12
|
+
import { readLiteralString } from "./read-literal-string.ts";
|
|
13
|
+
import { visitNonReferenceIdentifiers } from "./visit-non-reference-identifiers.ts";
|
|
14
|
+
|
|
15
|
+
const REQUIRE_FUNCTION_NAMES = new Set(["require", "__require"]);
|
|
16
|
+
|
|
17
|
+
const MODULE_LOADER_NAMES = new Set([...REQUIRE_FUNCTION_NAMES, "createRequire"]);
|
|
18
|
+
|
|
19
|
+
const readAttributeLoader = (attributes: readonly ImportAttribute[]) =>
|
|
20
|
+
attributes.find((attribute) =>
|
|
21
|
+
attribute.key.type === "Identifier"
|
|
22
|
+
? attribute.key.name === "type"
|
|
23
|
+
: attribute.key.value === "type",
|
|
24
|
+
)?.value.value ?? null;
|
|
25
|
+
|
|
26
|
+
const readObjectProperty = (expression: Expression | null, name: string) => {
|
|
27
|
+
if (expression?.type !== "ObjectExpression") {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const properties = expression.properties;
|
|
31
|
+
const hasComputedOrSpreadProperties = properties.some(
|
|
32
|
+
(property) => property.type === "SpreadElement" || property.computed,
|
|
33
|
+
);
|
|
34
|
+
if (hasComputedOrSpreadProperties) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
for (const property of properties.toReversed()) {
|
|
38
|
+
const isNamedProperty =
|
|
39
|
+
property.type === "Property" &&
|
|
40
|
+
(property.key.type === "Identifier"
|
|
41
|
+
? property.key.name === name
|
|
42
|
+
: property.key.type === "Literal" && property.key.value === name);
|
|
43
|
+
if (isNamedProperty) {
|
|
44
|
+
return property.value;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const readDynamicImportLoader = (options: Expression | null) => {
|
|
51
|
+
if (options === null) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
const attributes = readObjectProperty(options, "with");
|
|
55
|
+
const hasNoAttributes = attributes === undefined || attributes === null;
|
|
56
|
+
if (hasNoAttributes) {
|
|
57
|
+
return attributes;
|
|
58
|
+
}
|
|
59
|
+
const loader = readObjectProperty(attributes, "type");
|
|
60
|
+
const hasNoLoader = loader === undefined || loader === null;
|
|
61
|
+
if (hasNoLoader) {
|
|
62
|
+
return loader;
|
|
63
|
+
}
|
|
64
|
+
return readLiteralString(loader) ?? undefined;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const LITERAL_PATH_PREFIX = /^\.{0,2}\//u;
|
|
68
|
+
|
|
69
|
+
const readLiteralPrefix = (argument: Argument | undefined): string | null => {
|
|
70
|
+
if (argument?.type === "TemplateLiteral") {
|
|
71
|
+
return argument.quasis[0]?.value.cooked ?? null;
|
|
72
|
+
}
|
|
73
|
+
const isConcatenation = argument?.type === "BinaryExpression" && argument.operator === "+";
|
|
74
|
+
if (isConcatenation) {
|
|
75
|
+
return readLiteralPrefix(argument.left);
|
|
76
|
+
}
|
|
77
|
+
return readLiteralString(argument);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const readLiteralDirectory = (argument: Argument | undefined) => {
|
|
81
|
+
const prefix = readLiteralPrefix(argument);
|
|
82
|
+
const namesPathDirectory = prefix !== null && LITERAL_PATH_PREFIX.test(prefix);
|
|
83
|
+
return namesPathDirectory ? prefix.slice(0, prefix.lastIndexOf("/") + 1) : null;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const readRequireResolveName = (callee: CallExpression["callee"]) => {
|
|
87
|
+
const isResolveMember =
|
|
88
|
+
callee.type === "MemberExpression" && !callee.computed && callee.property.name === "resolve";
|
|
89
|
+
if (!isResolveMember) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
const owner = callee.object;
|
|
93
|
+
const isRequire = owner.type === "Identifier" && owner.name === "require";
|
|
94
|
+
if (isRequire) {
|
|
95
|
+
return "require.resolve";
|
|
96
|
+
}
|
|
97
|
+
const isGlobalRequire =
|
|
98
|
+
owner.type === "MemberExpression" &&
|
|
99
|
+
!owner.computed &&
|
|
100
|
+
owner.object.type === "Identifier" &&
|
|
101
|
+
owner.object.name === "globalThis" &&
|
|
102
|
+
owner.property.name === "require";
|
|
103
|
+
return isGlobalRequire ? "globalThis.require.resolve" : null;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export const readRequireFunctionName = (call: CallExpression) => {
|
|
107
|
+
const { callee } = call;
|
|
108
|
+
const isRequireFunction = callee.type === "Identifier" && REQUIRE_FUNCTION_NAMES.has(callee.name);
|
|
109
|
+
return isRequireFunction ? callee.name : null;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const readValueImportEqualsPath = (declaration: TSImportEqualsDeclaration) => {
|
|
113
|
+
const { moduleReference } = declaration;
|
|
114
|
+
const isValueExternalModuleImport =
|
|
115
|
+
declaration.importKind !== "type" && moduleReference.type === "TSExternalModuleReference";
|
|
116
|
+
return isValueExternalModuleImport ? moduleReference.expression : null;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export const listValueImports = (parsed: ReturnType<typeof parseSync>) => {
|
|
120
|
+
const typeOnlyImportDeclarationStarts = new Set(
|
|
121
|
+
parsed.program.body.flatMap((statement) => {
|
|
122
|
+
const isTypeOnlyImport =
|
|
123
|
+
statement.type === "ImportDeclaration" && statement.importKind === "type";
|
|
124
|
+
return isTypeOnlyImport ? [statement.start] : [];
|
|
125
|
+
}),
|
|
126
|
+
);
|
|
127
|
+
return parsed.module.staticImports.flatMap((declaration) => {
|
|
128
|
+
const valueEntries = declaration.entries.filter((importEntry) => !importEntry.isType);
|
|
129
|
+
const importsOnlyTypes =
|
|
130
|
+
typeOnlyImportDeclarationStarts.has(declaration.start) ||
|
|
131
|
+
(declaration.entries.length > 0 && valueEntries.length === 0);
|
|
132
|
+
return importsOnlyTypes ? [] : [{ declaration, valueEntries }];
|
|
133
|
+
});
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
export const listStaticImportSpecifiers = (parsed: ReturnType<typeof parseSync>) => {
|
|
137
|
+
const loaders = new Map(
|
|
138
|
+
parsed.program.body.flatMap((statement) => {
|
|
139
|
+
const hasImportAttributes =
|
|
140
|
+
statement.type === "ImportDeclaration" ||
|
|
141
|
+
statement.type === "ExportNamedDeclaration" ||
|
|
142
|
+
statement.type === "ExportAllDeclaration";
|
|
143
|
+
if (!hasImportAttributes) {
|
|
144
|
+
return [];
|
|
145
|
+
}
|
|
146
|
+
const { source, attributes } = statement;
|
|
147
|
+
return source === null ? [] : [[source.start, readAttributeLoader(attributes)] as const];
|
|
148
|
+
}),
|
|
149
|
+
);
|
|
150
|
+
return [
|
|
151
|
+
...listValueImports(parsed).map(({ declaration }) => declaration.moduleRequest),
|
|
152
|
+
...parsed.module.staticExports.flatMap((declaration) =>
|
|
153
|
+
declaration.entries.flatMap(({ moduleRequest, isType }) => {
|
|
154
|
+
const reexportsValues = moduleRequest !== null && !isType;
|
|
155
|
+
return reexportsValues ? [moduleRequest] : [];
|
|
156
|
+
}),
|
|
157
|
+
),
|
|
158
|
+
].map((moduleRequest) => ({
|
|
159
|
+
value: moduleRequest.value,
|
|
160
|
+
start: moduleRequest.start,
|
|
161
|
+
end: moduleRequest.end,
|
|
162
|
+
kind: "import" as const,
|
|
163
|
+
loader: loaders.get(moduleRequest.start) ?? null,
|
|
164
|
+
}));
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
export const findModuleLoads = (program: Program, text: string) => {
|
|
168
|
+
const literalLoads: {
|
|
169
|
+
readonly value: string;
|
|
170
|
+
readonly start: number;
|
|
171
|
+
readonly end: number;
|
|
172
|
+
readonly kind: "import" | "require.resolve" | "require";
|
|
173
|
+
readonly loader: string | null;
|
|
174
|
+
}[] = [];
|
|
175
|
+
const unfollowedLoads: {
|
|
176
|
+
readonly start: number;
|
|
177
|
+
readonly loader: string;
|
|
178
|
+
readonly argumentText: string | null;
|
|
179
|
+
readonly literalDirectory: string | null;
|
|
180
|
+
}[] = [];
|
|
181
|
+
const nonReferenceStarts = new Set<number>();
|
|
182
|
+
const followedLoaderStarts = new Set<number>();
|
|
183
|
+
new Visitor({
|
|
184
|
+
...visitNonReferenceIdentifiers(nonReferenceStarts),
|
|
185
|
+
MemberExpression: (member) => {
|
|
186
|
+
if (member.computed) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
nonReferenceStarts.add(member.property.start);
|
|
190
|
+
const resolveName = readRequireResolveName(member);
|
|
191
|
+
const isUnfollowedResolver = resolveName !== null && !followedLoaderStarts.has(member.start);
|
|
192
|
+
if (isUnfollowedResolver) {
|
|
193
|
+
unfollowedLoads.push({
|
|
194
|
+
start: member.start,
|
|
195
|
+
loader: resolveName,
|
|
196
|
+
argumentText: null,
|
|
197
|
+
literalDirectory: null,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
const isModuleOrImportMetaRequire =
|
|
201
|
+
member.property.name === "require" &&
|
|
202
|
+
((member.object.type === "Identifier" && member.object.name === "module") ||
|
|
203
|
+
member.object.type === "MetaProperty");
|
|
204
|
+
if (isModuleOrImportMetaRequire) {
|
|
205
|
+
unfollowedLoads.push({
|
|
206
|
+
start: member.start,
|
|
207
|
+
loader: member.object.type === "MetaProperty" ? "import.meta.require" : "module.require",
|
|
208
|
+
argumentText: null,
|
|
209
|
+
literalDirectory: null,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
ImportExpression: (dynamicImport) => {
|
|
214
|
+
const importedPath = readLiteralString(dynamicImport.source);
|
|
215
|
+
const loader = readDynamicImportLoader(dynamicImport.options);
|
|
216
|
+
if (loader === undefined) {
|
|
217
|
+
unfollowedLoads.push({
|
|
218
|
+
start: dynamicImport.start,
|
|
219
|
+
loader: "import attributes",
|
|
220
|
+
argumentText: null,
|
|
221
|
+
literalDirectory: null,
|
|
222
|
+
});
|
|
223
|
+
} else if (importedPath === null) {
|
|
224
|
+
unfollowedLoads.push({
|
|
225
|
+
start: dynamicImport.start,
|
|
226
|
+
loader: "import",
|
|
227
|
+
argumentText: text.slice(dynamicImport.source.start, dynamicImport.source.end),
|
|
228
|
+
literalDirectory: readLiteralDirectory(dynamicImport.source),
|
|
229
|
+
});
|
|
230
|
+
} else {
|
|
231
|
+
literalLoads.push({
|
|
232
|
+
value: importedPath,
|
|
233
|
+
start: dynamicImport.source.start,
|
|
234
|
+
end: dynamicImport.source.end,
|
|
235
|
+
kind: "import",
|
|
236
|
+
loader,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
CallExpression: (call) => {
|
|
241
|
+
const { callee } = call;
|
|
242
|
+
const resolveName = readRequireResolveName(callee);
|
|
243
|
+
const requireFunctionName = resolveName ?? readRequireFunctionName(call);
|
|
244
|
+
if (requireFunctionName === null) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
followedLoaderStarts.add(callee.start);
|
|
248
|
+
const isResolveCall = resolveName !== null && callee.type === "MemberExpression";
|
|
249
|
+
if (isResolveCall) {
|
|
250
|
+
followedLoaderStarts.add(callee.object.start);
|
|
251
|
+
if (call.arguments.length !== 1) {
|
|
252
|
+
unfollowedLoads.push({
|
|
253
|
+
start: call.start,
|
|
254
|
+
loader: resolveName,
|
|
255
|
+
argumentText: null,
|
|
256
|
+
literalDirectory: null,
|
|
257
|
+
});
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
const [requiredPathArgument] = call.arguments;
|
|
262
|
+
const requiredPath = readLiteralString(requiredPathArgument);
|
|
263
|
+
const isUnfollowedRequire = requiredPathArgument === undefined || requiredPath === null;
|
|
264
|
+
if (isUnfollowedRequire) {
|
|
265
|
+
unfollowedLoads.push({
|
|
266
|
+
start: call.start,
|
|
267
|
+
loader: requireFunctionName,
|
|
268
|
+
argumentText:
|
|
269
|
+
requiredPathArgument === undefined
|
|
270
|
+
? ""
|
|
271
|
+
: text.slice(requiredPathArgument.start, requiredPathArgument.end),
|
|
272
|
+
literalDirectory: readLiteralDirectory(requiredPathArgument),
|
|
273
|
+
});
|
|
274
|
+
} else {
|
|
275
|
+
literalLoads.push({
|
|
276
|
+
value: requiredPath,
|
|
277
|
+
start: requiredPathArgument.start,
|
|
278
|
+
end: requiredPathArgument.end,
|
|
279
|
+
kind: resolveName === null ? "require" : "require.resolve",
|
|
280
|
+
loader: null,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
Identifier: (identifier) => {
|
|
285
|
+
const isUnfollowedLoaderReference =
|
|
286
|
+
MODULE_LOADER_NAMES.has(identifier.name) &&
|
|
287
|
+
!nonReferenceStarts.has(identifier.start) &&
|
|
288
|
+
!followedLoaderStarts.has(identifier.start);
|
|
289
|
+
if (isUnfollowedLoaderReference) {
|
|
290
|
+
unfollowedLoads.push({
|
|
291
|
+
start: identifier.start,
|
|
292
|
+
loader: identifier.name,
|
|
293
|
+
argumentText: null,
|
|
294
|
+
literalDirectory: null,
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
},
|
|
298
|
+
TSImportEqualsDeclaration: (declaration) => {
|
|
299
|
+
const requiredPath = readValueImportEqualsPath(declaration);
|
|
300
|
+
if (requiredPath !== null) {
|
|
301
|
+
literalLoads.push({
|
|
302
|
+
value: requiredPath.value,
|
|
303
|
+
start: requiredPath.start,
|
|
304
|
+
end: requiredPath.end,
|
|
305
|
+
kind: "require",
|
|
306
|
+
loader: null,
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
}).visit(program);
|
|
311
|
+
return { literalLoads, unfollowedLoads };
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
export const listSpecifiersOnce = <Specifier extends { readonly start: number }>(
|
|
315
|
+
specifiers: readonly Specifier[],
|
|
316
|
+
) => [...new Map(specifiers.map((specifier) => [specifier.start, specifier])).values()];
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { SCRIPT_LOADERS } from "../../../../../shared/find-bun-loader-for-script.ts";
|
|
2
|
+
import {
|
|
3
|
+
ModuleLoadedByComputedPath,
|
|
4
|
+
ModuleLoaderRefused,
|
|
5
|
+
ModuleSyntaxInvalid,
|
|
6
|
+
} from "../../../shared/main-thread-errors.ts";
|
|
7
|
+
import type { ModuleScanRefusal } from "../../../shared/main-thread-errors.ts";
|
|
8
|
+
import { findTaskCallsBelowTopLevel, findTaskDeclarations } from "./find-task-declarations.ts";
|
|
9
|
+
import { parseModuleText } from "./parse-module-text.ts";
|
|
10
|
+
import {
|
|
11
|
+
findModuleLoads,
|
|
12
|
+
listSpecifiersOnce,
|
|
13
|
+
listStaticImportSpecifiers,
|
|
14
|
+
} from "./read-module-loads.ts";
|
|
15
|
+
|
|
16
|
+
export const scanProjectModule = (
|
|
17
|
+
moduleFilePath: string,
|
|
18
|
+
text: string,
|
|
19
|
+
configuredLoader: (typeof SCRIPT_LOADERS)[number] | null = null,
|
|
20
|
+
) => {
|
|
21
|
+
const parsed = parseModuleText(moduleFilePath, text, { configuredLoader });
|
|
22
|
+
const refusals: (typeof ModuleScanRefusal.Type)[] = [];
|
|
23
|
+
const lineAt = (offset: number) => text.slice(0, offset).split("\n").length;
|
|
24
|
+
|
|
25
|
+
for (const error of parsed.errors) {
|
|
26
|
+
const [firstLabel] = error.labels;
|
|
27
|
+
refusals.push(
|
|
28
|
+
new ModuleSyntaxInvalid({
|
|
29
|
+
line: firstLabel === undefined ? null : lineAt(firstLabel.start),
|
|
30
|
+
parserMessage: error.message,
|
|
31
|
+
}),
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const taskDeclarations = findTaskDeclarations(parsed, text, lineAt);
|
|
36
|
+
refusals.push(...taskDeclarations.importViolations);
|
|
37
|
+
const moduleLoads = findModuleLoads(parsed.program, text);
|
|
38
|
+
for (const unfollowedLoad of moduleLoads.unfollowedLoads) {
|
|
39
|
+
if (unfollowedLoad.argumentText === null) {
|
|
40
|
+
refusals.push(
|
|
41
|
+
new ModuleLoaderRefused({
|
|
42
|
+
line: lineAt(unfollowedLoad.start),
|
|
43
|
+
loader: unfollowedLoad.loader,
|
|
44
|
+
}),
|
|
45
|
+
);
|
|
46
|
+
} else {
|
|
47
|
+
refusals.push(
|
|
48
|
+
new ModuleLoadedByComputedPath({
|
|
49
|
+
line: lineAt(unfollowedLoad.start),
|
|
50
|
+
loader: unfollowedLoad.loader,
|
|
51
|
+
argumentText: unfollowedLoad.argumentText,
|
|
52
|
+
}),
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
refusals.push(
|
|
58
|
+
...taskDeclarations.nameViolations,
|
|
59
|
+
...findTaskCallsBelowTopLevel(parsed.program, taskDeclarations, lineAt),
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
specifiers: listSpecifiersOnce([
|
|
64
|
+
...listStaticImportSpecifiers(parsed),
|
|
65
|
+
...moduleLoads.literalLoads,
|
|
66
|
+
]),
|
|
67
|
+
madeTasks: taskDeclarations.madeTasks,
|
|
68
|
+
refusals,
|
|
69
|
+
};
|
|
70
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Expression } from "oxc-parser";
|
|
2
|
+
|
|
3
|
+
export const unwrapTypeAssertions = (expression: Expression): Expression => {
|
|
4
|
+
const isTypeAssertion =
|
|
5
|
+
expression.type === "TSAsExpression" ||
|
|
6
|
+
expression.type === "TSSatisfiesExpression" ||
|
|
7
|
+
expression.type === "TSNonNullExpression" ||
|
|
8
|
+
expression.type === "TSTypeAssertion";
|
|
9
|
+
return isTypeAssertion ? unwrapTypeAssertions(expression.expression) : expression;
|
|
10
|
+
};
|
package/src/lib/main-thread/lib/scan-module-source/internal/visit-non-reference-identifiers.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { VisitorObject } from "oxc-parser";
|
|
2
|
+
|
|
3
|
+
export const visitNonReferenceIdentifiers = (nonReferenceStarts: Set<number>) =>
|
|
4
|
+
({
|
|
5
|
+
Property: (property) => {
|
|
6
|
+
const hasNonReferenceKey = !property.computed && !property.shorthand;
|
|
7
|
+
if (hasNonReferenceKey) {
|
|
8
|
+
nonReferenceStarts.add(property.key.start);
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
PropertyDefinition: (definition) => {
|
|
12
|
+
if (!definition.computed) {
|
|
13
|
+
nonReferenceStarts.add(definition.key.start);
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
MethodDefinition: (definition) => {
|
|
17
|
+
if (!definition.computed) {
|
|
18
|
+
nonReferenceStarts.add(definition.key.start);
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
}) satisfies VisitorObject;
|