diff options
author | 2021-05-30 23:35:43 -0700 | |
---|---|---|
committer | 2021-05-30 23:35:43 -0700 | |
commit | 87d01c9f4a315341c1c5f57e09e29df88ce9c996 (patch) | |
tree | d1612d7198224cb6901edd119ff1e9da0e434ac3 | |
parent | dd72bf5ab657af89d45ee629e8432d9b860a2351 (diff) | |
download | bun-87d01c9f4a315341c1c5f57e09e29df88ce9c996.tar.gz bun-87d01c9f4a315341c1c5f57e09e29df88ce9c996.tar.zst bun-87d01c9f4a315341c1c5f57e09e29df88ce9c996.zip |
Fix printing bugs
Former-commit-id: 52f37e4fe4c8873617abcbc3b3af61e8f1d79edc
-rw-r--r-- | src/bundler.zig | 20 | ||||
-rw-r--r-- | src/global.zig | 7 | ||||
-rw-r--r-- | src/js_ast.zig | 18 | ||||
-rw-r--r-- | src/js_parser/js_parser.zig | 14 | ||||
-rw-r--r-- | src/js_printer.zig | 147 | ||||
-rw-r--r-- | src/json_parser.zig | 12 | ||||
-rw-r--r-- | src/options.zig | 2 | ||||
-rw-r--r-- | src/resolver/resolve_path.zig | 30 |
8 files changed, 206 insertions, 44 deletions
diff --git a/src/bundler.zig b/src/bundler.zig index 13a28edd4..0dfc59ae4 100644 --- a/src/bundler.zig +++ b/src/bundler.zig @@ -117,7 +117,7 @@ pub const Bundler = struct { } defer relative_path_allocator.reset(); - var pretty = try relative_paths_list.append(bundler.fs.relativeTo(source_path)); + var pretty = try relative_paths_list.append(bundler.fs.relative(source_dir, source_path)); var pathname = Fs.PathName.init(pretty); var absolute_pathname = Fs.PathName.init(source_path); @@ -224,11 +224,12 @@ pub const Bundler = struct { const ast = result.ast; for (ast.import_records) |*import_record| { - const source_dir = std.fs.path.dirname(file_path.text) orelse file_path.text; + const source_dir = file_path.name.dir; if (bundler.resolver.resolve(source_dir, import_record.path.text, import_record.kind)) |*resolved_import| { bundler.processImportRecord( - source_dir, + // Include trailing slash + file_path.text[0 .. source_dir.len + 1], resolved_import, import_record, ) catch continue; @@ -283,8 +284,7 @@ pub const Bundler = struct { const output_file = try bundler.print( result, ); - js_ast.Expr.Data.Store.reset(); - js_ast.Stmt.Data.Store.reset(); + return output_file; } @@ -534,7 +534,7 @@ pub const Bundler = struct { // 100.00 µs std.fifo.LinearFifo(resolver.resolver.Result,std.fifo.LinearFifoBufferType { .Dynamic = {}}).writeItemAssumeCapacity if (bundler.options.resolve_mode != .lazy) { - try bundler.resolve_queue.ensureUnusedCapacity(1000); + try bundler.resolve_queue.ensureUnusedCapacity(24); } var entry_points = try allocator.alloc(Resolver.Resolver.Result, bundler.options.entry_points.len); @@ -592,10 +592,16 @@ pub const Bundler = struct { entry = __entry; } + defer { + js_ast.Expr.Data.Store.reset(); + js_ast.Stmt.Data.Store.reset(); + } + const result = bundler.resolver.resolve(bundler.fs.top_level_dir, entry, .entry_point) catch |err| { Output.printError("Error resolving \"{s}\": {s}\n", .{ entry, @errorName(err) }); continue; }; + const key = result.path_pair.primary.text; if (bundler.resolve_results.contains(key)) { continue; @@ -614,6 +620,8 @@ pub const Bundler = struct { switch (bundler.options.resolve_mode) { .lazy, .dev, .bundle => { while (bundler.resolve_queue.readItem()) |item| { + defer js_ast.Expr.Data.Store.reset(); + defer js_ast.Stmt.Data.Store.reset(); const output_file = bundler.buildWithResolveResult(item) catch continue orelse continue; bundler.output_files.append(output_file) catch unreachable; } diff --git a/src/global.zig b/src/global.zig index 4e95b7d1f..e99baf378 100644 --- a/src/global.zig +++ b/src/global.zig @@ -36,6 +36,13 @@ pub const FeatureFlags = struct { // This doesn't really seem to do anything for us pub const disable_filesystem_cache = false and std.Target.current.os.tag == .macos; + pub const css_in_js_import_behavior = CSSModulePolyfill.facade; + + pub const CSSModulePolyfill = enum { + // When you import a .css file and you reference the import in JavaScript + // Just return whatever the property key they referenced was + facade, + }; }; pub const enableTracing = true; diff --git a/src/js_ast.zig b/src/js_ast.zig index d1c136847..00e76d27e 100644 --- a/src/js_ast.zig +++ b/src/js_ast.zig @@ -1000,7 +1000,7 @@ pub const E = struct { }; pub const Object = struct { - properties: []G.Property = &([_]G.Property{}), + properties: []G.Property, comma_after_spread: ?logger.Loc = null, is_single_line: bool = false, is_parenthesized: bool = false, @@ -1545,6 +1545,13 @@ pub const Stmt = struct { s_type_script, s_while, s_with, + + pub fn isExportLike(tag: Tag) bool { + return switch (tag) { + .s_export_clause, .s_export_default, .s_export_equals, .s_export_from, .s_export_star, .s_empty => true, + else => false, + }; + } }; pub const Data = union(Tag) { @@ -1891,9 +1898,10 @@ pub const Expr = struct { pub fn asProperty(expr: *const Expr, name: string) ?Query { if (std.meta.activeTag(expr.data) != .e_object) return null; - const obj = expr.getObject(); + const obj = expr.data.e_object; + if (@ptrToInt(obj.properties.ptr) == 0) return null; - for (obj.properties) |*prop| { + for (obj.properties) |prop| { const value = prop.value orelse continue; const key = prop.key orelse continue; if (std.meta.activeTag(key.data) != .e_string) continue; @@ -2052,7 +2060,7 @@ pub const Expr = struct { return Expr{ .loc = loc, .data = Data{ - .e_boolean = bool_values[@boolToInt(st.value)], + .e_boolean = st, }, }; }, @@ -2868,7 +2876,7 @@ pub const Expr = struct { e_require_or_require_resolve: *E.RequireOrRequireResolve, e_import: *E.Import, - e_boolean: *E.Boolean, + e_boolean: E.Boolean, e_number: *E.Number, e_big_int: *E.BigInt, e_string: *E.String, diff --git a/src/js_parser/js_parser.zig b/src/js_parser/js_parser.zig index 5ab16f072..3dea00dda 100644 --- a/src/js_parser/js_parser.zig +++ b/src/js_parser/js_parser.zig @@ -1692,7 +1692,7 @@ var jsxChildrenKeyData = Expr.Data{ .e_string = &Prefill.String.Children }; var nullExprValueData = E.Null{}; var falseExprValueData = E.Boolean{ .value = false }; var nullValueExpr = Expr.Data{ .e_null = nullExprValueData }; -var falseValueExpr = Expr.Data{ .e_boolean = &falseExprValueData }; +var falseValueExpr = Expr.Data{ .e_boolean = E.Boolean{ .value = false } }; // P is for Parser! // public only because of Binding.ToExpr @@ -9663,7 +9663,7 @@ pub const P = struct { .bin_strict_eq => { const equality = SideEffects.eql(e_.left.data, e_.right.data, p); if (equality.ok) { - return p.e(E.Boolean{ .value = equality.ok }, expr.loc); + return p.e(E.Boolean{ .value = equality.equal }, expr.loc); } // const after_op_loc = locAfterOp(e_.); @@ -9673,7 +9673,7 @@ pub const P = struct { .bin_loose_ne => { const equality = SideEffects.eql(e_.left.data, e_.right.data, p); if (equality.ok) { - return p.e(E.Boolean{ .value = !equality.ok }, expr.loc); + return p.e(E.Boolean{ .value = !equality.equal }, expr.loc); } // const after_op_loc = locAfterOp(e_.); // TODO: warn about equality check @@ -9687,7 +9687,7 @@ pub const P = struct { .bin_strict_ne => { const equality = SideEffects.eql(e_.left.data, e_.right.data, p); if (equality.ok) { - return p.e(E.Boolean{ .value = !equality.ok }, expr.loc); + return p.e(E.Boolean{ .value = !equality.equal }, expr.loc); } }, .bin_nullish_coalescing => { @@ -11034,7 +11034,7 @@ pub const P = struct { // The "else" clause is optional if (data.no) |no| { - if (effects.ok and !effects.value) { + if (effects.ok and effects.value) { const old = p.is_control_flow_dead; p.is_control_flow_dead = true; defer p.is_control_flow_dead = old; @@ -11515,7 +11515,7 @@ pub const P = struct { }, name_loc, ), - p.e(E.Object{}, name_loc), + p.e(E.Object{ .properties = &[_]G.Property{} }, name_loc), p.allocator, ), }, @@ -11534,7 +11534,7 @@ pub const P = struct { .right = Expr.assign( p.e(E.Identifier{ .ref = name_ref }, name_loc), p.e( - E.Object{}, + E.Object{ .properties = &[_]G.Property{} }, name_loc, ), p.allocator, diff --git a/src/js_printer.zig b/src/js_printer.zig index aa0684517..f3432eb33 100644 --- a/src/js_printer.zig +++ b/src/js_printer.zig @@ -140,6 +140,7 @@ pub fn NewPrinter(comptime ascii_only: bool) type { writer: MutableString.Writer, allocator: *std.mem.Allocator, renamer: rename.Renamer, + prev_stmt_tag: Stmt.Tag = .s_empty, const Printer = @This(); pub fn comptime_flush(p: *Printer) void {} @@ -743,7 +744,11 @@ pub fn NewPrinter(comptime ascii_only: bool) type { p.printIndent(); } p.addSourceMapping(record.range.loc); + + p.print("import("); p.printQuotedUTF8(record.path.text, true); + p.print(")"); + if (leading_interior_comments.len > 0) { p.printNewline(); p.options.unindent(); @@ -994,6 +999,7 @@ pub fn NewPrinter(comptime ascii_only: bool) type { if (wrap) { p.print("("); } + p.printSpaceBeforeIdentifier(); p.print("import("); if (e.leading_interior_comments.len > 0) { @@ -1294,14 +1300,14 @@ pub fn NewPrinter(comptime ascii_only: bool) type { // If this was originally a template literal, print it as one as long as we're not minifying if (e.prefer_template) { p.print("`"); - p.printString(e, '`'); + p.printStringContent(e, '`'); p.print("`"); return; } const c = p.bestQuoteCharForString(e.value, true); p.print(c); - p.printString(e, c); + p.printStringContent(e, c); p.print(c); }, .e_template => |e| { @@ -1321,7 +1327,7 @@ pub fn NewPrinter(comptime ascii_only: bool) type { if (e.tag != null) { p.print(e.head.utf8); } else { - p.printString(&e.head, '`'); + p.printStringContent(&e.head, '`'); } } @@ -1333,7 +1339,7 @@ pub fn NewPrinter(comptime ascii_only: bool) type { if (e.tag != null) { p.print(part.tail.utf8); } else { - p.printString(&part.tail, '`'); + p.printStringContent(&part.tail, '`'); } } } @@ -1663,12 +1669,49 @@ pub fn NewPrinter(comptime ascii_only: bool) type { p.print(")"); } - pub fn printString(p: *Printer, str: *const E.String, c: u8) void { + // This assumes the string has already been quoted. + pub fn printStringContent(p: *Printer, str: *const E.String, c: u8) void { if (!str.isUTF8()) { + // its already quoted for us! p.printQuotedUTF16(str.value, c); } else { - // its already quoted for us! - p.print(str.utf8); + p.printUTF8StringEscapedQuotes(str.utf8, c); + } + } + + // Add one outer branch so the inner loop does fewer branches + pub fn printUTF8StringEscapedQuotes(p: *Printer, str: string, c: u8) void { + switch (c) { + '`' => _printUTF8StringEscapedQuotes(p, str, '`'), + '"' => _printUTF8StringEscapedQuotes(p, str, '"'), + '\'' => _printUTF8StringEscapedQuotes(p, str, '\''), + else => unreachable, + } + } + + pub fn _printUTF8StringEscapedQuotes(p: *Printer, str: string, comptime c: u8) void { + var utf8 = str; + var i: usize = 0; + // Walk the string searching for quote characters + // Escape any we find + // Skip over already-escaped strings + while (i < utf8.len) : (i += 1) { + switch (utf8[i]) { + '\\' => { + i += 1; + }, + c => { + p.print(utf8[0..i]); + p.print("\\" ++ &[_]u8{c}); + utf8 = utf8[i + 1 ..]; + i = 0; + }, + + else => {}, + } + } + if (utf8.len > 0) { + p.print(utf8); } } @@ -1758,7 +1801,30 @@ pub fn NewPrinter(comptime ascii_only: bool) type { p.addSourceMapping(_key.loc); if (key.isUTF8()) { p.printSpaceBeforeIdentifier(); - p.printIdentifier(key.utf8); + var allow_shorthand: bool = true; + // In react/cjs/react.development.js, there's part of a function like this: + // var escaperLookup = { + // "=": "=0", + // ":": "=2" + // }; + // While each of those property keys are ASCII, a subset of ASCII is valid as the start of an identifier + // "=" and ":" are not valid + // So we need to check + if (js_lexer.isIdentifierStart(@intCast(js_lexer.CodePoint, key.utf8[0]))) { + p.print(key.utf8); + } else { + allow_shorthand = false; + const quote = p.bestQuoteCharForString(key.utf8, true); + if (quote == '`') { + p.print('['); + } + p.print(quote); + p.printUTF8StringEscapedQuotes(key.utf8, quote); + p.print(quote); + if (quote == '`') { + p.print(']'); + } + } // Use a shorthand property if the names are the same if (item.value) |val| { @@ -1773,7 +1839,9 @@ pub fn NewPrinter(comptime ascii_only: bool) type { if (item.initializer) |initial| { p.printInitializer(initial); } - return; + if (allow_shorthand) { + return; + } } // if (strings) {} }, @@ -1784,7 +1852,9 @@ pub fn NewPrinter(comptime ascii_only: bool) type { if (item.initializer) |initial| { p.printInitializer(initial); } - return; + if (allow_shorthand) { + return; + } } } }, @@ -2055,6 +2125,18 @@ pub fn NewPrinter(comptime ascii_only: bool) type { } pub fn printStmt(p: *Printer, stmt: Stmt) !void { + const prev_stmt_tag = p.prev_stmt_tag; + + // Give an extra newline for readaiblity + defer { + // + if (std.meta.activeTag(stmt.data) != .s_import and prev_stmt_tag == .s_import) { + p.printNewline(); + } + + p.prev_stmt_tag = std.meta.activeTag(stmt.data); + } + debug("<printStmt>: {s}\n", .{stmt}); defer debug("</printStmt>: {s}\n", .{stmt}); p.comptime_flush(); @@ -2086,6 +2168,11 @@ pub fn NewPrinter(comptime ascii_only: bool) type { p.printNewline(); }, .s_class => |s| { + // Give an extra newline for readaiblity + if (prev_stmt_tag != .s_empty) { + p.printNewline(); + } + p.printIndent(); p.printSpaceBeforeIdentifier(); if (s.is_export) { @@ -2102,6 +2189,11 @@ pub fn NewPrinter(comptime ascii_only: bool) type { p.printNewline(); }, .s_export_default => |s| { + // Give an extra newline for export default for readability + if (!prev_stmt_tag.isExportLike()) { + p.printNewline(); + } + p.printIndent(); p.printSpaceBeforeIdentifier(); p.print("export default"); @@ -2158,6 +2250,10 @@ pub fn NewPrinter(comptime ascii_only: bool) type { } }, .s_export_star => |s| { + // Give an extra newline for readaiblity + if (!prev_stmt_tag.isExportLike()) { + p.printNewline(); + } p.printIndent(); p.printSpaceBeforeIdentifier(); p.print("export"); @@ -2177,6 +2273,10 @@ pub fn NewPrinter(comptime ascii_only: bool) type { p.printSemicolonAfterStatement(); }, .s_export_clause => |s| { + // Give an extra newline for export default for readability + if (!prev_stmt_tag.isExportLike()) { + p.printNewline(); + } p.printIndent(); p.printSpaceBeforeIdentifier(); p.print("export"); @@ -2218,6 +2318,10 @@ pub fn NewPrinter(comptime ascii_only: bool) type { p.printSemicolonAfterStatement(); }, .s_export_from => |s| { + // Give an extra newline for readaiblity + if (!prev_stmt_tag.isExportLike()) { + p.printNewline(); + } p.printIndent(); p.printSpaceBeforeIdentifier(); p.print("export"); @@ -2479,9 +2583,32 @@ pub fn NewPrinter(comptime ascii_only: bool) type { p.needs_semicolon = false; }, .s_import => |s| { + if (FeatureFlags.css_in_js_import_behavior == .facade) { + // TODO: check loader instead + if (strings.eqlComptime(p.import_records[s.import_record_index].path.name.ext, ".css")) { + // This comment exists to let tooling authors know where CSS files originated + // To parse this, you just look for a line that starts with //@import url(" + p.print("//@import url(\""); + // We do not URL escape here. + p.print(p.import_records[s.import_record_index].path.text); + + // If they actually use the code, then we emit a facade that just echos whatever they write + if (s.default_name) |name| { + p.print("\"); css-module-facade\nvar "); + p.printSymbol(name.ref.?); + p.print(" = new Proxy({}, {get(_,className,__){return className;}});\n"); + } else { + p.print("\"); css-import-facade\n"); + } + + return; + } + } + var item_count: usize = 0; p.printIndent(); p.printSpaceBeforeIdentifier(); + p.print("import"); p.printSpace(); diff --git a/src/json_parser.zig b/src/json_parser.zig index 134de57b5..65a7205ec 100644 --- a/src/json_parser.zig +++ b/src/json_parser.zig @@ -158,17 +158,7 @@ fn JSONLikeParser(opts: js_lexer.JSONOptions) type { } } - var str: E.String = undefined; - if (p.lexer.string_literal_is_ascii) { - str = E.String{ - .utf8 = p.lexer.string_literal_slice, - }; - } else { - const value = p.lexer.stringLiteralUTF16(); - str = E.String{ - .value = value, - }; - } + var str = p.lexer.toEString(); const is_duplicate = duplicates.exists(p.lexer.string_literal_slice); if (!is_duplicate) { duplicates.put(p.lexer.string_literal_slice) catch unreachable; diff --git a/src/options.zig b/src/options.zig index 22d335aa3..b9e5350f6 100644 --- a/src/options.zig +++ b/src/options.zig @@ -523,7 +523,7 @@ pub const BundleOptions = struct { var user_defines = try stringHashMapFromArrays(defines.RawDefines, allocator, transform.define_keys, transform.define_values); if (transform.define_keys.len == 0) { - try user_defines.put("process.env.NODE_ENV", "development"); + try user_defines.put("process.env.NODE_ENV", "\"development\""); } var resolved_defines = try defines.DefineData.from_input(user_defines, log, allocator); diff --git a/src/resolver/resolve_path.zig b/src/resolver/resolve_path.zig index d5e56b418..cc1b4ae9f 100644 --- a/src/resolver/resolve_path.zig +++ b/src/resolver/resolve_path.zig @@ -174,7 +174,9 @@ pub fn relativeToCommonPath( comptime separator: u8, comptime always_copy: bool, ) []const u8 { - const common_path = if (_common_path.len > 0 and _common_path[0] == separator) _common_path[1..] else _common_path; + const has_leading_separator = _common_path.len > 0 and _common_path[0] == separator; + + const common_path = if (has_leading_separator) _common_path[1..] else _common_path; var shortest = std.math.min(normalized_from.len, normalized_to.len); @@ -213,6 +215,8 @@ pub fn relativeToCommonPath( // For example: from='/foo/bar/baz'; to='/foo/bar' if (normalized_from[common_path.len - 1] == separator) { last_common_separator = common_path.len - 1; + } else if (normalized_from[common_path.len] == separator) { + last_common_separator = common_path.len; } else if (common_path.len == 0) { // We get here if `to` is the root. // For example: from='/foo/bar'; to='/' @@ -227,10 +231,10 @@ pub fn relativeToCommonPath( var out_slice: []u8 = buf[0..0]; if (normalized_from.len > 0) { - var i: usize = last_common_separator; + var i: usize = @boolToInt(normalized_from[0] == separator) + 1 + last_common_separator; while (i <= normalized_from.len) : (i += 1) { - if (i == normalized_from.len or normalized_from[i] == separator) { + if (i == normalized_from.len or (normalized_from[i] == separator and i + 1 < normalized_from.len)) { if (out_slice.len == 0) { out_slice = buf[0 .. out_slice.len + 2]; out_slice[0] = '.'; @@ -265,7 +269,7 @@ pub fn relativeToCommonPath( std.mem.copy(u8, out_slice[start..], tail); } - return buf[0 .. out_slice.len + 1]; + return buf[0..out_slice.len]; } pub fn relativeNormalized(from: []const u8, to: []const u8, comptime platform: Platform, comptime always_copy: bool) []const u8 { @@ -274,6 +278,24 @@ pub fn relativeNormalized(from: []const u8, to: []const u8, comptime platform: P return relativeToCommonPath(common_path, from, to, &relative_to_common_path_buf, comptime platform.separator(), always_copy); } +pub fn dirname(str: []const u8, comptime platform: Platform) []const u8 { + switch (comptime platform.resolve()) { + .loose => { + const separator = lastIndexOfSeparatorLoose(str); + return str[0 .. separator + 1]; + }, + .posix => { + const separator = lastIndexOfSeparatorPosix(str); + return str[0 .. separator + 1]; + }, + .windows => { + const separator = lastIndexOfSeparatorWindows(str) orelse return std.fs.path.diskDesignatorWindows(str); + return str[0 .. separator + 1]; + }, + else => unreachable, + } +} + threadlocal var relative_from_buf: [4096]u8 = undefined; threadlocal var relative_to_buf: [4096]u8 = undefined; pub fn relative(from: []const u8, to: []const u8) []const u8 { |