ajalt / clikt Goto Github PK
View Code? Open in Web Editor NEWMultiplatform command line interface parsing for Kotlin
Home Page: https://ajalt.github.io/clikt/
License: Apache License 2.0
Multiplatform command line interface parsing for Kotlin
Home Page: https://ajalt.github.io/clikt/
License: Apache License 2.0
I am pulling 1.5.0 from jcenter, however the fix I see in the source code to rename subcommands to _subcommands to avoid the namng conflict for internal variables is not available in the current release, making the subcommands mechanism unusable as far as i can tell. Would you be able to make a new release? Thanks.
If I make a subcommand (e.g. sub
) with a required argument, and then do ./tool sub
, it prints the following:
Usage: tool [OPTIONS] COMMAND [ARGS]...
Error: Missing argument "ARGUMENT".
The usage printed is for the main command, but the error concerns the subcommand. It's not immediately clear that the argument missing actually belongs to the subcommand, unless you do ./tool sub -h
:
Usage: sub [OPTIONS] ARGUMENT
<help omitted>
Options:
-h, --help Show this message and exit
I think this should be changed to it's clear where the argument is coming from.
I'm unclear as to the intention of the exists
flag. I expected that an unspecified file argument would not exist, and therefore would result in an option-reading failure; on the contrary, clikt seems happy to yield a null file in this case. Is that intentional? Why wouldn't an existence requirement imply a non-null requirement?
Currently the NonInteractiveCliktConsole
only reads in the first line of any input file passed as the input stream.
This can be fixed by caching the buffered reader like I have below.
class NonInteractiveCliktConsole : CliktConsole {
private val bufferedReader by lazy {
System.`in`.bufferedReader()
}
override fun promptForLine(prompt: String, hideInput: Boolean) = try {
print(prompt, false)
bufferedReader.readLine()
} catch (err: IOException) {
null
}
override fun print(text: String, error: Boolean) {
if (error) {
System.err
} else {
System.out
}.print(text)
}
override val lineSeparator: String get() = System.lineSeparator()
}
I'm using version 1.5.0
.
Great Library!!!
This would be a nice enhancement to the existing .multiple()
method.
The regex could specify the separator to split regular expression.
E.g. var args by option().splitMultiple(",")
would parse --args foo,bar,baz
into listOf("foo","bar","baz")
Is there a support for REPL? If so, is there an example for the same??
It would be great if I could define options/arguments that I'll use in a lot of commands in just one place, and reuse them from multiple CliktCommand subclasses.
I have a number of subcommands that all require that the program be run in the context of a project directory, specified on the command line:
val baseDir by option("--dir",
help = "Path to your Spelunk directory (defaults to current directory)")
.file(exists = true, readable = true, folderOkay = true, fileOkay = false)
.default(Paths.get("").toFile())
I'd prefer not to have to copy that chunk of code to a bunch of different subcommand classes, and instead would like to have one instance of that delegate that I can reference from multiple places. (Or one thing that will manufacture it.)
I think the most straightforward way would be to have an option
entry point function that isn't an extension method.CliktCommand.option
is an extension, so it can only be created in the context of a CliktCommand. Having a static method would bypass this issue.
Current workarounds:
OptionWithValues
directly, which is rather fragile and requires reimplementing a few lines from the library.class CommonArgs {
companion object {
val baseDirDelegate = object : NoRunCliktCommand() {
val opt = option("--dir",
help = "Path to your Spelunk directory (defaults to current directory)")
.file(exists = true, readable = true, folderOkay = true, fileOkay = false)
.default(Paths.get("").toFile())
}.opt
}
}
// Use it elsewhere:
val baseDir: File by CommonArgs.baseDirDelegate
Thanks for this nice library. I would be great if we can make this available on maven central as this is the default repo (or mirror) for most enterprises. May be you can enable the jcenter feature, which allow us to sync to central automatically.
I create an IDEA gradle project and write sample code like the README shows, but when I run command ./hello
at root of project, it's not work and I got this error:
Java/WorkSpace/CliktDemo
➜ ./src/main/kotlin/Hello.kt --count=3
./src/main/kotlin/Hello.kt: line 1: import: command not found
...
then I clone the project and build it, it's work only when I run command ./runsample copy --help
, not like the README says ./hello --help
, did i miss something?
It would be nice if there would be an option that prints the help message, if no parameters are present.
At least I did not find any easy way to check for no parameters?
My request is similar to the sub commands behavior (if non given the help message is printed), but I also want it if no parameter is provided for a sub command, like
"./tool subcommand" -> should print help message
Hey There,
Love the overall semantics of this library!
One thing that I find myself struggling with is the nesting of commands as described by the documentations
myapp super arg1 sub arg2
# Example: let 'fluffy' be the argument denoting the resource I am trying to create
# pets is a super command, with subcommands: create/delete
# i.e. this is how we are forced to write the input
myapp pets fluffy create
# this is the desired state of how it intuitively works
myapp pets create fluffy
# real life example:
git remote add URL
# not
git remote URL add
# another real life example
aws ec2 launch-instance myinstance
# not
aws ec2 myinstance launch instance
I used this library to make my cli program, but the help message printed incorrectly like this:
-L, --lorem-ipsum
It is lorem ipsum text, just ignore it. I
think it
doesn't
works
correctly.
I expected help message like this:
-L, --lorem-ipsum
It is lorem ipsum text, just ignore it. I
think it doesn't works correctly.
So I traced that why the help message formats incorrectly. And I found the reason. If the length of local variable subsequentIndent
of Extension method StringBuilder.appendDefinitionList (in PlaintextHelpFormatter.kt)
is so long, The help message format broken. And I traced why. It's the problem of StringBuilder.wrapParagraph (in text.kt)
. It must resets the length of local variable currentWidth
to 0, but it resets to subsequentIndent.length
. So I changed the code. And it works.
Could you fix the code I pointed out? It would make this project more useful.
(Sorry for my bad english. Thank you for reading this article, which may be disrespectful or annoying.)
How do I return the command line arguments that are processed in the run() function. I built a map that contains all the arguments within the run functions but I am not able to return that to the main function.
I would consider printing messages into stderr in some cases. Namely by exceptions in CliktCommand.main()
:
For example GNU command ls
also prints to stderr when an invalid option is given.
I always thought stdout should be used for interesting command output (data) and stderr for messages (errors, logs).
Options --help and --version are correct to print to stdout, because that's the currently requested command behavior. But if we pass an invalid option, the error message and the following usage help should be printed in stderr in my optinion.
I have a case where I need to add a "multiple" option via environment variable and some of these are going to contain spaces.
It looks like my only option at this point is to add my own hacky whitespace escape scheme. I'd prefer to just change the split character to a comma. Can you add the envvarSplit pattern parameter to the "option" function?
Argument's cannot start with (for example) a forward slash or a backslash
That makes it difficult to use file paths as arguments.
E.g. the following tests fail
https://github.com/roaanv/clikt/blob/1ddcd6a6b3cf5fd7bcedd661befa89c61d5e5c69/clikt/src/test/kotlin/com/github/ajalt/clikt/parameters/ArgumentTest.kt#L335
https://github.com/roaanv/clikt/blob/1ddcd6a6b3cf5fd7bcedd661befa89c61d5e5c69/clikt/src/test/kotlin/com/github/ajalt/clikt/parameters/ArgumentTest.kt#L344
I was reading all the documentation and saw the following:
An easier way to do development is to used the installDist task provided by the plugin. This builds all the distribution scripts in your build folder, which you can then execute normally. See Clikt’s runsample script for an example of this approach.
I saw the "run sample script" which is calling to installDist but there have not nothing documentation about this into the readme. Where could I read something about this?
Regards
There is a difference in behavior using CliKt from executable jar. In case of running it without required options error message doesn't contain full information.
Example:
class Cli : CliktCommand(help = """
Run --help to show required options
""".trimIndent()) {
val apiKey: String by option(help = "Mailgun Private API Key").required()
val domain: String by option(help = "Sending domain").required()
val fromUser: String by option(help = "User name in From email field").required()
val fromEmail: String by option(help = "User email in From email field").required()
val toEmail: String by option(help = "Concrete email or mailing list").required()
val subject: String by option(help = "Subject of email").required()
val htmlFile: String by option(help = "Name of bundled template").required()
override fun run() = Unit
}
fun main(args: Array<String>) {
Cli().main(args)
}
Running this from executable jar like this:
java -jar target/mailgun-sender-0.0.1-SNAPSHOT-jar-with-dependencies.jar
results in the following output:
→ java -jar target/mailgun-sender-0.0.1-SNAPSHOT-jar-with-dependencies.jar
Usage: cli [OPTIONS]
Error: Missing option "--api-key".
But the following is expected:
Usage: cli [OPTIONS]
Run --help to show required options
Options:
--api-key TEXT Mailgun Private API Key
--domain TEXT Sending domain
--from-user TEXT User name in From email field
--from-email TEXT User email in From email field
--to-email TEXT Concrete email or mailing list
--subject TEXT Subject of email
--html-file TEXT Name of bundled template
-h, --help Show this message and exit
I am using this excellent library to pass JDBC connection info to my application. Unfortunately this leads to
the error "Error: no such option:" in the case when the JDBC URL contains an equals sign. How can I handle this case properly?
Here is an example of what happens:
λ java -jar database-consistency-checker-1.0.jar jdbc:test:host:port:schema/option1=value1
Usage: database-consistency-checker [OPTIONS] jdbcUrl dbUser dbSchema COMMAND
[ARGS]...
Error: no such option: "jdbc:test:host:port:schema/option1".
How can I build a option not required?
Every option will ask user to input even it has default value.
I wanna use default value when user do not wanna input this option.
thanks
Is there a way to make one Option dependent upon another Option?
Example: If a user specifies Option --action update
, then --id
Option is required.
In addition to being dependent, the Option might also have different choices and required status.
Hello,
First, thanks a lot for this great tool!
In one of my CLI, I decided to change slightly the API and I want to deprecated some options.
It would be nice if this could be supported out of the box by Clikt
.
class MyCommand : CliktCommand() {
val oldFlag by option("--old-api").convert { it.toBoolean() }.deprecated("Use --new-api instead")
val newFlag by option("--new-api").flag()
override fun run() {
if (oldFlag ?: newFlag) println("Hello world")
}
}
Then if one run --old-api true
, the program would not crash, but display a warning message:
WARNING: The option '--old-api' is deprecated: Use --new-api instead
Running --help
could either not show the option at all, or explicitly state it is deprecated.
Eventually deprecated
could take a severity argument (warning or error) where the "error" level would make the program crash with a nice and explicit error message in case the deprecated api has been used.
What do you think?
Hi! I really like this library and it would be really useful in a tool I develop (right now I use picocli but that's a bit cumbersome in Kotlin), but I'm a bit reluctant to adopt Clikt.
Right now, there is no public information regarding how well-tested is Clikt (I could go through the codebase and tests, but that's time consuming).
I would really appreciate if you could add at least some public continuous integration and code coverage tracking (Gradle with Jacoco and any of the free-for-open-source services like Codecov and Travis-CI would be fine). I would like to know that the library is reasonably tested and stable and that you intend to support it before committing to use it in my projects.
Let me know what are your plans with the library. Thanks!
Prompts and echo use the standard inputs and outputs which makes testing harder and clunky.
It would be nice to be able to inject custom input and output stream to CliktCommand
and TermUi
Hello,
I would like to let the user of the command line decide if he enters the password via the keyboard or if he specifies it via an environment variable.
My program looks like
class PromptDemo : CliktCommand(name="prompt") {
private val pwdViaKeyboard by option().prompt(
text = "Please enter your password",
default = null
)
private val pwdViaEnv by option()
val pwd : String
get() {
if(pwdViaKeyBoard!=null) {
return pwdViaKeyBoard
}
else if (pwdViaEnv!=null) {
val pwd = System.getenv(pwdViaKeyBoard)
if(pwd!=null) return pwd
}
throw IllegalArgumentException("You have to specify a password.")
}
override fun run() {
println("pwd: '$pwd'")
}
}
fun main(args: Array<String>) {
PromptDemo().main(args)
}
My expectation would be that, when I enter
prompt --pwd-via-env PWD
and have set the env variable PWD
to foo
, that I would get
pwd: 'foo'
But I get
Please enter your password:
.
My expectation would be that only when I specify the option --pwd-via-keyboard
I would get the password prompt.
Could you add an additional parameter in the prompt() option which lets me evaluate the option, only when the user has required it (via specifying this option)?
Best regards,
Dieter
Hi, first of all I want to say that this is a great library with an elegant API, thank you for writing it.
One thing that I want to nitpick is the use of exceptions in the public API. Exceptions live outside of the Kotlin type system (especially because exceptions are unchecked in Kotlin) and I'm not sure that incorrect user input should necessarily be considered exceptional. A simple sealed class which simulates an Either
type would be a good solution.
Examples of exception abuse:
I understand that there is more than one way to do things, so maybe you've chosen this design intentionally (in which case just ignore this ;) ), I'm just raising this issue in case you haven't really considered it. I'd also be happy to implement this in a PR.
I think it would be great if Clikt could support different I/O environments, not just the standard I/O provided by the System
class. This would make it much more flexible, as it could use be used within other applications that provide its own CLI.
Thanks for writing this library. I was wondering if you have / would consider making it possible to test error handling and custom return codes?
The idea is when implementing a CliktComand
or including it into another library / framework for their users would want to do the following:
Challenges I've encountered so far and some thoughts towards a solution, but those solutions themselves have accompanying issues:
fun main(argv: List<String>): Int { /* snip */ }
fun main(argv: Array<String>) = exitProcess(main(argv.asList()))
I can completely understanding being hesitant about adding return parameters to these functions as they're the entry point everyone uses, but the breakage should be very easy to fix. I can create a speculative pull request if you want to see what a possible set of changes might look like and we can see what options there might be.
Below is an example where I've done it from as a consumer of the library, this is within the broader context of me creating a tiny web framework mimicking Dropwizard's single fat jar which allows you to run migrations, start an application server, add custom sub-commands, etc...
import com.github.ajalt.clikt.core.CliktCommand
import kotlin.system.exitProcess
abstract class Command (
help: String = "",
epilog: String = "",
name: String? = null,
invokeWithoutSubcommand: Boolean = false
): CliktCommand(
help = help,
epilog = epilog,
name = name,
invokeWithoutSubcommand = invokeWithoutSubcommand) {
companion object {
const val SUCCESS = 0
const val COMMAND_PARSING_ERROR = 1 // used by Clikt
const val ERROR_WHILE_COMMAND_RUNNING = 2
}
/**
* Implement [doRun] and don't use [main]
*/
final override fun run() {
val exitCode = try {
doRun()
} catch (e: Exception) {
exceptionHandler(e)
}
exitProcess(exitCode)
}
abstract fun doRun(): Int
open fun exceptionHandler(e: Exception) = ERROR_WHILE_COMMAND_RUNNING
}
I'm trying to create a fairly complex CLI app and like the approach of the Dynamic Subcommand Example, but I'm not able to figure how how to properly have subcommands of my modules.
For my main file:
class Sample : CliktCommand(
help = """Sample Main App"""
) {
override fun run(){
}
}
@JvmStatic
fun main(args: Array<String>) {
val kodein = Kodein {
bind() from setBinding<CliktCommand>()
import(containersModule)
}
val commands: Set<CliktCommand> by kodein.instance()
Sample().subcommands(commands).main(args)
}
In the containers:
class Containers : CliktCommand(
help = """Container Operations"""
) {
override fun run() {
}
}
class List : CliktCommand() {
override fun run() {
echo("Hi")
}
}
val containersModule = Kodein.Module("containers") {
bind<CliktCommand>().inSet() with provider {
Containers().subcommands(List())
}
}
I'd like to have the main app, ./sample
have submodules and those submodules have commands such as:
./sample containers list
The above code samples has an Unresolved reference: subcommands
for Containers().subcommands(List())
. What is the proper way to have an imported module have subcommands?
When command's helpOptionNames is empty, the subcommand will not create context.
private fun createContext(parent: Context? = null) {
_context = Context.build(this, parent, _contextConfig)
if (context.helpOptionNames.isEmpty()) return // RETURN !!!
val names = context.helpOptionNames - registeredOptionNames()
if (names.isNotEmpty()) _options += helpOption(names, context.helpOptionMessage)
for (command in _subcommands) { // Create context
command.createContext(context)
}
}
Hi,
I'm not able to find a good explaination on how to build the final command line tool.
I'm aware that we need basically a wrapper around our finished Java 'Application' to call it from the command line.
The Docs are not really clear for a beginner like me.
Regards !
default()
doesn't work after applying multiple()
and multiple()
doesn't work after applying default()
.
I have a list of things that the user can provide, but if none are provided I want to default the list to be something non-empty.
The default help text for versionOption
ends with a dot: "Show the version and exit."
. This is inconsistent with the default help text for helpOption
and with the usually adopted conventions.
Documentation says:
* @param writable If false, fail of the given path is not writable
* @param readable If false, fail of the given path is not readable
but the code it runs is
if (writable && !it.canWrite()) fail("$name \"$it\" is not writable.")
if (readable && !it.canRead()) fail("$name \"$it\" is not readable.")
my use case for this is to NOT have CliktCommand wrap my whole application. (i.e. the main)
I want it for parsing and retrieving cl argumnets, but I don't want to inherit from it.
i.e. I want to simply have a clparser property at the top level somewhere, and use it for parsing the arguments. and then use it again for retrieving the arguments.
This is mostly possible using your 'NoRunCliktCommand' (a better name would be nice)
but there is no way I can see, from the docs, to retrieve a cl argument/option value from a NoRunCliktCommand object?
They are stored in the _options property...but I can't access it.
can we have a method in the API for doing this please.
e.g. findOption(name:String) : Option
java.nio.Path
supplants java.io.File
in JDK 8+; it would be quite useful to be able to have options of this type, e.g. with a path
extension to RawArgument
/RawOption
. This would be presumably identical to the .file(...)
API:
/**
* Convert the option to a [Path].
*
* @param exists If true, fail if the given path does not exist
* @param fileOkay If false, fail if the given path is a file
* @param folderOkay If false, fail if the given path is not a directory
* @param writable If true, fail if the given path is not writable
* @param readable If true, fail if the given path is not readable
* @param fileSystem If specified, the [FileSystem] with which to resolve paths.
*/
fun RawOption.path(
exists: Boolean = false,
fileOkay: Boolean = true,
folderOkay: Boolean = true,
writable: Boolean = false,
readable: Boolean = false,
filesystem: FileSystems.getDefault()): NullableOption<Path, Path>
The only real difference here is the addition of a FileSystem
param, which can be safely defaulted 99% of the time.
I'm using something like this internally at present; happy to open a PR if there are no objections to the API.
Is it possible to provide/capture an argument based on the value of a toggle.
e.g.
java -jar inventory.jar scrape --source remotehttps
java -jar inventory.jar scrape --source fakefromfilesysem /home/me/thewholeweb/
scrape is the command. source is an option. when source is fakefromfilesystem capture a path argument?
I think the default value for options would be a very useful part of the help output, but I've not found a way to achieve this. Any trick I'm missing, or is it just not possible?
Hi!
I'm trying to get your code example working but it fails with the following error:
src/main/kotlin/de/partyschaum/giftcard/domain/giftcard/client/clikt/GitftCard.kt: (22, 10): Expression 'subcommands' of type 'List<CliktCommand>' cannot be invoked as a function. The function 'invoke()' is not found
src/main/kotlin/de/partyschaum/giftcard/domain/giftcard/client/clikt/GitftCard.kt: (22, 10): Cannot access 'subcommands': it is invisible (private in a supertype) in 'Database'
Since I'm somewhat new to Kotlin it could be a problem on my side. Anyway, I'm stuck. In case you need more context you'll find the example code here.
Is it possible to add an err
option to TermUi.prompt()
like TermUi.echo()
? That would be useful for tools where the user is inclined to pipe stdout somewhere and is not really interesting in preserving password prompts and the like.
Say I have an argument as follows:
private val urls: List<String> by argument("urls", help="Some list of URLs.").multiple(true)
No exception is thrown when the argument is not provided in the CLI. I believe an exception should definitely be thrown in this case. Is there something I'm missing or is this a bug?
Thanks :)
Not an issue, rather a question:
Example
CliktCommand(help = "Sentence1.\nSentence2.")
Will generate help text without the newline characters. I have tried \n
and \r
. Am I asking for the impossible (im not familiar with cli low lvl impl) or am I doing something wrong?
I am trying this with gitbash on Windows.
Popular shells support programmable auto completions:
Clikt could provide options - much like the --version
and --help
options - that would output the commands that specify auto completion for a given shell.
Example:
my-program --generate-auto-completion=bash > /usr/local/etc/bash_completion
If the value of this option (bash
in this example) is omitted, the shell is determined from the value of the SHELL
env var.
Could we somehow highlight the mandatory / required options with same special char (like '*')
when the command usage is displayed?
Hello, I'm trying to write a unit-test for my class, but I cannot find any documentation on it. In particular, there are two questions I'm struggling with:
Example code:
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.options.convert
import com.github.ajalt.clikt.parameters.options.option
import com.github.ajalt.clikt.parameters.options.required
import io.mockk.mockk
import org.junit.jupiter.api.Test
import java.time.LocalDate
class Foo : CliktCommand() {
val day: LocalDate by option().convert { str -> LocalDate.parse(str) }.required()
override fun run() {
val service = connectToRealBillingService()
doOtherStuff(service)
}
fun connectToRealBillingService(): BillingService {
throw IllegalStateException("Shoudln't be called in tests")
}
fun doOtherStuff(service: BillingService): String {
// Stuff to test
return "$service with ${day.dayOfWeek}"
}
}
internal class FooTest {
@Test
fun testDoStuff() {
val unit = Foo()
val actual = unit.doOtherStuff(mockk<BillingService>()) // NPE on day.dayOfWeek
}
@Test
fun testDoStuff2() {
val unit = Foo()
unit.parse(listOf("--day=2019-01-01")) // Calls connectToRealBillingService()
// and maybe calls other functions I don't want to test here (I'm testing doStuff(), not run())
}
}
Would be great for CLI .
While using shadow I got the following error:
Task :runShadow FAILED
Error: Main method is not static in class com.github.ajalt.clikt.core.CliktCommand, please define the main method as:
public static void main(String[] args)
My gradle config:
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.3.21'
id 'org.jetbrains.dokka' version '0.9.18'
id 'com.github.johnrengelman.shadow' version '5.0.0'
id 'application'
}
repositories {
jcenter()
mavenCentral()
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation 'com.github.ajalt:clikt:1.7.0'
compile "org.apache.spark:spark-core_2.12:2.4.0"
compile "org.apache.spark:spark-graphx_2.12:2.4.0"
compile 'org.apache.spark:spark-sql_2.12:2.4.1'
compile 'ch.cern.sparkmeasure:spark-measure_2.11:0.13'
testImplementation('org.junit.jupiter:junit-jupiter-api:5.4.2')
testRuntime('org.junit.jupiter:junit-jupiter-engine:5.4.2')
}
dokka {
outputFormat = 'javadoc'
outputDirectory = "$buildDir/javadoc"
}
mainClassName = "com.company.project.module.Cli"
test {
useJUnitPlatform()
}
shadowJar {
zip64 true
}
version = "0.0.1"
Could you provide an sample integrating Shadow with Clikt?
After having built some CLI's lately using the library, I'm wondering if it would be beneficial to just expose print
and println
methods to mimic printing to the screen directly with System.out
or System.err
but in scope with the console's Context instead
Example proposed methods:
protected fun println() = echo("")
protected fun println(message: String) = echo(message)
protected fun println(message: Any?) = echo(message?.toString())
protected fun print(message: String) = echo(message, trailingNewLine = false)
protected fun print(message: Any?) = echo(message?.toString(), trailingNewLine = false)
We could also consider just exposing a out
and err
PrintStream on the CliktCommand type
class CliktCommand(...) {
protected val out: PrintStream by lazyPrintStream { echo(it, trailingNewLine = false) }
protected val err: PrintStream by lazyPrintStream { echo(it, trailingNewLine = false, err = true) }
...
private inline fun lazyPrintStream(output: (String) -> Unit): Lazy<PrintStream> = lazy(LazyThreadSafetyMode.NONE) {
PrintStream(object : OutputStream() {
override fun write(b: Int) {
output(b.toByte().toChar().toString())
}
})
}
}
at which point we could just alias the recommended methods above to go to out
by default
protected fun println() = out.println()
protected fun println(message: String) = out.println(message)
protected fun println(message: Any?) = out.println(message?.toString())
protected fun print(message: String) = out.print(message)
protected fun print(message: Any?) = out.print(message?.toString())
It currently points to "documenting.html"
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.