Skip to content

Commit

Permalink
Merge pull request square#861 from square/jwilson.1230.our_own_externals
Browse files Browse the repository at this point in the history
Define our own externals for Node.js fs and os APIs
  • Loading branch information
swankjesse authored Dec 30, 2020
2 parents ff192b7 + f21bfc0 commit eb53e5d
Show file tree
Hide file tree
Showing 7 changed files with 214 additions and 24 deletions.
1 change: 0 additions & 1 deletion okio/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@ kotlin {
dependsOn nonJvmMain
dependencies {
api deps.kotlin.stdLib.js
implementation("org.jetbrains.kotlinx:kotlinx-nodejs:0.0.7")
}
}
jsTest {
Expand Down
3 changes: 1 addition & 2 deletions okio/src/jsMain/kotlin/okio/FileSink.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
*/
package okio

import fs.writeSync
import org.khronos.webgl.Uint8Array

internal class FileSink(
Expand Down Expand Up @@ -46,6 +45,6 @@ internal class FileSink(
override fun close() {
if (closed) return
closed = true
fs.closeSync(fd)
closeSync(fd)
}
}
4 changes: 2 additions & 2 deletions okio/src/jsMain/kotlin/okio/FileSource.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ internal class FileSource(
check(!closed) { "closed" }

val data = Uint8Array(byteCount.toInt())
val readByteCount = fs.readSync(
val readByteCount = readSync(
fd = fd,
buffer = data,
length = byteCount,
Expand All @@ -50,6 +50,6 @@ internal class FileSource(
override fun close() {
if (closed) return
closed = true
fs.closeSync(fd)
closeSync(fd)
}
}
27 changes: 10 additions & 17 deletions okio/src/jsMain/kotlin/okio/NodeJsSystemFilesystem.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,6 @@
*/
package okio

import fs.Dirent
import fs.MakeDirectoryOptions
import fs.mkdirSync
import fs.openSync
import fs.opendirSync
import fs.statSync
import okio.Path.Companion.toPath

/**
Expand All @@ -39,7 +33,7 @@ internal object NodeJsSystemFilesystem : Filesystem() {

override fun canonicalize(path: Path): Path {
try {
val canonicalPath = fs.realpathSync(path.toString(), options = undefined as String?)
val canonicalPath = realpathSync(path.toString())
return canonicalPath.toString().toPath()
} catch (e: Throwable) {
throw IOException(e.message)
Expand Down Expand Up @@ -79,8 +73,7 @@ internal object NodeJsSystemFilesystem : Filesystem() {
try {
val result = mutableListOf<Path>()
while (true) {
// Note that the signature of readSync() returns a non-nullable Dirent; that's incorrect.
val dirent = (opendir.readSync() as Dirent?) ?: break
val dirent = opendir.readSync() ?: break
result += dir / dirent.name
}
return result
Expand All @@ -94,7 +87,7 @@ internal object NodeJsSystemFilesystem : Filesystem() {

override fun source(file: Path): Source {
try {
val fd = openSync(file.toString(), flags = "r", mode = undefined as String?)
val fd = openSync(file.toString(), flags = "r")
return FileSource(fd)
} catch (e: Throwable) {
throw IOException(e.message)
Expand All @@ -103,7 +96,7 @@ internal object NodeJsSystemFilesystem : Filesystem() {

override fun sink(file: Path): Sink {
try {
val fd = openSync(file.toString(), flags = "w", mode = undefined as String?)
val fd = openSync(file.toString(), flags = "w")
return FileSink(fd)
} catch (e: Throwable) {
throw IOException(e.message)
Expand All @@ -112,7 +105,7 @@ internal object NodeJsSystemFilesystem : Filesystem() {

override fun appendingSink(file: Path): Sink {
try {
val fd = openSync(file.toString(), flags = "a", mode = undefined as String?)
val fd = openSync(file.toString(), flags = "a")
return FileSink(fd)
} catch (e: Throwable) {
throw IOException(e.message)
Expand All @@ -121,15 +114,15 @@ internal object NodeJsSystemFilesystem : Filesystem() {

override fun createDirectory(dir: Path) {
try {
mkdirSync(dir.toString(), options = undefined as MakeDirectoryOptions?)
mkdirSync(dir.toString())
} catch (e: Throwable) {
throw IOException(e.message)
}
}

override fun atomicMove(source: Path, target: Path) {
try {
fs.renameSync(source.toString(), target.toString())
renameSync(source.toString(), target.toString())
} catch (e: Throwable) {
throw IOException(e.message)
}
Expand All @@ -139,16 +132,16 @@ internal object NodeJsSystemFilesystem : Filesystem() {
* We don't know if [path] is a file or a directory, but we don't (yet) have an API to delete
* either type. Just try each in sequence.
*
* TODO(jwilson): when Kotlin/JS uses a newer Node version, switch to fs.rmSync().
* TODO(jwilson): switch to fs.rmSync() when our minimum requirements are Node 14.14.0.
*/
override fun delete(path: Path) {
try {
fs.unlinkSync(path.toString())
unlinkSync(path.toString())
return
} catch (e: Throwable) {
}
try {
fs.rmdirSync(path.toString())
rmdirSync(path.toString())
} catch (e: Throwable) {
throw IOException(e.message)
}
Expand Down
4 changes: 2 additions & 2 deletions okio/src/jsMain/kotlin/okio/Platform.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ internal actual val PLATFORM_FILESYSTEM: Filesystem

@ExperimentalFilesystem
internal actual val PLATFORM_TEMPORARY_DIRECTORY: Path
get() = os.tmpdir().toPath()
get() = tmpdir().toPath()

internal actual val DIRECTORY_SEPARATOR: String
get() {
// TODO(swankjesse): return path.path.sep instead, once it has @JsNonModule
return when (os.platform()) {
return when (platform()) {
"win32" -> "\\"
else -> "/"
}
Expand Down
173 changes: 173 additions & 0 deletions okio/src/jsMain/kotlin/okio/fs.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
/*
* Copyright (C) 2020 Square, Inc.
*
* 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.
*/

/**
* This class declares the subset of Node.js filesystem APIs that we need in Okio.
*
*
* Why not Dukat?
* --------------
*
* This file does manually what ideally [Dukat] would do automatically.
*
* Dukat's generated stubs need awkward call sites to disambiguate overloads. For example, to call
* `mkdirSync()` we must specify an options parameter even though we just want the default:
*
* mkdirSync(dir.toString(), options = undefined as MakeDirectoryOptions?)
*
* By defining our own externals, we can omit the unwanted optional parameter from the declaration.
* This leads to nicer calling code!
*
* mkdirSync(dir.toString())
*
* Dukat also gets the nullability wrong for `Dirent.readSync()`.
*
*
* Why not Kotlinx-nodejs?
* -----------------------
*
* Even better than using Dukat directly would be to use the [official artifact][kotlinx_nodejs],
* itself generated with Dukat. We also don't use the official Node.js artifact for the reasons
* above, and also because it has an unstable API.
*
*
* Updating this file
* ------------------
*
* To declare new external APIs, run Dukat to generate a full set of Node stubs. The easiest way to
* do this is to add an NPM dependency on `@types/node` in `jsMain`, like this:
*
* ```
* jsMain {
* ...
* dependencies {
* implementation(npm('@types/node', '14.14.16', true))
* ...
* }
* }
* ```
*
* This will create a file with a full set of APIs to copy-paste from.
*
* ```
* okio/build/externals/okio-parent-okio/src/fs.fs.module_node.kt
* ```
*
* [Dukat]: https://github.com/kotlin/dukat
* [kotlinx_nodejs]: https://github.com/Kotlin/kotlinx-nodejs
*/
@file:JsModule("fs")
@file:JsNonModule
package okio

import org.khronos.webgl.Uint8Array
import kotlin.js.Date

internal external fun closeSync(fd: Number)

internal external fun mkdirSync(path: String): String?

internal external fun openSync(path: String, flags: String): Number

internal external fun opendirSync(path: String): Dir

internal external fun readSync(fd: Number, buffer: Uint8Array, offset: Number, length: Number, position: Number?): Number

internal external fun realpathSync(path: String): dynamic /* String | Buffer */

internal external fun renameSync(oldPath: String, newPath: String)

internal external fun rmdirSync(path: String)

internal external fun statSync(path: String): Stats

internal external fun unlinkSync(path: String)

internal external fun writeSync(fd: Number, buffer: Uint8Array): Number

internal open external class Dir {
open var path: String
open fun closeSync()
// Note that dukat's signature of readSync() returns a non-nullable Dirent; that's incorrect.
open fun readSync(): Dirent?
}

internal open external class Dirent {
open fun isFile(): Boolean
open fun isDirectory(): Boolean
open fun isBlockDevice(): Boolean
open fun isCharacterDevice(): Boolean
open fun isSymbolicLink(): Boolean
open fun isFIFO(): Boolean
open fun isSocket(): Boolean
open var name: String
}

internal external interface StatsBase<T> {
fun isFile(): Boolean
fun isDirectory(): Boolean
fun isBlockDevice(): Boolean
fun isCharacterDevice(): Boolean
fun isSymbolicLink(): Boolean
fun isFIFO(): Boolean
fun isSocket(): Boolean
var dev: T
var ino: T
var mode: T
var nlink: T
var uid: T
var gid: T
var rdev: T
var size: T
var blksize: T
var blocks: T
var atimeMs: T
var mtimeMs: T
var ctimeMs: T
var birthtimeMs: T
var atime: Date
var mtime: Date
var ctime: Date
var birthtime: Date
}

internal open external class Stats : StatsBase<Number> {
override fun isFile(): Boolean
override fun isDirectory(): Boolean
override fun isBlockDevice(): Boolean
override fun isCharacterDevice(): Boolean
override fun isSymbolicLink(): Boolean
override fun isFIFO(): Boolean
override fun isSocket(): Boolean
override var dev: Number
override var ino: Number
override var mode: Number
override var nlink: Number
override var uid: Number
override var gid: Number
override var rdev: Number
override var size: Number
override var blksize: Number
override var blocks: Number
override var atimeMs: Number
override var mtimeMs: Number
override var ctimeMs: Number
override var birthtimeMs: Number
override var atime: Date
override var mtime: Date
override var ctime: Date
override var birthtime: Date
}
26 changes: 26 additions & 0 deletions okio/src/jsMain/kotlin/okio/os.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright (C) 2020 Square, Inc.
*
* 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.
*/

/**
* See `fs.kt` for information on what this file does and how to keep it up-to-date.
*/
@file:JsModule("os")
@file:JsNonModule
package okio

internal external fun tmpdir(): String

internal external fun platform(): String

0 comments on commit eb53e5d

Please sign in to comment.