aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/macros/components/example.jsx17
-rw-r--r--examples/macros/components/index.tsx11
-rw-r--r--examples/macros/example.js4
-rw-r--r--examples/macros/matchInFile.tsx20
-rw-r--r--examples/macros/now.tsx10
-rw-r--r--examples/macros/package.json12
-rw-r--r--examples/macros/public/index.html14
-rw-r--r--examples/macros/styles.css26
-rw-r--r--examples/macros/tsconfig.json7
-rw-r--r--package.json5
-rw-r--r--src/ast/ast.js106
-rw-r--r--src/ast/ast.ts153
-rw-r--r--src/bundler.zig1
-rw-r--r--src/feature_flags.zig5
-rw-r--r--src/javascript/jsc/base.zig4
-rw-r--r--src/javascript/jsc/bindings/ZigGlobalObject.cpp22
-rw-r--r--src/javascript/jsc/bindings/bindings.cpp11
-rw-r--r--src/javascript/jsc/bindings/bindings.zig51
-rw-r--r--src/javascript/jsc/bindings/headers-cpp.h2
-rw-r--r--src/javascript/jsc/bindings/headers.h5
-rw-r--r--src/javascript/jsc/bindings/headers.zig54
-rw-r--r--src/javascript/jsc/javascript.zig249
-rw-r--r--src/js_ast.zig2361
-rw-r--r--src/js_lexer.zig10
-rw-r--r--src/js_parser/js_parser.zig326
-rw-r--r--src/js_printer.zig13
-rw-r--r--src/runtime.zig1
-rw-r--r--src/string_immutable.zig8
28 files changed, 2914 insertions, 594 deletions
diff --git a/examples/macros/components/example.jsx b/examples/macros/components/example.jsx
new file mode 100644
index 000000000..ad80ce9e1
--- /dev/null
+++ b/examples/macros/components/example.jsx
@@ -0,0 +1,17 @@
+// source code
+import { matchInFile } from "macro:matchInFile";
+
+export const IPAddresses = () => (
+ <div>
+ <h2>recent ip addresses</h2>
+ <div className="Lines">
+ {matchInFile("access.log", /^(?:[0-9]{1,3}\.){3}[0-9]{1,3}/).map(
+ (ipAddress, index) => (
+ <div className="Line" key={index}>
+ {ipAddress}
+ </div>
+ )
+ )}
+ </div>
+ </div>
+);
diff --git a/examples/macros/components/index.tsx b/examples/macros/components/index.tsx
new file mode 100644
index 000000000..6c3e39be7
--- /dev/null
+++ b/examples/macros/components/index.tsx
@@ -0,0 +1,11 @@
+import * as ReactDOM from "react-dom";
+import * as React from "react";
+import { IPAddresses } from "./example";
+
+const Start = function () {
+ const root = document.createElement("div");
+ document.body.appendChild(root);
+ ReactDOM.render(<IPAddresses />, root);
+};
+
+Start();
diff --git a/examples/macros/example.js b/examples/macros/example.js
new file mode 100644
index 000000000..d612c1fa4
--- /dev/null
+++ b/examples/macros/example.js
@@ -0,0 +1,4 @@
+// source code
+import { mysteryBox } from "macro:./mystery-box";
+
+export default "You roll! " + mysteryBox(123);
diff --git a/examples/macros/matchInFile.tsx b/examples/macros/matchInFile.tsx
new file mode 100644
index 000000000..a73f7ee8b
--- /dev/null
+++ b/examples/macros/matchInFile.tsx
@@ -0,0 +1,20 @@
+// macro code
+export function matchInFile(callExpression) {
+ const [filePathNode, matcherNode] = callExpression.arguments;
+ const filePath: string = filePathNode.get();
+ const matcher: RegExp = matcherNode.get();
+ const file: string = Bun.readFile(Bun.cwd + filePath);
+
+ return (
+ <array>
+ {file
+ .split("\n")
+ .map((line) => line.match(matcher))
+ .filter(Boolean)
+ .reverse()
+ .map((line) => (
+ <string value={line[0]} />
+ ))}
+ </array>
+ );
+}
diff --git a/examples/macros/now.tsx b/examples/macros/now.tsx
new file mode 100644
index 000000000..d5a9e7912
--- /dev/null
+++ b/examples/macros/now.tsx
@@ -0,0 +1,10 @@
+import moment from "moment";
+export function now(node) {
+ var fmt = "HH:mm:ss";
+ const args = node.arguments;
+ if (args[0] instanceof <string />) {
+ fmt = args[0].get();
+ }
+ const time = moment().format(fmt);
+ return <string value={time}></string>;
+}
diff --git a/examples/macros/package.json b/examples/macros/package.json
new file mode 100644
index 000000000..de4e26453
--- /dev/null
+++ b/examples/macros/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "macros",
+ "version": "1.0.0",
+ "main": "index.js",
+ "license": "MIT",
+ "dependencies": {
+ "moment": "^2.29.1",
+ "react": "^17.0.2",
+ "react-dom": "^17.0.2",
+ "react-refresh": "^0.10.0"
+ }
+}
diff --git a/examples/macros/public/index.html b/examples/macros/public/index.html
new file mode 100644
index 000000000..a2a985b72
--- /dev/null
+++ b/examples/macros/public/index.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Macro test</title>
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+
+ <link rel="stylesheet" href="/styles.css" type="text/css" />
+ </head>
+
+ <body>
+ <script async type="module" src="/src/index.tsx"></script>
+ </body>
+</html>
diff --git a/examples/macros/styles.css b/examples/macros/styles.css
new file mode 100644
index 000000000..87f0ef1a8
--- /dev/null
+++ b/examples/macros/styles.css
@@ -0,0 +1,26 @@
+html {
+ font-size: 4rem;
+ margin: 0;
+ padding: 0;
+ background-color: black;
+
+ color: rgb(0, 255, 0);
+ font-family: "Courier";
+}
+
+body {
+ margin: 48px auto;
+ text-align: center;
+}
+
+.Line {
+ font-size: 0.5rem;
+ font-family: monospace;
+}
+
+.Lines {
+ text-align: left;
+ margin: 0 auto;
+ max-width: fit-content;
+ line-height: 1.5;
+}
diff --git a/examples/macros/tsconfig.json b/examples/macros/tsconfig.json
new file mode 100644
index 000000000..4a98f5cad
--- /dev/null
+++ b/examples/macros/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "compilerOptions": {
+ "baseUrl": ".",
+ "paths": {},
+ "jsx": "preserve"
+ }
+}
diff --git a/package.json b/package.json
index 6a5e8d71c..2052081df 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,10 @@
{
"dependencies": {
+ "moment": "^2.29.1",
"peechy": "^0.4.18",
- "puppeteer": "^10.2.0"
+ "puppeteer": "^10.2.0",
+ "react": "^17.0.2",
+ "react-dom": "^17.0.2"
},
"scripts": {
"build-runtime": "esbuild --target=esnext --bundle src/runtime/index.ts --format=iife --platform=browser --global-name=BUN_RUNTIME > src/runtime.out.js; cat src/runtime.footer.js >> src/runtime.out.js",
diff --git a/src/ast/ast.js b/src/ast/ast.js
deleted file mode 100644
index 06be333c5..000000000
--- a/src/ast/ast.js
+++ /dev/null
@@ -1,106 +0,0 @@
-globalThis.BunASTNode ??= class BunASTNode {
- position = -1;
-};
-
-if (!globalThis.BunAST) {
- globalThis.BunAST = {
- EArray: class EArray extends BunASTNode {
- #ptr = Number.MAX_SAFE_INTEGER;
- },
- EUnary: class EUnary extends BunASTNode {
- #ptr = Number.MAX_SAFE_INTEGER;
- },
- EBinary: class EBinary extends BunASTNode {
- #ptr = Number.MAX_SAFE_INTEGER;
- },
- EClass: class EClass extends BunASTNode {
- #ptr = Number.MAX_SAFE_INTEGER;
- },
- ENew: class ENew extends BunASTNode {
- #ptr = Number.MAX_SAFE_INTEGER;
- },
- EFunction: class EFunction extends BunASTNode {
- #ptr = Number.MAX_SAFE_INTEGER;
- },
- ECall: class ECall extends BunASTNode {
- #ptr = Number.MAX_SAFE_INTEGER;
- },
- EDot: class EDot extends BunASTNode {
- #ptr = Number.MAX_SAFE_INTEGER;
- },
- EIndex: class EIndex extends BunASTNode {
- #ptr = Number.MAX_SAFE_INTEGER;
- },
- EArrow: class EArrow extends BunASTNode {
- #ptr = Number.MAX_SAFE_INTEGER;
- },
- EIdentifier: class EIdentifier extends BunASTNode {
- #ptr = Number.MAX_SAFE_INTEGER;
- },
- EImportIdentifier: class EImportIdentifier extends BunASTNode {
- #ptr = Number.MAX_SAFE_INTEGER;
- },
- EPrivateIdentifier: class EPrivateIdentifier extends BunASTNode {
- #ptr = Number.MAX_SAFE_INTEGER;
- },
- EJsxElement: class EJsxElement extends BunASTNode {
- #ptr = Number.MAX_SAFE_INTEGER;
- },
- EObject: class EObject extends BunASTNode {
- #ptr = Number.MAX_SAFE_INTEGER;
- },
- ESpread: class ESpread extends BunASTNode {
- #ptr = Number.MAX_SAFE_INTEGER;
- },
- ETemplatePart: class ETemplatePart extends BunASTNode {
- #ptr = Number.MAX_SAFE_INTEGER;
- },
- ETemplate: class ETemplate extends BunASTNode {
- #ptr = Number.MAX_SAFE_INTEGER;
- },
- ERegExp: class ERegExp extends BunASTNode {
- #ptr = Number.MAX_SAFE_INTEGER;
- },
- EAwait: class EAwait extends BunASTNode {
- #ptr = Number.MAX_SAFE_INTEGER;
- },
- EYield: class EYield extends BunASTNode {
- #ptr = Number.MAX_SAFE_INTEGER;
- },
- EIf: class EIf extends BunASTNode {
- no = Number.MAX_SAFE_INTEGER;
- yes = Number.MAX_SAFE_INTEGER;
- },
- ERequire: class ERequire extends BunASTNode {
- #ptr = Number.MAX_SAFE_INTEGER;
- },
- EImport: class EImport extends BunASTNode {
- #ptr = Number.MAX_SAFE_INTEGER;
- },
- EBoolean: class EBoolean extends BunASTNode {
- val = false;
- },
- ENumber: class ENumber extends BunASTNode {
- val = 0;
- },
- EBigInt: class EBigInt extends BunASTNode {
- #ptr = Number.MAX_SAFE_INTEGER;
- },
- EString: class EString extends BunASTNode {
- #ptr = Number.MAX_SAFE_INTEGER;
- },
- EMissing: class EMissing extends BunASTNode {
- #ptr = Number.MAX_SAFE_INTEGER;
- },
- EThis: class EThis extends BunASTNode {},
- ESuper: class ESuper extends BunASTNode {
- #ptr = Number.MAX_SAFE_INTEGER;
- },
- ENull: class ENull extends BunASTNode {},
- EUndefined: class EUndefined extends BunASTNode {},
- ENewTarget: class ENewTarget extends BunASTNode {
- #ptr = Number.MAX_SAFE_INTEGER;
- },
- EImportMeta: class EImportMeta extends BunASTNode {},
- };
-}
diff --git a/src/ast/ast.ts b/src/ast/ast.ts
new file mode 100644
index 000000000..2ab25644d
--- /dev/null
+++ b/src/ast/ast.ts
@@ -0,0 +1,153 @@
+class BunASTNode {
+ position = -1;
+}
+globalThis.BunASTNode = BunASTNode;
+// hint to JS engine to store it as a f64
+const NullPtrValue = Number.MAX_SAFE_INTEGER;
+const bindings = globalThis.BunASTBindings;
+
+const BunAST = {
+ EArray: class EArray extends BunASTNode {
+ #ptr = NullPtrValue;
+ },
+ EUnary: class EUnary extends BunASTNode {
+ #ptr = NullPtrValue;
+ },
+ EBinary: class EBinary extends BunASTNode {
+ #ptr = NullPtrValue;
+ },
+ EClass: class EClass extends BunASTNode {
+ #ptr = NullPtrValue;
+ },
+ ENew: class ENew extends BunASTNode {
+ #ptr = NullPtrValue;
+ },
+ EFunction: class EFunction extends BunASTNode {
+ #ptr = NullPtrValue;
+ },
+ ECall: class ECall extends BunASTNode {
+ #ptr = NullPtrValue;
+ },
+ EDot: class EDot extends BunASTNode {
+ #ptr = NullPtrValue;
+ },
+ EIndex: class EIndex extends BunASTNode {
+ #ptr = NullPtrValue;
+ },
+ EArrow: class EArrow extends BunASTNode {
+ #ptr = NullPtrValue;
+ },
+ EIdentifier: class EIdentifier extends BunASTNode {
+ #ptr = NullPtrValue;
+ },
+ EImportIdentifier: class EImportIdentifier extends BunASTNode {
+ #ptr = NullPtrValue;
+ },
+ EPrivateIdentifier: class EPrivateIdentifier extends BunASTNode {
+ #ptr = NullPtrValue;
+ },
+ EJsxElement: class EJsxElement extends BunASTNode {
+ #ptr = NullPtrValue;
+ },
+ EObject: class EObject extends BunASTNode {
+ #ptr = NullPtrValue;
+ },
+ ESpread: class ESpread extends BunASTNode {
+ #ptr = NullPtrValue;
+ },
+ ETemplatePart: class ETemplatePart extends BunASTNode {
+ #ptr = NullPtrValue;
+ },
+ ETemplate: class ETemplate extends BunASTNode {
+ #ptr = NullPtrValue;
+ },
+ ERegExp: class ERegExp extends BunASTNode {
+ #ptr = NullPtrValue;
+ },
+ EAwait: class EAwait extends BunASTNode {
+ #ptr = NullPtrValue;
+ },
+ EYield: class EYield extends BunASTNode {
+ #ptr = NullPtrValue;
+ },
+ EIf: class EIf extends BunASTNode {
+ no = NullPtrValue;
+ yes = NullPtrValue;
+ test = NullPtrValue;
+ },
+ ERequire: class ERequire extends BunASTNode {
+ #ptr = NullPtrValue;
+ },
+ EImport: class EImport extends BunASTNode {
+ #ptr = NullPtrValue;
+ },
+ EBoolean: class EBoolean extends BunASTNode {
+ val = false;
+ },
+ ENumber: class ENumber extends BunASTNode {
+ val = 0;
+ },
+ EBigInt: class EBigInt extends BunASTNode {
+ #ptr = NullPtrValue;
+ },
+ EString: class EString extends BunASTNode {
+ #ptr = NullPtrValue;
+ },
+ EMissing: class EMissing extends BunASTNode {
+ #ptr = NullPtrValue;
+ },
+ EThis: class EThis extends BunASTNode {},
+ ESuper: class ESuper extends BunASTNode {
+ #ptr = NullPtrValue;
+ },
+ ENull: class ENull extends BunASTNode {},
+ EUndefined: class EUndefined extends BunASTNode {},
+ ENewTarget: class ENewTarget extends BunASTNode {
+ #ptr = NullPtrValue;
+ },
+ EImportMeta: class EImportMeta extends BunASTNode {},
+ SImport: class SImport extends BunASTNode {
+ #ptr = NullPtrValue;
+ },
+};
+globalThis.BunAST = BunAST;
+const bunTags = [
+ BunAST.EArray,
+ BunAST.EUnary,
+ BunAST.EBinary,
+ BunAST.EClass,
+ BunAST.ENew,
+ BunAST.EFunction,
+ BunAST.ECall,
+ BunAST.EDot,
+ BunAST.EIndex,
+ BunAST.EArrow,
+ BunAST.EIdentifier,
+ BunAST.EImportIdentifier,
+ BunAST.EPrivateIdentifier,
+ BunAST.EJsxElement,
+ BunAST.EObject,
+ BunAST.ESpread,
+ BunAST.ETemplatePart,
+ BunAST.ETemplate,
+ BunAST.ERegExp,
+ BunAST.EAwait,
+ BunAST.EYield,
+ BunAST.EIf,
+ BunAST.ERequire,
+ BunAST.EImport,
+ BunAST.EBoolean,
+ BunAST.ENumber,
+ BunAST.EBigInt,
+ BunAST.EString,
+ BunAST.EMissing,
+ BunAST.EThis,
+ BunAST.ESuper,
+ BunAST.ENull,
+ BunAST.EUndefined,
+ BunAST.ENewTarget,
+ BunAST.EImportMeta,
+ BunAST.SImport,
+];
+globalThis.bunTags = bunTags;
+
diff --git a/src/bundler.zig b/src/bundler.zig
index d36795d52..64982c822 100644
--- a/src/bundler.zig
+++ b/src/bundler.zig
@@ -133,6 +133,7 @@ pub const Bundler = struct {
to.log = try allocator.create(logger.Log);
to.log.* = logger.Log.init(allocator);
to.setLog(to.log);
+ to.macro_context = null;
}
pub fn setLog(this: *ThisBundler, log: *logger.Log) void {
diff --git a/src/feature_flags.zig b/src/feature_flags.zig
index 9aa809879..8087cd6ba 100644
--- a/src/feature_flags.zig
+++ b/src/feature_flags.zig
@@ -68,4 +68,7 @@ pub const CSSInJSImportBehavior = enum {
pub const remote_inspector = false;
pub const auto_import_buffer = false;
-pub const is_macro_enabled = env.isDebug;
+pub const is_macro_enabled = true;
+
+pub const force_macro = false;
+pub const include_filename_in_jsx = false;
diff --git a/src/javascript/jsc/base.zig b/src/javascript/jsc/base.zig
index 36f00f59e..a5f2d336d 100644
--- a/src/javascript/jsc/base.zig
+++ b/src/javascript/jsc/base.zig
@@ -1539,7 +1539,7 @@ export fn MarkedArrayBuffer_deallocator(bytes_: *c_void, ctx_: *c_void) void {
pub fn castObj(obj: js.JSObjectRef, comptime Type: type) *Type {
return JSPrivateDataPtr.from(js.JSObjectGetPrivate(obj)).as(Type);
}
-const JSExpr = @import("../../js_ast.zig").Macro.JSExpr;
+const JSNode = @import("../../js_ast.zig").Macro.JSNode;
pub const JSPrivateDataPtr = TaggedPointerUnion(.{
ResolveError,
@@ -1550,7 +1550,7 @@ pub const JSPrivateDataPtr = TaggedPointerUnion(.{
Headers,
Body,
Router,
- JSExpr,
+ JSNode,
});
pub inline fn GetJSPrivateData(comptime Type: type, ref: js.JSObjectRef) ?*Type {
diff --git a/src/javascript/jsc/bindings/ZigGlobalObject.cpp b/src/javascript/jsc/bindings/ZigGlobalObject.cpp
index b7ebe53d2..712d1cc63 100644
--- a/src/javascript/jsc/bindings/ZigGlobalObject.cpp
+++ b/src/javascript/jsc/bindings/ZigGlobalObject.cpp
@@ -80,17 +80,21 @@ using JSObject = JSC::JSObject;
using JSNonFinalObject = JSC::JSNonFinalObject;
namespace JSCastingHelpers = JSC::JSCastingHelpers;
+bool has_loaded_jsc = false;
+
extern "C" JSC__JSGlobalObject *Zig__GlobalObject__create(JSClassRef *globalObjectClass, int count,
void *console_client) {
- JSC::Options::useSourceProviderCache() = true;
- JSC::Options::useUnlinkedCodeBlockJettisoning() = false;
- // JSC::Options::useTopLevelAwait() = true;
- JSC::Options::exposeInternalModuleLoader() = true;
-
- std::set_terminate([]() { Zig__GlobalObject__onCrash(); });
- WTF::initializeMainThread();
- JSC::initialize();
+ if (!has_loaded_jsc) {
+ JSC::Options::useSourceProviderCache() = true;
+ JSC::Options::useUnlinkedCodeBlockJettisoning() = false;
+ // JSC::Options::useTopLevelAwait() = true;
+ JSC::Options::exposeInternalModuleLoader() = true;
+ std::set_terminate([]() { Zig__GlobalObject__onCrash(); });
+ WTF::initializeMainThread();
+ JSC::initialize();
+ has_loaded_jsc = true;
+ }
// JSC::Options::useCodeCache() = false;
@@ -104,7 +108,7 @@ extern "C" JSC__JSGlobalObject *Zig__GlobalObject__create(JSClassRef *globalObje
JSC::JSLockHolder locker(vm);
Zig::GlobalObject *globalObject =
- Zig::GlobalObject::create(vm, Zig::GlobalObject::createStructure(vm, JSC::jsNull()));
+ Zig::GlobalObject::create(vm, Zig::GlobalObject::createStructure(vm, JSC::jsNull()));
globalObject->setConsole(globalObject);
if (count > 0) { globalObject->installAPIGlobals(globalObjectClass, count); }
diff --git a/src/javascript/jsc/bindings/bindings.cpp b/src/javascript/jsc/bindings/bindings.cpp
index 50e6978b5..d847a3121 100644
--- a/src/javascript/jsc/bindings/bindings.cpp
+++ b/src/javascript/jsc/bindings/bindings.cpp
@@ -10,6 +10,7 @@
#include <JavaScriptCore/Identifier.h>
#include <JavaScriptCore/IteratorOperations.h>
#include <JavaScriptCore/JSArray.h>
+#include <JavaScriptCore/JSArrayInlines.h>
#include <JavaScriptCore/JSCInlines.h>
#include <JavaScriptCore/JSCallbackObject.h>
#include <JavaScriptCore/JSClassRef.h>
@@ -51,6 +52,12 @@ JSC__JSValue JSC__JSValue__createEmptyObject(JSC__JSGlobalObject *globalObject,
JSC::constructEmptyObject(globalObject, globalObject->objectPrototype(), initialCapacity));
}
+uint32_t JSC__JSValue__getLengthOfArray(JSC__JSValue value, JSC__JSGlobalObject *globalObject) {
+ JSC::JSValue jsValue = JSC::JSValue::decode(value);
+ JSC::JSObject *object = jsValue.toObject(globalObject);
+ return JSC::toLength(globalObject, object);
+}
+
void JSC__JSObject__putRecord(JSC__JSObject *object, JSC__JSGlobalObject *global, ZigString *key,
ZigString *values, size_t valuesLen) {
auto scope = DECLARE_THROW_SCOPE(global->vm());
@@ -220,9 +227,9 @@ JSC__JSValue JSC__Exception__value(JSC__Exception *arg0) {
// JSC__PropertyNameArray__next(JSC__PropertyNameArray* arg0, size_t arg1);
// CPP_DECL void JSC__PropertyNameArray__release(JSC__PropertyNameArray* arg0);
size_t JSC__JSObject__getArrayLength(JSC__JSObject *arg0) { return arg0->getArrayLength(); }
-JSC__JSValue JSC__JSObject__getIndex(JSC__JSObject *arg0, JSC__JSGlobalObject *arg1,
+JSC__JSValue JSC__JSObject__getIndex(JSC__JSValue jsValue, JSC__JSGlobalObject *arg1,
uint32_t arg3) {
- return JSC::JSValue::encode(arg0->getIndex(arg1, arg3));
+ return JSC::JSValue::encode(JSC::JSValue::decode(jsValue).toObject(arg1)->getIndex(arg1, arg3));
}
JSC__JSValue JSC__JSObject__getDirect(JSC__JSObject *arg0, JSC__JSGlobalObject *arg1,
ZigString arg2) {
diff --git a/src/javascript/jsc/bindings/bindings.zig b/src/javascript/jsc/bindings/bindings.zig
index f9cc9bad3..3355fab44 100644
--- a/src/javascript/jsc/bindings/bindings.zig
+++ b/src/javascript/jsc/bindings/bindings.zig
@@ -41,7 +41,7 @@ pub const JSObject = extern struct {
return create(global, length, creator, Type.call);
}
- pub fn getIndex(this: *JSObject, globalThis: *JSGlobalObject, i: u32) JSValue {
+ pub fn getIndex(this: JSValue, globalThis: *JSGlobalObject, i: u32) JSValue {
return cppFn("getIndex", .{
this,
globalThis,
@@ -100,6 +100,10 @@ pub const ZigString = extern struct {
return ZigString{ .ptr = slice_.ptr, .len = slice_.len };
}
+ pub inline fn toRef(slice_: []const u8, global: *JSGlobalObject) C_API.JSValueRef {
+ return init(slice_).toValue(global).asRef();
+ }
+
pub const Empty = ZigString{ .ptr = "", .len = 0 };
pub fn slice(this: *const ZigString) []const u8 {
@@ -1076,6 +1080,30 @@ pub const URL = extern struct {
pub const Extern = [_][]const u8{ "fromFileSystemPath", "fromString", "isEmpty", "isValid", "protocol", "encodedUser", "encodedPassword", "host", "path", "lastPathComponent", "query", "fragmentIdentifier", "queryWithLeadingQuestionMark", "fragmentIdentifierWithLeadingNumberSign", "stringWithoutQueryOrFragmentIdentifier", "stringWithoutFragmentIdentifier", "protocolHostAndPort", "hostAndPort", "user", "password", "fileSystemPath", "setProtocol", "setHost", "setHostAndPort", "setUser", "setPassword", "setPath", "setQuery", "truncatedForUseAsBase" };
};
+pub const JSArrayIterator = struct {
+ i: u32 = 0,
+ len: u32 = 0,
+ array: JSValue,
+ global: *JSGlobalObject,
+
+ pub fn init(value: JSValue, global: *JSGlobalObject) JSArrayIterator {
+ return .{
+ .array = value,
+ .global = global,
+ .len = value.getLengthOfArray(global),
+ };
+ }
+
+ pub fn next(this: *JSArrayIterator) ?JSValue {
+ if (!(this.i < this.len)) {
+ return null;
+ }
+ const i = this.i;
+ this.i += 1;
+ return JSObject.getIndex(this.array, this.global, i);
+ }
+};
+
pub const String = extern struct {
pub const shim = Shimmer("WTF", "String", @This());
bytes: shim.Bytes,
@@ -1226,6 +1254,10 @@ pub const JSValue = enum(i64) {
});
}
+ pub inline fn arrayIterator(this: JSValue, global: *JSGlobalObject) JSArrayIterator {
+ return JSArrayIterator.init(this, global);
+ }
+
pub fn jsNumberFromDouble(i: f64) JSValue {
return cppFn("jsNumberFromDouble", .{i});
}
@@ -1411,6 +1443,17 @@ pub const JSValue = enum(i64) {
});
}
+ pub inline fn toU16(this: JSValue) u36 {
+ return @intCast(u16, this.toInt32());
+ }
+
+ pub fn getLengthOfArray(this: JSValue, globalThis: *JSGlobalObject) u32 {
+ return cppFn("getLengthOfArray", .{
+ this,
+ globalThis,
+ });
+ }
+
pub fn isAggregateError(this: JSValue, globalObject: *JSGlobalObject) bool {
return cppFn("isAggregateError", .{ this, globalObject });
}
@@ -1427,11 +1470,11 @@ pub const JSValue = enum(i64) {
}
pub inline fn asRef(this: JSValue) C_API.JSValueRef {
- return @intToPtr(C_API.JSValueRef, @intCast(usize, @enumToInt(this)));
+ return @intToPtr(C_API.JSValueRef, @bitCast(usize, @enumToInt(this)));
}
pub inline fn fromRef(this: C_API.JSValueRef) JSValue {
- return @intToEnum(JSValue, @intCast(i64, @ptrToInt(this)));
+ return @intToEnum(JSValue, @bitCast(i64, @ptrToInt(this)));
}
pub inline fn asObjectRef(this: JSValue) C_API.JSObjectRef {
@@ -1442,7 +1485,7 @@ pub const JSValue = enum(i64) {
return @intToPtr(*c_void, @intCast(usize, @enumToInt(this)));
}
- pub const Extern = [_][]const u8{ "toZigString", "createStringArray", "createEmptyObject", "putRecord", "asPromise", "isClass", "getNameProperty", "getClassName", "getErrorsProperty", "toInt32", "toBoolean", "isInt32", "isIterable", "forEach", "isAggregateError", "toZigException", "isException", "toWTFString", "hasProperty", "getPropertyNames", "getDirect", "putDirect", "get", "getIfExists", "asString", "asObject", "asNumber", "isError", "jsNull", "jsUndefined", "jsTDZValue", "jsBoolean", "jsDoubleNumber", "jsNumberFromDouble", "jsNumberFromChar", "jsNumberFromU16", "jsNumberFromInt32", "jsNumberFromInt64", "jsNumberFromUint64", "isUndefined", "isNull", "isUndefinedOrNull", "isBoolean", "isAnyInt", "isUInt32AsAnyInt", "isInt32AsAnyInt", "isNumber", "isString", "isBigInt", "isHeapBigInt", "isBigInt32", "isSymbol", "isPrimitive", "isGetterSetter", "isCustomGetterSetter", "isObject", "isCell", "asCell", "toString", "toStringOrNull", "toPropertyKey", "toPropertyKeyValue", "toObject", "toString", "getPrototype", "getPropertyByPropertyName", "eqlValue", "eqlCell", "isCallable" };
+ pub const Extern = [_][]const u8{ "getLengthOfArray", "toZigString", "createStringArray", "createEmptyObject", "putRecord", "asPromise", "isClass", "getNameProperty", "getClassName", "getErrorsProperty", "toInt32", "toBoolean", "isInt32", "isIterable", "forEach", "isAggregateError", "toZigException", "isException", "toWTFString", "hasProperty", "getPropertyNames", "getDirect", "putDirect", "get", "getIfExists", "asString", "asObject", "asNumber", "isError", "jsNull", "jsUndefined", "jsTDZValue", "jsBoolean", "jsDoubleNumber", "jsNumberFromDouble", "jsNumberFromChar", "jsNumberFromU16", "jsNumberFromInt32", "jsNumberFromInt64", "jsNumberFromUint64", "isUndefined", "isNull", "isUndefinedOrNull", "isBoolean", "isAnyInt", "isUInt32AsAnyInt", "isInt32AsAnyInt", "isNumber", "isString", "isBigInt", "isHeapBigInt", "isBigInt32", "isSymbol", "isPrimitive", "isGetterSetter", "isCustomGetterSetter", "isObject", "isCell", "asCell", "toString", "toStringOrNull", "toPropertyKey", "toPropertyKeyValue", "toObject", "toString", "getPrototype", "getPropertyByPropertyName", "eqlValue", "eqlCell", "isCallable" };
};
pub const PropertyName = extern struct {
diff --git a/src/javascript/jsc/bindings/headers-cpp.h b/src/javascript/jsc/bindings/headers-cpp.h
index 7ea94d875..63510791c 100644
--- a/src/javascript/jsc/bindings/headers-cpp.h
+++ b/src/javascript/jsc/bindings/headers-cpp.h
@@ -1,4 +1,4 @@
-//-- AUTOGENERATED FILE -- 1631749917
+//-- AUTOGENERATED FILE -- 1632635195
// clang-format off
#pragma once
diff --git a/src/javascript/jsc/bindings/headers.h b/src/javascript/jsc/bindings/headers.h
index e40651476..657d4a4f4 100644
--- a/src/javascript/jsc/bindings/headers.h
+++ b/src/javascript/jsc/bindings/headers.h
@@ -1,4 +1,4 @@
-//-- AUTOGENERATED FILE -- 1631749917
+//-- AUTOGENERATED FILE -- 1632635195
// clang-format: off
#pragma once
@@ -234,7 +234,7 @@ typedef void* JSClassRef;
CPP_DECL JSC__JSValue JSC__JSObject__create(JSC__JSGlobalObject* arg0, size_t arg1, void* arg2, void (* ArgFn3)(void* arg0, JSC__JSObject* arg1, JSC__JSGlobalObject* arg2));
CPP_DECL size_t JSC__JSObject__getArrayLength(JSC__JSObject* arg0);
CPP_DECL JSC__JSValue JSC__JSObject__getDirect(JSC__JSObject* arg0, JSC__JSGlobalObject* arg1, ZigString arg2);
-CPP_DECL JSC__JSValue JSC__JSObject__getIndex(JSC__JSObject* arg0, JSC__JSGlobalObject* arg1, uint32_t arg2);
+CPP_DECL JSC__JSValue JSC__JSObject__getIndex(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, uint32_t arg2);
CPP_DECL void JSC__JSObject__putDirect(JSC__JSObject* arg0, JSC__JSGlobalObject* arg1, ZigString arg2, JSC__JSValue JSValue3);
CPP_DECL void JSC__JSObject__putRecord(JSC__JSObject* arg0, JSC__JSGlobalObject* arg1, ZigString* arg2, ZigString* arg3, size_t arg4);
CPP_DECL JSC__JSValue ZigString__toErrorInstance(const ZigString* arg0, JSC__JSGlobalObject* arg1);
@@ -421,6 +421,7 @@ CPP_DECL bool JSC__JSValue__eqlValue(JSC__JSValue JSValue0, JSC__JSValue JSValue
CPP_DECL void JSC__JSValue__forEach(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, void (* ArgFn2)(JSC__VM* arg0, JSC__JSGlobalObject* arg1, JSC__JSValue JSValue2));
CPP_DECL void JSC__JSValue__getClassName(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, ZigString* arg2);
CPP_DECL JSC__JSValue JSC__JSValue__getErrorsProperty(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1);
+CPP_DECL uint32_t JSC__JSValue__getLengthOfArray(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1);
CPP_DECL void JSC__JSValue__getNameProperty(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, ZigString* arg2);
CPP_DECL JSC__JSValue JSC__JSValue__getPrototype(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1);
CPP_DECL bool JSC__JSValue__isAggregateError(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1);
diff --git a/src/javascript/jsc/bindings/headers.zig b/src/javascript/jsc/bindings/headers.zig
index 1b295fbad..a2c111d2d 100644
--- a/src/javascript/jsc/bindings/headers.zig
+++ b/src/javascript/jsc/bindings/headers.zig
@@ -37,69 +37,70 @@ pub const __mbstate_t = extern union {
pub const __darwin_mbstate_t = __mbstate_t;
pub const __darwin_ptrdiff_t = c_long;
pub const __darwin_size_t = c_ulong;
-
+
pub const JSC__RegExpPrototype = struct_JSC__RegExpPrototype;
-
+
pub const JSC__GeneratorPrototype = struct_JSC__GeneratorPrototype;
-
+
pub const JSC__ArrayIteratorPrototype = struct_JSC__ArrayIteratorPrototype;
-
+
pub const JSC__StringPrototype = struct_JSC__StringPrototype;
pub const WTF__StringView = bWTF__StringView;
-
+
pub const JSC__JSPromisePrototype = struct_JSC__JSPromisePrototype;
pub const JSC__CatchScope = bJSC__CatchScope;
pub const JSC__ThrowScope = bJSC__ThrowScope;
pub const JSC__PropertyName = bJSC__PropertyName;
pub const JSC__JSObject = bJSC__JSObject;
pub const WTF__ExternalStringImpl = bWTF__ExternalStringImpl;
-
+
pub const JSC__AsyncIteratorPrototype = struct_JSC__AsyncIteratorPrototype;
pub const WTF__StringImpl = bWTF__StringImpl;
pub const JSC__JSLock = bJSC__JSLock;
pub const JSC__JSModuleLoader = bJSC__JSModuleLoader;
pub const JSC__VM = bJSC__VM;
-
+
pub const JSC__AsyncGeneratorPrototype = struct_JSC__AsyncGeneratorPrototype;
-
+
pub const JSC__AsyncGeneratorFunctionPrototype = struct_JSC__AsyncGeneratorFunctionPrototype;
pub const JSC__JSGlobalObject = bJSC__JSGlobalObject;
pub const JSC__JSFunction = bJSC__JSFunction;
-
+
pub const JSC__ArrayPrototype = struct_JSC__ArrayPrototype;
-
+
pub const JSC__AsyncFunctionPrototype = struct_JSC__AsyncFunctionPrototype;
pub const JSC__Identifier = bJSC__Identifier;
pub const JSC__JSPromise = bJSC__JSPromise;
-
+
pub const JSC__SetIteratorPrototype = struct_JSC__SetIteratorPrototype;
pub const JSC__SourceCode = bJSC__SourceCode;
pub const JSC__JSCell = bJSC__JSCell;
-
+
pub const JSC__BigIntPrototype = struct_JSC__BigIntPrototype;
-
+
pub const JSC__GeneratorFunctionPrototype = struct_JSC__GeneratorFunctionPrototype;
pub const JSC__SourceOrigin = bJSC__SourceOrigin;
pub const JSC__JSModuleRecord = bJSC__JSModuleRecord;
pub const WTF__String = bWTF__String;
pub const WTF__URL = bWTF__URL;
-
+
+
pub const JSC__IteratorPrototype = struct_JSC__IteratorPrototype;
pub const JSC__JSInternalPromise = bJSC__JSInternalPromise;
-
+
pub const JSC__FunctionPrototype = struct_JSC__FunctionPrototype;
pub const Inspector__ScriptArguments = bInspector__ScriptArguments;
pub const JSC__Exception = bJSC__Exception;
pub const JSC__JSString = bJSC__JSString;
-
+
pub const JSC__ObjectPrototype = struct_JSC__ObjectPrototype;
pub const JSC__CallFrame = bJSC__CallFrame;
-
+
pub const JSC__MapIteratorPrototype = struct_JSC__MapIteratorPrototype;
pub extern fn JSC__JSObject__create(arg0: [*c]JSC__JSGlobalObject, arg1: usize, arg2: ?*c_void, ArgFn3: ?fn (?*c_void, [*c]JSC__JSObject, [*c]JSC__JSGlobalObject) callconv(.C) void) JSC__JSValue;
pub extern fn JSC__JSObject__getArrayLength(arg0: [*c]JSC__JSObject) usize;
pub extern fn JSC__JSObject__getDirect(arg0: [*c]JSC__JSObject, arg1: [*c]JSC__JSGlobalObject, arg2: ZigString) JSC__JSValue;
-pub extern fn JSC__JSObject__getIndex(arg0: [*c]JSC__JSObject, arg1: [*c]JSC__JSGlobalObject, arg2: u32) JSC__JSValue;
+pub extern fn JSC__JSObject__getIndex(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, arg2: u32) JSC__JSValue;
pub extern fn JSC__JSObject__putDirect(arg0: [*c]JSC__JSObject, arg1: [*c]JSC__JSGlobalObject, arg2: ZigString, JSValue3: JSC__JSValue) void;
pub extern fn JSC__JSObject__putRecord(arg0: [*c]JSC__JSObject, arg1: [*c]JSC__JSGlobalObject, arg2: [*c]ZigString, arg3: [*c]ZigString, arg4: usize) void;
pub extern fn ZigString__toErrorInstance(arg0: [*c]const ZigString, arg1: [*c]JSC__JSGlobalObject) JSC__JSValue;
@@ -154,14 +155,14 @@ pub extern fn JSC__JSInternalPromise__then(arg0: [*c]JSC__JSInternalPromise, arg
pub extern fn JSC__SourceOrigin__fromURL(arg0: [*c]const WTF__URL) bJSC__SourceOrigin;
pub extern fn JSC__SourceCode__fromString(arg0: [*c]JSC__SourceCode, arg1: [*c]const WTF__String, arg2: [*c]const JSC__SourceOrigin, arg3: [*c]WTF__String, SourceType4: u8) void;
pub extern fn JSC__JSFunction__calculatedDisplayName(arg0: [*c]JSC__JSFunction, arg1: [*c]JSC__VM) bWTF__String;
-pub extern fn JSC__JSFunction__callWithArguments(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, arg2: [*c]JSC__JSValue, arg3: usize, arg4: *?*JSC__Exception, arg5: [*c]const u8) JSC__JSValue;
-pub extern fn JSC__JSFunction__callWithArgumentsAndThis(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue, arg2: [*c]JSC__JSGlobalObject, arg3: [*c]JSC__JSValue, arg4: usize, arg5: *?*JSC__Exception, arg6: [*c]const u8) JSC__JSValue;
-pub extern fn JSC__JSFunction__callWithoutAnyArgumentsOrThis(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, arg2: *?*JSC__Exception, arg3: [*c]const u8) JSC__JSValue;
-pub extern fn JSC__JSFunction__callWithThis(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, JSValue2: JSC__JSValue, arg3: *?*JSC__Exception, arg4: [*c]const u8) JSC__JSValue;
-pub extern fn JSC__JSFunction__constructWithArguments(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, arg2: [*c]JSC__JSValue, arg3: usize, arg4: *?*JSC__Exception, arg5: [*c]const u8) JSC__JSValue;
-pub extern fn JSC__JSFunction__constructWithArgumentsAndNewTarget(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue, arg2: [*c]JSC__JSGlobalObject, arg3: [*c]JSC__JSValue, arg4: usize, arg5: *?*JSC__Exception, arg6: [*c]const u8) JSC__JSValue;
-pub extern fn JSC__JSFunction__constructWithNewTarget(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, JSValue2: JSC__JSValue, arg3: *?*JSC__Exception, arg4: [*c]const u8) JSC__JSValue;
-pub extern fn JSC__JSFunction__constructWithoutAnyArgumentsOrNewTarget(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, arg2: *?*JSC__Exception, arg3: [*c]const u8) JSC__JSValue;
+pub extern fn JSC__JSFunction__callWithArguments(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, arg2: [*c]JSC__JSValue, arg3: usize, arg4: *?*JSC__Exception , arg5: [*c]const u8) JSC__JSValue;
+pub extern fn JSC__JSFunction__callWithArgumentsAndThis(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue, arg2: [*c]JSC__JSGlobalObject, arg3: [*c]JSC__JSValue, arg4: usize, arg5: *?*JSC__Exception , arg6: [*c]const u8) JSC__JSValue;
+pub extern fn JSC__JSFunction__callWithoutAnyArgumentsOrThis(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, arg2: *?*JSC__Exception , arg3: [*c]const u8) JSC__JSValue;
+pub extern fn JSC__JSFunction__callWithThis(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, JSValue2: JSC__JSValue, arg3: *?*JSC__Exception , arg4: [*c]const u8) JSC__JSValue;
+pub extern fn JSC__JSFunction__constructWithArguments(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, arg2: [*c]JSC__JSValue, arg3: usize, arg4: *?*JSC__Exception , arg5: [*c]const u8) JSC__JSValue;
+pub extern fn JSC__JSFunction__constructWithArgumentsAndNewTarget(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue, arg2: [*c]JSC__JSGlobalObject, arg3: [*c]JSC__JSValue, arg4: usize, arg5: *?*JSC__Exception , arg6: [*c]const u8) JSC__JSValue;
+pub extern fn JSC__JSFunction__constructWithNewTarget(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, JSValue2: JSC__JSValue, arg3: *?*JSC__Exception , arg4: [*c]const u8) JSC__JSValue;
+pub extern fn JSC__JSFunction__constructWithoutAnyArgumentsOrNewTarget(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, arg2: *?*JSC__Exception , arg3: [*c]const u8) JSC__JSValue;
pub extern fn JSC__JSFunction__createFromNative(arg0: [*c]JSC__JSGlobalObject, arg1: u16, arg2: [*c]const WTF__String, arg3: ?*c_void, ArgFn4: ?fn (?*c_void, [*c]JSC__JSGlobalObject, [*c]JSC__CallFrame) callconv(.C) JSC__JSValue) [*c]JSC__JSFunction;
pub extern fn JSC__JSFunction__displayName(arg0: [*c]JSC__JSFunction, arg1: [*c]JSC__VM) bWTF__String;
pub extern fn JSC__JSFunction__getName(arg0: [*c]JSC__JSFunction, arg1: [*c]JSC__VM) bWTF__String;
@@ -244,6 +245,7 @@ pub extern fn JSC__JSValue__eqlValue(JSValue0: JSC__JSValue, JSValue1: JSC__JSVa
pub extern fn JSC__JSValue__forEach(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, ArgFn2: ?fn ([*c]JSC__VM, [*c]JSC__JSGlobalObject, JSC__JSValue) callconv(.C) void) void;
pub extern fn JSC__JSValue__getClassName(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, arg2: [*c]ZigString) void;
pub extern fn JSC__JSValue__getErrorsProperty(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject) JSC__JSValue;
+pub extern fn JSC__JSValue__getLengthOfArray(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject) u32;
pub extern fn JSC__JSValue__getNameProperty(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, arg2: [*c]ZigString) void;
pub extern fn JSC__JSValue__getPrototype(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject) JSC__JSValue;
pub extern fn JSC__JSValue__isAggregateError(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject) bool;
diff --git a/src/javascript/jsc/javascript.zig b/src/javascript/jsc/javascript.zig
index 8d7626afb..9b1d22a05 100644
--- a/src/javascript/jsc/javascript.zig
+++ b/src/javascript/jsc/javascript.zig
@@ -36,6 +36,7 @@ pub const GlobalClasses = [_]type{
ResolveError.Class,
Bun.Class,
Fetch.Class,
+ js_ast.Macro.JSNode.BunJSXCallbackFunction,
};
const Blob = @import("../../blob.zig");
@@ -178,6 +179,74 @@ pub const Bun = struct {
return ZigString.init(VirtualMachine.vm.bundler.options.routes.dir).toValue(VirtualMachine.vm.global).asRef();
}
+ pub fn getFilePath(ctx: js.JSContextRef, arguments: []const js.JSValueRef, buf: []u8, exception: js.ExceptionRef) ?string {
+ if (arguments.len != 1) {
+ JSError(getAllocator(ctx), "Expected a file path as a string or an array of strings to be part of a file path.", .{}, ctx, exception);
+ return null;
+ }
+
+ const value = arguments[0];
+ if (js.JSValueIsString(ctx, value)) {
+ var out = ZigString.Empty;
+ JSValue.toZigString(JSValue.fromRef(value), &out, VirtualMachine.vm.global);
+ var out_slice = out.slice();
+
+ // The dots are kind of unnecessary. They'll be normalized.
+ if (out.len == 0 or @ptrToInt(out.ptr) == 0 or std.mem.eql(u8, out_slice, ".") or std.mem.eql(u8, out_slice, "..") or std.mem.eql(u8, out_slice, "../")) {
+ JSError(getAllocator(ctx), "Expected a file path as a string or an array of strings to be part of a file path.", .{}, ctx, exception);
+ return null;
+ }
+
+ var parts = [_]string{out_slice};
+ // This does the equivalent of Node's path.normalize(path.join(cwd, out_slice))
+ var res = VirtualMachine.vm.bundler.fs.absBuf(&parts, buf);
+
+ return res;
+ } else if (js.JSValueIsArray(ctx, value)) {
+ var temp_strings_list: [32]string = undefined;
+ var temp_strings_list_len: u8 = 0;
+ defer {
+ for (temp_strings_list[0..temp_strings_list_len]) |_, i| {
+ temp_strings_list[i] = "";
+ }
+ }
+
+ var iter = JSValue.fromRef(value).arrayIterator(VirtualMachine.vm.global);
+ while (iter.next()) |item| {
+ if (temp_strings_list_len >= temp_strings_list.len) {
+ break;
+ }
+
+ if (!item.isString()) {
+ JSError(getAllocator(ctx), "Expected a file path as a string or an array of strings to be part of a file path.", .{}, ctx, exception);
+ return null;
+ }
+
+ var out = ZigString.Empty;
+ JSValue.toZigString(item, &out, VirtualMachine.vm.global);
+ const out_slice = out.slice();
+
+ temp_strings_list[temp_strings_list_len] = out_slice;
+ // The dots are kind of unnecessary. They'll be normalized.
+ if (out.len == 0 or @ptrToInt(out.ptr) == 0 or std.mem.eql(u8, out_slice, ".") or std.mem.eql(u8, out_slice, "..") or std.mem.eql(u8, out_slice, "../")) {
+ JSError(getAllocator(ctx), "Expected a file path as a string or an array of strings to be part of a file path.", .{}, ctx, exception);
+ return null;
+ }
+ temp_strings_list_len += 1;
+ }
+
+ if (temp_strings_list_len == 0) {
+ JSError(getAllocator(ctx), "Expected a file path as a string or an array of strings to be part of a file path.", .{}, ctx, exception);
+ return null;
+ }
+
+ return VirtualMachine.vm.bundler.fs.absBuf(temp_strings_list[0..temp_strings_list_len], buf);
+ } else {
+ JSError(getAllocator(ctx), "Expected a file path as a string or an array of strings to be part of a file path.", .{}, ctx, exception);
+ return null;
+ }
+ }
+
pub fn getImportedStyles(
this: void,
ctx: js.JSContextRef,
@@ -195,59 +264,52 @@ pub const Bun = struct {
return JSValue.createStringArray(VirtualMachine.vm.global, styles.ptr, styles.len).asRef();
}
- pub fn getRouteFiles(
- this: void,
+ pub fn readFileAsStringCallback(
ctx: js.JSContextRef,
- function: js.JSObjectRef,
- thisObject: js.JSObjectRef,
- arguments: []const js.JSValueRef,
+ buf_z: [:0]const u8,
exception: js.ExceptionRef,
) js.JSValueRef {
- if (VirtualMachine.vm.bundler.router == null) return js.JSValueMakeNull(ctx);
-
- const router = &(VirtualMachine.vm.bundler.router orelse unreachable);
- const list = router.getEntryPointsWithBuffer(VirtualMachine.vm.allocator, false) catch unreachable;
- VirtualMachine.vm.flush_list.append(list.buffer) catch {};
- defer VirtualMachine.vm.allocator.free(list.entry_points);
+ const path = buf_z.ptr[0..buf_z.len];
+ var file = std.fs.cwd().openFileZ(buf_z, .{ .read = true, .write = false }) catch |err| {
+ JSError(getAllocator(ctx), "Opening file {s} for path: \"{s}\"", .{ @errorName(err), path }, ctx, exception);
+ return js.JSValueMakeUndefined(ctx);
+ };
- for (routes_list_strings[0..std.math.min(list.entry_points.len, routes_list_strings.len)]) |_, i| {
- routes_list_strings[i] = ZigString.init(list.entry_points[i]);
- }
+ defer file.close();
- const ref = JSValue.createStringArray(VirtualMachine.vm.global, &routes_list_strings, list.entry_points.len).asRef();
- return ref;
- }
+ const stat = file.stat() catch |err| {
+ JSError(getAllocator(ctx), "Getting file size {s} for \"{s}\"", .{ @errorName(err), path }, ctx, exception);
+ return js.JSValueMakeUndefined(ctx);
+ };
- pub fn readFileAsBytes(
- this: void,
- ctx: js.JSContextRef,
- function: js.JSObjectRef,
- thisObject: js.JSObjectRef,
- arguments: []const js.JSValueRef,
- exception: js.ExceptionRef,
- ) js.JSValueRef {
- if (arguments.len != 1 or !JSValue.isString(JSValue.fromRef(arguments[0]))) {
- JSError(getAllocator(ctx), "readFileBytes expects a file path as a string. e.g. Bun.readFile(\"public/index.html\")", .{}, ctx, exception);
+ if (stat.kind != .File) {
+ JSError(getAllocator(ctx), "Can't read a {s} as a string (\"{s}\")", .{ @tagName(stat.kind), path }, ctx, exception);
return js.JSValueMakeUndefined(ctx);
}
- var out = ZigString.Empty;
- JSValue.toZigString(JSValue.fromRef(arguments[0]), &out, VirtualMachine.vm.global);
- var out_slice = out.slice();
- // The dots are kind of unnecessary. They'll be normalized.
- if (out.len == 0 or @ptrToInt(out.ptr) == 0 or std.mem.eql(u8, out_slice, ".") or std.mem.eql(u8, out_slice, "..") or std.mem.eql(u8, out_slice, "../")) {
- JSError(getAllocator(ctx), "readFileBytes expects a valid file path. e.g. Bun.readFile(\"public/index.html\")", .{}, ctx, exception);
+ var contents_buf = VirtualMachine.vm.allocator.alloc(u8, stat.size + 2) catch unreachable; // OOM
+ defer VirtualMachine.vm.allocator.free(contents_buf);
+ const contents_len = file.readAll(contents_buf) catch |err| {
+ JSError(getAllocator(ctx), "{s} reading file (\"{s}\")", .{ @errorName(err), path }, ctx, exception);
return js.JSValueMakeUndefined(ctx);
- }
+ };
- var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
+ contents_buf[contents_len] = 0;
- var parts = [_]string{out_slice};
- // This does the equivalent of Node's path.normalize(path.join(cwd, out_slice))
- var path = VirtualMachine.vm.bundler.fs.absBuf(&parts, &buf);
- buf[path.len] = 0;
+ // Very slow to do it this way. We're copying the string twice.
+ // But it's important that this string is garbage collected instead of manually managed.
+ // We can't really recycle this one.
+ // TODO: use external string
+ return js.JSValueMakeString(ctx, js.JSStringCreateWithUTF8CString(contents_buf.ptr));
+ }
+
+ pub fn readFileAsBytesCallback(
+ ctx: js.JSContextRef,
+ buf_z: [:0]const u8,
+ exception: js.ExceptionRef,
+ ) js.JSValueRef {
+ const path = buf_z.ptr[0..buf_z.len];
- const buf_z: [:0]const u8 = buf[0..path.len :0];
var file = std.fs.cwd().openFileZ(buf_z, .{ .read = true, .write = false }) catch |err| {
JSError(getAllocator(ctx), "Opening file {s} for path: \"{s}\"", .{ @errorName(err), path }, ctx, exception);
return js.JSValueMakeUndefined(ctx);
@@ -284,7 +346,7 @@ pub const Bun = struct {
return marked_array_buffer.toJSObjectRef(ctx, exception);
}
- pub fn readFileAsString(
+ pub fn getRouteFiles(
this: void,
ctx: js.JSContextRef,
function: js.JSObjectRef,
@@ -292,59 +354,53 @@ pub const Bun = struct {
arguments: []const js.JSValueRef,
exception: js.ExceptionRef,
) js.JSValueRef {
- if (arguments.len != 1 or !JSValue.isString(JSValue.fromRef(arguments[0]))) {
- JSError(getAllocator(ctx), "readFile expects a file path as a string. e.g. Bun.readFile(\"public/index.html\")", .{}, ctx, exception);
- return js.JSValueMakeUndefined(ctx);
- }
- var out = ZigString.Empty;
- JSValue.toZigString(JSValue.fromRef(arguments[0]), &out, VirtualMachine.vm.global);
- var out_slice = out.slice();
+ if (VirtualMachine.vm.bundler.router == null) return js.JSValueMakeNull(ctx);
- // The dots are kind of unnecessary. They'll be normalized.
- if (out.len == 0 or @ptrToInt(out.ptr) == 0 or std.mem.eql(u8, out_slice, ".") or std.mem.eql(u8, out_slice, "..") or std.mem.eql(u8, out_slice, "../")) {
- JSError(getAllocator(ctx), "readFile expects a valid file path. e.g. Bun.readFile(\"public/index.html\")", .{}, ctx, exception);
- return js.JSValueMakeUndefined(ctx);
+ const router = &(VirtualMachine.vm.bundler.router orelse unreachable);
+ const list = router.getEntryPointsWithBuffer(VirtualMachine.vm.allocator, false) catch unreachable;
+ VirtualMachine.vm.flush_list.append(list.buffer) catch {};
+ defer VirtualMachine.vm.allocator.free(list.entry_points);
+
+ for (routes_list_strings[0..std.math.min(list.entry_points.len, routes_list_strings.len)]) |_, i| {
+ routes_list_strings[i] = ZigString.init(list.entry_points[i]);
}
- var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
+ const ref = JSValue.createStringArray(VirtualMachine.vm.global, &routes_list_strings, list.entry_points.len).asRef();
+ return ref;
+ }
- var parts = [_]string{out_slice};
- // This does the equivalent of Node's path.normalize(path.join(cwd, out_slice))
- var path = VirtualMachine.vm.bundler.fs.absBuf(&parts, &buf);
+ pub fn readFileAsBytes(
+ this: void,
+ ctx: js.JSContextRef,
+ function: js.JSObjectRef,
+ thisObject: js.JSObjectRef,
+ arguments: []const js.JSValueRef,
+ exception: js.ExceptionRef,
+ ) js.JSValueRef {
+ var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
+ const path = getFilePath(ctx, arguments, &buf, exception) orelse return null;
buf[path.len] = 0;
const buf_z: [:0]const u8 = buf[0..path.len :0];
- var file = std.fs.cwd().openFileZ(buf_z, .{ .read = true, .write = false }) catch |err| {
- JSError(getAllocator(ctx), "Opening file {s} for path: \"{s}\"", .{ @errorName(err), path }, ctx, exception);
- return js.JSValueMakeUndefined(ctx);
- };
-
- defer file.close();
-
- const stat = file.stat() catch |err| {
- JSError(getAllocator(ctx), "Getting file size {s} for \"{s}\"", .{ @errorName(err), path }, ctx, exception);
- return js.JSValueMakeUndefined(ctx);
- };
-
- if (stat.kind != .File) {
- JSError(getAllocator(ctx), "Can't read a {s} as a string (\"{s}\")", .{ @tagName(stat.kind), path }, ctx, exception);
- return js.JSValueMakeUndefined(ctx);
- }
-
- var contents_buf = VirtualMachine.vm.allocator.alloc(u8, stat.size + 2) catch unreachable; // OOM
- defer VirtualMachine.vm.allocator.free(contents_buf);
- const contents_len = file.readAll(contents_buf) catch |err| {
- JSError(getAllocator(ctx), "{s} reading file (\"{s}\")", .{ @errorName(err), path }, ctx, exception);
- return js.JSValueMakeUndefined(ctx);
- };
+ const result = readFileAsBytesCallback(ctx, buf_z, exception);
+ return result;
+ }
- contents_buf[contents_len] = 0;
+ pub fn readFileAsString(
+ this: void,
+ ctx: js.JSContextRef,
+ function: js.JSObjectRef,
+ thisObject: js.JSObjectRef,
+ arguments: []const js.JSValueRef,
+ exception: js.ExceptionRef,
+ ) js.JSValueRef {
+ var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
+ const path = getFilePath(ctx, arguments, &buf, exception) orelse return null;
+ buf[path.len] = 0;
- // Very slow to do it this way. We're copying the string twice.
- // But it's important that this string is garbage collected instead of manually managed.
- // We can't really recycle this one.
- // TODO: use external string
- return js.JSValueMakeString(ctx, js.JSStringCreateWithUTF8CString(contents_buf.ptr));
+ const buf_z: [:0]const u8 = buf[0..path.len :0];
+ const result = readFileAsStringCallback(ctx, buf_z, exception);
+ return result;
}
pub fn getPublicPath(to: string, comptime Writer: type, writer: Writer) void {
@@ -649,17 +705,14 @@ pub const VirtualMachine = struct {
&vm.bundler.fs.fs,
) orelse 0),
};
- } else if (strings.eqlComptime(_specifier, Runtime.Runtime.Imports.Name)) {
+ } else if (vm.node_modules == null and strings.eqlComptime(_specifier, Runtime.Runtime.Imports.Name)) {
return ResolvedSource{
.allocator = null,
.source_code = ZigString.init(Runtime.Runtime.sourceContent()),
.specifier = ZigString.init(Runtime.Runtime.Imports.Name),
.source_url = ZigString.init(Runtime.Runtime.Imports.Name),
.hash = Runtime.Runtime.versionHash(),
- .bytecodecache_fd = std.math.lossyCast(
- u64,
- Runtime.Runtime.byteCodeCacheFile(&vm.bundler.fs.fs) orelse 0,
- ),
+ .bytecodecache_fd = 0,
};
// This is all complicated because the imports have to be linked and we want to run the printer on it
// so it consistently handles bundled imports
@@ -723,6 +776,19 @@ pub const VirtualMachine = struct {
.hash = 0,
.bytecodecache_fd = 0,
};
+ } else if (_specifier.len > js_ast.Macro.namespaceWithColon.len and
+ strings.eqlComptimeIgnoreLen(_specifier[0..js_ast.Macro.namespaceWithColon.len], js_ast.Macro.namespaceWithColon))
+ {
+ if (vm.macro_entry_points.get(MacroEntryPoint.generateIDFromSpecifier(_specifier))) |entry| {
+ return ResolvedSource{
+ .allocator = null,
+ .source_code = ZigString.init(entry.source.contents),
+ .specifier = ZigString.init(_specifier),
+ .source_url = ZigString.init(_specifier),
+ .hash = 0,
+ .bytecodecache_fd = 0,
+ };
+ }
}
const specifier = normalizeSpecifier(_specifier);
@@ -752,6 +818,7 @@ pub const VirtualMachine = struct {
vm.bundler.log = log;
vm.bundler.linker.log = log;
vm.bundler.resolver.log = log;
+
defer {
vm.bundler.log = old;
vm.bundler.linker.log = old;
@@ -825,7 +892,7 @@ pub const VirtualMachine = struct {
std.debug.assert(VirtualMachine.vm_loaded);
std.debug.assert(VirtualMachine.vm.global == global);
- if (vm.node_modules == null and strings.eqlComptime(specifier, Runtime.Runtime.Imports.Name)) {
+ if (vm.node_modules == null and strings.eqlComptime(std.fs.path.basename(specifier), Runtime.Runtime.Imports.alt_name)) {
ret.path = Runtime.Runtime.Imports.Name;
return;
} else if (vm.node_modules != null and strings.eql(specifier, bun_file_import_path)) {
diff --git a/src/js_ast.zig b/src/js_ast.zig
index 9ddafdf80..0a879b8cd 100644
--- a/src/js_ast.zig
+++ b/src/js_ast.zig
@@ -985,7 +985,7 @@ pub const E = struct {
/// lineNumber: number | null,
/// }```
/// - `children`:
- /// - multiple children? the function is React.jsxsDEV, "jsxs" instead of "jsx"
+ /// - static the function is React.jsxsDEV, "jsxs" instead of "jsx"
/// - one child? the function is React.jsxDEV,
/// - no children? the function is React.jsxDEV and children is an empty array.
/// `isStaticChildren`: https://github.com/facebook/react/blob/4ca62cac45c288878d2532e5056981d177f9fdac/packages/react/src/jsx/ReactJSXElementValidator.js#L369-L384
@@ -1034,6 +1034,7 @@ pub const E = struct {
pub const Number = struct {
value: f64,
+
pub fn jsonStringify(self: *const Number, opts: anytype, o: anytype) !void {
return try std.json.stringify(self.value, opts, o);
}
@@ -1042,6 +1043,8 @@ pub const E = struct {
pub const BigInt = struct {
value: string,
+ pub var empty = BigInt{ .value = "" };
+
pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void {
return try std.json.stringify(self.value, opts, o);
}
@@ -1057,10 +1060,16 @@ pub const E = struct {
pub const Spread = struct { value: ExprNodeIndex };
pub const String = struct {
- value: JavascriptString = &([_]u16{}),
+ value: []const u16 = &.{},
utf8: string = &([_]u8{}),
prefer_template: bool = false,
+ pub var empty = String{};
+ pub var @"true" = String{ .utf8 = "true" };
+ pub var @"false" = String{ .utf8 = "false" };
+ pub var @"null" = String{ .utf8 = "null" };
+ pub var @"undefined" = String{ .utf8 = "undefined" };
+
pub fn clone(str: *const String, allocator: *std.mem.Allocator) !String {
if (str.isUTF8()) {
return String{
@@ -1100,7 +1109,7 @@ pub const E = struct {
string => {
return strings.eql(s.utf8, other);
},
- JavascriptString => {
+ []u16, []const u16 => {
return strings.utf16EqlString(other, s.utf8);
},
else => {
@@ -1119,7 +1128,7 @@ pub const E = struct {
string => {
return strings.utf16EqlString(s.value, other);
},
- JavascriptString => {
+ []u16, []const u16 => {
return std.mem.eql(u16, other.value, s.value);
},
else => {
@@ -1145,7 +1154,7 @@ pub const E = struct {
return std.hash.Wyhash.hash(0, s.utf8);
} else {
// hash utf-16
- return std.hash.Wyhash.hash(0, @ptrCast([*]u8, s.value.ptr)[0 .. s.value.len * 2]);
+ return std.hash.Wyhash.hash(0, @ptrCast([*]const u8, s.value.ptr)[0 .. s.value.len * 2]);
}
}
@@ -1180,6 +1189,46 @@ pub const E = struct {
pub const RegExp = struct {
value: string,
+ // This exists for JavaScript bindings
+ // The RegExp constructor expects flags as a second argument.
+ // We want to avoid re-lexing the flags, so we store them here.
+ // This is the index of the first character in a flag, not the "/"
+ // /foo/gim
+ // ^
+ flags_offset: ?u16 = null,
+
+ pub var empty = RegExp{ .value = "" };
+
+ pub fn pattern(this: RegExp) string {
+
+ // rewind until we reach the /foo/gim
+ // ^
+ // should only ever be a single character
+ // but we're being cautious
+ if (this.flags_offset) |i_| {
+ var i = i_;
+ while (i > 0 and this.value[i] != '/') {
+ i -= 1;
+ }
+
+ return std.mem.trim(u8, this.value[0..i], "/");
+ }
+
+ return std.mem.trim(u8, this.value, "/");
+ }
+
+ pub fn flags(this: RegExp) string {
+ // rewind until we reach the /foo/gim
+ // ^
+ // should only ever be a single character
+ // but we're being cautious
+ if (this.flags_offset) |i| {
+ return this.value[i..];
+ }
+
+ return "";
+ }
+
pub fn jsonStringify(self: *const RegExp, opts: anytype, o: anytype) !void {
return try std.json.stringify(self.value, opts, o);
}
@@ -2544,160 +2593,8 @@ pub const Expr = struct {
e_class,
e_require,
- pub inline fn toPublicValue(this: Tag) u16 {
- return @intCast(u16, @enumToInt(this)) + 16;
- }
-
- pub inline fn fromPublicValue(comptime ValueType: type, value: ValueType) ?Tag {
- if (value < 16 or value > @enumToInt(Tag.e_require)) return null;
-
- switch (comptime ValueType) {
- f64 => {
- return @intToEnum(@floatToInt(u16, value - 16), Tag);
- },
- else => {
- return @intToEnum(@intCast(u6, @intCast(u16, value) - 16), Tag);
- },
- }
- }
-
- pub const names_strings = [_]string{
- "<array>",
- "<unary>",
- "<binary>",
- "<boolean>",
- "<super>",
- "<null>",
- "<void>",
- "<new>",
- "<function>",
- "<ntarget>",
- "<import>",
- "<call>",
- "<dot>",
- "<index>",
- "<arrow>",
- "<id>",
- "<importid>",
- "<private>",
- "<jsx>",
- "<missing>",
- "<number>",
- "<bigint>",
- "<object>",
- "<spread>",
- "<string>",
- "<tpart>",
- "<template>",
- "<regexp>",
- "<await>",
- "<yield>",
- "<if>",
- "<resolve>",
- "<import>",
- "<this>",
- "<class>",
- "<require>",
- };
- pub const valid_names_list: string = brk: {
- var names_list = names_strings[0];
- for (names_strings[1..]) |name_str, i| {
- names_list = names_list ++ "\n " ++ name_str;
- }
- break :brk " " ++ names_list;
- };
-
- pub const TagName = std.EnumArray(Tag, string);
-
- pub const names: TagName = brk: {
- var array = TagName.initUndefined();
- array.set(.e_array, names_strings[0]);
- array.set(.e_unary, names_strings[1]);
- array.set(.e_binary, names_strings[2]);
- array.set(.e_boolean, names_strings[3]);
- array.set(.e_super, names_strings[4]);
- array.set(.e_null, names_strings[5]);
- array.set(.e_undefined, names_strings[6]);
- array.set(.e_new, names_strings[7]);
- array.set(.e_function, names_strings[8]);
- array.set(.e_new_target, names_strings[9]);
- array.set(.e_import_meta, names_strings[10]);
- array.set(.e_call, names_strings[11]);
- array.set(.e_dot, names_strings[12]);
- array.set(.e_index, names_strings[13]);
- array.set(.e_arrow, names_strings[14]);
- array.set(.e_identifier, names_strings[15]);
- array.set(.e_import_identifier, names_strings[16]);
- array.set(.e_private_identifier, names_strings[17]);
- array.set(.e_jsx_element, names_strings[18]);
- array.set(.e_missing, names_strings[19]);
- array.set(.e_number, names_strings[20]);
- array.set(.e_big_int, names_strings[21]);
- array.set(.e_object, names_strings[22]);
- array.set(.e_spread, names_strings[23]);
- array.set(.e_string, names_strings[24]);
- array.set(.e_template_part, names_strings[25]);
- array.set(.e_template, names_strings[26]);
- array.set(.e_reg_exp, names_strings[27]);
- array.set(.e_await, names_strings[28]);
- array.set(.e_yield, names_strings[29]);
- array.set(.e_if, names_strings[30]);
- array.set(.e_require_or_require_resolve, names_strings[31]);
- array.set(.e_import, names_strings[32]);
- array.set(.e_this, names_strings[33]);
- array.set(.e_class, names_strings[34]);
- array.set(.e_require, names_strings[35]);
- break :brk array;
- };
- pub const TagExactSizeMatcher = strings.ExactSizeMatcher(8);
- pub fn find(name_: string) ?Tag {
- return switch (TagExactSizeMatcher.match(name_)) {
- TagExactSizeMatcher.case("array") => Tag.e_array,
- TagExactSizeMatcher.case("unary") => Tag.e_unary,
- TagExactSizeMatcher.case("binary") => Tag.e_binary,
- TagExactSizeMatcher.case("boolean") => Tag.e_boolean,
- TagExactSizeMatcher.case("true") => Tag.e_boolean,
- TagExactSizeMatcher.case("false") => Tag.e_boolean,
- TagExactSizeMatcher.case("super") => Tag.e_super,
- TagExactSizeMatcher.case("null") => Tag.e_null,
- TagExactSizeMatcher.case("void") => Tag.e_undefined,
- TagExactSizeMatcher.case("new") => Tag.e_new,
- TagExactSizeMatcher.case("function") => Tag.e_function,
- TagExactSizeMatcher.case("ntarget") => Tag.e_new_target,
- TagExactSizeMatcher.case("imeta") => Tag.e_import_meta,
- TagExactSizeMatcher.case("call") => Tag.e_call,
- TagExactSizeMatcher.case("dot") => Tag.e_dot,
- TagExactSizeMatcher.case("index") => Tag.e_index,
- TagExactSizeMatcher.case("arrow") => Tag.e_arrow,
- TagExactSizeMatcher.case("id") => Tag.e_identifier,
- TagExactSizeMatcher.case("importid") => Tag.e_import_identifier,
- TagExactSizeMatcher.case("jsx") => Tag.e_jsx_element,
- TagExactSizeMatcher.case("missing") => Tag.e_missing,
- TagExactSizeMatcher.case("number") => Tag.e_number,
- TagExactSizeMatcher.case("bigint") => Tag.e_big_int,
- TagExactSizeMatcher.case("object") => Tag.e_object,
- TagExactSizeMatcher.case("spread") => Tag.e_spread,
- TagExactSizeMatcher.case("string") => Tag.e_string,
- TagExactSizeMatcher.case("tpart") => Tag.e_template_part,
- TagExactSizeMatcher.case("template") => Tag.e_template,
- TagExactSizeMatcher.case("regexp") => Tag.e_reg_exp,
- TagExactSizeMatcher.case("await") => Tag.e_await,
- TagExactSizeMatcher.case("yield") => Tag.e_yield,
- TagExactSizeMatcher.case("if") => Tag.e_if,
- TagExactSizeMatcher.case("import") => Tag.e_import,
- TagExactSizeMatcher.case("this") => Tag.e_this,
- TagExactSizeMatcher.case("class") => Tag.e_class,
- TagExactSizeMatcher.case("require") => Tag.e_require,
- else => null,
- };
- }
-
- pub inline fn name(this: Tag) string {
- return names.get(this);
- }
-
pub fn jsonStringify(self: @This(), opts: anytype, o: anytype) !void {
- return try std.json.stringify(self.name(), opts, o);
+ return try std.json.stringify(@tagName(self), opts, o);
}
pub fn isArray(self: Tag) bool {
@@ -3352,7 +3249,7 @@ pub const S = struct {
pub const Comment = struct { text: string };
pub const Directive = struct {
- value: JavascriptString,
+ value: []const u16,
};
pub const ExportClause = struct { items: []ClauseItem, is_single_line: bool = false };
@@ -4204,38 +4101,2012 @@ pub const Macro = struct {
}
};
- pub const JSExpr = struct {
- expr: Expr,
+ pub const MacroResult = struct {
+ import_statements: []S.Import = &[_]S.Import{},
+ replacement: Expr,
+ };
+
+ pub const JSNode = struct {
+ loc: logger.Loc,
+ data: Data,
pub const Class = JSCBase.NewClass(
- JSExpr,
+ JSNode,
.{
- .name = "JSExpr",
+ .name = "JSNode",
.read_only = true,
},
.{
.toString = .{
- .rfn = toString,
+ .rfn = JSBindings.toString,
},
+
+ // .getAt = .{
+ // .rfn = JSBindings.getAt,
+ // },
+ // .valueAt = .{
+ // .rfn = JSBindings.valueAt,
+ // },
// .toNumber = .{
// .rfn = toNumber,
// },
+ .get = .{
+ .rfn = JSBindings.get,
+ .ro = true,
+ },
},
.{
.tag = .{
- .get = getTag,
+ .get = JSBindings.getTag,
.ro = true,
},
+
.tagName = .{
- .get = getTagName,
+ .get = JSBindings.getTagName,
.ro = true,
},
.position = .{
- .get = getPosition,
+ .get = JSBindings.getPosition,
+ .ro = true,
+ },
+ .value = .{
+ .get = JSBindings.getValue,
+ .ro = true,
+ },
+ .arguments = .{
+ .get = JSBindings.getCallArgs,
+ .ro = true,
+ },
+ .properties = .{
+ .get = JSBindings.getProperties,
.ro = true,
},
},
);
+ pub fn makeFromExpr(allocator: *std.mem.Allocator, expr: Expr) js.JSObjectRef {
+ var ptr = allocator.create(JSNode) catch unreachable;
+ ptr.* = JSNode.initExpr(expr);
+ // If we look at JSObjectMake, we can see that all it does with the ctx value is lookup what the global object is
+ // so it's safe to just avoid that and do it here like this:
+ return JSNode.Class.make(JavaScript.VirtualMachine.vm.global.ref(), ptr);
+ }
+ pub const JSBindings = struct {
+ const getAllocator = JSCBase.getAllocator;
+
+ threadlocal var temporary_call_args_array: [256]js.JSValueRef = undefined;
+ pub fn getCallArgs(
+ this: *JSNode,
+ ctx: js.JSContextRef,
+ thisObject: js.JSValueRef,
+ prop: js.JSStringRef,
+ exception: js.ExceptionRef,
+ ) js.JSObjectRef {
+ const args = this.data.callArgs();
+
+ switch (args.len) {
+ 0 => return js.JSObjectMakeArray(ctx, 0, null, exception),
+ 1...255 => {
+ var slice = temporary_call_args_array[0..args.len];
+ for (slice) |_, i| {
+ var node = JSCBase.getAllocator(ctx).create(JSNode) catch unreachable;
+ node.* = JSNode.initExpr(args[i]);
+ slice[i] = JSNode.Class.make(ctx, node);
+ }
+ return js.JSObjectMakeArray(ctx, args.len, slice.ptr, exception);
+ },
+ else => {
+ Output.prettyErrorln("are you for real? {d} args to your call expression? that has to be a bug.\n", .{args.len});
+ Output.flush();
+ return js.JSObjectMakeArray(ctx, 0, null, exception);
+ },
+ }
+ }
+
+ pub fn getProperties(
+ this: *JSNode,
+ ctx: js.JSContextRef,
+ thisObject: js.JSValueRef,
+ prop: js.JSStringRef,
+ exception: js.ExceptionRef,
+ ) js.JSObjectRef {
+ const args = if (this.data == .e_object) this.data.e_object.properties else &[_]G.Property{};
+
+ switch (args.len) {
+ 0 => return js.JSObjectMakeArray(ctx, 0, null, exception),
+ 1...255 => {
+ var slice = temporary_call_args_array[0..args.len];
+ for (slice) |_, i| {
+ var node = JSCBase.getAllocator(ctx).create(JSNode) catch unreachable;
+ node.* = JSNode{ .data = .{ .g_property = &args[i] }, .loc = this.loc };
+ slice[i] = JSNode.Class.make(ctx, node);
+ }
+ return js.JSObjectMakeArray(ctx, args.len, slice.ptr, exception);
+ },
+ else => {
+ return js.JSObjectMakeArray(ctx, 0, null, exception);
+ },
+ }
+ }
+
+ fn toNumberValue(this: *JSNode, number: E.Number) js.JSValueRef {
+ return JSC.JSValue.jsNumberFromDouble(number.value).asRef();
+ }
+
+ fn toStringValue(this: *JSNode, ctx: js.JSContextRef, str: E.String) js.JSObjectRef {
+ if (str.isBlank()) {
+ return JSC.ZigString.init("").toValue(JavaScript.VirtualMachine.vm.global).asRef();
+ }
+
+ if (str.isUTF8()) {
+ return JSC.ZigString.init(str.utf8).toValue(JavaScript.VirtualMachine.vm.global).asRef();
+ } else {
+ return js.JSValueMakeString(ctx, js.JSStringCreateWithCharactersNoCopy(str.value.ptr, str.value.len));
+ }
+ }
+
+ threadlocal var regex_value_array: [2]js.JSValueRef = undefined;
+
+ fn toRegexValue(this: *JSNode, ctx: js.JSContextRef, regex: *E.RegExp, exception: js.ExceptionRef) js.JSObjectRef {
+ if (regex.value.len == 0) {
+ return js.JSObjectMakeRegExp(ctx, 0, null, exception);
+ }
+
+ regex_value_array[0] = JSC.ZigString.init(regex.pattern()).toValue(JavaScript.VirtualMachine.vm.global).asRef();
+ regex_value_array[1] = JSC.ZigString.init(regex.flags()).toValue(JavaScript.VirtualMachine.vm.global).asRef();
+
+ return js.JSObjectMakeRegExp(ctx, 2, &regex_value_array, exception);
+ }
+
+ fn toArrayValue(this: *JSNode, ctx: js.JSContextRef, array: E.Array, exception: js.ExceptionRef) js.JSObjectRef {
+ if (array.items.len == 0) {
+ return js.JSObjectMakeArray(ctx, 0, null, exception);
+ }
+
+ for (array.items) |expr, i| {
+ var node = JSCBase.getAllocator(ctx).create(JSNode) catch unreachable;
+ node.* = JSNode.initExpr(expr);
+ temporary_call_args_array[i] = JSNode.Class.make(ctx, node);
+ }
+
+ return js.JSObjectMakeArray(ctx, array.items.len, &temporary_call_args_array, exception);
+ }
+
+ fn toArrayPrimitive(this: *JSNode, ctx: js.JSContextRef, array: E.Array, exception: js.ExceptionRef) js.JSObjectRef {
+ if (array.items.len == 0) {
+ return js.JSObjectMakeArray(ctx, 0, null, exception);
+ }
+
+ var node: JSNode = undefined;
+ for (array.items) |expr, i| {
+ node = JSNode.initExpr(expr);
+ temporary_call_args_array[i] = toPrimitive(&node, ctx, exception);
+ }
+
+ return js.JSObjectMakeArray(ctx, array.items.len, temporary_call_args_array[0..array.items.len].ptr, exception);
+ }
+
+ fn toObjectValue(this: *JSNode, ctx: js.JSContextRef, obj: E.Object, exception: js.ExceptionRef) js.JSObjectRef {
+ if (obj.properties.len == 0) {
+ return js.JSObjectMakeArray(ctx, 0, null, exception);
+ }
+
+ var object_properties_array: [64]js.JSObjectRef = undefined;
+
+ var did_allocate = false;
+ var properties_list = if (obj.properties.len < object_properties_array.len)
+ object_properties_array[0..obj.properties.len]
+ else brk: {
+ did_allocate = true;
+ break :brk getAllocator(ctx).alloc(js.JSObjectRef, obj.properties.len) catch unreachable;
+ };
+
+ defer if (did_allocate) getAllocator(ctx).free(properties_list);
+
+ for (obj.properties) |_, i| {
+ var node = JSCBase.getAllocator(ctx).create(JSNode) catch unreachable;
+ node.* = JSNode{
+ .data = .{
+ .g_property = &obj.properties[i],
+ },
+ .loc = this.loc,
+ };
+ properties_list[i] = JSNode.Class.make(ctx, node);
+ }
+
+ return js.JSObjectMakeArray(ctx, properties_list.len, properties_list.ptr, exception);
+ }
+
+ fn toObjectPrimitive(this: *JSNode, ctx: js.JSContextRef, obj: E.Object, exception: js.ExceptionRef) js.JSObjectRef {
+ return toObjectValue(this, ctx, obj, exception);
+ }
+
+ fn toPropertyPrimitive(this: *JSNode, ctx: js.JSContextRef, prop: G.Property, exception: js.ExceptionRef) js.JSObjectRef {
+ var entries: [3]js.JSValueRef = undefined;
+
+ entries[0] = js.JSValueMakeUndefined(ctx);
+ entries[1] = entries[0];
+ entries[2] = entries[0];
+
+ var other: JSNode = undefined;
+
+ if (prop.key) |key| {
+ other = JSNode.initExpr(key);
+ entries[0] = toPrimitive(
+ &other,
+ ctx,
+ exception,
+ ) orelse js.JSValueMakeUndefined(ctx);
+ }
+
+ if (prop.value) |value| {
+ other = JSNode.initExpr(value);
+ entries[1] = toPrimitive(
+ &other,
+ ctx,
+ exception,
+ ) orelse js.JSValueMakeUndefined(ctx);
+ }
+
+ if (prop.initializer) |value| {
+ other = JSNode.initExpr(value);
+ entries[2] = toPrimitive(
+ &other,
+ ctx,
+ exception,
+ ) orelse js.JSValueMakeUndefined(ctx);
+ }
+
+ const out = js.JSObjectMakeArray(ctx, 3, &entries, exception);
+ return out;
+ }
+
+ pub fn toString(
+ this: *JSNode,
+ ctx: js.JSContextRef,
+ function: js.JSObjectRef,
+ thisObject: js.JSObjectRef,
+ arguments: []const js.JSValueRef,
+ exception: js.ExceptionRef,
+ ) js.JSObjectRef {
+ switch (this.data) {
+ .e_string => |str| {
+ return toStringValue(this, ctx, str.*);
+ },
+ .e_template => |template| {
+ const str = template.head;
+
+ if (str.isBlank()) {
+ return JSC.ZigString.init("").toValue(JavaScript.VirtualMachine.vm.global).asRef();
+ }
+
+ if (str.isUTF8()) {
+ return JSC.ZigString.init(str.utf8).toValue(JavaScript.VirtualMachine.vm.global).asRef();
+ } else {
+ return js.JSValueMakeString(ctx, js.JSStringCreateWithCharactersNoCopy(str.value.ptr, str.value.len));
+ }
+ },
+ // .e_number => |number| {
+
+ // },
+ else => {
+ return JSC.ZigString.init("").toValue(JavaScript.VirtualMachine.vm.global).asRef();
+ },
+ }
+ }
+
+ fn toPrimitive(
+ this: *JSNode,
+ ctx: js.JSContextRef,
+ exception: js.ExceptionRef,
+ ) js.JSValueRef {
+ return @call(.{ .modifier = .always_inline }, toPrimitiveAllowRecursion, .{ this, ctx, exception, false });
+ }
+
+ fn toPrimitiveWithRecursion(
+ this: *JSNode,
+ ctx: js.JSContextRef,
+ exception: js.ExceptionRef,
+ ) js.JSValueRef {
+ return @call(.{ .modifier = .always_inline }, toPrimitiveAllowRecursion, .{ this, ctx, exception, true });
+ }
+
+ fn toPrimitiveAllowRecursion(this: *JSNode, ctx: js.JSContextRef, exception: js.ExceptionRef, comptime allow_recursion: bool) js.JSValueRef {
+ switch (this.data) {
+ .e_string => |str| {
+ return JSBindings.toStringValue(this, ctx, str.*);
+ },
+ .e_template => |template| {
+ return JSBindings.toStringValue(this, ctx, template.head);
+ // return JSBindings.toTemplatePrimitive(this, ctx, template.*);
+ },
+ .e_number => |number| {
+ return JSBindings.toNumberValue(this, number);
+ },
+ .e_reg_exp => |regex| {
+ return JSBindings.toRegexValue(this, ctx, regex, exception);
+ },
+ .e_object => |object| {
+ if (comptime !allow_recursion) return js.JSValueMakeUndefined(ctx);
+ return JSBindings.toObjectPrimitive(this, ctx, object.*, exception);
+ },
+ .e_array => |array| {
+ if (comptime !allow_recursion) return js.JSValueMakeUndefined(ctx);
+ return JSBindings.toArrayPrimitive(this, ctx, array.*, exception);
+ },
+
+ // Returns an Entry
+ // [string, number | regex | object | string | null | undefined]
+ .g_property => |property| {
+ return JSBindings.toPropertyPrimitive(this, ctx, property.*, exception);
+ },
+ .e_null => {
+ return js.JSValueMakeNull(ctx);
+ },
+ else => {
+ return js.JSValueMakeUndefined(ctx);
+ },
+ }
+ }
+
+ fn toValue(this: *JSNode, ctx: js.JSContextRef, exception: js.ExceptionRef) js.JSObjectRef {
+ switch (this.data) {
+ .e_await => |aw| {
+ return JSNode.makeFromExpr(getAllocator(ctx), aw.value);
+ },
+ .e_yield => |yi| {
+ return JSNode.makeFromExpr(getAllocator(ctx), yi.value orelse return null);
+ },
+ .e_spread => |spread| {
+ return JSNode.makeFromExpr(getAllocator(ctx), spread.value);
+ },
+ .e_reg_exp => |reg| {
+ return JSC.ZigString.toRef(reg.value, JavaScript.VirtualMachine.vm.global);
+ },
+
+ .e_array => |array| {
+ return toArrayValue(this, ctx, array.*, exception);
+ },
+ .e_object => |obj| {
+ return toObjectValue(this, ctx, obj.*, exception);
+ },
+ else => {
+ return null;
+ },
+ }
+ }
+
+ pub fn getValue(
+ this: *JSNode,
+ ctx: js.JSContextRef,
+ thisObject: js.JSValueRef,
+ prop: js.JSStringRef,
+ exception: js.ExceptionRef,
+ ) js.JSObjectRef {
+ return toValue(this, ctx, exception) orelse return thisObject;
+ }
+
+ pub fn get(
+ this: *JSNode,
+ ctx: js.JSContextRef,
+ function: js.JSObjectRef,
+ thisObject: js.JSObjectRef,
+ arguments: []const js.JSValueRef,
+ exception: js.ExceptionRef,
+ ) js.JSObjectRef {
+ return toPrimitiveWithRecursion(this, ctx, exception) orelse return js.JSValueMakeUndefined(ctx);
+ }
+
+ pub fn getTag(
+ this: *JSNode,
+ ctx: js.JSContextRef,
+ thisObject: js.JSValueRef,
+ prop: js.JSStringRef,
+ exception: js.ExceptionRef,
+ ) js.JSObjectRef {
+ return JSC.JSValue.jsNumberFromU16(@intCast(u16, @enumToInt(std.meta.activeTag(this.data)))).asRef();
+ }
+ pub fn getTagName(
+ this: *JSNode,
+ ctx: js.JSContextRef,
+ thisObject: js.JSValueRef,
+ prop: js.JSStringRef,
+ exception: js.ExceptionRef,
+ ) js.JSObjectRef {
+ return JSC.ZigString.init(@tagName(this.data)).toValue(JavaScript.VirtualMachine.vm.global).asRef();
+ }
+ pub fn getPosition(
+ this: *JSNode,
+ ctx: js.JSContextRef,
+ thisObject: js.JSValueRef,
+ prop: js.JSStringRef,
+ exception: js.ExceptionRef,
+ ) js.JSObjectRef {
+ return JSC.JSValue.jsNumberFromInt32(this.loc.start).asRef();
+ }
+ };
+
+ pub fn initExpr(this: Expr) JSNode {
+ switch (this.data) {
+ .e_array => |value| {
+ return JSNode{ .loc = this.loc, .data = .{ .e_array = value } };
+ },
+ .e_unary => |value| {
+ return JSNode{ .loc = this.loc, .data = .{ .e_unary = value } };
+ },
+ .e_binary => |value| {
+ return JSNode{ .loc = this.loc, .data = .{ .e_binary = value } };
+ },
+ .e_function => |value| {
+ return JSNode{ .loc = this.loc, .data = .{ .e_function = value } };
+ },
+ .e_new_target => |value| {
+ return JSNode{ .loc = this.loc, .data = .{ .e_new_target = value } };
+ },
+ .e_import_meta => |value| {
+ return JSNode{ .loc = this.loc, .data = .{ .e_import_meta = value } };
+ },
+ .e_call => |value| {
+ return JSNode{ .loc = this.loc, .data = .{ .e_call = value } };
+ },
+ .e_dot => |value| {
+ return JSNode{ .loc = this.loc, .data = .{ .e_dot = value } };
+ },
+ .e_index => |value| {
+ return JSNode{ .loc = this.loc, .data = .{ .e_index = value } };
+ },
+ .e_arrow => |value| {
+ return JSNode{ .loc = this.loc, .data = .{ .e_arrow = value } };
+ },
+ .e_identifier => |value| {
+ return JSNode{ .loc = this.loc, .data = .{ .e_identifier = value } };
+ },
+ .e_import_identifier => |value| {
+ return JSNode{ .loc = this.loc, .data = .{ .e_import_identifier = value } };
+ },
+ .e_private_identifier => |value| {
+ return JSNode{ .loc = this.loc, .data = .{ .e_private_identifier = value } };
+ },
+ .e_jsx_element => |value| {
+ return JSNode{ .loc = this.loc, .data = .{ .e_jsx_element = value } };
+ },
+ .e_big_int => |value| {
+ return JSNode{ .loc = this.loc, .data = .{ .e_big_int = value } };
+ },
+ .e_object => |value| {
+ return JSNode{ .loc = this.loc, .data = .{ .e_object = value } };
+ },
+ .e_spread => |value| {
+ return JSNode{ .loc = this.loc, .data = .{ .e_spread = value } };
+ },
+ .e_string => |value| {
+ return JSNode{ .loc = this.loc, .data = .{ .e_string = value } };
+ },
+ .e_template_part => |value| {
+ return JSNode{ .loc = this.loc, .data = .{ .e_template_part = value } };
+ },
+ .e_template => |value| {
+ return JSNode{ .loc = this.loc, .data = .{ .e_template = value } };
+ },
+ .e_reg_exp => |value| {
+ return JSNode{ .loc = this.loc, .data = .{ .e_reg_exp = value } };
+ },
+ .e_await => |value| {
+ return JSNode{ .loc = this.loc, .data = .{ .e_await = value } };
+ },
+ .e_yield => |value| {
+ return JSNode{ .loc = this.loc, .data = .{ .e_yield = value } };
+ },
+ .e_if => |value| {
+ return JSNode{ .loc = this.loc, .data = .{ .e_if = value } };
+ },
+ .e_require_or_require_resolve => |value| {
+ return JSNode{ .loc = this.loc, .data = .{ .e_require_or_require_resolve = value } };
+ },
+ .e_import => |value| {
+ return JSNode{ .loc = this.loc, .data = .{ .e_import = value } };
+ },
+ .e_this => |value| {
+ return JSNode{ .loc = this.loc, .data = .{ .e_this = value } };
+ },
+ .e_class => |value| {
+ return JSNode{ .loc = this.loc, .data = .{ .e_class = value } };
+ },
+ .e_require => |value| {
+ return JSNode{ .loc = this.loc, .data = .{ .e_require = value } };
+ },
+ .e_missing => |value| {
+ return JSNode{ .loc = this.loc, .data = .{ .e_missing = value } };
+ },
+ .e_boolean => |value| {
+ return JSNode{ .loc = this.loc, .data = .{ .e_boolean = value } };
+ },
+ .e_super => |value| {
+ return JSNode{ .loc = this.loc, .data = .{ .e_super = value } };
+ },
+ .e_null => |value| {
+ return JSNode{ .loc = this.loc, .data = .{ .e_null = value } };
+ },
+ .e_number => |value| {
+ return JSNode{ .loc = this.loc, .data = .{ .e_number = value } };
+ },
+ .e_undefined => |value| {
+ return JSNode{ .loc = this.loc, .data = .{ .e_undefined = value } };
+ },
+ else => {
+ return JSNode{ .loc = this.loc, .data = .{ .e_missing = .{} } };
+ },
+ }
+ }
+
+ pub fn toExpr(this: JSNode) Expr {
+ switch (this.data) {
+ .e_array => |value| {
+ return Expr{ .loc = this.loc, .data = .{ .e_array = value } };
+ },
+ .e_unary => |value| {
+ return Expr{ .loc = this.loc, .data = .{ .e_unary = value } };
+ },
+ .e_binary => |value| {
+ return Expr{ .loc = this.loc, .data = .{ .e_binary = value } };
+ },
+ .e_function => |value| {
+ return Expr{ .loc = this.loc, .data = .{ .e_function = value } };
+ },
+ .e_new_target => |value| {
+ return Expr{ .loc = this.loc, .data = .{ .e_new_target = value } };
+ },
+ .e_import_meta => |value| {
+ return Expr{ .loc = this.loc, .data = .{ .e_import_meta = value } };
+ },
+ .e_call => |value| {
+ return Expr{ .loc = this.loc, .data = .{ .e_call = value } };
+ },
+ .e_dot => |value| {
+ return Expr{ .loc = this.loc, .data = .{ .e_dot = value } };
+ },
+ .e_index => |value| {
+ return Expr{ .loc = this.loc, .data = .{ .e_index = value } };
+ },
+ .e_arrow => |value| {
+ return Expr{ .loc = this.loc, .data = .{ .e_arrow = value } };
+ },
+ .e_identifier => |value| {
+ return Expr{ .loc = this.loc, .data = .{ .e_identifier = value } };
+ },
+ .e_import_identifier => |value| {
+ return Expr{ .loc = this.loc, .data = .{ .e_import_identifier = value } };
+ },
+ .e_private_identifier => |value| {
+ return Expr{ .loc = this.loc, .data = .{ .e_private_identifier = value } };
+ },
+ .e_jsx_element => |value| {
+ return Expr{ .loc = this.loc, .data = .{ .e_jsx_element = value } };
+ },
+ .e_big_int => |value| {
+ return Expr{ .loc = this.loc, .data = .{ .e_big_int = value } };
+ },
+ .e_object => |value| {
+ return Expr{ .loc = this.loc, .data = .{ .e_object = value } };
+ },
+ .e_spread => |value| {
+ return Expr{ .loc = this.loc, .data = .{ .e_spread = value } };
+ },
+ .e_string => |value| {
+ return Expr{ .loc = this.loc, .data = .{ .e_string = value } };
+ },
+ .e_template_part => |value| {
+ return Expr{ .loc = this.loc, .data = .{ .e_template_part = value } };
+ },
+ .e_template => |value| {
+ return Expr{ .loc = this.loc, .data = .{ .e_template = value } };
+ },
+ .e_reg_exp => |value| {
+ return Expr{ .loc = this.loc, .data = .{ .e_reg_exp = value } };
+ },
+ .e_await => |value| {
+ return Expr{ .loc = this.loc, .data = .{ .e_await = value } };
+ },
+ .e_yield => |value| {
+ return Expr{ .loc = this.loc, .data = .{ .e_yield = value } };
+ },
+ .e_if => |value| {
+ return Expr{ .loc = this.loc, .data = .{ .e_if = value } };
+ },
+ .e_require_or_require_resolve => |value| {
+ return Expr{ .loc = this.loc, .data = .{ .e_require_or_require_resolve = value } };
+ },
+ .e_import => |value| {
+ return Expr{ .loc = this.loc, .data = .{ .e_import = value } };
+ },
+ .e_this => |value| {
+ return Expr{ .loc = this.loc, .data = .{ .e_this = value } };
+ },
+ .e_class => |value| {
+ return Expr{ .loc = this.loc, .data = .{ .e_class = value } };
+ },
+ .e_require => |value| {
+ return Expr{ .loc = this.loc, .data = .{ .e_require = value } };
+ },
+ .e_missing => |value| {
+ return Expr{ .loc = this.loc, .data = .{ .e_missing = value } };
+ },
+ .e_boolean => |value| {
+ return Expr{ .loc = this.loc, .data = .{ .e_boolean = value } };
+ },
+ .e_super => |value| {
+ return Expr{ .loc = this.loc, .data = .{ .e_super = value } };
+ },
+ .e_null => |value| {
+ return Expr{ .loc = this.loc, .data = .{ .e_null = value } };
+ },
+ .e_number => |value| {
+ return Expr{ .loc = this.loc, .data = .{ .e_number = value } };
+ },
+ .e_undefined => |value| {
+ return Expr{ .loc = this.loc, .data = .{ .e_undefined = value } };
+ },
+ else => {
+ return Expr{ .loc = this.loc, .data = .{ .e_missing = .{} } };
+ },
+ }
+ }
+
+ pub const Data = union(Tag) {
+ inline_false: void,
+ inline_true: void,
+ e_boolean: E.Boolean,
+ e_super: E.Super,
+ e_null: E.Null,
+ e_number: E.Number,
+ e_undefined: E.Undefined,
+ e_new_target: E.NewTarget,
+ e_import_meta: E.ImportMeta,
+ e_missing: E.Missing,
+ e_this: E.This,
+
+ e_array: *E.Array,
+ e_unary: *E.Unary,
+ e_binary: *E.Binary,
+ e_function: *E.Function,
+
+ e_call: *E.Call,
+ e_dot: *E.Dot,
+ e_index: *E.Index,
+ e_arrow: *E.Arrow,
+ e_identifier: *E.Identifier,
+ e_import_identifier: *E.ImportIdentifier,
+ e_private_identifier: *E.PrivateIdentifier,
+ e_jsx_element: *E.JSXElement,
+
+ e_big_int: *E.BigInt,
+ e_object: *E.Object,
+ e_spread: *E.Spread,
+ e_string: *E.String,
+ e_template_part: *E.TemplatePart,
+ e_template: *E.Template,
+ e_reg_exp: *E.RegExp,
+ e_await: *E.Await,
+ e_yield: *E.Yield,
+ e_if: *E.If,
+ e_require_or_require_resolve: *E.RequireOrRequireResolve,
+ e_import: *E.Import,
+
+ e_class: *E.Class,
+ e_require: *E.Require,
+
+ s_import: *S.Import,
+ s_block: *S.Block,
+
+ g_property: *G.Property,
+
+ pub fn callArgs(this: Data) ExprNodeList {
+ if (this == .e_call)
+ return this.e_call.args
+ else
+ return &[_]Expr{};
+ }
+
+ pub fn booleanValue(this: Data) bool {
+ return switch (this) {
+ .inline_false => false,
+ .inline_true => true,
+ .e_boolean => this.e_boolean.value,
+ };
+ }
+ };
+ pub const Tag = enum(u8) {
+ e_array,
+ e_unary,
+ e_binary,
+ e_function,
+ e_new_target,
+ e_import_meta,
+ e_call,
+ e_dot,
+ e_index,
+ e_arrow,
+ e_identifier,
+ e_import_identifier,
+ e_private_identifier,
+ e_jsx_element,
+ e_big_int,
+ e_object,
+ e_spread,
+ e_string,
+ e_template_part,
+ e_template,
+ e_reg_exp,
+ e_await,
+ e_yield,
+ e_if,
+ e_require_or_require_resolve,
+ e_import,
+ e_this,
+ e_class,
+ e_require,
+ s_import,
+ s_block,
+
+ g_property,
+
+ e_missing,
+ e_boolean,
+ e_super,
+ e_null,
+ e_number,
+ e_undefined,
+
+ inline_true,
+ inline_false,
+
+ pub const ids: std.EnumArray(Tag, Expr.Data) = brk: {
+ var list = std.EnumArray(Tag, Expr.Data).initFill(Expr.Data{ .e_number = E.Number{ .value = 0.0 } });
+ list.set(Tag.e_array, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_array)) },
+ });
+ list.set(Tag.e_unary, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_unary)) },
+ });
+ list.set(Tag.e_binary, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_binary)) },
+ });
+ list.set(Tag.e_boolean, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_boolean)) },
+ });
+ list.set(Tag.e_super, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_super)) },
+ });
+ list.set(Tag.e_null, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_null)) },
+ });
+ list.set(Tag.e_undefined, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_undefined)) },
+ });
+ list.set(Tag.e_function, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_function)) },
+ });
+ list.set(Tag.e_new_target, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_new_target)) },
+ });
+ list.set(Tag.e_import_meta, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_import_meta)) },
+ });
+ list.set(Tag.e_call, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_call)) },
+ });
+ list.set(Tag.e_dot, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_dot)) },
+ });
+ list.set(Tag.e_index, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_index)) },
+ });
+ list.set(Tag.e_arrow, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_arrow)) },
+ });
+ list.set(Tag.e_identifier, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_identifier)) },
+ });
+ list.set(Tag.e_import_identifier, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_import_identifier)) },
+ });
+ list.set(Tag.e_private_identifier, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_private_identifier)) },
+ });
+ list.set(Tag.e_jsx_element, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_jsx_element)) },
+ });
+ list.set(Tag.e_missing, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_missing)) },
+ });
+ list.set(Tag.e_number, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_number)) },
+ });
+ list.set(Tag.e_big_int, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_big_int)) },
+ });
+ list.set(Tag.e_object, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_object)) },
+ });
+ list.set(Tag.e_spread, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_spread)) },
+ });
+ list.set(Tag.e_string, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_string)) },
+ });
+ list.set(Tag.e_template_part, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_template_part)) },
+ });
+ list.set(Tag.e_template, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_template)) },
+ });
+ list.set(Tag.e_reg_exp, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_reg_exp)) },
+ });
+ list.set(Tag.e_await, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_await)) },
+ });
+ list.set(Tag.e_yield, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_yield)) },
+ });
+ list.set(Tag.e_if, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_if)) },
+ });
+ list.set(Tag.e_import, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_import)) },
+ });
+ list.set(Tag.e_this, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_this)) },
+ });
+ list.set(Tag.e_class, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_class)) },
+ });
+ list.set(Tag.e_require, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.e_require)) },
+ });
+ list.set(Tag.s_import, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.s_import)) },
+ });
+ list.set(Tag.g_property, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.g_property)) },
+ });
+ list.set(Tag.s_block, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.s_block)) },
+ });
+ list.set(Tag.inline_true, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.inline_true)) },
+ });
+ list.set(Tag.inline_false, Expr.Data{
+ .e_number = E.Number{ .value = @intToFloat(f64, @enumToInt(Tag.inline_false)) },
+ });
+ break :brk list;
+ };
+
+ pub const names = std.ComptimeStringMap(Tag, .{
+ .{ "array", Tag.e_array },
+ .{ "unary", Tag.e_unary },
+ .{ "binary", Tag.e_binary },
+ .{ "bool", Tag.e_boolean },
+ .{ "super", Tag.e_super },
+ .{ "null", Tag.e_null },
+ .{ "undefined", Tag.e_undefined },
+ .{ "function", Tag.e_function },
+ .{ "new_target", Tag.e_new_target },
+ .{ "import_meta", Tag.e_import_meta },
+ .{ "call", Tag.e_call },
+ .{ "dot", Tag.e_dot },
+ .{ "index", Tag.e_index },
+ .{ "arrow", Tag.e_arrow },
+ .{ "id", Tag.e_identifier },
+ .{ "import-id", Tag.e_import_identifier },
+ .{ "private-id", Tag.e_private_identifier },
+ .{ "jsx", Tag.e_jsx_element },
+ .{ "missing", Tag.e_missing },
+ .{ "number", Tag.e_number },
+ .{ "bigint", Tag.e_big_int },
+ .{ "object", Tag.e_object },
+ .{ "spread", Tag.e_spread },
+ .{ "string", Tag.e_string },
+ .{ "template-part", Tag.e_template_part },
+ .{ "template", Tag.e_template },
+ .{ "regex", Tag.e_reg_exp },
+ .{ "await", Tag.e_await },
+ .{ "yield", Tag.e_yield },
+ .{ "if", Tag.e_if },
+ .{ "dynamic", Tag.e_import },
+ .{ "this", Tag.e_this },
+ .{ "class", Tag.e_class },
+ .{ "require", Tag.e_require },
+ .{ "import", Tag.s_import },
+ .{ "property", Tag.g_property },
+ .{ "block", Tag.s_block },
+ .{ "true", Tag.inline_true },
+ .{ "false", Tag.inline_false },
+ });
+
+ pub const as_expr_tag: std.EnumArray(Tag, Expr.Tag) = brk: {
+ var list = std.EnumArray(Tag, Expr.Tag).initFill(Expr.Tag.e_missing);
+ list.set(Tag.e_array, Expr.Tag.e_array);
+ list.set(Tag.e_unary, Expr.Tag.e_unary);
+ list.set(Tag.e_binary, Expr.Tag.e_binary);
+ list.set(Tag.e_boolean, Expr.Tag.e_boolean);
+ list.set(Tag.e_super, Expr.Tag.e_super);
+ list.set(Tag.e_null, Expr.Tag.e_null);
+ list.set(Tag.e_undefined, Expr.Tag.e_undefined);
+ list.set(Tag.e_function, Expr.Tag.e_function);
+ list.set(Tag.e_new_target, Expr.Tag.e_new_target);
+ list.set(Tag.e_import_meta, Expr.Tag.e_import_meta);
+ list.set(Tag.e_call, Expr.Tag.e_call);
+ list.set(Tag.e_dot, Expr.Tag.e_dot);
+ list.set(Tag.e_index, Expr.Tag.e_index);
+ list.set(Tag.e_arrow, Expr.Tag.e_arrow);
+ list.set(Tag.e_identifier, Expr.Tag.e_identifier);
+ list.set(Tag.e_import_identifier, Expr.Tag.e_import_identifier);
+ list.set(Tag.e_private_identifier, Expr.Tag.e_private_identifier);
+ list.set(Tag.e_jsx_element, Expr.Tag.e_jsx_element);
+ list.set(Tag.e_missing, Expr.Tag.e_missing);
+ list.set(Tag.e_number, Expr.Tag.e_number);
+ list.set(Tag.e_big_int, Expr.Tag.e_big_int);
+ list.set(Tag.e_object, Expr.Tag.e_object);
+ list.set(Tag.e_spread, Expr.Tag.e_spread);
+ list.set(Tag.e_string, Expr.Tag.e_string);
+ list.set(Tag.e_template_part, Expr.Tag.e_template_part);
+ list.set(Tag.e_template, Expr.Tag.e_template);
+ list.set(Tag.e_reg_exp, Expr.Tag.e_reg_exp);
+ list.set(Tag.e_await, Expr.Tag.e_await);
+ list.set(Tag.e_yield, Expr.Tag.e_yield);
+ list.set(Tag.e_if, Expr.Tag.e_if);
+ list.set(Tag.e_require_or_require_resolve, Expr.Tag.e_require_or_require_resolve);
+ list.set(Tag.e_import, Expr.Tag.e_import);
+ list.set(Tag.e_this, Expr.Tag.e_this);
+ list.set(Tag.e_class, Expr.Tag.e_class);
+ list.set(Tag.e_require, Expr.Tag.e_require);
+ break :brk list;
+ };
+
+ pub const to_expr_tag: std.EnumArray(Expr.Tag, Tag) = brk: {
+ var list = std.EnumArray(Expr.Tag, Tag).initFill(Tag.wip);
+ list.set(Expr.Tag.e_array, Tag.e_array);
+ list.set(Expr.Tag.e_unary, Tag.e_unary);
+ list.set(Expr.Tag.e_binary, Tag.e_binary);
+ list.set(Expr.Tag.e_boolean, Tag.e_boolean);
+ list.set(Expr.Tag.e_super, Tag.e_super);
+ list.set(Expr.Tag.e_null, Tag.e_null);
+ list.set(Expr.Tag.e_undefined, Tag.e_undefined);
+ list.set(Expr.Tag.e_function, Tag.e_function);
+ list.set(Expr.Tag.e_new_target, Tag.e_new_target);
+ list.set(Expr.Tag.e_import_meta, Tag.e_import_meta);
+ list.set(Expr.Tag.e_call, Tag.e_call);
+ list.set(Expr.Tag.e_dot, Tag.e_dot);
+ list.set(Expr.Tag.e_index, Tag.e_index);
+ list.set(Expr.Tag.e_arrow, Tag.e_arrow);
+ list.set(Expr.Tag.e_identifier, Tag.e_identifier);
+ list.set(Expr.Tag.e_import_identifier, Tag.e_import_identifier);
+ list.set(Expr.Tag.e_private_identifier, Tag.e_private_identifier);
+ list.set(Expr.Tag.e_jsx_element, Tag.e_jsx_element);
+ list.set(Expr.Tag.e_missing, Tag.e_missing);
+ list.set(Expr.Tag.e_number, Tag.e_number);
+ list.set(Expr.Tag.e_big_int, Tag.e_big_int);
+ list.set(Expr.Tag.e_object, Tag.e_object);
+ list.set(Expr.Tag.e_spread, Tag.e_spread);
+ list.set(Expr.Tag.e_string, Tag.e_string);
+ list.set(Expr.Tag.e_template_part, Tag.e_template_part);
+ list.set(Expr.Tag.e_template, Tag.e_template);
+ list.set(Expr.Tag.e_reg_exp, Tag.e_reg_exp);
+ list.set(Expr.Tag.e_await, Tag.e_await);
+ list.set(Expr.Tag.e_yield, Tag.e_yield);
+ list.set(Expr.Tag.e_if, Tag.e_if);
+ list.set(Expr.Tag.e_require_or_require_resolve, Tag.e_require_or_require_resolve);
+ list.set(Expr.Tag.e_import, Tag.e_import);
+ list.set(Expr.Tag.e_this, Tag.e_this);
+ list.set(Expr.Tag.e_class, Tag.e_class);
+ list.set(Expr.Tag.e_require, Tag.e_require);
+ break :brk list;
+ };
+
+ pub const Validator = struct {
+ pub const List = std.EnumArray(JSNode.Tag, bool);
+ fn NewList(comptime valid_tags: anytype) List {
+ return comptime brk: {
+ var list = List.initFill(false);
+ for (std.meta.fieldNames(@TypeOf(valid_tags))) |index| {
+ const name = @tagName(@field(valid_tags, index));
+
+ if (!@hasField(JSNode.Tag, name)) {
+ @compileError(
+ "JSNode.Tag does not have a \"" ++ name ++ "\" field. Valid fields are " ++ std.fmt.comptimePrint(
+ "{s}",
+ .{
+ std.meta.fieldNames(@TypeOf(valid_tags)),
+ },
+ ),
+ );
+ }
+ list.set(@field(JSNode.Tag, name), true);
+ }
+
+ break :brk list;
+ };
+ }
+
+ pub const valid_object_tags = Tag.Validator.NewList(.{
+ .g_property,
+ .e_spread,
+ .e_identifier,
+ .e_import_identifier,
+ .e_index,
+ .e_call,
+ .e_private_identifier,
+ .e_dot,
+ .e_unary,
+ .e_binary,
+ });
+ };
+
+ pub const max_tag: u8 = brk: {
+ const Enum: std.builtin.TypeInfo.Enum = @typeInfo(Tag).Enum;
+ var max_value: u8 = 0;
+ for (Enum.fields) |field| {
+ max_value = std.math.max(@as(u8, field.value), max_value);
+ }
+ break :brk max_value;
+ };
+
+ pub const min_tag: u8 = brk: {
+ const Enum: std.builtin.TypeInfo.Enum = @typeInfo(Tag).Enum;
+ var min: u8 = 255;
+ for (Enum.fields) |field| {
+ min = std.math.min(@as(u8, field.value), min);
+ }
+ break :brk min;
+ };
+ };
+
+ pub fn NewJSXWriter(comptime P: type) type {
+ return struct {
+ const JSXWriter = @This();
+ p: *P,
+ bun_jsx_ref: Ref,
+ log: *logger.Log,
+ args: ExprList,
+ bun_identifier: *E.Identifier,
+ allocator: *std.mem.Allocator,
+ parent_tag: Tag = Tag.e_missing,
+
+ pub fn initWriter(p: *P, bun_identifier: *E.Identifier) JSXWriter {
+ return JSXWriter{
+ .p = p,
+ .log = p.log,
+ .bun_jsx_ref = p.bun_jsx_ref,
+ .args = ExprList.init(p.allocator),
+ .allocator = p.allocator,
+ .bun_identifier = bun_identifier,
+ };
+ }
+
+ fn hasPropertyNamed(props: []G.Property, comptime name: string) bool {
+ return indexOfPropertyByName(props, name) != null;
+ }
+
+ fn indexOfPropertyByName(props: []G.Property, comptime name: string) ?u32 {
+ for (props) |prop, i| {
+ const key = prop.key orelse continue;
+ if (key.data != .e_string or !key.data.e_string.isUTF8()) continue;
+ if (strings.eqlComptime(key.data.e_string.utf8, name)) return @intCast(u32, i);
+ }
+
+ return null;
+ }
+
+ fn propertyValueNamed(props: []G.Property, comptime name: string) ?Expr {
+ for (props) |prop| {
+ const key = prop.key orelse continue;
+ if (key.data != .e_string or !key.data.e_string.isUTF8()) continue;
+ if (strings.eqlComptime(key.data.e_string.utf8, name)) return prop.value;
+ }
+
+ return null;
+ }
+
+ pub fn writeExprType(self: *JSXWriter, expr: Expr) bool {}
+
+ pub fn writeNodeType(self: *JSXWriter, tag: JSNode.Tag, props: []G.Property, children: []Expr, loc: logger.Loc) bool {
+ switch (tag) {
+
+ // <bool value={foo} />
+ // intended for dynamic values
+ Tag.e_boolean => {
+ self.args.ensureUnusedCapacity(2) catch unreachable;
+ self.args.appendAssumeCapacity(Expr{ .loc = loc, .data = comptime Tag.ids.get(Tag.e_boolean) });
+ const value_i = indexOfPropertyByName(props, "value") orelse {
+ self.log.addError(self.p.source, loc, "<bool> should have a \"value\" prop") catch unreachable;
+ self.args.append(Expr{ .data = .{ .e_boolean = .{ .value = true } }, .loc = loc }) catch unreachable;
+ return true;
+ };
+ const value = props[value_i].value orelse Expr{ .data = .{ .e_boolean = .{ .value = true } }, .loc = loc };
+
+ switch (value.data) {
+ .e_jsx_element => |el| {
+ return self.writeElement(el.*);
+ },
+ .e_string => {
+ self.log.addError(self.p.source, value.loc, "\"value\" shouldn't be a string") catch unreachable;
+ self.args.appendAssumeCapacity(Expr{ .data = .{ .e_boolean = .{ .value = true } }, .loc = value.loc });
+ },
+ .e_boolean => {
+ self.args.appendAssumeCapacity(value);
+ },
+ .e_missing => {
+ self.args.appendAssumeCapacity(Expr{ .data = .{ .e_boolean = .{ .value = true } }, .loc = value.loc });
+ },
+ // null and undefined literals are coerced to false
+ .e_null, .e_undefined => {
+ self.args.appendAssumeCapacity(Expr{ .data = .{ .e_boolean = .{ .value = false } }, .loc = value.loc });
+ },
+ .e_number => {
+ // Numbers are cooerced to booleans
+ self.args.appendAssumeCapacity(Expr{ .data = .{ .e_boolean = .{ .value = value.data.e_number.value > 0.0 } }, .loc = value.loc });
+ },
+ // these ones are not statically analyzable so we just leave them in as-is
+ .e_if, .e_identifier, .e_import_identifier, .e_index, .e_call, .e_private_identifier, .e_dot, .e_unary, .e_binary => {
+ self.args.appendAssumeCapacity(self.p.visitExpr(value));
+ },
+ // everything else is invalid
+ else => {
+ self.log.addError(self.p.source, value.loc, "\"value\" should be a bool, jsx element, number, identifier, index, call, private identifier, or dot") catch unreachable;
+ self.args.appendAssumeCapacity(Expr{ .data = .{ .e_boolean = .{ .value = false } }, .loc = value.loc });
+ },
+ }
+
+ return true;
+ },
+ // <number value={1.0} />
+ Tag.e_number => {
+ self.args.ensureUnusedCapacity(2) catch unreachable;
+ self.args.appendAssumeCapacity(Expr{ .loc = loc, .data = comptime Tag.ids.get(Tag.e_number) });
+ const invalid_value = Expr{ .data = .{ .e_number = .{ .value = 0.0 } }, .loc = loc };
+ const value_i = indexOfPropertyByName(props, "value") orelse {
+ self.log.addError(self.p.source, loc, "<number> should have a \"value\" prop") catch unreachable;
+ self.args.append(invalid_value) catch unreachable;
+ return true;
+ };
+ const value = props[value_i].value orelse invalid_value;
+
+ switch (value.data) {
+ .e_jsx_element => |el| {
+ return self.writeElement(el.*);
+ },
+ .e_string => {
+ self.log.addError(self.p.source, loc, "<number> should not be a string.") catch unreachable;
+ self.args.appendAssumeCapacity(invalid_value);
+ },
+ .e_boolean => {
+ // Booleans are cooerced to numbers
+ self.args.appendAssumeCapacity(
+ Expr{
+ .data = .{
+ .e_number = E.Number{
+ .value = @intToFloat(f64, @boolToInt(value.data.e_boolean.value)),
+ },
+ },
+ .loc = value.loc,
+ },
+ );
+ },
+ .e_missing => {
+ self.args.appendAssumeCapacity(invalid_value);
+ },
+ // null and undefined literals are coerced to 0
+ .e_null, .e_undefined => {
+ self.args.appendAssumeCapacity(Expr{ .data = .{ .e_number = .{ .value = 0 } }, .loc = value.loc });
+ },
+ // <number>123</number>
+ .e_number => {
+ // Numbers are cooerced to booleans
+ self.args.appendAssumeCapacity(value);
+ },
+ // these ones are not statically analyzable so we just leave them in as-is
+ .e_if, .e_identifier, .e_import_identifier, .e_index, .e_call, .e_private_identifier, .e_dot, .e_unary, .e_binary => {
+ self.args.appendAssumeCapacity(self.p.visitExpr(value));
+ },
+ // everything else is invalid
+ else => {
+ self.log.addError(self.p.source, value.loc, "<number value> should be a number, jsx element, identifier, index, call, private identifier, or dot expression") catch unreachable;
+ self.args.appendAssumeCapacity(invalid_value);
+ },
+ }
+
+ return true;
+ },
+ Tag.e_big_int => {
+ self.args.ensureUnusedCapacity(2) catch unreachable;
+ self.args.appendAssumeCapacity(Expr{ .loc = loc, .data = comptime Tag.ids.get(Tag.e_big_int) });
+ const invalid_value = Expr{ .data = .{ .e_big_int = &E.BigInt.empty }, .loc = loc };
+ const value_i = indexOfPropertyByName(props, "value") orelse {
+ self.log.addError(self.p.source, loc, "<big-int> should have a \"value\" prop") catch unreachable;
+ self.args.append(invalid_value) catch unreachable;
+ return true;
+ };
+ const value = props[value_i].value orelse invalid_value;
+
+ switch (value.data) {
+ .e_jsx_element => |el| {
+ return self.writeElement(el.*);
+ },
+ .e_string => |str| {
+ self.args.appendAssumeCapacity(Expr.alloc(self.allocator, E.BigInt, E.BigInt{ .value = std.mem.trimRight(u8, str.utf8, "n") }, value.loc));
+ },
+ .e_big_int => |bigint| {
+ self.args.appendAssumeCapacity(value);
+ },
+ .e_missing => {
+ self.args.appendAssumeCapacity(invalid_value);
+ },
+ // null and undefined literals are coerced to 0
+ .e_null, .e_undefined => {
+ self.args.appendAssumeCapacity(Expr{ .data = .{ .e_big_int = &E.BigInt.empty }, .loc = value.loc });
+ },
+ // these ones are not statically analyzable so we just leave them in as-is
+ .e_if, .e_identifier, .e_import_identifier, .e_index, .e_call, .e_private_identifier, .e_dot, .e_unary, .e_binary => {
+ self.args.appendAssumeCapacity(self.p.visitExpr(value));
+ },
+ // everything else is invalid
+ else => {
+ self.log.addError(self.p.source, value.loc, "\"value\" should be a BigInt, jsx element, identifier, index, call, private identifier, or dot expression") catch unreachable;
+ self.args.appendAssumeCapacity(invalid_value);
+ },
+ }
+
+ return true;
+ },
+ Tag.e_array => {
+ self.args.ensureUnusedCapacity(2 + children.len) catch unreachable;
+ self.args.appendAssumeCapacity(Expr{ .loc = loc, .data = comptime Tag.ids.get(Tag.e_array) });
+ const children_count = @truncate(u16, children.len);
+ self.args.appendAssumeCapacity(Expr{ .loc = loc, .data = .{ .e_number = E.Number{ .value = @intToFloat(f64, children_count) } } });
+
+ var old_parent = self.parent_tag;
+ self.parent_tag = Tag.e_array;
+ defer self.parent_tag = old_parent;
+ for (children) |child, i| {
+ switch (child.data) {
+ .e_jsx_element => |el| {
+ if (!self.writeElement(el.*)) return false;
+ },
+ // TODO: handle when simplification changes the expr type
+ .e_spread, .e_identifier, .e_import_identifier, .e_index, .e_call, .e_private_identifier, .e_dot, .e_unary, .e_binary => {
+ const visited_expr = self.p.visitExpr(child);
+ switch (visited_expr.data) {
+ .e_jsx_element => |el| {
+ if (!self.writeElement(el.*)) return false;
+ },
+ .e_if, .e_spread, .e_identifier, .e_import_identifier, .e_index, .e_call, .e_private_identifier, .e_dot, .e_unary, .e_binary => {
+ self.args.append(visited_expr) catch unreachable;
+ },
+ else => {
+ self.log.addError(self.p.source, visited_expr.loc, "<array> should only contain other jsx elements") catch unreachable;
+ self.args.append(Expr{ .data = .{ .e_missing = E.Missing{} }, .loc = visited_expr.loc }) catch unreachable;
+ },
+ }
+ },
+ else => {
+ self.log.addError(self.p.source, child.loc, "<array> should only contain other jsx elements") catch unreachable;
+ self.args.append(Expr{ .data = .{ .e_missing = E.Missing{} }, .loc = child.loc }) catch unreachable;
+ },
+ }
+ }
+
+ return true;
+ },
+ Tag.e_object => {
+ self.args.ensureUnusedCapacity(2 + children.len) catch unreachable;
+ self.args.appendAssumeCapacity(Expr{ .loc = loc, .data = comptime Tag.ids.get(Tag.e_object) });
+ const children_count = @truncate(u16, children.len);
+ self.args.appendAssumeCapacity(Expr{ .loc = loc, .data = .{ .e_number = E.Number{ .value = @intToFloat(f64, children_count) } } });
+
+ var old_parent = self.parent_tag;
+ self.parent_tag = Tag.e_object;
+ defer self.parent_tag = old_parent;
+
+ for (children) |child, i| {
+ switch (child.data) {
+ .e_jsx_element => |el| {
+ if (!self.writeElementWithValidTagList(el.*, comptime Tag.Validator.valid_object_tags)) return false;
+ },
+ .e_if, .e_spread, .e_identifier, .e_import_identifier, .e_index, .e_call, .e_private_identifier, .e_dot, .e_unary, .e_binary => {
+ const visited = self.p.visitExpr(child);
+ switch (visited.data) {
+ .e_jsx_element => |el| {
+ if (!self.writeElementWithValidTagList(el.*, comptime Tag.Validator.valid_object_tags)) return false;
+ },
+ .e_if, .e_spread, .e_identifier, .e_import_identifier, .e_index, .e_call, .e_private_identifier, .e_dot, .e_unary, .e_binary => {
+ self.args.append(visited) catch unreachable;
+ },
+ else => {
+ self.log.addError(self.p.source, child.loc, "<object> should only contain other jsx elements") catch unreachable;
+ self.args.append(Expr{ .data = .{ .e_missing = E.Missing{} }, .loc = child.loc }) catch unreachable;
+ },
+ }
+ },
+ else => {
+ self.log.addError(self.p.source, child.loc, "<object> should only contain other jsx elements") catch unreachable;
+ self.args.append(Expr{ .data = .{ .e_missing = E.Missing{} }, .loc = child.loc }) catch unreachable;
+ },
+ }
+ }
+
+ return true;
+ },
+
+ Tag.g_property => {
+ const name_property = propertyValueNamed(props, "name");
+ const value_property = propertyValueNamed(props, "value");
+ const init_property = propertyValueNamed(props, "init");
+
+ var old_parent = self.parent_tag;
+ if (old_parent != .e_object) {
+ self.args.append(Expr{ .loc = loc, .data = comptime Tag.ids.get(Tag.g_property) }) catch unreachable;
+ }
+
+ self.parent_tag = Tag.g_property;
+ defer self.parent_tag = old_parent;
+
+ var is_spread = false;
+ if (value_property) |prop| {
+ switch (prop.data) {
+ .e_jsx_element => |el| {
+ if (!self.writeElement(el.*)) return false;
+ },
+ .e_if, .e_spread, .e_identifier, .e_import_identifier, .e_index, .e_call, .e_private_identifier, .e_dot, .e_unary, .e_binary => {
+ self.args.append(self.p.visitExpr(prop)) catch unreachable;
+ },
+ else => {
+ self.log.addError(self.p.source, prop.loc, "value should only contain other jsx elements") catch unreachable;
+ self.args.append(Expr{ .data = .{ .e_missing = E.Missing{} }, .loc = prop.loc }) catch unreachable;
+ },
+ }
+ } else {
+ self.args.append(Expr{ .data = comptime Tag.ids.get(.e_undefined), .loc = loc }) catch unreachable;
+ }
+
+ if (init_property) |prop| {
+ switch (prop.data) {
+ .e_jsx_element => |el| {
+ if (!self.writeElement(el.*)) return false;
+ },
+
+ .e_spread, .e_identifier, .e_import_identifier, .e_index, .e_call, .e_private_identifier, .e_dot, .e_unary, .e_binary => {
+ self.args.append(self.p.visitExpr(prop)) catch unreachable;
+ },
+ else => {
+ self.log.addError(self.p.source, prop.loc, "init should only contain other jsx elements") catch unreachable;
+ self.args.append(Expr{ .data = .{ .e_missing = E.Missing{} }, .loc = prop.loc }) catch unreachable;
+ },
+ }
+ } else {
+ self.args.append(Expr{ .data = comptime Tag.ids.get(.e_undefined), .loc = loc }) catch unreachable;
+ }
+
+ if (name_property) |prop| {
+ switch (prop.data) {
+ .e_jsx_element => |el| {
+ if (!self.writeElement(el.*)) return false;
+ },
+ .e_string => |str| {
+ self.args.append(prop) catch unreachable;
+ },
+ .e_if, .e_spread, .e_identifier, .e_import_identifier, .e_index, .e_call, .e_private_identifier, .e_dot, .e_unary, .e_binary => {
+ self.args.append(self.p.visitExpr(prop)) catch unreachable;
+ },
+ else => {
+ self.log.addError(self.p.source, prop.loc, "should only contain other jsx elements or a string") catch unreachable;
+ self.args.append(Expr{ .data = .{ .e_missing = E.Missing{} }, .loc = prop.loc }) catch unreachable;
+ },
+ }
+ }
+
+ return true;
+ },
+ Tag.e_string => {
+ self.args.ensureUnusedCapacity(2) catch unreachable;
+ self.args.appendAssumeCapacity(Expr{ .loc = loc, .data = comptime Tag.ids.get(Tag.e_string) });
+ const invalid_value = Expr{ .data = .{ .e_string = &E.String.empty }, .loc = loc };
+ const value_i = indexOfPropertyByName(props, "value") orelse {
+ self.log.addError(self.p.source, loc, "<string> should have a \"value\" prop") catch unreachable;
+ self.args.append(invalid_value) catch unreachable;
+ return true;
+ };
+ const value = props[value_i].value orelse invalid_value;
+
+ switch (value.data) {
+ .e_jsx_element => |el| {
+ return self.writeElement(el.*);
+ },
+ .e_string => {
+ self.args.appendAssumeCapacity(value);
+ },
+ .e_missing => {
+ self.args.appendAssumeCapacity(invalid_value);
+ },
+ // null is cooerced to "null"
+ .e_null => {
+ self.args.appendAssumeCapacity(Expr{ .loc = value.loc, .data = .{ .e_string = &E.String.@"null" } });
+ },
+ // undefined is cooerced to "undefined"
+ .e_undefined => {
+ self.args.appendAssumeCapacity(Expr{ .loc = value.loc, .data = .{ .e_string = &E.String.@"undefined" } });
+ },
+ .e_boolean => |boolean| {
+ self.args.appendAssumeCapacity(Expr{ .loc = value.loc, .data = .{ .e_string = if (boolean.value) &E.String.@"true" else &E.String.@"false" } });
+ },
+ // these ones are not statically analyzable so we just leave them in as-is
+ .e_if, .e_identifier, .e_import_identifier, .e_index, .e_call, .e_private_identifier, .e_dot, .e_unary, .e_binary => {
+ self.args.appendAssumeCapacity(self.p.visitExpr(value));
+ },
+ // everything else is invalid
+ else => {
+ self.log.addError(self.p.source, value.loc, "<string value> should be a string, jsx element, identifier, index, call, private identifier, or dot expression") catch unreachable;
+ self.args.appendAssumeCapacity(invalid_value);
+ },
+ }
+ },
+ Tag.e_reg_exp => {
+ self.args.ensureUnusedCapacity(2) catch unreachable;
+ self.args.appendAssumeCapacity(Expr{ .loc = loc, .data = comptime Tag.ids.get(Tag.e_reg_exp) });
+ const invalid_value = Expr{ .data = .{ .e_reg_exp = &E.RegExp.empty }, .loc = loc };
+
+ const value_i = indexOfPropertyByName(props, "value") orelse {
+ self.log.addError(self.p.source, loc, "<regex> should have a \"value\" prop") catch unreachable;
+ self.args.append(invalid_value) catch unreachable;
+ return true;
+ };
+
+ const value = props[value_i].value orelse invalid_value;
+
+ switch (value.data) {
+ .e_string => |str| {
+ self.args.appendAssumeCapacity(Expr.alloc(self.allocator, E.RegExp, E.RegExp{ .value = str.utf8 }, value.loc));
+ },
+ .e_reg_exp => {
+ self.args.appendAssumeCapacity(value);
+ },
+ .e_missing, .e_null, .e_undefined => {
+ self.args.appendAssumeCapacity(invalid_value);
+ },
+ // these ones are not statically analyzable so we just leave them in as-is
+ .e_if, .e_identifier, .e_import_identifier, .e_index, .e_call, .e_private_identifier, .e_dot, .e_unary, .e_binary => {
+ self.args.appendAssumeCapacity(self.p.visitExpr(value));
+ },
+ // everything else is invalid
+ else => {
+ self.log.addError(self.p.source, value.loc, "<regex value> should be a string, jsx element, identifier, index, call, private identifier, or dot expression") catch unreachable;
+ self.args.appendAssumeCapacity(invalid_value);
+ },
+ }
+
+ return true;
+ },
+ // Tag.e_jsx_element => unreachable,
+ // Tag.e_identifier => {
+ // // self.args.ensureUnusedCapacity(2) catch unreachable;
+ // Global.notimpl();
+ // },
+ // Tag.e_import_identifier => {
+ // Global.notimpl();
+ // },
+ // Tag.e_private_identifier => {
+ // Global.notimpl();
+ // },
+
+ // Tag.e_unary => {
+
+ // },
+ // Tag.e_binary => {},
+
+ // Tag.e_function => {},
+ // Tag.e_new_target => {},
+ // Tag.e_import_meta => {},
+ // Tag.e_call => {},
+ // Tag.e_dot => {},
+ // Tag.e_index => {},
+ // Tag.e_arrow => {},
+
+ // Tag.e_spread => {},
+
+ // Tag.e_template_part => {},
+ // Tag.e_template => {},
+ // Tag.e_regex => {},
+ // Tag.e_await => {},
+ // Tag.e_yield => {},
+ // Tag.e_if => {},
+ // Tag.e_import => {},
+
+ // Tag.e_class => {},
+ // Tag.e_require => {},
+ // Tag.s_import => {},
+
+ // Tag.s_block => {},
+
+ // The valueless ones
+ Tag.e_super, Tag.e_null, Tag.e_undefined, Tag.e_missing, Tag.inline_true, Tag.inline_false, Tag.e_this => {
+ self.args.append(Expr{ .loc = loc, .data = Tag.ids.get(tag) }) catch unreachable;
+ },
+ else => Global.panic("Tag \"{s}\" is not implemented yet.", .{@tagName(tag)}),
+ }
+
+ return true;
+ }
+
+ pub fn writeFunctionCall(self: *JSXWriter, element: E.JSXElement) Expr {
+ if (element.tag) |tag_expr| {
+ switch (tag_expr.data) {
+ .e_string => |str| {
+ self.p.recordUsage(self.bun_jsx_ref);
+ _ = self.writeElement(element);
+ var call_args = self.p.allocator.alloc(Expr, 1) catch unreachable;
+ call_args[0] = Expr.alloc(self.p.allocator, E.Array, E.Array{ .items = self.args.items }, tag_expr.loc);
+
+ return Expr.alloc(
+ self.p.allocator,
+ E.Call,
+ E.Call{
+ .target = Expr{
+ .data = .{
+ .e_identifier = self.bun_identifier,
+ },
+ .loc = tag_expr.loc,
+ },
+ .can_be_unwrapped_if_unused = true,
+ .args = call_args,
+ },
+ tag_expr.loc,
+ );
+ },
+ else => Global.panic("Not implemented yet top-level jsx element: {s}", .{@tagName(tag_expr.data)}),
+ }
+ }
+
+ return Expr{ .data = .{ .e_missing = .{} }, .loc = logger.Loc.Empty };
+ }
+
+ pub fn writeRootElement(self: JSXWriter, element: E.JSXElement) Expr {
+ var tag = element.tag orelse E.Array{ .items = &.{} };
+ switch (tag.data) {
+ .e_string, .e_array => {},
+ else => {},
+ }
+ }
+
+ fn writeElementWithValidTagList(self: *JSXWriter, element: E.JSXElement, comptime valid_tags: Tag.Validator.List) bool {
+ const tag_expr = element.tag orelse return false;
+ if (tag_expr.data != .e_string) return false;
+ const str = tag_expr.data.e_string;
+ var p = self.p;
+
+ const node_type: JSNode.Tag = JSNode.Tag.names.get(str.utf8) orelse {
+ if (!str.isUTF8()) {
+ self.log.addErrorFmt(p.source, tag_expr.loc, p.allocator, "Tag \"{s}\" is invalid", .{strings.toUTF8Alloc(self.p.allocator, str.value)}) catch unreachable;
+ } else {
+ self.log.addErrorFmt(p.source, tag_expr.loc, p.allocator, "Tag \"{s}\" is invalid", .{str.utf8}) catch unreachable;
+ }
+ return false;
+ };
+
+ if (!valid_tags.get(node_type)) {
+ self.log.addErrorFmt(p.source, tag_expr.loc, p.allocator, "Tag \"{s}\" is invalid here", .{str.utf8}) catch unreachable;
+ }
+
+ return self.writeNodeType(node_type, element.properties, element.children, tag_expr.loc);
+ }
+
+ pub fn writeElement(self: *JSXWriter, element: E.JSXElement) bool {
+ const tag_expr = element.tag orelse return false;
+ if (tag_expr.data != .e_string) return false;
+ const str = tag_expr.data.e_string;
+ var p = self.p;
+
+ const node_type: JSNode.Tag = JSNode.Tag.names.get(str.utf8) orelse {
+ if (!str.isUTF8()) {
+ self.log.addErrorFmt(p.source, tag_expr.loc, p.allocator, "Tag \"{s}\" is invalid", .{strings.toUTF8Alloc(self.p.allocator, str.value)}) catch unreachable;
+ } else {
+ self.log.addErrorFmt(p.source, tag_expr.loc, p.allocator, "Tag \"{s}\" is invalid", .{str.utf8}) catch unreachable;
+ }
+ return false;
+ };
+
+ return self.writeNodeType(node_type, element.properties, element.children, tag_expr.loc);
+ }
+ };
+ }
+
+ pub const Writer = struct {
+ log: *logger.Log,
+ exception: JSCBase.ExceptionValueRef = null,
+ ctx: js.JSContextRef,
+ errored: bool = false,
+ allocator: *std.mem.Allocator,
+ loc: logger.Loc,
+ args_value: JSC.JSValue,
+ args_i: u32 = 0,
+ args_len: u32 = 0,
+
+ pub inline fn eatArg(this: *Writer) ?JSC.JSValue {
+ if (this.args_i >= this.args_len) return null;
+ const i = this.args_i;
+ this.args_i += 1;
+ return JSC.JSObject.getIndex(this.args_value, JavaScript.VirtualMachine.vm.global, i);
+ }
+
+ pub inline fn peekArg(this: *Writer) ?JSC.JSValue {
+ if (this.args_i >= this.args_len) return null;
+ return JSC.JSObject.getIndex(this.args_value, JavaScript.VirtualMachine.vm.global, this.args_i);
+ }
+
+ pub inline fn nextJSValue(this: *Writer) ?JSC.JSValue {
+ return this.eatArg();
+ }
+
+ pub const TagOrJSNode = union(TagOrNodeType) {
+ tag: JSNode.Tag,
+ node: JSNode,
+ invalid: void,
+
+ pub const TagOrNodeType = enum {
+ tag,
+ node,
+ invalid,
+ };
+
+ pub fn fromJSValueRefNoValidate(ctx: js.JSContextRef, value: js.JSValueRef) TagOrJSNode {
+ switch (js.JSValueGetType(ctx, value)) {
+ js.JSType.kJSTypeNumber => {
+ const tag_int = @floatToInt(u8, JSC.JSValue.fromRef(value).asNumber());
+ if (tag_int < Tag.min_tag or tag_int > Tag.max_tag) {
+ return TagOrJSNode{ .invalid = .{} };
+ }
+ return TagOrJSNode{ .tag = @intToEnum(JSNode.Tag, tag_int) };
+ },
+ js.JSType.kJSTypeObject => {
+ if (JSCBase.GetJSPrivateData(JSNode, value)) |node| {
+ return TagOrJSNode{ .node = node.* };
+ }
+
+ return TagOrJSNode{ .invalid = .{} };
+ },
+ else => {
+ return TagOrJSNode{ .invalid = .{} };
+ },
+ }
+ }
+
+ pub fn fromJSValueRef(writer: *Writer, ctx: js.JSContextRef, value: js.JSValueRef) TagOrJSNode {
+ switch (js.JSValueGetType(ctx, value)) {
+ js.JSType.kJSTypeNumber => {
+ const tag_int = @floatToInt(u8, JSC.JSValue.fromRef(value).asNumber());
+ if (tag_int < Tag.min_tag or tag_int > Tag.max_tag) {
+ throwTypeError(ctx, "Node type has invalid value", writer.exception);
+ writer.errored = true;
+ return TagOrJSNode{ .invalid = .{} };
+ }
+ return TagOrJSNode{ .tag = @intToEnum(JSNode.Tag, tag_int) };
+ },
+ js.JSType.kJSTypeObject => {
+ if (JSCBase.GetJSPrivateData(JSNode, value)) |node| {
+ return TagOrJSNode{ .node = node.* };
+ }
+
+ return TagOrJSNode{ .invalid = .{} };
+ },
+ else => {
+ throwTypeError(writer.ctx, "Invalid Bun AST", writer.exception);
+ return TagOrJSNode{ .invalid = .{} };
+ },
+ }
+ }
+
+ pub fn fromJSValue(writer: *Writer, value: JSC.JSValue) TagOrJSNode {
+ return fromJSValueRef(writer, JavaScript.VirtualMachine.vm.global.ref(), value.asRef());
+ }
+ };
+
+ fn writeProperty(writer: *Writer, property: *G.Property) bool {
+
+ // Property is
+ // value
+ // initializer
+ // if property value is an e.spread, then key is skipped
+ // key
+
+ // value is first
+ var expect_key = true;
+ switch (TagOrJSNode.fromJSValue(writer, writer.eatArg() orelse return false)) {
+ TagOrJSNode.tag => |tag| {
+ var expr: Expr = Expr{ .loc = writer.loc, .data = .{ .e_null = E.Null{} } };
+
+ if (!writer.writeFromJSWithTagInExpr(tag, &expr)) return false;
+ property.value = switch (expr.data) {
+ .e_missing, .e_undefined => null,
+ else => expr,
+ };
+ property.flags.is_spread = expr.data == .e_spread;
+ expect_key = property.value == null or !property.flags.is_spread;
+ },
+ TagOrJSNode.node => |node| {
+ const expr = node.toExpr();
+ property.value = switch (expr.data) {
+ .e_missing, .e_undefined => null,
+ else => expr,
+ };
+ property.flags.is_spread = expr.data == .e_spread;
+ expect_key = property.value == null or !property.flags.is_spread;
+ },
+ TagOrJSNode.invalid => {
+ return false;
+ },
+ }
+
+ switch (TagOrJSNode.fromJSValue(writer, writer.eatArg() orelse return false)) {
+ TagOrJSNode.tag => |tag| {
+ var expr: Expr = Expr{ .loc = writer.loc, .data = .{ .e_null = E.Null{} } };
+
+ if (!writer.writeFromJSWithTagInExpr(tag, &expr)) return false;
+ property.initializer = switch (expr.data) {
+ .e_missing, .e_undefined => null,
+ else => expr,
+ };
+ },
+ TagOrJSNode.node => |node| {
+ const expr = node.toExpr();
+ property.initializer = switch (expr.data) {
+ .e_missing, .e_undefined => null,
+ else => expr,
+ };
+ },
+ TagOrJSNode.invalid => {
+ return false;
+ },
+ }
+
+ if (expect_key) {
+ var next_arg = writer.peekArg() orelse return false;
+ // its okay for property keys to literally be strings
+ // <property name="foo">
+ if (next_arg.isString()) {
+ var expr: Expr = Expr{ .loc = writer.loc, .data = .{ .e_string = &E.String.empty } };
+ if (!writer.writeFromJSWithTagInExpr(JSNode.Tag.e_string, &expr)) return false;
+ property.key = expr;
+ } else {
+ switch (TagOrJSNode.fromJSValue(writer, writer.eatArg() orelse return false)) {
+ TagOrJSNode.tag => |tag| {
+ var expr: Expr = Expr{ .loc = writer.loc, .data = .{ .e_null = E.Null{} } };
+ if (!writer.writeFromJSWithTagInExpr(tag, &expr)) return false;
+ property.key = expr;
+ },
+ TagOrJSNode.node => |node| {
+ property.key = node.toExpr();
+ },
+ TagOrJSNode.invalid => {
+ return false;
+ },
+ }
+ }
+ }
+
+ return true;
+ }
+
+ fn writeFromJSWithTagInExpr(writer: *Writer, tag: JSNode.Tag, expr: *Expr) bool {
+ switch (tag) {
+ .e_array => {
+ // var e_array: E.Array = E.Array{ .items = writer.allocator.alloc(E.Array, args.len) catch return false };
+ var count = (writer.nextJSValue() orelse return false).toU16();
+ var i: u16 = 0;
+ var items = ExprList.initCapacity(writer.allocator, count) catch unreachable;
+
+ while (i < count) {
+ var nextArg = writer.eatArg() orelse return false;
+ if (js.JSValueIsArray(writer.ctx, nextArg.asRef())) {
+ const extras = nextArg.getLengthOfArray(JavaScript.VirtualMachine.vm.global);
+ count += std.math.max(@truncate(u16, extras), 1) - 1;
+ items.ensureUnusedCapacity(extras) catch unreachable;
+ items.expandToCapacity();
+ var new_writer = writer.*;
+ new_writer.args_i = 0;
+ new_writer.args_len = extras;
+ new_writer.args_value = nextArg;
+
+ while (new_writer.nextJSValue()) |value| {
+ defer i += 1;
+ switch (TagOrJSNode.fromJSValue(&new_writer, value)) {
+ TagOrJSNode.tag => |tag_| {
+ if (!new_writer.writeFromJSWithTagInExpr(
+ tag_,
+ &items.items[i],
+ )) return false;
+ },
+ TagOrJSNode.node => |node_| {
+ const node: JSNode = node_;
+ switch (node.data) {
+ JSNode.Tag.s_import => |import| {
+ return false;
+ },
+ else => {
+ items.items[i] = node.toExpr();
+ },
+ }
+ },
+ TagOrJSNode.invalid => {
+ return false;
+ },
+ }
+ }
+ } else {
+ defer i += 1;
+
+ switch (TagOrJSNode.fromJSValue(writer, nextArg)) {
+ TagOrJSNode.tag => |tag_| {
+ if (!writer.writeFromJSWithTagInExpr(tag_, &items.items[i])) return false;
+ },
+ TagOrJSNode.node => |node_| {
+ const node: JSNode = node_;
+ switch (node.data) {
+ JSNode.Tag.s_import => |import| {
+ return false;
+ },
+ else => {
+ items.items[i] = node.toExpr();
+ },
+ }
+ },
+ TagOrJSNode.invalid => {
+ return false;
+ },
+ }
+ }
+ }
+ expr.* = Expr.alloc(writer.allocator, E.Array, E.Array{ .items = items.items[0..i] }, writer.loc);
+ return true;
+ },
+ .e_boolean => {
+ expr.* = Expr{ .loc = writer.loc, .data = .{ .e_boolean = .{
+ .value = JSC.JSValue.toBoolean(writer.nextJSValue() orelse return false),
+ } } };
+ return true;
+ },
+ .inline_true => {
+ expr.* = Expr{ .loc = writer.loc, .data = .{ .e_boolean = .{ .value = true } } };
+ return true;
+ },
+ .inline_false => {
+ expr.* = Expr{ .loc = writer.loc, .data = .{ .e_boolean = .{ .value = false } } };
+ return true;
+ },
+ .e_null => {
+ expr.* = Expr{ .loc = writer.loc, .data = .{ .e_null = E.Null{} } };
+ return true;
+ },
+ .e_undefined => {
+ expr.* = Expr{ .loc = writer.loc, .data = .{ .e_undefined = E.Undefined{} } };
+ return true;
+ },
+ .e_number => {
+ expr.* = Expr{
+ .loc = writer.loc,
+ .data = .{
+ .e_number = .{
+ .value = JSC.JSValue.asNumber(writer.nextJSValue() orelse return false),
+ },
+ },
+ };
+ return true;
+ },
+ .e_string => {
+ var wtf_string = JSC.JSValue.toWTFString(writer.nextJSValue() orelse return false, JavaScript.VirtualMachine.vm.global);
+ if (wtf_string.isEmpty()) {
+ expr.* = Expr{
+ .loc = writer.loc,
+ .data = .{
+ .e_string = &E.String.empty,
+ },
+ };
+ } else if (wtf_string.is8Bit()) {
+ expr.* = Expr.alloc(writer.allocator, E.String, E.String{ .utf8 = wtf_string.characters8()[0..wtf_string.length()] }, writer.loc);
+ } else if (wtf_string.is16Bit()) {
+ expr.* = Expr.alloc(writer.allocator, E.String, E.String{ .value = wtf_string.characters16()[0..wtf_string.length()] }, writer.loc);
+ } else {
+ unreachable;
+ }
+ return true;
+ },
+ .e_reg_exp => {
+ var jsstring = js.JSValueToStringCopy(writer.ctx, (writer.eatArg() orelse return false).asRef(), writer.exception);
+ defer js.JSStringRelease(jsstring);
+
+ const len = js.JSStringGetLength(jsstring);
+ var str = writer.allocator.alloc(u8, len + 1) catch unreachable;
+ const outlen = js.JSStringGetUTF8CString(jsstring, str.ptr, len + 1);
+ expr.* = Expr.alloc(writer.allocator, E.RegExp, E.RegExp{ .value = str[0..outlen] }, writer.loc);
+ return true;
+ },
+ .e_object => {
+ const len = (writer.nextJSValue() orelse return false).toU16();
+
+ var properties = writer.allocator.alloc(G.Property, len) catch return false;
+ var property_i: u16 = 0;
+
+ while (property_i < properties.len) : (property_i += 1) {
+ switch (TagOrJSNode.fromJSValue(writer, writer.eatArg() orelse return false)) {
+ TagOrJSNode.tag => |tag_| {
+ if (tag_ != JSNode.Tag.g_property) return false;
+
+ if (!writer.writeProperty(
+ &properties[property_i],
+ )) return false;
+ },
+ TagOrJSNode.node => |node_| {
+ const node: JSNode = node_;
+ switch (node.data) {
+ .g_property => |property| {
+ properties[property_i] = property.*;
+ },
+ else => {
+ return false;
+ },
+ }
+ },
+ TagOrJSNode.invalid => {
+ return false;
+ },
+ }
+ }
+
+ return true;
+ },
+ else => {
+ return false;
+ },
+
+ // .e_call => {},
+
+ // .e_dot => {},
+ // .e_index => {},
+ // .e_identifier => {},
+ // .e_import_identifier => {},
+
+ // .e_spread => {},
+
+ // .e_template_part => {},
+ // .e_template => {},
+
+ // .e_await => {},
+ // .e_yield => {},
+ // .e_if => {},
+ // .e_import => {},
+ // .e_this => {},
+ // .e_class => {},
+ // s_import => {},
+ }
+
+ return false;
+ }
+
+ pub fn writeFromJS(writer: *Writer) ?JSNode {
+ switch (TagOrJSNode.fromJSValueRef(writer, writer.ctx, (writer.eatArg() orelse return null).asRef())) {
+ TagOrJSNode.tag => |tag| {
+ var expr: Expr = Expr{ .loc = writer.loc, .data = .{ .e_null = E.Null{} } };
+
+ if (!writer.writeFromJSWithTagInExpr(tag, &expr)) return null;
+ return JSNode.initExpr(expr);
+ },
+ TagOrJSNode.node => |node| {
+ return node;
+ },
+ TagOrJSNode.invalid => {
+ return null;
+ },
+ }
+ }
+ };
+
// pub fn isInstanceOf(
// ctx: js.JSContextRef,
// obj: js.JSObjectRef,
@@ -4245,71 +6116,100 @@ pub const Macro = struct {
// js.JSValueToNumber(ctx, value, exception);
// }
- pub fn toString(
- this: *JSExpr,
+ fn throwTypeError(ctx: js.JSContextRef, comptime msg: string, exception: js.ExceptionRef) void {
+ JSCBase.JSError(JSCBase.getAllocator(ctx), msg, .{}, ctx, exception);
+ }
+
+ pub const BunJSXCallbackFunction = JSCBase.NewClass(
+ void,
+ .{ .name = "bunJSX" },
+ .{
+ .call = .{
+ .rfn = createFromJavaScript,
+ .ro = true,
+ },
+ .isNodeType = .{
+ .rfn = isNodeType,
+ .ro = true,
+ },
+ },
+ .{},
+ );
+
+ pub fn isNodeType(
+ this: void,
ctx: js.JSContextRef,
function: js.JSObjectRef,
thisObject: js.JSObjectRef,
arguments: []const js.JSValueRef,
exception: js.ExceptionRef,
) js.JSObjectRef {
- switch (this.expr.data) {
- .e_string => |str| {
- if (str.isBlank()) {
- return JSC.ZigString.init("").toValue(JavaScript.VirtualMachine.vm.global).asRef();
- }
+ if (arguments.len != 2) {
+ throwTypeError(ctx, "bunJSX.isNodeType() requires 2 arguments", exception);
+ return null;
+ }
- if (str.isUTF8()) {
- return JSC.ZigString.init(str.utf8).toValue(JavaScript.VirtualMachine.vm.global).asRef();
- } else {
- return js.JSValueMakeString(ctx, js.JSStringCreateWithCharactersNoCopy(str.value.ptr, str.value.len));
- }
- },
- .e_template => |template| {
- const str = template.head;
+ const TagOrNodeType = Writer.TagOrJSNode.TagOrNodeType;
- if (str.isBlank()) {
- return JSC.ZigString.init("").toValue(JavaScript.VirtualMachine.vm.global).asRef();
- }
+ const left = Writer.TagOrJSNode.fromJSValueRefNoValidate(ctx, arguments[0]);
+ const right = Writer.TagOrJSNode.fromJSValueRefNoValidate(ctx, arguments[1]);
- if (str.isUTF8()) {
- return JSC.ZigString.init(str.utf8).toValue(JavaScript.VirtualMachine.vm.global).asRef();
- } else {
- return js.JSValueMakeString(ctx, js.JSStringCreateWithCharactersNoCopy(str.value.ptr, str.value.len));
- }
- },
- else => {
- return JSC.ZigString.init("").toValue(JavaScript.VirtualMachine.vm.global).asRef();
- },
+ if (left == TagOrNodeType.invalid or right == TagOrNodeType.invalid) {
+ return js.JSValueMakeBoolean(ctx, false);
}
- }
- pub fn getTag(
- this: *JSExpr,
- ctx: js.JSContextRef,
- thisObject: js.JSValueRef,
- prop: js.JSStringRef,
- exception: js.ExceptionRef,
- ) js.JSObjectRef {
- return JSC.JSValue.jsNumberFromU16(@intCast(u16, @enumToInt(std.meta.activeTag(this.expr.data)))).asRef();
- }
- pub fn getTagName(
- this: *JSExpr,
- ctx: js.JSContextRef,
- thisObject: js.JSValueRef,
- prop: js.JSStringRef,
- exception: js.ExceptionRef,
- ) js.JSObjectRef {
- return JSC.ZigString.init(@tagName(this.expr.data)).toValue(JavaScript.VirtualMachine.vm.global).asRef();
+ if (left == TagOrNodeType.node and right == TagOrNodeType.node) {
+ return js.JSValueMakeBoolean(ctx, @as(Tag, left.node.data) == @as(Tag, right.node.data));
+ }
+
+ if (left == TagOrNodeType.node) {
+ return js.JSValueMakeBoolean(ctx, @as(Tag, left.node.data) == right.tag);
+ }
+
+ if (right == TagOrNodeType.node) {
+ return js.JSValueMakeBoolean(ctx, @as(Tag, right.node.data) == left.tag);
+ }
+
+ unreachable;
}
- pub fn getPosition(
- this: *JSExpr,
+
+ pub fn createFromJavaScript(
+ this: void,
ctx: js.JSContextRef,
- thisObject: js.JSValueRef,
- prop: js.JSStringRef,
+ function: js.JSObjectRef,
+ thisObject: js.JSObjectRef,
+ arguments: []const js.JSValueRef,
exception: js.ExceptionRef,
) js.JSObjectRef {
- return JSC.JSValue.jsNumberFromInt32(this.expr.loc.start).asRef();
+ if (arguments.len != 1 or !js.JSValueIsArray(ctx, arguments[0])) {
+ throwTypeError(ctx, "bunJSX requires one array argument", exception);
+ return null;
+ }
+
+ js.JSValueProtect(ctx, arguments[0]);
+ defer Output.flush();
+ const args_value = JSC.JSValue.fromRef(arguments[0]);
+ var writer = Writer{
+ .log = JavaScript.VirtualMachine.vm.log,
+ .ctx = ctx,
+ .loc = logger.Loc.Empty,
+ .allocator = JSCBase.getAllocator(ctx),
+ .exception = exception,
+ .args_value = args_value,
+ .args_len = args_value.getLengthOfArray(JavaScript.VirtualMachine.vm.global),
+ .args_i = 0,
+ .errored = false,
+ };
+
+ if (writer.writeFromJS()) |node| {
+ var ptr = writer.allocator.create(JSNode) catch unreachable;
+ ptr.* = node;
+ var result = JSNode.Class.make(ctx, ptr);
+ js.JSValueProtect(ctx, result);
+ return result;
+ }
+
+ return null;
}
};
@@ -4366,8 +6266,8 @@ pub const Macro = struct {
}
pub const Runner = struct {
- threadlocal var args_buf: [32]js.JSObjectRef = undefined;
- threadlocal var expr_nodes_buf: [32]JSExpr = undefined;
+ threadlocal var args_buf: [2]js.JSObjectRef = undefined;
+ threadlocal var expr_nodes_buf: [1]JSNode = undefined;
threadlocal var exception_holder: Zig.ZigException.Holder = undefined;
pub fn run(
macro: Macro,
@@ -4382,23 +6282,18 @@ pub const Macro = struct {
if (comptime isDebug) Output.prettyln("<r><d>[macro]<r> call <d><b>{s}<r>", .{function_name});
exception_holder = Zig.ZigException.Holder.init();
- expr_nodes_buf[0] = JSExpr{ .expr = caller };
- args_buf[0] = JSExpr.Class.make(
+ expr_nodes_buf[0] = JSNode.initExpr(caller);
+ args_buf[0] = JSNode.Class.make(
macro.vm.global.ref(),
&expr_nodes_buf[0],
);
- for (args) |arg, i| {
- expr_nodes_buf[i + 1] = JSExpr{ .expr = arg };
- args_buf[i + 1] =
- JSExpr.Class.make(
- macro.vm.global.ref(),
- &expr_nodes_buf[i + 1],
- );
- }
- args_buf[args.len + 2] = null;
+
+ args_buf[1] = null;
var macro_callback = macro.vm.macros.get(id) orelse return caller;
var result = js.JSObjectCallAsFunctionReturnValue(macro.vm.global.ref(), macro_callback, null, args.len + 1, &args_buf);
+ js.JSValueProtect(macro.vm.global.ref(), result.asRef());
+ defer js.JSValueUnprotect(macro.vm.global.ref(), result.asRef());
var promise = JSC.JSPromise.resolvedPromise(macro.vm.global, result);
macro.vm.global.vm().drainMicrotasks();
@@ -4409,7 +6304,11 @@ pub const Macro = struct {
const value = promise.result(macro.vm.global.vm());
- return caller;
+ if (JSCBase.GetJSPrivateData(JSNode, value.asObjectRef())) |node| {
+ return node.toExpr();
+ } else {
+ return Expr{ .data = .{ .e_missing = .{} }, .loc = caller.loc };
+ }
}
};
};
diff --git a/src/js_lexer.zig b/src/js_lexer.zig
index ec800cfb5..91f4fceef 100644
--- a/src/js_lexer.zig
+++ b/src/js_lexer.zig
@@ -76,6 +76,7 @@ pub const Lexer = struct {
number: f64 = 0.0,
rescan_close_brace_as_template_token: bool = false,
prev_error_loc: logger.Loc = logger.Loc.Empty,
+ regex_flags_start: ?u16 = null,
allocator: *std.mem.Allocator,
/// In JavaScript, strings are stored as UTF-16, but nearly every string is ascii.
/// This means, usually, we can skip UTF8 -> UTF16 conversions.
@@ -108,6 +109,7 @@ pub const Lexer = struct {
.all_original_comments = self.all_original_comments,
.code_point = self.code_point,
.identifier = self.identifier,
+ .regex_flags_start = self.regex_flags_start,
.jsx_factory_pragma_comment = self.jsx_factory_pragma_comment,
.jsx_fragment_pragma_comment = self.jsx_fragment_pragma_comment,
.source_mapping_url = self.source_mapping_url,
@@ -1756,13 +1758,21 @@ pub const Lexer = struct {
}
pub fn scanRegExp(lexer: *LexerType) !void {
+ lexer.regex_flags_start = null;
while (true) {
switch (lexer.code_point) {
'/' => {
try lexer.step();
+
+ var has_set_flags_start = false;
while (isIdentifierContinue(lexer.code_point)) {
switch (lexer.code_point) {
'g', 'i', 'm', 's', 'u', 'y' => {
+ if (!has_set_flags_start) {
+ lexer.regex_flags_start = @truncate(u16, lexer.end - lexer.start);
+ has_set_flags_start = true;
+ }
+
try lexer.step();
},
else => {
diff --git a/src/js_parser/js_parser.zig b/src/js_parser/js_parser.zig
index 916e3e061..c73ae5718 100644
--- a/src/js_parser/js_parser.zig
+++ b/src/js_parser/js_parser.zig
@@ -22,6 +22,9 @@ const JSXFactoryName = "JSX";
const JSXAutomaticName = "jsx_module";
const MacroRefs = std.AutoArrayHashMap(Ref, u32);
+const BunJSX = struct {
+ pub threadlocal var bun_jsx_identifier: E.Identifier = undefined;
+};
pub fn ExpressionTransposer(
comptime Kontext: type,
visitor: fn (ptr: *Kontext, arg: Expr, state: anytype) Expr,
@@ -88,6 +91,12 @@ pub const ImportScanner = struct {
.s_import => |st| {
var record: *ImportRecord = &p.import_records.items[st.import_record_index];
+ if (strings.eqlComptime(record.path.namespace, "macro")) {
+ record.is_unused = true;
+ record.path.is_disabled = true;
+ continue;
+ }
+
// The official TypeScript compiler always removes unused imported
// symbols. However, we deliberately deviate from the official
// TypeScript compiler's behavior doing this in a specific scenario:
@@ -1767,7 +1776,7 @@ pub const Parser = struct {
ts: bool = false,
keep_names: bool = true,
omit_runtime_for_tests: bool = false,
- ignore_dce_annotations: bool = true,
+ ignore_dce_annotations: bool = false,
preserve_unused_imports_ts: bool = false,
use_define_for_class_fields: bool = false,
suppress_warnings_about_weird_code: bool = true,
@@ -1846,9 +1855,10 @@ pub const Parser = struct {
// - import 'foo';
// - import("foo")
// - require("foo")
- import_record.is_unused = import_record.kind == .stmt and
+ import_record.is_unused = import_record.is_unused or
+ (import_record.kind == .stmt and
!import_record.was_originally_bare_import and
- !import_record.calls_run_time_re_export_fn;
+ !import_record.calls_run_time_re_export_fn);
}
var iter = scan_pass.used_symbols.iterator();
@@ -1996,7 +2006,7 @@ pub const Parser = struct {
}
// Auto-import JSX
- if (p.options.jsx.parse) {
+ if (ParserType.jsx_transform_type == .react) {
const jsx_filename_symbol = p.symbols.items[p.jsx_filename.ref.inner_index];
{
@@ -2693,8 +2703,9 @@ const ImportItemForNamespaceMap = std.StringArrayHashMap(LocRef);
pub fn NewParser(
comptime js_parser_features: ParserFeatures,
) type {
+ const js_parser_jsx = if (FeatureFlags.force_macro) JSXTransformType.macro else js_parser_features.jsx;
const is_typescript_enabled = js_parser_features.typescript;
- const is_jsx_enabled = js_parser_features.jsx != .none;
+ const is_jsx_enabled = js_parser_jsx != .none;
const only_scan_imports_and_do_not_visit = js_parser_features.scan_only;
const is_react_fast_refresh_enabled = js_parser_features.react_fast_refresh;
@@ -2706,7 +2717,7 @@ pub fn NewParser(
// public only because of Binding.ToExpr
return struct {
const P = @This();
- pub const jsx_transform_type: JSXTransformType = js_parser_features.jsx;
+ pub const jsx_transform_type: JSXTransformType = js_parser_jsx;
macro_refs: MacroRefs = undefined,
allocator: *std.mem.Allocator,
options: Parser.Options,
@@ -2796,6 +2807,8 @@ pub fn NewParser(
// only applicable when is_react_fast_refresh_enabled
jsx_refresh_runtime: GeneratedSymbol = GeneratedSymbol{ .ref = Ref.None, .primary = Ref.None, .backup = Ref.None },
+ bun_jsx_ref: Ref = Ref.None,
+
// Imports (both ES6 and CommonJS) are tracked at the top level
import_records: ImportRecordList,
import_records_for_current_part: List(u32),
@@ -3627,6 +3640,12 @@ pub fn NewParser(
}
},
.macro => {
+ p.bun_jsx_ref = p.declareSymbol(.other, logger.Loc.Empty, "bunJSX") catch unreachable;
+ BunJSX.bun_jsx_identifier = E.Identifier{
+ .ref = p.bun_jsx_ref,
+ .can_be_removed_if_unused = true,
+ .call_can_be_unwrapped_if_unused = true,
+ };
p.jsx_fragment = p.declareGeneratedSymbol(.other, "Fragment") catch unreachable;
},
else => {},
@@ -6150,6 +6169,7 @@ pub fn NewParser(
p.import_records.items[stmt.import_record_index].path.namespace = js_ast.Macro.namespace;
if (comptime only_scan_imports_and_do_not_visit) {
p.import_records.items[stmt.import_record_index].path.is_disabled = true;
+ p.import_records.items[stmt.import_record_index].is_internal = true;
}
}
@@ -6157,10 +6177,12 @@ pub fn NewParser(
const name = p.loadNameFromRef(stmt.namespace_ref);
stmt.namespace_ref = try p.declareSymbol(.import, star, name);
if (comptime ParsePassSymbolUsageType != void) {
- p.parse_pass_symbol_uses.put(name, .{
- .ref = stmt.namespace_ref,
- .import_record_index = stmt.import_record_index,
- }) catch unreachable;
+ if (!is_macro) {
+ p.parse_pass_symbol_uses.put(name, .{
+ .ref = stmt.namespace_ref,
+ .import_record_index = stmt.import_record_index,
+ }) catch unreachable;
+ }
}
if (is_macro) {
@@ -6190,10 +6212,12 @@ pub fn NewParser(
try p.is_import_item.put(ref, true);
name_loc.ref = ref;
if (comptime ParsePassSymbolUsageType != void) {
- p.parse_pass_symbol_uses.put(name, .{
- .ref = ref,
- .import_record_index = stmt.import_record_index,
- }) catch unreachable;
+ if (!is_macro) {
+ p.parse_pass_symbol_uses.put(name, .{
+ .ref = ref,
+ .import_record_index = stmt.import_record_index,
+ }) catch unreachable;
+ }
}
if (is_macro) {
@@ -6212,10 +6236,12 @@ pub fn NewParser(
item_refs.putAssumeCapacity(item.alias, LocRef{ .loc = item.name.loc, .ref = ref });
if (comptime ParsePassSymbolUsageType != void) {
- p.parse_pass_symbol_uses.put(name, .{
- .ref = ref,
- .import_record_index = stmt.import_record_index,
- }) catch unreachable;
+ if (!is_macro) {
+ p.parse_pass_symbol_uses.put(name, .{
+ .ref = ref,
+ .import_record_index = stmt.import_record_index,
+ }) catch unreachable;
+ }
}
if (is_macro) {
@@ -9752,9 +9778,12 @@ pub fn NewParser(
},
.t_slash, .t_slash_equals => {
try p.lexer.scanRegExp();
+ // always set regex_flags_start to null to make sure we don't accidentally use the wrong value later
+ defer p.lexer.regex_flags_start = null;
const value = p.lexer.raw();
try p.lexer.next();
- return p.e(E.RegExp{ .value = value }, loc);
+
+ return p.e(E.RegExp{ .value = value, .flags_offset = p.lexer.regex_flags_start }, loc);
},
.t_void => {
try p.lexer.next();
@@ -10154,7 +10183,11 @@ pub fn NewParser(
// do people do <API_URL>?
fn jsxStringsToMemberExpression(p: *P, loc: logger.Loc, ref: Ref) Expr {
p.recordUsage(ref);
- return p.e(E.Identifier{ .ref = ref }, loc);
+ return p.e(E.Identifier{
+ .ref = ref,
+ .can_be_removed_if_unused = true,
+ .call_can_be_unwrapped_if_unused = true,
+ }, loc);
}
// Note: The caller has already parsed the "import" keyword
@@ -10733,7 +10766,8 @@ pub fn NewParser(
});
}
- fn visitExpr(p: *P, expr: Expr) Expr {
+ // public for JSNode.JSXWriter usage
+ pub fn visitExpr(p: *P, expr: Expr) Expr {
if (only_scan_imports_and_do_not_visit) {
@compileError("only_scan_imports_and_do_not_visit must not run this.");
}
@@ -10925,52 +10959,9 @@ pub fn NewParser(
.e_jsx_element => |e_| {
switch (comptime jsx_transform_type) {
.macro => {
- const IdentifierOrNodeType = union(Tag) {
- identifier: Expr,
- expression: Expr.Tag,
- pub const Tag = enum { identifier, expression };
- };
- const tag: IdentifierOrNodeType = tagger: {
- if (e_.tag) |_tag| {
- switch (_tag.data) {
- .e_string => |str| {
- if (Expr.Tag.find(str.utf8)) |tagname| {
- break :tagger IdentifierOrNodeType{ .expression = tagname };
- }
-
- p.log.addErrorFmt(
- p.source,
- expr.loc,
- p.allocator,
- "Invalid expression tag: \"<{s}>\". Valid tags are:\n" ++ Expr.Tag.valid_names_list ++ "\n",
- .{str.utf8},
- ) catch unreachable;
- break :tagger IdentifierOrNodeType{ .identifier = p.visitExpr(_tag) };
- },
- else => {
- break :tagger IdentifierOrNodeType{ .identifier = p.visitExpr(_tag) };
- },
- }
- } else {
- break :tagger IdentifierOrNodeType{ .expression = Expr.Tag.e_array };
- }
- };
-
- for (e_.properties) |property, i| {
- if (property.kind != .spread) {
- e_.properties[i].key = p.visitExpr(e_.properties[i].key.?);
- }
-
- if (property.value != null) {
- e_.properties[i].value = p.visitExpr(e_.properties[i].value.?);
- }
-
- if (property.initializer != null) {
- e_.properties[i].initializer = p.visitExpr(e_.properties[i].initializer.?);
- }
- }
-
- return p.e(E.Missing{}, expr.loc);
+ const WriterType = js_ast.Macro.JSNode.NewJSXWriter(P);
+ var writer = WriterType.initWriter(p, &BunJSX.bun_jsx_identifier);
+ return writer.writeFunctionCall(e_.*);
},
.react => {
const tag: Expr = tagger: {
@@ -11056,6 +11047,7 @@ pub fn NewParser(
// Either:
// jsxDEV(type, arguments, key, isStaticChildren, source, self)
// jsx(type, arguments, key)
+ const include_filename = FeatureFlags.include_filename_in_jsx and p.options.jsx.development;
const args = p.allocator.alloc(Expr, if (p.options.jsx.development) @as(usize, 6) else @as(usize, 4)) catch unreachable;
args[0] = tag;
var props = List(G.Property).fromOwnedSlice(p.allocator, e_.properties);
@@ -11134,27 +11126,34 @@ pub fn NewParser(
},
};
- var source = p.allocator.alloc(G.Property, 2) catch unreachable;
- p.recordUsage(p.jsx_filename.ref);
- source[0] = G.Property{
- .key = Expr{ .loc = expr.loc, .data = Prefill.Data.Filename },
- .value = p.e(E.Identifier{ .ref = p.jsx_filename.ref }, expr.loc),
- };
+ if (include_filename) {
+ var source = p.allocator.alloc(G.Property, 2) catch unreachable;
+ p.recordUsage(p.jsx_filename.ref);
+ source[0] = G.Property{
+ .key = Expr{ .loc = expr.loc, .data = Prefill.Data.Filename },
+ .value = p.e(E.Identifier{
+ .ref = p.jsx_filename.ref,
+ .can_be_removed_if_unused = true,
+ }, expr.loc),
+ };
- source[1] = G.Property{
- .key = Expr{ .loc = expr.loc, .data = Prefill.Data.LineNumber },
- .value = p.e(E.Number{ .value = @intToFloat(f64, expr.loc.start) }, expr.loc),
- };
+ source[1] = G.Property{
+ .key = Expr{ .loc = expr.loc, .data = Prefill.Data.LineNumber },
+ .value = p.e(E.Number{ .value = @intToFloat(f64, expr.loc.start) }, expr.loc),
+ };
- // Officially, they ask for columnNumber. But I don't see any usages of it in the code!
- // source[2] = G.Property{
- // .key = Expr{ .loc = expr.loc, .data = Prefill.Data.ColumnNumber },
- // .value = p.e(E.Number{ .value = @intToFloat(f64, expr.loc.start) }, expr.loc),
- // };
+ // Officially, they ask for columnNumber. But I don't see any usages of it in the code!
+ // source[2] = G.Property{
+ // .key = Expr{ .loc = expr.loc, .data = Prefill.Data.ColumnNumber },
+ // .value = p.e(E.Number{ .value = @intToFloat(f64, expr.loc.start) }, expr.loc),
+ // };
+ args[4] = p.e(E.Object{
+ .properties = source,
+ }, expr.loc);
+ } else {
+ args[4] = p.e(E.Object{}, expr.loc);
+ }
- args[4] = p.e(E.Object{
- .properties = source,
- }, expr.loc);
args[5] = Expr{ .data = Prefill.Data.This, .loc = expr.loc };
}
@@ -11231,6 +11230,89 @@ pub fn NewParser(
const is_stmt_expr = @as(Expr.Tag, p.stmt_expr_value) == .e_binary and expr.data.e_binary == p.stmt_expr_value.e_binary;
const was_anonymous_named_expr = p.isAnonymousNamedExpr(e_.right);
+ if (comptime jsx_transform_type == .macro) {
+ if (e_.op == Op.Code.bin_instanceof and (e_.right.data == .e_jsx_element or e_.left.data == .e_jsx_element)) {
+ // foo instanceof <string />
+ // ->
+ // bunJSX.isNodeType(foo, 13)
+
+ // <string /> instanceof foo
+ // ->
+ // bunJSX.isNodeType(foo, 13)
+ var call_args = p.allocator.alloc(Expr, 2) catch unreachable;
+ call_args[0] = e_.left;
+ call_args[1] = e_.right;
+
+ if (e_.right.data == .e_jsx_element) {
+ const jsx_element = e_.right.data.e_jsx_element;
+ if (jsx_element.tag) |tag| {
+ if (tag.data == .e_string) {
+ const tag_string = tag.data.e_string.utf8;
+ if (js_ast.Macro.JSNode.Tag.names.get(tag_string)) |node_tag| {
+ call_args[1] = Expr{ .loc = tag.loc, .data = js_ast.Macro.JSNode.Tag.ids.get(node_tag) };
+ } else {
+ p.log.addRangeErrorFmt(
+ p.source,
+ js_lexer.rangeOfIdentifier(p.source, tag.loc),
+ p.allocator,
+ "Invalid JSX tag: \"{s}\"",
+ .{tag_string},
+ ) catch unreachable;
+ return expr;
+ }
+ }
+ } else {
+ call_args[1] = p.visitExpr(call_args[1]);
+ }
+ } else {
+ call_args[1] = p.visitExpr(call_args[1]);
+ }
+
+ if (e_.left.data == .e_jsx_element) {
+ const jsx_element = e_.left.data.e_jsx_element;
+ if (jsx_element.tag) |tag| {
+ if (tag.data == .e_string) {
+ const tag_string = tag.data.e_string.utf8;
+ if (js_ast.Macro.JSNode.Tag.names.get(tag_string)) |node_tag| {
+ call_args[0] = Expr{ .loc = tag.loc, .data = js_ast.Macro.JSNode.Tag.ids.get(node_tag) };
+ } else {
+ p.log.addRangeErrorFmt(
+ p.source,
+ js_lexer.rangeOfIdentifier(p.source, tag.loc),
+ p.allocator,
+ "Invalid JSX tag: \"{s}\"",
+ .{tag_string},
+ ) catch unreachable;
+ return expr;
+ }
+ }
+ } else {
+ call_args[0] = p.visitExpr(call_args[0]);
+ }
+ } else {
+ call_args[0] = p.visitExpr(call_args[0]);
+ }
+
+ return p.e(
+ E.Call{
+ .target = p.e(
+ E.Dot{
+ .name = "isNodeType",
+ .name_loc = expr.loc,
+ .target = p.e(BunJSX.bun_jsx_identifier, expr.loc),
+ .can_be_removed_if_unused = true,
+ .call_can_be_unwrapped_if_unused = true,
+ },
+ expr.loc,
+ ),
+ .args = call_args,
+ .can_be_unwrapped_if_unused = true,
+ },
+ expr.loc,
+ );
+ }
+ }
+
e_.left = p.visitExprInOut(e_.left, ExprIn{
.assign_target = e_.op.binaryAssignTarget(),
});
@@ -11857,7 +11939,10 @@ pub fn NewParser(
var has_spread = false;
var has_proto = false;
- for (e_.properties) |*property, i| {
+ var i: usize = 0;
+ while (i < e_.properties.len) : (i += 1) {
+ var property = &e_.properties[i];
+
if (property.kind != .spread) {
property.key = p.visitExpr(property.key orelse Global.panic("Expected property key", .{}));
const key = property.key.?;
@@ -11949,11 +12034,33 @@ pub fn NewParser(
.has_chain_parent = (e_.optional_chain orelse js_ast.OptionalChain.start) == .ccontinue,
});
- // TODO: wan about import namespace call
- var has_spread = false;
- for (e_.args) |*arg| {
- arg.* = p.visitExpr(arg.*);
- has_spread = has_spread or @as(Expr.Tag, arg.data) == .e_spread;
+ // Copy the call side effect flag over if this is a known target
+ switch (e_.target.data) {
+ .e_identifier => |ident| {
+ e_.can_be_unwrapped_if_unused = e_.can_be_unwrapped_if_unused or ident.call_can_be_unwrapped_if_unused;
+ },
+ .e_dot => |dot| {
+ e_.can_be_unwrapped_if_unused = e_.can_be_unwrapped_if_unused or dot.call_can_be_unwrapped_if_unused;
+ },
+ else => {},
+ }
+
+ const is_macro_ref: bool = if (comptime FeatureFlags.is_macro_enabled and
+ jsx_transform_type != .macro)
+ e_.target.data == .e_import_identifier and p.macro_refs.contains(e_.target.data.e_import_identifier.ref)
+ else
+ false;
+
+ {
+ const old_ce = p.options.ignore_dce_annotations;
+ defer p.options.ignore_dce_annotations = old_ce;
+ if (is_macro_ref)
+ p.options.ignore_dce_annotations = true;
+
+ for (e_.args) |_, i| {
+ const arg = e_.args[i];
+ e_.args[i] = p.visitExpr(arg);
+ }
}
if (e_.optional_chain == null and @as(Expr.Tag, e_.target.data) == .e_identifier and e_.target.data.e_identifier.ref.eql(p.require_ref)) {
@@ -11969,22 +12076,22 @@ pub fn NewParser(
}
if (comptime FeatureFlags.is_macro_enabled and jsx_transform_type != .macro) {
- if (e_.target.data == .e_import_identifier) {
+ if (is_macro_ref) {
const ref = e_.target.data.e_import_identifier.ref;
- if (p.macro_refs.get(ref)) |import_record_id| {
- const name = p.symbols.items[ref.inner_index].original_name;
- const record = &p.import_records.items[import_record_id];
- return p.options.macro_context.call(
- record.path.text,
- p.source.path.sourceDir(),
- p.log,
- p.source,
- record.range,
- expr,
- &.{},
- name,
- ) catch return expr;
- }
+ const import_record_id = p.macro_refs.get(ref).?;
+ const name = p.symbols.items[ref.inner_index].original_name;
+ const record = &p.import_records.items[import_record_id];
+ const copied = Expr{ .loc = expr.loc, .data = .{ .e_call = e_ } };
+ return p.options.macro_context.call(
+ record.path.text,
+ p.source.path.sourceDir(),
+ p.log,
+ p.source,
+ record.range,
+ copied,
+ &.{},
+ name,
+ ) catch return expr;
}
}
@@ -12311,6 +12418,7 @@ pub fn NewParser(
.un_typeof, .un_void, .un_not => {
return p.exprCanBeRemovedIfUnused(&ex.value);
},
+
else => {},
}
},
@@ -12571,7 +12679,7 @@ pub fn NewParser(
data.value.expr = p.visitExpr(expr);
// // Optionally preserve the name
- data.value.expr = p.maybeKeepExprSymbolName(expr, "default", was_anonymous_named_expr);
+ data.value.expr = p.maybeKeepExprSymbolName(data.value.expr, "default", was_anonymous_named_expr);
// Discard type-only export default statements
if (is_typescript_enabled) {
@@ -12594,7 +12702,7 @@ pub fn NewParser(
if (p.options.enable_bundling) {
var export_default_args = p.allocator.alloc(Expr, 2) catch unreachable;
export_default_args[0] = p.e(E.Identifier{ .ref = p.exports_ref }, expr.loc);
- export_default_args[1] = expr;
+ export_default_args[1] = data.value.expr;
stmts.append(p.s(S.SExpr{ .value = p.callRuntime(expr.loc, "__exportDefault", export_default_args) }, expr.loc)) catch unreachable;
return;
}
@@ -15250,7 +15358,7 @@ const JSParserMacro = NewParser(.{
.jsx = .macro,
});
const TSParserMacro = NewParser(.{
- .jsx = .react,
+ .jsx = .macro,
.typescript = true,
});
diff --git a/src/js_printer.zig b/src/js_printer.zig
index 3bdeb09ba..1419dbef2 100644
--- a/src/js_printer.zig
+++ b/src/js_printer.zig
@@ -567,7 +567,7 @@ pub fn NewPrinter(
std.fmt.formatFloatScientific(float, .{}, p) catch unreachable;
}
- pub fn printQuotedUTF16(e: *Printer, text: JavascriptString, quote: u8) void {
+ pub fn printQuotedUTF16(e: *Printer, text: []const u16, quote: u8) void {
// utf-8 is a max of 4 bytes
// we leave two extra chars for "\" and "u"
var temp = [6]u8{ 0, 0, 0, 0, 0, 0 };
@@ -857,6 +857,9 @@ pub fn NewPrinter(
}
}
+ // noop for now
+ pub inline fn printPure(p: *Printer) void {}
+
pub fn printQuotedUTF8(p: *Printer, str: string, allow_backtick: bool) void {
const quote = p.bestQuoteCharForString(str, allow_backtick);
p.print(quote);
@@ -872,7 +875,7 @@ pub fn NewPrinter(
}
}
- pub inline fn canPrintIdentifierUTF16(p: *Printer, name: JavascriptString) bool {
+ pub inline fn canPrintIdentifierUTF16(p: *Printer, name: []const u16) bool {
// TODO: fix this
// this is commented out because something isn't quite right
// the problem may lie in isIdentifierUTF16, or it may lie in how these are allocated.
@@ -930,7 +933,7 @@ pub fn NewPrinter(
}
if (has_pure_comment) {
- p.print("/* @__PURE__ */ ");
+ p.printPure();
}
p.printSpaceBeforeIdentifier();
@@ -978,7 +981,7 @@ pub fn NewPrinter(
if (has_pure_comment) {
const was_stmt_start = p.stmt_start == p.writer.written;
- p.print("/* @__PURE__ */ ");
+ p.printPure();
if (was_stmt_start) {
p.stmt_start = p.writer.written;
}
@@ -3707,7 +3710,7 @@ pub fn NewPrinter(
p.print(identifier);
}
- pub fn printIdentifierUTF16(p: *Printer, name: JavascriptString) !void {
+ pub fn printIdentifierUTF16(p: *Printer, name: []const u16) !void {
var temp = [_]u8{ 0, 0, 0, 0, 0, 0 };
const n = name.len;
var i: usize = 0;
diff --git a/src/runtime.zig b/src/runtime.zig
index c8aed0dc7..e0dd3bab7 100644
--- a/src/runtime.zig
+++ b/src/runtime.zig
@@ -252,6 +252,7 @@ pub const Runtime = struct {
"__exportDefault",
};
pub const Name = "<RUNTIME";
+ pub const alt_name = "__runtime.js";
pub const Iterator = struct {
i: usize = 0,
diff --git a/src/string_immutable.zig b/src/string_immutable.zig
index 1a144a747..c9437b757 100644
--- a/src/string_immutable.zig
+++ b/src/string_immutable.zig
@@ -437,11 +437,11 @@ pub fn index(self: string, str: string) i32 {
}
}
-pub fn eqlUtf16(comptime self: string, other: JavascriptString) bool {
+pub fn eqlUtf16(comptime self: string, other: []const u16) bool {
return std.mem.eql(u16, std.unicode.utf8ToUtf16LeStringLiteral(self), other);
}
-pub fn toUTF8Alloc(allocator: *std.mem.Allocator, js: JavascriptString) !string {
+pub fn toUTF8Alloc(allocator: *std.mem.Allocator, js: []const u16) !string {
var temp: [4]u8 = undefined;
var list = std.ArrayList(u8).initCapacity(allocator, js.len) catch unreachable;
var i: usize = 0;
@@ -461,7 +461,7 @@ pub fn toUTF8Alloc(allocator: *std.mem.Allocator, js: JavascriptString) !string
}
// Check utf16 string equals utf8 string without allocating extra memory
-pub fn utf16EqlString(text: []u16, str: string) bool {
+pub fn utf16EqlString(text: []const u16, str: string) bool {
if (text.len > str.len) {
// Strings can't be equal if UTF-16 encoding is longer than UTF-8 encoding
return false;
@@ -603,7 +603,7 @@ pub fn trim(slice: anytype, values_to_strip: []const u8) @TypeOf(slice) {
return slice[begin..end];
}
-pub fn containsNonBmpCodePointUTF16(_text: JavascriptString) bool {
+pub fn containsNonBmpCodePointUTF16(_text: []const u16) bool {
const n = _text.len;
if (n > 0) {
var i: usize = 0;