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
41 changes: 41 additions & 0 deletions Sources/TypeScriptAST/Component/PathPrefixReplacement.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import Foundation

public struct PathPrefixReplacement {
public init(
path: URL,
replacement: String
) {
self.path = path
self.replacement = replacement
}

public var path: URL
public var replacement: String
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

変換元は解決された実際のパスを見ています。
変換先は @src のような特殊な表記を考慮して単なる文字列です。


public func replace(path: URL) -> URL? {
let base = self.path.absoluteURL.standardized.pathComponents
let full = path.absoluteURL.standardized.pathComponents

guard full.starts(with: base) else {
return nil
}

let delta = full[base.count...]

return URL(fileURLWithPath: replacement)
.appendingPathComponent(delta.joined(separator: "/"))
}
}

public typealias PathPrefixReplacements = [PathPrefixReplacement]

extension PathPrefixReplacements {
public func replace(path: URL) -> URL? {
for x in self {
if let new = x.replace(path: path) {
return new
}
}
return nil
}
}
9 changes: 9 additions & 0 deletions Sources/TypeScriptAST/Dependency/AutoImport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ extension TSSourceFile {
from: URL,
symbolTable: SymbolTable,
fileExtension: ImportFileExtension,
pathPrefixReplacements: PathPrefixReplacements = [],
defaultFile: String? = nil
) throws -> [TSImportDecl] {
var fileToSymbols = FileToSymbols()
Expand Down Expand Up @@ -65,6 +66,14 @@ extension TSSourceFile {
let symbols = Set(fileToSymbols.symbols(for: file)).sorted()
if symbols.isEmpty { continue }

var file = file

if let newPath = pathPrefixReplacements.replace(
path: URL(fileURLWithPath: file, relativeTo: from)
) {
file = newPath.relativePath
}

imports.append(
TSImportDecl(names: symbols, from: file)
)
Expand Down
65 changes: 65 additions & 0 deletions Tests/TypeScriptASTTests/AutoImportTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,71 @@ final class AutoImportTests: TestCaseBase {
)
}

func testAlias() throws {
let s = TSSourceFile([
TSTypeDecl(modifiers: [.export], name: "S", type: TSObjectType([]))
])

let m = TSSourceFile([
TSTypeDecl(modifiers: [.export], name: "M", type: TSIdentType("S"))
])

var symbols = SymbolTable()
symbols.add(source: s, file: URL(fileURLWithPath: "lib/foo/s.ts"))

let imports = try m.buildAutoImportDecls(
from: URL(fileURLWithPath: "m/m.ts"),
symbolTable: symbols,
fileExtension: .none,
pathPrefixReplacements: [
.init(path: URL(fileURLWithPath: "lib/foo"), replacement: "@foo")
]
)
m.replaceImportDecls(imports)

assertPrint(
m, """
import { S } from "@foo/s";

export type M = S;

"""
)
}

func testUnrelatedAlias() throws {
let s = TSSourceFile([
TSTypeDecl(modifiers: [.export], name: "S", type: TSObjectType([]))
])

let m = TSSourceFile([
TSTypeDecl(modifiers: [.export], name: "M", type: TSIdentType("S"))
])

var symbols = SymbolTable()
symbols.add(source: s, file: URL(fileURLWithPath: "s/s.ts"))

let imports = try m.buildAutoImportDecls(
from: URL(fileURLWithPath: "m/m.ts"),
symbolTable: symbols,
fileExtension: .none,
pathPrefixReplacements: [
.init(path: URL(fileURLWithPath: "lib/foo"), replacement: "@foo")
]
)
m.replaceImportDecls(imports)

assertPrint(
m, """
import { S } from "../s/s";

export type M = S;

"""
)
}


func testDefaultImport() throws {
let s = TSSourceFile([
TSVarDecl(
Expand Down
30 changes: 30 additions & 0 deletions Tests/TypeScriptASTTests/UtilsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,34 @@ final class UtilsTests: XCTestCase {
XCTAssertNil(u.baseURL)
}
}

func testPathPrefixReplace() throws {
let args: [(from: String, to: String, input: String, expect: String?, line: UInt)] = [
// full to full
("/usr", "/foo", "/usr/lib", "/foo/lib", #line),
// full to rel
("/usr", "foo", "/usr/lib", "foo/lib", #line),
("/usr", "../foo", "/usr/lib", "../foo/lib", #line),
// rel to full
("usr", "/foo", "usr/lib", "/foo/lib", #line),
("../usr", "/foo", "../usr/lib", "/foo/lib", #line),
// rel to rel
("usr", "foo", "usr/lib", "foo/lib", #line),
("../usr", "foo", "../usr/lib", "foo/lib", #line),
// slash
("/usr", "/foo", "/usr2/lib", nil, #line),
]

for arg in args {
let replacement = PathPrefixReplacement(
path: URL(fileURLWithPath: arg.from), replacement: arg.to
)
let input = URL(fileURLWithPath: arg.input)
let expect = arg.expect.map { URL(fileURLWithPath: $0) }

let actual = replacement.replace(path: input)

XCTAssertEqual(actual, expect, file: #file, line: arg.line)
}
}
}