@nimbus-sh/worker 0.12.0 → 0.13.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.
- package/dist/esbuild-cli-artifact.generated.d.ts +1 -1
- package/dist/esbuild-cli-artifact.generated.js +4 -4
- package/dist/esbuild-wasm-bundle.generated.d.ts +11 -10
- package/dist/esbuild-wasm-bundle.generated.d.ts.map +1 -1
- package/dist/esbuild-wasm-bundle.generated.js +11 -10
- package/dist/facets/build-facet.d.ts +53 -0
- package/dist/facets/build-facet.d.ts.map +1 -0
- package/dist/facets/build-facet.js +346 -0
- package/dist/facets/cirrus-real.d.ts +3 -0
- package/dist/facets/cirrus-real.d.ts.map +1 -1
- package/dist/facets/cirrus-real.js +1 -2
- package/dist/facets/compose.d.ts +1 -0
- package/dist/facets/compose.d.ts.map +1 -1
- package/dist/facets/compose.js +3 -0
- package/dist/facets/data-plan.d.ts +14 -30
- package/dist/facets/data-plan.d.ts.map +1 -1
- package/dist/facets/data-plan.js +153 -13
- package/dist/facets/dev-stylesheet.d.ts +27 -0
- package/dist/facets/dev-stylesheet.d.ts.map +1 -0
- package/dist/facets/dev-stylesheet.js +65 -0
- package/dist/facets/esbuild-transform.d.ts +23 -18
- package/dist/facets/esbuild-transform.d.ts.map +1 -1
- package/dist/facets/esbuild-transform.js +63 -109
- package/dist/facets/manager.d.ts +49 -9
- package/dist/facets/manager.d.ts.map +1 -1
- package/dist/facets/manager.js +238 -69
- package/dist/facets/oxc-transform.d.ts +29 -0
- package/dist/facets/oxc-transform.d.ts.map +1 -0
- package/dist/facets/oxc-transform.js +198 -0
- package/dist/facets/prebundle-pool.d.ts +23 -0
- package/dist/facets/prebundle-pool.d.ts.map +1 -0
- package/dist/facets/prebundle-pool.js +55 -0
- package/dist/facets/process.d.ts +13 -6
- package/dist/facets/process.d.ts.map +1 -1
- package/dist/facets/process.js +37 -31
- package/dist/facets/transform-store.d.ts +4 -4
- package/dist/facets/transform-store.js +4 -4
- package/dist/facets/vite-dev-server.d.ts +89 -12
- package/dist/facets/vite-dev-server.d.ts.map +1 -1
- package/dist/facets/vite-dev-server.js +66 -43
- package/dist/hosted/commands.d.ts.map +1 -1
- package/dist/hosted/commands.js +20 -4
- package/dist/hosted/runtime.d.ts.map +1 -1
- package/dist/hosted/runtime.js +7 -2
- package/dist/hosted/services.d.ts +3 -3
- package/dist/hosted/services.d.ts.map +1 -1
- package/dist/hosted/services.js +102 -95
- package/dist/hosted/session.d.ts +1 -0
- package/dist/hosted/session.d.ts.map +1 -1
- package/dist/loaders/generated-workers.d.ts +3 -0
- package/dist/loaders/generated-workers.d.ts.map +1 -1
- package/dist/loaders/generated-workers.js +3 -0
- package/dist/napi-wasm-artifacts.generated.js +2 -2
- package/dist/node-shims-artifact.generated.d.ts +14 -1
- package/dist/node-shims-artifact.generated.d.ts.map +1 -1
- package/dist/node-shims-artifact.generated.js +23 -10
- package/dist/npm/bin-links.d.ts +7 -4
- package/dist/npm/bin-links.d.ts.map +1 -1
- package/dist/npm/bin-links.js +14 -55
- package/dist/npm/cache.d.ts +6 -0
- package/dist/npm/cache.d.ts.map +1 -1
- package/dist/npm/cache.js +26 -4
- package/dist/npm/installer.d.ts +1 -1
- package/dist/npm/installer.d.ts.map +1 -1
- package/dist/npm/installer.js +26 -52
- package/dist/npm/pre-bundle-facet.d.ts +15 -117
- package/dist/npm/pre-bundle-facet.d.ts.map +1 -1
- package/dist/npm/pre-bundle-facet.js +13 -511
- package/dist/oxc-facet-artifact.generated.d.ts +18 -0
- package/dist/oxc-facet-artifact.generated.d.ts.map +1 -0
- package/dist/oxc-facet-artifact.generated.js +17 -0
- package/dist/oxc-wasm-artifact.generated.d.ts +20 -0
- package/dist/oxc-wasm-artifact.generated.d.ts.map +1 -0
- package/dist/oxc-wasm-artifact.generated.js +19 -0
- package/dist/rolldown-facet-artifact.generated.d.ts +18 -0
- package/dist/rolldown-facet-artifact.generated.d.ts.map +1 -0
- package/dist/rolldown-facet-artifact.generated.js +17 -0
- package/dist/router/remote-api.d.ts.map +1 -1
- package/dist/router/remote-api.js +9 -0
- package/dist/runtime/bash-repl.d.ts.map +1 -1
- package/dist/runtime/bash-repl.js +1 -4
- package/dist/runtime/cpython-resident.d.ts.map +1 -1
- package/dist/runtime/cpython-resident.js +1 -0
- package/dist/runtime/esbuild-wasm-bytes.d.ts +25 -23
- package/dist/runtime/esbuild-wasm-bytes.d.ts.map +1 -1
- package/dist/runtime/esbuild-wasm-bytes.js +30 -25
- package/dist/runtime/facet-loader-host.d.ts +5 -4
- package/dist/runtime/facet-loader-host.d.ts.map +1 -1
- package/dist/runtime/facet-loader-host.js +5 -4
- package/dist/runtime/node-runner.d.ts +2 -0
- package/dist/runtime/node-runner.d.ts.map +1 -1
- package/dist/runtime/node-runner.js +1 -0
- package/dist/runtime/node-shims-artifact.d.ts +8 -1
- package/dist/runtime/node-shims-artifact.d.ts.map +1 -1
- package/dist/runtime/node-shims-artifact.js +32 -4
- package/dist/runtime/node-shims.d.ts.map +1 -1
- package/dist/runtime/node-shims.js +145 -109
- package/dist/runtime/opencode-facet-runner.d.ts.map +1 -1
- package/dist/runtime/opencode-facet-runner.js +6 -2
- package/dist/runtime/oxc-wasm-bytes.d.ts +19 -0
- package/dist/runtime/oxc-wasm-bytes.d.ts.map +1 -0
- package/dist/runtime/oxc-wasm-bytes.js +39 -0
- package/dist/runtime/package-manager.js +1 -1
- package/dist/runtime/process-logs-api.d.ts.map +1 -1
- package/dist/runtime/process-logs-api.js +2 -0
- package/dist/runtime/project-fs.d.ts +8 -1
- package/dist/runtime/project-fs.d.ts.map +1 -1
- package/dist/runtime/project-fs.js +15 -1
- package/dist/runtime/python-repl.d.ts +40 -1
- package/dist/runtime/python-repl.d.ts.map +1 -1
- package/dist/runtime/python-repl.js +19 -11
- package/dist/runtime/ruby-repl.d.ts +38 -0
- package/dist/runtime/ruby-repl.d.ts.map +1 -1
- package/dist/runtime/ruby-repl.js +24 -5
- package/dist/runtime/ruby-resident.d.ts.map +1 -1
- package/dist/runtime/ruby-resident.js +2 -0
- package/dist/session/nimbus-session.d.ts +13 -8
- package/dist/session/nimbus-session.d.ts.map +1 -1
- package/dist/session/nimbus-session.js +11 -5
- package/dist/session/programmatic.d.ts +25 -30
- package/dist/session/programmatic.d.ts.map +1 -1
- package/dist/session/programmatic.js +65 -93
- package/dist/session/routes.d.ts.map +1 -1
- package/dist/session/routes.js +23 -7
- package/dist/session/rpc.d.ts +16 -6
- package/dist/session/rpc.d.ts.map +1 -1
- package/dist/session/rpc.js +53 -13
- package/dist/session/start-real-vite.d.ts +24 -9
- package/dist/session/start-real-vite.d.ts.map +1 -1
- package/dist/session/start-real-vite.js +40 -4
- package/dist/session/supervisor-op.d.ts +14 -0
- package/dist/session/supervisor-op.d.ts.map +1 -1
- package/dist/session/supervisor-op.js +49 -0
- package/dist/session/supervisor-rpc.d.ts +11 -0
- package/dist/session/supervisor-rpc.d.ts.map +1 -1
- package/dist/session/supervisor-rpc.js +18 -0
- package/dist/session/vite-command.d.ts.map +1 -1
- package/dist/session/vite-command.js +23 -19
- package/dist/session/ws-relay.d.ts.map +1 -1
- package/dist/session/ws-relay.js +13 -1
- package/dist/shell/npm-bin-entrypoints.d.ts +3 -1
- package/dist/shell/npm-bin-entrypoints.d.ts.map +1 -1
- package/dist/shell/npm-bin-entrypoints.js +53 -25
- package/dist/vfs/facet-resident-store.d.ts.map +1 -1
- package/dist/vfs/facet-resident-store.js +11 -5
- package/dist/wrangler/nimbus-wrangler.d.ts +8 -0
- package/dist/wrangler/nimbus-wrangler.d.ts.map +1 -1
- package/dist/wrangler/nimbus-wrangler.js +54 -57
- package/package.json +6 -4
- package/public/_assets/esbuild-0.24.2.wasm +0 -0
- package/public/_assets/napi-wasm/loader/{99efae93dd1dcc2b → ab7e57fe9e8750c2}/napi-wasm-loader.mjs +4 -4
- package/public/_assets/napi-wasm/loader/{99efae93dd1dcc2b → ab7e57fe9e8750c2}/provenance.json +2 -2
- package/public/_assets/oxc/nimbus-oxc-1aa8c4c7057c6c28.provenance.json +72 -0
- package/public/_assets/oxc/nimbus-oxc-1aa8c4c7057c6c28.wasm +0 -0
- package/public/_assets/runtime/esbuild-cli-f13e6a112b03ac07.js +1143 -0
- package/public/_assets/runtime/js-interpreter-3c37c48024f0f651.js +23 -0
- package/public/_assets/runtime/js-interpreter-ops-2f4dfec6798e70fc.js +290 -0
- package/public/_assets/runtime/js-interpreter-primordials-e620a61b7e9800fa.js +1 -0
- package/public/_assets/runtime/{node-shims-f92f16c7d7c532e9.js → node-shims-e3adc4576da31e6e.js} +651 -103
- package/public/_assets/runtime/{esbuild-cli-b7aecffd4c6a36fe.js → oxc-facet-fd86fefcdf16bf9c.js} +217 -1047
- package/public/_assets/runtime/{resident-store-92386265e8b306dc.js → resident-store-009f39539ac989fc.js} +11 -5
- package/public/_assets/runtime/rolldown-facet-09983434e2dbe93e.js +15165 -0
- package/public/_assets/runtime/{vfs-write-ledger-d33ee0034446c3e0.js → vfs-write-ledger-9e9594591425a9b8.js} +4 -1
- package/scripts/acorn-primordials.mjs +637 -0
- package/scripts/bundle-esbuild-wasm.mjs +42 -50
- package/scripts/bundle-facet-workers.mjs +247 -18
- package/scripts/bundle-node-shims.mjs +29 -2
- package/scripts/bundle-oxc-wasm.mjs +105 -0
- package/scripts/interpreter-bundle.mjs +105 -0
- package/scripts/napi-wasm/build.mjs +4 -1
- package/scripts/napi-wasm/loader/napi-wasm-loader.ts +38 -3
- package/scripts/oxc-wasm/Cargo.lock +1246 -0
- package/scripts/oxc-wasm/Cargo.toml +30 -0
- package/scripts/oxc-wasm/build.mjs +249 -0
- package/scripts/oxc-wasm/examples/memory.rs +51 -0
- package/scripts/oxc-wasm/examples/transform.rs +20 -0
- package/scripts/oxc-wasm/inputs.mjs +32 -0
- package/scripts/oxc-wasm/rust-toolchain.toml +5 -0
- package/scripts/oxc-wasm/src/abi.rs +136 -0
- package/scripts/oxc-wasm/src/diagnostics.rs +49 -0
- package/scripts/oxc-wasm/src/helpers.rs +139 -0
- package/scripts/oxc-wasm/src/lib.rs +247 -0
- package/scripts/oxc-wasm/src/module.rs +978 -0
- package/scripts/oxc-wasm/src/names.rs +175 -0
- package/scripts/oxc-wasm/src/options.rs +154 -0
- package/scripts/parser-audit.mjs +492 -0
- package/scripts/rolldown-facet/entry.mjs +21 -0
- package/scripts/rolldown-facet/shims.mjs +64 -0
- package/dist/facets/esbuild-bundle-pool.d.ts +0 -43
- package/dist/facets/esbuild-bundle-pool.d.ts.map +0 -1
- package/dist/facets/esbuild-bundle-pool.js +0 -80
- package/dist/loaders/pre-bundle-preamble.d.ts +0 -44
- package/dist/loaders/pre-bundle-preamble.d.ts.map +0 -1
- package/dist/loaders/pre-bundle-preamble.js +0 -116
- package/dist/runtime/host-wasm.d.ts +0 -8
- package/dist/runtime/host-wasm.d.ts.map +0 -1
- package/dist/runtime/host-wasm.js +0 -33
- /package/public/_assets/napi-wasm/loader/{99efae93dd1dcc2b → ab7e57fe9e8750c2}/wasi-trampoline.wasm +0 -0
|
@@ -0,0 +1,978 @@
|
|
|
1
|
+
//! Module format conversion, after TypeScript, JSX and defines have run.
|
|
2
|
+
//!
|
|
3
|
+
//! `Format::Cjs` lowers ES module syntax to CommonJS the way esbuild 0.24's
|
|
4
|
+
//! transform API does, because that is the shape the rest of Nimbus was
|
|
5
|
+
//! written against (bundle-cell-transform.ts, server-launch.ts):
|
|
6
|
+
//!
|
|
7
|
+
//! ```js
|
|
8
|
+
//! // helpers (helpers.rs), only those used
|
|
9
|
+
//! var stdin_exports = {}; // a module with export syntax
|
|
10
|
+
//! __export(stdin_exports, { a: () => a, default: () => stdin_default });
|
|
11
|
+
//! module.exports = __toCommonJS(stdin_exports); // marks __esModule
|
|
12
|
+
//! var import_x = __toESM(require("x")); // every import, hoisted in order
|
|
13
|
+
//! __reExport(stdin_exports, require("y"), module.exports);
|
|
14
|
+
//! ...body, with each use of an imported binding read through its record
|
|
15
|
+
//! (`(0, import_x.f)()` for a call, so `this` stays undefined)
|
|
16
|
+
//! ```
|
|
17
|
+
//!
|
|
18
|
+
//! Live bindings come from the getters; `default` imports go through
|
|
19
|
+
//! `__toESM`, whose `__esModule` rule is Babel's (esbuild's), not Node's.
|
|
20
|
+
//! Every format also does what esbuild does to `import.meta` and `import()`
|
|
21
|
+
//! when the target lacks them, and replaces top-level `this` in an ES module
|
|
22
|
+
//! with `void 0`. `Format::Esm` wraps a CommonJS module (one that uses
|
|
23
|
+
//! `module`/`exports` and has no export syntax) as `__commonJS` and exports
|
|
24
|
+
//! its `module.exports` as `default`.
|
|
25
|
+
|
|
26
|
+
use rustc_hash::FxHashMap;
|
|
27
|
+
|
|
28
|
+
use oxc::allocator::{Allocator, Box as ArenaBox, GetAllocator, TakeIn, Vec as ArenaVec};
|
|
29
|
+
use oxc::ast::builder::AstBuilder;
|
|
30
|
+
use oxc::ast::ast::*;
|
|
31
|
+
use oxc::ast_visit::VisitMut;
|
|
32
|
+
use oxc::diagnostics::OxcDiagnostic;
|
|
33
|
+
use oxc::parser::Parser;
|
|
34
|
+
use oxc::semantic::{Scoping, SymbolId};
|
|
35
|
+
use oxc::span::{SPAN, SourceType, Span};
|
|
36
|
+
use oxc::str::Ident;
|
|
37
|
+
use oxc::syntax::identifier::is_identifier_name;
|
|
38
|
+
use oxc::syntax::number::NumberBase;
|
|
39
|
+
use oxc::syntax::operator::UnaryOperator;
|
|
40
|
+
use oxc_ecmascript::BoundNames;
|
|
41
|
+
use oxc_traverse::{Ancestor, Traverse, traverse_mut};
|
|
42
|
+
|
|
43
|
+
use crate::helpers::{Helper, Helpers};
|
|
44
|
+
use crate::names::{Names, ensure_valid_identifier, name_from_path};
|
|
45
|
+
use crate::options::Format;
|
|
46
|
+
|
|
47
|
+
type TraverseCtx<'a> = oxc_traverse::TraverseCtx<'a, ()>;
|
|
48
|
+
|
|
49
|
+
pub struct ModuleOptions {
|
|
50
|
+
pub format: Format,
|
|
51
|
+
pub dynamic_import: bool,
|
|
52
|
+
pub import_meta: bool,
|
|
53
|
+
/// The module is an ES module (lib.rs decides).
|
|
54
|
+
pub is_esm: bool,
|
|
55
|
+
pub has_export_syntax: bool,
|
|
56
|
+
pub has_import_meta: bool,
|
|
57
|
+
pub has_dynamic_import: bool,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/// A hoisted statement standing for an import, in source order.
|
|
61
|
+
enum Record {
|
|
62
|
+
/// `var <name> = require(<source>)`, through `__toESM` when a default or
|
|
63
|
+
/// namespace binding reads it.
|
|
64
|
+
Require { name: RecordName, source: String, to_esm: bool },
|
|
65
|
+
/// `export * from <source>`.
|
|
66
|
+
ReExport { source: String },
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
#[derive(Clone)]
|
|
70
|
+
enum RecordName {
|
|
71
|
+
Generated(String),
|
|
72
|
+
/// A namespace import keeps its own binding as the record.
|
|
73
|
+
Symbol(SymbolId),
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/// What an export's getter returns.
|
|
77
|
+
enum ExportValue {
|
|
78
|
+
Symbol(SymbolId),
|
|
79
|
+
Generated(String),
|
|
80
|
+
Member(RecordName, String),
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/// How a use of an imported binding reads.
|
|
84
|
+
#[derive(Clone)]
|
|
85
|
+
struct ImportBinding<'a> {
|
|
86
|
+
record: RecordName,
|
|
87
|
+
record_ident: Ident<'a>,
|
|
88
|
+
property: Ident<'a>,
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
pub struct ModulePass<'a> {
|
|
92
|
+
options: ModuleOptions,
|
|
93
|
+
/// ES module syntax is lowered to CommonJS.
|
|
94
|
+
to_cjs: bool,
|
|
95
|
+
/// The module is an ES module: its top-level `this` is undefined.
|
|
96
|
+
is_esm: bool,
|
|
97
|
+
wrap_commonjs: bool,
|
|
98
|
+
names: Option<Names>,
|
|
99
|
+
helpers: Helpers,
|
|
100
|
+
records: Vec<Record>,
|
|
101
|
+
exports: Vec<(String, ExportValue)>,
|
|
102
|
+
bindings: FxHashMap<SymbolId, ImportBinding<'a>>,
|
|
103
|
+
exports_name: Option<String>,
|
|
104
|
+
wrapper_name: Option<String>,
|
|
105
|
+
/// The wrapper's `exports` and `module` parameters: renamed where the
|
|
106
|
+
/// module declares its own.
|
|
107
|
+
wrapper_params: [String; 2],
|
|
108
|
+
import_meta_name: Option<Ident<'a>>,
|
|
109
|
+
function_depth: u32,
|
|
110
|
+
top_level_await: Option<Span>,
|
|
111
|
+
errors: Vec<OxcDiagnostic>,
|
|
112
|
+
warnings: Vec<OxcDiagnostic>,
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
impl<'a> ModulePass<'a> {
|
|
116
|
+
pub fn new(options: ModuleOptions) -> Self {
|
|
117
|
+
Self {
|
|
118
|
+
options,
|
|
119
|
+
to_cjs: false,
|
|
120
|
+
is_esm: false,
|
|
121
|
+
wrap_commonjs: false,
|
|
122
|
+
names: None,
|
|
123
|
+
helpers: Helpers::default(),
|
|
124
|
+
records: Vec::new(),
|
|
125
|
+
exports: Vec::new(),
|
|
126
|
+
bindings: FxHashMap::default(),
|
|
127
|
+
exports_name: None,
|
|
128
|
+
wrapper_name: None,
|
|
129
|
+
wrapper_params: ["exports".into(), "module".into()],
|
|
130
|
+
import_meta_name: None,
|
|
131
|
+
function_depth: 0,
|
|
132
|
+
top_level_await: None,
|
|
133
|
+
errors: Vec::new(),
|
|
134
|
+
warnings: Vec::new(),
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/// Run the pass. The outcome is the warnings, or the errors that refuse the module.
|
|
139
|
+
pub fn run(
|
|
140
|
+
mut self,
|
|
141
|
+
allocator: &'a Allocator,
|
|
142
|
+
program: &mut Program<'a>,
|
|
143
|
+
mut scoping: Scoping,
|
|
144
|
+
) -> (Scoping, Result<Vec<OxcDiagnostic>, Vec<OxcDiagnostic>>) {
|
|
145
|
+
// The TypeScript transform keeps a module whose imports and exports
|
|
146
|
+
// were all types a module with an `export {}` of its own; esbuild
|
|
147
|
+
// emits nothing for them, and what the source said decides the format.
|
|
148
|
+
program.body.retain(
|
|
149
|
+
|s| !matches!(s, Statement::ExportNamedDeclaration(e) if e.specifiers.is_empty() && e.span.is_unspanned()),
|
|
150
|
+
);
|
|
151
|
+
// Imports the transformer injected (the JSX runtime's) have no
|
|
152
|
+
// position; esbuild prints them ahead of the module's own.
|
|
153
|
+
let injected = |s: &Statement<'a>| matches!(s, Statement::ImportDeclaration(i) if i.span.is_unspanned());
|
|
154
|
+
if program.body.iter().any(injected) {
|
|
155
|
+
let (mut synthetic, own): (Vec<_>, Vec<_>) = program.body.take_in(&allocator).into_iter().partition(injected);
|
|
156
|
+
synthetic.extend(own);
|
|
157
|
+
program.body = ArenaVec::from_iter_in(synthetic, &allocator);
|
|
158
|
+
}
|
|
159
|
+
if self.options.format == Format::Esm {
|
|
160
|
+
Self::drop_empty_exports(program, allocator);
|
|
161
|
+
}
|
|
162
|
+
self.is_esm = self.options.is_esm;
|
|
163
|
+
self.to_cjs = self.options.format == Format::Cjs && self.is_esm;
|
|
164
|
+
// esbuild's rule: a module that reads `module` or `exports` and has no
|
|
165
|
+
// export syntax, `import.meta` or top-level await is CommonJS, even
|
|
166
|
+
// with imports (they stay in front of the wrapper).
|
|
167
|
+
self.wrap_commonjs = self.options.format == Format::Esm
|
|
168
|
+
&& !self.options.has_export_syntax
|
|
169
|
+
&& !self.options.has_import_meta
|
|
170
|
+
&& ["module", "exports"].iter().any(|&name| reads_commonjs_name(&scoping, name));
|
|
171
|
+
// Top-level await makes it an ES module after all; known before the
|
|
172
|
+
// walk, which rewrites `this` by it.
|
|
173
|
+
self.wrap_commonjs = self.wrap_commonjs && !has_top_level_await(program);
|
|
174
|
+
let lowers_dynamic_import = !self.options.dynamic_import && self.options.has_dynamic_import;
|
|
175
|
+
|
|
176
|
+
if self.to_cjs || self.wrap_commonjs || lowers_dynamic_import {
|
|
177
|
+
let mut names = Names::new(&scoping);
|
|
178
|
+
if self.to_cjs {
|
|
179
|
+
self.plan_cjs(program, &mut scoping, &mut names, allocator);
|
|
180
|
+
}
|
|
181
|
+
// A wrap is decided after the traversal (top-level await makes a
|
|
182
|
+
// module ES), so its helper's name is claimed in case.
|
|
183
|
+
let mut claimed = self.helpers;
|
|
184
|
+
if self.wrap_commonjs {
|
|
185
|
+
claimed.add(Helper::CommonJs);
|
|
186
|
+
self.wrapper_name = Some(names.generate("require_stdin", &mut scoping, allocator));
|
|
187
|
+
// `var exports` and `function module() {}` redeclare the
|
|
188
|
+
// wrapper's parameter, as in Node's own wrapper; a lexical or
|
|
189
|
+
// imported binding of the name would clash with it.
|
|
190
|
+
for (param, name) in self.wrapper_params.iter_mut().zip(["exports", "module"]) {
|
|
191
|
+
if let Some(symbol) = scoping.get_root_binding(name.into()) {
|
|
192
|
+
let flags = scoping.symbol_flags(symbol);
|
|
193
|
+
if !flags.is_function_scoped_declaration() && !flags.is_function() {
|
|
194
|
+
*param = names.claim_fresh(name);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if lowers_dynamic_import {
|
|
200
|
+
self.helpers.add(Helper::ToEsm);
|
|
201
|
+
claimed.add(Helper::ToEsm);
|
|
202
|
+
}
|
|
203
|
+
for helper in claimed.iter() {
|
|
204
|
+
if scoping.root_unresolved_references().contains_key(helper.name()) {
|
|
205
|
+
self.errors.push(OxcDiagnostic::error(format!(
|
|
206
|
+
"This module refers to a global named \"{}\", which module conversion needs for its helper",
|
|
207
|
+
helper.name()
|
|
208
|
+
)));
|
|
209
|
+
}
|
|
210
|
+
names.reserve_global(helper.name(), &mut scoping, allocator);
|
|
211
|
+
}
|
|
212
|
+
if !claimed.is_empty() {
|
|
213
|
+
names.reserve_global("Object", &mut scoping, allocator);
|
|
214
|
+
}
|
|
215
|
+
if lowers_dynamic_import {
|
|
216
|
+
names.reserve_global("Promise", &mut scoping, allocator);
|
|
217
|
+
}
|
|
218
|
+
// CommonJS's own names, which the module's code now runs beside:
|
|
219
|
+
// esbuild renames a module's `require`, `module` and `exports`
|
|
220
|
+
// whenever it converts one, used or not.
|
|
221
|
+
if self.to_cjs {
|
|
222
|
+
for name in ["require", "module", "exports"] {
|
|
223
|
+
names.reserve_global(name, &mut scoping, allocator);
|
|
224
|
+
}
|
|
225
|
+
} else if lowers_dynamic_import {
|
|
226
|
+
names.reserve_global("require", &mut scoping, allocator);
|
|
227
|
+
}
|
|
228
|
+
self.names = Some(names);
|
|
229
|
+
if !self.errors.is_empty() {
|
|
230
|
+
return (scoping, Err(self.errors));
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
let scoping = traverse_mut(&mut self, allocator, program, scoping, ());
|
|
235
|
+
if !self.to_cjs && self.names.is_some() {
|
|
236
|
+
Self::alias_renamed_exports(program, &scoping, allocator);
|
|
237
|
+
}
|
|
238
|
+
if self.options.format == Format::Cjs
|
|
239
|
+
&& let Some(span) = self.top_level_await
|
|
240
|
+
{
|
|
241
|
+
self.errors.push(OxcDiagnostic::error(crate::TOP_LEVEL_AWAIT_CJS).with_label(span));
|
|
242
|
+
}
|
|
243
|
+
if !self.errors.is_empty() {
|
|
244
|
+
return (scoping, Err(self.errors));
|
|
245
|
+
}
|
|
246
|
+
self.finish(allocator, program, &scoping);
|
|
247
|
+
(scoping, Ok(self.warnings))
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/// `export const Promise = 1`, its binding renamed out of the way of a
|
|
251
|
+
/// global the output reads: `const Promise2 = 1; export { Promise2 as
|
|
252
|
+
/// Promise }`, so the module's exports keep their names.
|
|
253
|
+
fn alias_renamed_exports(program: &mut Program<'a>, scoping: &Scoping, allocator: &'a Allocator) {
|
|
254
|
+
// Every name the declaration binds, as (its spelling now, its export
|
|
255
|
+
// name), once any of them was renamed: the declaration loses `export`,
|
|
256
|
+
// so each of its names needs a specifier.
|
|
257
|
+
let renamed = |decl: &Declaration<'a>| {
|
|
258
|
+
let mut pairs = Vec::new();
|
|
259
|
+
let mut any = false;
|
|
260
|
+
decl.bound_names(&mut |ident: &BindingIdentifier<'a>| {
|
|
261
|
+
let local = ident.symbol_id.get().map_or(ident.name.as_str(), |symbol| scoping.symbol_name(symbol));
|
|
262
|
+
any |= local != ident.name.as_str();
|
|
263
|
+
pairs.push((Ident::from(local), ident.name));
|
|
264
|
+
});
|
|
265
|
+
if !any {
|
|
266
|
+
pairs.clear();
|
|
267
|
+
}
|
|
268
|
+
pairs
|
|
269
|
+
};
|
|
270
|
+
let needs = program.body.iter().any(|s| matches!(s, Statement::ExportDeclaration(e) if !renamed(&e.declaration).is_empty()));
|
|
271
|
+
if !needs {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
let ast = AstBuilder::new(allocator);
|
|
275
|
+
let body = program.body.take_in(&allocator);
|
|
276
|
+
let mut kept = ArenaVec::with_capacity_in(body.len() + 1, &allocator);
|
|
277
|
+
for statement in body {
|
|
278
|
+
let Statement::ExportDeclaration(export) = statement else {
|
|
279
|
+
kept.push(statement);
|
|
280
|
+
continue;
|
|
281
|
+
};
|
|
282
|
+
let pairs = renamed(&export.declaration);
|
|
283
|
+
if pairs.is_empty() {
|
|
284
|
+
kept.push(Statement::ExportDeclaration(export));
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
let ExportDeclaration { span, declaration, .. } = export.unbox();
|
|
288
|
+
let specifiers = ArenaVec::from_iter_in(
|
|
289
|
+
pairs.into_iter().map(|(local, exported)| {
|
|
290
|
+
let local = ModuleExportName::IdentifierReference(IdentifierReference::new(
|
|
291
|
+
SPAN,
|
|
292
|
+
Ident::from_str_in(&local, &allocator),
|
|
293
|
+
&ast,
|
|
294
|
+
));
|
|
295
|
+
let exported = ModuleExportName::IdentifierName(IdentifierName::new(SPAN, exported, &ast));
|
|
296
|
+
ExportSpecifier::new(SPAN, local, exported, ImportOrExportKind::Value, &ast)
|
|
297
|
+
}),
|
|
298
|
+
&allocator,
|
|
299
|
+
);
|
|
300
|
+
kept.push(Statement::from(declaration));
|
|
301
|
+
kept.push(Statement::new_export_named_declaration(span, specifiers, ImportOrExportKind::Value, &ast));
|
|
302
|
+
}
|
|
303
|
+
program.body = kept;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/// An ES module printed as one has no empty export lists: `export {}` goes,
|
|
307
|
+
/// and `export {} from "x"` keeps only its import, as esbuild prints them.
|
|
308
|
+
fn drop_empty_exports(program: &mut Program<'a>, allocator: &'a Allocator) {
|
|
309
|
+
let ast = AstBuilder::new(allocator);
|
|
310
|
+
let body = program.body.take_in(&allocator);
|
|
311
|
+
let mut kept = ArenaVec::with_capacity_in(body.len(), &allocator);
|
|
312
|
+
for statement in body {
|
|
313
|
+
match statement {
|
|
314
|
+
Statement::ExportNamedDeclaration(e) if e.specifiers.is_empty() => {}
|
|
315
|
+
Statement::ExportFromDeclaration(e) if e.specifiers.is_empty() => {
|
|
316
|
+
let ExportFromDeclaration { span, source, with_clause, .. } = e.unbox();
|
|
317
|
+
kept.push(Statement::new_import_declaration(
|
|
318
|
+
span,
|
|
319
|
+
None,
|
|
320
|
+
source,
|
|
321
|
+
None,
|
|
322
|
+
with_clause,
|
|
323
|
+
ImportOrExportKind::Value,
|
|
324
|
+
&ast,
|
|
325
|
+
));
|
|
326
|
+
}
|
|
327
|
+
statement => kept.push(statement),
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
program.body = kept;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/// Take the module declarations out of the body, recording what each
|
|
334
|
+
/// imports and exports.
|
|
335
|
+
fn plan_cjs(
|
|
336
|
+
&mut self,
|
|
337
|
+
program: &mut Program<'a>,
|
|
338
|
+
scoping: &mut Scoping,
|
|
339
|
+
names: &mut Names,
|
|
340
|
+
allocator: &'a Allocator,
|
|
341
|
+
) {
|
|
342
|
+
let ast = AstBuilder::new(allocator);
|
|
343
|
+
if self.options.has_export_syntax {
|
|
344
|
+
self.exports_name = Some(names.generate("stdin_exports", scoping, allocator));
|
|
345
|
+
self.helpers.add(Helper::ToCommonJs);
|
|
346
|
+
}
|
|
347
|
+
let body = program.body.take_in(&allocator);
|
|
348
|
+
let mut kept = ArenaVec::with_capacity_in(body.len(), &allocator);
|
|
349
|
+
// Local exports name symbols that an import may bind; resolved once every import is known.
|
|
350
|
+
for statement in body {
|
|
351
|
+
match statement {
|
|
352
|
+
Statement::ImportDeclaration(decl) => {
|
|
353
|
+
let decl = decl.unbox();
|
|
354
|
+
self.plan_import(decl, scoping, names, allocator);
|
|
355
|
+
}
|
|
356
|
+
Statement::ExportAllDeclaration(decl) => {
|
|
357
|
+
let source = decl.source.value.to_string();
|
|
358
|
+
match &decl.exported {
|
|
359
|
+
None => {
|
|
360
|
+
self.helpers.add(Helper::ReExport);
|
|
361
|
+
self.records.push(Record::ReExport { source });
|
|
362
|
+
}
|
|
363
|
+
Some(exported) => {
|
|
364
|
+
let exported = exported.name().to_string();
|
|
365
|
+
let name = names.generate(&ensure_valid_identifier(&exported), scoping, allocator);
|
|
366
|
+
self.helpers.add(Helper::ToEsm);
|
|
367
|
+
self.records.push(Record::Require {
|
|
368
|
+
name: RecordName::Generated(name.clone()),
|
|
369
|
+
source,
|
|
370
|
+
to_esm: true,
|
|
371
|
+
});
|
|
372
|
+
self.exports.push((exported, ExportValue::Generated(name)));
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
Statement::ExportFromDeclaration(decl) => {
|
|
377
|
+
let to_esm = decl.specifiers.iter().any(|s| s.local.name() == "default");
|
|
378
|
+
let source = decl.source.value.to_string();
|
|
379
|
+
let name = names.generate(&format!("import_{}", name_from_path(&source)), scoping, allocator);
|
|
380
|
+
if to_esm {
|
|
381
|
+
self.helpers.add(Helper::ToEsm);
|
|
382
|
+
}
|
|
383
|
+
let record = RecordName::Generated(name);
|
|
384
|
+
self.records.push(Record::Require { name: record.clone(), source, to_esm });
|
|
385
|
+
for specifier in &decl.specifiers {
|
|
386
|
+
self.exports.push((
|
|
387
|
+
specifier.exported.name().to_string(),
|
|
388
|
+
ExportValue::Member(record.clone(), specifier.local.name().to_string()),
|
|
389
|
+
));
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
Statement::ExportNamedDeclaration(decl) => {
|
|
393
|
+
for specifier in &decl.specifiers {
|
|
394
|
+
let exported = specifier.exported.name().to_string();
|
|
395
|
+
let ModuleExportName::IdentifierReference(local) = &specifier.local else {
|
|
396
|
+
continue;
|
|
397
|
+
};
|
|
398
|
+
let symbol = local.reference_id.get().and_then(|id| scoping.get_reference(id).symbol_id());
|
|
399
|
+
match symbol {
|
|
400
|
+
Some(symbol) => self.exports.push((exported, ExportValue::Symbol(symbol))),
|
|
401
|
+
None => self.errors.push(
|
|
402
|
+
OxcDiagnostic::error(format!("\"{}\" is not declared in this file", local.name))
|
|
403
|
+
.with_label(local.span),
|
|
404
|
+
),
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
Statement::ExportDeclaration(decl) => {
|
|
409
|
+
let decl = decl.unbox();
|
|
410
|
+
let mut bound = Vec::new();
|
|
411
|
+
decl.declaration.bound_names(&mut |ident: &BindingIdentifier<'a>| {
|
|
412
|
+
if let Some(symbol) = ident.symbol_id.get() {
|
|
413
|
+
bound.push((ident.name.to_string(), symbol));
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
for (name, symbol) in bound {
|
|
417
|
+
self.exports.push((name, ExportValue::Symbol(symbol)));
|
|
418
|
+
}
|
|
419
|
+
kept.push(Statement::from(decl.declaration));
|
|
420
|
+
}
|
|
421
|
+
Statement::ExportDefaultDeclaration(decl) => {
|
|
422
|
+
let ExportDefaultDeclaration { span, declaration, .. } = decl.unbox();
|
|
423
|
+
match declaration {
|
|
424
|
+
ExportDefaultDeclarationKind::FunctionDeclaration(mut func) => {
|
|
425
|
+
let value = match func.id.as_ref().and_then(|id| id.symbol_id.get()) {
|
|
426
|
+
Some(symbol) => ExportValue::Symbol(symbol),
|
|
427
|
+
None => {
|
|
428
|
+
let name = names.generate("stdin_default", scoping, allocator);
|
|
429
|
+
func.id = Some(BindingIdentifier::new(SPAN, Ident::from_str_in(&name, &ast), &ast));
|
|
430
|
+
ExportValue::Generated(name)
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
self.exports.push(("default".into(), value));
|
|
434
|
+
kept.push(Statement::FunctionDeclaration(func));
|
|
435
|
+
}
|
|
436
|
+
ExportDefaultDeclarationKind::ClassDeclaration(mut class) => {
|
|
437
|
+
let value = match class.id.as_ref().and_then(|id| id.symbol_id.get()) {
|
|
438
|
+
Some(symbol) => ExportValue::Symbol(symbol),
|
|
439
|
+
None => {
|
|
440
|
+
let name = names.generate("stdin_default", scoping, allocator);
|
|
441
|
+
class.id = Some(BindingIdentifier::new(SPAN, Ident::from_str_in(&name, &ast), &ast));
|
|
442
|
+
ExportValue::Generated(name)
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
self.exports.push(("default".into(), value));
|
|
446
|
+
kept.push(Statement::ClassDeclaration(class));
|
|
447
|
+
}
|
|
448
|
+
ExportDefaultDeclarationKind::TSInterfaceDeclaration(_) => {}
|
|
449
|
+
declaration => {
|
|
450
|
+
let expression = declaration.into_expression();
|
|
451
|
+
let name = names.generate("stdin_default", scoping, allocator);
|
|
452
|
+
let id = BindingIdentifier::new(SPAN, Ident::from_str_in(&name, &ast), &ast);
|
|
453
|
+
let declarator = VariableDeclarator::new(
|
|
454
|
+
SPAN,
|
|
455
|
+
BindingPattern::BindingIdentifier(ArenaBox::new_in(id, &allocator)),
|
|
456
|
+
None,
|
|
457
|
+
Some(expression),
|
|
458
|
+
false,
|
|
459
|
+
&ast,
|
|
460
|
+
);
|
|
461
|
+
kept.push(Statement::from(Declaration::new_variable_declaration(
|
|
462
|
+
span,
|
|
463
|
+
VariableDeclarationKind::Var,
|
|
464
|
+
[declarator],
|
|
465
|
+
false,
|
|
466
|
+
&ast,
|
|
467
|
+
)));
|
|
468
|
+
self.exports.push(("default".into(), ExportValue::Generated(name)));
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
Statement::TSExportAssignment(_) | Statement::TSNamespaceExportDeclaration(_) => {}
|
|
473
|
+
statement => kept.push(statement),
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
if !self.exports.is_empty() {
|
|
477
|
+
self.helpers.add(Helper::Export);
|
|
478
|
+
}
|
|
479
|
+
program.body = kept;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
fn plan_import(
|
|
483
|
+
&mut self,
|
|
484
|
+
decl: ImportDeclaration<'a>,
|
|
485
|
+
scoping: &mut Scoping,
|
|
486
|
+
names: &mut Names,
|
|
487
|
+
allocator: &'a Allocator,
|
|
488
|
+
) {
|
|
489
|
+
let source = decl.source.value.to_string();
|
|
490
|
+
let specifiers = decl.specifiers.unwrap_or_else(|| ArenaVec::new_in(&allocator));
|
|
491
|
+
let namespace = specifiers.iter().find_map(|s| match s {
|
|
492
|
+
ImportDeclarationSpecifier::ImportNamespaceSpecifier(ns) => ns.local.symbol_id.get(),
|
|
493
|
+
_ => None,
|
|
494
|
+
});
|
|
495
|
+
let to_esm = namespace.is_some()
|
|
496
|
+
|| specifiers.iter().any(|s| match s {
|
|
497
|
+
ImportDeclarationSpecifier::ImportDefaultSpecifier(_) => true,
|
|
498
|
+
ImportDeclarationSpecifier::ImportSpecifier(s) => s.imported.name() == "default",
|
|
499
|
+
ImportDeclarationSpecifier::ImportNamespaceSpecifier(_) => false,
|
|
500
|
+
});
|
|
501
|
+
let (record, record_ident) = match namespace {
|
|
502
|
+
Some(symbol) => (RecordName::Symbol(symbol), Ident::from_str_in(scoping.symbol_name(symbol), &allocator)),
|
|
503
|
+
None => {
|
|
504
|
+
let name = names.generate(&format!("import_{}", name_from_path(&source)), scoping, allocator);
|
|
505
|
+
let ident = Ident::from_str_in(&name, &allocator);
|
|
506
|
+
(RecordName::Generated(name), ident)
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
for specifier in &specifiers {
|
|
510
|
+
let (local, property) = match specifier {
|
|
511
|
+
ImportDeclarationSpecifier::ImportSpecifier(s) => (&s.local, s.imported.name().as_str()),
|
|
512
|
+
ImportDeclarationSpecifier::ImportDefaultSpecifier(s) => (&s.local, "default"),
|
|
513
|
+
ImportDeclarationSpecifier::ImportNamespaceSpecifier(_) => continue,
|
|
514
|
+
};
|
|
515
|
+
if let Some(symbol) = local.symbol_id.get() {
|
|
516
|
+
self.bindings.insert(
|
|
517
|
+
symbol,
|
|
518
|
+
ImportBinding {
|
|
519
|
+
record: record.clone(),
|
|
520
|
+
record_ident,
|
|
521
|
+
property: Ident::from_str_in(property, &allocator),
|
|
522
|
+
},
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
if to_esm {
|
|
527
|
+
self.helpers.add(Helper::ToEsm);
|
|
528
|
+
}
|
|
529
|
+
self.records.push(Record::Require { name: record, source, to_esm });
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/// Put the generated statements in front of the body: helpers, then the
|
|
533
|
+
/// exports object, then the imports, then `import_meta`.
|
|
534
|
+
fn finish(&mut self, allocator: &'a Allocator, program: &mut Program<'a>, scoping: &Scoping) {
|
|
535
|
+
// A module with top-level await is an ES module, whatever else it reads.
|
|
536
|
+
let wrap = self.wrap_commonjs && self.top_level_await.is_none();
|
|
537
|
+
if wrap {
|
|
538
|
+
self.helpers.add(Helper::CommonJs);
|
|
539
|
+
}
|
|
540
|
+
let mut text = self.helpers.source();
|
|
541
|
+
let record_name = |name: &RecordName| match name {
|
|
542
|
+
RecordName::Generated(name) => name.clone(),
|
|
543
|
+
RecordName::Symbol(symbol) => scoping.symbol_name(*symbol).to_string(),
|
|
544
|
+
};
|
|
545
|
+
if let Some(exports) = &self.exports_name {
|
|
546
|
+
text.push_str(&format!("var {exports} = {{}};\n"));
|
|
547
|
+
if !self.exports.is_empty() {
|
|
548
|
+
self.exports.sort_by(|a, b| a.0.cmp(&b.0));
|
|
549
|
+
text.push_str(&format!("__export({exports}, {{\n"));
|
|
550
|
+
for (i, (exported, value)) in self.exports.iter().enumerate() {
|
|
551
|
+
let getter = match value {
|
|
552
|
+
ExportValue::Symbol(symbol) => match self.bindings.get(symbol) {
|
|
553
|
+
Some(binding) => member_text(&record_name(&binding.record), &binding.property),
|
|
554
|
+
None => scoping.symbol_name(*symbol).to_string(),
|
|
555
|
+
},
|
|
556
|
+
ExportValue::Generated(name) => name.clone(),
|
|
557
|
+
ExportValue::Member(record, property) => member_text(&record_name(record), property),
|
|
558
|
+
};
|
|
559
|
+
// `__proto__: ...` would set the getters object's prototype
|
|
560
|
+
// (esbuild prints it so, and loses the export).
|
|
561
|
+
let key = if exported == "__proto__" {
|
|
562
|
+
"[\"__proto__\"]".to_string()
|
|
563
|
+
} else if is_identifier_name(exported) {
|
|
564
|
+
exported.clone()
|
|
565
|
+
} else {
|
|
566
|
+
js_string(exported)
|
|
567
|
+
};
|
|
568
|
+
let comma = if i + 1 < self.exports.len() { "," } else { "" };
|
|
569
|
+
text.push_str(&format!(" {key}: () => {getter}{comma}\n"));
|
|
570
|
+
}
|
|
571
|
+
text.push_str("});\n");
|
|
572
|
+
}
|
|
573
|
+
text.push_str(&format!("module.exports = __toCommonJS({exports});\n"));
|
|
574
|
+
}
|
|
575
|
+
for record in &self.records {
|
|
576
|
+
match record {
|
|
577
|
+
Record::Require { name, source, to_esm } => {
|
|
578
|
+
let name = record_name(name);
|
|
579
|
+
let source = js_string(source);
|
|
580
|
+
if *to_esm {
|
|
581
|
+
text.push_str(&format!("var {name} = __toESM(require({source}));\n"));
|
|
582
|
+
} else {
|
|
583
|
+
text.push_str(&format!("var {name} = require({source});\n"));
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
Record::ReExport { source } => {
|
|
587
|
+
let exports = self.exports_name.as_deref().expect("export * has an exports object");
|
|
588
|
+
text.push_str(&format!("__reExport({exports}, require({}), module.exports);\n", js_string(source)));
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
if let Some(name) = self.import_meta_name {
|
|
593
|
+
text.push_str(&format!("const {name} = {{}};\n"));
|
|
594
|
+
}
|
|
595
|
+
if wrap {
|
|
596
|
+
self.wrap_commonjs_body(allocator, program, &text);
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
if text.is_empty() {
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
let prologue = parse_statements(allocator, text);
|
|
603
|
+
program.body.splice(0..0, prologue);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/// `var require_stdin = __commonJS({ "<stdin>"(exports, module) { body } }); export default require_stdin();`,
|
|
607
|
+
/// with the module's imports left in front.
|
|
608
|
+
fn wrap_commonjs_body(&mut self, allocator: &'a Allocator, program: &mut Program<'a>, prologue: &str) {
|
|
609
|
+
let (mut imports, mut body): (Vec<_>, Vec<_>) = program
|
|
610
|
+
.body
|
|
611
|
+
.take_in(&allocator)
|
|
612
|
+
.into_iter()
|
|
613
|
+
.partition(|s| matches!(s, Statement::ImportDeclaration(_)));
|
|
614
|
+
let mut text = prologue.to_string();
|
|
615
|
+
let name = self.wrapper_name.as_deref().expect("a wrapped module has a wrapper name");
|
|
616
|
+
let [exports, module] = &self.wrapper_params;
|
|
617
|
+
text.push_str(&format!(
|
|
618
|
+
"var {name} = __commonJS({{\n \"<stdin>\"({exports}, {module}) {{\n }}\n}});\nexport default {name}();\n"
|
|
619
|
+
));
|
|
620
|
+
let mut generated = parse_statements(allocator, text);
|
|
621
|
+
// The wrapper's `var` is the second-to-last statement.
|
|
622
|
+
let wrapper = generated.len() - 2;
|
|
623
|
+
if let Statement::VariableDeclaration(var) = &mut generated[wrapper]
|
|
624
|
+
&& let Some(Expression::CallExpression(call)) = &mut var.declarations[0].init
|
|
625
|
+
&& let Some(Argument::ObjectExpression(object)) = call.arguments.first_mut()
|
|
626
|
+
&& let Some(ObjectPropertyKind::ObjectProperty(property)) = object.properties.first_mut()
|
|
627
|
+
&& let Expression::FunctionExpression(function) = &mut property.value
|
|
628
|
+
&& let Some(function_body) = &mut function.body
|
|
629
|
+
{
|
|
630
|
+
function_body.statements.extend(body.drain(..));
|
|
631
|
+
}
|
|
632
|
+
let mut statements = ArenaVec::with_capacity_in(imports.len() + generated.len(), &allocator);
|
|
633
|
+
let helpers_len = generated.len() - 2;
|
|
634
|
+
let mut generated = generated.into_iter();
|
|
635
|
+
statements.extend(generated.by_ref().take(helpers_len));
|
|
636
|
+
statements.extend(imports.drain(..));
|
|
637
|
+
statements.extend(generated);
|
|
638
|
+
program.body = statements;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/// An assignment to an imported binding writes its record's property,
|
|
642
|
+
/// which throws as the assignment would have; esbuild warns, and so do we.
|
|
643
|
+
fn import_target(
|
|
644
|
+
&mut self,
|
|
645
|
+
ident: &IdentifierReference<'a>,
|
|
646
|
+
ctx: &mut TraverseCtx<'a>,
|
|
647
|
+
) -> Option<SimpleAssignmentTarget<'a>> {
|
|
648
|
+
let binding = ident
|
|
649
|
+
.reference_id
|
|
650
|
+
.get()
|
|
651
|
+
.and_then(|id| ctx.scoping().get_reference(id).symbol_id())
|
|
652
|
+
.and_then(|symbol| self.bindings.get(&symbol))
|
|
653
|
+
.cloned()?;
|
|
654
|
+
self.warnings.push(
|
|
655
|
+
OxcDiagnostic::warn(format!("This assignment will throw because \"{}\" is an import", ident.name))
|
|
656
|
+
.with_label(ident.span),
|
|
657
|
+
);
|
|
658
|
+
match binding_member(ident.span, &binding, ctx) {
|
|
659
|
+
Expression::StaticMemberExpression(member) => Some(SimpleAssignmentTarget::StaticMemberExpression(member)),
|
|
660
|
+
Expression::ComputedMemberExpression(member) => Some(SimpleAssignmentTarget::ComputedMemberExpression(member)),
|
|
661
|
+
_ => None,
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
fn rewrite_identifier(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
|
666
|
+
let Expression::Identifier(ident) = expr else { return };
|
|
667
|
+
let Some(binding) = ident
|
|
668
|
+
.reference_id
|
|
669
|
+
.get()
|
|
670
|
+
.and_then(|id| ctx.scoping().get_reference(id).symbol_id())
|
|
671
|
+
.and_then(|symbol| self.bindings.get(&symbol))
|
|
672
|
+
else {
|
|
673
|
+
return;
|
|
674
|
+
};
|
|
675
|
+
let span = ident.span;
|
|
676
|
+
let member = binding_member(span, binding, ctx);
|
|
677
|
+
// Called as a function or a tag, the binding is read off its record
|
|
678
|
+
// without becoming the receiver: `this` stays undefined, as in ESM.
|
|
679
|
+
*expr = if matches!(ctx.parent(), Ancestor::CallExpressionCallee(_) | Ancestor::TaggedTemplateExpressionTag(_)) {
|
|
680
|
+
let zero = Expression::new_numeric_literal(SPAN, 0.0, None, NumberBase::Decimal, ctx);
|
|
681
|
+
Expression::new_sequence_expression(span, [zero, member], ctx)
|
|
682
|
+
} else {
|
|
683
|
+
member
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
fn import_meta(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
|
688
|
+
let Expression::ImportMeta(meta) = expr else { return };
|
|
689
|
+
if self.options.format != Format::Cjs && self.options.import_meta {
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
let span = meta.span;
|
|
693
|
+
let name = match self.import_meta_name {
|
|
694
|
+
Some(name) => name,
|
|
695
|
+
None => {
|
|
696
|
+
let allocator = ctx.allocator();
|
|
697
|
+
let names = self.names.get_or_insert_with(|| Names::new(ctx.scoping()));
|
|
698
|
+
let name = names.generate("import_meta", ctx.scoping_mut(), allocator);
|
|
699
|
+
let ident = Ident::from_str_in(&name, ctx);
|
|
700
|
+
self.import_meta_name = Some(ident);
|
|
701
|
+
ident
|
|
702
|
+
}
|
|
703
|
+
};
|
|
704
|
+
self.warnings.push(
|
|
705
|
+
OxcDiagnostic::warn(if self.options.format == Format::Cjs {
|
|
706
|
+
"\"import.meta\" is not available with the \"cjs\" output format and will be empty"
|
|
707
|
+
} else {
|
|
708
|
+
"\"import.meta\" is not available in the configured target environment and will be empty"
|
|
709
|
+
})
|
|
710
|
+
.with_label(span),
|
|
711
|
+
);
|
|
712
|
+
*expr = Expression::new_identifier(span, name, ctx);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
/// `import(x)` → `Promise.resolve().then(() => __toESM(require(x)))`.
|
|
716
|
+
fn lower_dynamic_import(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
|
717
|
+
if self.options.dynamic_import {
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
let Expression::ImportExpression(import) = expr else { return };
|
|
721
|
+
let span = import.span;
|
|
722
|
+
let allocator = ctx.allocator();
|
|
723
|
+
let source = import.source.take_in(&allocator);
|
|
724
|
+
let mut lowered = parse_expression(allocator, "Promise.resolve().then(() => __toESM(require(0)))");
|
|
725
|
+
if let Expression::CallExpression(then) = &mut lowered
|
|
726
|
+
&& let Some(Argument::ArrowFunctionExpression(arrow)) = then.arguments.first_mut()
|
|
727
|
+
&& let ArrowFunctionBody::CallExpression(to_esm) = &mut arrow.body
|
|
728
|
+
&& let Some(Argument::CallExpression(require)) = to_esm.arguments.first_mut()
|
|
729
|
+
&& let Some(argument) = require.arguments.first_mut()
|
|
730
|
+
{
|
|
731
|
+
*argument = Argument::from(source);
|
|
732
|
+
}
|
|
733
|
+
if let Expression::CallExpression(then) = &mut lowered {
|
|
734
|
+
then.span = span;
|
|
735
|
+
}
|
|
736
|
+
*expr = lowered;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
impl<'a> Traverse<'a, ()> for ModulePass<'a> {
|
|
741
|
+
fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
|
742
|
+
match expr {
|
|
743
|
+
Expression::Identifier(_) if !self.bindings.is_empty() => self.rewrite_identifier(expr, ctx),
|
|
744
|
+
Expression::ImportMeta(_) => self.import_meta(expr, ctx),
|
|
745
|
+
// A CommonJS module's `this` is its `exports`, imports or not; the
|
|
746
|
+
// wrapper is called without a receiver, so it says so (esbuild too).
|
|
747
|
+
Expression::ThisExpression(this) if self.wrap_commonjs && this_is_module_level(ctx) => {
|
|
748
|
+
let exports = Ident::from_str_in(&self.wrapper_params[0], ctx);
|
|
749
|
+
*expr = Expression::new_identifier(this.span, exports, ctx);
|
|
750
|
+
}
|
|
751
|
+
Expression::ThisExpression(this) if self.is_esm && this_is_module_level(ctx) => {
|
|
752
|
+
let span = this.span;
|
|
753
|
+
let zero = Expression::new_numeric_literal(SPAN, 0.0, None, NumberBase::Decimal, ctx);
|
|
754
|
+
*expr = Expression::new_unary_expression(span, UnaryOperator::Void, zero, ctx);
|
|
755
|
+
}
|
|
756
|
+
_ => {}
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
// On exit: the lowering's arrow function has no scope of its own to walk
|
|
761
|
+
// into, and the source it wraps has had its own uses rewritten.
|
|
762
|
+
fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
|
763
|
+
match expr {
|
|
764
|
+
Expression::ImportExpression(import) => {
|
|
765
|
+
// esbuild reads an import path written as a template with
|
|
766
|
+
// nothing substituted as the string it is, and prints it so;
|
|
767
|
+
// so does a CommonJS output's `require(`x`)`, below.
|
|
768
|
+
template_as_string(&mut import.source, ctx);
|
|
769
|
+
self.lower_dynamic_import(expr, ctx);
|
|
770
|
+
}
|
|
771
|
+
Expression::CallExpression(call)
|
|
772
|
+
if self.options.format == Format::Cjs
|
|
773
|
+
&& call.arguments.len() == 1
|
|
774
|
+
&& matches!(&call.callee, Expression::Identifier(id) if id.name == "require"
|
|
775
|
+
&& id.reference_id.get().is_some_and(|r| ctx.scoping().get_reference(r).symbol_id().is_none())) =>
|
|
776
|
+
{
|
|
777
|
+
if let Some(argument) = call.arguments[0].as_expression_mut() {
|
|
778
|
+
template_as_string(argument, ctx);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
_ => {}
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
fn enter_simple_assignment_target(
|
|
786
|
+
&mut self,
|
|
787
|
+
target: &mut SimpleAssignmentTarget<'a>,
|
|
788
|
+
ctx: &mut TraverseCtx<'a>,
|
|
789
|
+
) {
|
|
790
|
+
let SimpleAssignmentTarget::AssignmentTargetIdentifier(ident) = target else { return };
|
|
791
|
+
if let Some(member) = self.import_target(ident, ctx) {
|
|
792
|
+
*target = member;
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
/// `({ a } = o)` with `a` imported: `({ a: import_x.a } = o)`.
|
|
797
|
+
fn enter_assignment_target_property(
|
|
798
|
+
&mut self,
|
|
799
|
+
property: &mut AssignmentTargetProperty<'a>,
|
|
800
|
+
ctx: &mut TraverseCtx<'a>,
|
|
801
|
+
) {
|
|
802
|
+
let AssignmentTargetProperty::AssignmentTargetPropertyIdentifier(shorthand) = property else { return };
|
|
803
|
+
let Some(member) = self.import_target(&shorthand.binding, ctx) else { return };
|
|
804
|
+
let span = shorthand.span;
|
|
805
|
+
let name = PropertyKey::new_static_identifier(shorthand.binding.span, shorthand.binding.name, ctx);
|
|
806
|
+
let binding = match shorthand.init.take() {
|
|
807
|
+
Some(init) => AssignmentTargetMaybeDefault::new_assignment_target_with_default(span, member.into(), init, ctx),
|
|
808
|
+
None => member.into(),
|
|
809
|
+
};
|
|
810
|
+
*property = AssignmentTargetProperty::new_assignment_target_property_property(span, name, binding, false, ctx);
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
fn enter_function(&mut self, _: &mut Function<'a>, _: &mut TraverseCtx<'a>) {
|
|
814
|
+
self.function_depth += 1;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
fn exit_function(&mut self, _: &mut Function<'a>, _: &mut TraverseCtx<'a>) {
|
|
818
|
+
self.function_depth -= 1;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
fn enter_arrow_function_expression(&mut self, _: &mut ArrowFunctionExpression<'a>, _: &mut TraverseCtx<'a>) {
|
|
822
|
+
self.function_depth += 1;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
fn exit_arrow_function_expression(&mut self, _: &mut ArrowFunctionExpression<'a>, _: &mut TraverseCtx<'a>) {
|
|
826
|
+
self.function_depth -= 1;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
fn enter_await_expression(&mut self, expr: &mut AwaitExpression<'a>, _: &mut TraverseCtx<'a>) {
|
|
830
|
+
if self.function_depth == 0 && self.top_level_await.is_none() {
|
|
831
|
+
self.top_level_await = Some(expr.span);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
fn enter_for_of_statement(&mut self, stmt: &mut ForOfStatement<'a>, _: &mut TraverseCtx<'a>) {
|
|
836
|
+
if stmt.r#await && self.function_depth == 0 && self.top_level_await.is_none() {
|
|
837
|
+
self.top_level_await = Some(Span::new(stmt.span.start + 4, stmt.span.start + 9));
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
fn enter_variable_declaration(&mut self, decl: &mut VariableDeclaration<'a>, _: &mut TraverseCtx<'a>) {
|
|
842
|
+
if decl.kind == VariableDeclarationKind::AwaitUsing && self.function_depth == 0 && self.top_level_await.is_none() {
|
|
843
|
+
self.top_level_await = Some(decl.span);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
/// Whether the program awaits at its top level (outside every function).
|
|
849
|
+
pub fn has_top_level_await(program: &Program<'_>) -> bool {
|
|
850
|
+
struct Finder(bool);
|
|
851
|
+
impl<'a> oxc::ast_visit::Visit<'a> for Finder {
|
|
852
|
+
fn visit_function(&mut self, _: &Function<'a>, _: oxc::semantic::ScopeFlags) {}
|
|
853
|
+
fn visit_arrow_function_expression(&mut self, _: &ArrowFunctionExpression<'a>) {}
|
|
854
|
+
fn visit_await_expression(&mut self, _: &AwaitExpression<'a>) {
|
|
855
|
+
self.0 = true;
|
|
856
|
+
}
|
|
857
|
+
fn visit_for_of_statement(&mut self, it: &ForOfStatement<'a>) {
|
|
858
|
+
self.0 |= it.r#await;
|
|
859
|
+
oxc::ast_visit::walk::walk_for_of_statement(self, it);
|
|
860
|
+
}
|
|
861
|
+
fn visit_variable_declaration(&mut self, it: &VariableDeclaration<'a>) {
|
|
862
|
+
self.0 |= it.kind == VariableDeclarationKind::AwaitUsing;
|
|
863
|
+
oxc::ast_visit::walk::walk_variable_declaration(self, it);
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
let mut finder = Finder(false);
|
|
867
|
+
oxc::ast_visit::Visit::visit_program(&mut finder, program);
|
|
868
|
+
finder.0
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
/// Whether the module uses CommonJS's `name` (`module` or `exports`): reads
|
|
872
|
+
/// it as a global, or uses a top-level `var`/`function` of that name, which
|
|
873
|
+
/// in a CommonJS module is the wrapper's parameter (a `let` is not).
|
|
874
|
+
fn reads_commonjs_name(scoping: &Scoping, name: &str) -> bool {
|
|
875
|
+
if scoping.root_unresolved_references().contains_key(name) {
|
|
876
|
+
return true;
|
|
877
|
+
}
|
|
878
|
+
scoping.get_root_binding(name.into()).is_some_and(|symbol| {
|
|
879
|
+
let flags = scoping.symbol_flags(symbol);
|
|
880
|
+
(flags.is_function_scoped_declaration() || flags.is_function())
|
|
881
|
+
&& !scoping.get_resolved_reference_ids(symbol).is_empty()
|
|
882
|
+
})
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
/// Whether `this` here is the module's: no function, class field
|
|
886
|
+
/// initializer or static block lies between it and the top level (a class's
|
|
887
|
+
/// computed keys, `extends` and decorators evaluate outside the class).
|
|
888
|
+
fn this_is_module_level(ctx: &TraverseCtx<'_>) -> bool {
|
|
889
|
+
!ctx.ancestors().any(|ancestor| {
|
|
890
|
+
matches!(
|
|
891
|
+
ancestor,
|
|
892
|
+
Ancestor::FunctionParams(_)
|
|
893
|
+
| Ancestor::FunctionBody(_)
|
|
894
|
+
| Ancestor::PropertyDefinitionValue(_)
|
|
895
|
+
| Ancestor::AccessorPropertyValue(_)
|
|
896
|
+
| Ancestor::StaticBlockBody(_)
|
|
897
|
+
)
|
|
898
|
+
})
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
/// A template with nothing substituted, as the string literal it evaluates to.
|
|
902
|
+
fn template_as_string<'a>(expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
|
903
|
+
if let Expression::TemplateLiteral(template) = expr
|
|
904
|
+
&& template.expressions.is_empty()
|
|
905
|
+
&& !template.quasis[0].lone_surrogates
|
|
906
|
+
&& let Some(cooked) = template.quasis[0].value.cooked
|
|
907
|
+
{
|
|
908
|
+
*expr = Expression::new_string_literal(template.span, cooked, None, ctx);
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
/// `record.property`, or `record["property"]` for a name that is not an identifier.
|
|
913
|
+
fn binding_member<'a>(span: Span, binding: &ImportBinding<'a>, ctx: &mut TraverseCtx<'a>) -> Expression<'a> {
|
|
914
|
+
let object = match binding.record {
|
|
915
|
+
RecordName::Symbol(symbol) => {
|
|
916
|
+
ctx.create_bound_ident_expr(SPAN, binding.record_ident, symbol, oxc::semantic::ReferenceFlags::Read)
|
|
917
|
+
}
|
|
918
|
+
RecordName::Generated(_) => Expression::new_identifier(SPAN, binding.record_ident, ctx),
|
|
919
|
+
};
|
|
920
|
+
if is_identifier_name(&binding.property) {
|
|
921
|
+
let property = IdentifierName::new(SPAN, binding.property, ctx);
|
|
922
|
+
Expression::new_static_member_expression(span, object, property, false, ctx)
|
|
923
|
+
} else {
|
|
924
|
+
let key = Expression::new_string_literal(SPAN, binding.property.as_arena_str(), None, ctx);
|
|
925
|
+
Expression::new_computed_member_expression(span, object, key, false, ctx)
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
fn member_text(object: &str, property: &str) -> String {
|
|
930
|
+
if is_identifier_name(property) { format!("{object}.{property}") } else { format!("{object}[{}]", js_string(property)) }
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
/// A double-quoted JavaScript string literal.
|
|
934
|
+
fn js_string(value: &str) -> String {
|
|
935
|
+
let mut out = String::with_capacity(value.len() + 2);
|
|
936
|
+
out.push('"');
|
|
937
|
+
for c in value.chars() {
|
|
938
|
+
match c {
|
|
939
|
+
'"' => out.push_str("\\\""),
|
|
940
|
+
'\\' => out.push_str("\\\\"),
|
|
941
|
+
'\n' => out.push_str("\\n"),
|
|
942
|
+
'\r' => out.push_str("\\r"),
|
|
943
|
+
'\u{2028}' => out.push_str("\\u2028"),
|
|
944
|
+
'\u{2029}' => out.push_str("\\u2029"),
|
|
945
|
+
c if (c as u32) < 0x20 => out.push_str(&format!("\\x{:02X}", c as u32)),
|
|
946
|
+
c => out.push(c),
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
out.push('"');
|
|
950
|
+
out
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
/// Parse generated statements into the module's arena, without positions:
|
|
954
|
+
/// they have no place in the source, so neither comments nor source maps may
|
|
955
|
+
/// attach to them.
|
|
956
|
+
fn parse_statements<'a>(allocator: &'a Allocator, text: String) -> ArenaVec<'a, Statement<'a>> {
|
|
957
|
+
let text = allocator.alloc_str(&text);
|
|
958
|
+
let mut parsed = Parser::new(allocator, text, SourceType::mjs()).parse();
|
|
959
|
+
debug_assert!(parsed.diagnostics.is_empty(), "generated code parses: {text}");
|
|
960
|
+
ZeroSpans.visit_program(&mut parsed.program);
|
|
961
|
+
parsed.program.body
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
fn parse_expression<'a>(allocator: &'a Allocator, text: &'static str) -> Expression<'a> {
|
|
965
|
+
let mut statements = parse_statements(allocator, text.to_string());
|
|
966
|
+
match statements.pop() {
|
|
967
|
+
Some(Statement::ExpressionStatement(statement)) => statement.unbox().expression,
|
|
968
|
+
_ => unreachable!("an expression statement"),
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
struct ZeroSpans;
|
|
973
|
+
|
|
974
|
+
impl<'a> VisitMut<'a> for ZeroSpans {
|
|
975
|
+
fn visit_span(&mut self, span: &mut Span) {
|
|
976
|
+
*span = SPAN;
|
|
977
|
+
}
|
|
978
|
+
}
|