Merge remote-tracking branch 'origin/master' into scaffold

This commit is contained in:
Marek Fořt 2020-02-11 22:33:49 +01:00
commit ad2ed05563
78 changed files with 1115 additions and 633 deletions

View File

@ -1,7 +1,7 @@
# https://help.github.com/en/github/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions#jobsjob_idname
name: Checks
on: [push, pull_request]
on: [push]
jobs:
swiftformat:

33
.github/workflows/fixture-generator.yml vendored Normal file
View File

@ -0,0 +1,33 @@
name: Fixture Generator
on:
push:
paths:
- fixtures/fixture_generator/**/*
- Sources/ProjectDescription/**/*
- .github/workflows/fixture-generator.yml
pull_request:
paths:
- fixtures/fixture_generator/**/*
- Sources/ProjectDescription/**/*
- .github/workflows/fixture-generator.yml
jobs:
test:
name: Test
runs-on: macOS-latest
steps:
- uses: actions/checkout@v1
- name: Select Xcode 11.2.1
run: sudo xcode-select -switch /Applications/Xcode_11.2.1.app
- name: Build Package
working-directory: ./fixtures/fixture_generator
run: swift build
- name: Generate Fixture
working-directory: ./fixtures/fixture_generator
run: swift run FixtureGenerator --projects 1 --targets 1 --sources 1
- name: Build Tuist
run: swift build
- name: Generate Fixture Project
run: swift run tuist generate --path ./fixtures/fixture_generator/Fixture

View File

@ -1,6 +1,6 @@
name: Tuist
on: ['push', 'pull_request']
on: ['push']
jobs:
unit_tests:

View File

@ -1,6 +1,6 @@
name: Website
on: ['push', 'pull_request']
on: ['push']
jobs:
build:

View File

@ -1 +1 @@
2.6.3
2.6.5

View File

@ -6,8 +6,20 @@ Please, check out guidelines: https://keepachangelog.com/en/1.0.0/
### Added
### Fixed
### Changed
## 1.2.0
### Added
- Best practices page to the documentation https://github.com/tuist/tuist/pull/843 by @pepibumur.
- Fail CI if there are broken links on the website https://github.com/tuist/tuist/pull/917 by @pepibumur.
- Excluding multiple files from a target https://github.com/tuist/tuist/pull/937 by @paciej00
- Better SEO to the website https://github.com/tuist/tuist/pull/945 by @pepibumur.
- Add fixture generator for stress testing Tuist https://github.com/tuist/tuist/pull/890 by @kwridan.
### Fixed
@ -18,6 +30,7 @@ Please, check out guidelines: https://keepachangelog.com/en/1.0.0/
- Fix project header attributes https://github.com/tuist/tuist/pull/895 by @kwridan
- Excluding files from target doesn't work in all cases https://github.com/tuist/tuist/pull/913 by @vytis
- Support for Core Data mapping modules https://github.com/tuist/tuist/pull/911 by @andreacipriani
- Deep nested hierarchy in the project generated by `tuist edit` https://github.com/tuist/tuist/pull/923 by @pepibumur
### Changed
- Turn models from `TuistCore` that are clases into structs https://github.com/tuist/tuist/pull/870 by @pepibumur.

View File

@ -16,7 +16,7 @@ GEM
atomos (0.1.3)
backports (3.11.4)
builder (3.2.3)
byebug (11.1.0)
byebug (11.1.1)
claide (1.0.3)
cocoapods (1.8.4)
activesupport (>= 4.0.2, < 5)

View File

