Update static.zip, automate its release process (#60)

`dev.js` entrypoint has been updated to include the recent JavaScriptKit runtime fix: https://github.com/swiftwasm/JavaScriptKit/pull/19

I've also added a new subcommand to `carton-release`, which is now able to automaticallly create a new `static.zip` and record updated hashes in the source code. It still doesn't upload the archive automatically to previous release assets, but I wonder if that should be done manually anyway, at least until we have some kind of tests that verify the whole process end-to-end.

Additionally, since the new runtime is not compatible with the old Swift parts of JavaScriptKit, `carton dev` now checks the revision of JavaScriptKit that projects have specified in their `Package.swift`. It doesn't block the build process, but I hope it gives enough warning about the incompatibility.
This commit is contained in:
Max Desiatov 2020-07-19 21:53:04 +01:00 committed by GitHub
parent 816984a674
commit 749a17d730
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 251 additions and 101 deletions

4
.gitignore vendored
View File

@ -94,3 +94,7 @@ Public
# Node.js # Node.js
node_modules node_modules
# carton
static
static.zip

View File

@ -66,6 +66,7 @@ let package = Package(
.product(name: "ArgumentParser", package: "swift-argument-parser"), .product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "AsyncHTTPClient", package: "async-http-client"), .product(name: "AsyncHTTPClient", package: "async-http-client"),
.product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"), .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"),
"CartonHelpers",
] ]
), ),
.testTarget( .testTarget(

View File

@ -60,6 +60,7 @@ let package = Package(
.product(name: "ArgumentParser", package: "swift-argument-parser"), .product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "AsyncHTTPClient", package: "async-http-client"), .product(name: "AsyncHTTPClient", package: "async-http-client"),
.product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"), .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"),
"CartonHelpers",
] ]
), ),
.testTarget( .testTarget(

View File

@ -23,6 +23,24 @@ struct Package: Codable {
let name: String let name: String
let products: [Product] let products: [Product]
let targets: [Target] let targets: [Target]
let dependencies: [Dependency]?
struct Dependency: Codable {
let name: String
let requirement: Requirement
struct Requirement: Codable {
let range: [Range]?
let branch: [String]?
let revision: [String]?
let exact: [String]?
struct Range: Codable {
let lowerBound: String
let upperBound: String
}
}
}
init(with swiftPath: AbsolutePath, _ terminal: TerminalController) throws { init(with swiftPath: AbsolutePath, _ terminal: TerminalController) throws {
terminal.write("\nParsing package manifest: ", inColor: .yellow) terminal.write("\nParsing package manifest: ", inColor: .yellow)

View File

@ -16,6 +16,8 @@ import CartonHelpers
import Foundation import Foundation
import TSCBasic import TSCBasic
private let compatibleJSKitRevision = "c90e82f"
enum ToolchainError: Error, CustomStringConvertible { enum ToolchainError: Error, CustomStringConvertible {
case directoryDoesNotExist(AbsolutePath) case directoryDoesNotExist(AbsolutePath)
case invalidResponseCode(UInt) case invalidResponseCode(UInt)
@ -148,6 +150,23 @@ public final class Toolchain {
guard let product = try inferDevProduct(hint: product) guard let product = try inferDevProduct(hint: product)
else { throw ToolchainError.noExecutableProduct } else { throw ToolchainError.noExecutableProduct }
if let package = package,
let jsKit = package.dependencies?.first(where: { $0.name == "JavaScriptKit" }),
jsKit.requirement.revision != ["c90e82f"] {
let version = jsKit.requirement.revision.flatMap { " (\($0[0]))" } ?? ""
terminal.write(
"""
This revision of JavaScriptKit\(version) is not known to be compatible with \
carton \(cartonVersion). Please specify a JavaScriptKit dependency to revision \
\(compatibleJSKitRevision) in your `Package.swift`.\n
""",
inColor: .red
)
}
let binPath = try inferBinPath() let binPath = try inferBinPath()
let mainWasmPath = binPath.appending(component: product) let mainWasmPath = binPath.appending(component: product)
terminal.logLookup("- development binary to serve: ", mainWasmPath.pathString) terminal.logLookup("- development binary to serve: ", mainWasmPath.pathString)

View File

@ -0,0 +1,68 @@
// Copyright 2020 Carton contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import ArgumentParser
import AsyncHTTPClient
import TSCBasic
struct Formula: ParsableCommand {
@Argument() var version: String
func run() throws {
let archiveURL = "https://github.com/swiftwasm/carton/archive/\(version).tar.gz"
let client = HTTPClient(eventLoopGroupProvider: .createNew)
let response: HTTPClient.Response = try await {
client.get(url: archiveURL).whenComplete($0)
}
try client.syncShutdown()
guard
var body = response.body,
let bytes = body.readBytes(length: body.readableBytes)
else { fatalError("download failed for URL \(archiveURL)") }
let downloadedArchive = ByteString(bytes)
let sha256 = SHA256().hash(downloadedArchive).hexadecimalRepresentation
let formula = #"""
class Carton < Formula
desc "📦 Watcher, bundler, and test runner for your SwiftWasm apps"
homepage "https://carton.dev"
head "https://github.com/swiftwasm/carton.git"
depends_on :xcode => "11.4"
stable do
version "\#(version)"
url "https://github.com/swiftwasm/carton/archive/#{version}.tar.gz"
sha256 "\#(sha256)"
end
def install
system "swift", "build", "--disable-sandbox", "-c", "release"
system "mv", ".build/release/carton", "carton"
bin.install "carton"
end
test do
system "carton -h"
end
end
"""#
print(formula)
}
}

View File

@ -0,0 +1,74 @@
// Copyright 2020 Carton contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import ArgumentParser
import CartonHelpers
import TSCBasic
struct HashArchive: ParsableCommand {
/** Converts a hexadecimal hash string to Swift code that represents a static
*/
private func arrayString(from hash: String) -> String {
precondition(hash.count == 64)
let commaSeparated = stride(from: 0, to: hash.count, by: 2)
.map { hash.dropLast(hash.count - $0 - 2).suffix(2) }
.map { "0x\($0)" }
.joined(separator: ", ")
precondition(commaSeparated.count == 190)
return """
\(commaSeparated.prefix(95))
\(commaSeparated.suffix(94))
"""
}
func run() throws {
let terminal = TerminalController(stream: stdoutStream)!
let cwd = localFileSystem.currentWorkingDirectory!
try ProcessRunner(["npm", "run", "build"], terminal).waitUntilFinished()
let devHash = try SHA256().hash(
localFileSystem.readFileContents(AbsolutePath(cwd, RelativePath("static/dev.js")))
).hexadecimalRepresentation.uppercased()
try ProcessRunner(["zip", "static.zip", "static/*"], terminal).waitUntilFinished()
let archiveHash = try SHA256().hash(
localFileSystem.readFileContents(AbsolutePath(
localFileSystem.currentWorkingDirectory!,
RelativePath("static.zip")
))
).hexadecimalRepresentation.uppercased()
let hashes = """
import TSCBasic
let devDependencySHA256 = ByteString([
\(arrayString(from: devHash))
])
let staticArchiveHash = ByteString([
\(arrayString(from: archiveHash)),
])
"""
try localFileSystem.writeFileContents(
AbsolutePath(cwd, RelativePath("Sources/carton/Server/StaticArchive.swift")),
bytes: ByteString(encodingAsUTF8: hashes)
)
}
}

View File

@ -13,58 +13,12 @@
// limitations under the License. // limitations under the License.
import ArgumentParser import ArgumentParser
import AsyncHTTPClient
import TSCBasic
struct CartonRelease: ParsableCommand { struct CartonRelease: ParsableCommand {
@Argument() var version: String static let configuration = CommandConfiguration(
abstract: "Carton release automation utility",
func run() throws { subcommands: [Formula.self, HashArchive.self]
let archiveURL = "https://github.com/swiftwasm/carton/archive/\(version).tar.gz" )
let client = HTTPClient(eventLoopGroupProvider: .createNew)
let response: HTTPClient.Response = try await {
client.get(url: archiveURL).whenComplete($0)
}
try client.syncShutdown()
guard
var body = response.body,
let bytes = body.readBytes(length: body.readableBytes)
else { fatalError("download failed for URL \(archiveURL)") }
let downloadedArchive = ByteString(bytes)
let sha256 = SHA256().hash(downloadedArchive).hexadecimalRepresentation
let formula = #"""
class Carton < Formula
desc "📦 Watcher, bundler, and test runner for your SwiftWasm apps"
homepage "https://carton.dev"
head "https://github.com/swiftwasm/carton.git"
depends_on :xcode => "11.4"
stable do
version "\#(version)"
url "https://github.com/swiftwasm/carton/archive/#{version}.tar.gz"
sha256 "\#(sha256)"
end
def install
system "swift", "build", "--disable-sandbox", "-c", "release"
system "mv", ".build/release/carton", "carton"
bin.install "carton"
end
test do
system "carton -h"
end
end
"""#
print(formula)
}
} }
CartonRelease.main() CartonRelease.main()

View File

@ -16,7 +16,7 @@ import ArgumentParser
import CartonHelpers import CartonHelpers
struct Carton: ParsableCommand { struct Carton: ParsableCommand {
static var configuration = CommandConfiguration( static let configuration = CommandConfiguration(
abstract: "📦 Watcher, bundler, and test runner for your SwiftWasm apps.", abstract: "📦 Watcher, bundler, and test runner for your SwiftWasm apps.",
version: cartonVersion, version: cartonVersion,
subcommands: [Init.self, Dev.self, SDK.self, Test.self] subcommands: [Init.self, Dev.self, SDK.self, Test.self]

View File

@ -24,10 +24,7 @@ import TSCBasic
private let dependency = Dependency( private let dependency = Dependency(
fileName: "dev.js", fileName: "dev.js",
sha256: ByteString([ sha256: devDependencySHA256
0xFA, 0x06, 0xE2, 0xA3, 0x2D, 0x45, 0xB9, 0xBB, 0x95, 0x9A, 0x89, 0x64, 0x3F, 0x6D, 0xAF, 0x1C,
0xF5, 0x49, 0xFC, 0x34, 0x59, 0xC5, 0xE0, 0xA6, 0x01, 0x59, 0xEB, 0x0C, 0xE6, 0xB2, 0x0B, 0x0C,
])
) )
struct Dev: ParsableCommand { struct Dev: ParsableCommand {
@ -40,7 +37,7 @@ struct Dev: ParsableCommand {
@Flag(help: "When specified, build in the release mode.") @Flag(help: "When specified, build in the release mode.")
var release = false var release = false
static var configuration = CommandConfiguration( static let configuration = CommandConfiguration(
abstract: "Watch the current directory, host the app, rebuild on change." abstract: "Watch the current directory, host the app, rebuild on change."
) )

View File

@ -18,7 +18,7 @@ import SwiftToolchain
import TSCBasic import TSCBasic
struct Init: ParsableCommand { struct Init: ParsableCommand {
static var configuration = CommandConfiguration( static let configuration = CommandConfiguration(
abstract: "Create a Swift package for a new SwiftWasm project.", abstract: "Create a Swift package for a new SwiftWasm project.",
subcommands: [ListTemplates.self] subcommands: [ListTemplates.self]
) )
@ -49,9 +49,9 @@ struct Init: ParsableCommand {
terminal.write(" in ") terminal.write(" in ")
terminal.write("\(name)\n", inColor: .cyan) terminal.write("\(name)\n", inColor: .cyan)
guard let packagePath = self.name == nil ? guard let packagePath = self.name == nil ?
localFileSystem.currentWorkingDirectory : localFileSystem.currentWorkingDirectory :
AbsolutePath(name, relativeTo: currentDir) AbsolutePath(name, relativeTo: currentDir)
else { else {
terminal.write("Path to project could be created.\n", inColor: .red) terminal.write("Path to project could be created.\n", inColor: .red)
return return

View File

@ -17,7 +17,7 @@ import SwiftToolchain
import TSCBasic import TSCBasic
struct Install: ParsableCommand { struct Install: ParsableCommand {
static var configuration = CommandConfiguration( static let configuration = CommandConfiguration(
abstract: "Install new Swift toolchain/SDK." abstract: "Install new Swift toolchain/SDK."
) )

View File

@ -16,7 +16,7 @@ import ArgumentParser
import TSCBasic import TSCBasic
struct ListTemplates: ParsableCommand { struct ListTemplates: ParsableCommand {
static var configuration = CommandConfiguration( static let configuration = CommandConfiguration(
abstract: "List the available templates" abstract: "List the available templates"
) )

View File

@ -16,7 +16,7 @@ import ArgumentParser
import TSCBasic import TSCBasic
struct Local: ParsableCommand { struct Local: ParsableCommand {
static var configuration = CommandConfiguration(abstract: """ static let configuration = CommandConfiguration(abstract: """
Prints SDK version used for the current project or saves it \ Prints SDK version used for the current project or saves it \
in the `.swift-version` file if a version is passed as an argument. in the `.swift-version` file if a version is passed as an argument.
""") """)

View File

@ -15,7 +15,7 @@
import ArgumentParser import ArgumentParser
struct SDK: ParsableCommand { struct SDK: ParsableCommand {
static var configuration = CommandConfiguration( static let configuration = CommandConfiguration(
abstract: "Manage installed Swift toolchains and SDKs.", abstract: "Manage installed Swift toolchains and SDKs.",
subcommands: [Install.self, Versions.self, Local.self] subcommands: [Install.self, Versions.self, Local.self]
) )

View File

@ -23,7 +23,7 @@ import SwiftToolchain
import TSCBasic import TSCBasic
struct Test: ParsableCommand { struct Test: ParsableCommand {
static var configuration = CommandConfiguration(abstract: "Run the tests in a WASI environment.") static let configuration = CommandConfiguration(abstract: "Run the tests in a WASI environment.")
@Flag(help: "When specified, build in the release mode.") @Flag(help: "When specified, build in the release mode.")
var release = false var release = false

View File

@ -17,7 +17,7 @@ import SwiftToolchain
import TSCBasic import TSCBasic
struct Versions: ParsableCommand { struct Versions: ParsableCommand {
static var configuration = CommandConfiguration( static let configuration = CommandConfiguration(
abstract: "Lists all installed toolchains/SDKs" abstract: "Lists all installed toolchains/SDKs"
) )

View File

@ -17,12 +17,8 @@ import Foundation
import TSCBasic import TSCBasic
import TSCUtility import TSCUtility
private let archiveHash = ByteString([ private let staticArchiveURL =
0x1D, 0xCC, 0x1A, 0x8B, 0x89, 0x3C, 0xFD, 0xF6, 0x07, 0xF3, 0x9A, 0xBE, 0x22, 0xF1, 0xB7, 0x22, "https://github.com/swiftwasm/carton/releases/download/0.3.1/static.zip"
0x5B, 0x7B, 0x41, 0x86, 0x66, 0xDF, 0x98, 0x52, 0x2C, 0x7B, 0xE5, 0x54, 0x73, 0xD2, 0x3E, 0x8A,
])
private let archiveURL = "https://github.com/swiftwasm/carton/releases/download/0.3.0/static.zip"
private let verifyHash = Equality<ByteString, String> { private let verifyHash = Equality<ByteString, String> {
""" """
@ -54,7 +50,7 @@ struct Dependency {
try fileSystem.removeFileTree(cartonDir) try fileSystem.removeFileTree(cartonDir)
let client = HTTPClient(eventLoopGroupProvider: .createNew) let client = HTTPClient(eventLoopGroupProvider: .createNew)
let request = try HTTPClient.Request.get(url: archiveURL) let request = try HTTPClient.Request.get(url: staticArchiveURL)
let response: HTTPClient.Response = try await { let response: HTTPClient.Response = try await {
client.execute(request: request).whenComplete($0) client.execute(request: request).whenComplete($0)
} }
@ -63,14 +59,14 @@ struct Dependency {
guard guard
var body = response.body, var body = response.body,
let bytes = body.readBytes(length: body.readableBytes) let bytes = body.readBytes(length: body.readableBytes)
else { throw DependencyError.downloadFailed(url: archiveURL) } else { throw DependencyError.downloadFailed(url: staticArchiveURL) }
terminal.logLookup("Polyfills archive successfully downloaded from ", archiveURL) terminal.logLookup("Polyfills archive successfully downloaded from ", staticArchiveURL)
let downloadedArchive = ByteString(bytes) let downloadedArchive = ByteString(bytes)
let downloadedHash = SHA256().hash(downloadedArchive) let downloadedHash = SHA256().hash(downloadedArchive)
try verifyHash(downloadedHash, archiveHash, context: archiveURL) try verifyHash(downloadedHash, staticArchiveHash, context: staticArchiveURL)
let archiveFile = cartonDir.appending(component: "static.zip") let archiveFile = cartonDir.appending(component: "static.zip")
try fileSystem.createDirectory(cartonDir, recursive: true) try fileSystem.createDirectory(cartonDir, recursive: true)

View File

@ -150,7 +150,11 @@ extension Templates {
fileSystem: fileSystem, fileSystem: fileSystem,
project: project, project: project,
dependencies: [ dependencies: [
.init(name: "Tokamak", url: "https://github.com/swiftwasm/Tokamak", version: .branch("main")), .init(
name: "Tokamak",
url: "https://github.com/swiftwasm/Tokamak",
version: .branch("main")
),
], ],
targetDepencencies: [ targetDepencencies: [
.init(name: "TokamakDOM", package: "Tokamak"), .init(name: "TokamakDOM", package: "Tokamak"),
@ -158,8 +162,8 @@ extension Templates {
terminal terminal
) )
try fileSystem.writeFileContents(project.path.appending( try fileSystem.writeFileContents(project.path.appending(
components: "Sources", components: "Sources",
project.name, project.name,
"main.swift" "main.swift"
)) { )) {
""" """
@ -176,7 +180,9 @@ extension Templates {
""" """
.write(to: $0) .write(to: $0)
} }
try fileSystem.writeFileContents(project.path.appending(components: "Sources", project.name, "ContentView.swift")) { try fileSystem.writeFileContents(
project.path.appending(components: "Sources", project.name, "ContentView.swift")
) {
""" """
import TokamakDOM import TokamakDOM

View File

@ -0,0 +1,11 @@
import TSCBasic
let devDependencySHA256 = ByteString([
0xE5, 0x21, 0x17, 0xB3, 0xA7, 0xDF, 0xFF, 0x9D, 0x48, 0x4F, 0x60, 0x06, 0x74, 0x4F, 0xC1, 0x6D,
0x28, 0x34, 0x23, 0xB4, 0xFD, 0xF0, 0xF8, 0xDC, 0x2C, 0xA2, 0xE4, 0xCF, 0x23, 0x54, 0xB1, 0x81,
])
let staticArchiveHash = ByteString([
0x5F, 0xC2, 0xAF, 0x9C, 0x0C, 0x3E, 0x1F, 0x65, 0x09, 0xE4, 0x16, 0xA3, 0x1B, 0x21, 0x20, 0x23,
0xA7, 0x7B, 0xD1, 0x32, 0xAF, 0x9C, 0x87, 0x6F, 0xF3, 0x08, 0x87, 0xF5, 0x99, 0x68, 0xD0, 0x7A,
])

View File

@ -5,8 +5,8 @@
"package": "JavaScriptKit", "package": "JavaScriptKit",
"repositoryURL": "https://github.com/kateinoigakukun/JavaScriptKit", "repositoryURL": "https://github.com/kateinoigakukun/JavaScriptKit",
"state": { "state": {
"branch": "85b8617", "branch": "c90e82f",
"revision": "85b8617eb107e161ede6b8ded5f1eb88b54648e0", "revision": "c90e82fe1d576a2ccd1aae798380bf80be7885fb",
"version": null "version": null
} }
} }

View File

@ -9,11 +9,12 @@ let package = Package(
.executable(name: "TestApp", targets: ["TestApp"]), .executable(name: "TestApp", targets: ["TestApp"]),
], ],
dependencies: [ dependencies: [
.package(url: "https://github.com/kateinoigakukun/JavaScriptKit", .revision("85b8617")), .package(url: "https://github.com/kateinoigakukun/JavaScriptKit", .revision("c90e82f")),
], ],
targets: [ targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite. // Targets are the basic building blocks of a package. A target can define a module or a test
// Targets can depend on other targets in this package, and on products in packages which this package depends on. // suite. Targets can depend on other targets in this package, and on products in packages which
// this package depends on.
.target(name: "TestApp", dependencies: ["JavaScriptKit", "TestLibrary", "CustomPathTarget"]), .target(name: "TestApp", dependencies: ["JavaScriptKit", "TestLibrary", "CustomPathTarget"]),
.target(name: "TestLibrary"), .target(name: "TestLibrary"),
.target(name: "CustomPathTarget", path: "CustomPathTarget"), .target(name: "CustomPathTarget", path: "CustomPathTarget"),

BIN
entrypoint/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

30
package-lock.json generated
View File

@ -239,9 +239,9 @@
"dev": true "dev": true
}, },
"ajv-keywords": { "ajv-keywords": {
"version": "3.5.0", "version": "3.5.1",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.0.tgz", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.1.tgz",
"integrity": "sha512-eyoaac3btgU8eJlvh01En8OCKzRqlLe2G5jDsCr3RiE2uLGMEEB1aaGwVVpwR8M95956tGH6R+9edC++OvzaVw==", "integrity": "sha512-KWcq3xN8fDjSB+IMoh2VaXVhRI0BBGxoYp3rx7Pkb6z0cFjYR9Q9l4yZqqals0/zsioCmocC5H6UvsGD4MoIBA==",
"dev": true "dev": true
}, },
"ansi-regex": { "ansi-regex": {
@ -713,9 +713,9 @@
} }
}, },
"chokidar": { "chokidar": {
"version": "3.4.0", "version": "3.4.1",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.0.tgz", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.1.tgz",
"integrity": "sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ==", "integrity": "sha512-TQTJyr2stihpC4Sya9hs2Xh+O2wf+igjL36Y75xx2WdHuiICcn/XJza46Jwt0eT5hVpQOzo3FpY3cj3RVYLX0g==",
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
@ -1233,9 +1233,9 @@
} }
}, },
"enhanced-resolve": { "enhanced-resolve": {
"version": "4.2.0", "version": "4.3.0",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.2.0.tgz", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz",
"integrity": "sha512-S7eiFb/erugyd1rLb6mQ3Vuq+EXHv5cpCkNqqIkYkBgN2QdFnyCZzFBleqwGEx4lgNGYij81BWnCrFNK7vxvjQ==", "integrity": "sha512-3e87LvavsdxyoCfGusJnrZ5G8SLPOFeHSNpZI/ATL9a5leXo2k0w6MKnbqhdBad9qTobSfB20Ld7UmgoNbAZkQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"graceful-fs": "^4.1.2", "graceful-fs": "^4.1.2",
@ -2428,9 +2428,9 @@
} }
}, },
"neo-async": { "neo-async": {
"version": "2.6.1", "version": "2.6.2",
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
"integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
"dev": true "dev": true
}, },
"nice-try": { "nice-try": {
@ -3481,9 +3481,9 @@
"dev": true "dev": true
}, },
"tar-stream": { "tar-stream": {
"version": "2.1.2", "version": "2.1.3",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.2.tgz", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.3.tgz",
"integrity": "sha512-UaF6FoJ32WqALZGOIAApXx+OdxhekNMChu6axLJR85zMMjXKWFGjbIRe+J6P4UnRGg9rAwWvbTT0oI7hD/Un7Q==", "integrity": "sha512-Z9yri56Dih8IaK8gncVPx4Wqt86NDmQTSh49XLZgjWpGZL9GK9HKParS2scqHCC4w6X9Gh2jwaU45V47XTKwVA==",
"dev": true, "dev": true,
"requires": { "requires": {
"bl": "^4.0.1", "bl": "^4.0.1",

View File

@ -1,5 +1,5 @@
const path = require("path"); const path = require("path");
const outputPath = path.resolve(__dirname, "Public"); const outputPath = path.resolve(__dirname, "static");
module.exports = { module.exports = {
entry: "./entrypoint/dev.js", entry: "./entrypoint/dev.js",