diff options
-rw-r--r-- | demos/hello-next/bun-framework-next/package.json | 7 | ||||
-rw-r--r-- | demos/hello-next/pages/index.tsx | 19 | ||||
-rw-r--r-- | src/http.zig | 34 | ||||
-rw-r--r-- | src/js_parser/js_parser.zig | 165 | ||||
-rw-r--r-- | src/options.zig | 4 | ||||
-rw-r--r-- | src/resolver/package_json.zig | 26 | ||||
-rw-r--r-- | src/runtime.version | 2 | ||||
-rw-r--r-- | src/runtime/hmr.ts | 1 |
8 files changed, 178 insertions, 80 deletions
diff --git a/demos/hello-next/bun-framework-next/package.json b/demos/hello-next/bun-framework-next/package.json index 7aa47a88f..901391964 100644 --- a/demos/hello-next/bun-framework-next/package.json +++ b/demos/hello-next/bun-framework-next/package.json @@ -1,6 +1,6 @@ { "name": "bun-framework-next", - "version": "0.0.0-6", + "version": "0.0.0-8", "description": "", "main": "package.json", "framework": { @@ -69,5 +69,8 @@ }, "author": "", "license": "ISC", - "dependencies": {} + "dependencies": { + "buffer": "^6.0.3", + "path": "^0.12.7" + } } diff --git a/demos/hello-next/pages/index.tsx b/demos/hello-next/pages/index.tsx index 2866bad51..621f2c401 100644 --- a/demos/hello-next/pages/index.tsx +++ b/demos/hello-next/pages/index.tsx @@ -3,13 +3,11 @@ import Image from "next/image"; import styles from "../styles/Home.module.css"; import Link from "next/link"; -import Title from "../components/Title"; - export default function Home() { return ( <div className={styles.container}> <Head> - <title>Create Next App</title> + <title>Create Next Ap123p</title> <meta name="description" content="Generated by create next app" /> <link rel="icon" href="/favicon.ico" /> </Head> @@ -40,15 +38,12 @@ export default function Home() { <h2>Learn →</h2> <p>Learn about Next.js in an interactive course with quizzes!</p> </a> - - <a - href="https://github.com/vercel/next.js/tree/master/examples" - className={styles.card} - > - <h2>Examples →</h2> - <p>Discover and deploy boilerplate example Next.js projects.</p> - </a> - + <Link href="/hi"> + <a className={styles.card}> + <h2>Examples →</h2> + <p>Discover and deploy boilerplate example Next.js projects.</p> + </a> + </Link> <a href="https://vercel.com/new?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app" className={styles.card} diff --git a/src/http.zig b/src/http.zig index 2848d78d4..9db247073 100644 --- a/src/http.zig +++ b/src/http.zig @@ -50,13 +50,6 @@ usingnamespace @import("./javascript/jsc/bindings/exports.zig"); const Router = @import("./router.zig"); pub const Watcher = watcher.NewWatcher(*Server); -const ENABLE_LOGGER = false; -pub fn println(comptime fmt: string, args: anytype) void { - // if (ENABLE_LOGGER) { - Output.println(fmt, args); - // } -} - const HTTPStatusCode = u10; const URLPath = @import("./http/url_path.zig"); @@ -1079,7 +1072,7 @@ pub const RequestContext = struct { ctx.appendHeader("Sec-WebSocket-Protocol", "bun-hmr"); try ctx.writeStatus(101); try ctx.flushHeaders(); - Output.println("101 - Websocket connected.", .{}); + Output.prettyln("<r><green>101<r><d> Hot Module Reloading connected.", .{}); Output.flush(); var cmd: Api.WebsocketCommand = undefined; @@ -1988,9 +1981,30 @@ pub const Server = struct { const status = req_ctx.status orelse @intCast(HTTPStatusCode, 500); if (req_ctx.log.msgs.items.len == 0) { - println("{d} – {s} {s} as {s}", .{ status, @tagName(req_ctx.method), req.path, req_ctx.mime_type.value }); + switch (status) { + 101, 199...399 => { + Output.prettyln("<r><green>{d}<r><d> {s} <r>{s}<d> as {s}<r>", .{ status, @tagName(req_ctx.method), req.path, req_ctx.mime_type.value }); + }, + 400...499 => { + Output.prettyln("<r><yellow>{d}<r><d> {s} <r>{s}<d> as {s}<r>", .{ status, @tagName(req_ctx.method), req.path, req_ctx.mime_type.value }); + }, + else => { + Output.prettyln("<r><red>{d}<r><d> {s} <r>{s}<d> as {s}<r>", .{ status, @tagName(req_ctx.method), req.path, req_ctx.mime_type.value }); + }, + } } else { - println("{s} {s}", .{ @tagName(req_ctx.method), req.path }); + switch (status) { + 101, 199...399 => { + Output.prettyln("<r><green>{d}<r><d> <r>{s}<d> {s} as {s}<r>", .{ status, @tagName(req_ctx.method), req.path, req_ctx.mime_type.value }); + }, + 400...499 => { + Output.prettyln("<r><yellow>{d}<r><d> <r>{s}<d> {s} as {s}<r>", .{ status, @tagName(req_ctx.method), req.path, req_ctx.mime_type.value }); + }, + else => { + Output.prettyln("<r><red>{d}<r><d> <r>{s}<d> {s} as {s}<r>", .{ status, @tagName(req_ctx.method), req.path, req_ctx.mime_type.value }); + }, + } + for (req_ctx.log.msgs.items) |msg| { msg.writeFormat(Output.errorWriter()) catch continue; } diff --git a/src/js_parser/js_parser.zig b/src/js_parser/js_parser.zig index 4d6189cbc..889da4c2e 100644 --- a/src/js_parser/js_parser.zig +++ b/src/js_parser/js_parser.zig @@ -1829,6 +1829,8 @@ pub const Parser = struct { // Auto-import JSX if (p.options.jsx.parse) { const jsx_symbol: Symbol = p.symbols.items[p.jsx_runtime_ref.inner_index]; + const jsx_static_symbol: Symbol = p.symbols.items[p.jsxs_runtime_ref.inner_index]; + const jsx_fragment_symbol: Symbol = p.symbols.items[p.jsx_fragment_ref.inner_index]; const jsx_factory_symbol: Symbol = p.symbols.items[p.jsx_factory_ref.inner_index]; const jsx_filename_symbol = p.symbols.items[p.jsx_filename_ref.inner_index]; @@ -1840,7 +1842,7 @@ pub const Parser = struct { // So a jsx_symbol usage means a jsx_factory_symbol usage // This is kind of a broken way of doing it because it wouldn't work if it was more than one level deep if (FeatureFlags.jsx_runtime_is_cjs) { - if (jsx_symbol.use_count_estimate > 0) { + if (jsx_symbol.use_count_estimate > 0 or jsx_static_symbol.use_count_estimate > 0) { p.recordUsage(p.jsx_automatic_ref); } @@ -1867,13 +1869,13 @@ pub const Parser = struct { const automatic_namespace_ref = p.jsx_automatic_ref; const decls_count: u32 = - @intCast(u32, @boolToInt(jsx_symbol.use_count_estimate > 0)) + + @intCast(u32, @boolToInt(jsx_symbol.use_count_estimate > 0)) * 2 + @intCast(u32, @boolToInt(jsx_static_symbol.use_count_estimate > 0)) * 2 + @intCast(u32, @boolToInt(jsx_factory_symbol.use_count_estimate > 0)) + @intCast(u32, @boolToInt(jsx_fragment_symbol.use_count_estimate > 0)) + @intCast(u32, @boolToInt(jsx_filename_symbol.use_count_estimate > 0)); const imports_count = - @intCast(u32, @boolToInt(jsx_symbol.use_count_estimate > 0)) + @intCast(u32, std.math.max(jsx_factory_symbol.use_count_estimate, jsx_fragment_symbol.use_count_estimate)) + @intCast(u32, @boolToInt(p.options.features.react_fast_refresh)); + @intCast(u32, @boolToInt(std.math.max(jsx_symbol.use_count_estimate, jsx_static_symbol.use_count_estimate) > 0)) + @intCast(u32, std.math.max(jsx_factory_symbol.use_count_estimate, jsx_fragment_symbol.use_count_estimate)) + @intCast(u32, @boolToInt(p.options.features.react_fast_refresh)); const stmts_count = imports_count + 1; const symbols_count: u32 = imports_count + decls_count; const loc = logger.Loc{ .start = 0 }; @@ -1892,9 +1894,7 @@ pub const Parser = struct { var require_call_args_i: usize = 0; var stmt_i: usize = 0; - if (jsx_symbol.use_count_estimate > 0) { - declared_symbols[declared_symbols_i] = .{ .ref = p.jsx_runtime_ref, .is_top_level = true }; - declared_symbols_i += 1; + if (jsx_symbol.use_count_estimate > 0 or jsx_static_symbol.use_count_estimate > 0) { declared_symbols[declared_symbols_i] = .{ .ref = automatic_namespace_ref, .is_top_level = true }; declared_symbols_i += 1; @@ -1909,24 +1909,54 @@ pub const Parser = struct { } }; - decls[decl_i] = G.Decl{ - .binding = p.b( - B.Identifier{ - .ref = p.jsx_runtime_ref, - }, - loc, - ), - .value = p.e( - E.Dot{ - .target = dot_call_target, - .name = p.options.jsx.jsx, - .name_loc = loc, - .can_be_removed_if_unused = true, - }, - loc, - ), - }; - decl_i += 1; + if (jsx_symbol.use_count_estimate > 0) { + declared_symbols[declared_symbols_i] = .{ .ref = p.jsx_runtime_ref, .is_top_level = true }; + declared_symbols_i += 1; + + decls[decl_i] = G.Decl{ + .binding = p.b( + B.Identifier{ + .ref = p.jsx_runtime_ref, + }, + loc, + ), + .value = p.e( + E.Dot{ + .target = dot_call_target, + .name = p.options.jsx.jsx, + .name_loc = loc, + .can_be_removed_if_unused = true, + }, + loc, + ), + }; + decl_i += 1; + } + + if (jsx_static_symbol.use_count_estimate > 0) { + declared_symbols[declared_symbols_i] = .{ .ref = p.jsxs_runtime_ref, .is_top_level = true }; + declared_symbols_i += 1; + + decls[decl_i] = G.Decl{ + .binding = p.b( + B.Identifier{ + .ref = p.jsxs_runtime_ref, + }, + loc, + ), + .value = p.e( + E.Dot{ + .target = dot_call_target, + .name = p.options.jsx.jsx_static, + .name_loc = loc, + .can_be_removed_if_unused = true, + }, + loc, + ), + }; + + decl_i += 1; + } if (jsx_filename_symbol.use_count_estimate > 0) { declared_symbols[declared_symbols_i] = .{ .ref = p.jsx_filename_ref, .is_top_level = true }; @@ -2360,8 +2390,6 @@ pub const Prefill = struct { }; pub const Runtime = struct { pub var JSXFilename = "__jsxFilename"; - pub var JSXDevelopmentImportName = "jsxDEV"; - pub var JSXImportName = "jsx"; pub var MarkAsModule = "__markAsModule"; pub var CommonJS = "__commonJS"; pub var ReExport = "__reExport"; @@ -2540,6 +2568,7 @@ pub fn NewParser( jsx_factory_ref: js_ast.Ref = Ref.None, jsx_fragment_ref: js_ast.Ref = Ref.None, jsx_automatic_ref: js_ast.Ref = Ref.None, + jsxs_runtime_ref: js_ast.Ref = Ref.None, jsx_classic_ref: js_ast.Ref = Ref.None, // only applicable when is_react_fast_refresh_enabled @@ -3344,9 +3373,9 @@ pub fn NewParser( if (p.options.jsx.development) { p.jsx_filename_ref = p.newSymbol(.hoisted, Prefill.Runtime.JSXFilename) catch unreachable; } - const jsx_importname = p.options.jsx.jsx; p.jsx_fragment_ref = p.declareSymbol(.hoisted, logger.Loc.Empty, p.options.jsx.fragment[p.options.jsx.fragment.len - 1]) catch unreachable; - p.jsx_runtime_ref = p.declareSymbol(.hoisted, logger.Loc.Empty, jsx_importname) catch unreachable; + p.jsx_runtime_ref = p.declareSymbol(.hoisted, logger.Loc.Empty, p.options.jsx.jsx) catch unreachable; + p.jsxs_runtime_ref = p.declareSymbol(.hoisted, logger.Loc.Empty, p.options.jsx.jsx_static) catch unreachable; p.jsx_factory_ref = p.declareSymbol(.hoisted, logger.Loc.Empty, p.options.jsx.factory[p.options.jsx.factory.len - 1]) catch unreachable; if (p.options.jsx.factory.len > 1 or FeatureFlags.jsx_runtime_is_cjs) { @@ -10484,7 +10513,7 @@ pub fn NewParser( } } - const runtime = if (p.options.jsx.runtime == .automatic and !e_.flags.is_key_before_rest) options.JSX.Runtime.automatic else options.JSX.Runtime.classic; + const runtime = if (e_.tag != null and p.options.jsx.runtime == .automatic and !e_.flags.is_key_before_rest) options.JSX.Runtime.automatic else options.JSX.Runtime.classic; var children_count = e_.children.len; const is_childless_tag = FeatureFlags.react_specific_warnings and children_count > 0 and tag.data == .e_string and tag.data.e_string.isUTF8() and js_lexer.ChildlessJSXTags.has(tag.data.e_string.utf8); @@ -10525,7 +10554,7 @@ pub fn NewParser( // Call createElement() return p.e(E.Call{ - .target = p.jsxStringsToMemberExpression(expr.loc, p.jsx_runtime_ref), + .target = p.jsxStringsToMemberExpression(expr.loc, p.jsx_factory_ref), .args = args, // Enable tree shaking .can_be_unwrapped_if_unused = !p.options.ignore_dce_annotations, @@ -10546,18 +10575,44 @@ pub fn NewParser( // children: [] // } - if (children_count > 0) { - for (e_.children[0..children_count]) |child, i| { - e_.children[i] = p.visitExpr(child); - } - const children_key = Expr{ .data = jsxChildrenKeyData, .loc = expr.loc }; - props.append(G.Property{ - .key = children_key, - .value = p.e(E.Array{ - .items = e_.children, - .is_single_line = e_.children.len < 2, - }, expr.loc), - }) catch unreachable; + const is_static_jsx = e_.children.len == 0 or e_.children.len > 1 or e_.children[0].data != .e_array; + + // if (p.options.jsx.development) { + switch (children_count) { + 0 => {}, + 1 => { + // static jsx must always be an array + if (is_static_jsx) { + const children_key = Expr{ .data = jsxChildrenKeyData, .loc = expr.loc }; + e_.children[0] = p.visitExpr(e_.children[0]); + props.append(G.Property{ + .key = children_key, + .value = p.e(E.Array{ + .items = e_.children, + .is_single_line = e_.children.len < 2, + }, expr.loc), + }) catch unreachable; + } else { + const children_key = Expr{ .data = jsxChildrenKeyData, .loc = expr.loc }; + props.append(G.Property{ + .key = children_key, + .value = p.visitExpr(e_.children[0]), + }) catch unreachable; + } + }, + else => { + for (e_.children[0..children_count]) |child, i| { + e_.children[i] = p.visitExpr(child); + } + const children_key = Expr{ .data = jsxChildrenKeyData, .loc = expr.loc }; + props.append(G.Property{ + .key = children_key, + .value = p.e(E.Array{ + .items = e_.children, + .is_single_line = e_.children.len < 2, + }, expr.loc), + }) catch unreachable; + }, } args[1] = p.e(E.Object{ @@ -10568,18 +10623,26 @@ pub fn NewParser( args[2] = key; } else { // if (maybeKey !== undefined) - args[2] = Expr{ .loc = expr.loc, .data = .{ - .e_undefined = E.Undefined{}, - } }; + args[2] = Expr{ + .loc = expr.loc, + .data = .{ + .e_undefined = E.Undefined{}, + }, + }; } if (p.options.jsx.development) { // is the return type of the first child an array? // It's dynamic // Else, it's static - args[3] = Expr{ .loc = expr.loc, .data = .{ .e_boolean = .{ - .value = e_.children.len == 0 or e_.children.len > 1 or std.meta.activeTag(e_.children[0].data) != .e_array, - } } }; + args[3] = Expr{ + .loc = expr.loc, + .data = .{ + .e_boolean = .{ + .value = is_static_jsx, + }, + }, + }; var source = p.allocator.alloc(G.Property, 2) catch unreachable; p.recordUsage(p.jsx_filename_ref); @@ -10606,7 +10669,7 @@ pub fn NewParser( } return p.e(E.Call{ - .target = p.jsxStringsToMemberExpressionAutomatic(expr.loc), + .target = p.jsxStringsToMemberExpressionAutomatic(expr.loc, is_static_jsx), .args = args, // Enable tree shaking .can_be_unwrapped_if_unused = !p.options.ignore_dce_annotations, @@ -11696,8 +11759,8 @@ pub fn NewParser( return false; } - pub fn jsxStringsToMemberExpressionAutomatic(p: *P, loc: logger.Loc) Expr { - return p.jsxStringsToMemberExpression(loc, p.jsx_runtime_ref); + pub fn jsxStringsToMemberExpressionAutomatic(p: *P, loc: logger.Loc, is_static: bool) Expr { + return p.jsxStringsToMemberExpression(loc, if (is_static and !p.options.jsx.development) p.jsxs_runtime_ref else p.jsx_runtime_ref); } // If we are currently in a hoisted child of the module scope, relocate these diff --git a/src/options.zig b/src/options.zig index 81c01f4fd..c989e32f2 100644 --- a/src/options.zig +++ b/src/options.zig @@ -432,7 +432,8 @@ pub const JSX = struct { refresh_runtime: string = "react-refresh/runtime", supports_fast_refresh: bool = false, - jsx: string = "jsxDEV", + jsx: string = Defaults.JSXFunction, + jsx_static: string = Defaults.JSXStaticFunction, development: bool = true, parse: bool = true, @@ -465,6 +466,7 @@ pub const JSX = struct { pub const ImportSourceDev = "react/jsx-dev-runtime"; pub const ImportSource = "react/jsx-runtime"; pub const JSXFunction = "jsx"; + pub const JSXStaticFunction = "jsxs"; pub const JSXFunctionDev = "jsxDEV"; }; diff --git a/src/resolver/package_json.zig b/src/resolver/package_json.zig index 5a6c2f5b0..45f5c3e48 100644 --- a/src/resolver/package_json.zig +++ b/src/resolver/package_json.zig @@ -21,6 +21,25 @@ pub const PackageJSON = struct { production, }; + const node_modules_path = std.fs.path.sep_str ++ "node_modules" ++ std.fs.path.sep_str; + pub fn nameForImport(this: *const PackageJSON, allocator: *std.mem.Allocator) !string { + if (strings.indexOf(this.source.path.text, node_modules_path)) |_| { + return this.name; + } else { + const parent = this.source.path.name.dirWithTrailingSlash(); + if (strings.indexOf(parent, fs.FileSystem.instance.top_level_dir)) |i| { + const relative_dir = parent[i + fs.FileSystem.instance.top_level_dir.len ..]; + var out_dir = try allocator.alloc(u8, relative_dir.len + 2); + std.mem.copy(u8, out_dir[2..], relative_dir); + out_dir[0] = '.'; + out_dir[1] = '/'; + return out_dir; + } + + return this.name; + } + } + pub const FrameworkRouterPair = struct { framework: *options.Framework, router: *options.RouteConfig, @@ -185,6 +204,7 @@ pub const PackageJSON = struct { if (static_prop.expr.asString(allocator)) |str| { if (str.len > 0) { pair.router.static_dir = str; + pair.router.static_dir_enabled = true; } } } @@ -284,7 +304,7 @@ pub const PackageJSON = struct { .development => { if (framework_object.expr.asProperty("development")) |env| { if (loadFrameworkExpression(pair.framework, env.expr, allocator, read_defines)) { - pair.framework.package = package_json.name; + pair.framework.package = package_json.nameForImport(allocator) catch unreachable; pair.framework.development = true; if (env.expr.asProperty("static")) |static_prop| { if (static_prop.expr.asString(allocator)) |str| { @@ -302,7 +322,7 @@ pub const PackageJSON = struct { .production => { if (framework_object.expr.asProperty("production")) |env| { if (loadFrameworkExpression(pair.framework, env.expr, allocator, read_defines)) { - pair.framework.package = package_json.name; + pair.framework.package = package_json.nameForImport(allocator) catch unreachable; pair.framework.development = false; if (env.expr.asProperty("static")) |static_prop| { @@ -322,7 +342,7 @@ pub const PackageJSON = struct { } if (loadFrameworkExpression(pair.framework, framework_object.expr, allocator, read_defines)) { - pair.framework.package = package_json.name; + pair.framework.package = package_json.nameForImport(allocator) catch unreachable; pair.framework.development = false; } } diff --git a/src/runtime.version b/src/runtime.version index fac0eba4e..dd9a0085a 100644 --- a/src/runtime.version +++ b/src/runtime.version @@ -1 +1 @@ -def44909172594cf
\ No newline at end of file +ef1f71fecb089b94
\ No newline at end of file diff --git a/src/runtime/hmr.ts b/src/runtime/hmr.ts index 99cdf634a..10f7e4a5d 100644 --- a/src/runtime/hmr.ts +++ b/src/runtime/hmr.ts @@ -629,6 +629,7 @@ if (typeof window !== "undefined") { // If so, it will use it. // Else, it will fall back to live reloading. case API.Loader.js: + case API.Loader.jsx: case API.Loader.json: case API.Loader.ts: case API.Loader.tsx: { |