diff --git a/.changeset/stupid-papayas-accept.md b/.changeset/stupid-papayas-accept.md new file mode 100644 index 000000000..c89f4c8ac --- /dev/null +++ b/.changeset/stupid-papayas-accept.md @@ -0,0 +1,5 @@ +--- +'@astrojs/compiler': patch +--- + +Improve fidelity of sourcemaps for frontmatter diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 000000000..091cec0f2 --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,9 @@ +# This configuration file was automatically generated by Gitpod. +# Please adjust to your needs (see https://www.gitpod.io/docs/config-gitpod-file) +# and commit this file to your remote git repository to share the goodness with others. + +tasks: + - init: pnpm install && pnpm run build && go get && go build ./... && go test ./... && make + command: go run . + + diff --git a/internal/js_scanner/js_scanner.go b/internal/js_scanner/js_scanner.go index fc12a580c..9e7fd3e02 100644 --- a/internal/js_scanner/js_scanner.go +++ b/internal/js_scanner/js_scanner.go @@ -13,15 +13,22 @@ import ( ) type HoistedScripts struct { - Hoisted [][]byte - Body []byte + Hoisted [][]byte + HoistedLocs []loc.Loc + Body [][]byte + BodyLocs []loc.Loc } func HoistExports(source []byte) HoistedScripts { shouldHoist := bytes.Contains(source, []byte("export")) if !shouldHoist { + body := make([][]byte, 0) + body = append(body, source) + bodyLocs := make([]loc.Loc, 0) + bodyLocs = append(bodyLocs, loc.Loc{Start: 0}) return HoistedScripts{ - Body: source, + Body: body, + BodyLocs: bodyLocs, } } @@ -29,8 +36,10 @@ func HoistExports(source []byte) HoistedScripts { i := 0 end := 0 - hoisted := make([][]byte, 1) - body := make([]byte, 0) + hoisted := make([][]byte, 0) + hoistedLocs := make([]loc.Loc, 0) + body := make([][]byte, 0) + bodyLocs := make([]loc.Loc, 0) pairs := make(map[byte]int) // Let's lex the script until we find what we need! @@ -47,8 +56,15 @@ outer: if token == js.ErrorToken { if l.Err() != io.EOF { + body := make([][]byte, 0) + body = append(body, source) + bodyLocs := make([]loc.Loc, 0) + bodyLocs = append(bodyLocs, loc.Loc{Start: 0}) return HoistedScripts{ - Body: source, + Hoisted: hoisted, + HoistedLocs: hoistedLocs, + Body: body, + BodyLocs: bodyLocs, } } break @@ -127,8 +143,10 @@ outer: if foundIdent && foundSemicolonOrLineTerminator && pairs['{'] == 0 && pairs['('] == 0 && pairs['['] == 0 { hoisted = append(hoisted, source[start:i]) + hoistedLocs = append(hoistedLocs, loc.Loc{Start: start}) if end < start { - body = append(body, source[end:start]...) + body = append(body, source[end:start]) + bodyLocs = append(bodyLocs, loc.Loc{Start: end}) } end = i continue outer @@ -136,8 +154,15 @@ outer: if next == js.ErrorToken { if l.Err() != io.EOF { + body := make([][]byte, 0) + body = append(body, source) + bodyLocs := make([]loc.Loc, 0) + bodyLocs = append(bodyLocs, loc.Loc{Start: 0}) return HoistedScripts{ - Body: source, + Hoisted: hoisted, + HoistedLocs: hoistedLocs, + Body: body, + BodyLocs: bodyLocs, } } break outer @@ -164,11 +189,14 @@ outer: i += len(value) } - body = append(body, source[end:]...) + body = append(body, source[end:]) + bodyLocs = append(bodyLocs, loc.Loc{Start: end}) return HoistedScripts{ - Hoisted: hoisted, - Body: body, + Hoisted: hoisted, + HoistedLocs: hoistedLocs, + Body: body, + BodyLocs: bodyLocs, } } @@ -178,18 +206,25 @@ func isKeyword(value []byte) bool { func HoistImports(source []byte) HoistedScripts { imports := make([][]byte, 0) - body := make([]byte, 0) + importLocs := make([]loc.Loc, 0) + body := make([][]byte, 0) + bodyLocs := make([]loc.Loc, 0) prev := 0 - for i, statement := NextImportStatement(source, 0); i > -1; i, statement = NextImportStatement(source, i) { - body = append(body, source[prev:statement.Span.Start]...) + for i, statement := NextImportStatement(source, 0); i > -1 && i < len(source)+1; i, statement = NextImportStatement(source, i) { + bodyLocs = append(bodyLocs, loc.Loc{Start: prev}) + body = append(body, source[prev:statement.Span.Start]) imports = append(imports, statement.Value) + importLocs = append(importLocs, loc.Loc{Start: statement.Span.Start}) prev = i } if prev == 0 { - return HoistedScripts{Body: source} + bodyLocs = append(bodyLocs, loc.Loc{Start: 0}) + body = append(body, source) + return HoistedScripts{Body: body, BodyLocs: bodyLocs} } - body = append(body, source[prev:]...) - return HoistedScripts{Hoisted: imports, Body: body} + bodyLocs = append(bodyLocs, loc.Loc{Start: prev}) + body = append(body, source[prev:]) + return HoistedScripts{Hoisted: imports, HoistedLocs: importLocs, Body: body, BodyLocs: bodyLocs} } type Props struct { @@ -467,7 +502,7 @@ func NextImportStatement(source []byte, pos int) (int, ImportStatement) { for { token, value := l.Next() - if token == js.DivToken || token == js.DivEqToken { + if len(source) > i && token == js.DivToken || token == js.DivEqToken { lns := bytes.Split(source[i+1:], []byte{'\n'}) if bytes.Contains(lns[0], []byte{'/'}) { token, value = l.RegExp() @@ -494,7 +529,7 @@ func NextImportStatement(source []byte, pos int) (int, ImportStatement) { pairs := make(map[byte]int) for { next, nextValue := l.Next() - if next == js.DivToken || next == js.DivEqToken { + if len(source) > i && (next == js.DivToken || next == js.DivEqToken) { lns := bytes.Split(source[i+1:], []byte{'\n'}) if bytes.Contains(lns[0], []byte{'/'}) { next, nextValue = l.RegExp() @@ -524,8 +559,10 @@ func NextImportStatement(source []byte, pos int) (int, ImportStatement) { } if !foundSpecifier && next == js.StringToken { - specifier = string(nextValue[1 : len(nextValue)-1]) - foundSpecifier = true + if len(nextValue) > 1 { + specifier = string(nextValue[1 : len(nextValue)-1]) + foundSpecifier = true + } continue } diff --git a/internal/js_scanner/js_scanner_test.go b/internal/js_scanner/js_scanner_test.go index ace7bf66c..004ffb839 100644 --- a/internal/js_scanner/js_scanner_test.go +++ b/internal/js_scanner/js_scanner_test.go @@ -5,6 +5,7 @@ import ( "encoding/json" "strings" "testing" + "unicode/utf8" "github.com/withastro/compiler/internal/test_utils" ) @@ -16,8 +17,8 @@ type testcase struct { only bool } -func TestHoistImport(t *testing.T) { - tests := []testcase{ +func fixturesHoistImport() []testcase { + return []testcase{ { name: "basic", source: `const value = "test"`, @@ -40,18 +41,18 @@ const article2 = await import('../markdown/article2.md') { name: "big import", source: `import { - a, - b, - c, - d, +a, +b, +c, +d, } from "package" const b = await fetch();`, want: `import { - a, - b, - c, - d, +a, +b, +c, +d, } from "package" `, }, @@ -73,18 +74,18 @@ const b = await fetch();`, name: "import assertion 2", source: `// comment import { - fn +fn } from - "package" assert { - it: 'works' - }; +"package" assert { +it: 'works' +}; const b = await fetch();`, want: `import { - fn +fn } from - "package" assert { - it: 'works' - }; +"package" assert { +it: 'works' +}; `, }, { @@ -96,10 +97,10 @@ import Test from "../components/Test.astro";`, { name: "import.meta.env II", source: `console.log( - import - .meta - .env - .FOO +import + .meta + .env + .FOO ); import Test from "../components/Test.astro";`, want: `import Test from "../components/Test.astro";`, @@ -115,7 +116,7 @@ const b = await fetch()`, name: "getStaticPaths", source: `import { fn } from "package"; export async function getStaticPaths() { - const content = Astro.fetchContent('**/*.md'); +const content = Astro.fetchContent('**/*.md'); } const b = await fetch()`, want: `import { fn } from "package";`, @@ -124,7 +125,7 @@ const b = await fetch()`, name: "getStaticPaths with comments", source: `import { fn } from "package"; export async function getStaticPaths() { - const content = Astro.fetchContent('**/*.md'); +const content = Astro.fetchContent('**/*.md'); } const b = await fetch()`, want: `import { fn } from "package";`, @@ -133,14 +134,14 @@ const b = await fetch()`, name: "getStaticPaths with semicolon", source: `import { fn } from "package"; export async function getStaticPaths() { - const content = Astro.fetchContent('**/*.md'); +const content = Astro.fetchContent('**/*.md'); }; const b = await fetch()`, want: `import { fn } from "package";`, }, { name: "getStaticPaths with RegExp escape", source: `export async function getStaticPaths() { - const pattern = /\.md$/g.test('value'); +const pattern = /\.md$/g.test('value'); } import a from "a";`, want: `import a from "a";`, @@ -148,14 +149,14 @@ import a from "a";`, { name: "getStaticPaths with divider", source: `export async function getStaticPaths() { - const pattern = a / b; +const pattern = a / b; }`, want: ``, }, { name: "getStaticPaths with divider and following content", source: `export async function getStaticPaths() { - const value = 1 / 2; +const value = 1 / 2; } // comment import { b } from "b"; @@ -165,7 +166,7 @@ const { a } = Astro.props;`, { name: "getStaticPaths with regex and following content", source: `export async function getStaticPaths() { - const value = /2/g; +const value = /2/g; } // comment import { b } from "b"; @@ -203,6 +204,10 @@ import { c } from "c"; `, }, } +} + +func TestHoistImport(t *testing.T) { + tests := fixturesHoistImport() for _, tt := range tests { if tt.only { tests = make([]testcase, 0) @@ -226,6 +231,24 @@ import { c } from "c"; } } +func FuzzHoistImport(f *testing.F) { + tests := fixturesHoistImport() + for _, tt := range tests { + f.Add(tt.source) // Use f.Add to provide a seed corpus + } + f.Fuzz(func(t *testing.T, source string) { + result := HoistImports([]byte(source)) + got := []byte{} + for _, imp := range result.Hoisted { + got = append(got, bytes.TrimSpace(imp)...) + got = append(got, '\n') + } + if utf8.ValidString(source) && !utf8.ValidString(string(got)) { + t.Errorf("Import hoisting produced an invalid string: %q", got) + } + }) +} + func TestHoistExport(t *testing.T) { tests := []testcase{ { diff --git a/internal/js_scanner/testdata/fuzz/FuzzHoistImport/ec55358ab2929fbf4deab52587664e42682f0a6ea201a325c5c33f9d18c50456 b/internal/js_scanner/testdata/fuzz/FuzzHoistImport/ec55358ab2929fbf4deab52587664e42682f0a6ea201a325c5c33f9d18c50456 new file mode 100644 index 000000000..8efd95f76 --- /dev/null +++ b/internal/js_scanner/testdata/fuzz/FuzzHoistImport/ec55358ab2929fbf4deab52587664e42682f0a6ea201a325c5c33f9d18c50456 @@ -0,0 +1,2 @@ +go test fuzz v1 +string("import\"\nimport \"\";") diff --git a/internal/printer/print-to-js.go b/internal/printer/print-to-js.go index 7a9b2e162..e6c13f9da 100644 --- a/internal/printer/print-to-js.go +++ b/internal/printer/print-to-js.go @@ -101,7 +101,6 @@ func render1(p *printer, n *Node, opts RenderOptions) { // Root of the document, print all children if n.Type == DocumentNode { - p.addNilSourceMapping() p.printInternalImports(p.opts.InternalURL, &opts) if opts.opts.StaticExtraction && n.FirstChild != nil && n.FirstChild.Type != FrontmatterNode { p.printCSSImports(opts.cssLen) @@ -131,45 +130,89 @@ func render1(p *printer, n *Node, opts RenderOptions) { for c := n.FirstChild; c != nil; c = c.NextSibling { if c.Type == TextNode { - p.addNilSourceMapping() p.printInternalImports(p.opts.InternalURL, &opts) + start := 0 if len(n.Loc) > 0 { - p.addSourceMapping(n.Loc[0]) + start = c.Loc[0].Start } render := js_scanner.HoistImports([]byte(c.Data)) - importStatements := "" if len(render.Hoisted) > 0 { - for _, hoisted := range render.Hoisted { - statement := string(bytes.TrimSpace(hoisted)) + "\n" - importStatements += statement + for i, hoisted := range render.Hoisted { + if len(bytes.TrimSpace(hoisted)) == 0 { + continue + } + hoistedLoc := render.HoistedLocs[i] + p.printTextWithSourcemap(string(hoisted)+"\n", loc.Loc{Start: start + hoistedLoc.Start}) } } - preprocessed := js_scanner.HoistExports(render.Body) - - if len(c.Loc) > 0 { - p.addSourceMapping(c.Loc[0]) - } - p.println(strings.TrimSpace(importStatements)) if opts.opts.StaticExtraction { + p.addNilSourceMapping() p.printCSSImports(opts.cssLen) } // 1. Component imports, if any exist. - p.printComponentMetadata(n.Parent, opts.opts, []byte(importStatements)) + p.addNilSourceMapping() + p.printComponentMetadata(n.Parent, opts.opts, []byte(p.sourcetext)) // 2. Top-level Astro global. + p.printTopLevelAstro(opts.opts) - if len(preprocessed.Hoisted) > 0 { - for _, hoisted := range preprocessed.Hoisted { - p.println(strings.TrimSpace(string(hoisted))) + exports := make([][]byte, 0) + exportLocs := make([]loc.Loc, 0) + bodies := make([][]byte, 0) + bodiesLocs := make([]loc.Loc, 0) + + if len(render.Body) > 0 { + for i, innerBody := range render.Body { + innerStart := render.BodyLocs[i].Start + if len(bytes.TrimSpace(innerBody)) == 0 { + continue + } + + // Extract exports + preprocessed := js_scanner.HoistExports(append(innerBody, '\n')) + if len(preprocessed.Hoisted) > 0 { + for j, exported := range preprocessed.Hoisted { + exportedLoc := preprocessed.HoistedLocs[j] + exportLocs = append(exportLocs, loc.Loc{Start: start + innerStart + exportedLoc.Start}) + exports = append(exports, exported) + } + } + + if len(preprocessed.Body) > 0 { + for j, body := range preprocessed.Body { + bodyLoc := preprocessed.BodyLocs[j] + bodiesLocs = append(bodiesLocs, loc.Loc{Start: start + innerStart + bodyLoc.Start}) + bodies = append(bodies, body) + } + } } } - p.printFuncPrelude(opts.opts) - p.print(strings.TrimSpace(string(preprocessed.Body))) + // PRINT EXPORTS + if len(exports) > 0 { + for i, exported := range exports { + exportLoc := exportLocs[i] + if len(bytes.TrimSpace(exported)) == 0 { + continue + } + p.printTextWithSourcemap(string(exported), exportLoc) + } + } + p.printFuncPrelude(opts.opts) + // PRINT BODY + if len(bodies) > 0 { + for i, body := range bodies { + bodyLoc := bodiesLocs[i] + if len(bytes.TrimSpace(body)) == 0 { + continue + } + p.printTextWithSourcemap(string(body), bodyLoc) + } + } // Print empty just to ensure a newline p.println("") if len(n.Parent.Styles) > 0 { @@ -259,15 +302,19 @@ func render1(p *printer, n *Node, opts RenderOptions) { return } text := escapeText(n.Data) - p.addSourceMapping(n.Loc[0]) - p.print(text) + p.printTextWithSourcemap(text, n.Loc[0]) return case ElementNode: // No-op. case CommentNode: - p.addSourceMapping(n.Loc[0]) + start := n.Loc[0].Start - 4 + p.addSourceMapping(loc.Loc{Start: start}) p.print("") return case DoctypeNode: @@ -296,7 +343,7 @@ func render1(p *printer, n *Node, opts RenderOptions) { for c := n.FirstChild; c != nil; c = c.NextSibling { p.addSourceMapping(c.Loc[0]) if c.Type == TextNode { - p.print(c.Data) + p.printTextWithSourcemap(c.Data, c.Loc[0]) continue } if c.PrevSibling == nil || c.PrevSibling.Type == TextNode { @@ -357,10 +404,11 @@ func render1(p *printer, n *Node, opts RenderOptions) { p.printMaybeRenderHead() } } + p.addSourceMapping(loc.Loc{Start: n.Loc[0].Start - 1}) p.print("<") } - p.addSourceMapping(loc.Loc{Start: n.Loc[0].Start + 1}) + p.addSourceMapping(n.Loc[0]) switch true { case isFragment: p.print("Fragment") @@ -373,7 +421,7 @@ func render1(p *printer, n *Node, opts RenderOptions) { p.print(n.Data) } - p.addSourceMapping(n.Loc[0]) + p.addNilSourceMapping() if isImplicit { // do nothing } else if isComponent { @@ -452,7 +500,7 @@ func render1(p *printer, n *Node, opts RenderOptions) { case "iframe", "noembed", "noframes", "noscript", "plaintext", "script", "style", "xmp": for c := n.FirstChild; c != nil; c = c.NextSibling { if c.Type == TextNode { - p.print(escapeText(c.Data)) + p.printTextWithSourcemap(escapeText(c.Data), c.Loc[0]) } else { render1(p, c, RenderOptions{ isRoot: false, @@ -676,7 +724,22 @@ func render1(p *printer, n *Node, opts RenderOptions) { *opts.printedMaybeHead = true p.printRenderHead() } - p.print(``) + start := 2 + if len(n.Loc) > 0 { + start = n.Loc[0].Start + } + if len(n.Loc) >= 2 { + start = n.Loc[1].Start + } + start -= 2 + p.addSourceMapping(loc.Loc{Start: start}) + p.print(``) } } diff --git a/internal/printer/printer.go b/internal/printer/printer.go index 068071386..74e1a535a 100644 --- a/internal/printer/printer.go +++ b/internal/printer/printer.go @@ -80,28 +80,47 @@ func (p *printer) printInternalImports(importSpecifier string, opts *RenderOptio return } p.addNilSourceMapping() + p.print("") p.print("import {\n ") + p.addNilSourceMapping() p.print(FRAGMENT + ",\n ") + p.addNilSourceMapping() p.print("render as " + TEMPLATE_TAG + ",\n ") + p.addNilSourceMapping() p.print("createAstro as " + CREATE_ASTRO + ",\n ") + p.addNilSourceMapping() p.print("createComponent as " + CREATE_COMPONENT + ",\n ") + p.addNilSourceMapping() p.print("renderComponent as " + RENDER_COMPONENT + ",\n ") + p.addNilSourceMapping() p.print("renderHead as " + RENDER_HEAD + ",\n ") + p.addNilSourceMapping() p.print("maybeRenderHead as " + MAYBE_RENDER_HEAD + ",\n ") + p.addNilSourceMapping() p.print("unescapeHTML as " + UNESCAPE_HTML + ",\n ") + p.addNilSourceMapping() p.print("renderSlot as " + RENDER_SLOT + ",\n ") + p.addNilSourceMapping() p.print("mergeSlots as " + MERGE_SLOTS + ",\n ") + p.addNilSourceMapping() p.print("addAttribute as " + ADD_ATTRIBUTE + ",\n ") + p.addNilSourceMapping() p.print("spreadAttributes as " + SPREAD_ATTRIBUTES + ",\n ") + p.addNilSourceMapping() p.print("defineStyleVars as " + DEFINE_STYLE_VARS + ",\n ") + p.addNilSourceMapping() p.print("defineScriptVars as " + DEFINE_SCRIPT_VARS + ",\n ") + // Only needed if using fallback `resolvePath` as it calls `$$metadata.resolvePath` if opts.opts.ResolvePath == nil { + p.addNilSourceMapping() p.print("createMetadata as " + CREATE_METADATA) } + p.addNilSourceMapping() p.print("\n} from \"") p.print(importSpecifier) p.print("\";\n") + p.addNilSourceMapping() p.hasInternalImports = true } @@ -217,9 +236,10 @@ func (p *printer) printFuncPrelude(opts transform.TransformOptions) { } componentName := getComponentName(opts.Pathname) p.addNilSourceMapping() - p.println("\n//@ts-ignore") p.println(fmt.Sprintf("const %s = %s(async (%s, $$props, %s) => {", componentName, CREATE_COMPONENT, RESULT, SLOTS)) + p.addNilSourceMapping() p.println(fmt.Sprintf("const Astro = %s.createAstro($$Astro, $$props, %s);", RESULT, SLOTS)) + p.addNilSourceMapping() p.println(fmt.Sprintf("Astro.self = %s;", componentName)) p.hasFuncPrelude = true } @@ -333,22 +353,29 @@ func (p *printer) printAttribute(attr astro.Attribute, n *astro.Node) { case astro.QuotedAttribute: p.addSourceMapping(attr.KeyLoc) p.print(attr.Key) - p.print("=") - p.addSourceMapping(attr.ValLoc) - p.print(`"` + encodeDoubleQuote(escapeInterpolation(escapeBackticks(attr.Val))) + `"`) + p.addNilSourceMapping() + p.print(`="`) + p.printTextWithSourcemap(encodeDoubleQuote(escapeInterpolation(escapeBackticks(attr.Val))), attr.ValLoc) + p.addNilSourceMapping() + p.print(`"`) case astro.EmptyAttribute: p.addSourceMapping(attr.KeyLoc) p.print(attr.Key) case astro.ExpressionAttribute: + p.addNilSourceMapping() p.print(fmt.Sprintf("${%s(", ADD_ATTRIBUTE)) - p.addSourceMapping(attr.ValLoc) if strings.TrimSpace(attr.Val) == "" { + p.addNilSourceMapping() p.print("(void 0)") } else { - p.print(strings.TrimSpace(attr.Val)) + p.printTextWithSourcemap(attr.Val, attr.ValLoc) } + p.addNilSourceMapping() + p.print(`, "`) p.addSourceMapping(attr.KeyLoc) - p.print(`, "` + strings.TrimSpace(attr.Key) + `")}`) + p.print(attr.Key) + p.addNilSourceMapping() + p.print(`")}`) case astro.SpreadAttribute: injectClass := false for p := n.Parent; p != nil; p = p.Parent { diff --git a/internal/printer/printer_test.go b/internal/printer/printer_test.go index 6ca226688..f5cbbe47d 100644 --- a/internal/printer/printer_test.go +++ b/internal/printer/printer_test.go @@ -32,10 +32,9 @@ var INTERNAL_IMPORTS = fmt.Sprintf("import {\n %s\n} from \"%s\";\n", strings.J "defineScriptVars as " + DEFINE_SCRIPT_VARS, "createMetadata as " + CREATE_METADATA, }, ",\n "), "http://localhost:3000/") -var PRELUDE = fmt.Sprintf(`//@ts-ignore -const $$Component = %s(async ($$result, $$props, %s) => { +var PRELUDE = fmt.Sprintf(`const $$Component = %s(async ($$result, $$props, %s) => { const Astro = $$result.createAstro($$Astro, $$props, %s); -Astro.self = $$Component;%s`, CREATE_COMPONENT, SLOTS, SLOTS, "\n") +Astro.self = $$Component;%s`, CREATE_COMPONENT, SLOTS, SLOTS, "\n\n") var RETURN = fmt.Sprintf("return %s%s", TEMPLATE_TAG, BACKTICK) var SUFFIX = fmt.Sprintf("%s;", BACKTICK) + ` }); @@ -333,18 +332,31 @@ mod.export(); }, }, { - name: "export comments", + name: "export comments I", source: `--- -// +// hmm export const foo = 0 /* */ ---`, want: want{ - frontmatter: []string{"", "//\n/*\n*/"}, + frontmatter: []string{"", "// hmm\n/*\n*/"}, getStaticPaths: "export const foo = 0", }, }, + { + name: "export comments II", + source: `--- +// hmm +export const foo = 0; +/* +*/ +---`, + want: want{ + frontmatter: []string{"", "// hmm\n/*\n*/"}, + getStaticPaths: "export const foo = 0;", + }, + }, { name: "import assertions", source: `--- @@ -1207,8 +1219,8 @@ import Widget2 from '../components/Widget2.astro';`}, { name: "slots (dynamic name)", source: `--- - import Component from 'test'; - const name = 'named'; +import Component from 'test'; +const name = 'named'; ---
Named
@@ -2442,13 +2454,13 @@ const items = ["Dog", "Cat", "Platipus"]; metadata += "] }" toMatch += "\n\n" + fmt.Sprintf("export const %s = %s(import.meta.url, %s);\n\n", METADATA, CREATE_METADATA, metadata) - toMatch += test_utils.Dedent(CREATE_ASTRO_CALL) + "\n\n" + toMatch += test_utils.Dedent(CREATE_ASTRO_CALL) + "\n" if len(tt.want.getStaticPaths) > 0 { toMatch += strings.TrimSpace(test_utils.Dedent(tt.want.getStaticPaths)) + "\n\n" } toMatch += test_utils.Dedent(PRELUDE) + "\n" if len(tt.want.frontmatter) > 1 { - toMatch += test_utils.Dedent(tt.want.frontmatter[1]) + toMatch += strings.TrimSpace(test_utils.Dedent(tt.want.frontmatter[1])) } toMatch += "\n" if len(tt.want.definedVars) > 0 { @@ -2489,7 +2501,7 @@ const items = ["Dog", "Cat", "Platipus"]; } // compare to expected string, show diff if mismatch - if diff := test_utils.ANSIDiff(test_utils.Dedent(toMatch), test_utils.Dedent(output)); diff != "" { + if diff := test_utils.ANSIDiff(test_utils.RemoveNewlines(test_utils.Dedent(toMatch)), test_utils.RemoveNewlines(test_utils.Dedent(output))); diff != "" { t.Errorf("mismatch (-want +got):\n%s", diff) } }) diff --git a/internal/test_utils/test_utils.go b/internal/test_utils/test_utils.go index 735216d13..76937c50e 100644 --- a/internal/test_utils/test_utils.go +++ b/internal/test_utils/test_utils.go @@ -8,6 +8,10 @@ import ( "github.com/lithammer/dedent" ) +func RemoveNewlines(input string) string { + return strings.ReplaceAll(input, "\n", "") +} + func Dedent(input string) string { return dedent.Dedent( // removes any leading whitespace strings.ReplaceAll( // compress linebreaks to 1 or 2 lines max diff --git a/packages/compiler/sourcemap.astro.js b/packages/compiler/sourcemap.astro.js deleted file mode 100644 index e0f575e99..000000000 --- a/packages/compiler/sourcemap.astro.js +++ /dev/null @@ -1,6 +0,0 @@ - -{/** prettier-ignore */}
  • - - -export default function Index__AstroComponent_(_props: Record): any {} -//# sourceMappingURL=data:application/json;charset=utf-8;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiaW5kZXguYXN0cm8iXSwKICAic291cmNlc0NvbnRlbnQiOiBbIlxuXHUwMDNjIS0tIHByZXR0aWVyLWlnbm9yZSAtLVx1MDAzZVxuXHUwMDNjbGkgL1x1MDAzZVxuIl0sCiAgIm1hcHBpbmdzIjogIkFBQUEsQUFBQTtBQUFBLElBQ0ksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxBQURwQixJQUVBLENBQUMsRUFBRSxDQUFDLEVBQUU7QUFDTixBQUhBO0FBQUE7IiwKICAibmFtZXMiOiBbXQp9 \ No newline at end of file diff --git a/packages/compiler/test/js-sourcemaps/complex-frontmatter.ts b/packages/compiler/test/js-sourcemaps/complex-frontmatter.ts new file mode 100644 index 000000000..06b1c2811 --- /dev/null +++ b/packages/compiler/test/js-sourcemaps/complex-frontmatter.ts @@ -0,0 +1,46 @@ +import { test } from 'uvu'; +import * as assert from 'uvu/assert'; +import { testJSSourcemap } from '../utils'; + +const input = `--- +// TODO: Due to this issue: https://github.com/withastro/astro/issues/1438, this route can't be in the same folder +// as the paginated article list is or they'll conflict, so this means our articles URL are \`/article\/\${slug}\` instead +// of \`/articles/\${slug}\` (with a s), once that issue is fixed, we'll be able to put it back in the right place + +const foobar = true; + +import { Article, postProcessArticle } from "$data/articles"; +import type { GetStaticPaths, MDXInstance } from "$data/shared"; +import ArticleLayout from "$layouts/ArticleLayout.astro"; +import { getSlugFromFile } from "$utils"; + +export const getStaticPaths: GetStaticPaths = async () => { + const articles = await Astro.glob
    ("/content/articles/**/*.mdx"); + return articles.map((article) => { + const augmentedFrontmatter = postProcessArticle(article.frontmatter, article.file); + + return { + params: { slug: getSlugFromFile(article.file) }, + props: { article: { ...article, frontmatter: augmentedFrontmatter } }, + }; + }); +}; + +interface Props { + article: MDXInstance
    ; +} + +const { article } = Astro.props; +--- + +`; + +test('tracks getStaticPaths', async () => { + const loc = await testJSSourcemap(input, 'getStaticPaths'); + assert.equal(loc, { source: 'index.astro', line: 13, column: 14, name: null }); +}); + +test('tracks foobar', async () => { + const loc = await testJSSourcemap(input, 'foobar'); + assert.equal(loc, { source: 'index.astro', line: 6, column: 7, name: null }); +}); diff --git a/packages/compiler/test/js-sourcemaps/deprecated.ts b/packages/compiler/test/js-sourcemaps/deprecated.ts new file mode 100644 index 000000000..d1227b4c0 --- /dev/null +++ b/packages/compiler/test/js-sourcemaps/deprecated.ts @@ -0,0 +1,23 @@ +import { test } from 'uvu'; +import * as assert from 'uvu/assert'; +import { testJSSourcemap } from '../utils'; + +test('script is:inline', async () => { + const input = `--- + /** @deprecated */ +const deprecated = "Astro" +deprecated; +const hello = "Astro" +--- +`; + const output = await testJSSourcemap(input, `deprecated;`); + + assert.equal(output, { + line: 4, + column: 1, + source: 'index.astro', + name: null, + }); +}); + +test.run(); diff --git a/packages/compiler/test/js-sourcemaps/error.ts b/packages/compiler/test/js-sourcemaps/error.ts new file mode 100644 index 000000000..628a59062 --- /dev/null +++ b/packages/compiler/test/js-sourcemaps/error.ts @@ -0,0 +1,48 @@ +import { test } from 'uvu'; +import * as assert from 'uvu/assert'; +import { testJSSourcemap } from '../utils'; + +test('svelte error', async () => { + const input = `--- +import SvelteOptionalProps from "./SvelteOptionalProps.svelte" +--- + +`; + const output = await testJSSourcemap(input, ''); + + assert.equal(output, { + line: 5, + column: 1, + source: 'index.astro', + name: null, + }); +}); + +test('vue error', async () => { + const input = `--- +import SvelteError from "./SvelteError.svelte" +import VueError from "./VueError.vue" +--- + + +`; + const svelte = await testJSSourcemap(input, ''); + + assert.equal(svelte, { + line: 6, + column: 1, + source: 'index.astro', + name: null, + }); + + const vue = await testJSSourcemap(input, ''); + + assert.equal(vue, { + line: 7, + column: 1, + source: 'index.astro', + name: null, + }); +}); + +test.run(); diff --git a/packages/compiler/test/js-sourcemaps/frontmatter.ts b/packages/compiler/test/js-sourcemaps/frontmatter.ts new file mode 100644 index 000000000..ec9b950d6 --- /dev/null +++ b/packages/compiler/test/js-sourcemaps/frontmatter.ts @@ -0,0 +1,20 @@ +import { test } from 'uvu'; +import * as assert from 'uvu/assert'; +import { testJSSourcemap } from '../utils'; + +test('frontmatter', async () => { + const input = `--- +nonexistent +--- +`; + const output = await testJSSourcemap(input, 'nonexistent'); + + assert.equal(output, { + line: 2, + column: 1, + source: 'index.astro', + name: null, + }); +}); + +test.run(); diff --git a/packages/compiler/test/js-sourcemaps/hover.ts b/packages/compiler/test/js-sourcemaps/hover.ts new file mode 100644 index 000000000..355adaa43 --- /dev/null +++ b/packages/compiler/test/js-sourcemaps/hover.ts @@ -0,0 +1,52 @@ +import { test } from 'uvu'; +import * as assert from 'uvu/assert'; +import { testJSSourcemap } from '../utils'; + +const fixture = `--- + const MyVariable = "Astro" + + /** Documentation */ + const MyDocumentedVariable = "Astro" + + /** @author Astro */ + const MyJSDocVariable = "Astro" +--- +`; + +test('hover I', async () => { + const input = fixture; + const output = await testJSSourcemap(input, 'MyVariable'); + + assert.equal(output, { + line: 2, + column: 11, + source: 'index.astro', + name: null, + }); +}); + +test('hover II', async () => { + const input = fixture; + const output = await testJSSourcemap(input, 'MyDocumentedVariable'); + + assert.equal(output, { + line: 5, + column: 11, + source: 'index.astro', + name: null, + }); +}); + +test('hover III', async () => { + const input = fixture; + const output = await testJSSourcemap(input, 'MyJSDocVariable'); + + assert.equal(output, { + line: 8, + column: 11, + source: 'index.astro', + name: null, + }); +}); + +test.run(); diff --git a/packages/compiler/test/js-sourcemaps/module.ts b/packages/compiler/test/js-sourcemaps/module.ts new file mode 100644 index 000000000..e05ce2891 --- /dev/null +++ b/packages/compiler/test/js-sourcemaps/module.ts @@ -0,0 +1,27 @@ +import { test } from 'uvu'; +import * as assert from 'uvu/assert'; +import { testJSSourcemap } from '../utils'; + +test('script is:inline', async () => { + const input = `--- + // valid + import { foo } from './script.js'; + import ComponentAstro from './astro.astro'; + import ComponentSvelte from './svelte.svelte'; + import ComponentVue from './vue.vue'; + // invalid + import { baz } from './script'; + foo;baz;ComponentAstro;ComponentSvelte;ComponentVue; +--- +`; + const output = await testJSSourcemap(input, `'./script'`); + + assert.equal(output, { + line: 8, + column: 23, + source: 'index.astro', + name: null, + }); +}); + +test.run(); diff --git a/packages/compiler/test/js-sourcemaps/script.ts b/packages/compiler/test/js-sourcemaps/script.ts new file mode 100644 index 000000000..964cd8f0d --- /dev/null +++ b/packages/compiler/test/js-sourcemaps/script.ts @@ -0,0 +1,21 @@ +import { test } from 'uvu'; +import * as assert from 'uvu/assert'; +import { testJSSourcemap } from '../utils'; + +test('script is:inline', async () => { + const input = ` +`; + const output = await testJSSourcemap(input, '\n'); + + assert.equal(output, { + line: 1, + column: 18, + source: 'index.astro', + name: null, + }); +}); + +test.run(); diff --git a/packages/compiler/test/js-sourcemaps/template.ts b/packages/compiler/test/js-sourcemaps/template.ts new file mode 100644 index 000000000..5b553d276 --- /dev/null +++ b/packages/compiler/test/js-sourcemaps/template.ts @@ -0,0 +1,93 @@ +import { test } from 'uvu'; +import * as assert from 'uvu/assert'; +import { testJSSourcemap } from '../utils'; + +test('template expression basic', async () => { + const input = `
    {nonexistent}
    `; + + const output = await testJSSourcemap(input, 'nonexistent'); + assert.equal(output, { + source: 'index.astro', + line: 1, + column: 6, + name: null, + }); +}); + +test('template expression has dot', async () => { + const input = `
    {console.log(hey)}
    `; + const output = await testJSSourcemap(input, 'log'); + assert.equal(output, { + source: 'index.astro', + line: 1, + column: 14, + name: null, + }); +}); + +test('template expression with addition', async () => { + const input = `{"hello" + hey}`; + const output = await testJSSourcemap(input, 'hey'); + assert.equal(output, { + source: 'index.astro', + line: 1, + column: 11, + name: null, + }); +}); + +test('html attribute', async () => { + const input = ``; + const output = await testJSSourcemap(input, 'color'); + assert.equal(output, { + source: 'index.astro', + name: null, + line: 1, + column: 5, + }); +}); + +test('complex template expression', async () => { + const input = `{[].map(ITEM => { +v = "what"; +return
    {ITEMS}
    +})}`; + const item = await testJSSourcemap(input, 'ITEM'); + const items = await testJSSourcemap(input, 'ITEMS'); + assert.equal(item, { + source: 'index.astro', + name: null, + line: 1, + column: 8, + }); + assert.equal(items, { + source: 'index.astro', + name: null, + line: 3, + column: 14, + }); +}); + +test('attributes', async () => { + const input = `
    `; + const className = await testJSSourcemap(input, 'className'); + assert.equal(className, { + source: 'index.astro', + name: null, + line: 1, + column: 5, + }); +}); + +test('special attributes', async () => { + const input = `
    `; + const onClick = await testJSSourcemap(input, '@on.click'); + assert.equal(onClick, { + source: 'index.astro', + name: null, + line: 1, + column: 5, + }); +}); + +test.run(); diff --git a/packages/compiler/test/tsx-sourcemaps/attributes.ts b/packages/compiler/test/tsx-sourcemaps/attributes.ts index d197d0c4c..42e1dc1f7 100644 --- a/packages/compiler/test/tsx-sourcemaps/attributes.ts +++ b/packages/compiler/test/tsx-sourcemaps/attributes.ts @@ -1,11 +1,11 @@ import { test } from 'uvu'; import * as assert from 'uvu/assert'; -import { testSourcemap } from '../utils'; +import { testTSXSourcemap } from '../utils'; test('shorthand attribute', async () => { const input = `
    `; - const output = await testSourcemap(input, 'name'); + const output = await testTSXSourcemap(input, 'name'); assert.equal(output, { source: 'index.astro', line: 1, @@ -17,7 +17,7 @@ test('shorthand attribute', async () => { test('empty quoted attribute', async () => { const input = `
    `; - const open = await testSourcemap(input, '"'); + const open = await testTSXSourcemap(input, '"'); assert.equal(open, { source: 'index.astro', line: 1, @@ -31,7 +31,7 @@ test('template literal attribute', async () => { --- `; - const open = await testSourcemap(input, 'foo'); + const open = await testTSXSourcemap(input, 'foo'); assert.equal(open, { source: 'index.astro', line: 3, diff --git a/packages/compiler/test/tsx-sourcemaps/deprecated.ts b/packages/compiler/test/tsx-sourcemaps/deprecated.ts index b9711747e..0cd1d1ae9 100644 --- a/packages/compiler/test/tsx-sourcemaps/deprecated.ts +++ b/packages/compiler/test/tsx-sourcemaps/deprecated.ts @@ -1,6 +1,6 @@ import { test } from 'uvu'; import * as assert from 'uvu/assert'; -import { testSourcemap } from '../utils'; +import { testTSXSourcemap } from '../utils'; test('script is:inline', async () => { const input = `--- @@ -10,7 +10,7 @@ deprecated; const hello = "Astro" --- `; - const output = await testSourcemap(input, `deprecated;`); + const output = await testTSXSourcemap(input, `deprecated;`); assert.equal(output, { line: 4, diff --git a/packages/compiler/test/tsx-sourcemaps/error.ts b/packages/compiler/test/tsx-sourcemaps/error.ts index 8b78ce942..6dd0d1b45 100644 --- a/packages/compiler/test/tsx-sourcemaps/error.ts +++ b/packages/compiler/test/tsx-sourcemaps/error.ts @@ -1,6 +1,6 @@ import { test } from 'uvu'; import * as assert from 'uvu/assert'; -import { testSourcemap } from '../utils'; +import { testTSXSourcemap } from '../utils'; test('svelte error', async () => { const input = `--- @@ -8,7 +8,7 @@ import SvelteOptionalProps from "./SvelteOptionalProps.svelte" --- `; - const output = await testSourcemap(input, ''); + const output = await testTSXSourcemap(input, ''); assert.equal(output, { line: 5, @@ -26,7 +26,7 @@ import VueError from "./VueError.vue" `; - const svelte = await testSourcemap(input, ''); + const svelte = await testTSXSourcemap(input, ''); assert.equal(svelte, { line: 6, @@ -35,7 +35,7 @@ import VueError from "./VueError.vue" name: null, }); - const vue = await testSourcemap(input, ''); + const vue = await testTSXSourcemap(input, ''); assert.equal(vue, { line: 7, diff --git a/packages/compiler/test/tsx-sourcemaps/frontmatter.ts b/packages/compiler/test/tsx-sourcemaps/frontmatter.ts index 6e0c4e432..6064f8b20 100644 --- a/packages/compiler/test/tsx-sourcemaps/frontmatter.ts +++ b/packages/compiler/test/tsx-sourcemaps/frontmatter.ts @@ -1,13 +1,13 @@ import { test } from 'uvu'; import * as assert from 'uvu/assert'; -import { testSourcemap } from '../utils'; +import { testTSXSourcemap } from '../utils'; test('frontmatter', async () => { const input = `--- nonexistent --- `; - const output = await testSourcemap(input, 'nonexistent'); + const output = await testTSXSourcemap(input, 'nonexistent'); assert.equal(output, { line: 2, diff --git a/packages/compiler/test/tsx-sourcemaps/hover.ts b/packages/compiler/test/tsx-sourcemaps/hover.ts index c2ee40989..2e43a7c96 100644 --- a/packages/compiler/test/tsx-sourcemaps/hover.ts +++ b/packages/compiler/test/tsx-sourcemaps/hover.ts @@ -1,6 +1,6 @@ import { test } from 'uvu'; import * as assert from 'uvu/assert'; -import { testSourcemap } from '../utils'; +import { testTSXSourcemap } from '../utils'; const fixture = `--- const MyVariable = "Astro" @@ -15,7 +15,7 @@ const fixture = `--- test('hover I', async () => { const input = fixture; - const output = await testSourcemap(input, 'MyVariable'); + const output = await testTSXSourcemap(input, 'MyVariable'); assert.equal(output, { line: 2, @@ -27,7 +27,7 @@ test('hover I', async () => { test('hover II', async () => { const input = fixture; - const output = await testSourcemap(input, 'MyDocumentedVariable'); + const output = await testTSXSourcemap(input, 'MyDocumentedVariable'); assert.equal(output, { line: 5, @@ -39,7 +39,7 @@ test('hover II', async () => { test('hover III', async () => { const input = fixture; - const output = await testSourcemap(input, 'MyJSDocVariable'); + const output = await testTSXSourcemap(input, 'MyJSDocVariable'); assert.equal(output, { line: 8, diff --git a/packages/compiler/test/tsx-sourcemaps/module.ts b/packages/compiler/test/tsx-sourcemaps/module.ts index 6ed22e0eb..1428fc852 100644 --- a/packages/compiler/test/tsx-sourcemaps/module.ts +++ b/packages/compiler/test/tsx-sourcemaps/module.ts @@ -1,6 +1,6 @@ import { test } from 'uvu'; import * as assert from 'uvu/assert'; -import { testSourcemap } from '../utils'; +import { testTSXSourcemap } from '../utils'; test('script is:inline', async () => { const input = `--- @@ -14,7 +14,7 @@ test('script is:inline', async () => { foo;baz;ComponentAstro;ComponentSvelte;ComponentVue; --- `; - const output = await testSourcemap(input, `'./script'`); + const output = await testTSXSourcemap(input, `'./script'`); assert.equal(output, { line: 8, diff --git a/packages/compiler/test/tsx-sourcemaps/script.ts b/packages/compiler/test/tsx-sourcemaps/script.ts index 9118eb022..9b5e7da82 100644 --- a/packages/compiler/test/tsx-sourcemaps/script.ts +++ b/packages/compiler/test/tsx-sourcemaps/script.ts @@ -1,6 +1,6 @@ import { test } from 'uvu'; import * as assert from 'uvu/assert'; -import { testSourcemap } from '../utils'; +import { testTSXSourcemap } from '../utils'; test('script is:inline', async () => { const input = ` `; - const output = await testSourcemap(input, '\n'); + const output = await testTSXSourcemap(input, '\n'); assert.equal(output, { line: 1, diff --git a/packages/compiler/test/tsx-sourcemaps/tags.ts b/packages/compiler/test/tsx-sourcemaps/tags.ts index 4f04bffb9..b0679546e 100644 --- a/packages/compiler/test/tsx-sourcemaps/tags.ts +++ b/packages/compiler/test/tsx-sourcemaps/tags.ts @@ -1,10 +1,10 @@ import { test } from 'uvu'; import * as assert from 'uvu/assert'; -import { testSourcemap } from '../utils'; +import { testTSXSourcemap } from '../utils'; test('tag close', async () => { const input = ``; - const output = await testSourcemap(input, '>'); + const output = await testTSXSourcemap(input, '>'); assert.equal(output, { line: 1, diff --git a/packages/compiler/test/tsx-sourcemaps/template-windows.ts b/packages/compiler/test/tsx-sourcemaps/template-windows.ts index e79ebb7e8..b74803e53 100644 --- a/packages/compiler/test/tsx-sourcemaps/template-windows.ts +++ b/packages/compiler/test/tsx-sourcemaps/template-windows.ts @@ -1,12 +1,12 @@ import { test } from 'uvu'; import * as assert from 'uvu/assert'; import { convertToTSX } from '@astrojs/compiler'; -import { testSourcemap } from '../utils'; +import { testTSXSourcemap } from '../utils'; test('template expression basic', async () => { const input = `
    {\r\nnonexistent\r\n}
    `; - const output = await testSourcemap(input, 'nonexistent'); + const output = await testTSXSourcemap(input, 'nonexistent'); assert.equal(output, { source: 'index.astro', line: 2, @@ -17,7 +17,7 @@ test('template expression basic', async () => { test('template expression has dot', async () => { const input = `
    {\nconsole.log(hey)\n}
    `; - const output = await testSourcemap(input, 'log'); + const output = await testTSXSourcemap(input, 'log'); assert.equal(output, { source: 'index.astro', line: 2, @@ -28,7 +28,7 @@ test('template expression has dot', async () => { test('template expression has dot', async () => { const input = `
    {\r\nconsole.log(hey)\r\n}
    `; - const output = await testSourcemap(input, 'log'); + const output = await testTSXSourcemap(input, 'log'); assert.equal(output, { source: 'index.astro', line: 2, @@ -39,7 +39,7 @@ test('template expression has dot', async () => { test('template expression with addition', async () => { const input = `{"hello" + \nhey}`; - const output = await testSourcemap(input, 'hey'); + const output = await testTSXSourcemap(input, 'hey'); assert.equal(output, { source: 'index.astro', line: 2, @@ -50,7 +50,7 @@ test('template expression with addition', async () => { test('template expression with addition', async () => { const input = `{"hello" + \r\nhey}`; - const output = await testSourcemap(input, 'hey'); + const output = await testTSXSourcemap(input, 'hey'); assert.equal(output, { source: 'index.astro', line: 2, @@ -61,7 +61,7 @@ test('template expression with addition', async () => { test('html attribute', async () => { const input = ``; - const output = await testSourcemap(input, 'color'); + const output = await testTSXSourcemap(input, 'color'); assert.equal(output, { source: 'index.astro', name: null, @@ -72,7 +72,7 @@ test('html attribute', async () => { test('html attribute', async () => { const input = ``; - const output = await testSourcemap(input, 'color'); + const output = await testTSXSourcemap(input, 'color'); assert.equal(output, { source: 'index.astro', name: null, @@ -83,8 +83,8 @@ test('html attribute', async () => { test('complex template expression', async () => { const input = `{[].map(ITEM => {\r\nv = "what";\r\nreturn
    {ITEMS}
    \r\n})}`; - const item = await testSourcemap(input, 'ITEM'); - const items = await testSourcemap(input, 'ITEMS'); + const item = await testTSXSourcemap(input, 'ITEM'); + const items = await testTSXSourcemap(input, 'ITEMS'); assert.equal(item, { source: 'index.astro', name: null, @@ -101,7 +101,7 @@ test('complex template expression', async () => { test('attributes', async () => { const input = ``; - const className = await testSourcemap(input, 'className'); + const className = await testTSXSourcemap(input, 'className'); assert.equal(className, { source: 'index.astro', name: null, @@ -112,7 +112,7 @@ test('attributes', async () => { test('special attributes', async () => { const input = ``; - const onClick = await testSourcemap(input, '@on.click'); + const onClick = await testTSXSourcemap(input, '@on.click'); assert.equal(onClick, { source: 'index.astro', name: null, @@ -126,7 +126,7 @@ test('whitespace', async () => { const { code } = await convertToTSX(input, { sourcemap: 'both', sourcefile: 'index.astro' }); assert.match(code, '\t', 'output includes \\t'); - const B = await testSourcemap(input, 'B'); + const B = await testTSXSourcemap(input, 'B'); assert.equal(B, { source: 'index.astro', name: null, diff --git a/packages/compiler/test/tsx-sourcemaps/template.ts b/packages/compiler/test/tsx-sourcemaps/template.ts index e3afb5ea5..b3572e0bf 100644 --- a/packages/compiler/test/tsx-sourcemaps/template.ts +++ b/packages/compiler/test/tsx-sourcemaps/template.ts @@ -1,11 +1,11 @@ import { test } from 'uvu'; import * as assert from 'uvu/assert'; -import { testSourcemap } from '../utils'; +import { testTSXSourcemap } from '../utils'; test('template expression basic', async () => { const input = `
    {nonexistent}
    `; - const output = await testSourcemap(input, 'nonexistent'); + const output = await testTSXSourcemap(input, 'nonexistent'); assert.equal(output, { source: 'index.astro', line: 1, @@ -16,7 +16,7 @@ test('template expression basic', async () => { test('template expression has dot', async () => { const input = `
    {console.log(hey)}
    `; - const output = await testSourcemap(input, 'log'); + const output = await testTSXSourcemap(input, 'log'); assert.equal(output, { source: 'index.astro', line: 1, @@ -27,7 +27,7 @@ test('template expression has dot', async () => { test('template expression with addition', async () => { const input = `{"hello" + hey}`; - const output = await testSourcemap(input, 'hey'); + const output = await testTSXSourcemap(input, 'hey'); assert.equal(output, { source: 'index.astro', line: 1, @@ -38,7 +38,7 @@ test('template expression with addition', async () => { test('html attribute', async () => { const input = ``; - const output = await testSourcemap(input, 'color'); + const output = await testTSXSourcemap(input, 'color'); assert.equal(output, { source: 'index.astro', name: null, @@ -52,8 +52,8 @@ test('complex template expression', async () => { v = "what"; return
    {ITEMS}
    })}`; - const item = await testSourcemap(input, 'ITEM'); - const items = await testSourcemap(input, 'ITEMS'); + const item = await testTSXSourcemap(input, 'ITEM'); + const items = await testTSXSourcemap(input, 'ITEMS'); assert.equal(item, { source: 'index.astro', name: null, @@ -70,7 +70,7 @@ return
    {ITEMS}
    test('attributes', async () => { const input = `
    `; - const className = await testSourcemap(input, 'className'); + const className = await testTSXSourcemap(input, 'className'); assert.equal(className, { source: 'index.astro', name: null, @@ -81,7 +81,7 @@ test('attributes', async () => { test('special attributes', async () => { const input = `
    `; - const onClick = await testSourcemap(input, '@on.click'); + const onClick = await testTSXSourcemap(input, '@on.click'); assert.equal(onClick, { source: 'index.astro', name: null, diff --git a/packages/compiler/test/utils.ts b/packages/compiler/test/utils.ts index db3b26ff5..0629b520e 100644 --- a/packages/compiler/test/utils.ts +++ b/packages/compiler/test/utils.ts @@ -1,4 +1,4 @@ -import { convertToTSX } from '@astrojs/compiler'; +import { transform, convertToTSX } from '@astrojs/compiler'; import { generatedPositionFor, originalPositionFor, TraceMap } from '@jridgewell/trace-mapping'; import sass from 'sass'; @@ -43,7 +43,7 @@ export function getPositionFor(input: string, snippet: string) { return null; } -export async function testSourcemap(input: string, snippet: string) { +export async function testTSXSourcemap(input: string, snippet: string) { const snippetLoc = getPositionFor(input, snippet); if (!snippetLoc) throw new Error(`Unable to find "${snippet}"`); @@ -60,3 +60,21 @@ export async function testSourcemap(input: string, snippet: string) { return originalPosition; } + +export async function testJSSourcemap(input: string, snippet: string) { + const snippetLoc = getPositionFor(input, snippet); + if (!snippetLoc) throw new Error(`Unable to find "${snippet}"`); + + const { code, map } = await transform(input, { sourcemap: 'both', sourcefile: 'index.astro', experimentalStaticExtraction: true, resolvePath: (i: string) => i }); + const tracer = new TraceMap(map); + + const generated = generatedPositionFor(tracer, { source: 'index.astro', line: snippetLoc.line, column: snippetLoc.column }); + if (!generated || generated.line === null) { + // eslint-disable-next-line no-console + console.log(code); + throw new Error(`"${snippet}" position incorrectly mapped in generated output.`); + } + const originalPosition = originalPositionFor(tracer, { line: generated.line, column: generated.column }); + + return originalPosition; +}