Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions tsc/internal/contentmapper/contentmapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,12 @@ type Mapper struct {
ContributionID string `json:"-"`
}

var supportedVirtualExtensions = collections.NewSetFromItems(
".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx", ".mts", ".cts", ".json",
)

func IsSupportedVirtualExtension(extension string) bool {
return supportedVirtualExtensions.Has(extension)
switch extension {
case ".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx", ".mts", ".cts", ".json":
return true
}
return false
}

func IsValidExtension(extension string) bool {
Expand Down
35 changes: 19 additions & 16 deletions tsc/internal/execute/build/buildtask.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package build

import (
"fmt"
"iter"
"slices"
"strconv"
"strings"
Expand Down Expand Up @@ -491,7 +490,7 @@ func (t *BuildTask) getUpToDateStatus(orchestrator *Orchestrator, configPath tsp
if seenRoots.Has(inputPath) || resolvedRoots.Has(inputPath) {
continue
}
if isContentMapperSupplementalBuildInfoPath(inputPath, getBuildInfoRootInfoReader().Roots()) &&
if isContentMapperVirtualFilePath(inputPath, getBuildInfoRootInfoReader()) &&
!orchestrator.host.FS().FileExists(inputFileName) {
continue
}
Expand Down Expand Up @@ -620,21 +619,25 @@ func (t *BuildTask) getUpToDateStatus(orchestrator *Orchestrator, configPath tsp
}
}

func isContentMapperSupplementalBuildInfoPath(inputPath tspath.PathKey, roots iter.Seq[tspath.PathKey]) bool {
for root := range roots {
suffix, ok := strings.CutPrefix(string(inputPath), string(root)+".")
if !ok {
continue
}
index, extension, ok := strings.Cut(suffix, ".")
if !ok || extension == "" {
continue
}
if _, err := strconv.Atoi(index); err == nil && contentmapper.IsSupportedVirtualExtension("."+extension) {
return true
}
func isContentMapperVirtualFilePath(inputPath tspath.PathKey, roots *incremental.BuildInfoRootInfoReader) bool {
extension := inputPath.Extension()
if !contentmapper.IsSupportedVirtualExtension(extension) {
return false
}
return false

pathWithoutExtension := tspath.RemoveExtension(inputPath.AsString(), extension)
rootName, index, ok := strings.CutLast(pathWithoutExtension, ".")
if !ok {
return false
}

rootPath, ok := tspath.TryPathKeyFromCanonical(rootName)
if !ok || !roots.HasRoot(rootPath) {
return false
}

_, err := strconv.Atoi(index)
return err == nil
}

func (t *BuildTask) reportUpToDateStatus(orchestrator *Orchestrator) {
Expand Down
94 changes: 86 additions & 8 deletions tsc/internal/execute/build/buildtask_contentmapper_test.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,98 @@
package build

import (
"slices"
"strconv"
"testing"

"github.com/microsoft/TypeScript/tsc/internal/execute/incremental"
"github.com/microsoft/TypeScript/tsc/internal/tspath"
"gotest.tools/v3/assert"
)

func TestIsContentMapperSupplementalBuildInfoPath(t *testing.T) {
func TestContentMapperVirtualFilePaths(t *testing.T) {
t.Parallel()
roots := []tspath.PathKey{"/src/app.vue", "/src/index.ts"}
buildInfo := &incremental.BuildInfo{
Root: []*incremental.BuildInfoRoot{
{NonIncremental: "/src/a.vue"},
{NonIncremental: "/src/b.ts"},
{NonIncremental: "/src/a.vue.0"},
},
}
roots := buildInfo.GetBuildInfoRootInfoReader("/", tspath.CaseSensitive)

assert.Assert(t, isContentMapperSupplementalBuildInfoPath("/src/app.vue.0.ts", slices.Values(roots)))
assert.Assert(t, isContentMapperSupplementalBuildInfoPath("/src/app.vue.12.mts", slices.Values(roots)))
assert.Assert(t, !isContentMapperSupplementalBuildInfoPath("/src/app.vue.ts", slices.Values(roots)))
assert.Assert(t, !isContentMapperSupplementalBuildInfoPath("/src/app.vue.0.txt", slices.Values(roots)))
assert.Assert(t, !isContentMapperSupplementalBuildInfoPath("/src/other.vue.0.ts", slices.Values(roots)))
assert.Assert(t, isContentMapperVirtualFilePath("/src/a.vue.0.ts", roots))
assert.Assert(t, isContentMapperVirtualFilePath("/src/a.vue.1.tsx", roots))
assert.Assert(t, isContentMapperVirtualFilePath("/src/a.vue.12.mts", roots))
assert.Assert(t, isContentMapperVirtualFilePath("/src/a.vue.2.cts", roots))
assert.Assert(t, isContentMapperVirtualFilePath("/src/a.vue.0.js", roots))
assert.Assert(t, isContentMapperVirtualFilePath("/src/a.vue.0.jsx", roots))
assert.Assert(t, isContentMapperVirtualFilePath("/src/a.vue.0.mjs", roots))
assert.Assert(t, isContentMapperVirtualFilePath("/src/a.vue.0.cjs", roots))
assert.Assert(t, isContentMapperVirtualFilePath("/src/a.vue.0.json", roots))
assert.Assert(t, isContentMapperVirtualFilePath("/src/b.ts.0.ts", roots))
assert.Assert(t, isContentMapperVirtualFilePath("/src/a.vue.0.1.ts", roots))
assert.Assert(t, isContentMapperVirtualFilePath("/src/a.vue.+1.ts", roots))
assert.Assert(t, isContentMapperVirtualFilePath("/src/a.vue.-1.ts", roots))
assert.Assert(t, isContentMapperVirtualFilePath("/src/a.vue.001.ts", roots))
assert.Assert(t, !isContentMapperVirtualFilePath("/src/a.vue.ts", roots))
assert.Assert(t, !isContentMapperVirtualFilePath("/src/a.vue.0.txt", roots))
assert.Assert(t, !isContentMapperVirtualFilePath("/src/a.vue.0.d.ts", roots))
assert.Assert(t, !isContentMapperVirtualFilePath("/src/a.vue.0.d.mts", roots))
assert.Assert(t, !isContentMapperVirtualFilePath("/src/a.vue.0.d.cts", roots))
assert.Assert(t, !isContentMapperVirtualFilePath("/src/a.vue.0", roots))
assert.Assert(t, !isContentMapperVirtualFilePath("/src/a.vue..ts", roots))
assert.Assert(t, !isContentMapperVirtualFilePath("/src/a.vue.x.ts", roots))
assert.Assert(t, !isContentMapperVirtualFilePath("/src/a.vue.99999999999999999999.ts", roots))
assert.Assert(t, !isContentMapperVirtualFilePath("/src/a.vue.1.0.ts", roots))
assert.Assert(t, !isContentMapperVirtualFilePath("/src/b.vue.0.ts", roots))
assert.Assert(t, !isContentMapperVirtualFilePath("/src/a.vuex.0.ts", roots))
assert.Assert(t, !isContentMapperVirtualFilePath("/src/a.ts", roots))
assert.Assert(t, !isContentMapperVirtualFilePath("/src/.0.ts", roots))
assert.Assert(t, !isContentMapperVirtualFilePath("/src/..0.ts", roots))
}

func TestContentMapperVirtualFilePathsUseOriginalRoots(t *testing.T) {
t.Parallel()
buildInfo := &incremental.BuildInfo{
FileNames: []incremental.BuildInfoPath{"./src/a.vue", "./src/b.vue"},
Root: []*incremental.BuildInfoRoot{{Start: 2}},
ResolvedRoot: []*incremental.BuildInfoResolvedRoot{{Resolved: 2, Root: 1}},
}
roots := buildInfo.GetBuildInfoRootInfoReader("/", tspath.CaseSensitive)

assert.Assert(t, isContentMapperVirtualFilePath("/src/a.vue.0.ts", roots))
assert.Assert(t, !isContentMapperVirtualFilePath("/src/b.vue.0.ts", roots))
}

func BenchmarkContentMapperVirtualFilePaths(b *testing.B) {
for _, count := range []int{100, 1000, 10000} {
buildInfo := &incremental.BuildInfo{}
for i := range count {
buildInfo.Root = append(buildInfo.Root, &incremental.BuildInfoRoot{
NonIncremental: incremental.BuildInfoPath("/project/packages/a/src/components/a" + strconv.Itoa(i) + ".vue"),
})
}
roots := buildInfo.GetBuildInfoRootInfoReader("/", tspath.CaseSensitive)
b.Run("roots="+strconv.Itoa(count), func(b *testing.B) {
b.Run("declarationFile", func(b *testing.B) {
b.ReportAllocs()
for b.Loop() {
isContentMapperVirtualFilePath("/project/node_modules/a/b.d.ts", roots)
}
})
b.Run("sourceFile", func(b *testing.B) {
b.ReportAllocs()
for b.Loop() {
isContentMapperVirtualFilePath("/project/node_modules/a/b.js", roots)
}
})
b.Run("virtualFile", func(b *testing.B) {
path := tspath.PathKeyFromCanonical(buildInfo.Root[count-1].NonIncremental.AsString() + ".0.ts")
b.ReportAllocs()
for b.Loop() {
isContentMapperVirtualFilePath(path, roots)
}
})
})
}
}
4 changes: 4 additions & 0 deletions tsc/internal/execute/incremental/buildInfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,10 @@ func (b *BuildInfoRootInfoReader) Roots() iter.Seq[tspath.PathKey] {
return b.rootToResolved.Keys()
}

func (b *BuildInfoRootInfoReader) HasRoot(path tspath.PathKey) bool {
return b.rootToResolved.Has(path)
}

func (b *BuildInfoRootInfoReader) RootFileName(path tspath.PathKey) tspath.RootedFilePath {
fileName, ok := b.rootFileNames[path]
if !ok {
Expand Down
Loading