@ -28,15 +28,6 @@
"version": "1.0.2"
}
},
{
"package": "OpenCombine",
"repositoryURL": "https://github.com/broadwaylamb/OpenCombine.git",
"state": {
"branch": null,
"revision": "1496bab2724bf6f794cba8ac4d97d2a5013eeba7",
"version": "0.8.0"
}
},
{
"package": "PathKit",
"repositoryURL": "https://github.com/kylef/PathKit",

View File

@ -32,7 +32,6 @@ let package = Package(
.package(url: "https://github.com/IBM-Swift/BlueSignals", .upToNextMajor(from: "1.0.21")),
.package(url: "https://github.com/ReactiveX/RxSwift.git", .upToNextMajor(from: "5.0.1")),
.package(url: "https://github.com/rnine/Checksum.git", .upToNextMajor(from: "1.0.2")),
.package(url: "https://github.com/broadwaylamb/OpenCombine.git", .upToNextMajor(from: "0.8.0")),
],
targets: [
.target(
@ -53,7 +52,7 @@ let package = Package(
),
.target(
name: "TuistKit",
dependencies: ["XcodeProj", "SPMUtility", "TuistSupport", "TuistGenerator", "TuistGalaxy", "TuistAutomation", "ProjectDescription", "Signals", "RxSwift", "Checksum", "TuistLoader"]
dependencies: ["XcodeProj", "SPMUtility", "TuistSupport", "TuistGenerator", "TuistCache", "TuistAutomation", "ProjectDescription", "Signals", "RxSwift", "Checksum", "TuistLoader"]
),
.testTarget(
name: "TuistKitTests",
@ -69,7 +68,7 @@ let package = Package(
),
.target(
name: "TuistEnvKit",
dependencies: ["SPMUtility", "TuistSupport", "OpenCombine", "OpenCombineFoundation"]
dependencies: ["SPMUtility", "TuistSupport"]
),
.testTarget(
name: "TuistEnvKitTests",
@ -89,7 +88,7 @@ let package = Package(
),
.target(
name: "TuistSupport",
dependencies: ["SPMUtility", "RxSwift", "RxRelay", "OpenCombine", "OpenCombineDispatch"]
dependencies: ["SPMUtility", "RxSwift", "RxRelay"]
),
.target(
name: "TuistSupportTesting",
@ -116,16 +115,16 @@ let package = Package(
dependencies: ["TuistGenerator", "TuistSupportTesting"]
),
.target(
name: "TuistGalaxy",
name: "TuistCache",
dependencies: ["XcodeProj", "SPMUtility", "TuistCore", "TuistSupport"]
),
.testTarget(
name: "TuistGalaxyTests",
dependencies: ["TuistGalaxy", "TuistSupportTesting", "TuistCoreTesting", "RxBlocking"]
name: "TuistCacheTests",
dependencies: ["TuistCache", "TuistSupportTesting", "TuistCoreTesting", "RxBlocking"]
),
.testTarget(
name: "TuistGalaxyIntegrationTests",
dependencies: ["TuistGalaxy", "TuistSupportTesting", "RxBlocking"]
name: "TuistCacheIntegrationTests",
dependencies: ["TuistCache", "TuistSupportTesting", "RxBlocking"]
),
.target(
name: "TuistAutomation",

View File

@ -1,15 +0,0 @@
import Foundation
// MARK: - Galaxy
public struct Galaxy: Codable, Equatable {
/// The project token to authenticate with the API.
public let token: String
/// Initializes the Galaxy instance with its attributes.
/// - Parameter token: The project token to authenticate with the API.
public init(token: String) {
self.token = token
dumpIfNeeded(self)
}
}

View File

@ -5,8 +5,8 @@ public struct SourceFileGlob: ExpressibleByStringLiteral, Codable, Equatable {
/// Relative glob pattern.
public let glob: Path
/// Relative glob pattern for excluded files
public let excluding: Path?
/// Relative glob patterns for excluded files.
public let excluding: [Path]
/// Compiler flags.
public let compilerFlags: String?
@ -15,13 +15,19 @@ public struct SourceFileGlob: ExpressibleByStringLiteral, Codable, Equatable {
///
/// - Parameters:
/// - glob: Relative glob pattern.
/// - excluding: Relative glob patterns for excluded files.
/// - compilerFlags: Compiler flags.
public init(_ glob: Path, excluding: Path? = nil, compilerFlags: String? = nil) {
public init(_ glob: Path, excluding: [Path] = [], compilerFlags: String? = nil) {
self.glob = glob
self.excluding = excluding
self.compilerFlags = compilerFlags
}
public init(_ glob: Path, excluding: Path?, compilerFlags: String? = nil) {
let paths: [Path] = excluding.flatMap { [$0] } ?? []
self.init(glob, excluding: paths, compilerFlags: compilerFlags)
}
public init(stringLiteral value: String) {
self.init(Path(value))
}

View File

@ -1 +1 @@
// TuistAutomation placeholder

View File

@ -466,7 +466,9 @@ public class Graph: Graphing {
return references
}
public func findAll<T: GraphNode, S: GraphNode>(targetNode: TargetNode, test: (T) -> Bool = { _ in true }, skip: (S) -> Bool = { _ in false }) -> Set<T> {
public func findAll<T: GraphNode, S: GraphNode>(targetNode: TargetNode,
test: (T) -> Bool = { _ in true },
skip: (S) -> Bool = { _ in false }) -> Set<T> {
var stack = Stack<GraphNode>()
stack.push(targetNode)

View File

@ -4,7 +4,7 @@ import TuistSupport
public struct Target: Equatable, Hashable {
public typealias SourceFile = (path: AbsolutePath, compilerFlags: String?)
public typealias SourceFileGlob = (glob: String, excluding: String?, compilerFlags: String?)
public typealias SourceFileGlob = (glob: String, excluding: [String], compilerFlags: String?)
// MARK: - Static
@ -115,10 +115,12 @@ public struct Target: Equatable, Hashable {
let base = AbsolutePath(sourcePath.dirname)
// Paths that should be excluded from sources
let excluded = source.excluding
.map { AbsolutePath($0) }
.map { excludePath in AbsolutePath(excludePath.dirname).glob(excludePath.basename) }
?? []
var excluded: [AbsolutePath] = []
source.excluding.forEach { path in
let absolute = AbsolutePath(path)
let globs = AbsolutePath(absolute.dirname).glob(absolute.basename)
excluded.append(contentsOf: globs)
}
Set(base.glob(sourcePath.basename))
.subtracting(excluded)

View File

@ -1,6 +1,4 @@
import Foundation
import OpenCombine
import OpenCombineFoundation
protocol URLSessionScheduling: AnyObject {
/// Schedules an URLSession request and returns the result synchronously.
@ -8,10 +6,6 @@ protocol URLSessionScheduling: AnyObject {
/// - Parameter request: request to be executed.
/// - Returns: request's response.
func schedule(request: URLRequest) -> (error: Error?, data: Data?)
/// Returns a publisher that sends the given request. The publisher forwards the response data and error to the subscribers.
/// - Parameter request: The request to be sent.
func publisher(request: URLRequest) -> OpenCombine.AnyPublisher<(data: Data, response: URLResponse), URLError>
}
final class URLSessionScheduler: URLSessionScheduling {
@ -50,8 +44,4 @@ final class URLSessionScheduler: URLSessionScheduling {
_ = semaphore.wait(timeout: .now() + 3)
return (error: error, data: data)
}
func publisher(request: URLRequest) -> OpenCombine.AnyPublisher<(data: Data, response: URLResponse), URLError> {
URLSession.OCombine.DataTaskPublisher(request: request, session: session).eraseToAnyPublisher()
}
}

View File

@ -76,11 +76,11 @@ final class ConfigGenerator: ConfigGenerating {
let buildConfigurations = Set(projectBuildConfigurations).union(targetBuildConfigurations)
let configurationsTuples: [(BuildConfiguration, Configuration?)] = buildConfigurations
.map { buildConfiguration in
if let configuration = target.settings?.configurations[buildConfiguration] {
return (buildConfiguration, configuration)
if let configuration = target.settings?.configurations[buildConfiguration] {
return (buildConfiguration, configuration)
}
return (buildConfiguration, nil)
}
return (buildConfiguration, nil)
}
let configurations = Dictionary(uniqueKeysWithValues: configurationsTuples)
let nonEmptyConfigurations = !configurations.isEmpty ? configurations : Settings.default.configurations
let orderedConfigurations = nonEmptyConfigurations.sortedByBuildConfigurationName()

View File

@ -18,7 +18,9 @@ public protocol Generating {
/// - Parameters:
/// - project: The project to be generated.
/// - graph: The dependencies graph.
func generateProject(_ project: Project, graph: Graphing) throws -> AbsolutePath
/// - sourceRootPath: The path all the files in the Xcode project will be realtived to. When it's nil, it's assumed that all the paths are relative to the directory that contains the manifest.
/// - xcodeprojPath: Path where the .xcodeproj directory will be generated. When the attribute is nil, the project is generated in the manifest's directory.
func generateProject(_ project: Project, graph: Graphing, sourceRootPath: AbsolutePath?, xcodeprojPath: AbsolutePath?) throws -> AbsolutePath
/// Generate an Xcode workspace for the project at a given path. All the project's dependencies will also be generated and included.
///
@ -97,10 +99,18 @@ public class Generator: Generating {
self.environmentLinter = environmentLinter
}
public func generateProject(_ project: Project, graph: Graphing) throws -> AbsolutePath {
public func generateProject(_ project: Project,
graph: Graphing,
sourceRootPath: AbsolutePath? = nil,
xcodeprojPath: AbsolutePath? = nil) throws -> AbsolutePath {
/// When the source root path is not given, we assume paths
/// are relative to the directory that contains the manifest.
let sourceRootPath = sourceRootPath ?? project.path
let generatedProject = try projectGenerator.generate(project: project,
graph: graph,
sourceRootPath: project.path)
sourceRootPath: sourceRootPath,
xcodeprojPath: xcodeprojPath)
return generatedProject.path
}
@ -113,7 +123,8 @@ public class Generator: Generating {
let generatedProject = try projectGenerator.generate(project: project,
graph: graph,
sourceRootPath: path)
sourceRootPath: path,
xcodeprojPath: nil)
return generatedProject.path
}

View File

@ -23,9 +23,16 @@ extension ProjectConstants {
}
protocol ProjectGenerating: AnyObject {
/// Generates the given project.
/// - Parameters:
/// - project: Project to be generated.
/// - graph: Dependencies graph.
/// - sourceRootPath: Directory where the files are relative to.
/// - xcodeprojPath: Path to the Xcode project. When not given, the xcodeproj is generated at sourceRootPath.
func generate(project: Project,
graph: Graphing,
sourceRootPath: AbsolutePath?) throws -> GeneratedProject
sourceRootPath: AbsolutePath?,
xcodeprojPath: AbsolutePath?) throws -> GeneratedProject
}
final class ProjectGenerator: ProjectGenerating {
@ -66,13 +73,15 @@ final class ProjectGenerator: ProjectGenerating {
func generate(project: Project,
graph: Graphing,
sourceRootPath: AbsolutePath? = nil) throws -> GeneratedProject {
sourceRootPath: AbsolutePath? = nil,
xcodeprojPath: AbsolutePath? = nil) throws -> GeneratedProject {
Printer.shared.print("Generating project \(project.name)")
// Getting the path.
let sourceRootPath = sourceRootPath ?? project.path
let xcodeprojPath = sourceRootPath.appending(component: "\(project.fileName).xcodeproj")
// If the xcodeproj path is not given, we generate it under the source root path.
let xcodeprojPath = xcodeprojPath ?? sourceRootPath.appending(component: "\(project.fileName).xcodeproj")
// Project and workspace.
return try generateProjectAndWorkspace(project: project,
@ -96,7 +105,7 @@ final class ProjectGenerator: ProjectGenerating {
let pbxproj = PBXProj(objectVersion: projectConstants.objectVersion,
archiveVersion: projectConstants.archiveVersion,
classes: [:])
let groups = ProjectGroups.generate(project: project, pbxproj: pbxproj, sourceRootPath: sourceRootPath)
let groups = ProjectGroups.generate(project: project, pbxproj: pbxproj, xcodeprojPath: xcodeprojPath, sourceRootPath: sourceRootPath)
let fileElements = ProjectFileElements()
try fileElements.generateProjectFiles(project: project,
graph: graph,

View File

@ -66,10 +66,11 @@ class ProjectGroups {
static func generate(project: Project,
pbxproj: PBXProj,
xcodeprojPath: AbsolutePath,
sourceRootPath: AbsolutePath,
playgrounds: Playgrounding = Playgrounds()) -> ProjectGroups {
/// Main
let projectRelativePath = project.path.relative(to: sourceRootPath).pathString
let projectRelativePath = sourceRootPath.relative(to: xcodeprojPath.parentDirectory).pathString
let mainGroup = PBXGroup(children: [],
sourceTree: .group,
path: (projectRelativePath != ".") ? projectRelativePath : nil)

View File

@ -96,7 +96,8 @@ final class WorkspaceGenerator: WorkspaceGenerating {
try graph.projects.forEach { project in
let generatedProject = try projectGenerator.generate(project: project,
graph: graph,
sourceRootPath: project.path)
sourceRootPath: project.path,
xcodeprojPath: nil)
generatedProjects[project.path] = generatedProject
}

View File

@ -59,7 +59,9 @@ final class ProjectEditor: ProjectEditing {
self.helpersDirectoryLocator = helpersDirectoryLocator
}
func edit(at: AbsolutePath, in destinationDirectory: AbsolutePath) throws -> AbsolutePath {
func edit(at: AbsolutePath, in dstDirectory: AbsolutePath) throws -> AbsolutePath {
let xcodeprojPath = dstDirectory.appending(component: "Manifests.xcodeproj")
let projectDesciptionPath = try resourceLocator.projectDescription()
let manifests = manifestFilesLocator.locate(at: at)
var helpers: [AbsolutePath] = []
@ -72,10 +74,13 @@ final class ProjectEditor: ProjectEditing {
throw ProjectEditorError.noEditableFiles(at)
}
let (project, graph) = projectEditorMapper.map(sourceRootPath: destinationDirectory,
let (project, graph) = projectEditorMapper.map(sourceRootPath: at,
manifests: manifests.map { $0.1 },
helpers: helpers,
projectDescriptionPath: projectDesciptionPath)
return try generator.generateProject(project, graph: graph)
return try generator.generateProject(project,
graph: graph,
sourceRootPath: project.path,
xcodeprojPath: xcodeprojPath)
}
}

View File

@ -292,10 +292,11 @@ extension TuistCore.Target {
let entitlements = try manifest.entitlements.map { try generatorPaths.resolve(path: $0) }
let settings = try manifest.settings.map { try TuistCore.Settings.from(manifest: $0, path: path, generatorPaths: generatorPaths) }
let sources = try TuistCore.Target.sources(projectPath: path, sources: manifest.sources?.globs.map {
let glob = try generatorPaths.resolve(path: $0.glob).pathString
let excluding = try $0.excluding.map { try generatorPaths.resolve(path: $0).pathString }
return (glob: glob, excluding: excluding, compilerFlags: $0.compilerFlags)
let sources = try TuistCore.Target.sources(projectPath: path,
sources: manifest.sources?.globs.map { (glob: ProjectDescription.SourceFileGlob) in
let globPath = try generatorPaths.resolve(path: glob.glob).pathString
let excluding: [String] = try glob.excluding.compactMap { try generatorPaths.resolve(path: $0).pathString }
return (glob: globPath, excluding: excluding, compilerFlags: glob.compilerFlags)
} ?? [])
let resourceFilter = { (path: AbsolutePath) -> Bool in

View File

@ -58,13 +58,6 @@ public protocol ManifestLoading {
/// - Throws: An error if the file has a syntax error.
func loadTuistConfig(at path: AbsolutePath) throws -> ProjectDescription.TuistConfig
/// Loads the Galaxy.swift in the given directory.
///
/// - Parameter path: Path to the directory that contains the Galaxy.swift file.
/// - Returns: Loaded Galaxy.swift file.
/// - Throws: An error if the file has a syntax error.
func loadGalaxy(at path: AbsolutePath) throws -> ProjectDescription.Galaxy
/// Loads the Project.swift in the given directory.
/// - Parameter path: Path to the directory that contains the Project.swift.
func loadProject(at path: AbsolutePath) throws -> ProjectDescription.Project
@ -115,10 +108,6 @@ public class ManifestLoader: ManifestLoading {
try loadManifest(.tuistConfig, at: path)
}
public func loadGalaxy(at path: AbsolutePath) throws -> ProjectDescription.Galaxy {
try loadManifest(.galaxy, at: path)
}
public func loadProject(at path: AbsolutePath) throws -> ProjectDescription.Project {
try loadManifest(.project, at: path)
}

View File

@ -22,14 +22,6 @@ final class MockManifestLoader: ManifestLoading {
var loadTuistConfigCount: UInt = 0
var loadTuistConfigStub: ((AbsolutePath) throws -> ProjectDescription.TuistConfig)?
var loadGalaxyCount: UInt = 0
var loadGalaxyStub: ((AbsolutePath) throws -> ProjectDescription.Galaxy)?
func loadGalaxy(at path: AbsolutePath) throws -> ProjectDescription.Galaxy {
loadGalaxyCount += 1
return try loadGalaxyStub?(path) ?? ProjectDescription.Galaxy.test()
}
func loadProject(at path: AbsolutePath) throws -> ProjectDescription.Project {
try loadProjectStub?(path) ?? ProjectDescription.Project.test()
}

View File

@ -17,12 +17,6 @@ extension Workspace {
}
}
extension Galaxy {
public static func test(token: String = "xyz") -> Galaxy {
Galaxy(token: token)
}
}
extension Project {
public static func test(name: String = "Project",
settings: Settings? = nil,

View File

@ -1,29 +0,0 @@
import Foundation
import OpenCombine
public extension OpenCombine.Publisher {
/// Waits for the publisher to complete and then returns the values or the error.
/// This method blocks the thread from which it's called from so it should be used cautiously.
func wait() throws -> [Output] {
var output: [Self.Output] = []
var error: Self.Failure?
let semaphore = DispatchSemaphore(value: 0)
_ = handleEvents(receiveOutput: { receivedOutput in
output.append(receivedOutput)
}, receiveCompletion: { completion in
if case let .failure(receivedError) = completion {
error = receivedError
}
semaphore.signal()
}, receiveCancel: {
semaphore.signal()
})
semaphore.wait()
if let error = error {
throw error
}
return output
}
}

View File

@ -5,7 +5,7 @@ public struct Constants {
public static let binFolderName = ".tuist-bin"
public static let binName = "tuist"
public static let gitRepositoryURL = "https://github.com/tuist/tuist.git"
public static let version = "1.1.0"
public static let version = "1.2.0"
public static let swiftVersion: String = "5.1"
public static let bundleName: String = "tuist.zip"
public static let trueValues: [String] = ["1", "true", "TRUE", "yes", "YES"]

View File

@ -0,0 +1,34 @@
import Foundation
public protocol CIChecking: AnyObject {
/// Returns true when the environment in which the tuist process is running is a CI environment.
func isCI() -> Bool
}
public final class CIChecker: CIChecking {
static let variables = [
// GitHub: https://help.github.com/en/actions/automating-your-workflow-with-github-actions/using-environment-variables
"GITHUB_RUN_ID",
// CircleCI: https://circleci.com/docs/2.0/env-vars/
// Bitrise: https://devcenter.bitrise.io/builds/available-environment-variables/
// Buildkite: https://buildkite.com/docs/pipelines/environment-variables
// Travis: https://docs.travis-ci.com/user/environment-variables/
"CI",
// Jenkins: https://wiki.jenkins.io/display/JENKINS/Building+a+software+project
"BUILD_NUMBER",
]
// MARK: - CIChecking
public func isCI() -> Bool {
isCI(environment: ProcessInfo.processInfo.environment)
}
// MARK: - Internal
func isCI(environment: [String: String]) -> Bool {
environment.first(where: {
CIChecker.variables.contains($0.key) && Constants.trueValues.contains($0.value)
}) != nil
}
}

View File

@ -0,0 +1,10 @@
import Foundation
import TuistSupport
public final class MockCIChecker: CIChecking {
var isCIStub: Bool = false
public func isCI() -> Bool {
isCIStub
}
}

View File

@ -1,12 +0,0 @@
import Foundation
import TuistSupportTesting
import XCTest
@testable import ProjectDescription
final class GalaxyTests: XCTestCase {
func test_codable() throws {
let subject = Galaxy(token: "token")
XCTAssertCodable(subject)
}
}

View File

@ -5,8 +5,8 @@ import SPMUtility
import TuistCore
import TuistSupport
import XCTest
@testable import TuistCache
@testable import TuistCoreTesting
@testable import TuistGalaxy
@testable import TuistSupportTesting
final class CacheLocalStorageIntegrationTests: TuistTestCase {

View File

@ -4,8 +4,8 @@ import SPMUtility
import TuistCore
import TuistSupport
import XCTest
@testable import TuistCache
@testable import TuistCoreTesting
@testable import TuistGalaxy
@testable import TuistSupportTesting
final class XCFrameworkBuilderIntegrationTests: TuistTestCase {

View File

@ -5,7 +5,7 @@ import TuistCoreTesting
import TuistSupport
import XCTest
@testable import TuistGalaxy
@testable import TuistCache
@testable import TuistSupportTesting
final class ContentHashingIntegrationTests: TuistTestCase {

View File

@ -1,7 +1,7 @@
import Foundation
import XCTest
@testable import TuistGalaxy
@testable import TuistCache
@testable import TuistSupportTesting
final class CacheLocalStorageErrorTests: TuistUnitTestCase {

View File

@ -3,7 +3,7 @@ import Foundation
import TuistCore
import TuistCoreTesting
import XCTest
@testable import TuistGalaxy
@testable import TuistCache
final class GraphContentHasherTests: XCTestCase {
private var sut: GraphContentHasher!

View File

@ -61,8 +61,8 @@ final class TargetTests: TuistUnitTestCase {
// When
let sources = try Target.sources(projectPath: temporaryPath,
sources: [
(glob: temporaryPath.appending(RelativePath("sources/**")).pathString, excluding: nil, compilerFlags: nil),
(glob: temporaryPath.appending(RelativePath("sources/**")).pathString, excluding: nil, compilerFlags: nil),
(glob: temporaryPath.appending(RelativePath("sources/**")).pathString, excluding: [], compilerFlags: nil),
(glob: temporaryPath.appending(RelativePath("sources/**")).pathString, excluding: [], compilerFlags: nil),
])
// Then
@ -92,11 +92,52 @@ final class TargetTests: TuistUnitTestCase {
// When
let sources = try Target.sources(projectPath: temporaryPath,
sources: [(
glob: temporaryPath.appending(RelativePath("sources/**")).pathString,
excluding: temporaryPath.appending(RelativePath("sources/**/*Tests.swift")).pathString,
compilerFlags: nil
)])
sources: [
(glob: temporaryPath.appending(RelativePath("sources/**")).pathString,
excluding: [temporaryPath.appending(RelativePath("sources/**/*Tests.swift")).pathString],
compilerFlags: nil),
])
// Then
let relativeSources = sources.map { $0.path.relative(to: temporaryPath).pathString }
XCTAssertEqual(Set(relativeSources), Set([
"sources/a.swift",
"sources/b.swift",
"sources/c/c.swift",
]))
}
func test_sources_excluding_multiple_paths() throws {
// Given
let temporaryPath = try self.temporaryPath()
try createFiles([
"sources/a.swift",
"sources/b.swift",
"sources/aTests.swift",
"sources/c/cType+Fake.swift",
"sources/aType+Fake.swift",
"sources/bTests.swift",
"sources/kTests.kt",
"sources/c/c.swift",
"sources/c/cTests.swift",
"sources/d.h",
"sources/d.m",
"sources/d/d.m",
])
let excluding: [String] = [
temporaryPath.appending(RelativePath("sources/**/*Tests.swift")).pathString,
temporaryPath.appending(RelativePath("sources/**/*Fake.swift")).pathString,
temporaryPath.appending(RelativePath("sources/**/*.m")).pathString,
]
// When
let sources = try Target.sources(projectPath: temporaryPath,
sources: [
(glob: temporaryPath.appending(RelativePath("sources/**")).pathString,
excluding: excluding,
compilerFlags: nil),
])
// Then
let relativeSources = sources.map { $0.path.relative(to: temporaryPath).pathString }

View File

@ -1,5 +1,4 @@
import Foundation
import OpenCombine
@testable import TuistEnvKit
final class MockURLSessionScheduler: URLSessionScheduling {
@ -14,28 +13,9 @@ final class MockURLSessionScheduler: URLSessionScheduling {
}
func schedule(request: URLRequest) -> (error: Error?, data: Data?) {
guard let stub = self.stubs[request] else {
guard let stub = stubs[request] else {
return (error: nil, data: nil)
}
return stub
}
func publisher(request: URLRequest) -> OpenCombine.AnyPublisher<(data: Data, response: URLResponse), URLError> {
guard let stub = self.stubs[request] else {
return OpenCombine.Fail<(data: Data, response: URLResponse), URLError>(error: URLError(.badServerResponse))
.eraseToAnyPublisher()
}
if let data = stub.data {
let response = URLResponse()
return OpenCombine.Just<(data: Data, response: URLResponse)>.init((data: data, response: response))
.setFailureType(to: URLError.self)
.eraseToAnyPublisher()
} else if let error = stub.error {
return OpenCombine.Fail<(data: Data, response: URLResponse), URLError>(error: error)
.eraseToAnyPublisher()
} else {
return OpenCombine.Fail<(data: Data, response: URLResponse), URLError>(error: URLError(.badServerResponse))
.eraseToAnyPublisher()
}
}
}

View File

@ -345,7 +345,10 @@ final class ConfigGeneratorTests: TuistUnitTestCase {
settings: .default,
targets: [target])
let fileElements = ProjectFileElements()
let groups = ProjectGroups.generate(project: project, pbxproj: pbxproj, sourceRootPath: dir.path)
let groups = ProjectGroups.generate(project: project,
pbxproj: pbxproj,
xcodeprojPath: project.path.appending(component: "\(project.fileName).xcodeproj"),
sourceRootPath: dir.path)
let graph = Graph.test()
try fileElements.generateProjectFiles(project: project,
graph: graph,

View File

@ -7,12 +7,13 @@ import TuistSupport
final class MockProjectGenerator: ProjectGenerating {
var generatedProjects: [Project] = []
var generateStub: ((Project, Graphing, AbsolutePath?) throws -> GeneratedProject)?
var generateStub: ((Project, Graphing, AbsolutePath?, AbsolutePath?) throws -> GeneratedProject)?
func generate(project: Project,
graph: Graphing,
sourceRootPath: AbsolutePath?) throws -> GeneratedProject {
sourceRootPath: AbsolutePath?,
xcodeprojPath: AbsolutePath?) throws -> GeneratedProject {
generatedProjects.append(project)
return try generateStub?(project, graph, sourceRootPath) ?? GeneratedProject.test()
return try generateStub?(project, graph, sourceRootPath, xcodeprojPath) ?? GeneratedProject.test()
}
}

View File

@ -21,6 +21,7 @@ final class ProjectFileElementsTests: TuistUnitTestCase {
pbxproj = PBXProj()
groups = ProjectGroups.generate(project: .test(),
pbxproj: pbxproj,
xcodeprojPath: "/path/Project.xcodeproj",
sourceRootPath: "/path",
playgrounds: MockPlaygrounds())
@ -314,6 +315,7 @@ final class ProjectFileElementsTests: TuistUnitTestCase {
let graph = Graph.test()
let groups = ProjectGroups.generate(project: project,
pbxproj: pbxproj,
xcodeprojPath: project.path.appending(component: "\(project.fileName).xcodeproj"),
sourceRootPath: project.path)
// When
@ -348,6 +350,7 @@ final class ProjectFileElementsTests: TuistUnitTestCase {
let graph = Graph.test()
let groups = ProjectGroups.generate(project: project,
pbxproj: pbxproj,
xcodeprojPath: project.path.appending(component: "\(project.fileName).xcodeproj"),
sourceRootPath: project.path)
// When
@ -378,6 +381,7 @@ final class ProjectFileElementsTests: TuistUnitTestCase {
let graph = Graph.test()
let groups = ProjectGroups.generate(project: project,
pbxproj: pbxproj,
xcodeprojPath: project.path.appending(component: "\(project.fileName).xcodeproj"),
sourceRootPath: project.path)
// When
@ -401,6 +405,7 @@ final class ProjectFileElementsTests: TuistUnitTestCase {
let project = Project.test(path: AbsolutePath("/"), filesGroup: projectGroup, targets: [target])
let groups = ProjectGroups.generate(project: project,
pbxproj: pbxproj,
xcodeprojPath: project.path.appending(component: "\(project.fileName).xcodeproj"),
sourceRootPath: sourceRootPath)
var dependencies: Set<GraphDependencyReference> = Set()
let precompiledNode = GraphDependencyReference.absolute(project.path.appending(component: "waka.framework"))
@ -427,6 +432,7 @@ final class ProjectFileElementsTests: TuistUnitTestCase {
let sourceRootPath = AbsolutePath("/a/project/")
let groups = ProjectGroups.generate(project: project,
pbxproj: pbxproj,
xcodeprojPath: project.path.appending(component: "\(project.fileName).xcodeproj"),
sourceRootPath: sourceRootPath)
// When
@ -534,6 +540,7 @@ final class ProjectFileElementsTests: TuistUnitTestCase {
let project = Project.test(path: temporaryPath)
let groups = ProjectGroups.generate(project: project,
pbxproj: pbxproj,
xcodeprojPath: project.path.appending(component: "\(project.fileName).xcodeproj"),
sourceRootPath: temporaryPath,
playgrounds: playgrounds)
@ -599,6 +606,7 @@ final class ProjectFileElementsTests: TuistUnitTestCase {
let sourceRootPath = AbsolutePath("/a/project/")
let groups = ProjectGroups.generate(project: project,
pbxproj: pbxproj,
xcodeprojPath: project.path.appending(component: "\(project.fileName).xcodeproj"),
sourceRootPath: sourceRootPath)
let sdk = try SDKNode(name: "ARKit.framework",
@ -633,6 +641,7 @@ final class ProjectFileElementsTests: TuistUnitTestCase {
packages: [.remote(url: "url", requirement: .branch("master"))])
let groups = ProjectGroups.generate(project: .test(),
pbxproj: pbxproj,
xcodeprojPath: project.path.appending(component: "\(project.fileName).xcodeproj"),
sourceRootPath: project.path)
let package = PackageProductNode(product: "A", path: "/packages/url")

View File

@ -44,6 +44,7 @@ final class ProjectGroupsTests: XCTestCase {
}
subject = ProjectGroups.generate(project: project,
pbxproj: pbxproj,
xcodeprojPath: project.path.appending(component: "\(project.fileName).xcodeproj"),
sourceRootPath: sourceRootPath,
playgrounds: playgrounds)
@ -81,7 +82,11 @@ final class ProjectGroupsTests: XCTestCase {
playgrounds.pathsStub = { _ in
[]
}
subject = ProjectGroups.generate(project: project, pbxproj: pbxproj, sourceRootPath: sourceRootPath, playgrounds: playgrounds)
subject = ProjectGroups.generate(project: project,
pbxproj: pbxproj,
xcodeprojPath: project.path.appending(component: "\(project.fileName).xcodeproj"),
sourceRootPath: sourceRootPath,
playgrounds: playgrounds)
XCTAssertNil(subject.playgrounds)
}
@ -99,7 +104,11 @@ final class ProjectGroupsTests: XCTestCase {
targets: [target1, target2, target3, target4])
// When
subject = ProjectGroups.generate(project: project, pbxproj: pbxproj, sourceRootPath: sourceRootPath, playgrounds: playgrounds)
subject = ProjectGroups.generate(project: project,
pbxproj: pbxproj,
xcodeprojPath: project.path.appending(component: "\(project.fileName).xcodeproj"),
sourceRootPath: sourceRootPath,
playgrounds: playgrounds)
// Then
let paths = subject.main.children.compactMap { $0.nameOrPath }
@ -115,7 +124,11 @@ final class ProjectGroupsTests: XCTestCase {
}
func test_targetFrameworks() throws {
subject = ProjectGroups.generate(project: project, pbxproj: pbxproj, sourceRootPath: sourceRootPath, playgrounds: playgrounds)
subject = ProjectGroups.generate(project: project,
pbxproj: pbxproj,
xcodeprojPath: project.path.appending(component: "\(project.fileName).xcodeproj"),
sourceRootPath: sourceRootPath,
playgrounds: playgrounds)
let got = try subject.targetFrameworks(target: "Test")
XCTAssertEqual(got.name, "Test")
@ -125,7 +138,11 @@ final class ProjectGroupsTests: XCTestCase {
func test_projectGroup_unknownProjectGroups() throws {
// Given
subject = ProjectGroups.generate(project: project, pbxproj: pbxproj, sourceRootPath: sourceRootPath, playgrounds: playgrounds)
subject = ProjectGroups.generate(project: project,
pbxproj: pbxproj,
xcodeprojPath: project.path.appending(component: "\(project.fileName).xcodeproj"),
sourceRootPath: sourceRootPath,
playgrounds: playgrounds)
// When / Then
XCTAssertThrowsSpecific(try subject.projectGroup(named: "abc"),
@ -140,7 +157,11 @@ final class ProjectGroupsTests: XCTestCase {
let project = Project.test(filesGroup: .group(name: "P"),
targets: [target1, target2, target3])
subject = ProjectGroups.generate(project: project, pbxproj: pbxproj, sourceRootPath: sourceRootPath, playgrounds: playgrounds)
subject = ProjectGroups.generate(project: project,
pbxproj: pbxproj,
xcodeprojPath: project.path.appending(component: "\(project.fileName).xcodeproj"),
sourceRootPath: sourceRootPath,
playgrounds: playgrounds)
// When / Then
XCTAssertNotNil(try? subject.projectGroup(named: "A"))

View File

@ -45,6 +45,7 @@ final class TargetGeneratorTests: XCTestCase {
let graph = Graph.test()
let groups = ProjectGroups.generate(project: project,
pbxproj: pbxproj,
xcodeprojPath: project.path.appending(component: "\(project.fileName).xcodeproj"),
sourceRootPath: path,
playgrounds: MockPlaygrounds())
try fileElements.generateProjectFiles(project: project,
@ -121,6 +122,7 @@ final class TargetGeneratorTests: XCTestCase {
let project = Project.test(path: path, targets: [target])
let groups = ProjectGroups.generate(project: project,
pbxproj: pbxproj,
xcodeprojPath: project.path.appending(component: "\(project.fileName).xcodeproj"),
sourceRootPath: path,
playgrounds: MockPlaygrounds())
try fileElements.generateProjectFiles(project: project,

View File

@ -9,9 +9,9 @@ class MockGenerator: Generating {
try generateProjectAtStub?(path) ?? AbsolutePath("/test")
}
var generateProjectStub: ((Project) throws -> AbsolutePath)?
func generateProject(_ project: Project, graph _: Graphing) throws -> AbsolutePath {
try generateProjectStub?(project) ?? AbsolutePath("/test")
var generateProjectStub: ((Project, AbsolutePath?, AbsolutePath?) throws -> AbsolutePath)?
func generateProject(_ project: Project, graph _: Graphing, sourceRootPath: AbsolutePath?, xcodeprojPath: AbsolutePath?) throws -> AbsolutePath {
try generateProjectStub?(project, sourceRootPath, xcodeprojPath) ?? AbsolutePath("/test")
}
var generateProjectWorkspaceStub: ((AbsolutePath, [AbsolutePath]) throws -> AbsolutePath)?

View File

@ -68,7 +68,7 @@ final class ProjectEditorTests: TuistUnitTestCase {
helpersDirectoryLocator.locateStub = helpersDirectory
projectEditorMapper.mapStub = (project, graph)
var generatedProject: Project?
generator.generateProjectStub = { project in
generator.generateProjectStub = { project, _, _ in
generatedProject = project
return directory.appending(component: "Edit.xcodeproj")
}

View File

@ -0,0 +1,39 @@
import Foundation
import XCTest
@testable import TuistSupport
final class CICheckerTests: XCTestCase {
var subject: CIChecker!
override func setUp() {
super.setUp()
subject = CIChecker()
}
override func tearDown() {
subject = nil
super.tearDown()
}
func testIsCI_when_isCI() {
// Given
let env = ["CI": "1"]
// When
let got = subject.isCI(environment: env)
// Then
XCTAssertTrue(got)
}
func testIsCI_when_isNotCI() {
// Given
let env: [String: String] = [:]
// When
let got = subject.isCI(environment: env)
// Then
XCTAssertFalse(got)
}
}

View File

@ -3,6 +3,10 @@
This folder contains sample projects we use in the integration and acceptance tests.
Please keep this keep in alphabetical order.
## fixture_generator
This is a Swift Package that generates a large application / workspace to stress test Tuist. The generated fixture itself is not checked in as it can vary based on the test being conducted.
## invalid_workspace_manifest_name
Contains a single file `Workspac.swift`, incorrectly named workspace manifest file.
@ -297,3 +301,4 @@ e.g. `ios_app_with_xcframeworks/Frameworks/MyFramework/build.sh`
## ios_app_with_coredate
A simple iOS app with a Core Data model and Mapping Model (.xcmappingmodel)

7
fixtures/fixture_generator/.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
.DS_Store
/.build
/Packages
/*.xcodeproj
xcuserdata/
Fixture
.swiftpm

View File

@ -0,0 +1,25 @@
{
"object": {
"pins": [
{
"package": "llbuild",
"repositoryURL": "https://github.com/apple/swift-llbuild.git",
"state": {
"branch": null,
"revision": "f73b84bc1525998e5e267f9d830c1411487ac65e",
"version": "0.2.0"
}
},
{
"package": "SwiftPM",
"repositoryURL": "https://github.com/apple/swift-package-manager",
"state": {
"branch": null,
"revision": "9abcc2260438177cecd7cf5185b144d13e74122b",
"version": "0.5.0"
}
}
]
},
"version": 1
}

View File

@ -0,0 +1,22 @@
// swift-tools-version:5.1
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "FixtureGenerator",
dependencies: [
// Dependencies declare other packages that this package depends on.
.package(url: "https://github.com/apple/swift-package-manager", from: "0.5.0"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
.target(
name: "FixtureGenerator",
dependencies: [
"SPMUtility",
]
),
]
)

View File

@ -0,0 +1,24 @@
# Fixture Generator
A tool to generate large fixtures for the purposes of stress testing Tuist,
# Usage
```sh
swift run FixtureGenerator --projects 10 --targets 10 --sources 500
```
Options:
- `projects`: Number of projects to generate
- `targets`: Number of targets to generate
- `sources`: Number of sources to generate
# Features
- [x] Control number of projects
- [x] Control number of targets
- [x] Control number of sources
- [ ] Add pre-compiled libraries
- [ ] Add pre-compiled frameworks
- [ ] Add pre-compiled xcframeworks

View File

@ -0,0 +1,64 @@
import Basic
import Foundation
import SPMUtility
enum GenerateCommandError: Error {
case invalidPath
}
final class GenerateCommand {
private let pathArgument: OptionArgument<String>
private let projectsArgument: OptionArgument<Int>
private let targetsArgument: OptionArgument<Int>
private let sourcesArgument: OptionArgument<Int>
private let fileSystem: FileSystem
init(fileSystem: FileSystem,
parser: ArgumentParser) {
self.fileSystem = fileSystem
pathArgument = parser.add(option: "--path",
kind: String.self,
usage: "The path where the fixture will be generated.",
completion: .filename)
projectsArgument = parser.add(option: "--projects",
shortName: "-s",
kind: Int.self,
usage: "Number of projects to generate.")
targetsArgument = parser.add(option: "--targets",
shortName: "-t",
kind: Int.self,
usage: "Number of targets to generate within each project.")
sourcesArgument = parser.add(option: "--sources",
shortName: "-s",
kind: Int.self,
usage: "Number of sources to generate within each target.")
}
func run(with arguments: ArgumentParser.Result) throws {
let defaultConfig = GeneratorConfig.default
let path = try determineFixturePath(using: arguments)
let projects = arguments.get(projectsArgument) ?? defaultConfig.projects
let targets = arguments.get(targetsArgument) ?? defaultConfig.targets
let sources = arguments.get(sourcesArgument) ?? defaultConfig.sources
let config = GeneratorConfig(projects: projects, targets: targets, sources: sources)
let generator = Generator(fileSystem: fileSystem, config: config)
try generator.generate(at: path)
}
private func determineFixturePath(using arguments: ArgumentParser.Result) throws -> AbsolutePath {
guard let currentPath = fileSystem.currentWorkingDirectory else {
throw GenerateCommandError.invalidPath
}
guard let path = arguments.get(pathArgument) else {
return currentPath
}
return AbsolutePath(path, relativeTo: currentPath)
}
}

View File

@ -0,0 +1,86 @@
import Basic
import Foundation
class Generator {
private let fileSystem: FileSystem
private let config: GeneratorConfig
private let sourceTemplate: SourceTemplate
private let manifestTemplate: ManifestTemplate
init(fileSystem: FileSystem, config: GeneratorConfig) {
self.fileSystem = fileSystem
self.config = config
sourceTemplate = SourceTemplate()
manifestTemplate = ManifestTemplate()
}
func generate(at path: AbsolutePath) throws {
let rootPath = path.appending(component: "Fixture")
let projects = (1 ... config.projects).map { "Project\($0)" }
try fileSystem.createDirectory(rootPath)
try initWorkspaceManifest(at: rootPath,
name: "Workspace",
projects: projects)
try projects.forEach {
try initProject(at: rootPath,
name: $0)
}
}
private func initWorkspaceManifest(at path: AbsolutePath,
name: String,
projects: [String]) throws {
let manifestPath = path.appending(component: "Workspace.swift")
let manifest = manifestTemplate.generate(workspaceName: name,
projects: projects)
try fileSystem.writeFileContents(manifestPath,
bytes: ByteString(encodingAsUTF8: manifest))
}
private func initProject(at path: AbsolutePath,
name: String) throws {
let projectPath = path.appending(component: name)
let targets = (1 ... config.targets).map { "Target\($0)" }
try fileSystem.createDirectory(projectPath)
try initProjectManifest(at: projectPath, name: name, targets: targets)
try targets.forEach {
try initTarget(at: projectPath, name: $0)
}
}
private func initProjectManifest(at path: AbsolutePath,
name: String,
targets: [String]) throws {
let manifestPath = path.appending(component: "Project.swift")
let manifest = manifestTemplate.generate(projectName: name,
targets: targets)
try fileSystem.writeFileContents(manifestPath,
bytes: ByteString(encodingAsUTF8: manifest))
}
private func initTarget(at path: AbsolutePath, name: String) throws {
let targetPath = path.appending(component: name)
try fileSystem.createDirectory(targetPath)
try initSources(at: targetPath, targetName: name)
}
private func initSources(at path: AbsolutePath, targetName: String) throws {
let sourcesPath = path.appending(component: "Sources")
try fileSystem.createDirectory(sourcesPath)
try (1 ... config.sources).forEach {
let sourceName = "Source\($0).swift"
let source = sourceTemplate.generate(frameworkName: targetName, number: $0)
try fileSystem.writeFileContents(sourcesPath.appending(component: sourceName),
bytes: ByteString(encodingAsUTF8: source))
}
}
}

View File

@ -0,0 +1,14 @@
import Foundation
struct GeneratorConfig {
var projects: Int
var targets: Int
var sources: Int
}
extension GeneratorConfig {
static var `default`: GeneratorConfig {
return GeneratorConfig(projects: 5, targets: 10, sources: 50)
}
}

View File

@ -0,0 +1,70 @@
import Foundation
class ManifestTemplate {
private let workspaceTemplate = """
import ProjectDescription
let workspace = Workspace(
name: "{WorkspaceName}",
projects: [
{Projects}
])
"""
private let workspaceProjectTemplate = """
"{Project}"
"""
private let projectTemplate = """
import ProjectDescription
let project = Project(
name: "{ProjectName}",
targets: [
{Targets}
])
"""
private let targetTemplate = """
Target(
name: "{TargetName}",
platform: .iOS,
product: .framework,
bundleId: "io.tuist.{TargetName}",
infoPlist: .default,
sources: [
"{TargetName}/Sources/**"
],
resources: [
],
dependencies: [
])
"""
func generate(workspaceName: String, projects: [String]) -> String {
return workspaceTemplate
.replacingOccurrences(of: "{WorkspaceName}", with: workspaceName)
.replacingOccurrences(of: "{Projects}", with: generate(projects: projects))
}
func generate(projectName: String, targets: [String]) -> String {
return projectTemplate
.replacingOccurrences(of: "{ProjectName}", with: projectName)
.replacingOccurrences(of: "{Targets}", with: generate(targets: targets))
}
private func generate(projects: [String]) -> String {
return projects.map {
workspaceProjectTemplate.replacingOccurrences(of: "{Project}", with: $0)
}.joined(separator: ",\n")
}
private func generate(targets: [String]) -> String {
return targets.map {
targetTemplate.replacingOccurrences(of: "{TargetName}", with: $0)
}.joined(separator: ",\n")
}
}

View File

@ -0,0 +1,25 @@
import Foundation
class SourceTemplate {
private let template = """
import Foundation
public class {FrameworkName}SomeClass{Number} {
public init() {
}
public func hello() {
}
}
"""
func generate(frameworkName: String, number: Int) -> String {
return template
.replacingOccurrences(of: "{FrameworkName}", with: frameworkName)
.replacingOccurrences(of: "{Number}", with: "\(number)")
}
}

View File

@ -0,0 +1,23 @@
import Basic
import Foundation
import SPMUtility
func main() throws {
let parser = ArgumentParser(commandName: "FixtureGenerator",
usage: "FixtureGenerator <options>",
overview: "Generates large fixtures for the purposes of stress testing Tuist")
let fileSystem = localFileSystem
let generateCommand = GenerateCommand(fileSystem: fileSystem,
parser: parser)
let arguments = ProcessInfo.processInfo.arguments
let results = try parser.parse(Array(arguments.dropFirst()))
try generateCommand.run(with: results)
}
do {
try main()
} catch {
print("Error: \(error.localizedDescription)")
}

View File

@ -23,3 +23,8 @@
twitter: marekfort
name: Marek Fořt
avatar: https://avatars1.githubusercontent.com/u/9371695?s=460&v=4
- handle: vytis
github: vytis
twitter: vytis0
name: Vytis Šibonis
avatar: https://avatars1.githubusercontent.com/u/33914?s=460&v=4

View File

@ -1,10 +1,12 @@
const remarkSlug = require('remark-slug')
const title = `Tuist - Xcode on steroids`
const siteUrl = 'https://tuist.io'
module.exports = {
siteMetadata: {
title: `Tuist - Xcode on steroids`,
title: title,
description: `Tuist is a tool that helps developers manage large Xcode projects by leveraging project generation. Moreover, it provides some tools to automate most common tasks, allowing developers to focus on building apps.`,
siteUrl: 'https://tuist.io',
siteUrl: siteUrl,
githubUrl: 'https://github.com/tuist',
releasesUrl: 'https://github.com/tuist/tuist/releases',
documentationUrl: 'https://docs.tuist.io/',
@ -213,6 +215,41 @@ module.exports = {
generateMatchPathRewrites: true, // boolean to turn off automatic creation of redirect rules for client only paths
},
},
{
resolve: 'gatsby-plugin-next-seo',
options: {
titleTemplate: '%s | Tuist',
openGraph: {
type: 'website',
locale: 'en_IE',
url: siteUrl,
site_name: title,
keywords: [
`tuist`,
`engineering`,
`xcode`,
`swift`,
`project generation`,
`xcode project generation`,
`xcodeproj`,
`xcodegen`,
'ios',
'uikit',
'foundation',
'tvos',
'ios',
'watchos',
'objective-c',
'swift package manager',
'swift packages',
],
},
twitter: {
site: siteUrl,
cardType: 'summary_large_image',
},
},
},
'gatsby-plugin-meta-redirect',
`gatsby-plugin-robots-txt`,
],

View File

@ -333,11 +333,11 @@ It represents a glob pattern that refers to source files and the compiler flags
},
{
name: 'Excluding',
description: 'Glob pattern for source files that will be excluded.',
type: 'Path',
description: 'Glob patterns for source files that will be excluded.',
type: '[Path]',
typeLink: '#path',
optional: true,
default: 'nil',
default: '[]',
},
{
name: 'Compiler flags',

View File

@ -0,0 +1,25 @@
---
layout: post
title: Tuist 1.2.0 - Bugfixes, improvements and future direction
categories: [tuist, release, swift, 1.2.0, project generation, xcode, swift]
excerpt: The newest release brings you many bugfixes and improvements. However with the number of contributors steadily growing the team has also been busy thinking and writing about the direction of Tuist. We have a rough plan for Tuist 2.0 and work has started on compiling a manifesto to explain a bit more about the goals and values of the project.
author: vytis
---
We are slowly learning how to do smaller [bi-weekly releases](https://tuist.io/docs/contribution/release/#cadence). This release was planned for last week but it was postponed because there were not enough changes. However lots of new things got merged recently and Tuist 1.2.0 is here for you to enjoy!
## New contributors
Firstly, warm welcome to another first-time Tuist contributor [Maciej Piotrowski](https://github.com/paciej00) 👏👏👏. He improved the API introduced in the previous release for excluding files from a target. It makes exclude patterns more flexible which can be very useful when migrating an existing project to Tuist!
## Future of Tuist
There are plenty of ideas floating around what other features Tuist should have and [Pedro](https://github.com/pepibumur) laid them out in nicely in [The journey towards Tuist 2.0](https://github.com/tuist/tuist/issues/951). In addition, he also opened a work-in-progress PR for Tuist's [manifesto](https://github.com/tuist/tuist/pull/952). With more and more people joining the project it's very important to lay down the core principles in writing to ensure that everyone is up to speed. For more practical information we also have a new [Best Practices](https://tuist.io/docs/usage/best-practices/) section on the website. It is quite short at the moment, but will contain a growing list of things that can be useful for both new Tuist users and developers trying to improve their setup.
## Focus on improving performance
A common complaint, especially for bigger projects, is the time it takes to generate the project. The main focus until this point was adding features, but time has come to start thinking about performance. End of last year [Kas](https://github.com/kwridan) proposed an [idea](https://github.com/tuist/tuist/issues/820) to start performance testing. First part of this initiative already got [merged](https://github.com/tuist/tuist/pull/890). The [`FixtureGenerator`](https://github.com/tuist/tuist/tree/master/fixtures/fixture_generator) tool will allow us to easily generate a project with potentially hundreds of targets and iron out performance issues much more systematically. It is just the beginning and the plan is eventually to integrate performance testing as part of GitHub checks so we can be sure that adding new features would not have an impact on project generation times.
## Full Changelog
Of all the things mentioned, there are also plenty of bugfixes and improvements in this release. The full changelog can be found on the [release page](https://github.com/tuist/tuist/releases/tag/1.2.0).

View File

@ -14,35 +14,36 @@
"@mdx-js/mdx": "^1.5.5",
"@mdx-js/react": "^1.5.5",
"@mdx-js/tag": "^0.20.3",
"@theme-ui/color": "^0.2.53",
"@theme-ui/presets": "^0.2.44",
"@theme-ui/prism": "^0.2.50",
"@theme-ui/typography": "^0.2.46",
"@theme-ui/color": "^0.3.1",
"@theme-ui/presets": "^0.3.0",
"@theme-ui/prism": "^0.3.0",
"@theme-ui/typography": "^0.3.0",
"focus-visible": "^5.0.2",
"gatsby": "^2.18.21",
"gatsby-image": "^2.2.39",
"gatsby-image": "^2.2.40",
"gatsby-plugin-favicon": "3.1.6",
"gatsby-plugin-feed": "^2.3.26",
"gatsby-plugin-manifest": "2.2.37",
"gatsby-plugin-feed": "^2.3.27",
"gatsby-plugin-manifest": "2.2.41",
"gatsby-plugin-mdx": "^1.0.67",
"gatsby-plugin-meta-redirect": "^1.1.1",
"gatsby-plugin-netlify": "^2.1.31",
"gatsby-plugin-offline": "^3.0.32",
"gatsby-plugin-react-helmet": "^3.1.21",
"gatsby-plugin-next-seo": "^1.4.0",
"gatsby-plugin-offline": "^3.0.34",
"gatsby-plugin-react-helmet": "^3.1.22",
"gatsby-plugin-react-svg": "^3.0.0",
"gatsby-plugin-robots-txt": "^1.5.0",
"gatsby-plugin-sharp": "2.4.0",
"gatsby-plugin-sitemap": "^2.2.26",
"gatsby-plugin-theme-ui": "^0.2.53",
"gatsby-plugin-sitemap": "^2.2.27",
"gatsby-plugin-theme-ui": "^0.3.0",
"gatsby-redirect-from": "^0.2.1",
"gatsby-remark-check-links": "^2.1.0",
"gatsby-remark-images": "^3.1.42",
"gatsby-remark-smartypants": "^2.1.20",
"gatsby-remark-smartypants": "^2.1.21",
"gatsby-remark-social-cards": "https://github.com/pepibumur/gatsby-remark-social-cards.git#0.5.2",
"gatsby-source-filesystem": "^2.1.46",
"gatsby-transformer-yaml": "^2.2.23",
"gatsby-source-filesystem": "^2.1.48",
"gatsby-transformer-yaml": "^2.2.24",
"moment": "^2.24.0",
"polished": "^3.4.2",
"polished": "^3.4.4",
"prettier": "^1.19.1",
"prismjs": "^1.19.0",
"prop-types": "^15.7.2",
@ -51,6 +52,7 @@
"react-burger-menu": "^2.6.13",
"react-dom": "^16.12.0",
"react-helmet": "^5.2.0",
"react-helmet-async": "^1.0.4",
"react-markdown": "^4.3.1",
"react-pose": "^4.0.10",
"react-share": "^4.0.1",
@ -58,7 +60,7 @@
"react-use-visibility": "^0.3.0",
"remark-slug": "^5.1.2",
"semantic-ui-react": "^0.88.1",
"slug": "^2.1.0",
"slug": "^2.1.1",
"theme-ui": "^0.2.52",
"url-join": "^4.0.1"
},

View File

@ -0,0 +1,35 @@
/** @jsx jsx */
import { jsx } from 'theme-ui'
import React from 'react'
import { GatsbySeo } from 'gatsby-plugin-next-seo'
import { LogoJsonLd } from 'gatsby-plugin-next-seo'
import logo from '../../static/tuist.png'
import { useStaticQuery, graphql } from 'gatsby'
export default ({ description, title, ...other }) => {
const {
site: {
siteMetadata: { siteUrl, description: defaultDescription },
},
} = useStaticQuery(graphql`
query {
site {
siteMetadata {
siteUrl
description
}
}
}
`)
return (
<>
<GatsbySeo
description={description != null ? description : defaultDescription}
title={title != null ? title : 'Tuist'}
titleTemplate={title != null ? `%s | Tuist` : '%s'}
{...other}
/>
<LogoJsonLd logo={logo} url={siteUrl} />
</>
)
}

View File

@ -1,11 +1,12 @@
/** @jsx jsx */
import { jsx } from 'theme-ui'
import { withPrefix, Link, useStaticQuery, graphql } from 'gatsby'
import { Link, useStaticQuery, graphql } from 'gatsby'
import { Styled } from 'theme-ui'
import { Location } from '@reach/router'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faGithub } from '@fortawesome/free-brands-svg-icons'
import { faSlack } from '@fortawesome/free-brands-svg-icons'
import logo from '../../static/logo.svg'
export default () => {
const hoverStyle = {
@ -56,7 +57,7 @@ export default () => {
}}
>
<img
src={withPrefix('logo.svg')}
src={logo}
sx={{ height: 30, width: 30 }}
alt="Tuist's logotype"
/>

View File

@ -2,13 +2,11 @@ import React from 'react'
import { Styled } from 'theme-ui'
import GlobalStyle from './global-style'
import Header from '../components/header'
import { LogoStructuredData } from '../components/structured-data'
const Layout = ({ children }) => {
return (
<>
<GlobalStyle />
<LogoStructuredData />
<Styled.root>
<Header />
<main>{children}</main>

View File

@ -1,82 +0,0 @@
import React from 'react'
import Helmet from 'react-helmet'
import { useStaticQuery, graphql, withPrefix } from 'gatsby'
import urljoin from 'url-join'
function Meta({ description, lang, meta, keywords, title, author, slug }) {
const { site } = useStaticQuery(
graphql`
query {
site {
siteMetadata {
siteUrl
title
description
}
}
}
`
)
const metaDescription = description || site.siteMetadata.description
const metaTitle = title || site.siteMetadata.title
const titleTemplate = title ? `%s | ${site.siteMetadata.title}` : `%s`
return (
<Helmet
htmlAttributes={{
lang,
}}
title={metaTitle}
titleTemplate={titleTemplate}
>
<meta property="og:title" content={title} />
<meta property="og:description" content={metaDescription} />
<meta property="og:type" content="website" />
<meta
property="og:image"
content={urljoin(
site.siteMetadata.siteUrl,
withPrefix('squared-logo.png')
)}
/>
<meta name="twitter:creator" content={site.siteMetadata.author} />
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={metaDescription} />
<meta name="description" content={metaDescription} />
<meta name="keywords" content={keywords.join(`, `)} />
{slug && (
<meta
name="twitter:image"
content={`${site.siteMetadata.siteUrl}${slug}twitter-card.jpg`}
/>
)}
{slug && <meta name="twitter:card" content="summary_large_image" />}
</Helmet>
)
}
Meta.defaultProps = {
lang: `en`,
meta: [],
keywords: [
`tuist`,
`engineering`,
`xcode`,
`swift`,
`project generation`,
'ios',
'uikit',
'foundation',
'tvos',
'ios',
'watchos',
'objective-c',
'swift package manager',
'swift packages',
],
}
export default Meta

View File

@ -1,129 +0,0 @@
/** @jsx jsx */
import { jsx, Styled } from 'theme-ui'
import { withPrefix, useStaticQuery, graphql } from 'gatsby'
import Helmet from 'react-helmet'
import urljoin from 'url-join'
const ArticleStructuredData = ({
url,
title,
excerpt,
author,
siteUrl,
date,
}) => {
const structuredData = `
{
"@context": "https://schema.org",
"@type": "NewsArticle",
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "https://google.com/article"
},
"headline": "${title}",
"datePublished": "${date.toISOString()}",
"author": {
"@type": "Person",
"name": "${author}"
},
"publisher": {
"@type": "Organization",
"name": "Tuist",
"logo": {
"@type": "ImageObject",
"url": "${urljoin(siteUrl, withPrefix('tuist.png'))}"
}
},
"description": "${excerpt}"
}
`
return (
<Helmet>
<script type="application/ld+json">{structuredData}</script>
</Helmet>
)
}
const LogoStructuredData = () => {
const {
site: {
siteMetadata: { siteUrl },
},
} = useStaticQuery(graphql`
query {
site {
siteMetadata {
siteUrl
}
}
}
`)
const structuredData = `
{
"@context": "https://schema.org",
"@type": "Organization",
"url": "${siteUrl}",
"logo": "${urljoin(siteUrl, withPrefix('tuist.png'))}"
}
`
return (
<Helmet>
<script type="application/ld+json">{structuredData}</script>
</Helmet>
)
}
const FAQStructuredData = ({ items }) => {
const itemListElement = items.map((item, index) => {
return {
'@type': 'Question',
name: item[0],
acceptedAnswer: {
'@type': 'Answer',
text: item[1],
},
}
})
const structuredData = `
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": ${JSON.stringify(itemListElement)}
}
`
return (
<Helmet>
<script type="application/ld+json">{structuredData}</script>
</Helmet>
)
}
const BreadcrumbStructuredData = ({ items }) => {
const itemListElement = items.map((item, index) => {
return {
'@type': 'ListItem',
position: index,
name: item[0],
item: item[1],
}
})
const structuredData = `
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": ${JSON.stringify(itemListElement)}
}
`
return (
<Helmet>
<script type="application/ld+json">{structuredData}</script>
</Helmet>
)
}
export {
ArticleStructuredData,
BreadcrumbStructuredData,
LogoStructuredData,
FAQStructuredData,
}

View File

@ -2,11 +2,11 @@
import { jsx, Styled } from 'theme-ui'
import { useStaticQuery, graphql } from 'gatsby'
import Layout from '../components/layout'
import Meta from '../components/meta'
import Main from '../components/main'
import { MDXRenderer } from 'gatsby-plugin-mdx'
import { FAQStructuredData } from '../components/structured-data'
import { FAQJsonLd, GatsbySeo } from 'gatsby-plugin-next-seo'
import Footer from '../components/footer'
import SEO from '../components/SEO'
const Question = ({ question, body, index }) => {
return (
@ -38,22 +38,15 @@ export default () => {
}
`)
const structuredQuestions = questions.map(question => {
return [question.frontmatter.question, question.excerpt]
return { question: question.frontmatter.question, answer: question.excerpt }
})
return (
<Layout>
<FAQStructuredData items={structuredQuestions} />
<Meta
<SEO />
<FAQJsonLd questions={structuredQuestions} />
<GatsbySeo
title="FAQ"
description={`This page contains answers for questions that are frequently asked by users. Questions such as "Should I gitignore my project?" or "How does Tuist compare to the Swift Package Manager?"`}
keywords={[
'tuist',
'project generation',
'frequently asked questions',
'xcode',
'swift',
'faq',
]}
/>
<Main>
<Styled.h1>Frequently asked questions</Styled.h1>
@ -62,7 +55,7 @@ export default () => {
<Question
question={question.frontmatter.question}
body={question.body}
index={index}
key={index}
/>
)
})}

View File

@ -2,7 +2,6 @@
import { jsx, Styled } from 'theme-ui'
import Layout from '../components/layout'
import Meta from '../components/meta'
import Footer from '../components/footer'
import Main from '../components/main'
import { graphql, Link } from 'gatsby'
@ -14,11 +13,10 @@ import Message from '../../assets/message.svg'
import Framework from '../../assets/framework.svg'
import Arrow from '../../assets/arrow.svg'
import Swift from '../../assets/swift.svg'
import Soundcloud from '../../assets/soundcloud.svg'
import Mytaxi from '../../assets/mytaxi.svg'
import posed from 'react-pose'
import Code from '../gatsby-plugin-theme-ui/code'
import Quote from '../../assets/quote.svg'
import SEO from '../components/SEO'
const PressableButton = posed.div({
hoverable: true,
@ -391,6 +389,7 @@ const Reflection = ({ name, avatarUrl, testimony, role, company }) => {
<div sx={{ mt: 4, mb: 3, display: 'inherit' }}>
<img
src={avatarUrl}
alt={`${name} avatar`}
sx={{ bg: 'gray6', width: 60, height: 60, borderRadius: 30, ml: 3 }}
/>
<div
@ -419,7 +418,7 @@ const Reflection = ({ name, avatarUrl, testimony, role, company }) => {
p: 3,
}}
>
<quote>"{testimony}"</quote>
<Quote>"{testimony}"</Quote>
</div>
<div
sx={{
@ -609,6 +608,7 @@ const Contribute = () => {
height: 50,
borderRadius: 25,
}}
alt="Ollie's avatar"
src="https://avatars2.githubusercontent.com/u/1382565?s=460&v=4"
/>
</a>
@ -619,6 +619,7 @@ const Contribute = () => {
>
<img
sx={{ width: 50, height: 50, borderRadius: 25 }}
alt="Kas' avatar"
src="https://avatars2.githubusercontent.com/u/11914919?s=460&v=4"
/>
</a>
@ -635,6 +636,7 @@ const Contribute = () => {
height: 50,
borderRadius: 25,
}}
alt="Marek's profile"
src="https://avatars1.githubusercontent.com/u/9371695?s=460&v=4"
/>
</a>
@ -681,6 +683,7 @@ const Contribute = () => {
height: 50,
borderRadius: 25,
}}
alt="Lakpa's avatar"
src="https://avatars1.githubusercontent.com/u/389328?s=400&v=4"
/>
</a>
@ -707,6 +710,7 @@ const Contribute = () => {
height: 50,
borderRadius: 25,
}}
alt="Marcin's avatar"
src="https://avatars1.githubusercontent.com/u/946649?s=460&v=4"
/>
</a>
@ -719,7 +723,7 @@ const Contribute = () => {
const IndexPage = () => {
return (
<Layout>
<Meta />
<SEO />
<Steroids />
<Workspaces />
<Principles />

View File

@ -2,14 +2,15 @@
import { jsx, Styled } from 'theme-ui'
import Layout from '../components/layout'
import Meta from '../components/meta'
import Footer from '../components/footer'
import { Link } from 'gatsby'
import { graphql } from 'gatsby'
import Main from '../components/main'
import { findWhere } from 'underscore'
import { BreadcrumbStructuredData } from '../components/structured-data'
import { BreadcrumbJsonLd, BlogJsonLd } from 'gatsby-plugin-next-seo'
import urljoin from 'url-join'
import moment from 'moment'
import SEO from '../components/SEO'
const Post = ({ post, index, authors }) => {
const authorHandle = post.frontmatter.author
@ -125,17 +126,39 @@ const BlogList = ({
allAuthorsYaml: { nodes: authors },
},
}) => {
const breadcrumb = [['Blog', urljoin(siteUrl, '/blog')]]
const breadcrumb = [
{ position: 1, name: 'Blog', item: urljoin(siteUrl, '/blog') },
]
const description =
'Read about Tuist updates: new releases, engineering challenges, and road-map updates.'
return (
<Layout>
<BreadcrumbStructuredData items={breadcrumb} />
<Meta title="Blog" description={description} />
<BreadcrumbJsonLd itemListElements={breadcrumb} />
<SEO title="Blog" description={description} />
<BlogJsonLd
url={urljoin(siteUrl, '/blog')}
headline="Tuist Blog"
posts={edges.map(edge => {
const authorHandle = edge.node.frontmatter.author
const author = findWhere(authors, { handle: authorHandle })
return {
headline: edge.node.frontmatter.title,
author: author.name,
datePublished: moment(edge.node.fields.date).format(),
image: author.avatar,
publisherName: author.name,
publisherLogo: author.avatar,
}
})}
authorName="Tuist"
description={description}
/>
<Main>
<Styled.h1>Blog</Styled.h1>
{edges.map(({ node }, index) => {
return <Post post={node} index={index} authors={authors} />
return <Post post={node} key={index} authors={authors} />
})}
<PostsFooter {...pageContext} />
</Main>

View File

@ -3,18 +3,15 @@ import { jsx, Styled } from 'theme-ui'
import { MDXRenderer } from 'gatsby-plugin-mdx'
import Layout from '../components/layout'
import Meta from '../components/meta'
import Footer from '../components/footer'
import { graphql } from 'gatsby'
import moment from 'moment'
import Main from '../components/main'
import EditPage from '../components/edit-page'
import Share from '../components/share'
import {
BreadcrumbStructuredData,
ArticleStructuredData,
} from '../components/structured-data'
import urljoin from 'url-join'
import SEO from '../components/SEO'
import { NewsArticleJsonLd, BreadcrumbJsonLd } from 'gatsby-plugin-next-seo'
const Avatar = ({ author: { avatar, twitter } }) => {
return (
@ -52,26 +49,39 @@ const IndexPage = ({
).format('MMMM Do YYYY')}`
const breadcrumb = [
['Blog', urljoin(siteUrl, '/blog')],
[post.frontmatter.title, urljoin(siteUrl, post.fields.slug)],
{ position: 1, name: 'Blog', item: urljoin(siteUrl, '/blog') },
{
position: 2,
name: post.frontmatter.title,
item: urljoin(siteUrl, post.fields.slug),
},
]
return (
<Layout>
<BreadcrumbStructuredData items={breadcrumb} />
<ArticleStructuredData
title={post.frontmatter.title}
excerpt={post.frontmatter.excerpt}
author={author.name}
<BreadcrumbJsonLd itemListElements={breadcrumb} />
<NewsArticleJsonLd
url={urljoin(siteUrl, post.fields.slug)}
siteUrl={siteUrl}
date={moment(post.fields.date)}
title={post.frontmatter.title}
keywords={post.frontmatter.categories}
datePublished={moment(post.fields.date).format()}
author={author.name}
description={post.frontmatter.excerpt}
/>
<Meta
<SEO
title={post.frontmatter.title}
description={post.frontmatter.excerpt}
keywords={post.frontmatter.categories}
author={author.twitter}
slug={post.fields.slug}
openGraph={{
title: post.frontmatter.title,
description: post.frontmatter.excerpt,
url: urljoin(siteUrl, post.fields.slug),
type: 'article',
article: {
publishedTime: moment(post.fields.date).format(),
authors: [`https://www.twitter.com/${author.twitter}`],
tags: post.frontmatter.categories,
},
}}
/>
<Main>
<div

View File

@ -1,26 +1,54 @@
/** @jsx jsx */
import { jsx, Styled } from 'theme-ui'
import { MDXRenderer } from 'gatsby-plugin-mdx'
import { graphql, Link } from 'gatsby'
import { graphql, Link, withPrefix } from 'gatsby'
import Layout from '../components/layout'
import Footer from '../components/footer'
import Meta from '../components/meta'
import { ArticleJsonLd, BreadcrumbJsonLd } from 'gatsby-plugin-next-seo'
import urljoin from 'url-join'
import moment from 'moment'
import SEO from '../components/SEO'
const DocumentationPage = ({
data: {
mdx,
allFile: { nodes: files },
site: {
siteMetadata: { documentationCategories },
siteMetadata: { documentationCategories, siteUrl },
},
},
}) => {
const post = mdx
const page = mdx
return (
<Layout>
<Meta
title={post.frontmatter.name}
description={post.frontmatter.excerpt}
<SEO
title={page.frontmatter.name}
description={page.frontmatter.excerpt}
/>
<ArticleJsonLd
url={urljoin(siteUrl, page.fields.slug)}
headline={page.frontmatter.name}
description={page.frontmatter.excerpt}
images={[urljoin(siteUrl, withPrefix('squared-logo.png'))]}
authorName="Tuist"
publisherName="Tuist"
publisherLogo={urljoin(siteUrl, withPrefix('squared-logo.png'))}
datePublished={moment().format()}
dateModified={moment().format()}
/>
<BreadcrumbJsonLd
itemListElements={[
{
position: 1,
name: 'Documentation',
item: urljoin(siteUrl, 'docs'),
},
{
position: 2,
name: page.frontmatter.name,
item: urljoin(siteUrl, page.fields.slug),
},
]}
/>
<div
sx={{ display: 'flex', flexDirection: ['column', 'row'], flex: '1' }}
@ -77,7 +105,7 @@ const DocumentationPage = ({
boxShadow: theme => `-1px -1px 12px -4px ${theme.colors.gray5}`,
}}
>
<MDXRenderer>{post.body}</MDXRenderer>
<MDXRenderer>{page.body}</MDXRenderer>
</div>
</div>
<Footer />

View File

@ -831,14 +831,7 @@
core-js-pure "^3.0.0"
regenerator-runtime "^0.13.2"
"@babel/runtime@^7.1.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.4", "@babel/runtime@^7.7.6":
version "7.7.6"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.7.6.tgz#d18c511121aff1b4f2cd1d452f1bac9601dd830f"
integrity sha512-BWAJxpNVa0QlE5gZdWjSxXtemZyZ9RmrmVozxt3NUXeZhVIJ5ANyqmMc0JDrivBZyxUuQvFxlvH4OWWOogGfUw==
dependencies:
regenerator-runtime "^0.13.2"
"@babel/runtime@^7.5.1":
"@babel/runtime@^7", "@babel/runtime@^7.1.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.4", "@babel/runtime@^7.7.6":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.3.tgz#0811944f73a6c926bb2ad35e918dcc1bfab279f1"
integrity sha512-fVHx1rzEmwB130VTkLnxR+HmxcTjGzH12LYQcFFoBwakMd3aOMD4OsRN7tGG/UOYE2ektgFrS8uACAoRk1CY0w==
@ -1765,7 +1758,7 @@
prop-types "^15.7.2"
react-is "^16.6.3"
"@styled-system/css@^5.0.16", "@styled-system/css@^5.0.23":
"@styled-system/css@^5.0.16":
version "5.1.4"
resolved "https://registry.yarnpkg.com/@styled-system/css/-/css-5.1.4.tgz#fc51d0789a69b3831e00e6f8daf9f1d345eebdc3"
integrity sha512-79IFT37Kxb6dlbx/0hwIGOakNHkK5oU3cMypGziShnEK8WMgK/+vuAi4MHO7uLI+FZ5U8MGYvGY9Gtk0mBzxSg==
@ -1777,111 +1770,129 @@
dependencies:
defer-to-connect "^1.0.1"
"@theme-ui/color@^0.2.53":
version "0.2.53"
resolved "https://registry.yarnpkg.com/@theme-ui/color/-/color-0.2.53.tgz#d4ac58b99639d1370c7e541bfeebb69f0036ca0c"
integrity sha512-Xz9146bG+tYZ9cJslAk1oq7hmlHmpso2bKLpjAW9O9382gWGNNoie44W0xi6XkVvDXtmnNei4nM/H5Wbtu9V0w==
"@theme-ui/color@^0.3.1":
version "0.3.1"
resolved "https://registry.yarnpkg.com/@theme-ui/color/-/color-0.3.1.tgz#fceffc0be305ad6ca6be9755ac9cafc955b1e6f5"
integrity sha512-Bgew6etspp087AdrKuMpcWjIQDQ8UTp28wtHCXgi1Ip5ClFn9i91DvaKgqXk9UwQJFL/IwydX8Bks/+u9tf0WA==
dependencies:
"@styled-system/css" "^5.0.23"
"@theme-ui/css" "^0.3.1"
polished "^3.4.1"
"@theme-ui/css@^0.3.1":
version "0.3.1"
resolved "https://registry.yarnpkg.com/@theme-ui/css/-/css-0.3.1.tgz#b85c7e8fae948dc0de65aa30b853368993e25cb3"
integrity sha512-QB2/fZBpo4inaLHL3OrB8NOBgNfwnj8GtHzXWHb9iQSRjmtNX8zPXBe32jLT7qQP0+y8JxPT4YChZIkm5ZyIdg==
"@theme-ui/preset-base@^0.2.44":
version "0.2.44"
resolved "https://registry.yarnpkg.com/@theme-ui/preset-base/-/preset-base-0.2.44.tgz#ae8e938288a534f129111a56b1971e1cc12ddc9d"
integrity sha512-g2BaHKz5/32wHIFoRifHxYikBJICqYx+weypA/zwz+d7rZTE9q+EgTvlFSdVlf1HtR2a3uLB1lp4se0tlI2zwg==
"@theme-ui/preset-bootstrap@^0.2.40":
version "0.2.40"
resolved "https://registry.yarnpkg.com/@theme-ui/preset-bootstrap/-/preset-bootstrap-0.2.40.tgz#6494fff018ce20c6cce4e4c06a7aca3c8df6b635"
integrity sha512-eIWrmxDYDhw0A0FfiUScuhIVR7xlq/CS56nzidsdKl5R9kBg5K1pcyHPGQrW6jLDnhFL6bOqZuvzchvHXooSew==
"@theme-ui/preset-base@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@theme-ui/preset-base/-/preset-base-0.3.0.tgz#853a78195beead04ece4391c5672d690899bec47"
integrity sha512-bFCYoxfe/Ugr5k5BEy3Gv5jg8+idZHWDt/NM95jgla4qITlaOAMuEL6afoQ5HXbJjp5TnZFZ73qO6cKI8/BFfQ==
"@theme-ui/preset-bulma@^0.2.44":
version "0.2.44"
resolved "https://registry.yarnpkg.com/@theme-ui/preset-bulma/-/preset-bulma-0.2.44.tgz#6ee9f7de30471b7b1d6dde7eb00b7ed4c988b37a"
integrity sha512-gJ5daQzQZPQVcr34mWE3R2vf9ASRpb7Xjd//FVwcwKn95ZdaeLTAksW9FMDisWtrmF7O8NiF1JHSHnu6i9M3dQ==
"@theme-ui/preset-bootstrap@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@theme-ui/preset-bootstrap/-/preset-bootstrap-0.3.0.tgz#0a50dff50eb2b444dce1e0273cf359026ef4e10c"
integrity sha512-UoBvNNo4JMsdVPuF3tzEPhZu6T1vuFTEDPUwrannaq2ycFfZZVBfr0YaDNtM+n5lcPp3DBXvZrMjDCURteGjVA==
"@theme-ui/preset-bulma@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@theme-ui/preset-bulma/-/preset-bulma-0.3.0.tgz#e7a13222da030052607d48a61b2948ad4761d9c3"
integrity sha512-9p7SWvEzqfOVqwzpXFn6xB9fiPj3aomb6gG79FoaSzXRv+ATRtsQam9o8GWhGljiQbNazbxFd95RUP92sflncw==
dependencies:
"@theme-ui/preset-base" "^0.3.0"
"@theme-ui/preset-dark@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@theme-ui/preset-dark/-/preset-dark-0.3.0.tgz#bce2a12768b64e730ff7307145809fcabac16da1"
integrity sha512-biKyaGTy4awA+JbeUO4o2TzkeB4i1nregukS9BUOrub7I4jrKjPvjIlHxlU2uiQIYbxKq4n/QqBIsCbTDdg74w==
"@theme-ui/preset-deep@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@theme-ui/preset-deep/-/preset-deep-0.3.0.tgz#acdf3028ef8dc5af2d20b5a82f9fed1062cf74d6"
integrity sha512-5XZckaa1nhm7afb57bCMGKR/TCaf9cjTwhNkfAaFjKVklVB4CCinS43zQ0sDadwd3oihCJmGbXyvgwBb8mp/iw==
"@theme-ui/preset-funk@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@theme-ui/preset-funk/-/preset-funk-0.3.0.tgz#c1fb55d3d49db5b147cb4b58e504e8db50312d1f"
integrity sha512-YT/2OgZwx9/FpkM+ol7ixnmusBcMRRwLxlys76cV4Ky1cbb4aL92uDHOh8QIfXilXnyoi8mLVduCMOJCd+1h2Q==
dependencies:
"@theme-ui/preset-base" "^0.3.0"
"@theme-ui/preset-future@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@theme-ui/preset-future/-/preset-future-0.3.0.tgz#cd9384749328fb49856ef3452f6da8d9796d51fa"
integrity sha512-KicYV6Ck2hE3nT+Snkk4mFhshyfrX/jgSHGrmfOGMhLjkapQYUpuIKRQMPjlm2dDtd5exbaxOOjX4edRIkqQyg==
dependencies:
"@theme-ui/preset-base" "^0.3.0"
"@theme-ui/preset-polaris@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@theme-ui/preset-polaris/-/preset-polaris-0.3.0.tgz#1e9791122d386a813928b2e6f87d3d41fe690fa1"
integrity sha512-Tw1xKIbKt28OkevT/WynXnnjHizCQI8XN6P3e3baj5VCdGSXmRJbtZXA85tgjTf6gSTQZrWnaRg2vm/wXn/Wlw==
dependencies:
"@theme-ui/preset-base" "^0.2.44"
"@theme-ui/preset-dark@^0.2.40":
version "0.2.40"
resolved "https://registry.yarnpkg.com/@theme-ui/preset-dark/-/preset-dark-0.2.40.tgz#1328b942d07a1fcdb44e593627ea560187cb375e"
integrity sha512-0Zuhrm7UmMgyxa9BRQcBwm8uA7jV4cBhbCVJfx0AO9E1OjrfjsW0kuU82BiBUY14wlqgQuleX0bcTdszXqSgzg==
"@theme-ui/preset-deep@^0.2.40":
version "0.2.40"
resolved "https://registry.yarnpkg.com/@theme-ui/preset-deep/-/preset-deep-0.2.40.tgz#1bf1def6ad8e1f0367fd111bee81e7eaf85ec816"
integrity sha512-Cj9yYj6QYhhf4MXPtIFd9lL82WzsP8C6obqBI3Fmh51VoUrW6eYbBkmD5N/qvarkMJla29jF/3FjcS6gwMsOqQ==
"@theme-ui/preset-funk@^0.2.44":
version "0.2.44"
resolved "https://registry.yarnpkg.com/@theme-ui/preset-funk/-/preset-funk-0.2.44.tgz#2860063e5c5c92f24ea1cb2ff881cad2050d4934"
integrity sha512-GEFmDXu21tGnXjYD+V5I5YPl8liWI5kEIFCuVz8awJoE8BYHPDdGo4Gm2TlZQvetiI9BIY2Lk5u/RYS2Zd7wYw==
"@theme-ui/preset-roboto@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@theme-ui/preset-roboto/-/preset-roboto-0.3.0.tgz#4b7afa633416caf597f91d3624b3166e2786b1be"
integrity sha512-7/Bjo3CeVJ73r1PcrnvGVl7TaI+gnwA5ekwfv0yifE5gJBbzvhBRnNIGcBt4aE3pd5WQ+iSKdfBBfxFjkTXQAQ==
dependencies:
"@theme-ui/preset-base" "^0.2.44"
"@theme-ui/preset-base" "^0.3.0"
"@theme-ui/preset-future@^0.2.44":
version "0.2.44"
resolved "https://registry.yarnpkg.com/@theme-ui/preset-future/-/preset-future-0.2.44.tgz#43c38ad701d6efcabc4533edf089da93178e161e"
integrity sha512-y6LipmPOYxXb3yOGWACKMTLrP+Dg6gwxWGaH3OdqAUl3LX0If+4BLubvl4VJGxfAXb7YVXxDN3Xu+etX76boWg==
"@theme-ui/preset-swiss@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@theme-ui/preset-swiss/-/preset-swiss-0.3.0.tgz#ea170bc6e29d92a2e5b6e4cf90a17c68ca6f38f9"
integrity sha512-ijH0FPHMR9tT7ZOIV73lY4VHEvah2FftwrYUt7w4UhjId8SnKBp5GitU+IG4E5mRdP8IiPRQCBrfs9IuSFxnWA==
"@theme-ui/preset-system@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@theme-ui/preset-system/-/preset-system-0.3.0.tgz#80d0feb3f8a945fe67fed3dc881e37e0ce70b3f8"
integrity sha512-mpQbwuqPZysjdpRxtMxEU7kuafzt0QmYmT8t/1OcQMCIb/qIenu/xZsCXxrJQGZcxgO4Pq5Z9FDzXbQwNQBWcA==
"@theme-ui/preset-tailwind@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@theme-ui/preset-tailwind/-/preset-tailwind-0.3.0.tgz#e916023e5111df129b8e04603250ebe5d95f87e8"
integrity sha512-Y9BA9ESkOizaN3TV9H+D1gMYgoBTWxo+YGFFfTxn3RRO8V35UUM5tHbhNUWuuu5L5h/syN9f3WENCZ/cGcuaDA==
"@theme-ui/preset-tosh@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@theme-ui/preset-tosh/-/preset-tosh-0.3.0.tgz#c368edaa88703b8552fa731605bffd668ae5cf3b"
integrity sha512-YgrRHVhekvS13VGsKSYPa+ixJXmIQ/8hLIAVq6OmJQMHmPFgu/2pp45Plb1RPNlYsZwtXisO1T6nhet/jqiJvw==
"@theme-ui/presets@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@theme-ui/presets/-/presets-0.3.0.tgz#b2636faaa86c77f6b56c6623c954c1ce665705b0"
integrity sha512-mPxsNACJZT+EAZ76vLRFUyVMopkOEgTps3l22PhxLWIT5MtPsGXDoyTmjbOVy1suHv5mYwdQdsJ2isW4nsePrA==
dependencies:
"@theme-ui/preset-base" "^0.2.44"
"@theme-ui/preset-base" "^0.3.0"
"@theme-ui/preset-bootstrap" "^0.3.0"
"@theme-ui/preset-bulma" "^0.3.0"
"@theme-ui/preset-dark" "^0.3.0"
"@theme-ui/preset-deep" "^0.3.0"
"@theme-ui/preset-funk" "^0.3.0"
"@theme-ui/preset-future" "^0.3.0"
"@theme-ui/preset-polaris" "^0.3.0"
"@theme-ui/preset-roboto" "^0.3.0"
"@theme-ui/preset-swiss" "^0.3.0"
"@theme-ui/preset-system" "^0.3.0"
"@theme-ui/preset-tailwind" "^0.3.0"
"@theme-ui/preset-tosh" "^0.3.0"
"@theme-ui/preset-roboto@^0.2.44":
version "0.2.44"
resolved "https://registry.yarnpkg.com/@theme-ui/preset-roboto/-/preset-roboto-0.2.44.tgz#465b6bb5e5785070b9347636f8ec4065aefdb243"
integrity sha512-ZEUzsfp7Yj7o8CVJucrs/uJnd39e4z98jsn6+qbF3cZBtLoUmpikQaiWsM2OgUsptvGKMusvfcfFASUCfKr35Q==
dependencies:
"@theme-ui/preset-base" "^0.2.44"
"@theme-ui/preset-swiss@^0.2.40":
version "0.2.40"
resolved "https://registry.yarnpkg.com/@theme-ui/preset-swiss/-/preset-swiss-0.2.40.tgz#4030f121ce7fcdd191732520c95c9eea68898d9c"
integrity sha512-4LHNNrvBBuNNt367haJCztBHUpcrq5VLfkjpq8cyuG8HbTcVbT/Hcad+t1B/0d0NKdE4q5paPXA+l7fv1A5q0Q==
"@theme-ui/preset-system@^0.2.40":
version "0.2.40"
resolved "https://registry.yarnpkg.com/@theme-ui/preset-system/-/preset-system-0.2.40.tgz#a84c1589cd37b33fffd8c7c46dfcbf5a8556a3fc"
integrity sha512-AI321YIipLM6++6ligmSaAFiBwaWFJWLyHZk5ma415hn2neYddQ5vXeBI7P4Tfhx208qg2ete5qGWM+Q7KA3Sw==
"@theme-ui/preset-tailwind@^0.2.42":
version "0.2.42"
resolved "https://registry.yarnpkg.com/@theme-ui/preset-tailwind/-/preset-tailwind-0.2.42.tgz#00e009827da2ffea628fa5b89002cd8cb6db805f"
integrity sha512-giQQ0xoGB2btWvLlg9GQSyRiQ6vGCpVF+uf5OxU5TJt2DGyuYckxvlG3za93o80cxHnVlm5YyO2sLNzXyQSi0w==
"@theme-ui/preset-tosh@^0.2.40":
version "0.2.40"
resolved "https://registry.yarnpkg.com/@theme-ui/preset-tosh/-/preset-tosh-0.2.40.tgz#68cf4ed5e4c748e7c1762676ddb6119653679ac1"
integrity sha512-LatVzEDlVR1pqyn9u+i4ZcZrdfWx0Hvd7ktNGLrA89RD9IX7hDHdvsr0wI6z6nrPtTNoa41YZe7xU8QzgwnpCg==
"@theme-ui/presets@^0.2.44":
version "0.2.44"
resolved "https://registry.yarnpkg.com/@theme-ui/presets/-/presets-0.2.44.tgz#5c420cea0e2841e42543cfb4f2a8b019c91d0064"
integrity sha512-jPiz3LMwNkz2FU7eqBhOqi+e1YKdFajHg5+7tmwjXkOhWYSGFGi391IFj+cTN9VEC12Og8JWLg1Aig3QL/fi2Q==
dependencies:
"@theme-ui/preset-base" "^0.2.44"
"@theme-ui/preset-bootstrap" "^0.2.40"
"@theme-ui/preset-bulma" "^0.2.44"
"@theme-ui/preset-dark" "^0.2.40"
"@theme-ui/preset-deep" "^0.2.40"
"@theme-ui/preset-funk" "^0.2.44"
"@theme-ui/preset-future" "^0.2.44"
"@theme-ui/preset-roboto" "^0.2.44"
"@theme-ui/preset-swiss" "^0.2.40"
"@theme-ui/preset-system" "^0.2.40"
"@theme-ui/preset-tailwind" "^0.2.42"
"@theme-ui/preset-tosh" "^0.2.40"
"@theme-ui/prism@^0.2.50":
version "0.2.50"
resolved "https://registry.yarnpkg.com/@theme-ui/prism/-/prism-0.2.50.tgz#0f1dafb7d71248ba26f90cddc643b414dd9f4378"
integrity sha512-FD9UtROhol8limL2XtQPfnTUfUJETUC37wYW6dSIfVCDR4l0CSbqYS+niQAqVGcuD0wtL1pbkvb/qDtUg94Ebw==
"@theme-ui/prism@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@theme-ui/prism/-/prism-0.3.0.tgz#69c2f8bf5f5c5d02f1bde5879a149f6cadab2b7c"
integrity sha512-5M3uH5zShttu5k84OfCIWrFKFTrPdck/m/1iU/9i8CMM899mIu7VsVTgaUTKkOCMoreH2u+d7Y76j4Ur2ZMzEw==
dependencies:
prism-react-renderer "^1.0.2"
"@theme-ui/typography@^0.2.46":
version "0.2.46"
resolved "https://registry.yarnpkg.com/@theme-ui/typography/-/typography-0.2.46.tgz#24e20989450a35eb715967b99cbfd8fa4291ba17"
integrity sha512-66VMjZ/7fvhwtJHwYZH4/Vl+wYpLXN2QzNU6wn/7VcBf9xJZPXEumI8wSp9P1R2eEjLAlaz8tpl1VDMGPDxsVA==
"@theme-ui/typography@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@theme-ui/typography/-/typography-0.3.0.tgz#4e9a33a9c42e7bfac3abed4f1d7f86232a4c4ec4"
integrity sha512-71xT7M73FbgqBLVISaSjjL65V8Tn75cnoIH9MAPfmLP7+YhH0BtzBWfGiuB4Rz6pHj111MNnnn1PmStgtmDYQw==
dependencies:
compass-vertical-rhythm "^1.4.5"
modularscale "^2.0.1"
@ -1998,6 +2009,13 @@
"@types/history" "*"
"@types/react" "*"
"@types/react-helmet@^5":
version "5.0.15"
resolved "https://registry.yarnpkg.com/@types/react-helmet/-/react-helmet-5.0.15.tgz#af0370617307e9f062c6aee0f1f5588224ce646e"
integrity sha512-CCjqvecDJTXRrHG8aTc2YECcQCl26za/q+NaBRvy/wtm0Uh38koM2dpv2bG1xJV4ckz3t1lm2/5KU6nt2s9BWg==
dependencies:
"@types/react" "*"
"@types/react@*":
version "16.9.16"
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.16.tgz#4f12515707148b1f53a8eaa4341dae5dfefb066d"
@ -6435,10 +6453,10 @@ gatsby-cli@^2.8.26:
ink "^2.6.0"
ink-spinner "^3.0.1"
gatsby-core-utils@^1.0.26:
version "1.0.26"
resolved "https://registry.yarnpkg.com/gatsby-core-utils/-/gatsby-core-utils-1.0.26.tgz#e1cbdfad498d58d677d9d74f21a1ede661b49d6f"
integrity sha512-NPflmXmyTcg3x2mp6cqp/51QeAHRKepfbf0X4erDsnVlewFJuGTe+25ZJvWkkwU2g1cPAxuwzlPe0jOL92iU4A==
gatsby-core-utils@^1.0.26, gatsby-core-utils@^1.0.28:
version "1.0.28"
resolved "https://registry.yarnpkg.com/gatsby-core-utils/-/gatsby-core-utils-1.0.28.tgz#aa66e49cdcc1892e7817a32fdd806cf5e16ea309"
integrity sha512-XWKR9Rk2v6iQkmBsTLCdI3adyC9PCh1s5BQ85nlGitlgcVVQq98jZlQdcy0v9mJOrTuce0uf5RqkeGDWFLekoA==
dependencies:
ci-info "2.0.0"
node-object-hash "^2.0.0"
@ -6450,10 +6468,10 @@ gatsby-graphiql-explorer@^0.2.32:
dependencies:
"@babel/runtime" "^7.7.6"
gatsby-image@^2.2.39:
version "2.2.39"
resolved "https://registry.yarnpkg.com/gatsby-image/-/gatsby-image-2.2.39.tgz#c2982152923409139b03446c58fbaa99c15e47a2"
integrity sha512-ypb5J+ACeHoGhyw//O0ye7z8sm8yW/MVqpuZ+gkCyXWt3zexp4jjHqV3uDOkvI9fPQC/QCZLXepE7Ve77iAeEg==
gatsby-image@^2.2.40:
version "2.2.40"
resolved "https://registry.yarnpkg.com/gatsby-image/-/gatsby-image-2.2.40.tgz#2ccb965ba03ed7456a87f42fe0a04bcaa6367b6d"
integrity sha512-K1rYYMu36GSJcodFw0Z+MSkxLsyA5J5jx3HsMFcTTWL9m5yNdjiPxaepx23ijNKQ7sWvGdsOfhCciSnMVz0AFg==
dependencies:
"@babel/runtime" "^7.7.6"
object-fit-images "^3.2.4"
@ -6491,10 +6509,10 @@ gatsby-plugin-favicon@3.1.6:
html-react-parser "^0.6.4"
lodash "^4.17.11"
gatsby-plugin-feed@^2.3.26:
version "2.3.26"
resolved "https://registry.yarnpkg.com/gatsby-plugin-feed/-/gatsby-plugin-feed-2.3.26.tgz#00043a581b81b73c595b7620fe0f381d241ca8da"
integrity sha512-L2/LyCkZ+cPF2QAISszEwi2D2CuRk6ELp75d8tN7oCRDKkcSCJZND6gzIvBxNzGjEnbxtTk9OhL/1TnmJTALNQ==
gatsby-plugin-feed@^2.3.27:
version "2.3.27"
resolved "https://registry.yarnpkg.com/gatsby-plugin-feed/-/gatsby-plugin-feed-2.3.27.tgz#c3dd793bb22d62563687350adfd7cedbd84d826b"
integrity sha512-YdYyMi2k1d2vJtHq7GCFilWqHGBP74kMfv4vEjbGKnHwjUN+hu1u0ZbZHCO56OLz/Yyi+rG+pNdTDlbDFWD7lw==
dependencies:
"@babel/runtime" "^7.7.6"
"@hapi/joi" "^15.1.1"
@ -6502,13 +6520,13 @@ gatsby-plugin-feed@^2.3.26:
lodash.merge "^4.6.2"
rss "^1.2.2"
gatsby-plugin-manifest@2.2.37:
version "2.2.37"
resolved "https://registry.yarnpkg.com/gatsby-plugin-manifest/-/gatsby-plugin-manifest-2.2.37.tgz#ea385bbd453d8455d3c4715c4f3b1e8c1313a97b"
integrity sha512-RT7ALsnq/H1gZrmyHJ6jJFn+Z/OzDJgiwVlP5qIUmAzAoqC95bq3XbXB5yHCePAvZjKn0liIRjUyp0OHVKQlNQ==
gatsby-plugin-manifest@2.2.41:
version "2.2.41"
resolved "https://registry.yarnpkg.com/gatsby-plugin-manifest/-/gatsby-plugin-manifest-2.2.41.tgz#c6952e466b163145b74aa071822a97a797a68e01"
integrity sha512-eKbnpDSLbGUzmnfE65uLit/J8XS4LYslyRHtqMhTja6JcdzjXVWFsO9043tqUXfL8+JBw5PccCxZM8BIN23+Vw==
dependencies:
"@babel/runtime" "^7.7.6"
gatsby-core-utils "^1.0.26"
gatsby-core-utils "^1.0.28"
semver "^5.7.1"
sharp "^0.23.4"
@ -6571,14 +6589,24 @@ gatsby-plugin-netlify@^2.1.31:
lodash "^4.17.15"
webpack-assets-manifest "^3.1.1"
gatsby-plugin-offline@^3.0.32:
version "3.0.32"
resolved "https://registry.yarnpkg.com/gatsby-plugin-offline/-/gatsby-plugin-offline-3.0.32.tgz#9907421e7359f79b22da7f654c4a69749cf136c5"
integrity sha512-TPVRhD4LaVcCLBMzdbPUZr4bb+dv1h/HMn7ryFozFafYJ1p7XuKv2VHtqNYpfL1UKiZnWIJmEWXxfcWszLoC4w==
gatsby-plugin-next-seo@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/gatsby-plugin-next-seo/-/gatsby-plugin-next-seo-1.4.0.tgz#1014250ab3b5822e0811a27a6fc6650880e6e148"
integrity sha512-w8kIpihLbiqPxlGCAdD5/euFswCOssjgwEtjlGV5Gi3qg+TARD9P6U5K0sAAe/yI5fAKb1FvFum44htM3BfXxQ==
dependencies:
"@babel/runtime" "^7"
"@types/react-helmet" "^5"
schema-dts "0.4.5"
type-fest "^0.8"
gatsby-plugin-offline@^3.0.34:
version "3.0.34"
resolved "https://registry.yarnpkg.com/gatsby-plugin-offline/-/gatsby-plugin-offline-3.0.34.tgz#6aae8c4cf48a683e7ee107cded8ddd29cf4036bb"
integrity sha512-iyps2wM7ktB9osJ8jCxM5gZn3BK3JzKrIgv6R3mD/Gqc9XcF4Ew367AIjNowPQclGfcBHbykjrwlK5T5vMwNEg==
dependencies:
"@babel/runtime" "^7.7.6"
cheerio "^1.0.0-rc.3"
gatsby-core-utils "^1.0.26"
gatsby-core-utils "^1.0.28"
glob "^7.1.6"
idb-keyval "^3.2.0"
lodash "^4.17.15"
@ -6597,10 +6625,10 @@ gatsby-plugin-page-creator@^2.1.38:
lodash "^4.17.15"
micromatch "^3.1.10"
gatsby-plugin-react-helmet@^3.1.21:
version "3.1.21"
resolved "https://registry.yarnpkg.com/gatsby-plugin-react-helmet/-/gatsby-plugin-react-helmet-3.1.21.tgz#659f31020295f42d017e2eb03175d73516ab824d"
integrity sha512-6LZ2LEYTwqD+ZqyCH55mVpk2xEXbQoCTfijP1W4ZCQsKtpWGJP+vyd6b96FWVyEb2k5LsQ1u+jk4R8xXULSX+w==
gatsby-plugin-react-helmet@^3.1.22:
version "3.1.22"
resolved "https://registry.yarnpkg.com/gatsby-plugin-react-helmet/-/gatsby-plugin-react-helmet-3.1.22.tgz#f4c148dfe964af1e1364e6bcd9a4cd3226e8cd7a"
integrity sha512-eG57C7Rm84dOpaFYxqQsKSzP0ge/6SnAEsPH5JcAcJ7vETtn3rCS6SB8qs+Nk/jhziAjdGjBw3CSJnOkg/QUJA==
dependencies:
"@babel/runtime" "^7.7.6"
@ -6644,20 +6672,20 @@ gatsby-plugin-sharp@2.4.0:
svgo "1.3.2"
uuid "^3.3.3"
gatsby-plugin-sitemap@^2.2.26:
version "2.2.26"
resolved "https://registry.yarnpkg.com/gatsby-plugin-sitemap/-/gatsby-plugin-sitemap-2.2.26.tgz#d0b9a9064966fd31b9ae46e769b05c904c20fd2f"
integrity sha512-0kqMM6zD4IWha7Af6kfzwk78870S8XGpOVNJojgtQ83eUu6mKQXdRuae/i52hjclSDsEprbvUfQT0yMxgOotGw==
gatsby-plugin-sitemap@^2.2.27:
version "2.2.27"
resolved "https://registry.yarnpkg.com/gatsby-plugin-sitemap/-/gatsby-plugin-sitemap-2.2.27.tgz#efdf0407ae754f806a172785eaab468e8baf42a9"
integrity sha512-Iq/nUwMhdl76CPkkUEcprKnGyc3Viaqiaxgn9L2kfF7LhggKVdSKYpJjBa80B0856cDQRJVkatumD43+kf3NZA==
dependencies:
"@babel/runtime" "^7.7.6"
minimatch "^3.0.4"
pify "^3.0.0"
sitemap "^1.13.0"
gatsby-plugin-theme-ui@^0.2.53:
version "0.2.53"
resolved "https://registry.yarnpkg.com/gatsby-plugin-theme-ui/-/gatsby-plugin-theme-ui-0.2.53.tgz#57a52339e50ede7ef4df0b1b5593d360b56b597d"
integrity sha512-AlQC+uC9lvrP3LlGsLe0f0azp7B5c49qWl4b3FDj8xbravBoqFmJT7XrNTpYYbxnCnx/K1v0QtwP8qindw0S2g==
gatsby-plugin-theme-ui@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/gatsby-plugin-theme-ui/-/gatsby-plugin-theme-ui-0.3.0.tgz#ab84216536ae45abe09a6edf24156b9dbf50d6a5"
integrity sha512-Q2tS8EeYMy7AAtt6hvDtEsd1uwrLMjkDNqabyXhAo38AFoWQ0oKtq9u1YqbiRvp1TK06pAMPQQ3to48LAqc9Cw==
gatsby-react-router-scroll@^2.1.20:
version "2.1.20"
@ -6697,10 +6725,10 @@ gatsby-remark-images@^3.1.42:
unist-util-select "^1.5.0"
unist-util-visit-parents "^2.1.2"
gatsby-remark-smartypants@^2.1.20:
version "2.1.20"
resolved "https://registry.yarnpkg.com/gatsby-remark-smartypants/-/gatsby-remark-smartypants-2.1.20.tgz#d081d93d50b0f1b48dc2b4d660ef6c2b72c00f97"
integrity sha512-kvCW8GuXLoqmWp+JQeMuK5x4u+6ObrOInup+hVsxdv5J4FGYACXQYGGf+QgeC7vsBh15J+ha0ZIAOBwB/tSGSQ==
gatsby-remark-smartypants@^2.1.21:
version "2.1.21"
resolved "https://registry.yarnpkg.com/gatsby-remark-smartypants/-/gatsby-remark-smartypants-2.1.21.tgz#2b6ba95a0da4c1f069e3375bc20da8d216618e7e"
integrity sha512-iV+DaYeoo7w+IDv21JD0b3jrbxV7Q/0enJj7zB58q8o/lmbdTeFcuwJ/BJI5RW3TsqSiUeFct8n9FkdoAVhK3g==
dependencies:
"@babel/runtime" "^7.7.6"
retext "^5.0.0"
@ -6715,10 +6743,10 @@ gatsby-remark-smartypants@^2.1.20:
jimp "^0.6.0"
path "^0.12.7"
gatsby-source-filesystem@^2.1.46:
version "2.1.46"
resolved "https://registry.yarnpkg.com/gatsby-source-filesystem/-/gatsby-source-filesystem-2.1.46.tgz#ab90c4520f0664218c7334dbec6cd90acc85cf9d"
integrity sha512-5LC90+qMKK+/hJzZxKcazx5JvvOO1wHH+ZE7JDSHSzZ1QB+RKWnkvG4a7n6dyiFybo1HN3ql5YQXQLkBEiIfMg==
gatsby-source-filesystem@^2.1.48:
version "2.1.48"
resolved "https://registry.yarnpkg.com/gatsby-source-filesystem/-/gatsby-source-filesystem-2.1.48.tgz#85bd83b953278a7597be38b2e8cd9c69de32fa64"
integrity sha512-m1RIYDoV9rhMe2p0ZJA1ae4IIX+iIJCMpNnQiQVtTf+mfihiWyDAdi4lsWzJUxPt8FQwDgUG7GzY3sAoGctvzQ==
dependencies:
"@babel/runtime" "^7.7.6"
better-queue "^3.8.10"
@ -6726,8 +6754,8 @@ gatsby-source-filesystem@^2.1.46:
chokidar "3.3.0"
file-type "^12.4.0"
fs-extra "^8.1.0"
gatsby-core-utils "^1.0.26"
got "^7.1.0"
gatsby-core-utils "^1.0.28"
got "^8.3.2"
md5-file "^3.2.3"
mime "^2.4.4"
pretty-bytes "^5.3.0"
@ -6759,10 +6787,10 @@ gatsby-telemetry@^1.1.47:
stack-utils "1.0.2"
uuid "3.3.3"
gatsby-transformer-yaml@^2.2.23:
version "2.2.23"
resolved "https://registry.yarnpkg.com/gatsby-transformer-yaml/-/gatsby-transformer-yaml-2.2.23.tgz#44c478c6bd1c467a27ccd43db6fc97e7087a4840"
integrity sha512-fGf+LsS6UqzLl/c64qEzyFZhv6uc+zcWBsVFNwKU3hTYf8dd96roo5JdnfiI+kaXsUZ926UOqZsxoZA6Of/0bQ==
gatsby-transformer-yaml@^2.2.24:
version "2.2.24"
resolved "https://registry.yarnpkg.com/gatsby-transformer-yaml/-/gatsby-transformer-yaml-2.2.24.tgz#93989ac6cf7a1c22c597f7b6559ccfff17ed8e84"
integrity sha512-6+uQwzIaMRc6WPIFJIaR1MqAULpev8+eo6pdZFm/6/MAMSb9t6A9dge8+V4ahhGhLJr26HI4d2/JpnzLihKTTg==
dependencies:
"@babel/runtime" "^7.7.6"
js-yaml "^3.13.1"
@ -7182,7 +7210,7 @@ got@8.3.2, got@^8.3.1, got@^8.3.2:
url-parse-lax "^3.0.0"
url-to-options "^1.0.1"
got@^7.0.0, got@^7.1.0:
got@^7.0.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a"
integrity sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw==
@ -10965,10 +10993,10 @@ pnp-webpack-plugin@^1.5.0:
dependencies:
ts-pnp "^1.1.2"
polished@^3.4.1, polished@^3.4.2:
version "3.4.2"
resolved "https://registry.yarnpkg.com/polished/-/polished-3.4.2.tgz#b4780dad81d64df55615fbfc77acb52fd17d88cd"
integrity sha512-9Rch6iMZckABr6EFCLPZsxodeBpXMo9H4fRlfR/9VjMEyy5xpo1/WgXlJGgSjPyVhEZNycbW7UmYMNyWS5MI0g==
polished@^3.4.1, polished@^3.4.4:
version "3.4.4"
resolved "https://registry.yarnpkg.com/polished/-/polished-3.4.4.tgz#ac8cd6e704887398f3b802718f9d389b9ea4307b"
integrity sha512-x9PKeExyI9AhWrJP3Q57I1k7GInujjiVBJMPFmycj9hX1yCOo/X9eu9eZwxgOziiXge3WbFQ5XOmkzunOntBSA==
dependencies:
"@babel/runtime" "^7.6.3"
@ -11809,11 +11837,22 @@ react-error-overlay@^3.0.0:
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-3.0.0.tgz#c2bc8f4d91f1375b3dad6d75265d51cd5eeaf655"
integrity sha512-XzgvowFrwDo6TWcpJ/WTiarb9UI6lhA4PMzS7n1joK3sHfBBBOQHUc0U4u57D6DWO9vHv6lVSWx2Q/Ymfyv4hw==
react-fast-compare@^2.0.2:
react-fast-compare@^2.0.2, react-fast-compare@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9"
integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==
react-helmet-async@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/react-helmet-async/-/react-helmet-async-1.0.4.tgz#079ef10b7fefcaee6240fefd150711e62463cc97"
integrity sha512-KTGHE9sz8N7+fCkZ2a3vzXH9eIkiTNhL2NhKR7XzzQl3WsGlCHh76arauJUIiGdfhjeMp7DY7PkASAmYFXeJYg==
dependencies:
"@babel/runtime" "^7.3.4"
invariant "^2.2.4"
prop-types "^15.7.2"
react-fast-compare "^2.0.4"
shallowequal "^1.1.0"
react-helmet@^5.2.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/react-helmet/-/react-helmet-5.2.1.tgz#16a7192fdd09951f8e0fe22ffccbf9bb3e591ffa"
@ -12720,6 +12759,11 @@ scheduler@^0.18.0:
loose-envify "^1.1.0"
object-assign "^4.1.1"
schema-dts@0.4.5:
version "0.4.5"
resolved "https://registry.yarnpkg.com/schema-dts/-/schema-dts-0.4.5.tgz#7ba5f705bf21301756d8f32acb17d7f9b7f279d1"
integrity sha512-LiWBc0/7dGwd5NqWfLBxOwqhBkH2cCuctMKpIjXLI0qD0gu6XqpKGPDDUhAgZOr2BlvYQHhdrEEM1wmTEWWqMw==
schema-utils@^0.4.0, schema-utils@^0.4.5:
version "0.4.7"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.7.tgz#ba74f597d2be2ea880131746ee17d0a093c68187"
@ -13048,10 +13092,10 @@ slice-ansi@^3.0.0:
astral-regex "^2.0.0"
is-fullwidth-code-point "^3.0.0"
slug@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/slug/-/slug-2.1.0.tgz#293f8d53de7e55c15871846fd1bc36114841a8c7"
integrity sha512-Q4foEgcE7E8UB/BFg4kEzFUICoppzsbbfRjrdKiOM4Z4EFZF5tdn6amkgeaGur3kI4lMWP2BoMv7XJcKZvLg9Q==
slug@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/slug/-/slug-2.1.1.tgz#08df390d4b4d51bafb41ac0067c0c2dd70734ef2"
integrity sha512-yNGhDdS0DR0JyxnPC84qIx/Vd01RHVY4guJeBqBNdBoOLNWnzw5zkWJvxVSmsuUb92bikdnQFnw3PfGY8uZ82g==
dependencies:
unicode ">= 0.3.1"
@ -14214,7 +14258,7 @@ type-fest@^0.3.0:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.3.1.tgz#63d00d204e059474fe5e1b7c011112bbd1dc29e1"
integrity sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==
type-fest@^0.8.1:
type-fest@^0.8, type-fest@^0.8.1:
version "0.8.1"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==